diff --git a/build.sh b/build.sh new file mode 100644 index 000000000..139c28359 --- /dev/null +++ b/build.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -e +set -x + +if [ ! -d "csources" ]; then + git clone --depth 1 https://github.com/nim-lang/csources.git +fi + +cd "csources" +sh build.sh +cd ".." + +./bin/nim c koch +./koch boot -d:release + +cp -f install.sh.template install.sh +chmod +x install.sh + +exit 0 diff --git a/compiler/aliases.nim b/compiler/aliases.nim index a26b94303..3f3d45ff7 100644 --- a/compiler/aliases.nim +++ b/compiler/aliases.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -16,9 +16,9 @@ type TAnalysisResult* = enum arNo, arMaybe, arYes -proc isPartOfAux(a, b: PType, marker: var TIntSet): TAnalysisResult +proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult -proc isPartOfAux(n: PNode, b: PType, marker: var TIntSet): TAnalysisResult = +proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult = result = arNo case n.kind of nkRecList: @@ -39,7 +39,7 @@ proc isPartOfAux(n: PNode, b: PType, marker: var TIntSet): TAnalysisResult = result = isPartOfAux(n.sym.typ, b, marker) else: internalError(n.info, "isPartOfAux()") -proc isPartOfAux(a, b: PType, marker: var TIntSet): TAnalysisResult = +proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult = result = arNo if a == nil or b == nil: return if containsOrIncl(marker, a.id): return diff --git a/compiler/ast.nim b/compiler/ast.nim index 0c828a6d9..565bb4353 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -160,6 +160,7 @@ type nkConstDef, # a const definition nkTypeDef, # a type definition nkYieldStmt, # the yield statement as a tree + nkDefer, # the 'defer' statement nkTryStmt, # a try statement nkFinally, # a finally section nkRaiseStmt, # a raise statement @@ -293,6 +294,7 @@ const # require RC ops sfCompileToCpp* = sfInfixCall # compile the module as C++ code sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code + sfExperimental* = sfOverriden # module uses the .experimental switch const # getting ready for the future expr/stmt merge @@ -566,8 +568,8 @@ type mBool, mChar, mString, mCstring, mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt, mTypeDesc, mVoidType, mPNimrodNode, mShared, mGuarded, mLock, mSpawn, mDeepCopy, - mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion, mNimrodMajor, - mNimrodMinor, mNimrodPatch, mCpuEndian, mHostOS, mHostCPU, mAppType, + mIsMainModule, mCompileDate, mCompileTime, mProcCall, + mCpuEndian, mHostOS, mHostCPU, mAppType, mNaN, mInf, mNegInf, mCompileOption, mCompileOptionArg, mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNKind, @@ -610,7 +612,7 @@ const # thus cannot be overloaded (also documented in the spec!): SpecialSemMagics* = { mDefined, mDefinedInScope, mCompiles, mLow, mHigh, mSizeOf, mIs, mOf, - mEcho, mShallowCopy, mExpandToAst, mParallel, mSpawn} + mEcho, mShallowCopy, mExpandToAst, mParallel, mSpawn, mAstToStr} type PNode* = ref TNode @@ -681,7 +683,6 @@ type heapRoot*: PRope # keeps track of the enclosing heap object that # owns this location (required by GC algorithms # employing heap snapshots or sliding views) - a*: int # ---------------- end of backend information ------------------------------ @@ -734,7 +735,7 @@ type # check for the owner when touching 'usedGenerics'. usedGenerics*: seq[PInstantiation] tab*: TStrTable # interface table for modules - of skLet, skVar, skField: + of skLet, skVar, skField, skForVar: guard*: PSym else: nil magic*: TMagic @@ -772,6 +773,7 @@ type # it won't cause problems TTypeSeq* = seq[PType] + TLockLevel* = distinct int16 TType* {.acyclic.} = object of TIdObj # \ # types are identical iff they have the # same id; there may be multiple copies of a type @@ -798,11 +800,12 @@ type deepCopy*: PSym # overriden 'deepCopy' operation size*: BiggestInt # the size of the type in bytes # -1 means that the size is unkwown - align*: int # the type's alignment requirements + align*: int16 # the type's alignment requirements + lockLevel*: TLockLevel # lock level as required for deadlock checking loc*: TLoc TPair*{.final.} = object - key*, val*: PObject + key*, val*: RootRef TPairSeq* = seq[TPair] TTable*{.final.} = object # the same as table[PObject] of PObject @@ -811,7 +814,7 @@ type TIdPair*{.final.} = object key*: PIdObj - val*: PObject + val*: RootRef TIdPairSeq* = seq[TIdPair] TIdTable*{.final.} = object # the same as table[PIdent] of PObject @@ -838,7 +841,7 @@ type counter*: int data*: TNodePairSeq - TObjectSeq* = seq[PObject] + TObjectSeq* = seq[RootRef] TObjectSet*{.final.} = object counter*: int data*: TObjectSeq @@ -1054,7 +1057,7 @@ proc discardSons(father: PNode) = father.sons = nil when defined(useNodeIds): - const nodeIdToDebug* = 310841 # 612794 + const nodeIdToDebug* = -1 # 884953 # 612794 #612840 # 612905 # 614635 # 614637 # 614641 # 423408 #429107 # 430443 # 441048 # 441090 # 441153 @@ -1166,6 +1169,16 @@ proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode, result.sons = @[name, pattern, genericParams, params, pragmas, exceptions, body] +const + UnspecifiedLockLevel* = TLockLevel(-1'i16) + MaxLockLevel* = 1000'i16 + UnknownLockLevel* = TLockLevel(1001'i16) + +proc `$`*(x: TLockLevel): string = + if x.ord == UnspecifiedLockLevel.ord: result = "" + elif x.ord == UnknownLockLevel.ord: result = "" + else: result = $int16(x) + proc newType(kind: TTypeKind, owner: PSym): PType = new(result) result.kind = kind @@ -1173,6 +1186,7 @@ proc newType(kind: TTypeKind, owner: PSym): PType = result.size = - 1 result.align = 2 # default alignment result.id = getID() + result.lockLevel = UnspecifiedLockLevel when debugIds: registerId(result) #if result.id < 2000: @@ -1184,7 +1198,7 @@ proc mergeLoc(a: var TLoc, b: TLoc) = a.flags = a.flags + b.flags if a.t == nil: a.t = b.t if a.r == nil: a.r = b.r - if a.a == 0: a.a = b.a + #if a.a == 0: a.a = b.a proc assignType(dest, src: PType) = dest.kind = src.kind @@ -1195,6 +1209,7 @@ proc assignType(dest, src: PType) = dest.align = src.align dest.destructor = src.destructor dest.deepCopy = src.deepCopy + dest.lockLevel = src.lockLevel # this fixes 'type TLock = TSysLock': if src.sym != nil: if dest.sym != nil: @@ -1545,3 +1560,9 @@ proc isEmptyType*(t: PType): bool {.inline.} = ## 'void' and 'stmt' types are often equivalent to 'nil' these days: result = t == nil or t.kind in {tyEmpty, tyStmt} +proc makeStmtList*(n: PNode): PNode = + if n.kind == nkStmtList: + result = n + else: + result = newNodeI(nkStmtList, n.info) + result.add n diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index eb7ffc63e..f23e9a983 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -14,7 +14,7 @@ import ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils -proc hashNode*(p: PObject): THash +proc hashNode*(p: RootRef): THash proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): PRope # Convert a tree into its YAML representation; this is used by the # YAML code generator and it is invaluable for debugging purposes. @@ -24,21 +24,21 @@ proc symToYaml*(n: PSym, indent: int = 0, maxRecDepth: int = - 1): PRope proc lineInfoToStr*(info: TLineInfo): PRope # ----------------------- node sets: --------------------------------------- -proc objectSetContains*(t: TObjectSet, obj: PObject): bool +proc objectSetContains*(t: TObjectSet, obj: RootRef): bool # returns true whether n is in t -proc objectSetIncl*(t: var TObjectSet, obj: PObject) +proc objectSetIncl*(t: var TObjectSet, obj: RootRef) # include an element n in the table t -proc objectSetContainsOrIncl*(t: var TObjectSet, obj: PObject): bool +proc objectSetContainsOrIncl*(t: var TObjectSet, obj: RootRef): bool # more are not needed ... # ----------------------- (key, val)-Hashtables ---------------------------- -proc tablePut*(t: var TTable, key, val: PObject) -proc tableGet*(t: TTable, key: PObject): PObject +proc tablePut*(t: var TTable, key, val: RootRef) +proc tableGet*(t: TTable, key: RootRef): RootRef type - TCmpProc* = proc (key, closure: PObject): bool {.nimcall.} # true if found + TCmpProc* = proc (key, closure: RootRef): bool {.nimcall.} # true if found -proc tableSearch*(t: TTable, key, closure: PObject, - comparator: TCmpProc): PObject +proc tableSearch*(t: TTable, key, closure: RootRef, + comparator: TCmpProc): RootRef # return val as soon as comparator returns true; if this never happens, # nil is returned @@ -79,9 +79,9 @@ proc debug*(n: PType) {.deprecated.} proc debug*(n: PNode) {.deprecated.} # --------------------------- ident tables ---------------------------------- -proc idTableGet*(t: TIdTable, key: PIdObj): PObject -proc idTableGet*(t: TIdTable, key: int): PObject -proc idTablePut*(t: var TIdTable, key: PIdObj, val: PObject) +proc idTableGet*(t: TIdTable, key: PIdObj): RootRef +proc idTableGet*(t: TIdTable, key: int): RootRef +proc idTablePut*(t: var TIdTable, key: PIdObj, val: RootRef) proc idTableHasObjectAsKey*(t: TIdTable, key: PIdObj): bool # checks if `t` contains the `key` (compared by the pointer value, not only # `key`'s id) @@ -196,7 +196,7 @@ proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym = else: internalError(list.info, "getSymFromList") result = nil -proc hashNode(p: PObject): THash = +proc hashNode(p: RootRef): THash = result = hash(cast[pointer](p)) proc mustRehash(length, counter: int): bool = @@ -245,13 +245,13 @@ proc lineInfoToStr(info: TLineInfo): PRope = toRope(toLinenumber(info)), toRope(toColumn(info))]) -proc treeToYamlAux(n: PNode, marker: var TIntSet, +proc treeToYamlAux(n: PNode, marker: var IntSet, indent, maxRecDepth: int): PRope -proc symToYamlAux(n: PSym, marker: var TIntSet, +proc symToYamlAux(n: PSym, marker: var IntSet, indent, maxRecDepth: int): PRope -proc typeToYamlAux(n: PType, marker: var TIntSet, +proc typeToYamlAux(n: PType, marker: var IntSet, indent, maxRecDepth: int): PRope -proc strTableToYaml(n: TStrTable, marker: var TIntSet, indent: int, +proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int, maxRecDepth: int): PRope = var istr = spaces(indent + 2) result = toRope("[") @@ -277,13 +277,13 @@ proc ropeConstr(indent: int, c: openArray[PRope]): PRope = inc(i, 2) appf(result, "$N$1}", [spaces(indent)]) -proc symToYamlAux(n: PSym, marker: var TIntSet, indent: int, +proc symToYamlAux(n: PSym, marker: var IntSet, indent: int, maxRecDepth: int): PRope = if n == nil: result = toRope("null") elif containsOrIncl(marker, n.id): result = ropef("\"$1 @$2\"", [toRope(n.name.s), toRope( - strutils.toHex(cast[TAddress](n), sizeof(n) * 2))]) + strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]) else: var ast = treeToYamlAux(n.ast, marker, indent + 2, maxRecDepth - 1) result = ropeConstr(indent, [toRope("kind"), @@ -298,13 +298,13 @@ proc symToYamlAux(n: PSym, marker: var TIntSet, indent: int, flagsToStr(n.options), toRope("position"), toRope(n.position)]) -proc typeToYamlAux(n: PType, marker: var TIntSet, indent: int, +proc typeToYamlAux(n: PType, marker: var IntSet, indent: int, maxRecDepth: int): PRope = if n == nil: result = toRope("null") elif containsOrIncl(marker, n.id): result = ropef("\"$1 @$2\"", [toRope($n.kind), toRope( - strutils.toHex(cast[TAddress](n), sizeof(n) * 2))]) + strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]) else: if sonsLen(n) > 0: result = toRope("[") @@ -326,7 +326,7 @@ proc typeToYamlAux(n: PType, marker: var TIntSet, indent: int, toRope("align"), toRope(n.align), toRope("sons"), result]) -proc treeToYamlAux(n: PNode, marker: var TIntSet, indent: int, +proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int, maxRecDepth: int): PRope = if n == nil: result = toRope("null") @@ -450,9 +450,10 @@ proc debug(n: PSym) = writeln(stdout, "skUnknown") else: #writeln(stdout, ropeToStr(symToYaml(n, 0, 1))) - writeln(stdout, ropeToStr(ropef("$1_$2: $3, $4, $5", [ - toRope(n.name.s), toRope(n.id), flagsToStr(n.flags), - flagsToStr(n.loc.flags), lineInfoToStr(n.info)]))) + writeln(stdout, "$1_$2: $3, $4, $5, $6" % [ + n.name.s, $n.id, flagsToStr(n.flags).ropeToStr, + flagsToStr(n.loc.flags).ropeToStr, lineInfoToStr(n.info).ropeToStr, + $n.kind]) proc debug(n: PType) = writeln(stdout, ropeToStr(debugType(n))) @@ -469,7 +470,7 @@ proc nextTry(h, maxHash: THash): THash = # generates each int in range(maxHash) exactly once (see any text on # random-number generation for proof). -proc objectSetContains(t: TObjectSet, obj: PObject): bool = +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 while t.data[h] != nil: @@ -478,7 +479,7 @@ proc objectSetContains(t: TObjectSet, obj: PObject): bool = h = nextTry(h, high(t.data)) result = false -proc objectSetRawInsert(data: var TObjectSeq, obj: PObject) = +proc objectSetRawInsert(data: var TObjectSeq, obj: RootRef) = var h: THash = hashNode(obj) and high(data) while data[h] != nil: assert(data[h] != obj) @@ -493,12 +494,12 @@ proc objectSetEnlarge(t: var TObjectSet) = if t.data[i] != nil: objectSetRawInsert(n, t.data[i]) swap(t.data, n) -proc objectSetIncl(t: var TObjectSet, obj: PObject) = +proc objectSetIncl(t: var TObjectSet, obj: RootRef) = if mustRehash(len(t.data), t.counter): objectSetEnlarge(t) objectSetRawInsert(t.data, obj) inc(t.counter) -proc objectSetContainsOrIncl(t: var TObjectSet, obj: PObject): bool = +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) while true: @@ -516,7 +517,7 @@ proc objectSetContainsOrIncl(t: var TObjectSet, obj: PObject): bool = inc(t.counter) result = false -proc tableRawGet(t: TTable, key: PObject): int = +proc tableRawGet(t: TTable, key: RootRef): int = var h: THash = hashNode(key) and high(t.data) # start with real hash value while t.data[h].key != nil: if t.data[h].key == key: @@ -524,8 +525,8 @@ proc tableRawGet(t: TTable, key: PObject): int = h = nextTry(h, high(t.data)) result = -1 -proc tableSearch(t: TTable, key, closure: PObject, - comparator: TCmpProc): PObject = +proc tableSearch(t: TTable, key, closure: RootRef, + comparator: TCmpProc): RootRef = var h: THash = hashNode(key) and high(t.data) # start with real hash value while t.data[h].key != nil: if t.data[h].key == key: @@ -535,12 +536,12 @@ proc tableSearch(t: TTable, key, closure: PObject, h = nextTry(h, high(t.data)) result = nil -proc tableGet(t: TTable, key: PObject): PObject = +proc tableGet(t: TTable, key: RootRef): RootRef = var index = tableRawGet(t, key) if index >= 0: result = t.data[index].val else: result = nil -proc tableRawInsert(data: var TPairSeq, key, val: PObject) = +proc tableRawInsert(data: var TPairSeq, key, val: RootRef) = var h: THash = hashNode(key) and high(data) while data[h].key != nil: assert(data[h].key != key) @@ -556,7 +557,7 @@ proc tableEnlarge(t: var TTable) = if t.data[i].key != nil: tableRawInsert(n, t.data[i].key, t.data[i].val) swap(t.data, n) -proc tablePut(t: var TTable, key, val: PObject) = +proc tablePut(t: var TTable, key, val: RootRef) = var index = tableRawGet(t, key) if index >= 0: t.data[index].val = val @@ -626,20 +627,34 @@ proc strTableAdd(t: var TStrTable, n: PSym) = strTableRawInsert(t.data, n) inc(t.counter) +proc reallySameIdent(a, b: string): bool {.inline.} = + when defined(nimfix): + result = a[0] == b[0] + else: + result = true + proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} = # returns true if n is already in the string table: # 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 replaceSlot = -1 while true: var it = t.data[h] if it == nil: break - if it.name.id == n.name.id: - t.data[h] = n # overwrite it with newer definition! - return true # found it + # Semantic checking can happen multiple times thanks to templates + # and overloading: (var x=@[]; x).mapIt(it). + # So it is possible the very same sym is added multiple + # times to the symbol table which we allow here with the 'it == n' check. + if it.name.id == n.name.id and reallySameIdent(it.name.s, n.name.s): + if it == n: return false + replaceSlot = h h = nextTry(h, high(t.data)) - if mustRehash(len(t.data), t.counter): + if replaceSlot >= 0: + t.data[replaceSlot] = n # overwrite it with newer definition! + return true # found it + elif mustRehash(len(t.data), t.counter): strTableEnlarge(t) strTableRawInsert(t.data, n) else: @@ -677,7 +692,7 @@ proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym = ti.h = nextTry(h, high(tab.data)) proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable, - excluding: TIntSet): PSym = + excluding: IntSet): PSym = var h: THash = ti.h and high(tab.data) var start = h result = tab.data[h] @@ -693,7 +708,7 @@ proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable, if result != nil and contains(excluding, result.id): result = nil proc firstIdentExcluding*(ti: var TIdentIter, tab: TStrTable, s: PIdent, - excluding: TIntSet): PSym = + excluding: IntSet): PSym = ti.h = s.h ti.name = s if tab.counter == 0: result = nil @@ -740,22 +755,22 @@ proc idTableHasObjectAsKey(t: TIdTable, key: PIdObj): bool = if index >= 0: result = t.data[index].key == key else: result = false -proc idTableGet(t: TIdTable, key: PIdObj): PObject = +proc idTableGet(t: TIdTable, key: PIdObj): RootRef = var index = idTableRawGet(t, key.id) if index >= 0: result = t.data[index].val else: result = nil -proc idTableGet(t: TIdTable, key: int): PObject = +proc idTableGet(t: TIdTable, key: int): RootRef = var index = idTableRawGet(t, key) if index >= 0: result = t.data[index].val else: result = nil -iterator pairs*(t: TIdTable): tuple[key: int, value: PObject] = +iterator pairs*(t: TIdTable): tuple[key: int, value: RootRef] = for i in 0..high(t.data): if t.data[i].key != nil: yield (t.data[i].key.id, t.data[i].val) -proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: PObject) = +proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) = var h: THash h = key.id and high(data) while data[h].key != nil: @@ -765,7 +780,7 @@ proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: PObject) = data[h].key = key data[h].val = val -proc idTablePut(t: var TIdTable, key: PIdObj, val: PObject) = +proc idTablePut(t: var TIdTable, key: PIdObj, val: RootRef) = var index: int n: TIdPairSeq @@ -784,7 +799,7 @@ proc idTablePut(t: var TIdTable, key: PIdObj, val: PObject) = idTableRawInsert(t.data, key, val) inc(t.counter) -iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: PObject] = +iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] = for i in 0 .. high(t.data): if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val) diff --git a/compiler/bitsets.nim b/compiler/bitsets.nim index 740bdd5ef..a2324f4e2 100644 --- a/compiler/bitsets.nim +++ b/compiler/bitsets.nim @@ -1,14 +1,14 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -# this unit handles Nimrod sets; it implements bit sets -# the code here should be reused in the Nimrod standard library +# this unit handles Nim sets; it implements bit sets +# the code here should be reused in the Nim standard library type TBitSet* = seq[int8] # we use byte here to avoid issues with diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim index 3bc4eb029..14448f0e8 100644 --- a/compiler/canonicalizer.nim +++ b/compiler/canonicalizer.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -370,7 +370,7 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) = # the last entry of a symbol: if s.ast != nil: # we used to attempt to save space here by only storing a dummy AST if - # it is not necessary, but Nimrod's heavy compile-time evaluation features + # it is not necessary, but Nim's heavy compile-time evaluation features # make that unfeasible nowadays: encodeNode(w, s.info, s.ast, result) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index adcc95e84..b01a81c5e 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -103,9 +103,9 @@ proc openArrayLoc(p: BProc, n: PNode): PRope = result = ropef("$1, $1Len0", [rdLoc(a)]) of tyString, tySequence: if skipTypes(n.typ, abstractInst).kind == tyVar: - result = ropef("(*$1)->data, (*$1)->$2", [a.rdLoc, lenField()]) + result = ropef("(*$1)->data, (*$1)->$2", [a.rdLoc, lenField(p)]) else: - result = ropef("$1->data, $1->$2", [a.rdLoc, lenField()]) + result = ropef("$1->data, $1->$2", [a.rdLoc, lenField(p)]) of tyArray, tyArrayConstr: result = ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))]) else: internalError("openArrayLoc: " & typeToString(a.t)) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index b5817de05..4a3be0027 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -11,11 +11,19 @@ # -------------------------- constant expressions ------------------------ +proc int64Literal(i: BiggestInt): PRope = + if i > low(int64): + result = rfmt(nil, "IL64($1)", toRope(i)) + else: + result = ~"(IL64(-9223372036854775807) - IL64(1))" + +proc uint64Literal(i: uint64): PRope = toRope($i & "ULL") + proc intLiteral(i: BiggestInt): PRope = - if (i > low(int32)) and (i <= high(int32)): + if i > low(int32) and i <= high(int32): result = toRope(i) elif i == low(int32): - # Nimrod has the same bug for the same reasons :-) + # Nim has the same bug for the same reasons :-) result = ~"(-2147483647 -1)" elif i > low(int64): result = rfmt(nil, "IL64($1)", toRope(i)) @@ -31,7 +39,7 @@ proc int32Literal(i: int): PRope = proc genHexLiteral(v: PNode): PRope = # hex literals are unsigned in C # so we don't generate hex literals any longer. - if not (v.kind in {nkIntLit..nkUInt64Lit}): + if v.kind notin {nkIntLit..nkUInt64Lit}: internalError(v.info, "genHexLiteral") result = intLiteral(v.intVal) @@ -46,16 +54,18 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): PRope = case n.kind of nkCharLit..nkUInt64Lit: case skipTypes(ty, abstractVarRange).kind - of tyChar, tyInt64, tyNil: + of tyChar, tyNil: result = intLiteral(n.intVal) of tyInt: - if (n.intVal >= low(int32)) and (n.intVal <= high(int32)): + if n.intVal >= low(int32) and n.intVal <= high(int32): result = int32Literal(int32(n.intVal)) else: result = intLiteral(n.intVal) of tyBool: if n.intVal != 0: result = ~"NIM_TRUE" else: result = ~"NIM_FALSE" + of tyInt64: result = int64Literal(n.intVal) + of tyUInt64: result = uint64Literal(uint64(n.intVal)) else: result = ropef("(($1) $2)", [getTypeDesc(p.module, skipTypes(ty, abstractVarRange)), intLiteral(n.intVal)]) @@ -214,9 +224,9 @@ proc optAsgnLoc(a: TLoc, t: PType, field: PRope): TLoc = proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = let newflags = if src.k == locData: - flags + { needToCopy } + flags + {needToCopy} elif tfShallow in dest.t.flags: - flags - { needToCopy } + flags - {needToCopy} else: flags for i in 0 .. Losing auto vectorization then? + #if e[0].kind != nkBracketExpr: + # message(e.info, warnUser, "CAME HERE " & renderTree(e)) expr(p, e.sons[0], d) else: + var a: TLoc initLocExpr(p, e.sons[0], a) case skipTypes(a.t, abstractInst).kind of tyRef: @@ -663,7 +676,15 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) = of tyPtr: d.s = OnUnknown # BUGFIX! else: internalError(e.info, "genDeref " & $a.t.kind) - putIntoDest(p, d, a.t.sons[0], ropef("(*$1)", [rdLoc(a)])) + if enforceDeref and mt == ctPtrToArray: + # we lie about the type for better C interop: 'ptr array[3,T]' is + # translated to 'ptr T', but for deref'ing this produces wrong code. + # See tmissingderef. So we get rid of the deref instead. The codegen + # ends up using 'memcpy' for the array assignment, + # so the '&' and '*' cancel out: + putIntoDest(p, d, a.t.sons[0], rdLoc(a)) + else: + putIntoDest(p, d, a.t.sons[0], ropef("(*$1)", [rdLoc(a)])) proc genAddr(p: BProc, e: PNode, d: var TLoc) = # careful 'addr(myptrToArray)' needs to get the ampersand: @@ -723,7 +744,7 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = internalError(e.info, "genRecordField") field = lookupInRecord(ty.n, f.name) if field != nil: break - if gCmd != cmdCompileToCpp: app(r, ".Sup") + if not p.module.compileToCpp: app(r, ".Sup") ty = getUniqueType(ty.sons[0]) if field == nil: internalError(e.info, "genRecordField 2 ") if field.loc.r == nil: internalError(e.info, "genRecordField 3") @@ -775,7 +796,7 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = assert(ty.kind in {tyTuple, tyObject}) field = lookupInRecord(ty.n, f.name) if field != nil: break - if gCmd != cmdCompileToCpp: app(r, ".Sup") + if not p.module.compileToCpp: app(r, ".Sup") ty = getUniqueType(ty.sons[0]) if field == nil: internalError(e.info, "genCheckedRecordField") if field.loc.r == nil: @@ -786,15 +807,15 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = else: genRecordField(p, e.sons[0], d) -proc genArrayElem(p: BProc, e: PNode, d: var TLoc) = +proc genArrayElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc - initLocExpr(p, e.sons[0], a) - initLocExpr(p, e.sons[1], b) + initLocExpr(p, x, a) + initLocExpr(p, y, b) var ty = skipTypes(skipTypes(a.t, abstractVarRange), abstractPtrs) var first = intLiteral(firstOrd(ty)) # emit range check: if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags: - if not isConstExpr(e.sons[1]): + if not isConstExpr(y): # semantic pass has already checked for const index expressions if firstOrd(ty) == 0: if (firstOrd(b.t) < firstOrd(ty)) or (lastOrd(b.t) > lastOrd(ty)): @@ -804,26 +825,26 @@ proc genArrayElem(p: BProc, e: PNode, d: var TLoc) = linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseIndexError();$n", rdCharLoc(b), first, intLiteral(lastOrd(ty))) else: - let idx = getOrdValue(e.sons[1]) + let idx = getOrdValue(y) if idx < firstOrd(ty) or idx > lastOrd(ty): - localError(e.info, errIndexOutOfBounds) + localError(x.info, errIndexOutOfBounds) d.inheritLocation(a) putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first)) -proc genCStringElem(p: BProc, e: PNode, d: var TLoc) = +proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc - initLocExpr(p, e.sons[0], a) - initLocExpr(p, e.sons[1], b) + initLocExpr(p, x, a) + initLocExpr(p, y, b) var ty = skipTypes(a.t, abstractVarRange) if d.k == locNone: d.s = a.s putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b))) -proc genOpenArrayElem(p: BProc, e: PNode, d: var TLoc) = +proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc - initLocExpr(p, e.sons[0], a) - initLocExpr(p, e.sons[1], b) # emit range check: + initLocExpr(p, x, a) + initLocExpr(p, y, b) # emit range check: if optBoundsCheck in p.options: linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len0)) #raiseIndexError();$n", rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``! @@ -831,10 +852,10 @@ proc genOpenArrayElem(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b))) -proc genSeqElem(p: BProc, e: PNode, d: var TLoc) = +proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc - initLocExpr(p, e.sons[0], a) - initLocExpr(p, e.sons[1], b) + initLocExpr(p, x, a) + initLocExpr(p, y, b) var ty = skipTypes(a.t, abstractVarRange) if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check: @@ -842,11 +863,11 @@ proc genSeqElem(p: BProc, e: PNode, d: var TLoc) = if ty.kind == tyString: linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2->$3)) #raiseIndexError();$n", - rdLoc(b), rdLoc(a), lenField()) + rdLoc(b), rdLoc(a), lenField(p)) else: linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", - rdLoc(b), rdLoc(a), lenField()) + rdLoc(b), rdLoc(a), lenField(p)) if d.k == locNone: d.s = OnHeap d.heapRoot = a.r if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: @@ -854,6 +875,17 @@ proc genSeqElem(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b))) +proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = + var ty = skipTypes(n.sons[0].typ, abstractVarRange) + if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) + case ty.kind + of tyArray, tyArrayConstr: genArrayElem(p, n.sons[0], n.sons[1], d) + of tyOpenArray, tyVarargs: genOpenArrayElem(p, n.sons[0], n.sons[1], d) + of tySequence, tyString: genSeqElem(p, n.sons[0], n.sons[1], d) + of tyCString: genCStringElem(p, n.sons[0], n.sons[1], d) + of tyTuple: genTupleElem(p, n, d) + else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') + proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = # how to generate code? # 'expr1 and expr2' becomes: @@ -895,20 +927,21 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = proc genEcho(p: BProc, n: PNode) = # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)`` # is threadsafe. + internalAssert n.kind == nkBracket discard lists.includeStr(p.module.headerFiles, "") var args: PRope = nil var a: TLoc - for i in countup(1, n.len-1): + for i in countup(0, n.len-1): initLocExpr(p, n.sons[i], a) appf(args, ", ($1)->data", [rdLoc(a)]) linefmt(p, cpsStmts, "printf($1$2);$n", - makeCString(repeatStr(n.len-1, "%s") & tnl), args) + makeCString(repeatStr(n.len, "%s") & tnl), args) proc gcUsage(n: PNode) = if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree) proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = - # + # # s = 'Hello ' & name & ', how do you feel?' & 'z' # # @@ -939,7 +972,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = if e.sons[i + 1].kind in {nkStrLit..nkTripleStrLit}: inc(L, len(e.sons[i + 1].strVal)) else: - appf(lens, "$1->$2 + ", [rdLoc(a), lenField()]) + appf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)]) app(appends, rfmt(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a))) linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, toRope(L)) app(p.s(cpsStmts), appends) @@ -951,7 +984,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = gcUsage(e) proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = - # + # # s &= 'Hello ' & name & ', how do you feel?' & 'z' # // BUG: what if s is on the left side too? # @@ -979,7 +1012,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = if e.sons[i + 2].kind in {nkStrLit..nkTripleStrLit}: inc(L, len(e.sons[i + 2].strVal)) else: - appf(lens, "$1->$2 + ", [rdLoc(a), lenField()]) + appf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)]) app(appends, rfmt(p.module, "#appendString($1, $2);$n", rdLoc(dest), rdLoc(a))) linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n", @@ -992,7 +1025,7 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = # seq &= x --> # seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x)); # seq->data[seq->len-1] = x; - let seqAppendPattern = if gCmd != cmdCompileToCpp: + let seqAppendPattern = if not p.module.compileToCpp: "$1 = ($2) #incrSeq(&($1)->Sup, sizeof($3));$n" else: "$1 = ($2) #incrSeq($1, sizeof($3));$n" @@ -1005,7 +1038,7 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = getTypeDesc(p.module, skipTypes(e.sons[2].typ, abstractVar))]) keepAlive(p, a) initLoc(dest, locExpr, b.t, OnHeap) - dest.r = rfmt(nil, "$1->data[$1->$2-1]", rdLoc(a), lenField()) + dest.r = rfmt(nil, "$1->data[$1->$2-1]", rdLoc(a), lenField(p)) genAssignment(p, dest, b, {needToCopy, afDestIsNil}) gcUsage(e) @@ -1099,11 +1132,11 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = while ty != nil: field = lookupInRecord(ty.n, it.sons[0].sym.name) if field != nil: break - if gCmd != cmdCompileToCpp: app(tmp2.r, ".Sup") + if not p.module.compileToCpp: app(tmp2.r, ".Sup") ty = getUniqueType(ty.sons[0]) if field == nil or field.loc.r == nil: internalError(e.info, "genObjConstr") if it.len == 3 and optFieldCheck in p.options: - genFieldCheck(p, it.sons[2], r, field) + genFieldCheck(p, it.sons[2], tmp2.r, field) app(tmp2.r, ".") app(tmp2.r, field.loc.r) tmp2.k = locTemp @@ -1200,8 +1233,8 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = if t.kind != tyVar: nilCheck = r r = rfmt(nil, "(*$1)", r) t = skipTypes(t.lastSon, typedescInst) - if gCmd != cmdCompileToCpp: - while (t.kind == tyObject) and (t.sons[0] != nil): + if not p.module.compileToCpp: + while t.kind == tyObject and t.sons[0] != nil: app(r, ~".Sup") t = skipTypes(t.sons[0], typedescInst) if isObjLackingTypeField(t): @@ -1246,7 +1279,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, b, e.typ, ropef("$1, $1Len0", [rdLoc(a)])) of tyString, tySequence: putIntoDest(p, b, e.typ, - ropef("$1->data, $1->$2", [rdLoc(a), lenField()])) + ropef("$1->data, $1->$2", [rdLoc(a), lenField(p)])) of tyArray, tyArrayConstr: putIntoDest(p, b, e.typ, ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))])) @@ -1289,7 +1322,7 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = if op == mHigh: unaryExpr(p, e, d, "(strlen($1)-1)") else: unaryExpr(p, e, d, "strlen($1)") of tyString, tySequence: - if gCmd != cmdCompileToCpp: + if not p.module.compileToCpp: if op == mHigh: unaryExpr(p, e, d, "($1->Sup.len-1)") else: unaryExpr(p, e, d, "$1->Sup.len") else: @@ -1307,7 +1340,7 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) var t = skipTypes(e.sons[1].typ, abstractVar) - let setLenPattern = if gCmd != cmdCompileToCpp: + let setLenPattern = if not p.module.compileToCpp: "$1 = ($3) #setLengthSeq(&($1)->Sup, sizeof($4), $2);$n" else: "$1 = ($3) #setLengthSeq($1, sizeof($4), $2);$n" @@ -1508,7 +1541,6 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n", getTypeDesc(p.module, srct), getTypeDesc(p.module, destt), lbl) tmp.k = locExpr - tmp.a = -1 tmp.t = srct tmp.s = OnStack tmp.flags = {} @@ -1564,11 +1596,11 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = elif (a.kind in {nkStrLit..nkTripleStrLit}) and (a.strVal == ""): initLocExpr(p, e.sons[2], x) putIntoDest(p, d, e.typ, - rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField())) + rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p))) elif (b.kind in {nkStrLit..nkTripleStrLit}) and (b.strVal == ""): initLocExpr(p, e.sons[1], x) putIntoDest(p, d, e.typ, - rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField())) + rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p))) else: binaryExpr(p, e, d, "#eqStrings($1, $2)") @@ -1673,7 +1705,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = discard cgsym(p.module, opr.loc.r.ropeToStr) genCall(p, e, d) of mReset: genReset(p, e) - of mEcho: genEcho(p, e) + of mEcho: genEcho(p, e[1].skipConv) of mArrToSeq: genArrToSeq(p, e, d) of mNLen..mNError: localError(e.info, errCannotGenerateCodeForX, e.sons[0].sym.name.s) @@ -1695,7 +1727,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = proc genConstExpr(p: BProc, n: PNode): PRope proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = - if (nfAllConst in n.flags) and (d.k == locNone) and (sonsLen(n) > 0): + if nfAllConst in n.flags and d.k == locNone and n.len > 0 and n.isDeepConstExpr: var t = getUniqueType(n.typ) discard getTypeDesc(p.module, t) # so that any fields are initialized var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId) @@ -1827,7 +1859,7 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = if t.kind != tyVar: nilCheck = r r = ropef("(*$1)", [r]) t = skipTypes(t.lastSon, abstractInst) - if gCmd != cmdCompileToCpp: + if not p.module.compileToCpp: while t.kind == tyObject and t.sons[0] != nil: app(r, ".Sup") t = skipTypes(t.sons[0], abstractInst) @@ -1845,22 +1877,38 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = [getTypeDesc(p.module, dest), addrLoc(a)])) proc downConv(p: BProc, n: PNode, d: var TLoc) = - if gCmd == cmdCompileToCpp: + if p.module.compileToCpp: expr(p, n.sons[0], d) # downcast does C++ for us else: var dest = skipTypes(n.typ, abstractPtrs) - var src = skipTypes(n.sons[0].typ, abstractPtrs) + + var arg = n.sons[0] + while arg.kind == nkObjDownConv: arg = arg.sons[0] + + var src = skipTypes(arg.typ, abstractPtrs) var a: TLoc - initLocExpr(p, n.sons[0], a) + initLocExpr(p, arg, a) var r = rdLoc(a) - if skipTypes(n.sons[0].typ, abstractInst).kind in {tyRef, tyPtr, tyVar} and - n.sons[0].kind notin {nkHiddenAddr, nkAddr, nkObjDownConv}: + let isRef = skipTypes(arg.typ, abstractInst).kind in {tyRef, tyPtr, tyVar} + if isRef: app(r, "->Sup") - for i in countup(2, abs(inheritanceDiff(dest, src))): app(r, ".Sup") - r = con("&", r) else: - for i in countup(1, abs(inheritanceDiff(dest, src))): app(r, ".Sup") - putIntoDest(p, d, n.typ, r) + app(r, ".Sup") + for i in countup(2, abs(inheritanceDiff(dest, src))): app(r, ".Sup") + if isRef: + # it can happen that we end up generating '&&x->Sup' here, so we pack + # the '&x->Sup' into a temporary and then those address is taken + # (see bug #837). However sometimes using a temporary is not correct: + # init(TFigure(my)) # where it is passed to a 'var TFigure'. We test + # this by ensuring the destination is also a pointer: + if d.k == locNone and skipTypes(n.typ, abstractInst).kind in {tyRef, tyPtr, tyVar}: + getTemp(p, n.typ, d) + linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r) + else: + r = con("&", r) + putIntoDest(p, d, n.typ, r) + else: + putIntoDest(p, d, n.typ, r) proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = var t = getUniqueType(n.typ) @@ -1978,16 +2026,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkCast: genCast(p, n, d) of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, d) of nkHiddenAddr, nkAddr: genAddr(p, n, d) - of nkBracketExpr: - var ty = skipTypes(n.sons[0].typ, abstractVarRange) - if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) - case ty.kind - of tyArray, tyArrayConstr: genArrayElem(p, n, d) - of tyOpenArray, tyVarargs: genOpenArrayElem(p, n, d) - of tySequence, tyString: genSeqElem(p, n, d) - of tyCString: genCStringElem(p, n, d) - of tyTuple: genTupleElem(p, n, d) - else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') + of nkBracketExpr: genBracketExpr(p, n, d) of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d) of nkDotExpr: genRecordField(p, n, d) of nkCheckedFieldExpr: genCheckedRecordField(p, n, d) @@ -2031,7 +2070,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = initLocExpr(p, n.sons[0], a) of nkAsmStmt: genAsmStmt(p, n) of nkTryStmt: - if gCmd == cmdCompileToCpp: genTryCpp(p, n, d) + if p.module.compileToCpp: genTryCpp(p, n, d) else: genTry(p, n, d) of nkRaiseStmt: genRaiseStmt(p, n) of nkTypeSection: @@ -2043,6 +2082,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = nkFromStmt, nkTemplateDef, nkMacroDef: discard of nkPragma: genPragma(p, n) + of nkPragmaBlock: expr(p, n.lastSon, d) of nkProcDef, nkMethodDef, nkConverterDef: if (n.sons[genericParamsPos].kind == nkEmpty): var prc = n.sons[namePos].sym diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim index 2d27257ce..36da68d23 100644 --- a/compiler/ccgmerge.nim +++ b/compiler/ccgmerge.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -83,7 +83,7 @@ proc writeTypeCache(a: TIdTable, s: var string) = inc i s.add('}') -proc writeIntSet(a: TIntSet, s: var string) = +proc writeIntSet(a: IntSet, s: var string) = var i = 0 for x in items(a): if i == 10: @@ -200,7 +200,7 @@ proc readTypeCache(L: var TBaseLexer, result: var TIdTable) = idTablePut(result, newFakeType(key), value.toRope) inc L.bufpos -proc readIntSet(L: var TBaseLexer, result: var TIntSet) = +proc readIntSet(L: var TBaseLexer, result: var IntSet) = if ^L.bufpos != '{': internalError("ccgmerge: '{' expected") inc L.bufpos while ^L.bufpos != '}': @@ -249,7 +249,7 @@ proc readMergeInfo*(cfilename: string, m: BModule) = break type - TMergeSections = object {.pure.} + TMergeSections = object f: TCFileSections p: TCProcSections diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 037594e89..6c7d40d1c 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -46,7 +46,6 @@ proc genVarTuple(p: BProc, n: PNode) = if useLowering: genStmts(p, lowerTupleUnpacking(n, p.prc)) return - genLineDir(p, n) initLocExpr(p, n.sons[L-1], tup) var t = tup.t @@ -69,10 +68,21 @@ proc genVarTuple(p: BProc, n: PNode) = [rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)]) putLocIntoDest(p, v.loc, field) +proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) + proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} = if ri.kind in nkCallKinds and (ri.sons[0].kind != nkSym or ri.sons[0].sym.magic == mNone): genAsgnCall(p, le, ri, a) + elif ri.kind in {nkDerefExpr, nkHiddenDeref}: + # this is a hacky way to fix #1181 (tmissingderef):: + # + # var arr1 = cast[ptr array[4, int8]](addr foo)[] + # + # However, fixing this properly really requires modelling 'array' as + # a 'struct' in C to preserve dereferencing semantics completely. Not + # worth the effort until version 1.0 is out. + genDeref(p, ri, a, enforceDeref=true) else: expr(p, ri, a) @@ -294,8 +304,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) = var alreadyPoppedCnt = p.inExceptBlock for i in countup(1, howManyTrys): - - if gCmd != cmdCompileToCpp: + if not p.module.compileToCpp: # Pop safe points generated by try if alreadyPoppedCnt > 0: dec alreadyPoppedCnt @@ -317,7 +326,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) = for i in countdown(howManyTrys-1, 0): p.nestedTryStmts.add(stack[i]) - if gCmd != cmdCompileToCpp: + if not p.module.compileToCpp: # Pop exceptions that was handled by the # except-blocks we are in for i in countdown(howManyExcepts-1, 0): @@ -448,7 +457,7 @@ proc genBlock(p: BProc, t: PNode, d: var TLoc) = assert(t.sons[0].kind == nkSym) var sym = t.sons[0].sym sym.loc.k = locOther - sym.loc.a = p.breakIdx + sym.position = p.breakIdx+1 expr(p, t.sons[1], d) endBlock(p) @@ -487,7 +496,7 @@ proc genBreakStmt(p: BProc, t: PNode) = assert(t.sons[0].kind == nkSym) var sym = t.sons[0].sym assert(sym.loc.k == locOther) - idx = sym.loc.a + idx = sym.position-1 else: # an unnamed 'break' can only break a loop after 'transf' pass: while idx >= 0 and not p.blocks[idx].isLoop: dec idx @@ -501,8 +510,10 @@ proc genBreakStmt(p: BProc, t: PNode) = lineF(p, cpsStmts, "goto $1;$n", [label]) proc getRaiseFrmt(p: BProc): string = - if gCmd == cmdCompileToCpp: + if p.module.compileToCpp: result = "throw NimException($1, $2);$n" + elif getCompilerProc("Exception") != nil: + result = "#raiseException((#Exception*)$1, $2);$n" else: result = "#raiseException((#E_Base*)$1, $2);$n" @@ -520,10 +531,10 @@ proc genRaiseStmt(p: BProc, t: PNode) = var typ = skipTypes(t.sons[0].typ, abstractPtrs) genLineDir(p, t) lineCg(p, cpsStmts, getRaiseFrmt(p), [e, makeCString(typ.sym.name.s)]) - else: + else: genLineDir(p, t) # reraise the last exception: - if gCmd == cmdCompileToCpp: + if p.module.compileToCpp: line(p, cpsStmts, ~"throw;$n") else: linefmt(p, cpsStmts, "#reraiseException();$n") @@ -559,7 +570,7 @@ proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc, proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc, rangeFormat, eqFormat: TFormatStr, until: int, a: TLoc): TLabel = - # generate a C-if statement for a Nimrod case statement + # generate a C-if statement for a Nim case statement var labId = p.labels for i in 1..until: inc(p.labels) @@ -747,7 +758,10 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = i, length, blen: int genLineDir(p, t) exc = getTempName() - discard cgsym(p.module, "E_Base") + if getCompilerProc("Exception") != nil: + discard cgsym(p.module, "Exception") + else: + discard cgsym(p.module, "E_Base") add(p.nestedTryStmts, t) startBlock(p, "try {$n") expr(p, t.sons[0], d) @@ -784,7 +798,9 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = startBlock(p) var finallyBlock = t.lastSon if finallyBlock.kind == nkFinally: - expr(p, finallyBlock.sons[0], d) + #expr(p, finallyBlock.sons[0], d) + genStmts(p, finallyBlock.sons[0]) + line(p, cpsStmts, ~"throw;$n") endBlock(p) @@ -793,7 +809,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = discard pop(p.nestedTryStmts) if (i < length) and (t.sons[i].kind == nkFinally): - exprBlock(p, t.sons[i].sons[0], d) + genSimpleBlock(p, t.sons[i].sons[0]) proc genTry(p: BProc, t: PNode, d: var TLoc) = # code to generate: @@ -829,7 +845,10 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = discard lists.includeStr(p.module.headerFiles, "") genLineDir(p, t) var safePoint = getTempName() - discard cgsym(p.module, "E_Base") + if getCompilerProc("Exception") != nil: + discard cgsym(p.module, "Exception") + else: + discard cgsym(p.module, "E_Base") linefmt(p, cpsLocals, "#TSafePoint $1;$n", safePoint) linefmt(p, cpsStmts, "#pushSafePoint(&$1);$n", safePoint) if isDefined("nimStdSetjmp"): @@ -882,7 +901,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = endBlock(p) # end of else block if i < length and t.sons[i].kind == nkFinally: p.finallySafePoints.add(safePoint) - exprBlock(p, t.sons[i].sons[0], d) + genSimpleBlock(p, t.sons[i].sons[0]) discard pop(p.finallySafePoints) linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint) diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim index c00b931ef..c24dd5c41 100644 --- a/compiler/ccgthreadvars.nim +++ b/compiler/ccgthreadvars.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -57,6 +57,9 @@ proc generateThreadLocalStorage(m: BModule) = proc generateThreadVarsSize(m: BModule) = if nimtv != nil: - app(m.s[cfsProcs], - "NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}" & tnl) - + let externc = if gCmd != cmdCompileToCpp and + sfCompileToCpp in m.module.flags: "extern \"C\"" + else: "" + appf(m.s[cfsProcs], + "$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n", + [externc.toRope]) diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index a5bf9e7a7..8bb820283 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -49,8 +49,8 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, n: PNode) = genTraverseProc(c, ropef("$1.$2", accessor, field.loc.r), field.loc.t) else: internalError(n.info, "genTraverseProc()") -proc parentObj(accessor: PRope): PRope {.inline.} = - if gCmd != cmdCompileToCpp: +proc parentObj(accessor: PRope; m: BModule): PRope {.inline.} = + if not m.compileToCpp: result = ropef("$1.Sup", accessor) else: result = accessor @@ -71,7 +71,7 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) = lineF(p, cpsStmts, "}$n") of tyObject: for i in countup(0, sonsLen(typ) - 1): - genTraverseProc(c, accessor.parentObj, typ.sons[i]) + genTraverseProc(c, accessor.parentObj(c.p.module), typ.sons[i]) if typ.n != nil: genTraverseProc(c, accessor, typ.n) of tyTuple: let typ = getUniqueType(typ) @@ -91,7 +91,7 @@ proc genTraverseProcSeq(c: var TTraversalClosure, accessor: PRope, typ: PType) = var i: TLoc getTemp(p, getSysType(tyInt), i) lineF(p, cpsStmts, "for ($1 = 0; $1 < $2->$3; $1++) {$n", - i.r, accessor, toRope(if gCmd != cmdCompileToCpp: "Sup.len" else: "len")) + i.r, accessor, toRope(if c.p.module.compileToCpp: "len" else: "Sup.len")) genTraverseProc(c, ropef("$1->data[$2]", accessor, i.r), typ.sons[0]) lineF(p, cpsStmts, "}$n") diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index fc6febc6f..460cb9297 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -162,10 +162,14 @@ proc mapReturnType(typ: PType): TCTypeKind = if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr else: result = mapType(typ) -proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope +proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope proc needsComplexAssignment(typ: PType): bool = result = containsGarbageCollectedRef(typ) +proc isObjLackingTypeField(typ: PType): bool {.inline.} = + result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and + (typ.sons[0] == nil) or isPureObject(typ)) + proc isInvalidReturnType(rettype: PType): bool = # Arrays and sets cannot be returned by a C procedure, because C is # such a poor programming language. @@ -177,10 +181,12 @@ proc isInvalidReturnType(rettype: PType): bool = of ctArray: result = not (skipTypes(rettype, typedescInst).kind in {tyVar, tyRef, tyPtr}) - of ctStruct: - result = needsComplexAssignment(skipTypes(rettype, typedescInst)) + of ctStruct: + let t = skipTypes(rettype, typedescInst) + result = needsComplexAssignment(t) or + (t.kind == tyObject and not isObjLackingTypeField(t)) else: result = false - + const CallingConvToStr: array[TCallingConvention, string] = ["N_NIMCALL", "N_STDCALL", "N_CDECL", "N_SAFECALL", @@ -228,7 +234,7 @@ proc fillResult(param: PSym) = incl(param.loc.flags, lfIndirect) param.loc.s = OnUnknown -proc getParamTypeDesc(m: BModule, t: PType, check: var TIntSet): PRope = +proc getParamTypeDesc(m: BModule, t: PType, check: var IntSet): PRope = when false: if t.Kind in {tyRef, tyPtr, tyVar}: var b = skipTypes(t.lastson, typedescInst) @@ -243,7 +249,7 @@ proc paramStorageLoc(param: PSym): TStorageLoc = result = OnUnknown proc genProcParams(m: BModule, t: PType, rettype, params: var PRope, - check: var TIntSet, declareEnvironment=true) = + check: var IntSet, declareEnvironment=true) = params = nil if (t.sons[0] == nil) or isInvalidReturnType(t.sons[0]): rettype = ~"void" @@ -330,7 +336,7 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): PRope = of tyNil: result = typeNameOrLiteral(typ, "0") of tyInt..tyUInt64: result = typeNameOrLiteral(typ, NumericalTypeToStr[typ.kind]) - of tyRange: result = getSimpleTypeDesc(m, typ.sons[0]) + of tyDistinct, tyRange: result = getSimpleTypeDesc(m, typ.sons[0]) else: result = nil proc getTypePre(m: BModule, typ: PType): PRope = @@ -342,8 +348,8 @@ proc getTypePre(m: BModule, typ: PType): PRope = proc structOrUnion(t: PType): PRope = (if tfUnion in t.flags: toRope("union") else: toRope("struct")) -proc getForwardStructFormat(): string = - if gCmd == cmdCompileToCpp: result = "$1 $2;$n" +proc getForwardStructFormat(m: BModule): string = + if m.compileToCpp: result = "$1 $2;$n" else: result = "typedef $1 $2 $2;$n" proc getTypeForward(m: BModule, typ: PType): PRope = @@ -355,7 +361,7 @@ proc getTypeForward(m: BModule, typ: PType): PRope = of tySequence, tyTuple, tyObject: result = getTypeName(typ) if not isImportedType(typ): - appf(m.s[cfsForwardTypes], getForwardStructFormat(), + appf(m.s[cfsForwardTypes], getForwardStructFormat(m), [structOrUnion(typ), result]) idTablePut(m.forwTypeCache, typ, result) else: internalError("getTypeForward(" & $typ.kind & ')') @@ -370,7 +376,7 @@ proc mangleRecFieldName(field: PSym, rectype: PType): PRope = proc genRecordFieldsAux(m: BModule, n: PNode, accessExpr: PRope, rectype: PType, - check: var TIntSet): PRope = + check: var IntSet): PRope = var ae, uname, sname, a: PRope k: PNode @@ -419,19 +425,19 @@ proc genRecordFieldsAux(m: BModule, n: PNode, appf(result, "$1 $2;$n", [getTypeDescAux(m, fieldType, check), sname]) else: internalError(n.info, "genRecordFieldsAux()") -proc getRecordFields(m: BModule, typ: PType, check: var TIntSet): PRope = +proc getRecordFields(m: BModule, typ: PType, check: var IntSet): PRope = result = genRecordFieldsAux(m, typ.n, nil, typ, check) proc getRecordDesc(m: BModule, typ: PType, name: PRope, - check: var TIntSet): PRope = + check: var IntSet): PRope = # declare the record: var hasField = false var attribute: PRope = - if tfPacked in typ.flags: toRope(CC[ccompiler].packedPragma) + if tfPacked in typ.flags: toRope(CC[cCompiler].packedPragma) else: nil - result = ropecg(m, CC[ccompiler].structStmtFmt, + result = ropecg(m, CC[cCompiler].structStmtFmt, [structOrUnion(typ), name, attribute]) if typ.kind == tyObject: @@ -442,7 +448,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope, else: appcg(m, result, " {$n#TNimType* m_type;$n", [name, attribute]) hasField = true - elif gCmd == cmdCompileToCpp: + elif m.compileToCpp: appcg(m, result, " : public $1 {$n", [getTypeDescAux(m, typ.sons[0], check)]) hasField = true @@ -461,7 +467,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope, app(result, "};" & tnl) proc getTupleDesc(m: BModule, typ: PType, name: PRope, - check: var TIntSet): PRope = + check: var IntSet): PRope = result = ropef("$1 $2 {$n", [structOrUnion(typ), name]) var desc: PRope = nil for i in countup(0, sonsLen(typ) - 1): @@ -474,7 +480,7 @@ proc getTupleDesc(m: BModule, typ: PType, name: PRope, proc pushType(m: BModule, typ: PType) = add(m.typeStack, typ) -proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope = +proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): PRope = # returns only the type's name var name, rettype, desc, recdesc: PRope @@ -538,7 +544,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope = if result == nil: result = getTypeName(t) if not isImportedType(t): - appf(m.s[cfsForwardTypes], getForwardStructFormat(), + appf(m.s[cfsForwardTypes], getForwardStructFormat(m), [structOrUnion(t), result]) idTablePut(m.forwTypeCache, t, result) assert(cacheGetType(m.typeCache, t) == nil) @@ -550,7 +556,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope = cSeq = "struct $2 {$n" & " #TGenericSeq Sup;$n" appcg(m, m.s[cfsSeqTypes], - (if gCmd == cmdCompileToCpp: cppSeq else: cSeq) & + (if m.compileToCpp: cppSeq else: cSeq) & " $1 data[SEQ_DECL_SIZE];$n" & "};$n", [getTypeDescAux(m, t.sons[0], check), result]) else: @@ -570,7 +576,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope = if result == nil: result = getTypeName(t) if not isImportedType(t): - appf(m.s[cfsForwardTypes], getForwardStructFormat(), + appf(m.s[cfsForwardTypes], getForwardStructFormat(m), [structOrUnion(t), result]) idTablePut(m.forwTypeCache, t, result) idTablePut(m.typeCache, t, result) # always call for sideeffects: @@ -678,10 +684,6 @@ when false: var tmp = getNimType(m) appf(m.s[cfsTypeInit2], "$2 = &$1;$n", [tmp, name]) -proc isObjLackingTypeField(typ: PType): bool {.inline.} = - result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and - (typ.sons[0] == nil) or isPureObject(typ)) - proc genTypeInfoAuxBase(m: BModule, typ: PType, name, base: PRope) = var nimtypeKind: int #allocMemTI(m, typ, name) @@ -898,7 +900,7 @@ include ccgtrav proc genDeepCopyProc(m: BModule; s: PSym; result: PRope) = genProc(m, s) - appf(m.s[cfsTypeInit3], "$1.deepcopy = (N_NIMCALL_PTR(void*, void*)) $2;$n", + appf(m.s[cfsTypeInit3], "$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n", [result, s.loc.r]) proc genTypeInfo(m: BModule, t: PType): PRope = diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index 65957584a..7396c0bf8 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -149,7 +149,7 @@ proc getUniqueType*(key: PType): PType = idTablePut(gTypeTable[k], key, key) result = key -proc tableGetType*(tab: TIdTable, key: PType): PObject = +proc tableGetType*(tab: TIdTable, key: PType): RootRef = # returns nil if we need to declare this type result = idTableGet(tab, key) if (result == nil) and (tab.counter > 0): @@ -171,7 +171,7 @@ proc mangle*(name: string): string = ## Lowercases the given name and manges any non-alphanumeric characters ## so they are represented as `HEX____`. If the name starts with a number, ## `N` is prepended - result = "" + result = newStringOfCap(name.len) case name[0] of Letters: result.add(name[0].toLower) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 359fa3309..c645b81aa 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -55,7 +55,6 @@ proc initLoc(result: var TLoc, k: TLocKind, typ: PType, s: TStorageLoc) = result.s = s result.t = getUniqueType(typ) result.r = nil - result.a = - 1 result.flags = {} proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: PRope, s: TStorageLoc) = @@ -63,7 +62,6 @@ proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: PRope, s: TStorageLoc) = if a.k == locNone: a.k = k a.t = getUniqueType(typ) - a.a = - 1 a.s = s if a.r == nil: a.r = r @@ -139,7 +137,7 @@ proc ropecg(m: BModule, frmt: TFormatStr, args: varargs[PRope]): PRope = if i - 1 >= start: app(result, substr(frmt, start, i - 1)) -const compileTimeRopeFmt = not defined(booting) +const compileTimeRopeFmt = false when compileTimeRopeFmt: import macros @@ -186,7 +184,7 @@ when compileTimeRopeFmt: while s[i] in IdentChars: inc i yield (kind: ffSym, value: substr(s, j, i-1), intValue: 0) start = i - else: nil + else: discard while i < length: if s[i] != '$' and s[i] != '#': inc i @@ -201,7 +199,7 @@ when compileTimeRopeFmt: ## the compilation of nimrod itself or will the macro execution time ## offset the gains? result = newCall(bindSym"ropeConcat") - for frag in fmtStringFragments(fmt.strVal): + for frag in fmtStringFragments(fmt): case frag.kind of ffSym: result.add(newCall(bindSym"cgsym", m, newStrLitNode(frag.value))) @@ -286,7 +284,7 @@ proc genLineDir(p: BProc, t: PNode) = line.toRope, makeCString(toFilename(t.info))) elif ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and - (p.prc == nil or sfPure notin p.prc.flags): + (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex >= 0: linefmt(p, cpsStmts, "nimln($1, $2);$n", line.toRope, t.info.quotedFilename) @@ -297,6 +295,9 @@ proc accessThreadLocalVar(p: BProc, s: PSym) proc emulatedThreadVars(): bool {.inline.} proc genProc(m: BModule, prc: PSym) +template compileToCpp(m: BModule): expr = + gCmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags + include "ccgtypes.nim" # ------------------------------ Manager of temporaries ------------------ @@ -309,7 +310,7 @@ proc rdLoc(a: TLoc): PRope = proc addrLoc(a: TLoc): PRope = result = a.r if lfIndirect notin a.flags and mapType(a.t) != ctArray: - result = con("&", result) + result = con("(&", result).con(")") proc rdCharLoc(a: TLoc): PRope = # read a location that may need a char-cast: @@ -326,7 +327,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc, var r = rdLoc(a) if not takeAddr: r = ropef("(*$1)", [r]) var s = skipTypes(t, abstractInst) - if gCmd != cmdCompileToCpp: + if not p.module.compileToCpp: while (s.kind == tyObject) and (s.sons[0] != nil): app(r, ".Sup") s = skipTypes(s.sons[0], abstractInst) @@ -407,7 +408,7 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = result.r = con("LOC", toRope(p.labels)) linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r) result.k = locTemp - result.a = - 1 + #result.a = - 1 result.t = getUniqueType(t) result.s = OnStack result.flags = {} @@ -425,7 +426,7 @@ proc keepAlive(p: BProc, toKeepAlive: TLoc) = [getTypeDesc(p.module, toKeepAlive.t), fid]) inc(p.gcFrameId) result.k = locTemp - result.a = -1 + #result.a = -1 result.t = toKeepAlive.t result.s = OnStack result.flags = {} @@ -505,9 +506,7 @@ proc assignLocalVar(p: BProc, s: PSym) = if sfRegister in s.flags: app(decl, " register") #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: # app(decl, " GC_GUARD") - if sfVolatile in s.flags or (p.nestedTryStmts.len > 0 and - gCmd != cmdCompileToCpp): - app(decl, " volatile") + if sfVolatile in s.flags: app(decl, " volatile") appf(decl, " $1;$n", [s.loc.r]) else: decl = ropef(s.cgDeclFrmt & ";$n", decl, s.loc.r) @@ -587,8 +586,8 @@ proc initLocExpr(p: BProc, e: PNode, result: var TLoc) = initLoc(result, locNone, e.typ, OnUnknown) expr(p, e, result) -proc lenField: PRope {.inline.} = - result = toRope(if gCmd != cmdCompileToCpp: "Sup.len" else: "len") +proc lenField(p: BProc): PRope = + result = toRope(if p.module.compileToCpp: "len" else: "Sup.len") include ccgcalls, "ccgstmts.nim", "ccgexprs.nim" @@ -812,7 +811,12 @@ proc genProcAux(m: BModule, prc: PSym) = app(generatedProc, returnStmt) app(generatedProc, ~"}$N") app(m.s[cfsProcs], generatedProc) - + +proc crossesCppBoundary(m: BModule; sym: PSym): bool {.inline.} = + result = sfCompileToCpp in m.module.flags and + sfCompileToCpp notin sym.getModule().flags and + gCmd != cmdCompileToCpp + proc genProcPrototype(m: BModule, sym: PSym) = useHeader(m, sym) if lfNoDecl in sym.loc.flags: return @@ -824,6 +828,8 @@ proc genProcPrototype(m: BModule, sym: PSym) = if gCmd == cmdCompileToLLVM: incl(sym.loc.flags, lfIndirect) elif not containsOrIncl(m.declaredProtos, sym.id): var header = genProcHeader(m, sym) + if sym.typ.callConv != ccInline and crossesCppBoundary(m, sym): + header = con("extern \"C\" ", header) if sfPure in sym.flags and hasNakedAttribute in CC[cCompiler].props: header.app(" __attribute__((naked))") app(m.s[cfsProcHeaders], rfmt(nil, "$1;$n", header)) @@ -915,30 +921,30 @@ proc addIntTypes(result: var PRope) {.inline.} = appf(result, "#define NIM_INTBITS $1", [ platform.CPU[targetCPU].intSize.toRope]) -proc getCopyright(cfilenoext: string): PRope = +proc getCopyright(cfile: string): PRope = if optCompileOnly in gGlobalOptions: - result = ropeff("/* Generated by Nimrod Compiler v$1 */$N" & + result = ropeff("/* Generated by Nim Compiler v$1 */$N" & "/* (c) 2014 Andreas Rumpf */$N" & "/* The generated code is subject to the original license. */$N", - "; Generated by Nimrod Compiler v$1$N" & + "; Generated by Nim Compiler v$1$N" & "; (c) 2012 Andreas Rumpf$N", [toRope(VersionAsString)]) else: - result = ropeff("/* Generated by Nimrod Compiler v$1 */$N" & + result = ropeff("/* Generated by Nim Compiler v$1 */$N" & "/* (c) 2014 Andreas Rumpf */$N" & "/* The generated code is subject to the original license. */$N" & "/* Compiled for: $2, $3, $4 */$N" & "/* Command for C compiler:$n $5 */$N", - "; Generated by Nimrod Compiler v$1$N" & + "; Generated by Nim Compiler v$1$N" & "; (c) 2014 Andreas Rumpf$N" & "; Compiled for: $2, $3, $4$N" & "; Command for LLVM compiler:$N $5$N", [toRope(VersionAsString), toRope(platform.OS[targetOS].name), toRope(platform.CPU[targetCPU].name), toRope(extccomp.CC[extccomp.cCompiler].name), - toRope(getCompileCFileCmd(cfilenoext))]) + toRope(getCompileCFileCmd(cfile))]) -proc getFileHeader(cfilenoext: string): PRope = - result = getCopyright(cfilenoext) +proc getFileHeader(cfile: string): PRope = + result = getCopyright(cfile) addIntTypes(result) proc genFilenames(m: BModule): PRope = @@ -963,7 +969,7 @@ proc genMainProc(m: BModule) = "\tvoid (*volatile inner)();$N" & "\tsystemDatInit();$N" & "\tinner = PreMainInner;$N" & - "$4" & + "$4$5" & "\t(*inner)();$N" & "}$N$N" @@ -1051,12 +1057,16 @@ proc genMainProc(m: BModule) = gBreakpoints.app(m.genFilenames) let initStackBottomCall = - if emulatedThreadVars() or - platform.targetOS == osStandalone: "".toRope + if platform.targetOS == osStandalone: "".toRope else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N") inc(m.labels) appcg(m, m.s[cfsProcs], PreMainBody, [ - mainDatInit, gBreakpoints, otherModsInit, initStackBottomCall]) + mainDatInit, gBreakpoints, otherModsInit, + if emulatedThreadVars() and platform.targetOS != osStandalone: + ropecg(m, "\t#initThreadVarsEmulation();$N") + else: + "".toRope, + initStackBottomCall]) appcg(m, m.s[cfsProcs], nimMain, [mainModInit, initStackBottomCall, toRope(m.labels)]) if optNoMain notin gGlobalOptions: @@ -1078,9 +1088,9 @@ proc registerModuleToMain(m: PSym) = var init = m.getInitName datInit = m.getDatInitName - appff(mainModProcs, "N_NOINLINE(void, $1)(void);$N", + appff(mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", "declare void $1() noinline$N", [init]) - appff(mainModProcs, "N_NOINLINE(void, $1)(void);$N", + appff(mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", "declare void $1() noinline$N", [datInit]) if sfSystemModule notin m.flags: appff(mainDatInit, "\t$1();$N", "call void ()* $1$n", [datInit]) @@ -1092,7 +1102,7 @@ proc registerModuleToMain(m: PSym) = proc genInitCode(m: BModule) = var initname = getInitName(m.module) - var prc = ropeff("N_NOINLINE(void, $1)(void) {$N", + var prc = ropeff("NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N", "define void $1() noinline {$n", [initname]) if m.typeNodes > 0: appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n", @@ -1135,7 +1145,7 @@ proc genInitCode(m: BModule) = app(prc, deinitGCFrame(m.initProc)) appf(prc, "}$N$N") - prc.appff("N_NOINLINE(void, $1)(void) {$N", + prc.appff("NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N", "define void $1() noinline {$n", [getDatInitName(m.module)]) for i in cfsTypeInit1..cfsDynLibInit: @@ -1155,8 +1165,8 @@ proc genInitCode(m: BModule) = (i.ord - '0'.ord).toRope, el) app(m.s[cfsInitProc], ex) -proc genModule(m: BModule, cfilenoext: string): PRope = - result = getFileHeader(cfilenoext) +proc genModule(m: BModule, cfile: string): PRope = + result = getFileHeader(cfile) result.app(genMergeInfo(m)) generateHeaders(m) @@ -1297,7 +1307,11 @@ proc writeHeader(m: BModule) = writeRope(result, m.filename) proc getCFile(m: BModule): string = - result = changeFileExt(completeCFilePath(m.cfilename.withPackageName), cExt) + let ext = + if m.compileToCpp: ".cpp" + elif gCmd == cmdCompileToOC or sfCompileToObjC in m.module.flags: ".m" + else: ".c" + result = changeFileExt(completeCFilePath(m.cfilename.withPackageName), ext) proc myOpenCached(module: PSym, rd: PRodReader): PPassContext = assert optSymbolFiles in gGlobalOptions @@ -1326,10 +1340,10 @@ proc finishModule(m: BModule) = dec(gForwardedProcsCounter, i) setLen(m.forwardedProcs, 0) -proc shouldRecompile(code: PRope, cfile, cfilenoext: string): bool = +proc shouldRecompile(code: PRope, cfile: string): bool = result = true if optForceFullMake notin gGlobalOptions: - var objFile = toObjFile(cfilenoext) + var objFile = toObjFile(cfile) if writeRopeIfNotEqual(code, cfile): return if existsFile(objFile) and os.fileNewer(objFile, cfile): result = false else: @@ -1354,26 +1368,26 @@ proc writeModule(m: BModule, pending: bool) = app(m.s[cfsProcHeaders], mainModProcs) generateThreadVarsSize(m) - var code = genModule(m, cfilenoext) + var code = genModule(m, cfile) when hasTinyCBackend: if gCmd == cmdRun: tccgen.compileCCode(ropeToStr(code)) return - if shouldRecompile(code, cfile, cfilenoext): - addFileToCompile(cfilenoext) + if shouldRecompile(code, cfile): + addFileToCompile(cfile) elif pending and mergeRequired(m) and sfMainModule notin m.module.flags: mergeFiles(cfile, m) genInitCode(m) finishTypeDescriptions(m) - var code = genModule(m, cfilenoext) + var code = genModule(m, cfile) writeRope(code, cfile) - addFileToCompile(cfilenoext) + addFileToCompile(cfile) elif not existsFile(toObjFile(cfilenoext)): # Consider: first compilation compiles ``system.nim`` and produces # ``system.c`` but then compilation fails due to an error. This means # that ``system.o`` is missing, so we need to call the C compiler for it: - addFileToCompile(cfilenoext) + addFileToCompile(cfile) addFileToLink(cfilenoext) @@ -1385,9 +1399,9 @@ proc updateCachedModule(m: BModule) = mergeFiles(cfile, m) genInitCode(m) finishTypeDescriptions(m) - var code = genModule(m, cfilenoext) + var code = genModule(m, cfile) writeRope(code, cfile) - addFileToCompile(cfilenoext) + addFileToCompile(cfile) addFileToLink(cfilenoext) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 12041c55b..508f98074 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -62,7 +62,7 @@ type frameLen*: int16 TCProc{.final.} = object # represents C proc that is currently generated - prc*: PSym # the Nimrod proc that this C proc belongs to + prc*: PSym # the Nim proc that this C proc belongs to beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed threadVarAccessed*: bool # true if the proc already accessed some threadvar nestedTryStmts*: seq[PNode] # in how many nested try statements we are @@ -101,10 +101,10 @@ type # without extension) typeCache*: TIdTable # cache the generated types forwTypeCache*: TIdTable # cache for forward declarations of types - declaredThings*: TIntSet # things we have declared in this .c file - declaredProtos*: TIntSet # prototypes we have declared in this .c file + declaredThings*: IntSet # things we have declared in this .c file + declaredProtos*: IntSet # prototypes we have declared in this .c file headerFiles*: TLinkedList # needed headers to include - typeInfoMarker*: TIntSet # needed for generating type information + typeInfoMarker*: IntSet # needed for generating type information initProc*: BProc # code for init procedure postInitProc*: BProc # code to be executed after the init proc preInitProc*: BProc # code executed before the init proc diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index d881226d4..6c997b983 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -155,7 +155,7 @@ proc relevantCol(methods: TSymSeq, col: int): bool = if not sameType(t2, t): return true -proc cmpSignatures(a, b: PSym, relevantCols: TIntSet): int = +proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int = for col in countup(1, sonsLen(a.typ) - 1): if contains(relevantCols, col): var aa = skipTypes(a.typ.sons[col], skipPtrs) @@ -164,7 +164,7 @@ proc cmpSignatures(a, b: PSym, relevantCols: TIntSet): int = if (d != high(int)): return d -proc sortBucket(a: var TSymSeq, relevantCols: TIntSet) = +proc sortBucket(a: var TSymSeq, relevantCols: IntSet) = # we use shellsort here; fast and simple var n = len(a) var h = 1 @@ -183,7 +183,7 @@ proc sortBucket(a: var TSymSeq, relevantCols: TIntSet) = a[j] = v if h == 1: break -proc genDispatcher(methods: TSymSeq, relevantCols: TIntSet): PSym = +proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym = var base = lastSon(methods[0].ast).sym result = base var paramLen = sonsLen(base.typ) diff --git a/compiler/commands.nim b/compiler/commands.nim index cea965f5c..a5b0b40d7 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -26,7 +26,7 @@ bootSwitch(usedNoGC, defined(nogc), "--gc:none") import os, msgs, options, nversion, condsyms, strutils, extccomp, platform, lists, - wordrecg, parseutils, babelcmd, idents + wordrecg, parseutils, nimblecmd, idents, parseopt # but some have deps to imported modules. Yay. bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc") @@ -43,7 +43,7 @@ type TCmdLinePass* = enum passCmd1, # first pass over the command line passCmd2, # second pass over the command line - passPP # preprocessor called ProcessCommand() + passPP # preprocessor called processCommand() proc processCommand*(switch: string, pass: TCmdLinePass) proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) @@ -51,7 +51,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) # implementation const - HelpMessage = "Nimrod Compiler Version $1 (" & CompileDate & ") [$2: $3]\n" & + HelpMessage = "Nim Compiler Version $1 (" & CompileDate & ") [$2: $3]\n" & "Copyright (c) 2006-2014 by Andreas Rumpf\n" const @@ -226,6 +226,7 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool = of "tlsemulation": result = contains(gGlobalOptions, optTlsEmulation) of "implicitstatic": result = contains(gOptions, optImplicitStatic) of "patterns": result = contains(gOptions, optPatterns) + of "experimental": result = gExperimentalMode else: invalidCmdLineOption(passCmd1, switch, info) proc processPath(path: string, notRelativeToProj = false): string = @@ -234,7 +235,9 @@ proc processPath(path: string, notRelativeToProj = false): string = path else: options.gProjectPath / path - result = unixToNativePath(p % ["nimrod", getPrefixDir(), "lib", libpath, + result = unixToNativePath(p % ["nimrod", getPrefixDir(), + "nim", getPrefixDir(), + "lib", libpath, "home", removeTrailingDirSep(os.getHomeDir()), "projectname", options.gProjectName, "projectpath", options.gProjectPath]) @@ -280,14 +283,15 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "path", "p": expectArg(switch, arg, pass, info) addPath(processPath(arg), info) - of "babelpath": - if pass in {passCmd2, passPP} and not options.gNoBabelPath: + of "nimblepath", "babelpath": + # keep the old name for compat + if pass in {passCmd2, passPP} and not options.gNoNimblePath: expectArg(switch, arg, pass, info) let path = processPath(arg, notRelativeToProj=true) - babelpath(path, info) - of "nobabelpath": + nimblePath(path, info) + of "nonimblepath", "nobabelpath": expectNoArg(switch, arg, pass, info) - options.gNoBabelPath = true + options.gNoNimblePath = true of "excludepath": expectArg(switch, arg, pass, info) let path = processPath(arg) @@ -307,8 +311,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = expectArg(switch, arg, pass, info) options.docSeeSrcUrl = arg of "mainmodule", "m": - expectArg(switch, arg, pass, info) - optMainModule = arg + discard "allow for backwards compatibility, but don't do anything" of "define", "d": expectArg(switch, arg, pass, info) defineSymbol(arg) @@ -566,6 +569,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = of "none": idents.firstCharIsCS = false else: localError(info, errGenerated, "'partial' or 'none' expected, but found " & arg) + of "experimental": + expectNoArg(switch, arg, pass, info) + gExperimentalMode = true else: if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg) else: invalidCmdLineOption(pass, switch, info) @@ -574,3 +580,33 @@ proc processCommand(switch: string, pass: TCmdLinePass) = var cmd, arg: string splitSwitch(switch, cmd, arg, pass, gCmdLineInfo) processSwitch(cmd, arg, pass, gCmdLineInfo) + + +var + arguments* = "" + # the arguments to be passed to the program that + # should be run + +proc processSwitch*(pass: TCmdLinePass; p: OptParser) = + # hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off") + # we fix this here + var bracketLe = strutils.find(p.key, '[') + if bracketLe >= 0: + var key = substr(p.key, 0, bracketLe - 1) + var val = substr(p.key, bracketLe + 1) & ':' & p.val + processSwitch(key, val, pass, gCmdLineInfo) + else: + processSwitch(p.key, p.val, pass, gCmdLineInfo) + +proc processArgument*(pass: TCmdLinePass; p: OptParser; + argsCount: var int): bool = + if argsCount == 0: + options.command = p.key + else: + if pass == passCmd1: options.commandArgs.add p.key + if argsCount == 1: + # support UNIX style filenames anywhere for portable build scripts: + options.gProjectName = unixToNativePath(p.key) + arguments = cmdLineRest(p) + result = true + inc argsCount diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 6d144ad96..0472a8731 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -14,7 +14,7 @@ import # We need to use a PStringTable here as defined symbols are always guaranteed # to be style insensitive. Otherwise hell would break lose. -var gSymbols: PStringTable +var gSymbols: StringTableRef proc defineSymbol*(symbol: string) = gSymbols[symbol] = "true" @@ -41,12 +41,12 @@ proc countDefinedSymbols*(): int = for key, val in pairs(gSymbols): if val == "true": inc(result) -# For ease of bootstrapping, we keep there here and not in the global config +# For ease of bootstrapping, we keep them here and not in the global config # file for now: const additionalSymbols = """ x86 itanium x8664 - msdos mswindows win32 unix posix sunos bsd macintosh RISCOS doslike hpux + msdos mswindows win32 unix posix sunos bsd macintosh RISCOS hpux mac hppa hp9000 hp9000s300 hp9000s700 hp9000s800 hp9000s820 ELATE sparcv9 @@ -88,6 +88,7 @@ proc initDefines*() = defineSymbol("nimrequiresnimframe") defineSymbol("nimparsebiggestfloatmagic") defineSymbol("nimalias") + defineSymbol("nimlocks") # add platform specific symbols: for c in low(CPU)..high(CPU): diff --git a/compiler/crc.nim b/compiler/crc.nim index ae1df3ff1..a8b61f2a5 100644 --- a/compiler/crc.nim +++ b/compiler/crc.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -102,7 +102,7 @@ proc crcFromFile(filename: string): TCrc32 = const bufSize = 8000 # don't use 8K for the memory allocator! var - bin: TFile + bin: File result = InitCrc32 if not open(bin, filename): return # not equal if file does not exist diff --git a/compiler/depends.nim b/compiler/depends.nim index a43eaf844..115a98f84 100644 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 434e2a65b..35acf1379 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -23,7 +23,7 @@ type id: int # for generating IDs toc, section: TSections indexValFilename: string - seenSymbols: PStringTable # avoids duplicate symbol generation for HTML. + seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML. PDoc* = ref TDocumentor ## Alias to type less. @@ -41,6 +41,7 @@ proc compilerMsgHandler(filename: string, line, col: int, of mwRedefinitionOfLabel: k = warnRedefinitionOfLabel of mwUnknownSubstitution: k = warnUnknownSubstitutionX of mwUnsupportedLanguage: k = warnLanguageXNotSupported + of mwUnsupportedField: k = warnFieldXNotSupported globalError(newLineInfo(filename, line, col), k, arg) proc docgenFindFile(s: string): string {.procvar.} = @@ -55,7 +56,7 @@ proc parseRst(text, filename: string, result = rstParse(text, filename, line, column, hasToc, rstOptions, docgenFindFile, compilerMsgHandler) -proc newDocumentor*(filename: string, config: PStringTable): PDoc = +proc newDocumentor*(filename: string, config: StringTableRef): PDoc = new(result) initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex), options.gConfigVars, filename, {roSupportRawDirective}, @@ -374,11 +375,11 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = cleanPlainSymbol = renderPlainSymbolName(nameNode) complexSymbol = complexName(k, n, cleanPlainSymbol) plainSymbolRope = toRope(cleanPlainSymbol) - plainSymbolEncRope = toRope(URLEncode(cleanPlainSymbol)) + plainSymbolEncRope = toRope(encodeUrl(cleanPlainSymbol)) itemIDRope = toRope(d.id) symbolOrId = d.newUniquePlainSymbol(complexSymbol) symbolOrIdRope = symbolOrId.toRope - symbolOrIdEncRope = URLEncode(symbolOrId).toRope + symbolOrIdEncRope = encodeUrl(symbolOrId).toRope var seeSrcRope: PRope = nil let docItemSeeSrc = getConfigVar("doc.item.seesrc") @@ -413,7 +414,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = setIndexTerm(d[], symbolOrId, name, linkTitle, xmltree.escape(plainDocstring.docstringSummary)) -proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): PJsonNode = +proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode = if not isVisible(nameNode): return var name = getName(d, nameNode) @@ -473,7 +474,7 @@ proc generateDoc*(d: PDoc, n: PNode) = of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0]) else: discard -proc generateJson(d: PDoc, n: PNode, jArray: PJsonNode = nil): PJsonNode = +proc generateJson(d: PDoc, n: PNode, jArray: JsonNode = nil): JsonNode = case n.kind of nkCommentStmt: if n.comment != nil and startsWith(n.comment, "##"): diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index d76be8e3c..aa832b6df 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -26,7 +26,7 @@ proc close(p: PPassContext, n: PNode): PNode = writeOutput(g.doc, g.module.filename, HtmlExt, useWarning) try: generateIndex(g.doc) - except EIO: + except IOError: discard proc processNode(c: PPassContext, n: PNode): PNode = diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index f798a43ac..d6b3a1aa5 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -1,13 +1,13 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## This file implements the FFI part of the evaluator for Nimrod code. +## This file implements the FFI part of the evaluator for Nim code. import ast, astalgo, ropes, types, options, tables, dynlib, libffi, msgs, os diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 78cdbb45f..78cc691c0 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index f0e5dad11..ad9c38904 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -1,14 +1,16 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -# module for calling the different external C compilers -# some things are read in from the configuration file +# Module providing functions for calling the different external C compilers +# Uses some hard-wired facts about each C/C++ compiler, plus options read +# from a configuration file, to provide generalized procedures to compile +# nim files. import lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, crc @@ -59,6 +61,7 @@ type template compiler(name: expr, settings: stmt): stmt {.immediate.} = proc name: TInfoCC {.compileTime.} = settings +# GNU C and C++ Compiler compiler gcc: result = ( name: "gcc", @@ -84,21 +87,24 @@ compiler gcc: props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm, hasNakedAttribute}) +# LLVM Frontend for GCC/G++ compiler llvmGcc: - result = gcc() - + result = gcc() # Uses settings from GCC + result.name = "llvm_gcc" result.compilerExe = "llvm-gcc" result.cppCompiler = "llvm-g++" result.buildLib = "llvm-ar rcs $libfile $objfiles" +# Clang (LLVM) C/C++ Compiler compiler clang: - result = llvmGcc() + result = llvmGcc() # Uses settings from llvmGcc result.name = "clang" result.compilerExe = "clang" result.cppCompiler = "clang++" +# Microsoft Visual C/C++ Compiler compiler vcc: result = ( name: "vcc", @@ -123,6 +129,7 @@ compiler vcc: packedPragma: "#pragma pack(1)", props: {hasCpp, hasAssume, hasNakedDeclspec}) +# Intel C/C++ Compiler compiler icl: # Intel compilers try to imitate the native ones (gcc and msvc) when defined(windows): @@ -134,6 +141,7 @@ compiler icl: result.compilerExe = "icl" result.linkerExe = "icl" +# Local C Compiler compiler lcc: result = ( name: "lcc", @@ -158,6 +166,7 @@ compiler lcc: packedPragma: "", # XXX: not supported yet props: {}) +# Borland C Compiler compiler bcc: result = ( name: "bcc", @@ -182,6 +191,7 @@ compiler bcc: packedPragma: "", # XXX: not supported yet props: {hasCpp}) +# Digital Mars C Compiler compiler dmc: result = ( name: "dmc", @@ -206,6 +216,7 @@ compiler dmc: packedPragma: "#pragma pack(1)", props: {hasCpp}) +# Watcom C Compiler compiler wcc: result = ( name: "wcc", @@ -230,6 +241,7 @@ compiler wcc: packedPragma: "", # XXX: not supported yet props: {hasCpp}) +# Tiny C Compiler compiler tcc: result = ( name: "tcc", @@ -254,6 +266,7 @@ compiler tcc: packedPragma: "", # XXX: not supported yet props: {hasSwitchRange, hasComputedGoto}) +# Pelles C Compiler compiler pcc: # Pelles C result = ( @@ -279,6 +292,7 @@ compiler pcc: packedPragma: "", # XXX: not supported yet props: {}) +# Your C Compiler compiler ucc: result = ( name: "ucc", @@ -323,10 +337,7 @@ const var cCompiler* = ccGcc # the used compiler - - cExt* = ".c" # extension of generated C/C++ files - # (can be changed to .cpp later) - + gMixedMode*: bool # true if some module triggered C++ codegen cIncludes*: seq[string] = @[] # directories to search for included files cLibs*: seq[string] = @[] # directories to search for lib files cLinkedLibs*: seq[string] = @[] # libraries to link @@ -342,7 +353,9 @@ var compileOptions: string = "" ccompilerpath: string = "" -proc nameToCC*(name: string): TSystemCC = +proc nameToCC*(name: string): TSystemCC = + ## Returns the kind of compiler referred to by `name`, or ccNone + ## if the name doesn't refer to any known compiler. for i in countup(succ(ccNone), high(TSystemCC)): if cmpIgnoreStyle(name, CC[i].name) == 0: return i @@ -351,17 +364,18 @@ proc nameToCC*(name: string): TSystemCC = proc getConfigVar(c: TSystemCC, suffix: string): string = # use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given # for niminst support + let fullSuffix = (if gCmd == cmdCompileToCpp: ".cpp" & suffix else: suffix) if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and optCompileOnly notin gGlobalOptions: let fullCCname = platform.CPU[targetCPU].name & '.' & platform.OS[targetOS].name & '.' & - CC[c].name & suffix + CC[c].name & fullSuffix result = getConfigVar(fullCCname) if result.len == 0: # not overriden for this cross compilation setting? - result = getConfigVar(CC[c].name & suffix) + result = getConfigVar(CC[c].name & fullSuffix) else: - result = getConfigVar(CC[c].name & suffix) + result = getConfigVar(CC[c].name & fullSuffix) proc setCC*(ccname: string) = cCompiler = nameToCC(ccname) @@ -387,8 +401,6 @@ proc initVars*() = # we need to define the symbol here, because ``CC`` may have never been set! for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name) defineSymbol(CC[cCompiler].name) - if gCmd == cmdCompileToCpp: cExt = ".cpp" - elif gCmd == cmdCompileToOC: cExt = ".m" addCompileOption(getConfigVar(cCompiler, ".options.always")) addLinkOption(getConfigVar(cCompiler, ".options.linker")) if len(ccompilerpath) == 0: @@ -397,9 +409,9 @@ proc initVars*() = proc completeCFilePath*(cfile: string, createSubDir: bool = true): string = result = completeGeneratedFilePath(cfile, createSubDir) -proc toObjFile*(filenameWithoutExt: string): string = +proc toObjFile*(filename: string): string = # Object file for compilation - result = changeFileExt(filenameWithoutExt, CC[cCompiler].objExt) + result = changeFileExt(filename, CC[cCompiler].objExt) proc addFileToCompile*(filename: string) = appendStr(toCompile, filename) @@ -417,8 +429,12 @@ proc addFileToLink*(filename: string) = prependStr(toLink, filename) # BUGFIX: was ``appendStr`` -proc execExternalProgram*(cmd: string) = - if optListCmd in gGlobalOptions or gVerbosity > 0: msgWriteln(cmd) +proc execExternalProgram*(cmd: string, prettyCmd = "") = + if optListCmd in gGlobalOptions or gVerbosity > 0: + if prettyCmd != "": + msgWriteln(prettyCmd) + else: + msgWriteln(cmd) if execCmd(cmd) != 0: rawMessage(errExecutionOfProgramFailed, "") proc generateScript(projectFile: string, script: PRope) = @@ -487,7 +503,7 @@ proc getLinkOptions: string = proc needsExeExt(): bool {.inline.} = result = (optGenScript in gGlobalOptions and targetOS == osWindows) or - (platform.hostOS == osWindows) + (platform.hostOS == osWindows) proc getCompilerExe(compiler: TSystemCC): string = result = if gCmd == cmdCompileToCpp: CC[compiler].cppCompiler @@ -497,6 +513,7 @@ proc getCompilerExe(compiler: TSystemCC): string = proc getLinkerExe(compiler: TSystemCC): string = result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe + elif gMixedMode and gCmd != cmdCompileToCpp: CC[compiler].cppCompiler else: compiler.getCompilerExe proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = @@ -507,11 +524,11 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = if needsExeExt(): exe = addFileExt(exe, "exe") if optGenDynLib in gGlobalOptions and - ospNeedsPIC in platform.OS[targetOS].props: + ospNeedsPIC in platform.OS[targetOS].props: add(options, ' ' & CC[c].pic) var includeCmd, compilePattern: string - if not noAbsolutePaths(): + if not noAbsolutePaths(): # compute include paths: includeCmd = CC[c].includeCmd & quoteShell(libpath) @@ -519,29 +536,31 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = includeCmd.add([CC[c].includeCmd, includeDir.quoteShell]) compilePattern = joinPath(ccompilerpath, exe) - else: + else: includeCmd = "" compilePattern = c.getCompilerExe - var cfile = if noAbsolutePaths(): extractFilename(cfilename) + var cfile = if noAbsolutePaths(): extractFilename(cfilename) else: cfilename - var objfile = if not isExternal or noAbsolutePaths(): - toObjFile(cfile) - else: + var objfile = if not isExternal or noAbsolutePaths(): + toObjFile(cfile) + else: completeCFilePath(toObjFile(cfile)) - cfile = quoteShell(addFileExt(cfile, cExt)) objfile = quoteShell(objfile) result = quoteShell(compilePattern % [ - "file", cfile, "objfile", objfile, "options", options, - "include", includeCmd, "nimrod", getPrefixDir(), "lib", libpath]) + "file", cfile, "objfile", objfile, "options", options, + "include", includeCmd, "nimrod", getPrefixDir(), + "nim", getPrefixDir(), "lib", libpath]) add(result, ' ') addf(result, CC[c].compileTmpl, [ - "file", cfile, "objfile", objfile, - "options", options, "include", includeCmd, - "nimrod", quoteShell(getPrefixDir()), + "file", cfile, "objfile", objfile, + "options", options, "include", includeCmd, + "nimrod", quoteShell(getPrefixDir()), + "nim", quoteShell(getPrefixDir()), "lib", quoteShell(libpath)]) proc footprint(filename: string): TCrc32 = + # note, '><' further modifies a crc value with a string. result = crcFromFile(filename) >< platform.OS[targetOS].name >< platform.CPU[targetCPU].name >< @@ -551,7 +570,7 @@ proc footprint(filename: string): TCrc32 = proc externalFileChanged(filename: string): bool = var crcFile = toGeneratedFile(filename.withPackageName, "crc") var currentCrc = int(footprint(filename)) - var f: TFile + var f: File if open(f, crcFile, fmRead): var line = newStringOfCap(40) if not f.readLine(line): line = "0" @@ -569,14 +588,16 @@ proc addExternalFileToCompile*(filename: string) = if optForceFullMake in gGlobalOptions or externalFileChanged(filename): appendStr(externalToCompile, filename) -proc compileCFile(list: TLinkedList, script: var PRope, cmds: var TStringSeq, - isExternal: bool) = +proc compileCFile(list: TLinkedList, script: var PRope, cmds: var TStringSeq, + prettyCmds: var TStringSeq, isExternal: bool) = var it = PStrEntry(list.head) while it != nil: inc(fileCounter) # call the C compiler for the .c file: var compileCmd = getCompileCFileCmd(it.data, isExternal) if optCompileOnly notin gGlobalOptions: add(cmds, compileCmd) + let (dir, name, ext) = splitFile(it.data) + add(prettyCmds, "CC: " & name) if optGenScript in gGlobalOptions: app(script, compileCmd) app(script, tnl) @@ -592,18 +613,24 @@ proc callCCompiler*(projectfile: string) = var c = cCompiler var script: PRope = nil var cmds: TStringSeq = @[] - compileCFile(toCompile, script, cmds, false) - compileCFile(externalToCompile, script, cmds, true) + var prettyCmds: TStringSeq = @[] + let prettyCb = proc (idx: int) = + echo prettyCmds[idx] + compileCFile(toCompile, script, cmds, prettyCmds, false) + compileCFile(externalToCompile, script, cmds, prettyCmds, true) if optCompileOnly notin gGlobalOptions: if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors() var res = 0 if gNumberOfProcessors <= 1: for i in countup(0, high(cmds)): res = max(execCmd(cmds[i]), res) - elif optListCmd in gGlobalOptions or gVerbosity > 0: - res = execProcesses(cmds, {poEchoCmd, poUseShell, poParentStreams}, + elif optListCmd in gGlobalOptions or gVerbosity > 1: + res = execProcesses(cmds, {poEchoCmd, poUseShell, poParentStreams}, gNumberOfProcessors) - else: - res = execProcesses(cmds, {poUseShell, poParentStreams}, + elif gVerbosity == 1: + res = execProcesses(cmds, {poUseShell, poParentStreams}, + gNumberOfProcessors, prettyCb) + else: + res = execProcesses(cmds, {poUseShell, poParentStreams}, gNumberOfProcessors) if res != 0: if gNumberOfProcessors <= 1: @@ -625,7 +652,6 @@ proc callCCompiler*(projectfile: string) = if optGenStaticLib in gGlobalOptions: linkCmd = CC[c].buildLib % ["libfile", (libNameTmpl() % gProjectName), "objfiles", objfiles] - if optCompileOnly notin gGlobalOptions: execExternalProgram(linkCmd) else: var linkerExe = getConfigVar(c, ".linkerexe") if len(linkerExe) == 0: linkerExe = c.getLinkerExe @@ -657,7 +683,11 @@ proc callCCompiler*(projectfile: string) = "objfiles", objfiles, "exefile", exefile, "nimrod", quoteShell(getPrefixDir()), "lib", quoteShell(libpath)]) - if optCompileOnly notin gGlobalOptions: execExternalProgram(linkCmd) + if optCompileOnly notin gGlobalOptions: + if gVerbosity == 1: + execExternalProgram(linkCmd, "[Linking]") + else: + execExternalProgram(linkCmd) else: linkCmd = "" if optGenScript in gGlobalOptions: @@ -668,7 +698,7 @@ proc callCCompiler*(projectfile: string) = proc genMappingFiles(list: TLinkedList): PRope = var it = PStrEntry(list.head) while it != nil: - appf(result, "--file:r\"$1\"$N", [toRope(addFileExt(it.data, cExt))]) + appf(result, "--file:r\"$1\"$N", [toRope(it.data)]) it = PStrEntry(it.next) proc writeMapping*(gSymbolMapping: PRope) = diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim index 0014e9c78..7b975dbaa 100644 --- a/compiler/filter_tmpl.nim +++ b/compiler/filter_tmpl.nim @@ -1,13 +1,13 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -# This module implements Nimrod's standard template filter. +# This module implements Nim's standard template filter. import llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, @@ -148,7 +148,7 @@ proc parseLine(p: var TTmplParser) = inc(j) else: if p.x[j] == p.subsChar: - # parse Nimrod expression: + # parse Nim expression: inc(j) case p.x[j] of '{': diff --git a/compiler/filters.nim b/compiler/filters.nim index ce0ffd196..783a320a4 100644 --- a/compiler/filters.nim +++ b/compiler/filters.nim @@ -1,13 +1,13 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -# This module implements Nimrod's simple filters and helpers for filters. +# This module implements Nim's simple filters and helpers for filters. import llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, diff --git a/compiler/guards.nim b/compiler/guards.nim index 431de6156..cd0aaf296 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -838,6 +838,22 @@ proc addAsgnFact*(m: var TModel, key, value: PNode) = fact.sons[2] = value m.add fact +proc sameSubexprs*(m: TModel; a, b: PNode): bool = + # This should be used to check whether two *path expressions* refer to the + # same memory location according to 'm'. This is tricky: + # lock a[i].guard: + # ... + # access a[i].guarded + # + # Here a[i] is the same as a[i] iff 'i' and 'a' are not changed via '...'. + # However, nil checking requires exactly the same mechanism! But for now + # we simply use sameTree and live with the unsoundness of the analysis. + var check = newNodeI(nkCall, a.info, 3) + check.sons[0] = newSymNode(opEq) + check.sons[1] = a + check.sons[2] = b + result = m.doesImply(check) == impYes + proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) = let branch = n.sons[i] if branch.kind == nkOfBranch: diff --git a/compiler/hlo.nim b/compiler/hlo.nim index c75d6519f..363d100be 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/compiler/idents.nim b/compiler/idents.nim index ec903826a..775bffa00 100644 --- a/compiler/idents.nim +++ b/compiler/idents.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -15,7 +15,7 @@ import hashes, strutils type - TIdObj* = object of TObject + TIdObj* = object of RootObj id*: int # unique id; use this for comparisons and not the pointers PIdObj* = ref TIdObj diff --git a/compiler/idgen.nim b/compiler/idgen.nim index d932e3d9d..3c5669b54 100644 --- a/compiler/idgen.nim +++ b/compiler/idgen.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -53,7 +53,7 @@ proc saveMaxIds*(project: string) = f.close() proc loadMaxIds*(project: string) = - var f: TFile + var f: File if open(f, project.toGid, fmRead): var line = newStringOfCap(20) if f.readLine(line): diff --git a/compiler/importer.nim b/compiler/importer.nim index 33ed7e055..fbf3be4f2 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -113,7 +113,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = e = nextIdentIter(it, fromMod.tab) else: rawImportSymbol(c, s) -proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: TIntSet) = +proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) = var i: TTabIter var s = initTabIter(i, fromMod.tab) while s != nil: @@ -126,10 +126,10 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: TIntSet) = s = nextIter(i, fromMod.tab) proc importAllSymbols*(c: PContext, fromMod: PSym) = - var exceptSet: TIntSet + var exceptSet: IntSet importAllSymbolsExcept(c, fromMod, exceptSet) -proc importForwarded(c: PContext, n: PNode, exceptSet: TIntSet) = +proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) = if n.isNil: return case n.kind of nkExportStmt: @@ -166,7 +166,7 @@ proc myImportModule(c: PContext, n: PNode): PSym = proc evalImport(c: PContext, n: PNode): PNode = result = n - var emptySet: TIntSet + var emptySet: IntSet for i in countup(0, sonsLen(n) - 1): var m = myImportModule(c, n.sons[i]) if m != nil: diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index dfe498e47..a3423b1d5 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -33,7 +33,7 @@ import ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, os, times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils, - intsets, cgmeth + intsets, cgmeth, lowerings type TTarget = enum @@ -71,8 +71,8 @@ type TGlobals = object typeInfo, code: PRope forwarded: seq[PSym] - generatedSyms: TIntSet - typeInfoGenerated: TIntSet + generatedSyms: IntSet + typeInfoGenerated: IntSet PGlobals = ref TGlobals PProc = ref TProc @@ -113,7 +113,7 @@ proc rdLoc(a: TCompRes): PRope {.inline.} = result = ropef("$1[$2]", a.address, a.res) proc newProc(globals: PGlobals, module: BModule, procDef: PNode, - options: TOptions): PProc = + options: TOptions): PProc = result = PProc( blocks: @[], options: options, @@ -601,15 +601,13 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = if p.target == targetJS: app(p.body, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl) if i < length and n.sons[i].kind == nkFinally: - gen(p, n.sons[i].sons[0], a) - moveInto(p, a, r) + genStmt(p, n.sons[i].sons[0]) if p.target == targetJS: app(p.body, "}" & tnl) if p.target == targetLua: # we need to repeat the finally block for Lua ... if i < length and n.sons[i].kind == nkFinally: - gen(p, n.sons[i].sons[0], a) - moveInto(p, a, r) + genStmt(p, n.sons[i].sons[0]) proc genRaiseStmt(p: PProc, n: PNode) = genLineDir(p, n) @@ -724,7 +722,7 @@ proc genBlock(p: PProc, n: PNode, r: var TCompRes) = if (n.sons[0].kind != nkSym): internalError(n.info, "genBlock") var sym = n.sons[0].sym sym.loc.k = locOther - sym.loc.a = idx + sym.position = idx+1 setLen(p.blocks, idx + 1) p.blocks[idx].id = - p.unique # negative because it isn't used yet let labl = p.unique @@ -741,7 +739,7 @@ proc genBreakStmt(p: PProc, n: PNode) = assert(n.sons[0].kind == nkSym) let sym = n.sons[0].sym assert(sym.loc.k == locOther) - idx = sym.loc.a + idx = sym.position-1 else: # an unnamed 'break' can only break a loop after 'transf' pass: idx = len(p.blocks) - 1 @@ -942,22 +940,21 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = case s.kind of skVar, skLet, skResult: r.kind = resExpr - if mapType(n.typ) == etyObject: + if mapType(n.sons[0].typ) == etyObject: # make addr() a no-op: r.typ = etyNone r.res = s.loc.r r.address = nil - elif sfGlobal in s.flags: - # globals are always indirect accessible - r.typ = etyBaseIndex - r.address = toRope("Globals") - r.res = makeJSString(ropeToStr(s.loc.r)) - elif sfAddrTaken in s.flags: + elif {sfGlobal, sfAddrTaken} * s.flags != {}: + # for ease of code generation, we do not distinguish between + # sfAddrTaken and sfGlobal. r.typ = etyBaseIndex r.address = s.loc.r r.res = toRope("0") else: - internalError(n.info, "genAddr: 4") + # 'var openArray' for instance produces an 'addr' but this is harmless: + gen(p, n.sons[0], r) + #internalError(n.info, "genAddr: 4 " & renderTree(n)) else: internalError(n.info, "genAddr: 2") of nkCheckedFieldExpr: genCheckedFieldAddr(p, n, r) @@ -981,7 +978,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = of skVar, skLet, skParam, skTemp, skResult: if s.loc.r == nil: internalError(n.info, "symbol has no generated name: " & s.name.s) - var k = mapType(s.typ) + let k = mapType(s.typ) if k == etyBaseIndex: r.typ = etyBaseIndex if {sfAddrTaken, sfGlobal} * s.flags != {}: @@ -990,7 +987,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = else: r.address = s.loc.r r.res = con(s.loc.r, "_Idx") - elif k != etyObject and sfAddrTaken in s.flags: + elif k != etyObject and {sfAddrTaken, sfGlobal} * s.flags != {}: r.res = ropef("$1[0]", [s.loc.r]) else: r.res = s.loc.r @@ -1078,8 +1075,16 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = proc genEcho(p: PProc, n: PNode, r: var TCompRes) = useMagic(p, "rawEcho") - app(r.res, "rawEcho") - genArgs(p, n, r) + app(r.res, "rawEcho(") + let n = n[1].skipConv + internalAssert n.kind == nkBracket + for i in countup(0, sonsLen(n) - 1): + let it = n.sons[i] + if it.typ.isCompileTimeOnly: continue + if i > 0: app(r.res, ", ") + genArg(p, it, r) + app(r.res, ")") + r.kind = resExpr proc putToSeq(s: string, indirect: bool): PRope = result = toRope(s) @@ -1162,8 +1167,9 @@ proc createVar(p: PProc, typ: PType, indirect: bool): PRope = internalError("createVar: " & $t.kind) result = nil -proc isIndirect(v: PSym): bool = - result = (sfAddrTaken in v.flags) and (mapType(v.typ) != etyObject) and +proc isIndirect(v: PSym): bool = + result = {sfAddrTaken, sfGlobal} * v.flags != {} and + (mapType(v.typ) != etyObject) and v.kind notin {skProc, skConverter, skMethod, skIterator, skClosureIterator} proc genVarInit(p: PProc, v: PSym, n: PNode) = @@ -1203,13 +1209,17 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = proc genVarStmt(p: PProc, n: PNode) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue - assert(a.kind == nkIdentDefs) - assert(a.sons[0].kind == nkSym) - var v = a.sons[0].sym - if lfNoDecl in v.loc.flags: continue - genLineDir(p, a) - genVarInit(p, v, a.sons[2]) + if a.kind != nkCommentStmt: + if a.kind == nkVarTuple: + let unpacked = lowerTupleUnpacking(a, p.prc) + genStmt(p, unpacked) + else: + assert(a.kind == nkIdentDefs) + assert(a.sons[0].kind == nkSym) + var v = a.sons[0].sym + if lfNoDecl notin v.loc.flags: + genLineDir(p, a) + genVarInit(p, v, a.sons[2]) proc genConstant(p: PProc, c: PSym) = if lfNoDecl notin c.loc.flags and not p.g.generatedSyms.containsOrIncl(c.id): @@ -1425,7 +1435,7 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = r.res = toRope("{") r.kind = resExpr for i in countup(1, sonsLen(n) - 1): - if i > 0: app(r.res, ", ") + if i > 1: app(r.res, ", ") var it = n.sons[i] internalAssert it.kind == nkExprColonExpr gen(p, it.sons[1], a) @@ -1654,6 +1664,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = r.res = nil of nkGotoState, nkState: internalError(n.info, "first class iterators not implemented") + of nkPragmaBlock: gen(p, n.lastSon, r) else: internalError(n.info, "gen: unknown node type: " & $n.kind) var globals: PGlobals @@ -1664,11 +1675,11 @@ proc newModule(module: PSym): BModule = if globals == nil: globals = newGlobals() proc genHeader(): PRope = - result = ropef("/* Generated by the Nimrod Compiler v$1 */$n" & + result = ropef("/* Generated by the Nim Compiler v$1 */$n" & "/* (c) 2014 Andreas Rumpf */$n$n" & - "$nvar Globals = this;$n" & "var framePtr = null;$n" & - "var excHandler = null;$n", + "var excHandler = null;$n" & + "var lastJSError = null;$n", [toRope(VersionAsString)]) proc genModule(p: PProc, n: PNode) = diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index 009fc7935..8b032c3ce 100644 --- a/compiler/jstypes.nim +++ b/compiler/jstypes.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 62e13b9c4..194396ddd 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -128,7 +128,7 @@ type obj: PType PEnv = ref TEnv - TEnv {.final.} = object of TObject + TEnv {.final.} = object of RootObj attachedNode, replacementNode: PNode createdVar: PNode # if != nil it is a used environment; for closure # iterators this can be 'envParam.env' @@ -140,12 +140,12 @@ type fn: PSym # function that belongs to this scope; # if up.fn != fn then we cross function boundaries. # This is an important case to consider. - vars: TIntSet # variables belonging to this environment + vars: IntSet # variables belonging to this environment TOuterContext = object fn: PSym # may also be a module! head: PEnv - capturedVars, processed: TIntSet + capturedVars, processed: IntSet localsToAccess: TIdNodeTable lambdasToEnv: TIdTable # PSym->PEnv mapping diff --git a/compiler/lexer.nim b/compiler/lexer.nim index ea51a1399..a6b85d7f3 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -21,10 +21,10 @@ import const MaxLineLength* = 80 # lines longer than this lead to a warning - numChars*: TCharSet = {'0'..'9', 'a'..'z', 'A'..'Z'} - SymChars*: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'} - SymStartChars*: TCharSet = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'} - OpChars*: TCharSet = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.', + numChars*: set[char] = {'0'..'9', 'a'..'z', 'A'..'Z'} + SymChars*: set[char] = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'} + SymStartChars*: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'} + OpChars*: set[char] = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.', '|', '=', '%', '&', '$', '@', '~', ':', '\x80'..'\xFF'} # don't forget to update the 'highlite' module if these charsets should change @@ -35,9 +35,10 @@ type tkSymbol, # keywords: tkAddr, tkAnd, tkAs, tkAsm, tkAtomic, tkBind, tkBlock, tkBreak, tkCase, tkCast, - tkConst, tkContinue, tkConverter, tkDiscard, tkDistinct, tkDiv, tkDo, + tkConst, tkContinue, tkConverter, + tkDefer, tkDiscard, tkDistinct, tkDiv, tkDo, tkElif, tkElse, tkEnd, tkEnum, tkExcept, tkExport, - tkFinally, tkFor, tkFrom, + tkFinally, tkFor, tkFrom, tkFunc, tkGeneric, tkIf, tkImport, tkIn, tkInclude, tkInterface, tkIs, tkIsnot, tkIterator, tkLet, @@ -71,11 +72,12 @@ const "tkSymbol", "addr", "and", "as", "asm", "atomic", "bind", "block", "break", "case", "cast", - "const", "continue", "converter", "discard", "distinct", "div", "do", + "const", "continue", "converter", + "defer", "discard", "distinct", "div", "do", "elif", "else", "end", "enum", "except", "export", - "finally", "for", "from", "generic", "if", + "finally", "for", "from", "func", "generic", "if", "import", "in", "include", "interface", "is", "isnot", "iterator", - "let", + "let", "macro", "method", "mixin", "mod", "nil", "not", "notin", "object", "of", "or", "out", "proc", "ptr", "raise", "ref", "return", @@ -102,7 +104,7 @@ type # so that it is the correct default value base2, base8, base16 - TToken* = object # a Nimrod token + TToken* = object # a Nim token tokType*: TTokType # the type of the token indent*: int # the indentation; != -1 if the token has been # preceeded with indentation @@ -116,7 +118,8 @@ type literal*: string # the parsed (string) literal; and # documentation comments are here too line*, col*: int - + + TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string) TLexer* = object of TBaseLexer fileIdx*: int32 indentAhead*: int # if > 0 an indendation has already been read @@ -124,7 +127,7 @@ type # needs so much look-ahead currLineIndent*: int strongSpaces*: bool - + errorHandler*: TErrorHandler var gLinesCompiled*: int # all lines that have been compiled @@ -148,7 +151,7 @@ proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") proc isKeyword(kind: TTokType): bool = result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh) -proc isNimrodIdentifier*(s: string): bool = +proc isNimIdentifier*(s: string): bool = if s[0] in SymStartChars: var i = 1 while i < s.len: @@ -222,14 +225,20 @@ proc getColumn(L: TLexer): int = proc getLineInfo(L: TLexer): TLineInfo = result = newLineInfo(L.fileIdx, L.lineNumber, getColNumber(L, L.bufpos)) -proc lexMessage(L: TLexer, msg: TMsgKind, arg = "") = - msgs.message(getLineInfo(L), msg, arg) +proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) = + if L.errorHandler.isNil: + msgs.message(info, msg, arg) + else: + L.errorHandler(info, msg, arg) -proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") = +proc lexMessage(L: TLexer, msg: TMsgKind, arg = "") = + L.dispMessage(getLineInfo(L), msg, arg) + +proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") = var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart) - msgs.message(info, msg, arg) + L.dispMessage(info, msg, arg) -proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: TCharSet) = +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: @@ -246,7 +255,7 @@ proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: TCharSet) = inc(pos) L.bufpos = pos -proc matchTwoChars(L: TLexer, first: char, second: TCharSet): bool = +proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool = result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second) proc isFloatLiteral(s: string): bool = @@ -429,9 +438,9 @@ proc getNumber(L: var TLexer): TToken = elif result.tokType == tkInt16Lit and (result.iNumber < int16.low or result.iNumber > int16.high): lexMessage(L, errNumberOutOfRange, result.literal) - except EInvalidValue: + except ValueError: lexMessage(L, errInvalidNumber, result.literal) - except EOverflow, EOutOfRange: + except OverflowError, RangeError: lexMessage(L, errNumberOutOfRange, result.literal) L.bufpos = endpos @@ -482,7 +491,7 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) = add(tok.literal, VT) inc(L.bufpos) of 't', 'T': - add(tok.literal, Tabulator) + add(tok.literal, '\t') inc(L.bufpos) of '\'', '\"': add(tok.literal, L.buf[L.bufpos]) @@ -519,7 +528,7 @@ proc handleCRLF(L: var TLexer, pos: int): int = lexMessagePos(L, hintLineTooLong, pos) if optEmbedOrigSrc in gGlobalOptions: - let lineStart = cast[TAddress](L.buf) + L.lineStart + let lineStart = cast[ByteAddress](L.buf) + L.lineStart let line = newString(cast[cstring](lineStart), col) addSourceLine(L.fileIdx, line) @@ -659,28 +668,32 @@ proc getOperator(L: var TLexer, tok: var TToken) = if buf[pos] in {CR, LF, nimlexbase.EndOfFile}: tok.strongSpaceB = -1 -proc scanComment(L: var TLexer, tok: var TToken) = +proc scanComment(L: var TLexer, tok: var TToken) = var pos = L.bufpos - var buf = L.buf - # a comment ends if the next line does not start with the # on the same - # column after only whitespace + var buf = L.buf + when not defined(nimfix): + assert buf[pos+1] == '#' + if buf[pos+2] == '[': + lexMessagePos(L, warnDeprecated, pos, "use '## [' instead; '##['") tok.tokType = tkComment # iNumber contains the number of '\n' in the token tok.iNumber = 0 - var col = getColNumber(L, pos) + when defined(nimfix): + var col = getColNumber(L, pos) while true: var lastBackslash = -1 while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: if buf[pos] == '\\': lastBackslash = pos+1 add(tok.literal, buf[pos]) inc(pos) - if lastBackslash > 0: - # a backslash is a continuation character if only followed by spaces - # plus a newline: - while buf[lastBackslash] == ' ': inc(lastBackslash) - if buf[lastBackslash] notin {CR, LF, nimlexbase.EndOfFile}: - # false positive: - lastBackslash = -1 + when defined(nimfix): + if lastBackslash > 0: + # a backslash is a continuation character if only followed by spaces + # plus a newline: + while buf[lastBackslash] == ' ': inc(lastBackslash) + if buf[lastBackslash] notin {CR, LF, nimlexbase.EndOfFile}: + # false positive: + lastBackslash = -1 pos = handleCRLF(L, pos) buf = L.buf @@ -688,9 +701,16 @@ proc scanComment(L: var TLexer, tok: var TToken) = while buf[pos] == ' ': inc(pos) inc(indent) - if buf[pos] == '#' and (col == indent or lastBackslash > 0): + + when defined(nimfix): + template doContinue(): expr = + buf[pos] == '#' and (col == indent or lastBackslash > 0) + else: + template doContinue(): expr = + buf[pos] == '#' and buf[pos+1] == '#' + if doContinue(): tok.literal.add "\n" - col = indent + when defined(nimfix): col = indent inc tok.iNumber else: if buf[pos] > ' ': @@ -707,7 +727,7 @@ proc skip(L: var TLexer, tok: var TToken) = of ' ': inc(pos) inc(tok.strongSpaceA) - of Tabulator: + of '\t': lexMessagePos(L, errTabulatorsAreNotAllowed, pos) inc(pos) of CR, LF: @@ -718,10 +738,24 @@ proc skip(L: var TLexer, tok: var TToken) = inc(pos) inc(indent) tok.strongSpaceA = 0 - if buf[pos] > ' ': + when defined(nimfix): + template doBreak(): expr = buf[pos] > ' ' + else: + template doBreak(): expr = + buf[pos] > ' ' and (buf[pos] != '#' or buf[pos+1] == '#') + if doBreak(): tok.indent = indent L.currLineIndent = indent break + of '#': + when defined(nimfix): + break + else: + # do not skip documentation comment: + if buf[pos+1] == '#': break + if buf[pos+1] == '[': + lexMessagePos(L, warnDeprecated, pos, "use '# [' instead; '#['") + while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos) else: break # EndOfFile also leaves the loop L.bufpos = pos diff --git a/compiler/lists.nim b/compiler/lists.nim index efffe60fe..1b2b91bff 100644 --- a/compiler/lists.nim +++ b/compiler/lists.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -12,7 +12,7 @@ import os type PListEntry* = ref TListEntry - TListEntry* = object of TObject + TListEntry* = object of RootObj prev*, next*: PListEntry TStrEntry* = object of TListEntry diff --git a/compiler/llstream.nim b/compiler/llstream.nim index 5aefd468a..be469548d 100644 --- a/compiler/llstream.nim +++ b/compiler/llstream.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -21,9 +21,9 @@ type llsString, # stream encapsulates a string llsFile, # stream encapsulates a file llsStdIn # stream encapsulates stdin - TLLStream* = object of TObject + TLLStream* = object of RootObj kind*: TLLStreamKind # accessible for low-level access (lexbase uses this) - f*: TFile + f*: File s*: string rd*, wr*: int # for string streams lineOffset*: int # for fake stdin line numbers @@ -31,8 +31,8 @@ type PLLStream* = ref TLLStream proc llStreamOpen*(data: string): PLLStream -proc llStreamOpen*(f: var TFile): PLLStream -proc llStreamOpen*(filename: string, mode: TFileMode): PLLStream +proc llStreamOpen*(f: var File): PLLStream +proc llStreamOpen*(filename: string, mode: FileMode): PLLStream proc llStreamOpen*(): PLLStream proc llStreamOpenStdIn*(): PLLStream proc llStreamClose*(s: PLLStream) @@ -50,12 +50,12 @@ proc llStreamOpen(data: string): PLLStream = result.s = data result.kind = llsString -proc llStreamOpen(f: var TFile): PLLStream = +proc llStreamOpen(f: var File): PLLStream = new(result) result.f = f result.kind = llsFile -proc llStreamOpen(filename: string, mode: TFileMode): PLLStream = +proc llStreamOpen(filename: string, mode: FileMode): PLLStream = new(result) result.kind = llsFile if not open(result.f, filename, mode): result = nil diff --git a/compiler/lookups.nim b/compiler/lookups.nim index d486585ef..fa1837296 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -114,7 +114,7 @@ type mode*: TOverloadIterMode symChoiceIndex*: int scope*: PScope - inSymChoice: TIntSet + inSymChoice: IntSet proc getSymRepr*(s: PSym): string = case s.kind diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index e1fb09e44..ef43c4d09 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -51,13 +51,14 @@ proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode = incl(temp.flags, sfFromGeneric) var v = newNodeI(nkVarSection, value.info) - v.addVar(newSymNode(temp)) + let tempAsNode = newSymNode(temp) + v.addVar(tempAsNode) result.add(v) - result.add newAsgnStmt(newSymNode(temp), value) + result.add newAsgnStmt(tempAsNode, value) for i in 0 .. n.len-3: if n.sons[i].kind == nkSym: v.addVar(n.sons[i]) - result.add newAsgnStmt(n.sons[i], newTupleAccess(value, i)) + result.add newAsgnStmt(n.sons[i], newTupleAccess(tempAsNode, i)) proc createObj*(owner: PSym, info: TLineInfo): PType = result = newType(tyObject, owner) @@ -204,7 +205,7 @@ proc flowVarKind(t: PType): TFlowVarKind = else: fvBlob proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType; - v: PNode): PSym = + v: PNode; useShallowCopy=false): PSym = result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info) result.typ = typ incl(result.flags, sfFromGeneric) @@ -215,11 +216,14 @@ proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType; vpart.sons[2] = if varInit.isNil: v else: ast.emptyNode varSection.add vpart if varInit != nil: - let deepCopyCall = newNodeI(nkCall, varInit.info, 3) - deepCopyCall.sons[0] = newSymNode(createMagic("deepCopy", mDeepCopy)) - deepCopyCall.sons[1] = newSymNode(result) - deepCopyCall.sons[2] = v - varInit.add deepCopyCall + if useShallowCopy: + varInit.add newFastAsgnStmt(newSymNode(result), v) + else: + let deepCopyCall = newNodeI(nkCall, varInit.info, 3) + deepCopyCall.sons[0] = newSymNode(createMagic("deepCopy", mDeepCopy)) + deepCopyCall.sons[1] = newSymNode(result) + deepCopyCall.sons[2] = v + varInit.add deepCopyCall discard """ We generate roughly this: @@ -261,7 +265,7 @@ proc createWrapperProc(f: PNode; threadParam, argsParam: PSym; threadLocalBarrier = addLocalVar(varSection2, nil, argsParam.owner, barrier.typ, barrier) body.add varSection2 - body.add callCodeGenProc("barrierEnter", threadLocalBarrier.newSymNode) + body.add callCodegenProc("barrierEnter", threadLocalBarrier.newSymNode) var threadLocalProm: PSym if spawnKind == srByVar: threadLocalProm = addLocalVar(varSection, nil, argsParam.owner, fv.typ, fv) @@ -276,7 +280,7 @@ proc createWrapperProc(f: PNode; threadParam, argsParam: PSym; body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode, "owner", fv.info), threadParam.newSymNode) - body.add callCodeGenProc("nimArgsPassingDone", threadParam.newSymNode) + body.add callCodegenProc("nimArgsPassingDone", threadParam.newSymNode) if spawnKind == srByVar: body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call) elif fv != nil: @@ -286,14 +290,20 @@ proc createWrapperProc(f: PNode; threadParam, argsParam: PSym; typeToString(fv.typ.sons[1])) body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode, if fk == fvGC: "data" else: "blob", fv.info), call) + if fk == fvGC: + let incRefCall = newNodeI(nkCall, fv.info, 2) + incRefCall.sons[0] = newSymNode(createMagic("GCref", mGCref)) + incRefCall.sons[1] = indirectAccess(threadLocalProm.newSymNode, + "data", fv.info) + body.add incRefCall if barrier == nil: # by now 'fv' is shared and thus might have beeen overwritten! we need # to use the thread-local view instead: - body.add callCodeGenProc("nimFlowVarSignal", threadLocalProm.newSymNode) + body.add callCodegenProc("nimFlowVarSignal", threadLocalProm.newSymNode) else: body.add call if barrier != nil: - body.add callCodeGenProc("barrierLeave", threadLocalBarrier.newSymNode) + body.add callCodegenProc("barrierLeave", threadLocalBarrier.newSymNode) var params = newNodeI(nkFormalParams, f.info) params.add emptyNode @@ -414,7 +424,8 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym; result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3]) let threadLocal = addLocalVar(varSection,nil, objType.owner, fieldA.typ, - indirectAccess(castExpr, fieldA, n.info)) + indirectAccess(castExpr, fieldA, n.info), + useShallowCopy=true) slice.sons[2] = threadLocal.newSymNode else: let a = genAddrOf(n) @@ -428,7 +439,8 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym; slice.sons[1] = genDeref(indirectAccess(castExpr, field, n.info)) let threadLocal = addLocalVar(varSection,nil, objType.owner, fieldB.typ, - indirectAccess(castExpr, fieldB, n.info)) + indirectAccess(castExpr, fieldB, n.info), + useShallowCopy=true) slice.sons[3] = threadLocal.newSymNode call.add slice elif (let size = computeSize(argType); size < 0 or size > 16) and @@ -439,7 +451,8 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym; objType.addField(field) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) let threadLocal = addLocalVar(varSection,nil, objType.owner, field.typ, - indirectAccess(castExpr, field, n.info)) + indirectAccess(castExpr, field, n.info), + useShallowCopy=true) call.add(genDeref(threadLocal.newSymNode)) else: # boring case @@ -448,7 +461,8 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym; result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n) let threadLocal = addLocalVar(varSection, varInit, objType.owner, field.typ, - indirectAccess(castExpr, field, n.info)) + indirectAccess(castExpr, field, n.info), + useShallowCopy=true) call.add(threadLocal.newSymNode) proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; @@ -543,7 +557,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; # create flowVar: result.add newFastAsgnStmt(fvField, callProc(spawnExpr[2])) if barrier == nil: - result.add callCodeGenProc("nimFlowVarCreateCondVar", fvField) + result.add callCodegenProc("nimFlowVarCreateSemaphore", fvField) elif spawnKind == srByVar: var field = newSym(skField, getIdent"fv", owner, n.info) @@ -556,7 +570,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; let wrapper = createWrapperProc(fn, threadParam, argsParam, varSection, varInit, call, barrierAsExpr, fvAsExpr, spawnKind) - result.add callCodeGenProc("nimSpawn", wrapper.newSymNode, + result.add callCodegenProc("nimSpawn", wrapper.newSymNode, genAddrOf(scratchObj.newSymNode)) if spawnKind == srFlowVar: result.add fvField diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index 475326161..6d4c65268 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -35,7 +35,7 @@ proc registerSysType(t: PType) = proc newSysType(kind: TTypeKind, size: int): PType = result = newType(kind, systemModule) result.size = size - result.align = size + result.align = size.int16 proc getSysSym(name: string): PSym = result = strTableGet(systemModule.tab, getIdent(name)) @@ -44,6 +44,7 @@ proc getSysSym(name: string): PSym = result = newSym(skError, getIdent(name), systemModule, systemModule.info) result.typ = newType(tyError, systemModule) if result.kind == skStub: loadStub(result) + if result.kind == skAlias: result = result.owner proc getSysMagic*(name: string, m: TMagic): PSym = var ti: TIdentIter @@ -165,13 +166,14 @@ proc setIntLitType*(result: PNode) = proc getCompilerProc(name: string): PSym = var ident = getIdent(name, hashIgnoreStyle(name)) result = strTableGet(compilerprocs, ident) - if result == nil: + if result == nil: result = strTableGet(rodCompilerprocs, ident) - if result != nil: + if result != nil: strTableAdd(compilerprocs, result) if result.kind == skStub: loadStub(result) - -proc registerCompilerProc(s: PSym) = + if result.kind == skAlias: result = result.owner + +proc registerCompilerProc(s: PSym) = strTableAdd(compilerprocs, s) proc finishSystem(tab: TStrTable) = discard diff --git a/compiler/main.nim b/compiler/main.nim index b4af49248..82e55058c 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -15,8 +15,7 @@ import wordrecg, sem, semdata, idents, passes, docgen, extccomp, cgen, jsgen, json, nversion, platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen, - tables, docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists, - pretty + tables, docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists from magicsys import systemModule, resetSysTypes @@ -164,19 +163,6 @@ proc commandEval(exp: string) = var echoExp = "echo \"eval\\t\", " & "repr(" & exp & ")" evalNim(echoExp.parseString, makeStdinModule()) -proc commandPrettyOld = - var projectFile = addFileExt(mainCommandArg(), NimExt) - var module = parseFile(projectFile.fileInfoIdx) - if module != nil: - renderModule(module, getOutFile(mainCommandArg(), "pretty." & NimExt)) - -proc commandPretty = - msgs.gErrorMax = high(int) # do not stop after first error - semanticPasses() - registerPass(prettyPass) - compileProject() - pretty.overwriteFiles() - proc commandScan = var f = addFileExt(mainCommandArg(), NimExt) var stream = llStreamOpen(f, fmRead) @@ -217,25 +203,6 @@ proc commandSuggest = else: gProjectMainIdx compileProject(projFile) -proc wantMainModule = - if gProjectFull.len == 0: - if optMainModule.len == 0: - fatal(gCmdLineInfo, errCommandExpectsFilename) - else: - gProjectName = optMainModule - gProjectFull = gProjectPath / gProjectName - - gProjectMainIdx = addFileExt(gProjectFull, NimExt).fileInfoIdx - -proc requireMainModuleOption = - if optMainModule.len == 0: - fatal(gCmdLineInfo, errMainModuleMustBeSpecified) - else: - gProjectName = optMainModule - gProjectFull = gProjectPath / gProjectName - - gProjectMainIdx = addFileExt(gProjectFull, NimExt).fileInfoIdx - proc resetMemory = resetCompilationLists() ccgutils.resetCaches() @@ -299,30 +266,22 @@ proc mainCommand* = # current path is always looked first for modules prependStr(searchPaths, gProjectPath) setId(100) - passes.gIncludeFile = includeModule - passes.gImportModule = importModule case command.normalize of "c", "cc", "compile", "compiletoc": # compile means compileToC currently gCmd = cmdCompileToC - wantMainModule() commandCompileToC() of "cpp", "compiletocpp": - extccomp.cExt = ".cpp" gCmd = cmdCompileToCpp if cCompiler == ccGcc: setCC("gcc") - wantMainModule() defineSymbol("cpp") commandCompileToC() of "objc", "compiletooc": - extccomp.cExt = ".m" gCmd = cmdCompileToOC - wantMainModule() defineSymbol("objc") commandCompileToC() of "run": gCmd = cmdRun - wantMainModule() when hasTinyCBackend: extccomp.setCC("tcc") commandCompileToC() @@ -330,41 +289,33 @@ proc mainCommand* = rawMessage(errInvalidCommandX, command) of "js", "compiletojs": gCmd = cmdCompileToJS - wantMainModule() commandCompileToJS() of "compiletollvm": gCmd = cmdCompileToLLVM - wantMainModule() when hasLLVM_Backend: CommandCompileToLLVM() else: rawMessage(errInvalidCommandX, command) - of "pretty": - gCmd = cmdPretty - wantMainModule() - commandPretty() of "doc": + wantMainModule() gCmd = cmdDoc loadConfigs(DocConfig) - wantMainModule() commandDoc() of "doc2": gCmd = cmdDoc loadConfigs(DocConfig) - wantMainModule() defineSymbol("nimdoc") commandDoc2() of "rst2html": gCmd = cmdRst2html loadConfigs(DocConfig) - wantMainModule() commandRst2Html() of "rst2tex": gCmd = cmdRst2tex loadConfigs(DocTexConfig) - wantMainModule() commandRst2TeX() of "jsondoc": + wantMainModule() gCmd = cmdDoc loadConfigs(DocConfig) wantMainModule() @@ -376,12 +327,11 @@ proc mainCommand* = commandBuildIndex() of "gendepend": gCmd = cmdGenDepend - wantMainModule() commandGenDepend() of "dump": gCmd = cmdDump if getConfigVar("dump.format") == "json": - requireMainModuleOption() + wantMainModule() var definedSymbols = newJArray() for s in definedSymbolNames(): definedSymbols.elems.add(%s) @@ -405,7 +355,6 @@ proc mainCommand* = for it in iterSearchPath(searchPaths): msgWriteln(it) of "check": gCmd = cmdCheck - wantMainModule() commandCheck() of "parse": gCmd = cmdParse @@ -429,7 +378,6 @@ proc mainCommand* = if gEvalExpr != "": commandEval(gEvalExpr) else: - wantMainModule() commandSuggest() of "serve": isServing = true diff --git a/compiler/modules.nim b/compiler/modules.nim index b102224cd..05b795473 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -117,7 +117,7 @@ proc newModule(fileIdx: int32): PSym = result.kind = skModule let filename = fileIdx.toFullPath result.name = getIdent(splitFile(filename).name) - if not isNimrodIdentifier(result.name.s): + if not isNimIdentifier(result.name.s): rawMessage(errInvalidModuleName, result.name.s) result.info = newLineInfo(fileIdx, 1, 1) @@ -177,7 +177,7 @@ proc includeModule*(s: PSym, fileIdx: int32): PNode {.procvar.} = proc `==^`(a, b: string): bool = try: result = sameFile(a, b) - except EOS: + except OSError: result = false proc compileSystemModule* = @@ -185,8 +185,18 @@ proc compileSystemModule* = systemFileIdx = fileInfoIdx(options.libpath/"system.nim") discard compileModule(systemFileIdx, {sfSystemModule}) -proc compileProject*(projectFile = gProjectMainIdx) = +proc wantMainModule* = + if gProjectFull.len == 0: + fatal(gCmdLineInfo, errCommandExpectsFilename) + gProjectMainIdx = addFileExt(gProjectFull, NimExt).fileInfoIdx + +passes.gIncludeFile = includeModule +passes.gImportModule = importModule + +proc compileProject*(projectFileIdx = -1'i32) = + wantMainModule() let systemFileIdx = fileInfoIdx(options.libpath / "system.nim") + let projectFile = if projectFileIdx < 0: gProjectMainIdx else: projectFileIdx if projectFile == systemFileIdx: discard compileModule(projectFile, {sfMainModule, sfSystemModule}) else: diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 730cb9605..7f4f81dd0 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -114,17 +114,18 @@ type warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, warnDeprecated, warnConfigDeprecated, warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, - warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored, + warnUnknownSubstitutionX, warnLanguageXNotSupported, + warnFieldXNotSupported, warnCommentXIgnored, warnNilStatement, warnAnalysisLoophole, warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, warnEachIdentIsTuple, warnShadowIdent, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, - warnUninit, warnGcMem, warnUser, + warnUninit, warnGcMem, warnLockLevel, warnUser, hintSuccess, hintSuccessX, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, - hintConditionAlwaysTrue, hintPattern, + hintConditionAlwaysTrue, hintName, hintPattern, hintUser const @@ -368,13 +369,14 @@ const warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", warnXIsNeverRead: "\'$1\' is never read [XIsNeverRead]", warnXmightNotBeenInit: "\'$1\' might not have been initialized [XmightNotBeenInit]", - warnDeprecated: "\'$1\' is deprecated [Deprecated]", + warnDeprecated: "$1 is deprecated [Deprecated]", warnConfigDeprecated: "config file '$1' is deprecated [ConfigDeprecated]", warnSmallLshouldNotBeUsed: "\'l\' should not be used as an identifier; may look like \'1\' (one) [SmallLshouldNotBeUsed]", warnUnknownMagic: "unknown magic \'$1\' might crash the compiler [UnknownMagic]", warnRedefinitionOfLabel: "redefinition of label \'$1\' [RedefinitionOfLabel]", warnUnknownSubstitutionX: "unknown substitution \'$1\' [UnknownSubstitutionX]", warnLanguageXNotSupported: "language \'$1\' not supported [LanguageXNotSupported]", + warnFieldXNotSupported: "field \'$1\' not supported [FieldXNotSupported]", warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]", warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead [NilStmt]", warnAnalysisLoophole: "thread analysis incomplete due to unknown call '$1' [AnalysisLoophole]", @@ -387,9 +389,10 @@ const warnProveField: "cannot prove that field '$1' is accessible [ProveField]", warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]", warnGcUnsafe: "not GC-safe: '$1' [GcUnsafe]", - warnGcUnsafe2: "cannot prove '$1' is GC-safe. This will become a compile time error in the future.", + warnGcUnsafe2: "cannot prove '$1' is GC-safe. Does not compile with --threads:on.", warnUninit: "'$1' might not have been initialized [Uninit]", warnGcMem: "'$1' uses GC'ed memory [GcMem]", + warnLockLevel: "$1 [LockLevel]", warnUser: "$1 [User]", hintSuccess: "operation successful [Success]", hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]", @@ -405,25 +408,27 @@ const hintConf: "used config file \'$1\' [Conf]", hintPath: "added path: '$1' [Path]", hintConditionAlwaysTrue: "condition is always true: '$1' [CondTrue]", + hintName: "name should be: '$1' [Name]", hintPattern: "$1 [Pattern]", hintUser: "$1 [User]"] const - WarningsToStr*: array[0..26, string] = ["CannotOpenFile", "OctalEscape", + WarningsToStr*: array[0..28, string] = ["CannotOpenFile", "OctalEscape", "XIsNeverRead", "XmightNotBeenInit", "Deprecated", "ConfigDeprecated", "SmallLshouldNotBeUsed", "UnknownMagic", - "RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported", + "RedefinitionOfLabel", "UnknownSubstitutionX", + "LanguageXNotSupported", "FieldXNotSupported", "CommentXIgnored", "NilStmt", "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap", "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", - "GcMem", "User"] + "GcMem", "LockLevel", "User"] - HintsToStr*: array[0..15, string] = ["Success", "SuccessX", "LineTooLong", + HintsToStr*: array[0..16, string] = ["Success", "SuccessX", "LineTooLong", "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", - "Path", "CondTrue", "Pattern", + "Path", "CondTrue", "Name", "Pattern", "User"] const @@ -441,11 +446,11 @@ type TNoteKinds* = set[TNoteKind] TFileInfo*{.final.} = object - fullPath*: string # This is a canonical full filesystem path + fullPath: string # This is a canonical full filesystem path projPath*: string # This is relative to the project's root shortName*: string # short name of the module quotedName*: PRope # cached quoted short name for codegen - # purpoes + # purposes lines*: seq[PRope] # the source code of the module # used for better error messages and @@ -468,8 +473,8 @@ type TErrorOutputs* = set[TErrorOutput] - ERecoverableError* = object of EInvalidValue - ESuggestDone* = object of E_Base + ERecoverableError* = object of ValueError + ESuggestDone* = object of Exception const InvalidFileIDX* = int32(-1) @@ -567,7 +572,7 @@ var gErrorMax*: int = 1 # stop after gErrorMax errors when useCaas: - var stdoutSocket*: TSocket + var stdoutSocket*: Socket proc unknownLineInfo*(): TLineInfo = result.line = int16(-1) @@ -784,6 +789,14 @@ proc writeSurroundingSrc(info: TLineInfo) = msgWriteln(indent & info.sourceLine.ropeToStr) msgWriteln(indent & repeatChar(info.col, ' ') & '^') +proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string = + let frmt = case msg + of warnMin..warnMax: PosWarningFormat + of hintMin..hintMax: PosHintFormat + else: PosErrorFormat + result = frmt % [toMsgFilename(info), coordToStr(info.line), + coordToStr(info.col), getMessageStr(msg, arg)] + proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, eh: TErrorHandling) = var frmt: string @@ -858,7 +871,7 @@ proc sourceLine*(i: TLineInfo): PRope = try: for line in lines(i.toFullPath): addSourceLine i.fileIndex, line.string - except EIO: + except IOError: discard internalAssert i.fileIndex < fileInfos.len # can happen if the error points to EOF: diff --git a/compiler/nimrod.ini b/compiler/nim.ini similarity index 85% rename from compiler/nimrod.ini rename to compiler/nim.ini index 7135b3490..576b6d2bb 100644 --- a/compiler/nimrod.ini +++ b/compiler/nim.ini @@ -2,7 +2,7 @@ ; and project. [Project] -Name: "Nimrod" +Name: "Nim" Version: "$version" Platforms: """ windows: i386;amd64 @@ -16,15 +16,15 @@ Platforms: """ """ Authors: "Andreas Rumpf" -Description: """This is the Nimrod Compiler. Nimrod is a new statically typed, +Description: """This is the Nim Compiler. Nim is a new statically typed, imperative programming language, that supports procedural, functional, object oriented and generic programming styles while remaining simple and efficient. -A special feature that Nimrod inherited from Lisp is that Nimrod's abstract +A special feature that Nim inherited from Lisp is that Nim's abstract syntax tree (AST) is part of the specification - this allows a powerful macro system which can be used to create domain specific languages. -Nimrod is a compiled, garbage-collected systems programming language -which has an excellent productivity/performance ratio. Nimrod's design +Nim is a compiled, garbage-collected systems programming language +which has an excellent productivity/performance ratio. Nim's design focuses on the 3E: efficiency, expressiveness, elegance (in the order of priority).""" @@ -32,7 +32,7 @@ App: Console License: "copying.txt" [Config] -Files: "config/nimrod.cfg" +Files: "config/nim.cfg" Files: "config/nimdoc.cfg" Files: "config/nimdoc.tex.cfg" @@ -61,8 +61,8 @@ Files: "icons/koch.res" Files: "icons/koch_icon.o" Files: "compiler/readme.txt" -Files: "compiler/nimrod.ini" -Files: "compiler/nimrod.cfg" +Files: "compiler/nim.ini" +Files: "compiler/nim.nimrod.cfg" Files: "compiler/*.nim" Files: "doc/*.txt" Files: "compiler/nimfix/*.nim" @@ -113,10 +113,9 @@ Files: "examples/*.tmpl" [Windows] -Files: "bin/nimrod.exe" -Files: "bin/nimrod_debug.exe" +Files: "bin/nim.exe" +Files: "bin/nim_debug.exe" Files: "bin/c2nim.exe" -Files: "bin/niminst.exe" Files: "bin/nimgrep.exe" Files: "dist/*.dll" @@ -132,7 +131,7 @@ Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia ; for now only NSIS supports optional downloads [UnixBin] -Files: "bin/nimrod" +Files: "bin/nim" [Unix] @@ -156,6 +155,5 @@ flags = "-w" [deb] buildDepends: "gcc (>= 4:4.3.2)" pkgDepends: "gcc (>= 4:4.3.2)" -shortDesc: "The Nimrod Compiler" -licenses: "bin/nimrod,MIT;lib/*,MIT;" - +shortDesc: "The Nim Compiler" +licenses: "bin/nim,MIT;lib/*,MIT;" diff --git a/compiler/nimrod.nim b/compiler/nim.nim similarity index 92% rename from compiler/nimrod.nim rename to compiler/nim.nim index 618d98698..a87e0a1ac 100644 --- a/compiler/nimrod.nim +++ b/compiler/nim.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -40,7 +40,7 @@ proc handleCmdLine() = if gProjectName != "": try: gProjectFull = canonicalizePath(gProjectName) - except EOS: + except OSError: gProjectFull = gProjectName var p = splitFile(gProjectFull) gProjectPath = p.dir @@ -58,7 +58,7 @@ proc handleCmdLine() = if msgs.gErrorCounter == 0: when hasTinyCBackend: if gCmd == cmdRun: - tccgen.run(service.arguments) + tccgen.run(commands.arguments) if optRun in gGlobalOptions: if gCmd == cmdCompileToJS: var ex: string @@ -67,7 +67,7 @@ proc handleCmdLine() = else: ex = quoteShell( completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir)) - execExternalProgram("node " & ex & ' ' & service.arguments) + execExternalProgram("node " & ex & ' ' & commands.arguments) else: var binPath: string if options.outFile.len > 0: @@ -77,7 +77,7 @@ proc handleCmdLine() = # Figure out ourselves a valid binary name. binPath = changeFileExt(gProjectFull, ExeExt).prependCurDir var ex = quoteShell(binPath) - execExternalProgram(ex & ' ' & service.arguments) + execExternalProgram(ex & ' ' & commands.arguments) when declared(GC_setMaxPause): GC_setMaxPause 2_000 diff --git a/compiler/nimrod.nimrod.cfg b/compiler/nim.nimrod.cfg similarity index 77% rename from compiler/nimrod.nimrod.cfg rename to compiler/nim.nimrod.cfg index 2c6e6f249..ba7697c4c 100644 --- a/compiler/nimrod.nimrod.cfg +++ b/compiler/nim.nimrod.cfg @@ -1,6 +1,4 @@ -# Special configuration file for the Nimrod project - -mainModule:"nimrod.nim" +# Special configuration file for the Nim project # gc:markAndSweep @@ -21,3 +19,4 @@ define:useStdoutAsStdmsg cs:partial #define:useNodeIds +symbol:nimfix diff --git a/compiler/babelcmd.nim b/compiler/nimblecmd.nim similarity index 82% rename from compiler/babelcmd.nim rename to compiler/nimblecmd.nim index 7fa233732..049b94aa9 100644 --- a/compiler/babelcmd.nim +++ b/compiler/nimblecmd.nim @@ -1,13 +1,13 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## Implements some helper procs for Babel (Nimrod's package manager) support. +## Implements some helper procs for Nimble (Nim's package manager) support. import parseutils, strutils, strtabs, os, options, msgs, lists @@ -43,7 +43,7 @@ proc `<.`(a, b: string): bool = if a[i] == '.': inc i if b[j] == '.': inc j -proc addPackage(packages: PStringTable, p: string) = +proc addPackage(packages: StringTableRef, p: string) = let x = versionSplitPos(p) let name = p.substr(0, x-1) if x < p.len: @@ -53,12 +53,12 @@ proc addPackage(packages: PStringTable, p: string) = else: packages[name] = latest -iterator chosen(packages: PStringTable): string = +iterator chosen(packages: StringTableRef): string = for key, val in pairs(packages): let res = if val == latest: key else: key & '-' & val yield res -proc addBabelPath(p: string, info: TLineInfo) = +proc addNimblePath(p: string, info: TLineInfo) = if not contains(options.searchPaths, p): if gVerbosity >= 1: message(info, hintPath, p) lists.prependStr(options.lazyPaths, p) @@ -70,10 +70,10 @@ proc addPathWithNimFiles(p: string, info: TLineInfo) = result = true break if hasNimFile(p): - addBabelPath(p, info) + addNimblePath(p, info) else: for kind, p2 in walkDir(p): - if hasNimFile(p2): addBabelPath(p2, info) + if hasNimFile(p2): addNimblePath(p2, info) proc addPathRec(dir: string, info: TLineInfo) = var packages = newStringTable(modeStyleInsensitive) @@ -83,8 +83,8 @@ proc addPathRec(dir: string, info: TLineInfo) = if k == pcDir and p[pos] != '.': addPackage(packages, p) for p in packages.chosen: - addBabelPath(p, info) + addNimblePath(p, info) -proc babelPath*(path: string, info: TLineInfo) = +proc nimblePath*(path: string, info: TLineInfo) = addPathRec(path, info) - addBabelPath(path, info) + addNimblePath(path, info) diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 136a0d454..bcf9b5359 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -14,7 +14,7 @@ import options, idents, wordrecg # ---------------- configuration file parser ----------------------------- -# we use Nimrod's scanner here to safe space and work +# we use Nim's scanner here to safe space and work proc ppGetTok(L: var TLexer, tok: var TToken) = # simple filter @@ -113,7 +113,7 @@ proc parseDirective(L: var TLexer, tok: var TToken) = case whichKeyword(tok.ident) of wIf: setLen(condStack, len(condStack) + 1) - var res = evalppIf(L, tok) + let res = evalppIf(L, tok) condStack[high(condStack)] = res if not res: jumpToDirective(L, tok, jdElseEndif) of wElif: doElif(L, tok) @@ -224,8 +224,10 @@ proc loadConfigs*(cfg: string) = if libpath == "": # choose default libpath: var prefix = getPrefixDir() - if prefix == "/usr": libpath = "/usr/lib/nimrod" - elif prefix == "/usr/local": libpath = "/usr/local/lib/nimrod" + when defined(posix): + if prefix == "/usr": libpath = "/usr/lib/nim" + elif prefix == "/usr/local": libpath = "/usr/local/lib/nim" + else: libpath = joinPath(prefix, "lib") else: libpath = joinPath(prefix, "lib") if optSkipConfigFile notin gGlobalOptions: @@ -244,5 +246,6 @@ proc loadConfigs*(cfg: string) = if gProjectName.len != 0: # new project wide config file: - readConfigFile(changeFileExt(gProjectFull, "nimrod.cfg")) - + let projectConfig = changeFileExt(gProjectFull, "nim.cfg") + if fileExists(projectConfig): readConfigFile(projectConfig) + else: readConfigFile(changeFileExt(gProjectFull, "nimrod.cfg")) diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index 0ee108d48..5693cbe91 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -1,13 +1,13 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## exposes the Nimrod VM to clients. +## exposes the Nim VM to clients. import ast, modules, passes, passaux, condsyms, diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim index e561b6d4d..918f58e3d 100644 --- a/compiler/nimfix/nimfix.nim +++ b/compiler/nimfix/nimfix.nim @@ -16,7 +16,7 @@ import options, commands, modules, sem, passes, passaux, pretty, msgs, nimconf, const Usage = """ Nimfix - Tool to patch Nim code Usage: - nimfix [options] projectflie.nim + nimfix [options] projectfile.nim Options: --overwriteFiles:on|off overwrite the original nim files. @@ -26,12 +26,13 @@ Options: --styleCheck:on|off|auto performs style checking for identifiers and suggests an alternative spelling; 'auto' corrects the spelling. + --bestEffort try to fix the code even when there + are errors. In addition, all command line options of Nim are supported. """ proc mainCommand = - #msgs.gErrorMax = high(int) # do not stop after first error registerPass verbosePass registerPass semPass gCmd = cmdPretty @@ -63,13 +64,14 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) = of "on": gCheckExtern = true of "off": gCheckExtern = false else: localError(gCmdLineInfo, errOnOrOffExpected) - of "stylecheck": + of "stylecheck": case p.val.normalize of "off": gStyleCheck = StyleCheck.None of "on": gStyleCheck = StyleCheck.Warn of "auto": gStyleCheck = StyleCheck.Auto else: localError(gCmdLineInfo, errOnOrOffExpected) of "wholeproject": gOnlyMainfile = false + of "besteffort": msgs.gErrorMax = high(int) # dont stop after first error else: processSwitch(pass, p) of cmdArgument: diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim index 8e0f5db6d..225b78479 100644 --- a/compiler/nimfix/prettybase.nim +++ b/compiler/nimfix/prettybase.nim @@ -34,7 +34,7 @@ proc loadFile*(info: TLineInfo) = for line in lines(path): gSourceFiles[i].lines.add(line) # extract line ending of the file: - var lex: TBaseLexer + var lex: BaseLexer open(lex, newFileStream(path, fmRead)) var pos = lex.bufpos while true: diff --git a/compiler/nimlexbase.nim b/compiler/nimlexbase.nim index 038573c35..e18e1c22a 100644 --- a/compiler/nimlexbase.nim +++ b/compiler/nimlexbase.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -37,7 +37,7 @@ const NewLines* = {CR, LF} type - TBaseLexer* = object of TObject + TBaseLexer* = object of RootObj bufpos*: int buf*: cstring bufLen*: int # length of buffer in characters diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim index d65618e0a..aa7686d30 100644 --- a/compiler/nimsets.nim +++ b/compiler/nimsets.nim @@ -1,13 +1,13 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -# this unit handles Nimrod sets; it implements symbolic sets +# this unit handles Nim sets; it implements symbolic sets import ast, astalgo, trees, nversion, msgs, platform, bitsets, types, renderer diff --git a/compiler/nversion.nim b/compiler/nversion.nim index bc69f5a85..8dc4b90b2 100644 --- a/compiler/nversion.nim +++ b/compiler/nversion.nim @@ -1,21 +1,17 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -# This module contains Nimrod's version. It is the only place where it needs +# This module contains Nim's version. It is the only place where it needs # to be changed. const MaxSetElements* = 1 shl 16 # (2^16) to support unicode character sets? - VersionMajor* = 0 - VersionMinor* = 9 - VersionPatch* = 7 - VersionAsString* = $VersionMajor & "." & $VersionMinor & "." & $VersionPatch - + VersionAsString* = system.NimVersion RodFileVersion* = "1215" # modify this if the rod-format changes! diff --git a/compiler/options.nim b/compiler/options.nim index 02719cacc..415ac8430 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -68,7 +68,7 @@ type # please make sure we have under 32 options # also: generate header file TGlobalOptions* = set[TGlobalOption] - TCommands* = enum # Nimrod's commands + TCommands* = enum # Nim's commands # **keep binary compatible** cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS, cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc, @@ -114,7 +114,8 @@ var gDirtyBufferIdx* = 0'i32 # indicates the fileIdx of the dirty version of # the tracked source X, saved by the CAAS client. gDirtyOriginalIdx* = 0'i32 # the original source file of the dirtified buffer. - gNoBabelPath* = false + gNoNimblePath* = false + gExperimentalMode*: bool proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools} proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc @@ -139,7 +140,7 @@ const JsonExt* = "json" TexExt* = "tex" IniExt* = "ini" - DefaultConfig* = "nimrod.cfg" + DefaultConfig* = "nim.cfg" DocConfig* = "nimdoc.cfg" DocTexConfig* = "nimdoc.tex.cfg" @@ -152,7 +153,6 @@ var gProjectPath* = "" # holds a path like /home/alice/projects/nimrod/compiler/ gProjectFull* = "" # projectPath/projectName gProjectMainIdx*: int32 # the canonical path id of the main module - optMainModule* = "" # the main module that should be used for idetools commands nimcacheDir* = "" command* = "" # the main command (e.g. cc, check, scan, etc) commandArgs*: seq[string] = @[] # any arguments after the main command @@ -189,8 +189,8 @@ proc getPrefixDir*(): string = result = splitPath(getAppDir()).head proc canonicalizePath*(path: string): string = - result = path.expandFilename - when not FileSystemCaseSensitive: result = result.toLower + when not FileSystemCaseSensitive: result = path.expandFilename.toLower + else: result = path.expandFilename proc shortenDir*(dir: string): string = ## returns the interesting part of a dir @@ -238,6 +238,9 @@ proc getPackageName*(path: string): string = #echo "from cache ", d, " |", packageCache[d], "|", path.splitFile.name return packageCache[d] inc parents + for file in walkFiles(d / "*.nimble"): + result = file.splitFile.name + break packageSearch for file in walkFiles(d / "*.babel"): result = file.splitFile.name break packageSearch @@ -295,7 +298,7 @@ proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string = createDir(subdir) when noTimeMachine: excludeDirFromTimeMachine(subdir) - except EOS: + except OSError: writeln(stdout, "cannot create directory: " & subdir) quit(1) result = joinPath(subdir, tail) @@ -335,6 +338,16 @@ proc findFile*(f: string): string {.procvar.} = proc findModule*(modulename, currentModule: string): string = # returns path to module + when defined(nimfix): + # '.nimfix' modules are preferred over '.nim' modules so that specialized + # versions can be kept for 'nimfix'. + block: + let m = addFileExt(modulename, "nimfix") + let currentPath = currentModule.splitFile.dir + result = currentPath / m + if not existsFile(result): + result = findFile(m) + if existsFile(result): return result let m = addFileExt(modulename, NimExt) let currentPath = currentModule.splitFile.dir result = currentPath / m diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index bbdba8c22..300abea1e 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/compiler/parser.nim b/compiler/parser.nim index 6ff0c2dfc..fab3c453b 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1,13 +1,13 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -# This module implements the parser of the standard Nimrod syntax. +# This module implements the parser of the standard Nim syntax. # The parser strictly reflects the grammar ("doc/grammar.txt"); however # it uses several helper routines to keep the parser small. A special # efficient algorithm is used for the precedence levels. The parser here can @@ -41,8 +41,7 @@ type proc parseAll*(p: var TParser): PNode proc closeParser*(p: var TParser) proc parseTopLevelStmt*(p: var TParser): PNode -proc parseString*(s: string, filename: string = "", line: int = 0): PNode - + # helpers for the other parsers proc isOperator*(tok: TToken): bool proc getTok*(p: var TParser) @@ -84,7 +83,7 @@ proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream, proc openParser*(p: var TParser, filename: string, inputStream: PLLStream, strongSpaces=false) = - openParser(p, filename.fileInfoIdx, inputstream, strongSpaces) + openParser(p, filename.fileInfoIdx, inputStream, strongSpaces) proc closeParser(p: var TParser) = ## Close a parser, freeing up its resources. @@ -96,7 +95,7 @@ proc parMessage(p: TParser, msg: TMsgKind, arg = "") = proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) = ## Produce and emit a parser message to output about the token `tok` - lexMessage(p.lex, msg, prettyTok(tok)) + parMessage(p, msg, prettyTok(tok)) template withInd(p: expr, body: stmt) {.immediate.} = let oldInd = p.currInd @@ -189,18 +188,13 @@ proc parseTypeDesc(p: var TParser): PNode proc parseDoBlocks(p: var TParser, call: PNode) proc parseParamList(p: var TParser, retColon = true): PNode -proc relevantOprChar(ident: PIdent): char {.inline.} = - result = ident.s[0] - var L = ident.s.len - if result == '\\' and L > 1: - result = ident.s[1] - proc isSigilLike(tok: TToken): bool {.inline.} = - result = tok.tokType == tkOpr and relevantOprChar(tok.ident) == '@' + result = tok.tokType == tkOpr and tok.ident.s[0] == '@' -proc isLeftAssociative(tok: TToken): bool {.inline.} = - ## Determines whether the token is left assocative. - result = tok.tokType != tkOpr or relevantOprChar(tok.ident) != '^' +proc isRightAssociative(tok: TToken): bool {.inline.} = + ## Determines whether the token is right assocative. + result = tok.tokType == tkOpr and (tok.ident.s[0] == '^' or + (let L = tok.ident.s.len; L > 1 and tok.ident.s[L-1] == '>')) proc getPrecedence(tok: TToken, strongSpaces: bool): int = ## Calculates the precedence of the given token. @@ -210,7 +204,10 @@ proc getPrecedence(tok: TToken, strongSpaces: bool): int = case tok.tokType of tkOpr: let L = tok.ident.s.len - let relevantChar = relevantOprChar(tok.ident) + let relevantChar = tok.ident.s[0] + + # arrow like? + if L > 1 and tok.ident.s[L-1] == '>': return considerStrongSpaces(1) template considerAsgn(value: expr) = result = if tok.ident.s[L-1] == '=': 1 else: considerStrongSpaces(value) @@ -270,17 +267,18 @@ proc checkBinary(p: TParser) {.inline.} = #| #| optInd = COMMENT? #| optPar = (IND{>} | IND{=})? -#| -#| simpleExpr = assignExpr (OP0 optInd assignExpr)* -#| assignExpr = orExpr (OP1 optInd orExpr)* -#| orExpr = andExpr (OP2 optInd andExpr)* -#| andExpr = cmpExpr (OP3 optInd cmpExpr)* -#| cmpExpr = sliceExpr (OP4 optInd sliceExpr)* -#| sliceExpr = ampExpr (OP5 optInd ampExpr)* -#| ampExpr = plusExpr (OP6 optInd plusExpr)* -#| plusExpr = mulExpr (OP7 optInd mulExpr)* -#| mulExpr = dollarExpr (OP8 optInd dollarExpr)* -#| dollarExpr = primary (OP9 optInd primary)* +#| +#| simpleExpr = arrowExpr (OP0 optInd arrowExpr)* +#| arrowExpr = assignExpr (OP1 optInd assignExpr)* +#| assignExpr = orExpr (OP2 optInd orExpr)* +#| orExpr = andExpr (OP3 optInd andExpr)* +#| andExpr = cmpExpr (OP4 optInd cmpExpr)* +#| cmpExpr = sliceExpr (OP5 optInd sliceExpr)* +#| sliceExpr = ampExpr (OP6 optInd ampExpr)* +#| ampExpr = plusExpr (OP7 optInd plusExpr)* +#| plusExpr = mulExpr (OP8 optInd mulExpr)* +#| mulExpr = dollarExpr (OP9 optInd dollarExpr)* +#| dollarExpr = primary (OP10 optInd primary)* proc colcom(p: var TParser, n: PNode) = eat(p, tkColon) @@ -507,7 +505,7 @@ proc parsePar(p: var TParser): PNode = getTok(p) optInd(p, result) if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase, - tkTry, tkFinally, tkExcept, tkFor, tkBlock, + tkTry, tkDefer, tkFinally, tkExcept, tkFor, tkBlock, tkConst, tkLet, tkWhen, tkVar, tkMixin}: # XXX 'bind' used to be an expression, so we exclude it here; @@ -735,7 +733,7 @@ proc parseOperators(p: var TParser, headNode: PNode, # the operator itself must not start on a new line: while opPrec >= limit and p.tok.indent < 0 and not isUnary(p): checkBinary(p) - var leftAssoc = ord(isLeftAssociative(p.tok)) + var leftAssoc = 1-ord(isRightAssociative(p.tok)) var a = newNodeP(nkInfix, p) var opNode = newIdentNodeP(p.tok.ident, p) # skip operator: getTok(p) @@ -896,7 +894,8 @@ proc parseParamList(p: var TParser, retColon = true): PNode = var a: PNode result = newNodeP(nkFormalParams, p) addSon(result, ast.emptyNode) # return type - if p.tok.tokType == tkParLe and p.tok.indent < 0: + let hasParLe = p.tok.tokType == tkParLe and p.tok.indent < 0 + if hasParLe: getTok(p) optInd(p, result) while true: @@ -920,6 +919,9 @@ proc parseParamList(p: var TParser, retColon = true): PNode = getTok(p) optInd(p, result) result.sons[0] = parseTypeDesc(p) + elif not retColon and not hasParle: + # Mark as "not there" in order to mark for deprecation in the semantic pass: + result = ast.emptyNode proc optPragmas(p: var TParser): PNode = if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)): @@ -1132,7 +1134,7 @@ proc parseMacroColon(p: var TParser, x: PNode): PNode = skipComment(p, result) if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}: let body = parseStmt(p) - addSon(result, newProcNode(nkDo, body.info, body)) + addSon(result, makeStmtList(body)) while sameInd(p): var b: PNode case p.tok.tokType @@ -1424,9 +1426,10 @@ proc parseBlock(p: var TParser): PNode = colcom(p, result) addSon(result, parseStmt(p)) -proc parseStatic(p: var TParser): PNode = +proc parseStaticOrDefer(p: var TParser; k: TNodeKind): PNode = #| staticStmt = 'static' colcom stmt - result = newNodeP(nkStaticStmt, p) + #| deferStmt = 'defer' colcom stmt + result = newNodeP(k, p) getTokNoInd(p) colcom(p, result) addSon(result, parseStmt(p)) @@ -1864,7 +1867,7 @@ proc simpleStmt(p: var TParser): PNode = proc complexOrSimpleStmt(p: var TParser): PNode = #| complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt #| | tryStmt | finallyStmt | exceptStmt | forStmt - #| | blockStmt | staticStmt | asmStmt + #| | blockStmt | staticStmt | deferStmt | asmStmt #| | 'proc' routine #| | 'method' routine #| | 'iterator' routine @@ -1885,7 +1888,8 @@ proc complexOrSimpleStmt(p: var TParser): PNode = of tkExcept: result = parseExceptBlock(p, nkExceptBranch) of tkFor: result = parseFor(p) of tkBlock: result = parseBlock(p) - of tkStatic: result = parseStatic(p) + of tkStatic: result = parseStaticOrDefer(p, nkStaticStmt) + of tkDefer: result = parseStaticOrDefer(p, nkDefer) of tkAsm: result = parseAsm(p) of tkProc: result = parseRoutine(p, nkProcDef) of tkMethod: result = parseRoutine(p, nkMethodDef) @@ -1980,22 +1984,25 @@ proc parseTopLevelStmt(p: var TParser): PNode = ## top-level statement or emptyNode if end of stream. result = ast.emptyNode while true: - if p.tok.indent != 0: + if p.tok.indent != 0: if p.firstTok and p.tok.indent < 0: discard - else: parMessage(p, errInvalidIndentation) + elif p.tok.tokType != tkSemiColon: + parMessage(p, errInvalidIndentation) p.firstTok = false case p.tok.tokType of tkSemiColon: getTok(p) if p.tok.indent <= 0: discard else: parMessage(p, errInvalidIndentation) + p.firstTok = true of tkEof: break else: result = complexOrSimpleStmt(p) if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) break -proc parseString(s: string, filename: string = "", line: int = 0): PNode = +proc parseString*(s: string; filename: string = ""; line: int = 0; + errorHandler: TErrorHandler = nil): PNode = ## Parses a string into an AST, returning the top node. ## `filename` and `line`, although optional, provide info so that the ## compiler can generate correct error messages referring to the original @@ -2007,6 +2014,7 @@ proc parseString(s: string, filename: string = "", line: int = 0): PNode = # XXX for now the builtin 'parseStmt/Expr' functions do not know about strong # spaces... openParser(parser, filename, stream, false) + parser.lex.errorHandler = errorHandler result = parser.parseAll closeParser(parser) diff --git a/compiler/passaux.nim b/compiler/passaux.nim index 0ba9f22d0..f754c80e5 100644 --- a/compiler/passaux.nim +++ b/compiler/passaux.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/compiler/passes.nim b/compiler/passes.nim index 66a1a4954..a63e29b35 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -16,7 +16,7 @@ import nimsets, syntaxes, times, rodread, idgen type - TPassContext* = object of TObject # the pass's context + TPassContext* = object of RootObj # the pass's context fromCache*: bool # true if created by "openCached" PPassContext* = ref TPassContext diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 5e21289b5..9ac0988c5 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -260,7 +260,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode = # couldn't bind parameter: if isNil(x): return nil result.add(x) - if requiresAA: addToArgList(args, n) + if requiresAA: addToArgList(args, x) # perform alias analysis here: if requiresAA: for i in 1 .. < params.len: diff --git a/compiler/pbraces.nim b/compiler/pbraces.nim index ce6e0d9a9..cf4dbffc5 100644 --- a/compiler/pbraces.nim +++ b/compiler/pbraces.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/compiler/platform.nim b/compiler/platform.nim index 2e78d4fc5..c6cb3d244 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index d73494c6e..8e639f083 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -45,7 +45,7 @@ const wBreakpoint, wWatchPoint, wPassl, wPassc, wDeadCodeElim, wDeprecated, wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, wLinearScanEnd, wPatterns, wEffects, wNoForward, wComputedGoto, - wInjectStmt, wDeprecated} + wInjectStmt, wDeprecated, wExperimental} lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader, wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, @@ -71,7 +71,7 @@ const proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) # implementation -proc invalidPragma(n: PNode) = +proc invalidPragma(n: PNode) = localError(n.info, errInvalidPragmaX, renderTree(n, {renderNoComments})) proc pragmaAsm*(c: PContext, n: PNode): char = @@ -130,6 +130,7 @@ proc processImportCpp(s: PSym, extname: string) = excl(s.flags, sfForward) let m = s.getModule() incl(m.flags, sfCompileToCpp) + extccomp.gMixedMode = true proc processImportObjC(s: PSym, extname: string) = setExternName(s, extname) @@ -356,11 +357,11 @@ proc processPush(c: PContext, n: PNode, start: int) = append(c.optionStack, x) for i in countup(start, sonsLen(n) - 1): if processOption(c, n.sons[i]): - # simply store it somehwere: + # simply store it somewhere: if x.otherPragmas.isNil: x.otherPragmas = newNodeI(nkPragma, n.info) x.otherPragmas.add n.sons[i] - #LocalError(n.info, errOptionExpected) + #localError(n.info, errOptionExpected) proc processPop(c: PContext, n: PNode) = if c.optionStack.counter <= 1: @@ -447,6 +448,9 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode = addSon(result, newSymNode(e)) else: addSon(result, newStrNode(nkStrLit, sub)) + else: + # an empty '``' produces a single '`' + addSon(result, newStrNode(nkStrLit, $marker)) if c < 0: break a = c + 1 else: illFormedAst(n) @@ -518,6 +522,28 @@ proc pragmaRaisesOrTags(c: PContext, n: PNode) = else: invalidPragma(n) +proc pragmaLockStmt(c: PContext; it: PNode) = + if it.kind != nkExprColonExpr: + invalidPragma(it) + else: + let n = it[1] + if n.kind != nkBracket: + localError(n.info, errGenerated, "locks pragma takes a list of expressions") + else: + for i in 0 .. MaxLockLevel: + localError(it[1].info, "integer must be within 0.." & $MaxLockLevel) + else: + result = TLockLevel(x) + proc typeBorrow(sym: PSym, n: PNode) = if n.kind == nkExprColonExpr: let it = n.sons[1] @@ -575,6 +601,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, if c.instCounter > 100: globalError(it.info, errRecursiveDependencyX, userPragma.name.s) pragma(c, sym, userPragma.ast, validPragmas) + # ensure the pragma is also remember for generic instantiations in other + # modules: + n.sons[i] = userPragma.ast dec c.instCounter else: var k = whichKeyword(key.ident) @@ -807,6 +836,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, if sym == nil: invalidPragma(it) of wLine: pragmaLine(c, it) of wRaises, wTags: pragmaRaisesOrTags(c, it) + of wLocks: + if sym == nil: pragmaLockStmt(c, it) + elif sym.typ == nil: invalidPragma(it) + else: sym.typ.lockLevel = pragmaLocks(c, it) of wGuard: if sym == nil or sym.kind notin {skVar, skLet, skField}: invalidPragma(it) @@ -817,6 +850,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, localError(it.info, errExprExpected) else: it.sons[1] = c.semExpr(c, it.sons[1]) + of wExperimental: + noVal(it) + if isTopLevel(c): + c.module.flags.incl sfExperimental + else: + localError(it.info, "'experimental' pragma only valid as toplevel statement") else: invalidPragma(it) else: invalidPragma(it) else: processNote(c, it) @@ -853,8 +892,13 @@ proc hasPragma*(n: PNode, pragma: TSpecialWord): bool = return false -proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = +proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = if n == nil: return for i in countup(0, sonsLen(n) - 1): - if singlePragma(c, sym, n, i, validPragmas): break + if n.sons[i].kind == nkPragma: pragmaRec(c, sym, n.sons[i], validPragmas) + elif singlePragma(c, sym, n, i, validPragmas): break + +proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = + if n == nil: return + pragmaRec(c, sym, n, validPragmas) implicitPragmas(c, sym, n, validPragmas) diff --git a/compiler/pretty.nim b/compiler/pretty.nim deleted file mode 100644 index 17311f9e6..000000000 --- a/compiler/pretty.nim +++ /dev/null @@ -1,281 +0,0 @@ -# -# -# The Nimrod Compiler -# (c) Copyright 2014 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module implements the code "prettifier". This is part of the toolchain -## to convert Nimrod code into a consistent style. - -import - strutils, os, options, ast, astalgo, msgs, ropes, idents, passes, - intsets, strtabs, semdata - -const - removeTP = false # when true, "nimrod pretty" converts TTyp to Typ. - -type - TGen = object of TPassContext - module*: PSym - PGen = ref TGen - - TSourceFile = object - lines: seq[string] - dirty: bool - fullpath: string - -var - gSourceFiles: seq[TSourceFile] = @[] - gCheckExtern: bool - rules: PStringTable - -proc loadFile(info: TLineInfo) = - let i = info.fileIndex - if i >= gSourceFiles.len: - gSourceFiles.setLen(i+1) - if gSourceFiles[i].lines.isNil: - gSourceFiles[i].lines = @[] - let path = info.toFullPath - gSourceFiles[i].fullpath = path - # we want to die here for EIO: - for line in lines(path): - gSourceFiles[i].lines.add(line) - -proc overwriteFiles*() = - let overWrite = options.getConfigVar("pretty.overwrite").normalize == "on" - let doStrip = options.getConfigVar("pretty.strip").normalize == "on" - for i in 0 .. high(gSourceFiles): - if not gSourceFiles[i].dirty: continue - let newFile = if overWrite: gSourceFiles[i].fullpath - else: gSourceFiles[i].fullpath.changeFileExt(".pretty.nim") - try: - var f = open(newFile, fmWrite) - for line in gSourceFiles[i].lines: - if doStrip: - f.write line.strip(leading = false, trailing = true) - else: - f.write line - f.write("\L") - f.close - except EIO: - rawMessage(errCannotOpenFile, newFile) - -proc `=~`(s: string, a: openArray[string]): bool = - for x in a: - if s.startsWith(x): return true - -proc beautifyName(s: string, k: TSymKind): string = - # minimal set of rules here for transition: - # GC_ is allowed - - let allUpper = allCharsInSet(s, {'A'..'Z', '0'..'9', '_'}) - if allUpper and k in {skConst, skEnumField, skType}: return s - result = newStringOfCap(s.len) - var i = 0 - case k - of skType, skGenericParam: - # Types should start with a capital unless builtins like 'int' etc.: - when removeTP: - if s[0] == 'T' and s[1] in {'A'..'Z'}: - i = 1 - if s =~ ["int", "uint", "cint", "cuint", "clong", "cstring", "string", - "char", "byte", "bool", "openArray", "seq", "array", "void", - "pointer", "float", "csize", "cdouble", "cchar", "cschar", - "cshort", "cu", "nil", "expr", "stmt", "typedesc", "auto", "any", - "range", "openarray", "varargs", "set", "cfloat" - ]: - result.add s[i] - else: - result.add toUpper(s[i]) - of skConst, skEnumField: - # for 'const' we keep how it's spelt; either upper case or lower case: - result.add s[0] - else: - # as a special rule, don't transform 'L' to 'l' - if s.len == 1 and s[0] == 'L': result.add 'L' - elif '_' in s: result.add(s[i]) - else: result.add toLower(s[0]) - inc i - while i < s.len: - if s[i] == '_': - if i > 0 and s[i-1] in {'A'..'Z'}: - # don't skip '_' as it's essential for e.g. 'GC_disable' - result.add('_') - inc i - result.add s[i] - else: - inc i - result.add toUpper(s[i]) - elif allUpper: - result.add toLower(s[i]) - else: - result.add s[i] - inc i - -proc checkStyle*(info: TLineInfo, s: string, k: TSymKind) = - let beau = beautifyName(s, k) - if s != beau: - message(info, errGenerated, "name should be: " & beau) - -const - Letters = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'} - -proc identLen(line: string, start: int): int = - while start+result < line.len and line[start+result] in Letters: - inc result - -proc differ(line: string, a, b: int, x: string): bool = - let y = line[a..b] - result = cmpIgnoreStyle(y, x) == 0 and y != x - when false: - var j = 0 - for i in a..b: - if line[i] != x[j]: return true - inc j - return false - -proc checkDef*(n: PNode; s: PSym) = - # operators stay as they are: - if s.kind in {skResult, skTemp} or s.name.s[0] notin Letters: return - if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return - - if {sfImportc, sfExportc} * s.flags == {} or gCheckExtern: - checkStyle(n.info, s.name.s, s.kind) - -proc checkDef(c: PGen; n: PNode) = - if n.kind != nkSym: return - checkDef(n, n.sym) - -proc checkUse*(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 Letters: return - if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return - let newName = s.name.s - - loadFile(info) - - let line = gSourceFiles[info.fileIndex].lines[info.line-1] - var first = min(info.col.int, line.len) - if first < 0: return - #inc first, skipIgnoreCase(line, "proc ", first) - 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 differ(line, first, last, newName): - # last-first+1 != newName.len or - var x = line.substr(0, first-1) & newName & line.substr(last+1) - when removeTP: - # the WinAPI module is full of 'TX = X' which after the substitution - # becomes 'X = X'. We remove those lines: - if x.match(peg"\s* {\ident} \s* '=' \s* y$1 ('#' .*)?"): - x = "" - - system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) - gSourceFiles[info.fileIndex].dirty = true - -when false: - var cannotRename = initIntSet() - - proc beautifyName(s: string, k: TSymKind): string = - let allUpper = allCharsInSet(s, {'A'..'Z', '0'..'9', '_'}) - result = newStringOfCap(s.len) - var i = 0 - case k - of skType, skGenericParam: - # skip leading 'T' - when removeTP: - if s[0] == 'T' and s[1] in {'A'..'Z'}: - i = 1 - if s =~ ["int", "uint", "cint", "cuint", "clong", "cstring", "string", - "char", "byte", "bool", "openArray", "seq", "array", "void", - "pointer", "float", "csize", "cdouble", "cchar", "cschar", - "cshort", "cu"]: - result.add s[i] - else: - result.add toUpper(s[i]) - of skConst, skEnumField: - # for 'const' we keep how it's spelt; either upper case or lower case: - result.add s[0] - else: - # as a special rule, don't transform 'L' to 'l' - if s.len == 1 and s[0] == 'L': result.add 'L' - else: result.add toLower(s[0]) - inc i - while i < s.len: - if s[i] == '_': - inc i - result.add toUpper(s[i]) - elif allUpper: - result.add toLower(s[i]) - else: - result.add s[i] - inc i - -proc check(c: PGen, n: PNode) = - case n.kind - of nkSym: checkUse(n.info, n.sym) - of nkBlockStmt, nkBlockExpr, nkBlockType: - checkDef(c, n[0]) - check(c, n.sons[1]) - of nkForStmt, nkParForStmt: - let L = n.len - for i in countup(0, L-3): - checkDef(c, n[i]) - check(c, n[L-2]) - check(c, n[L-1]) - of nkProcDef, nkLambdaKinds, nkMethodDef, nkIteratorDef, nkTemplateDef, - nkMacroDef, nkConverterDef: - checkDef(c, n[namePos]) - for i in namePos+1 .. 0: - let colon = line.find(':') - if colon > 0: - rules[line.substr(0, colon-1)] = line.substr(colon+1) - else: - rules[line] = line - -const prettyPass* = makePass(open = myOpen, process = processSym) - diff --git a/compiler/procfind.nim b/compiler/procfind.nim index 9f52cc117..473965a3d 100644 --- a/compiler/procfind.nim +++ b/compiler/procfind.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -11,7 +11,7 @@ # This is needed for proper handling of forward declarations. import - ast, astalgo, msgs, semdata, types, trees + ast, astalgo, msgs, semdata, types, trees, strutils proc equalGenericParams(procA, procB: PNode): bool = if sonsLen(procA) != sonsLen(procB): return @@ -68,11 +68,17 @@ proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym = ExactConstraints, IgnoreCC} var it: TIdentIter + result = initIdentIter(it, scope.symbols, fn.name) while result != nil: if result.kind in skProcKinds and sameType(result.typ, fn.typ, flags): case equalParams(result.typ.n, fn.typ.n) of paramsEqual: + if (sfExported notin result.flags) and (sfExported in fn.flags): + let message = ("public implementation '$1' has non-public " & + "forward declaration in $2") % + [getProcHeader(result), $result.info] + localError(fn.info, errGenerated, message) return of paramsIncompatible: localError(fn.info, errNotOverloadable, fn.name.s) diff --git a/compiler/readme.txt b/compiler/readme.txt index 3d3cf4b29..c4934e031 100644 --- a/compiler/readme.txt +++ b/compiler/readme.txt @@ -1,4 +1,4 @@ -This directory contains the Nimrod compiler written in Nimrod. Note that this +This directory contains the Nim compiler written in Nim. Note that this code has been translated from a bootstrapping version written in Pascal, so -the code is **not** a poster child of good Nimrod code. +the code is **not** a poster child of good Nim code. diff --git a/compiler/renderer.nim b/compiler/renderer.nim index c97b2f321..2bbe8ac80 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1,13 +1,13 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -# This module implements the renderer of the standard Nimrod representation. +# This module implements the renderer of the standard Nim representation. import lexer, options, idents, strutils, ast, msgs, lists @@ -34,6 +34,7 @@ type comStack*: seq[PNode] # comment stack flags*: TRenderFlags checkAnon: bool # we're in a context that can contain sfAnon + inPragma: int proc renderModule*(n: PNode, filename: string, renderFlags: TRenderFlags = {}) @@ -620,7 +621,7 @@ proc gpattern(g: var TSrcGen, n: PNode) = if longMode(n) or (lsub(n.sons[0]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments - gstmts(g, n.sons[0], c) + gstmts(g, n, c) put(g, tkCurlyRi, "}") proc gpragmaBlock(g: var TSrcGen, n: PNode) = @@ -894,8 +895,15 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkParLe, "(") for i in countup(0, sonsLen(n) - 1): if i > 0: put(g, tkOpr, "|") - gsub(g, n.sons[i], c) - put(g, tkParRi, ")") + if n.sons[i].kind == nkSym: + let s = n[i].sym + if s.owner != nil: + put g, tkSymbol, n[i].sym.owner.name.s + put g, tkOpr, "." + put g, tkSymbol, n[i].sym.name.s + else: + gsub(g, n.sons[i], c) + put(g, tkParRi, if n.kind == nkOpenSymChoice: "|...)" else: ")") of nkPar, nkClosure: put(g, tkParLe, "(") gcomma(g, n, c) @@ -940,10 +948,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkConstDef, nkIdentDefs: gcomma(g, n, 0, -3) var L = sonsLen(n) - if n.sons[L - 2].kind != nkEmpty: + if L >= 2 and n.sons[L - 2].kind != nkEmpty: putWithSpace(g, tkColon, ":") gsub(g, n.sons[L - 2]) - if n.sons[L - 1].kind != nkEmpty: + if L >= 1 and n.sons[L - 1].kind != nkEmpty: put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") gsub(g, n.sons[L - 1], c) @@ -1009,9 +1017,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkElse, " else") putWithSpace(g, tkColon, ":") gsub(g, n.sons[0]) - of nkTypeOfExpr: + of nkTypeOfExpr: putWithSpace(g, tkType, "type") - gsub(g, n.sons[0]) + if n.len > 0: gsub(g, n.sons[0]) of nkRefTy: if sonsLen(n) > 0: putWithSpace(g, tkRef, "ref") @@ -1177,12 +1185,17 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkContinueStmt: putWithSpace(g, tkContinue, "continue") gsub(g, n.sons[0]) - of nkPragma: + of nkPragma: if renderNoPragmas notin g.flags: - put(g, tkSpaces, Space) - put(g, tkCurlyDotLe, "{.") - gcomma(g, n, emptyContext) - put(g, tkCurlyDotRi, ".}") + if g.inPragma <= 0: + inc g.inPragma + put(g, tkSpaces, Space) + put(g, tkCurlyDotLe, "{.") + gcomma(g, n, emptyContext) + put(g, tkCurlyDotRi, ".}") + dec g.inPragma + else: + gcomma(g, n, emptyContext) of nkImportStmt, nkExportStmt: if n.kind == nkImportStmt: putWithSpace(g, tkImport, "import") @@ -1306,7 +1319,7 @@ proc renderTree(n: PNode, renderFlags: TRenderFlags = {}): string = proc renderModule(n: PNode, filename: string, renderFlags: TRenderFlags = {}) = var - f: TFile + f: File g: TSrcGen initSrcGen(g, renderFlags) for i in countup(0, sonsLen(n) - 1): diff --git a/compiler/rodread.nim b/compiler/rodread.nim index 036e6cc3c..3b3538e5d 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -121,7 +121,7 @@ type r*: string # writers use this offset*: int # readers use this - TRodReader* = object of TObject + TRodReader* = object of RootObj pos: int # position; used for parsing s: cstring # mmap'ed file contents options: TOptions @@ -137,7 +137,7 @@ type line: int # only used for debugging, but is always in the code moduleID: int syms: TIdTable # already processed symbols - memfile: TMemFile # unfortunately there is no point in time where we + memfile: MemFile # unfortunately there is no point in time where we # can close this! XXX methods*: TSymSeq origFile: string @@ -280,11 +280,6 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) = loc.r = toRope(decodeStr(r.s, r.pos)) else: loc.r = nil - if r.s[r.pos] == '?': - inc(r.pos) - loc.a = decodeVInt(r.s, r.pos) - else: - loc.a = 0 if r.s[r.pos] == '>': inc(r.pos) else: internalError(info, "decodeLoc " & r.s[r.pos]) @@ -326,7 +321,7 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType = result.size = - 1 if r.s[r.pos] == '=': inc(r.pos) - result.align = decodeVInt(r.s, r.pos) + result.align = decodeVInt(r.s, r.pos).int16 else: result.align = 2 decodeLoc(r, result.loc, info) @@ -503,7 +498,7 @@ proc processCompilerProcs(r: PRodReader, module: PSym) = idTablePut(r.syms, s, s) strTableAdd(rodCompilerprocs, s) -proc processIndex(r: PRodReader; idx: var TIndex; outf: TFile = nil) = +proc processIndex(r: PRodReader; idx: var TIndex; outf: File = nil) = var key, val, tmp: int inc(r.pos, 2) # skip "(\10" inc(r.line) @@ -659,7 +654,7 @@ proc newRodReader(modfilename: string, crc: TCrc32, new(result) try: result.memfile = memfiles.open(modfilename) - except EOS: + except OSError: return nil result.files = @[] result.modDeps = @[] @@ -916,7 +911,7 @@ initIdTable(gTypeTable) initStrTable(rodCompilerprocs) # viewer: -proc writeNode(f: TFile; n: PNode) = +proc writeNode(f: File; n: PNode) = f.write("(") if n != nil: f.write($n.kind) @@ -947,7 +942,7 @@ proc writeNode(f: TFile; n: PNode) = writeNode(f, n.sons[i]) f.write(")") -proc writeSym(f: TFile; s: PSym) = +proc writeSym(f: File; s: PSym) = if s == nil: f.write("{}\n") return @@ -985,7 +980,7 @@ proc writeSym(f: TFile; s: PSym) = f.writeNode(s.ast) f.write("}\n") -proc writeType(f: TFile; t: PType) = +proc writeType(f: File; t: PType) = if t == nil: f.write("[]\n") return diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim index 09b92cd8a..e0ef3c397 100644 --- a/compiler/rodutils.nim +++ b/compiler/rodutils.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index 4231da2d0..9fed7ac52 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -187,9 +187,6 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = if loc.r != nil: add(result, '!') encodeStr(ropeToStr(loc.r), result) - if loc.a != 0: - add(result, '?') - encodeVInt(loc.a, result) if oldLen + 1 == result.len: # no data was necessary, so remove the '<' again: setLen(result, oldLen) @@ -297,7 +294,7 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) = # the last entry of a symbol: if s.ast != nil: # we used to attempt to save space here by only storing a dummy AST if - # it is not necessary, but Nimrod's heavy compile-time evaluation features + # it is not necessary, but Nim's heavy compile-time evaluation features # make that unfeasible nowadays: encodeNode(w, s.info, s.ast, result) when false: @@ -422,7 +419,7 @@ proc addStmt(w: PRodWriter, n: PNode) = proc writeRod(w: PRodWriter) = processStacks(w, true) - var f: TFile + var f: File if not open(f, completeGeneratedFilePath(changeFileExt( w.filename.withPackageName, RodExt)), fmWrite): diff --git a/compiler/ropes.nim b/compiler/ropes.nim index fcf5dd202..b14081694 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -12,7 +12,7 @@ # Ropes are a data structure that represents a very long string # efficiently; especially concatenation is done in O(1) instead of O(N). # Ropes make use a lazy evaluation: They are essentially concatenation -# trees that are only flattened when converting to a native Nimrod +# trees that are only flattened when converting to a native Nim # string or when written to disk. The empty string is represented by a # nil pointer. # A little picture makes everything clear: @@ -64,7 +64,7 @@ type # copy the format strings # though it is not necessary) PRope* = ref TRope - TRope*{.acyclic.} = object of TObject # the empty rope is represented + TRope*{.acyclic.} = object of RootObj # the empty rope is represented # by nil to safe space left*, right*: PRope length*: int @@ -216,7 +216,7 @@ proc app(a: var PRope, b: PRope) = a = con(a, b) proc app(a: var PRope, b: string) = a = con(a, b) proc prepend(a: var PRope, b: PRope) = a = con(b, a) -proc writeRope*(f: TFile, c: PRope) = +proc writeRope*(f: File, c: PRope) = var stack = @[c] while len(stack) > 0: var it = pop(stack) @@ -228,7 +228,7 @@ proc writeRope*(f: TFile, c: PRope) = write(f, it.data) proc writeRope*(head: PRope, filename: string, useWarning = false) = - var f: TFile + var f: File if open(f, filename, fmWrite): if head != nil: writeRope(f, head) close(f) @@ -299,7 +299,7 @@ proc appf(c: var PRope, frmt: TFormatStr, args: varargs[PRope]) = const bufSize = 1024 # 1 KB is reasonable -proc auxRopeEqualsFile(r: PRope, bin: var TFile, buf: pointer): bool = +proc auxRopeEqualsFile(r: PRope, bin: var File, buf: pointer): bool = if r.data != nil: if r.length > bufSize: errorHandler(rTokenTooLong, r.data) @@ -312,7 +312,7 @@ proc auxRopeEqualsFile(r: PRope, bin: var TFile, buf: pointer): bool = if result: result = auxRopeEqualsFile(r.right, bin, buf) proc ropeEqualsFile(r: PRope, f: string): bool = - var bin: TFile + var bin: File result = open(bin, f) if not result: return # not equal if file does not exist diff --git a/compiler/saturate.nim b/compiler/saturate.nim index ed197bdd1..f4fe29a20 100644 --- a/compiler/saturate.nim +++ b/compiler/saturate.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/compiler/sem.nim b/compiler/sem.nim index 26a59334c..9ac7ad139 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -15,9 +15,12 @@ import magicsys, parser, nversion, nimsets, semfold, importer, procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch, intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, - evaltempl, patterns, parampatterns, sempass2, pretty, semmacrosanity, + evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity, semparallel, lowerings +when defined(nimfix): + import nimfix.prettybase + # implementation proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.procvar.} @@ -37,9 +40,7 @@ proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) proc addParams(c: PContext, n: PNode, kind: TSymKind) proc maybeAddResult(c: PContext, s: PSym, n: PNode) proc instGenericContainer(c: PContext, n: PNode, header: PType): PType -proc tryExpr(c: PContext, n: PNode, - flags: TExprFlags = {}, bufferErrors = false): PNode -proc fixImmediateParams(n: PNode): PNode +proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc activate(c: PContext, n: PNode) proc semQuoteAst(c: PContext, n: PNode): PNode proc finishMethod(c: PContext, s: PSym) @@ -66,12 +67,25 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode = # error correction: result = copyTree(arg) result.typ = formal + else: + let x = result.skipConv + if x.kind == nkPar and formal.kind != tyExpr: + changeType(x, formal, check=true) proc inferWithMetatype(c: PContext, formal: PType, arg: PNode, coerceDistincts = false): PNode var commonTypeBegin = PType(kind: tyExpr) +proc isEmptyContainer(t: PType): bool = + case t.kind + of tyExpr, tyNil: result = true + of tyArray, tyArrayConstr: result = t.sons[1].kind == tyEmpty + of tySet, tySequence, tyOpenArray, tyVarargs: + result = t.sons[0].kind == tyEmpty + of tyGenericInst: result = isEmptyContainer(t.lastSon) + else: result = false + proc commonType*(x, y: PType): PType = # new type relation that is used for array constructors, # if expressions, etc.: @@ -95,6 +109,13 @@ proc commonType*(x, y: PType): PType = # check for seq[empty] vs. seq[int] let idx = ord(b.kind in {tyArray, tyArrayConstr}) if a.sons[idx].kind == tyEmpty: return y + elif a.kind == tyTuple and b.kind == tyTuple and a.len == b.len: + var nt: PType + for i in 0.. 2: + localError(forLoop.info, errGenerated, + "parallel 'fields' iterator does not work for 'case' objects") + return + # iterate over the selector: + asgnForObjectFields(c, typ[0], forLoop, father) + # we need to generate a case statement: + var caseStmt = newNodeI(nkCaseStmt, c.info) + # generate selector: + var access = newNodeI(nkDotExpr, forLoop.info, 2) + access.sons[0] = call.sons[1] + access.sons[1] = newSymNode(typ.sons[0].sym, forLoop.info) + caseStmt.add(semExprWithType(c.c, access)) + # copy the branches over, but replace the fields with the for loop body: + for i in 1 .. 0 if we are in a user-defined type class inGenericContext*: int # > 0 if we are in a generic type @@ -72,8 +72,7 @@ type libs*: TLinkedList # all libs used by this module semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.} - semTryExpr*: proc (c: PContext, n: PNode,flags: TExprFlags = {}, - bufferErrors = false): PNode {.nimcall.} + semTryExpr*: proc (c: PContext, n: PNode,flags: TExprFlags = {}): PNode {.nimcall.} semTryConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.} semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet @@ -83,15 +82,18 @@ type semInferredLambda*: proc(c: PContext, pt: TIdTable, n: PNode): PNode semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable, info: TLineInfo): PSym - includedFiles*: TIntSet # used to detect recursive include files + includedFiles*: IntSet # used to detect recursive include files userPragmas*: TStrTable evalContext*: PEvalContext - unknownIdents*: TIntSet # ids of all unknown identifiers to prevent + unknownIdents*: IntSet # ids of all unknown identifiers to prevent # naming it multiple times generics*: seq[TInstantiationPair] # pending list of instantiated generics to compile lastGenericIdx*: int # used for the generics stack hloLoopDetector*: int # used to prevent endless loops in the HLO inParallelStmt*: int + instDeepCopy*: proc (c: PContext; dc: PSym; t: PType; + info: TLineInfo): PSym {.nimcall.} + proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = result.genericSym = s @@ -310,3 +312,8 @@ proc checkSonsLen*(n: PNode, length: int) = proc checkMinSonsLen*(n: PNode, length: int) = if sonsLen(n) < length: illFormedAst(n) +proc isTopLevel*(c: PContext): bool {.inline.} = + result = c.currentScope.depthLevel <= 2 + +proc experimentalMode*(c: PContext): bool {.inline.} = + result = gExperimentalMode or sfExperimental in c.module.flags diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim index 3c30dc1bd..4ce610bf9 100644 --- a/compiler/semdestruct.nim +++ b/compiler/semdestruct.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 58cef36f9..ee3391e3e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -13,6 +13,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, flags: TExprFlags = {}): PNode = markUsed(n.info, s) + styleCheckUse(n.info, s) pushInfoContext(n.info) result = evalTemplate(n, s, getCurrOwner()) if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, s, flags) @@ -79,6 +80,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = case s.kind of skConst: markUsed(n.info, s) + styleCheckUse(n.info, s) case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, tyTuple, tySet, tyUInt..tyUInt64: @@ -103,6 +105,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = of skTemplate: result = semTemplateExpr(c, n, s, flags) of skVar, skLet, skResult, skParam, skForVar: markUsed(n.info, s) + styleCheckUse(n.info, s) # if a proc accesses a global variable, it is not side effect free: if sfGlobal in s.flags: incl(c.p.owner.flags, sfSideEffect) @@ -115,6 +118,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = # var len = 0 # but won't be called # genericThatUsesLen(x) # marked as taking a closure? of skGenericParam: + styleCheckUse(n.info, s) if s.typ.kind == tyStatic: result = newSymNode(s, n.info) result.typ = s.typ @@ -125,12 +129,14 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = return n of skType: markUsed(n.info, s) + styleCheckUse(n.info, s) if s.typ.kind == tyStatic and s.typ.n != nil: return s.typ.n result = newSymNode(s, n.info) result.typ = makeTypeDesc(c, s.typ) else: markUsed(n.info, s) + styleCheckUse(n.info, s) result = newSymNode(s, n.info) type @@ -259,6 +265,7 @@ proc semConv(c: PContext, n: PNode): PNode = let status = checkConvertible(c, result.typ, it.typ) if status in {convOK, convNotNeedeed}: markUsed(n.info, it.sym) + styleCheckUse(n.info, it.sym) markIndirect(c, it.sym) return it localError(n.info, errUseQualifier, op.sons[0].sym.name.s) @@ -360,6 +367,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode = result = newIntNode(nkIntLit, ord(t.kind == tyProc and t.callConv == ccClosure and tfIterator notin t.flags)) + else: discard else: var t2 = n[2].typ.skipTypes({tyTypeDesc}) maybeLiftType(t2, c, n.info) @@ -415,34 +423,22 @@ proc overloadedCallOpr(c: PContext, n: PNode): PNode = for i in countup(0, sonsLen(n) - 1): addSon(result, n.sons[i]) result = semExpr(c, result) -proc changeType(n: PNode, newType: PType, check: bool) = +proc changeType(n: PNode, newType: PType, check: bool) = case n.kind - of nkCurly, nkBracket: - for i in countup(0, sonsLen(n) - 1): + of nkCurly, nkBracket: + for i in countup(0, sonsLen(n) - 1): changeType(n.sons[i], elemType(newType), check) - of nkPar: - if newType.kind != tyTuple: + of nkPar: + let tup = newType.skipTypes({tyGenericInst}) + if tup.kind != tyTuple: internalError(n.info, "changeType: no tuple type for constructor") - elif newType.n == nil: discard - elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr: - for i in countup(0, sonsLen(n) - 1): - var m = n.sons[i].sons[0] - if m.kind != nkSym: - internalError(m.info, "changeType(): invalid tuple constr") - return - var f = getSymFromList(newType.n, m.sym.name) - if f == nil: - internalError(m.info, "changeType(): invalid identifier") - return - changeType(n.sons[i].sons[1], f.typ, check) else: for i in countup(0, sonsLen(n) - 1): var m = n.sons[i] - var a = newNodeIT(nkExprColonExpr, m.info, newType.sons[i]) - addSon(a, newSymNode(newType.n.sons[i].sym)) - addSon(a, m) - changeType(m, newType.sons[i], check) - n.sons[i] = a + if m.kind == nkExprColonExpr: + m = m.sons[1] + n.sons[i] = m + changeType(m, tup.sons[i], check) of nkCharLit..nkUInt64Lit: if check: let value = n.intVal @@ -533,7 +529,8 @@ proc fixAbstractType(c: PContext, n: PNode) = elif skipTypes(it.sons[1].typ, abstractVar).kind in {tyNil, tyArrayConstr, tyTuple, tySet}: var s = skipTypes(it.typ, abstractVar) - changeType(it.sons[1], s, check=true) + if s.kind != tyExpr: + changeType(it.sons[1], s, check=true) n.sons[i] = it.sons[1] of nkBracket: # an implicitly constructed array (passed to an open array): @@ -721,7 +718,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, case callee.kind of skMacro, skTemplate: discard else: - if (callee.kind in skIterators) and (callee.id == c.p.owner.id): + if callee.kind in skIterators and callee.id == c.p.owner.id: localError(n.info, errRecursiveDependencyX, callee.name.s) if sfNoSideEffect notin callee.flags: if {sfImportc, sfSideEffect} * callee.flags != {}: @@ -832,45 +829,17 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = if result != nil: result = afterCallActions(c, result, nOrig, flags) else: result = errorNode(c, n) -proc buildStringify(c: PContext, arg: PNode): PNode = - if arg.typ != nil and - skipTypes(arg.typ, abstractInst-{tyTypeDesc}).kind == tyString: - result = arg - else: - result = newNodeI(nkCall, arg.info) - addSon(result, newIdentNode(getIdent"$", arg.info)) - addSon(result, arg) - -proc semEcho(c: PContext, n: PNode): PNode = - # this really is a macro - checkMinSonsLen(n, 1) - for i in countup(1, sonsLen(n) - 1): - var arg = semExprWithType(c, n.sons[i]) - arg = semExprWithType(c, buildStringify(c, arg)) - n.sons[i] = arg - let t = arg.typ - if (t == nil or t.skipTypes(abstractInst).kind != tyString) and - arg.kind != nkEmpty: - localError(n.info, errGenerated, - "implicitly invoked '$' does not return string") - let t = n.sons[0].typ - if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect) - result = n - proc buildEchoStmt(c: PContext, n: PNode): PNode = - # we MUST not check 'n' for semantics again here! + # we MUST not check 'n' for semantics again here! But for now we give up: result = newNodeI(nkCall, n.info) var e = strTableGet(magicsys.systemModule.tab, getIdent"echo") if e != nil: - addSon(result, newSymNode(e)) + add(result, newSymNode(e)) else: localError(n.info, errSystemNeeds, "echo") - addSon(result, errorNode(c, n)) - var arg = buildStringify(c, n) - # problem is: implicit '$' is not checked for semantics yet. So we give up - # and check 'arg' for semantics again: - arg = semExpr(c, arg) - if arg != nil: addSon(result, arg) + add(result, errorNode(c, n)) + add(result, n) + result = semExpr(c, result) proc semExprNoType(c: PContext, n: PNode): PNode = result = semExpr(c, n, {efWantStmt}) @@ -958,17 +927,18 @@ proc readTypeParameter(c: PContext, typ: PType, paramName: PIdent, info: TLineInfo): PNode = let ty = if typ.kind == tyGenericInst: typ.skipGenericAlias else: (internalAssert(typ.kind == tyCompositeTypeClass); typ.sons[1]) - + #debug ty let tbody = ty.sons[0] for s in countup(0, tbody.len-2): let tParam = tbody.sons[s] - if tParam.sym.name == paramName: + if tParam.sym.name.id == paramName.id: let rawTyp = ty.sons[s + 1] if rawTyp.kind == tyStatic: return rawTyp.n else: let foundTyp = makeTypeDesc(c, rawTyp) return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info) + #echo "came here: returned nil" proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = ## returns nil if it's not a built-in field access @@ -980,8 +950,14 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) if s != nil: - markUsed(n.sons[1].info, s) - return semSym(c, n, s, flags) + if s.kind in OverloadableSyms: + result = symChoice(c, n, s, scClosed) + if result.kind == nkSym: result = semSym(c, n, s, flags) + else: + markUsed(n.sons[1].info, s) + result = semSym(c, n, s, flags) + styleCheckUse(n.sons[1].info, s) + return n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType}) #restoreOldStyleType(n.sons[0]) @@ -997,13 +973,14 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = # look up if the identifier belongs to the enum: while ty != nil: f = getSymFromList(ty.n, i) - if f != nil: break + if f != nil: break ty = ty.sons[0] # enum inheritance if f != nil: result = newSymNode(f) result.info = n.info result.typ = ty markUsed(n.info, f) + styleCheckUse(n.info, f) return of tyTypeParamsHolders: return readTypeParameter(c, ty, i, n.info) @@ -1036,12 +1013,13 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = if fieldVisible(c, f): # is the access to a public field or in the same module or in a friend? markUsed(n.sons[1].info, f) + styleCheckUse(n.sons[1].info, f) n.sons[0] = makeDeref(n.sons[0]) n.sons[1] = newSymNode(f) # we now have the correct field n.typ = f.typ - if check == nil: + if check == nil: result = n - else: + else: check.sons[0] = n check.typ = n.typ result = check @@ -1049,6 +1027,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = f = getSymFromList(ty.n, i) if f != nil: markUsed(n.sons[1].info, f) + styleCheckUse(n.sons[1].info, f) n.sons[0] = makeDeref(n.sons[0]) n.sons[1] = newSymNode(f) n.typ = f.typ @@ -1260,8 +1239,8 @@ proc semAsgn(c: PContext, n: PNode): PNode = proc semReturn(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1) - if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro} or - c.p.owner.kind == skClosureIterator: + if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro, + skClosureIterator}: if n.sons[0].kind != nkEmpty: # transform ``return expr`` to ``result = expr; return`` if c.p.resultSym != nil: @@ -1444,6 +1423,8 @@ proc newAnonSym(kind: TSymKind, info: TLineInfo, proc semUsing(c: PContext, n: PNode): PNode = result = newNodeI(nkEmpty, n.info) + if not experimentalMode(c): + localError(n.info, "use the {.experimental.} pragma to enable 'using'") for e in n.sons: let usedSym = semExpr(c, e) if usedSym.kind == nkSym: @@ -1465,6 +1446,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode = macroCall.sons[0] = newSymNode(expandedSym, macroCall.info) markUsed(n.info, expandedSym) + styleCheckUse(n.info, expandedSym) for i in countup(1, macroCall.len-1): macroCall.sons[i] = semExprWithType(c, macroCall[i], {}) @@ -1525,11 +1507,11 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = doBlk.sons[namePos] = newAnonSym(skTemplate, n.info).newSymNode if ids.len > 0: - doBlk[paramsPos].sons.setLen(2) - doBlk[paramsPos].sons[0] = getSysSym("stmt").newSymNode # return type + doBlk.sons[paramsPos] = newNodeI(nkFormalParams, n.info) + doBlk[paramsPos].add getSysSym("stmt").newSymNode # return type ids.add getSysSym("expr").newSymNode # params type ids.add emptyNode # no default value - doBlk[paramsPos].sons[1] = newNode(nkIdentDefs, n.info, ids) + doBlk[paramsPos].add newNode(nkIdentDefs, n.info, ids) var tmpl = semTemplateDef(c, doBlk) quotes[0] = tmpl[namePos] @@ -1538,8 +1520,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = newNode(nkCall, n.info, quotes)]) result = semExpandToAst(c, result) -proc tryExpr(c: PContext, n: PNode, - flags: TExprFlags = {}, bufferErrors = false): PNode = +proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # watch out, hacks ahead: let oldErrorCount = msgs.gErrorCounter let oldErrorMax = msgs.gErrorMax @@ -1553,7 +1534,7 @@ proc tryExpr(c: PContext, n: PNode, let oldOwnerLen = len(gOwners) let oldGenerics = c.generics let oldErrorOutputs = errorOutputs - errorOutputs = if bufferErrors: {eInMemory} else: {} + errorOutputs = {} let oldContextLen = msgs.getInfoContextLen() let oldInGenericContext = c.inGenericContext @@ -1638,7 +1619,6 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = of mSizeOf: result = semSizeof(c, setMs(n, s)) of mIs: result = semIs(c, setMs(n, s)) of mOf: result = semOf(c, setMs(n, s)) - of mEcho: result = semEcho(c, setMs(n, s)) of mShallowCopy: result = semShallowCopy(c, n, flags) of mExpandToAst: result = semExpandToAst(c, n, s, flags) of mQuoteAst: result = semQuoteAst(c, n) @@ -1662,6 +1642,10 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = else: result.typ = result[1].typ result.add instantiateCreateFlowVarCall(c, result[1].typ, n.info).newSymNode + of mProcCall: + result = setMs(n, s) + result.sons[1] = semExpr(c, n.sons[1]) + result.typ = n[1].typ else: result = semDirectOp(c, n, flags) proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = @@ -1821,7 +1805,7 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = addSonSkipIntLit(typ, n.sons[i].typ) result.typ = typ -proc checkInitialized(n: PNode, ids: TIntSet, info: TLineInfo) = +proc checkInitialized(n: PNode, ids: IntSet, info: TLineInfo) = case n.kind of nkRecList: for i in countup(0, sonsLen(n) - 1): @@ -1902,6 +1886,7 @@ proc semBlock(c: PContext, n: PNode): PNode = addDecl(c, labl) n.sons[0] = newSymNode(labl, n.sons[0].info) suggestSym(n.sons[0].info, labl) + styleCheckDef(labl) n.sons[1] = semExpr(c, n.sons[1]) n.typ = n.sons[1].typ if isEmptyType(n.typ): n.kind = nkBlockStmt @@ -1909,19 +1894,6 @@ proc semBlock(c: PContext, n: PNode): PNode = closeScope(c) dec(c.p.nestedBlockCounter) -proc doBlockIsStmtList(n: PNode): bool = - result = n.kind == nkDo and - n[paramsPos].sonsLen == 1 and - n[paramsPos][0].kind == nkEmpty - -proc fixImmediateParams(n: PNode): PNode = - # XXX: Temporary work-around until we carry out - # the planned overload resolution reforms - for i in 1 .. 2: + localError(forLoop.info, errGenerated, + "parallel 'fields' iterator does not work for 'case' objects") + return + # iterate over the selector: + semForObjectFields(c, typ[0], forLoop, father) + # we need to generate a case statement: + var caseStmt = newNodeI(nkCaseStmt, forLoop.info) + # generate selector: + var access = newNodeI(nkDotExpr, forLoop.info, 2) + access.sons[0] = call.sons[1] + access.sons[1] = newSymNode(typ.sons[0].sym, forLoop.info) + caseStmt.add(semExprWithType(c.c, access)) + # copy the branches over, but replace the fields with the for loop body: + for i in 1 .. = 0: result = makeRange(a.typ, 0, b.intVal) of mModU: let a = n.sons[1] let b = n.sons[2] - if b.kind in {nkIntLit..nkUInt64Lit}: + if b.kind in {nkIntLit..nkUInt32Lit}: if b.intVal >= 0: result = makeRange(a.typ, 0, b.intVal-1) else: @@ -255,6 +257,7 @@ proc evalIs(n, a: PNode): PNode = result = newIntNode(nkIntLit, ord(t.kind == tyProc and t.callConv == ccClosure and tfIterator in t.flags)) + else: discard else: # XXX semexprs.isOpImpl is slightly different and requires a context. yay. let t2 = n[2].typ @@ -318,8 +321,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of tyInt64, tyInt, tyUInt..tyUInt64: result = newIntNodeT(`shr`(getInt(a), getInt(b)), n) else: internalError(n.info, "constant folding for shr") - of mDivI, mDivI64: result = newIntNodeT(getInt(a) div getInt(b), n) - of mModI, mModI64: result = newIntNodeT(getInt(a) mod getInt(b), n) + of mDivI, mDivI64: + let y = getInt(b) + if y != 0: + result = newIntNodeT(getInt(a) div y, n) + of mModI, mModI64: + let y = getInt(b) + if y != 0: + result = newIntNodeT(getInt(a) mod y, n) of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n) of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n) of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n) @@ -358,8 +367,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n) of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n) of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n) - of mModU: result = newIntNodeT(`%%`(getInt(a), getInt(b)), n) - of mDivU: result = newIntNodeT(`/%`(getInt(a), getInt(b)), n) + of mModU: + let y = getInt(b) + if y != 0: + result = newIntNodeT(`%%`(getInt(a), y), n) + of mDivU: + let y = getInt(b) + if y != 0: + result = newIntNodeT(`/%`(getInt(a), y), n) of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n) of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n) of mLtSet: @@ -501,7 +516,7 @@ proc foldConv*(n, a: PNode; check = false): PNode = of tyInt..tyInt64: case skipTypes(a.typ, abstractRange).kind of tyFloat..tyFloat64: - result = newIntNodeT(system.toInt(getFloat(a)), n) + result = newIntNodeT(int(getFloat(a)), n) of tyChar: result = newIntNodeT(getOrdValue(a), n) else: result = a @@ -600,10 +615,6 @@ proc getConstExpr(m: PSym, n: PNode): PNode = of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n) of mCompileDate: result = newStrNodeT(times.getDateStr(), n) of mCompileTime: result = newStrNodeT(times.getClockStr(), n) - of mNimrodVersion: result = newStrNodeT(VersionAsString, n) - of mNimrodMajor: result = newIntNodeT(VersionMajor, n) - of mNimrodMinor: result = newIntNodeT(VersionMinor, n) - of mNimrodPatch: result = newIntNodeT(VersionPatch, n) of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n) of mHostOS: result = newStrNodeT(toLower(platform.OS[targetOS].name), n) of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLower, n) @@ -683,9 +694,9 @@ proc getConstExpr(m: PSym, n: PNode): PNode = result = evalIs(n, a) else: result = magicCall(m, n) - except EOverflow: + except OverflowError: localError(n.info, errOverOrUnderflow) - except EDivByZero: + except DivByZeroError: localError(n.info, errConstantDivisionByZero) of nkAddr: var a = getConstExpr(m, n.sons[0]) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index a004d1465..7c32b0051 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -13,7 +13,7 @@ # A problem is that it cannot be detected if the symbol is introduced # as in ``var x = ...`` or used because macros/templates can hide this! # So we have to eval templates/macros right here so that symbol -# lookup can be accurate. XXX But this can only be done for immediate macros! +# lookup can be accurate. # included from sem.nim @@ -28,7 +28,7 @@ proc getIdentNode(n: PNode): PNode = proc semGenericStmtScope(c: PContext, n: PNode, flags: TSemGenericFlags, - ctx: var TIntSet): PNode = + ctx: var IntSet): PNode = openScope(c) result = semGenericStmt(c, n, flags, ctx) closeScope(c) @@ -37,7 +37,7 @@ template macroToExpand(s: expr): expr = s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfImmediate in s.flags) proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, - ctx: var TIntSet): PNode = + ctx: var IntSet): PNode = incl(s.flags, sfUsed) case s.kind of skUnknown: @@ -47,33 +47,39 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, result = symChoice(c, n, s, scOpen) of skTemplate: if macroToExpand(s): - let n = fixImmediateParams(n) + styleCheckUse(n.info, s) result = semTemplateExpr(c, n, s, {efNoSemCheck}) result = semGenericStmt(c, result, {}, ctx) else: result = symChoice(c, n, s, scOpen) - of skMacro: + of skMacro: if macroToExpand(s): + styleCheckUse(n.info, s) result = semMacroExpr(c, n, n, s, {efNoSemCheck}) result = semGenericStmt(c, result, {}, ctx) else: result = symChoice(c, n, s, scOpen) of skGenericParam: result = newSymNodeTypeDesc(s, n.info) - of skParam: + styleCheckUse(n.info, s) + of skParam: result = n + styleCheckUse(n.info, s) of skType: if (s.typ != nil) and (s.typ.kind != tyGenericParam): result = newSymNodeTypeDesc(s, n.info) else: result = n - else: result = newSymNode(s, n.info) + styleCheckUse(n.info, s) + else: + result = newSymNode(s, n.info) + styleCheckUse(n.info, s) proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, - ctx: var TIntSet): PNode = + ctx: var IntSet): PNode = result = n let ident = considerQuotedIdent(n) - var s = searchInScopes(c, ident) + var s = searchInScopes(c, ident).skipAlias(n) if s == nil: if ident.id notin ctx and withinMixin notin flags: localError(n.info, errUndeclaredIdentifier, ident.s) @@ -92,7 +98,7 @@ proc newDot(n, b: PNode): PNode = result.add(b) proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, - ctx: var TIntSet): PNode = + ctx: var IntSet; isMacro: var bool): PNode = assert n.kind == nkDotExpr let luf = if withinMixin notin flags: {checkUndeclared} else: {} @@ -104,8 +110,9 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, result = n let n = n[1] let ident = considerQuotedIdent(n) - var s = searchInScopes(c, ident) + var s = searchInScopes(c, ident).skipAlias(n) if s != nil and s.kind in routineKinds: + isMacro = s.kind in {skTemplate, skMacro} if withinBind in flags: result = newDot(result, symChoice(c, n, s, scClosed)) elif s.name.id in ctx: @@ -117,8 +124,13 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, else: result = newDot(result, sym) +proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) = + let s = newSymS(skUnknown, getIdentNode(n), c) + addPrelimDecl(c, s) + styleCheckDef(n.info, s, kind) + proc semGenericStmt(c: PContext, n: PNode, - flags: TSemGenericFlags, ctx: var TIntSet): PNode = + flags: TSemGenericFlags, ctx: var IntSet): PNode = result = n if gCmd == cmdIdeTools: suggestStmt(c, n) case n.kind @@ -129,7 +141,8 @@ proc semGenericStmt(c: PContext, n: PNode, #var s = qualifiedLookUp(c, n, luf) #if s != nil: result = semGenericStmtSymbol(c, n, s) # XXX for example: ``result.add`` -- ``add`` needs to be looked up here... - result = fuzzyLookup(c, n, flags, ctx) + var dummy: bool + result = fuzzyLookup(c, n, flags, ctx, dummy) of nkEmpty, nkSym..nkNilLit: # see tests/compile/tgensymgeneric.nim: # We need to open the gensym'ed symbol again so that the instantiation @@ -153,50 +166,61 @@ proc semGenericStmt(c: PContext, n: PNode, localError(n.info, errUndeclaredIdentifier, fn.renderTree) var first = 0 - var isDefinedMagic = false - if s != nil: + var mixinContext = false + if s != nil: incl(s.flags, sfUsed) - isDefinedMagic = s.magic in {mDefined, mDefinedInScope, mCompiles} + mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles} let scOption = if s.name.id in ctx: scForceOpen else: scOpen case s.kind of skMacro: if macroToExpand(s): + styleCheckUse(fn.info, s) result = semMacroExpr(c, n, n, s, {efNoSemCheck}) result = semGenericStmt(c, result, {}, ctx) else: - n.sons[0] = symChoice(c, n.sons[0], s, scOption) + n.sons[0] = symChoice(c, fn, s, scOption) result = n - of skTemplate: + mixinContext = true + of skTemplate: if macroToExpand(s): - let n = fixImmediateParams(n) + styleCheckUse(fn.info, s) result = semTemplateExpr(c, n, s, {efNoSemCheck}) result = semGenericStmt(c, result, {}, ctx) else: - n.sons[0] = symChoice(c, n.sons[0], s, scOption) + n.sons[0] = symChoice(c, fn, s, scOption) result = n # BUGFIX: we must not return here, we need to do first phase of - # symbol lookup ... + # symbol lookup. Also since templates and macros can do scope injections + # we need to put the ``c`` in ``t(c)`` in a mixin context to prevent + # the famous "undeclared identifier: it" bug: + mixinContext = true of skUnknown, skParam: # Leave it as an identifier. discard of skProc, skMethod, skIterators, skConverter: - result.sons[0] = symChoice(c, n.sons[0], s, scOption) + result.sons[0] = symChoice(c, fn, s, scOption) first = 1 of skGenericParam: - result.sons[0] = newSymNodeTypeDesc(s, n.sons[0].info) + result.sons[0] = newSymNodeTypeDesc(s, fn.info) + styleCheckUse(fn.info, s) first = 1 of skType: # bad hack for generics: if (s.typ != nil) and (s.typ.kind != tyGenericParam): - result.sons[0] = newSymNodeTypeDesc(s, n.sons[0].info) + result.sons[0] = newSymNodeTypeDesc(s, fn.info) + styleCheckUse(fn.info, s) first = 1 else: - result.sons[0] = newSymNode(s, n.sons[0].info) + result.sons[0] = newSymNode(s, fn.info) + styleCheckUse(fn.info, s) first = 1 + elif fn.kind == nkDotExpr: + result.sons[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext) + first = 1 # Consider 'when defined(globalsSlot): ThreadVarSetValue(globalsSlot, ...)' # in threads.nim: the subtle preprocessing here binds 'globalsSlot' which # is not exported and yet the generic 'threadProcWrapper' works correctly. - let flags = if isDefinedMagic: flags+{withinMixin} else: flags + let flags = if mixinContext: flags+{withinMixin} else: flags for i in countup(first, sonsLen(result) - 1): result.sons[i] = semGenericStmt(c, result.sons[i], flags, ctx) of nkIfStmt: @@ -226,14 +250,14 @@ proc semGenericStmt(c: PContext, n: PNode, openScope(c) n.sons[L - 2] = semGenericStmt(c, n.sons[L-2], flags, ctx) for i in countup(0, L - 3): - addPrelimDecl(c, newSymS(skUnknown, n.sons[i], c)) + addTempDecl(c, n.sons[i], skForVar) n.sons[L - 1] = semGenericStmt(c, n.sons[L-1], flags, ctx) closeScope(c) of nkBlockStmt, nkBlockExpr, nkBlockType: checkSonsLen(n, 2) openScope(c) if n.sons[0].kind != nkEmpty: - addPrelimDecl(c, newSymS(skUnknown, n.sons[0], c)) + addTempDecl(c, n.sons[0], skLabel) n.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx) closeScope(c) of nkTryStmt: @@ -256,7 +280,7 @@ proc semGenericStmt(c: PContext, n: PNode, a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) for j in countup(0, L-3): - addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c)) + addTempDecl(c, getIdentNode(a.sons[j]), skVar) of nkGenericParams: for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] @@ -266,23 +290,23 @@ proc semGenericStmt(c: PContext, n: PNode, a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) # do not perform symbol lookup for default expressions for j in countup(0, L-3): - addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c)) + addTempDecl(c, getIdentNode(a.sons[j]), skType) of nkConstSection: for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue if (a.kind != nkConstDef): illFormedAst(a) checkSonsLen(a, 3) - addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[0]), c)) + addTempDecl(c, getIdentNode(a.sons[0]), skConst) a.sons[1] = semGenericStmt(c, a.sons[1], flags+{withinTypeDesc}, ctx) a.sons[2] = semGenericStmt(c, a.sons[2], flags, ctx) - of nkTypeSection: + of nkTypeSection: for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue if (a.kind != nkTypeDef): illFormedAst(a) checkSonsLen(a, 3) - addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[0]), c)) + addTempDecl(c, getIdentNode(a.sons[0]), skType) for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue @@ -306,9 +330,9 @@ proc semGenericStmt(c: PContext, n: PNode, of nkIdent: a = n.sons[i] else: illFormedAst(n) addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c)) - of nkObjectTy, nkTupleTy: + of nkObjectTy, nkTupleTy: discard - of nkFormalParams: + of nkFormalParams: checkMinSonsLen(n, 1) if n.sons[0].kind != nkEmpty: n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx) @@ -320,17 +344,17 @@ proc semGenericStmt(c: PContext, n: PNode, a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) for j in countup(0, L-3): - addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c)) + addTempDecl(c, getIdentNode(a.sons[j]), skParam) of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, nkIteratorDef, nkLambdaKinds: checkSonsLen(n, bodyPos + 1) - if n.kind notin nkLambdaKinds: - addPrelimDecl(c, newSymS(skUnknown, getIdentNode(n.sons[0]), c)) + if n.sons[namePos].kind != nkEmpty: + addTempDecl(c, getIdentNode(n.sons[0]), skProc) openScope(c) n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos], flags, ctx) if n.sons[paramsPos].kind != nkEmpty: - if n.sons[paramsPos].sons[0].kind != nkEmpty: + if n.sons[paramsPos].sons[0].kind != nkEmpty: addPrelimDecl(c, newSym(skUnknown, getIdent("result"), nil, n.info)) n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, ctx) n.sons[pragmasPos] = semGenericStmt(c, n.sons[pragmasPos], flags, ctx) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index b205eb09a..2decb5d0b 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -157,8 +157,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable, # # The solution would be to move this logic into semtypinst, but # at this point semtypinst have to become part of sem, because it - # will need to use openScope, addDecl, etc - # + # will need to use openScope, addDecl, etc. addDecl(c, prc) pushInfoContext(info) @@ -223,7 +222,11 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, n.sons[genericParamsPos] = ast.emptyNode var oldPrc = genericCacheGet(fn, entry[]) if oldPrc == nil: - fn.procInstCache.safeAdd(entry) + # we MUST not add potentially wrong instantiations to the caching mechanism. + # This means recursive instantiations behave differently when in + # a ``compiles`` context but this is the lesser evil. See + # bug #1055 (tevilcompiles). + if c.inCompilesContext == 0: fn.procInstCache.safeAdd(entry) c.generics.add(makeInstPair(fn, entry)) if n.sons[pragmasPos].kind != nkEmpty: pragma(c, result, n.sons[pragmasPos], allRoutinePragmas) diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim index 1bece95c2..c685d602d 100644 --- a/compiler/semmacrosanity.nim +++ b/compiler/semmacrosanity.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -55,7 +55,7 @@ proc annotateType*(n: PNode, t: PType) = else: globalError(n.info, "() must have an object or tuple type") of nkBracket: - if x.kind in {tyArrayConstr, tyArray, tySequence, tyOpenarray}: + if x.kind in {tyArrayConstr, tyArray, tySequence, tyOpenArray}: n.typ = t for m in n: annotateType(m, x.elemType) else: diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index d4aeba32a..d6c420955 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -130,4 +130,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mShallowCopy: result = semShallowCopy(c, n, flags) of mNBindSym: result = semBindSym(c, n) of mLocals: result = semLocals(c, n) + of mProcCall: + result = n + result.typ = n[1].typ else: result = n diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index 7c489c3b6..bd3152b54 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -258,14 +258,18 @@ proc min(a, b: PNode): PNode = proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags +template pushSpawnId(c: expr, body: stmt) {.immediate, dirty.} = + inc c.spawns + let oldSpawnId = c.currentSpawnId + c.currentSpawnId = c.spawns + body + c.currentSpawnId = oldSpawnId + proc analyseCall(c: var AnalysisCtx; n: PNode; op: PSym) = if op.magic == mSpawn: - inc c.spawns - let oldSpawnId = c.currentSpawnId - c.currentSpawnId = c.spawns - gatherArgs(c, n[1]) - analyseSons(c, n) - c.currentSpawnId = oldSpawnId + pushSpawnId(c): + gatherArgs(c, n[1]) + analyseSons(c, n) elif op.magic == mInc or (op.name.s == "+=" and op.fromSystem): if n[1].isLocal: let incr = n[2].skipConv @@ -320,9 +324,16 @@ proc analyse(c: var AnalysisCtx; n: PNode) = # since we already ensure sfAddrTaken is not in s.flags, we only need to # prevent direct assignments to the monotonic variable: let slot = c.getSlot(n[0].sym) - slot.blackListed = true + slot.blacklisted = true invalidateFacts(c.guards, n[0]) - analyseSons(c, n) + let value = n[1] + if getMagic(value) == mSpawn: + pushSpawnId(c): + gatherArgs(c, value[1]) + analyseSons(c, value[1]) + analyse(c, n[0]) + else: + analyseSons(c, n) addAsgnFact(c.guards, n[0], n[1]) of nkCallKinds: # direct call: @@ -335,16 +346,21 @@ proc analyse(c: var AnalysisCtx; n: PNode) = localError(n.info, "invalid control flow for 'parallel'") # 'break' that leaves the 'parallel' section is not valid either # or maybe we should generate a 'try' XXX - of nkVarSection: + of nkVarSection, nkLetSection: for it in n: let value = it.lastSon + let isSpawned = getMagic(value) == mSpawn + if isSpawned: + pushSpawnId(c): + gatherArgs(c, value[1]) + analyseSons(c, value[1]) if value.kind != nkEmpty: for j in 0 .. it.len-3: if it[j].isLocal: let slot = c.getSlot(it[j].sym) if slot.lower.isNil: slot.lower = value else: internalError(it.info, "slot already has a lower bound") - analyse(c, value) + if not isSpawned: analyse(c, value) of nkCaseStmt: analyseCase(c, n) of nkIfStmt, nkIfExpr: analyseIf(c, n) of nkWhileStmt: @@ -396,7 +412,7 @@ proc transformSpawnSons(owner: PSym; n, barrier: PNode): PNode = proc transformSpawn(owner: PSym; n, barrier: PNode): PNode = case n.kind - of nkVarSection: + of nkVarSection, nkLetSection: result = nil for it in n: let b = it.lastSon @@ -464,6 +480,6 @@ proc liftParallel*(owner: PSym; n: PNode): PNode = result = newNodeI(nkStmtList, n.info) generateAliasChecks(a, result) result.add varSection - result.add callCodeGenProc("openBarrier", barrier) + result.add callCodegenProc("openBarrier", barrier) result.add transformSpawn(owner, body, barrier) - result.add callCodeGenProc("closeBarrier", barrier) + result.add callCodegenProc("closeBarrier", barrier) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index da4adcf49..4300aee20 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -57,30 +57,125 @@ discard """ c() --> we need a stack of scopes for this analysis -""" -const trackGlobals = false ## we don't need it for now + # XXX enhance the algorithm to care about 'dirty' expressions: + lock a[i].L: + inc i # mark 'i' dirty + lock a[j].L: + access a[i], a[j] # --> reject a[i] +""" type TEffects = object exc: PNode # stack of exceptions tags: PNode # list of tags - uses: PNode # list of used global variables - bottom: int + bottom, inTryStmt: int owner: PSym init: seq[int] # list of initialized variables guards: TModel # nested guards locked: seq[PNode] # locked locations - gcUnsafe, isRecursive: bool + gcUnsafe, isRecursive, isToplevel: bool + maxLockLevel, currLockLevel: TLockLevel PEffects = var TEffects +proc `<`(a, b: TLockLevel): bool {.borrow.} +proc `<=`(a, b: TLockLevel): bool {.borrow.} +proc `==`(a, b: TLockLevel): bool {.borrow.} +proc max(a, b: TLockLevel): TLockLevel {.borrow.} + proc isLocalVar(a: PEffects, s: PSym): bool = s.kind in {skVar, skResult} and sfGlobal notin s.flags and s.owner == a.owner -proc initVar(a: PEffects, n: PNode) = +proc getLockLevel(t: PType): TLockLevel = + var t = t + # tyGenericInst(TLock {tyGenericBody}, tyStatic, tyObject): + if t.kind == tyGenericInst and t.len == 3: t = t.sons[1] + if t.kind == tyStatic and t.n != nil and t.n.kind in {nkCharLit..nkInt64Lit}: + result = t.n.intVal.TLockLevel + +proc lockLocations(a: PEffects; pragma: PNode) = + if pragma.kind != nkExprColonExpr: + localError(pragma.info, errGenerated, "locks pragma without argument") + return + var firstLL = TLockLevel(-1'i16) + for x in pragma[1]: + let thisLL = getLockLevel(x.typ) + if thisLL != 0.TLockLevel: + if thisLL < 0.TLockLevel or thisLL > MaxLockLevel.TLockLevel: + localError(x.info, "invalid lock level: " & $thisLL) + elif firstLL < 0.TLockLevel: firstLL = thisLL + elif firstLL != thisLL: + localError(x.info, errGenerated, + "multi-lock requires the same static lock level for every operand") + a.maxLockLevel = max(a.maxLockLevel, firstLL) + a.locked.add x + if firstLL >= 0.TLockLevel and firstLL != a.currLockLevel: + if a.currLockLevel > 0.TLockLevel and a.currLockLevel <= firstLL: + localError(pragma.info, errGenerated, + "invalid nested locking") + a.currLockLevel = firstLL + +proc guardGlobal(a: PEffects; n: PNode; guard: PSym) = + # check whether the corresponding lock is held: + for L in a.locked: + if L.kind == nkSym and L.sym == guard: return + # we allow accesses nevertheless in top level statements for + # easier initialization: + #if a.isTopLevel: + # message(n.info, warnUnguardedAccess, renderTree(n)) + #else: + if not a.isTopLevel: + localError(n.info, errGenerated, "unguarded access: " & renderTree(n)) + +# 'guard*' are checks which are concerned with 'guard' annotations +# (var x{.guard: y.}: int) +proc guardDotAccess(a: PEffects; n: PNode) = + let ri = n.sons[1] + if ri.kind != nkSym or ri.sym.kind != skField: return + var g = ri.sym.guard + if g.isNil or a.isTopLevel: return + # fixup guard: + if g.kind == skUnknown: + var field: PSym = nil + var ty = n.sons[0].typ.skipTypes(abstractPtrs) + if ty.kind == tyTuple: + field = lookupInRecord(ty.n, g.name) + else: + while ty != nil and ty.kind == tyObject: + field = lookupInRecord(ty.n, g.name) + if field != nil: break + ty = ty.sons[0] + if ty == nil: break + ty = ty.skipTypes(abstractPtrs) + if field == nil: + localError(n.info, errGenerated, "invalid guard field: " & g.name.s) + return + g = field + #ri.sym.guard = field + # XXX unfortunately this is not correct for generic instantiations! + if g.kind == skField: + let dot = newNodeI(nkDotExpr, n.info, 2) + dot.sons[0] = n.sons[0] + dot.sons[1] = newSymNode(g) + dot.typ = g.typ + for L in a.locked: + #if a.guards.sameSubexprs(dot, L): return + if guards.sameTree(dot, L): return + localError(n.info, errGenerated, "unguarded access: " & renderTree(n)) + else: + guardGlobal(a, n, g) + +proc makeVolatile(a: PEffects; s: PSym) {.inline.} = + template compileToCpp(a): expr = + gCmd == cmdCompileToCpp or sfCompileToCpp in getModule(a.owner).flags + if a.inTryStmt > 0 and not compileToCpp(a): + incl(s.flags, sfVolatile) + +proc initVar(a: PEffects, n: PNode; volatileCheck: bool) = if n.kind != nkSym: return let s = n.sym if isLocalVar(a, s): + if volatileCheck: makeVolatile(a, s) for x in a.init: if x == s.id: return a.init.add s.id @@ -91,14 +186,13 @@ proc initVarViaNew(a: PEffects, n: PNode) = if {tfNeedsInit, tfNotNil} * s.typ.flags <= {tfNotNil}: # 'x' is not nil, but that doesn't mean its "not nil" children # are initialized: - initVar(a, n) + initVar(a, n, volatileCheck=true) + elif isLocalVar(a, s): + makeVolatile(a, s) -when trackGlobals: - proc addUse(a: PEffects, e: PNode) = - var aa = a.uses - for i in 0 .. = tracked.currLockLevel: + # if in lock section: + if tracked.currLockLevel > 0.TLockLevel: + localError n.info, errGenerated, + "expected lock level < " & $tracked.currLockLevel & + " but got lock level " & $lockLevel + tracked.maxLockLevel = max(tracked.maxLockLevel, lockLevel) + proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) = let pragma = s.ast.sons[pragmasPos] let spec = effectSpec(pragma, wRaises) @@ -331,8 +447,9 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) = mergeTags(tracked, tagSpec, n) if notGcSafe(s.typ) and sfImportc notin s.flags: - if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n)) + if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) tracked.gcUnsafe = true + mergeLockLevels(tracked, n, s.getLockLevel) proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = let n = n.skipConv @@ -352,8 +469,22 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = message(n.info, errGenerated, "'$1' is provably nil" % n.renderTree) of impYes: discard +proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) = + addEffect(tracked, createRaise(n)) + addTag(tracked, createTag(n)) + let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel + else: op.lockLevel + #if lockLevel == UnknownLockLevel: + # message(n.info, warnUser, "had to assume the worst here") + mergeLockLevels(tracked, n, lockLevel) + +proc isOwnedProcVar(n: PNode; owner: PSym): bool = + # XXX prove the soundness of this effect system rule + result = n.kind == nkSym and n.sym.kind == skParam and owner == n.sym.owner + proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = - let op = skipConvAndClosure(n).typ + let a = skipConvAndClosure(n) + let op = a.typ if op != nil and op.kind == tyProc and n.kind != nkNilLit: internalAssert op.n.sons[0].kind == nkEffectList var effectList = op.n.sons[0] @@ -365,21 +496,18 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = # we have no explicit effects but it's a forward declaration and so it's # stated there are no additional effects, so simply propagate them: propagateEffects(tracked, n, n.sym) - else: + elif not isOwnedProcVar(a, tracked.owner): # we have no explicit effects so assume the worst: - addEffect(tracked, createRaise(n)) - addTag(tracked, createTag(n)) - when trackGlobals: addUse(tracked, createAnyGlobal(n)) + assumeTheWorst(tracked, n, op) # assume GcUnsafe unless in its type; 'forward' does not matter: - if notGcSafe(op): - if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n)) + if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner): + if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) tracked.gcUnsafe = true else: mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n) - when trackGlobals: mergeUses(tracked, effectList.sons[usesEffects], n) if notGcSafe(op): - if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n)) + if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) tracked.gcUnsafe = true notNilCheck(tracked, n, paramType) @@ -494,7 +622,8 @@ proc track(tracked: PEffects, n: PNode) = useVar(tracked, n) of nkRaiseStmt: n.sons[0].info = n.info - throws(tracked.exc, n.sons[0]) + #throws(tracked.exc, n.sons[0]) + addEffect(tracked, n.sons[0], useLineInfo=false) for i in 0 .. disp.typ.lockLevel: + when true: + message(branch.info, warnLockLevel, + "base method has lock level $1, but dispatcher has $2" % + [$branch.typ.lockLevel, $disp.typ.lockLevel]) + else: + # XXX make this an error after bigbreak has been released: + localError(branch.info, + "base method has lock level $1, but dispatcher has $2" % + [$branch.typ.lockLevel, $disp.typ.lockLevel]) proc setEffectsForProcType*(t: PType, n: PNode) = var effects = t.n.sons[0] @@ -659,14 +811,13 @@ proc initEffects(effects: PNode; s: PSym; t: var TEffects) = newSeq(effects.sons, effectListLen) effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info) effects.sons[tagEffects] = newNodeI(nkArgList, s.info) - effects.sons[usesEffects] = newNodeI(nkArgList, s.info) t.exc = effects.sons[exceptionEffects] t.tags = effects.sons[tagEffects] - t.uses = effects.sons[usesEffects] t.owner = s t.init = @[] t.guards = @[] + t.locked = @[] proc trackProc*(s: PSym, body: PNode) = var effects = s.typ.n.sons[0] @@ -700,9 +851,17 @@ proc trackProc*(s: PSym, body: PNode) = if optThreadAnalysis in gGlobalOptions: if sfThread in s.flags and t.gcUnsafe: - #localError(s.info, warnGcUnsafe2, s.name.s) - localError(s.info, "'$1' is not GC-safe" % s.name.s) + if optThreads in gGlobalOptions: + localError(s.info, "'$1' is not GC-safe" % s.name.s) + else: + localError(s.info, warnGcUnsafe2, s.name.s) if not t.gcUnsafe: s.typ.flags.incl tfGcSafe + if s.typ.lockLevel == UnspecifiedLockLevel: + s.typ.lockLevel = t.maxLockLevel + elif t.maxLockLevel > s.typ.lockLevel: + localError(s.info, + "declared lock level is $1, but real lock level is $2" % + [$s.typ.lockLevel, $t.maxLockLevel]) proc trackTopLevelStmt*(module: PSym; n: PNode) = if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, @@ -711,5 +870,5 @@ proc trackTopLevelStmt*(module: PSym; n: PNode) = var effects = newNode(nkEffectList, n.info) var t: TEffects initEffects(effects, module, t) - + t.isToplevel = true track(t, n) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index d394a2ae5..a7603147c 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -29,17 +29,18 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode = of nkIdent: s = lookUp(c, n.sons[0]) of nkSym: s = n.sons[0].sym else: illFormedAst(n) - if s.kind == skLabel and s.owner.id == c.p.owner.id: + if s.kind == skLabel and s.owner.id == c.p.owner.id: var x = newSymNode(s) x.info = n.info incl(s.flags, sfUsed) n.sons[0] = x suggestSym(x.info, s) + styleCheckUse(x.info, s) else: localError(n.info, errInvalidControlFlowX, s.name.s) else: localError(n.info, errGenerated, "'continue' cannot have a label") - elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0): + elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0): localError(n.info, errInvalidControlFlowX, renderTree(n, {renderNoComments})) @@ -198,11 +199,12 @@ proc semCase(c: PContext, n: PNode): PNode = var covered: BiggestInt = 0 var typ = commonTypeBegin var hasElse = false + var notOrdinal = false case skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc}).kind of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool: chckCovered = true of tyFloat..tyFloat128, tyString, tyError: - discard + notOrdinal = true else: localError(n.info, errSelectorMustBeOfCertainTypes) return @@ -232,6 +234,9 @@ proc semCase(c: PContext, n: PNode): PNode = hasElse = true else: illFormedAst(x) + if notOrdinal and not hasElse: + message(n.info, warnDeprecated, + "use 'else: discard'; non-ordinal case without 'else'") if chckCovered: if covered == toCover(n.sons[0].typ): hasElse = true @@ -258,12 +263,13 @@ proc semTry(c: PContext, n: PNode): PNode = n.sons[0] = semExprBranchScope(c, n.sons[0]) typ = commonType(typ, n.sons[0].typ) var check = initIntSet() - for i in countup(1, sonsLen(n) - 1): + var last = sonsLen(n) - 1 + for i in countup(1, last): var a = n.sons[i] checkMinSonsLen(a, 1) var length = sonsLen(a) if a.kind == nkExceptBranch: - # XXX what does this do? so that ``except [a, b, c]`` is supported? + # so that ``except [a, b, c]`` is supported: if length == 2 and a.sons[0].kind == nkBracket: a.sons[0..0] = a.sons[0].sons length = a.sonsLen @@ -277,11 +283,12 @@ proc semTry(c: PContext, n: PNode): PNode = a.sons[j].typ = typ if containsOrIncl(check, typ.id): localError(a.sons[j].info, errExceptionAlreadyHandled) - elif a.kind != nkFinally: + elif a.kind != nkFinally: illFormedAst(n) # last child of an nkExcept/nkFinally branch is a statement: a.sons[length-1] = semExprBranchScope(c, a.sons[length-1]) - typ = commonType(typ, a.sons[length-1].typ) + if a.kind != nkFinally: typ = commonType(typ, a.sons[length-1].typ) + else: dec last dec c.p.inTryStmt if isEmptyType(typ) or typ.kind == tyNil: discardCheck(c, n.sons[0]) @@ -289,13 +296,14 @@ proc semTry(c: PContext, n: PNode): PNode = if typ == enforceVoidContext: result.typ = enforceVoidContext else: + if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon) n.sons[0] = fitNode(c, typ, n.sons[0]) - for i in 1..n.len-1: + for i in 1..last: var it = n.sons[i] let j = it.len-1 it.sons[j] = fitNode(c, typ, it.sons[j]) result.typ = typ - + proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode = result = fitNode(c, typ, n) if result.kind in {nkHiddenStdConv, nkHiddenSubConv}: @@ -323,6 +331,7 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym = else: result = semIdentWithPragma(c, kind, n, {}) suggestSym(n.info, result) + styleCheckDef(result) proc checkNilable(v: PSym) = if sfGlobal in v.flags and {tfNotNil, tfNeedsInit} * v.typ.flags != {}: @@ -350,9 +359,15 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var def: PNode if a.sons[length-1].kind != nkEmpty: def = semExprWithType(c, a.sons[length-1], {efAllowDestructor}) - # BUGFIX: ``fitNode`` is needed here! - # check type compability between def.typ and typ: - if typ != nil: def = fitNode(c, typ, def) + if typ != nil: + if typ.isMetaType: + def = inferWithMetatype(c, typ, def) + typ = def.typ + else: + # BUGFIX: ``fitNode`` is needed here! + # check type compability between def.typ and typ + def = fitNode(c, typ, def) + #changeType(def.skipConv, typ, check=true) else: typ = skipIntLit(def.typ) if typ.kind in {tySequence, tyArray, tySet} and @@ -441,7 +456,7 @@ proc semConst(c: PContext, n: PNode): PNode = if typ == nil: localError(a.sons[2].info, errConstExprExpected) continue - if not typeAllowed(typ, skConst): + if not typeAllowed(typ, skConst) and def.kind != nkNilLit: localError(a.info, errXisNoType, typeToString(typ)) continue v.typ = typ @@ -454,153 +469,7 @@ proc semConst(c: PContext, n: PNode): PNode = addSon(b, copyTree(def)) addSon(result, b) -type - TFieldInstCtx = object # either 'tup[i]' or 'field' is valid - tupleType: PType # if != nil we're traversing a tuple - tupleIndex: int - field: PSym - replaceByFieldName: bool - -proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = - case n.kind - of nkEmpty..pred(nkIdent), succ(nkIdent)..nkNilLit: result = n - of nkIdent: - result = n - var L = sonsLen(forLoop) - if c.replaceByFieldName: - if n.ident.id == forLoop[0].ident.id: - let fieldName = if c.tupleType.isNil: c.field.name.s - elif c.tupleType.n.isNil: "Field" & $c.tupleIndex - else: c.tupleType.n.sons[c.tupleIndex].sym.name.s - result = newStrNode(nkStrLit, fieldName) - return - # other fields: - for i in ord(c.replaceByFieldName)..L-3: - if n.ident.id == forLoop[i].ident.id: - var call = forLoop.sons[L-2] - var tupl = call.sons[i+1-ord(c.replaceByFieldName)] - if c.field.isNil: - result = newNodeI(nkBracketExpr, n.info) - result.add(tupl) - result.add(newIntNode(nkIntLit, c.tupleIndex)) - else: - result = newNodeI(nkDotExpr, n.info) - result.add(tupl) - result.add(newSymNode(c.field, n.info)) - break - else: - if n.kind == nkContinueStmt: - localError(n.info, errGenerated, - "'continue' not supported in a 'fields' loop") - result = copyNode(n) - newSons(result, sonsLen(n)) - for i in countup(0, sonsLen(n)-1): - result.sons[i] = instFieldLoopBody(c, n.sons[i], forLoop) - -type - TFieldsCtx = object - c: PContext - m: TMagic - -proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) = - case typ.kind - of nkSym: - var fc: TFieldInstCtx # either 'tup[i]' or 'field' is valid - fc.field = typ.sym - fc.replaceByFieldName = c.m == mFieldPairs - openScope(c.c) - inc c.c.inUnrolledContext - let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop) - father.add(semStmt(c.c, body)) - dec c.c.inUnrolledContext - closeScope(c.c) - of nkNilLit: discard - of nkRecCase: - let L = forLoop.len - let call = forLoop.sons[L-2] - if call.len > 2: - localError(forLoop.info, errGenerated, - "parallel 'fields' iterator does not work for 'case' objects") - return - # iterate over the selector: - semForObjectFields(c, typ[0], forLoop, father) - # we need to generate a case statement: - var caseStmt = newNodeI(nkCaseStmt, forLoop.info) - # generate selector: - var access = newNodeI(nkDotExpr, forLoop.info, 2) - access.sons[0] = call.sons[1] - access.sons[1] = newSymNode(typ.sons[0].sym, forLoop.info) - caseStmt.add(semExprWithType(c.c, access)) - # copy the branches over, but replace the fields with the for loop body: - for i in 1 .. 0 and n.sons[genericParamsPos].kind == nkEmpty: @@ -1014,14 +887,21 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) = proc semOverride(c: PContext, s: PSym, n: PNode) = case s.name.s.normalize - of "destroy": doDestructorStuff(c, s, n) + of "destroy": + doDestructorStuff(c, s, n) + if not experimentalMode(c): + localError n.info, "use the {.experimental.} pragma to enable destructors" of "deepcopy": if s.typ.len == 2 and s.typ.sons[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and sameType(s.typ.sons[1], s.typ.sons[0]): # Note: we store the deepCopy in the base of the pointer to mitigate # the problem that pointers are structural types: - let t = s.typ.sons[1].skipTypes(abstractInst).lastSon.skipTypes(abstractInst) + var t = s.typ.sons[1].skipTypes(abstractInst).lastSon.skipTypes(abstractInst) + while true: + if t.kind == tyGenericBody: t = t.lastSon + elif t.kind == tyGenericInvokation: t = t.sons[0] + else: break if t.kind in {tyObject, tyDistinct, tyEnum}: if t.deepCopy.isNil: t.deepCopy = s else: @@ -1036,6 +916,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = of "=": discard else: localError(n.info, errGenerated, "'destroy' or 'deepCopy' expected for 'override'") + incl(s.flags, sfUsed) type TProcCompilationSteps = enum @@ -1128,7 +1009,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, pragma(c, s, n.sons[pragmasPos], validPragmas) else: implicitPragmas(c, s, n, validPragmas) - else: + else: if n.sons[pragmasPos].kind != nkEmpty: localError(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProc) if sfForward notin proto.flags: @@ -1292,9 +1173,14 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode = let pragmaList = n.sons[0] pragma(c, nil, pragmaList, exprPragmas) result = semExpr(c, n.sons[1]) + n.sons[1] = result for i in 0 .. 0: var lastStmt = lastSon(result) - if lastStmt.kind != nkNilLit and not ImplicitlyDiscardable(lastStmt): + if lastStmt.kind != nkNilLit and not implicitlyDiscardable(lastStmt): result.typ = lastStmt.typ #localError(lastStmt.info, errGenerated, # "Last expression must be explicitly returned if it " & diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index ee8b1ccb8..38b0536db 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -63,7 +63,8 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = else: # semantic checking requires a type; ``fitNode`` deals with it # appropriately - let kind = if r == scClosed: nkClosedSymChoice else: nkOpenSymChoice + let kind = if r == scClosed or n.kind == nkDotExpr: nkClosedSymChoice + else: nkOpenSymChoice result = newNodeIT(kind, n.info, newTypeS(tyNone, c)) a = initOverloadIter(o, c, n) while a != nil: @@ -71,7 +72,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = addSon(result, newSymNode(a, n.info)) a = nextOverloadIter(o, c, n) -proc semBindStmt(c: PContext, n: PNode, toBind: var TIntSet): PNode = +proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode = for i in 0 .. < n.len: var a = n.sons[i] # If 'a' is an overloaded symbol, we used to use the first symbol @@ -91,7 +92,7 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var TIntSet): PNode = illFormedAst(a) result = newNodeI(nkEmpty, n.info) -proc semMixinStmt(c: PContext, n: PNode, toMixin: var TIntSet): PNode = +proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode = for i in 0 .. < n.len: toMixin.incl(considerQuotedIdent(n.sons[i]).id) result = newNodeI(nkEmpty, n.info) @@ -106,7 +107,7 @@ proc replaceIdentBySym(n: var PNode, s: PNode) = type TemplCtx {.pure, final.} = object c: PContext - toBind, toMixin, toInject: TIntSet + toBind, toMixin, toInject: IntSet owner: PSym proc getIdentNode(c: var TemplCtx, n: PNode): PNode = @@ -146,6 +147,7 @@ proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode = if s.owner == c.owner and s.kind == skParam: incl(s.flags, sfUsed) result = newSymNode(s, n.info) + styleCheckUse(n.info, s) else: for i in 0 .. MaxSetElements: @@ -169,9 +170,10 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = range[1] = semExprWithType(c, n[2], {efDetermineType}) var rangeT: array[2, PType] - for i in 0..1: rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit + for i in 0..1: + rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit - if not sameType(rangeT[0], rangeT[1]): + if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})): localError(n.info, errPureTypeMismatch) elif not rangeT[0].isOrdinalType: localError(n.info, errOrdinalTypeExpected) @@ -279,9 +281,15 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = if n.kind == nkSym: result = n.sym else: - result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) + when defined(nimfix): + result = pickSym(c, n, skType) + if result.isNil: + result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) + else: + result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) if result != nil: markUsed(n.info, result) + styleCheckUse(n.info, result) if result.kind == skParam and result.typ.kind == tyTypeDesc: # This is a typedesc param. is it already bound? # it's not bound when it's used multiple times in the @@ -358,7 +366,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType = else: addSon(result.n, newSymNode(field)) addSonSkipIntLit(result, typ) - if gCmd == cmdPretty: checkDef(a.sons[j], field) + if gCmd == cmdPretty: styleCheckDef(a.sons[j].info, field) proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym = @@ -378,13 +386,13 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, else: result = newSymG(kind, n, c) -proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, - allowed: TSymFlags): PSym = - if n.kind == nkPragmaExpr: +proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, + allowed: TSymFlags): PSym = + if n.kind == nkPragmaExpr: checkSonsLen(n, 2) result = semIdentVis(c, kind, n.sons[0], allowed) case kind - of skType: + of skType: # process pragmas later, because result.typ has not been set yet discard of skField: pragma(c, result, n.sons[1], fieldPragmas) @@ -394,8 +402,8 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, else: discard else: result = semIdentVis(c, kind, n, allowed) - if gCmd == cmdPretty: checkDef(n, result) - + if gCmd == cmdPretty: styleCheckDef(n.info, result) + proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) = let ex = t[branchIndex][currentEx].skipConv for i in countup(1, branchIndex): @@ -461,9 +469,9 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, swap(branch.sons[L-2], branch.sons[L-1]) checkForOverlap(c, t, i, branchIndex) -proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, +proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, father: PNode, rectype: PType) -proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int, +proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int, father: PNode, rectype: PType) = var a = copyNode(n) checkMinSonsLen(n, 2) @@ -498,7 +506,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int, localError(a.info, errNotAllCasesCovered) addSon(father, a) -proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, +proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, father: PNode, rectype: PType) = if n == nil: return case n.kind @@ -524,7 +532,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, else: illFormedAst(n) if c.inGenericContext > 0: # use a new check intset here for each branch: - var newCheck: TIntSet + var newCheck: IntSet assign(newCheck, check) var newPos = pos var newf = newNodeI(nkRecList, n.info) @@ -574,11 +582,12 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, localError(n.sons[i].info, errAttemptToRedefine, f.name.s) if a.kind == nkEmpty: addSon(father, newSymNode(f)) else: addSon(a, newSymNode(f)) + styleCheckDef(f) if a.kind != nkEmpty: addSon(father, a) of nkEmpty: discard else: illFormedAst(n) -proc addInheritedFieldsAux(c: PContext, check: var TIntSet, pos: var int, +proc addInheritedFieldsAux(c: PContext, check: var IntSet, pos: var int, n: PNode) = case n.kind of nkRecCase: @@ -597,7 +606,7 @@ proc addInheritedFieldsAux(c: PContext, check: var TIntSet, pos: var int, inc(pos) else: internalError(n.info, "addInheritedFieldsAux()") -proc addInheritedFields(c: PContext, check: var TIntSet, pos: var int, +proc addInheritedFields(c: PContext, check: var IntSet, pos: var int, obj: PType) = if (sonsLen(obj) > 0) and (obj.sons[0] != nil): addInheritedFields(c, check, pos, obj.sons[0]) @@ -814,10 +823,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, for i in 1 .. = 0: - var key = substr(p.key, 0, bracketLe - 1) - var val = substr(p.key, bracketLe + 1) & ':' & p.val - processSwitch(key, val, pass, gCmdLineInfo) - else: - processSwitch(p.key, p.val, pass, gCmdLineInfo) + of cmdEnd: break + of cmdLongoption, cmdShortOption: processSwitch(pass, p) of cmdArgument: - if argsCount == 0: - options.command = p.key - else: - if pass == passCmd1: options.commandArgs.add p.key - if argsCount == 1: - # support UNIX style filenames anywhere for portable build scripts: - options.gProjectName = unixToNativePath(p.key) - arguments = cmdLineRest(p) - break - inc argsCount - + if processArgument(pass, p, argsCount): break if pass == passCmd2: if optRun notin gGlobalOptions and arguments != "" and options.command.normalize != "run": rawMessage(errArgsNeedRunOption, []) @@ -84,9 +62,9 @@ proc serve*(action: proc (){.nimcall.}) = of "tcp", "": when useCaas: var server = socket() - if server == invalidSocket: osError(osLastError()) + if server == invalidSocket: raiseOSError(osLastError()) let p = getConfigVar("server.port") - let port = if p.len > 0: parseInt(p).TPort else: 6000.TPort + let port = if p.len > 0: parseInt(p).Port else: 6000.Port server.bindAddr(port, getConfigVar("server.address")) var inp = "".TaintedString server.listen() diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index e5bf08097..06f9d58b7 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -12,7 +12,8 @@ import intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst, - magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees + magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees, + nimfix.pretty when not defined(noDocgen): import docgen @@ -21,7 +22,8 @@ type TCandidateState* = enum csEmpty, csMatch, csNoMatch - TCandidate* {.final.} = object + CandidateErrors* = seq[PSym] + TCandidate* = object c*: PContext exactMatches*: int # also misused to prefer iters over procs genericMatches: int # also misused to prefer constraints @@ -44,7 +46,7 @@ type # a distrinct type typedescMatched: bool inheritancePenalty: int # to prefer closest father object type - errors*: seq[string] # additional clarifications to be displayed to the + errors*: CandidateErrors # additional clarifications to be displayed to the # user if overload resolution fails TTypeRelation* = enum # order is important! @@ -201,16 +203,17 @@ proc writeMatches*(c: TCandidate) = writeln(stdout, "intconv matches: " & $c.intConvMatches) writeln(stdout, "generic matches: " & $c.genericMatches) -proc argTypeToString(arg: PNode): string = +proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string = if arg.kind in nkSymChoices: - result = typeToString(arg[0].typ) + result = typeToString(arg[0].typ, prefer) for i in 1 .. 0 and f.sons[0].kind != tyNone: result = typeRel(c, f.lastSon, a) + if doBind and result notin {isNone, isGeneric}: + let concrete = concreteType(c, a) + if concrete == nil: return isNone + put(c.bindings, f, concrete) else: result = isGeneric @@ -1073,6 +1083,11 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType, arg: PNode): PNode = # arg.typ can be nil in 'suggest': if isNil(arg.typ): return nil + + # sem'checking for 'echo' needs to be re-entrant: + # XXX we will revisit this issue after 0.10.2 is released + if f == arg.typ and arg.kind == nkHiddenStdConv: return arg + var call = newNodeI(nkCall, arg.info) call.add(f.n.copyTree) call.add(arg.copyTree) @@ -1143,8 +1158,8 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, of isEqual: inc(m.exactMatches) of isNone: discard - if f.kind == tyStmt and argOrig.kind == nkDo: - return argOrig[bodyPos] + if f.kind == tyStmt: + return arg elif f.kind == tyTypeDesc: return arg elif f.kind == tyStatic: @@ -1272,6 +1287,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, else: # only one valid interpretation found: markUsed(arg.info, arg.sons[best].sym) + styleCheckUse(arg.info, arg.sons[best].sym) result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best], argOrig) @@ -1319,7 +1335,7 @@ proc incrIndexType(t: PType) = inc t.sons[0].n.sons[1].intVal proc matchesAux(c: PContext, n, nOrig: PNode, - m: var TCandidate, marker: var TIntSet) = + m: var TCandidate, marker: var IntSet) = template checkConstraint(n: expr) {.immediate, dirty.} = if not formal.constraint.isNil: if matchNodeKinds(formal.constraint, n): @@ -1480,8 +1496,23 @@ proc argtypeMatches*(c: PContext, f, a: PType): bool = # instantiate generic converters for that result = res != nil +proc instDeepCopy*(c: PContext; dc: PSym; t: PType; info: TLineInfo): PSym {. + procvar.} = + var m: TCandidate + initCandidate(c, m, dc.typ) + var f = dc.typ.sons[1] + if f.kind in {tyRef, tyPtr}: f = f.lastSon + if typeRel(m, f, t) == isNone: + localError(info, errGenerated, "cannot instantiate 'deepCopy'") + else: + result = c.semGenerateInstance(c, dc, m.bindings, info) + assert sfFromGeneric in result.flags + include suggest +when not declared(tests): + template tests(s: stmt) {.immediate.} = discard + tests: var dummyOwner = newSym(skModule, getIdent("test_module"), nil, UnknownLineInfo()) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index c2bdfc5c3..c700db323 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -11,7 +11,7 @@ # included from sigmatch.nim -import algorithm, sequtils, pretty +import algorithm, sequtils const sep = '\t' @@ -331,7 +331,6 @@ proc markUsed(info: TLineInfo; s: PSym) = if sfDeprecated in s.flags: message(info, warnDeprecated, s.name.s) if sfError in s.flags: localError(info, errWrongSymbolX, s.name.s) suggestSym(info, s) - if gCmd == cmdPretty: checkUse(info, s) proc useSym*(sym: PSym): PNode = result = newSymNode(sym) diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index d5062544f..61677a641 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -44,7 +44,7 @@ proc parseTopLevelStmt*(p: var TParsers): PNode proc parseFile(fileIdx: int32): PNode = var p: TParsers - f: TFile + f: File let filename = fileIdx.toFullPath if not open(f, filename): rawMessage(errCannotOpenFile, filename) diff --git a/compiler/tccgen.nim b/compiler/tccgen.nim index a571d416e..0082dfcbb 100644 --- a/compiler/tccgen.nim +++ b/compiler/tccgen.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/compiler/transf.nim b/compiler/transf.nim index 1ec31d605..3409acb74 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -582,7 +582,7 @@ proc getMergeOp(n: PNode): PSym = else: discard proc flattenTreeAux(d, a: PNode, op: PSym) = - var op2 = getMergeOp(a) + let op2 = getMergeOp(a) if op2 != nil and (op2.id == op.id or op.magic != mNone and op2.magic == op.magic): for i in countup(1, sonsLen(a)-1): flattenTreeAux(d, a.sons[i], op) @@ -590,7 +590,7 @@ proc flattenTreeAux(d, a: PNode, op: PSym) = addSon(d, copyTree(a)) proc flattenTree(root: PNode): PNode = - var op = getMergeOp(root) + let op = getMergeOp(root) if op != nil: result = copyNode(root) addSon(result, copyTree(root.sons[0])) @@ -600,8 +600,9 @@ proc flattenTree(root: PNode): PNode = proc transformCall(c: PTransf, n: PNode): PTransNode = var n = flattenTree(n) - var op = getMergeOp(n) - if (op != nil) and (op.magic != mNone) and (sonsLen(n) >= 3): + let op = getMergeOp(n) + let magic = getMagic(n) + if op != nil and op.magic != mNone and n.len >= 3: result = newTransNode(nkCall, n, 0) add(result, transform(c, n.sons[0])) var j = 1 @@ -616,9 +617,12 @@ proc transformCall(c: PTransf, n: PNode): PTransNode = inc(j) add(result, a.PTransNode) if len(result) == 2: result = result[1] - elif getMagic(n) == mNBindSym: + elif magic == mNBindSym: # for bindSym(myconst) we MUST NOT perform constant folding: result = n.PTransNode + elif magic == mProcCall: + # but do not change to its dispatcher: + result = transformSons(c, n[1]) else: let s = transformSons(c, n).PNode # bugfix: check after 'transformSons' if it's still a method call: @@ -785,8 +789,8 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode = # result = lambdalifting.liftIterator(prc, result) incl(result.flags, nfTransf) when useEffectSystem: trackProc(prc, result) - if prc.name.s == "testbody": - echo renderTree(result) + #if prc.name.s == "testbody": + # echo renderTree(result) proc transformStmt*(module: PSym, n: PNode): PNode = if nfTransf in n.flags: diff --git a/compiler/trees.nim b/compiler/trees.nim index 35e9334cc..86a1139a0 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -117,7 +117,9 @@ proc isDeepConstExpr*(n: PNode): bool = of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure: for i in 0 .. 0: result = firstOrd(lastSon(t)) @@ -757,7 +762,7 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool = # two tuples are equivalent iff the names, types and positions are the same; # however, both types may not have any field names (t.n may be nil) which # complicates the matter a bit. - if sonsLen(a) == sonsLen(b): + if sonsLen(a) == sonsLen(b): result = true for i in countup(0, sonsLen(a) - 1): var x = a.sons[i] @@ -768,17 +773,6 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool = result = sameTypeAux(x, y, c) if not result: return - if a.n != nil and b.n != nil and IgnoreTupleFields notin c.flags: - for i in countup(0, sonsLen(a.n) - 1): - # check field names: - if a.n.sons[i].kind == nkSym and b.n.sons[i].kind == nkSym: - var x = a.n.sons[i].sym - var y = b.n.sons[i].sym - result = x.name.id == y.name.id - if not result: break - else: internalError(a.n.info, "sameTuple") - else: - result = false template ifFastObjectTypeCheckFailed(a, b: PType, body: stmt) {.immediate.} = if tfFromGeneric notin a.flags + b.flags: @@ -969,6 +963,7 @@ proc inheritanceDiff*(a, b: PType): int = # | returns: -x iff `a` is the x'th direct superclass of `b` # | returns: +x iff `a` is the x'th direct subclass of `b` # | returns: `maxint` iff `a` and `b` are not compatible at all + if a == b or a.kind == tyError or b.kind == tyError: return 0 assert a.kind == tyObject assert b.kind == tyObject var x = a @@ -1014,10 +1009,10 @@ type TTypeAllowedFlags = set[TTypeAllowedFlag] -proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind, +proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, flags: TTypeAllowedFlags = {}): bool -proc typeAllowedNode(marker: var TIntSet, n: PNode, kind: TSymKind, +proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind, flags: TTypeAllowedFlags = {}): bool = result = true if n != nil: @@ -1041,7 +1036,7 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]], a = a.sons[i] result = a.kind == last -proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind, +proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, flags: TTypeAllowedFlags = {}): bool = assert(kind in {skVar, skLet, skConst, skParam, skResult}) # if we have already checked the type, return true, because we stop the @@ -1207,8 +1202,11 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = else: result = ptrSize a = ptrSize of tyNil, tyCString, tyString, tySequence, tyPtr, tyRef, tyVar, tyOpenArray, - tyBigNum: - result = ptrSize + tyBigNum: + let base = typ.lastSon + if base == typ or (base.kind == tyTuple and base.size==szIllegalRecursion): + result = szIllegalRecursion + else: result = ptrSize a = result of tyArray, tyArrayConstr: let elemSize = computeSizeAux(typ.sons[1], a) @@ -1270,7 +1268,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = #internalError("computeSizeAux()") result = szUnknownSize typ.size = result - typ.align = int(a) + typ.align = int16(a) proc computeSize(typ: PType): BiggestInt = var a: BiggestInt = 1 @@ -1285,7 +1283,7 @@ proc getSize(typ: PType): BiggestInt = result = computeSize(typ) if result < 0: internalError("getSize: " & $typ.kind) -proc containsGenericTypeIter(t: PType, closure: PObject): bool = +proc containsGenericTypeIter(t: PType, closure: RootRef): bool = if t.kind == tyStatic: return t.n == nil @@ -1359,7 +1357,8 @@ proc compatibleEffects*(formal, actual: PType): bool = if real.len == 0: return false result = compatibleEffectsAux(st, real.sons[tagEffects]) if not result: return - result = true + result = formal.lockLevel.ord < 0 or + actual.lockLevel.ord <= formal.lockLevel.ord proc isCompileTimeOnly*(t: PType): bool {.inline.} = result = t.kind in {tyTypeDesc, tyStatic} diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim index f3121a0b4..432a26666 100644 --- a/compiler/typesrenderer.nim +++ b/compiler/typesrenderer.nim @@ -1,3 +1,12 @@ +# +# +# The Nim Compiler +# (c) Copyright 2014 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + import renderer, strutils, ast, msgs, types, astalgo const defaultParamSeparator* = "," @@ -26,7 +35,7 @@ proc renderPlainSymbolName*(n: PNode): string = result = renderPlainSymbolName(n[= 0: + # we're at the end of the *except list*, but maybe there is another + # *except branch*? + pc2 = nextExceptOrFinally+1 + if c.code[pc2].opcode == opcExcept: + nextExceptOrFinally = pc2 + c.code[pc2].regBx - wordExcess + if nextExceptOrFinally >= 0: pc2 = nextExceptOrFinally if c.code[pc2].opcode == opcFinally: @@ -318,7 +328,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = myreset(dest); dest.kind = rkInt case skipTypes(srctyp, abstractRange).kind of tyFloat..tyFloat64: - dest.intVal = system.toInt(src.floatVal) + dest.intVal = int(src.floatVal) else: dest.intVal = src.intVal if dest.intVal < firstOrd(desttyp) or dest.intVal > lastOrd(desttyp): @@ -328,7 +338,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = myreset(dest); dest.kind = rkInt case skipTypes(srctyp, abstractRange).kind of tyFloat..tyFloat64: - dest.intVal = system.toInt(src.floatVal) + dest.intVal = int(src.floatVal) else: dest.intVal = src.intVal and ((1 shl (desttyp.size*8))-1) of tyFloat..tyFloat64: @@ -444,8 +454,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcLdStrIdx: decodeBC(rkInt) let idx = regs[rc].intVal.int - if idx <=% regs[rb].node.strVal.len: - regs[ra].intVal = regs[rb].node.strVal[idx].ord + let s = regs[rb].node.strVal + if s.isNil: + stackTrace(c, tos, pc, errNilAccess) + elif idx <=% s.len: + regs[ra].intVal = s[idx].ord else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcWrArr: @@ -801,7 +814,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let bb = regs[rb].node let isClosure = bb.kind == nkPar let prc = if not isClosure: bb.sym else: bb.sons[0].sym - if sfImportc in prc.flags: + if prc.offset < -1: + # it's a callback: + c.callbacks[-prc.offset-2].value( + VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[pointer](regs), + currentException: c.currentExceptionB)) + elif sfImportc in prc.flags: if allowFFI notin c.features: globalError(c.debug[pc], errGenerated, "VM not allowed to do FFI") # we pass 'tos.slots' instead of 'regs' so that the compiler can keep @@ -832,9 +850,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if isClosure: newFrame.slots[rc].kind = rkNode newFrame.slots[rc].node = regs[rb].node.sons[1] - # allocate the temporaries: - #for i in rc+ord(isClosure) .. max: stackTrace(c, tos, pc, errGenerated, msgKindToString(errUnhandledExceptionX) % "value out of range") @@ -1043,7 +1061,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].intVal = regs[ra].intVal and ((1'i64 shl rb)-1) of opcIsNil: decodeB(rkInt) - regs[ra].intVal = ord(regs[rb].node.kind == nkNilLit) + let node = regs[rb].node + regs[ra].intVal = ord(node.kind == nkNilLit or + (node.kind in {nkStrLit..nkTripleStrLit} and node.strVal.isNil)) of opcNBindSym: decodeBx(rkNode) regs[ra].node = copyTree(c.constants.sons[rbx]) @@ -1132,16 +1152,34 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcParseExprToAst: decodeB(rkNode) # c.debug[pc].line.int - countLines(regs[rb].strVal) ? - let ast = parseString(regs[rb].node.strVal, c.debug[pc].toFilename, - c.debug[pc].line.int) - if sonsLen(ast) != 1: - globalError(c.debug[pc], errExprExpected, "multiple statements") - regs[ra].node = ast.sons[0] + var error: string + let ast = parseString(regs[rb].node.strVal, c.debug[pc].toFullPath, + c.debug[pc].line.int, + proc (info: TLineInfo; msg: TMsgKind; arg: string) = + if error.isNil and msg <= msgs.errMax: + error = formatMsg(info, msg, arg)) + if not error.isNil: + c.errorFlag = error + elif sonsLen(ast) != 1: + c.errorFlag = formatMsg(c.debug[pc], errExprExpected, "multiple statements") + else: + regs[ra].node = ast.sons[0] of opcParseStmtToAst: decodeB(rkNode) - let ast = parseString(regs[rb].node.strVal, c.debug[pc].toFilename, - c.debug[pc].line.int) - regs[ra].node = ast + var error: string + let ast = parseString(regs[rb].node.strVal, c.debug[pc].toFullPath, + c.debug[pc].line.int, + proc (info: TLineInfo; msg: TMsgKind; arg: string) = + if error.isNil and msg <= msgs.errMax: + error = formatMsg(info, msg, arg)) + if not error.isNil: + c.errorFlag = error + else: + regs[ra].node = ast + of opcQueryErrorFlag: + createStr regs[ra] + regs[ra].node.strVal = c.errorFlag + c.errorFlag.setLen 0 of opcCallSite: ensureKind(rkNode) if c.callsite != nil: regs[ra].node = c.callsite @@ -1316,6 +1354,8 @@ proc evalExpr*(c: PCtx, n: PNode): PNode = assert c.code[start].opcode != opcEof result = execute(c, start) +include vmops + # for now we share the 'globals' environment. XXX Coming soon: An API for # storing&loading the 'globals' environment to get what a component system # requires. @@ -1325,6 +1365,7 @@ var proc setupGlobalCtx(module: PSym) = if globalCtx.isNil: globalCtx = newCtx(module) else: refresh(globalCtx, module) + registerAdditionalOps(globalCtx) proc myOpen(module: PSym): PPassContext = #var c = newEvalContext(module, emRepl) @@ -1390,6 +1431,12 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode = inc(evalMacroCounter) if evalMacroCounter > 100: globalError(n.info, errTemplateInstantiationTooNested) + + # immediate macros can bypass any type and arity checking so we check the + # arity here too: + if sym.typ.len > n.safeLen and sym.typ.len > 1: + globalError(n.info, "got $#, but expected $# argument(s)" % [$ = L + # return value: tos.slots[0].kind = rkNode tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0]) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index cad48abea..3d49cb130 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -92,6 +92,7 @@ type opcGorge, opcParseExprToAst, opcParseStmtToAst, + opcQueryErrorFlag, opcNError, opcNWarning, opcNHint, @@ -163,14 +164,21 @@ type slotTempInt, # some temporary int slotTempFloat, # some temporary float slotTempStr, # some temporary string - slotTempComplex # some complex temporary (s.node field is used) + slotTempComplex, # some complex temporary (s.node field is used) + slotTempPerm # slot is temporary but permanent (hack) PProc* = ref object blocks*: seq[TBlock] # blocks; temp data structure sym*: PSym slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]] maxSlots*: int - + + VmArgs* = object + ra*, rb*, rc*: Natural + slots*: pointer + currentException*: PNode + VmCallback* = proc (args: VmArgs) {.closure.} + PCtx* = ref TCtx TCtx* = object of passes.TPassContext # code gen context code*: seq[TInstr] @@ -189,6 +197,8 @@ type traceActive*: bool loopIterations*: int comesFromHeuristic*: TLineInfo # Heuristic for better macro stack traces + callbacks*: seq[tuple[key: string, value: VmCallback]] + errorFlag*: string TPosition* = distinct int @@ -198,12 +208,15 @@ proc newCtx*(module: PSym): PCtx = PCtx(code: @[], debug: @[], globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[], prc: PProc(blocks: @[]), module: module, loopIterations: MaxLoopIterations, - comesFromHeuristic: unknownLineInfo()) + comesFromHeuristic: unknownLineInfo(), callbacks: @[], errorFlag: "") proc refresh*(c: PCtx, module: PSym) = c.module = module c.prc = PProc(blocks: @[]) +proc registerCallback*(c: PCtx; name: string; callback: VmCallback) = + c.callbacks.add((name, callback)) + const firstABxInstr* = opcTJmp largeInstrs* = { # instructions which use 2 int32s instead of 1: diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index fdd8276cc..6673d0bd2 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -9,7 +9,7 @@ import ast, types, msgs, osproc, streams, options -proc readOutput(p: PProcess): string = +proc readOutput(p: Process): string = result = "" var output = p.outputStream while not output.atEnd: @@ -33,6 +33,6 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym): string = # the module dependencies are accurate: appendToModule(module, newNode(nkIncludeStmt, info, @[ newStrNode(nkStrLit, filename)])) - except EIO: + except IOError: localError(info, errCannotOpenFile, file) result = "" diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index a4ddc2e15..9a3fc260a 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -16,7 +16,7 @@ # types that use the 'node' field; the reason is that slots are # re-used in a register based VM. Example: # -# .. code-block:: nimrod +# .. code-block:: nim # let s = a & b # no matter what, create fresh node # s = a & b # no matter what, keep the node # @@ -28,7 +28,7 @@ # this copy depends on the involved types. import - unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef, + unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef, trees, intsets, rodread, magicsys, options, lowerings from os import splitFile @@ -46,7 +46,6 @@ proc debugInfo(info: TLineInfo): string = proc codeListing(c: PCtx, result: var string, start=0; last = -1) = # first iteration: compute all necessary labels: var jumpTargets = initIntSet() - let last = if last < 0: c.code.len-1 else: min(last, c.code.len-1) for i in start..last: let x = c.code[i] @@ -59,6 +58,7 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) = if i in jumpTargets: result.addf("L$1:\n", i) let x = c.code[i] + result.add($i) let opc = opcode(x) if opc in {opcConv, opcCast}: let y = c.code[i+1] @@ -83,7 +83,7 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) = result.add("\n") inc i -proc echoCode*(c: PCtx, start=0; last = -1) {.deprecated.} = +proc echoCode*(c: PCtx; start=0; last = -1) {.deprecated.} = var buf = "" codeListing(c, buf, start, last) echo buf @@ -189,7 +189,7 @@ proc getTemp(c: PCtx; typ: PType): TRegister = proc freeTemp(c: PCtx; r: TRegister) = let c = c.prc - if c.slots[r].kind >= slotSomeTemp: c.slots[r].inUse = false + if c.slots[r].kind in {slotSomeTemp..slotTempComplex}: c.slots[r].inUse = false proc getTempRange(c: PCtx; n: int; kind: TSlotKind): TRegister = # if register pressure is high, we re-use more aggressively: @@ -371,8 +371,8 @@ proc sameConstant*(a, b: PNode): bool = of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal - of nkType: result = a.typ == b.typ - of nkEmpty, nkNilLit: result = true + of nkType, nkNilLit: result = a.typ == b.typ + of nkEmpty: result = true else: if sonsLen(a) == sonsLen(b): for i in countup(0, sonsLen(a) - 1): @@ -464,7 +464,7 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) = # from the stack c.gABx(fin, opcFinally, 0, 0) if fin.kind == nkFinally: - c.gen(fin.sons[0], dest) + c.gen(fin.sons[0]) c.clearDest(n, dest) c.gABx(fin, opcFinallyEnd, 0, 0) @@ -900,12 +900,14 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = c.freeTemp(tmp) of mEcho: unused(n, dest) - let x = c.getTempRange(n.len-1, slotTempUnknown) - for i in 1.. = slotTempUnknown: gABC(c, n, opcAddrNode, dest, tmp) + # hack ahead; in order to fix bug #1781 we mark the temporary as + # permanent, so that it's not used for anything else: + c.prc.slots[tmp].kind = slotTempPerm + # XXX this is still a hack + #message(n.info, warnUser, "suspicious opcode used") else: gABC(c, n, opcAddrReg, dest, tmp) c.freeTemp(tmp) @@ -1124,6 +1136,7 @@ proc checkCanEval(c: PCtx; n: PNode) = # we need to ensure that we don't evaluate 'x' here: # proc foo() = var x ... let s = n.sym + if {sfCompileTime, sfGlobal} <= s.flags: return if s.kind in {skVar, skTemp, skLet, skParam, skResult} and not s.isOwnedBy(c.prc.sym) and s.owner != c.module: cannotEval(n) @@ -1134,6 +1147,9 @@ proc isTemp(c: PCtx; dest: TDest): bool = template needsAdditionalCopy(n): expr = not c.isTemp(dest) and not fitsRegister(n.typ) +proc skipDeref(n: PNode): PNode = + result = if n.kind in {nkDerefExpr, nkHiddenDeref}: n.sons[0] else: n + proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode; dest, idx, value: TRegister) = # opcLdObj et al really means "load address". We sometimes have to create a @@ -1337,7 +1353,9 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = result = newNodeIT(nkUIntLit, info, t) of tyFloat..tyFloat128: result = newNodeIT(nkFloatLit, info, t) - of tyVar, tyPointer, tyPtr, tyCString, tySequence, tyString, tyExpr, + of tyCString, tyString: + result = newNodeIT(nkStrLit, info, t) + of tyVar, tyPointer, tyPtr, tySequence, tyExpr, tyStmt, tyTypeDesc, tyStatic, tyRef: result = newNodeIT(nkNilLit, info, t) of tyProc: @@ -1497,6 +1515,26 @@ proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) = proc genProc*(c: PCtx; s: PSym): int +proc matches(s: PSym; x: string): bool = + let y = x.split('.') + var s = s + var L = y.len-1 + while L >= 0: + if s == nil or y[L].cmpIgnoreStyle(s.name.s) != 0: return false + s = s.owner + dec L + result = true + +proc procIsCallback(c: PCtx; s: PSym): bool = + if s.offset < -1: return true + var i = -2 + for key, value in items(c.callbacks): + if s.matches(key): + doAssert s.offset == -1 + s.offset = i + return true + dec i + proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = case n.kind of nkSym: @@ -1507,7 +1545,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = genRdVar(c, n, dest, flags) of skProc, skConverter, skMacro, skTemplate, skMethod, skIterators: # 'skTemplate' is only allowed for 'getAst' support: - if sfImportc in s.flags: c.importcSym(n.info, s) + if procIsCallback(c, s): discard + elif sfImportc in s.flags: c.importcSym(n.info, s) genLit(c, n, dest) of skConst: gen(c, s.ast, dest) @@ -1536,7 +1575,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = genLit(c, n, dest) of nkUIntLit..pred(nkNilLit): genLit(c, n, dest) of nkNilLit: - if not n.typ.isEmptyType: genLit(c, n, dest) + if not n.typ.isEmptyType: genLit(c, getNullValue(n.typ, n.info), dest) else: unused(n, dest) of nkAsgn, nkFastAsgn: unused(n, dest) @@ -1569,11 +1608,15 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = let L = n.len-1 for i in 0 .. -

$sectionTitle

+

$sectionTitle

$content
@@ -17,8 +17,8 @@ $content doc.section.toc = """
  • - $sectionTitle -
      + $sectionTitle +
        $content
      @@ -41,6 +41,7 @@ doc.item = """
      $header
      $desc +$seeSrc
      """ @@ -58,22 +59,25 @@ doc.item.toc = """ # * $line: line of the item in the original source file. # * $url: whatever you did pass through the --docSeeSrcUrl switch (which also # gets variables path/line replaced!) -doc.item.seesrc = """See source""" +doc.item.seesrc = """  Source""" doc.toc = """ -""" +""" doc.body_toc = """ -$tableofcontents -
      -$moduledesc -$content +
      +
      + $tableofcontents +
      +
      +

      $moduledesc

      + $content +
      """ @@ -89,136 +93,782 @@ doc.file = """ + + + + + + + + + $title -
      -

      $title

      -$content -Generated: $date $time UTC +
      +

      $title

      + $content +
      + +
      +
      diff --git a/config/nimrod.cfg b/config/nimrod.cfg index 075b65d63..b7210b329 100644 --- a/config/nimrod.cfg +++ b/config/nimrod.cfg @@ -1,5 +1,5 @@ -# Configuration file for the Nimrod Compiler. -# (c) 2013 Andreas Rumpf +# Configuration file for the Nim Compiler. +# (c) 2014 Andreas Rumpf # Feel free to edit the default values as you need. @@ -13,6 +13,8 @@ cc = gcc arm.linux.gcc.exe = "arm-linux-gcc" arm.linux.gcc.linkerexe = "arm-linux-gcc" +cs:partial + path="$lib/core" path="$lib/pure" path="$lib/pure/collections" diff --git a/doc/advopt.txt b/doc/advopt.txt index 7a11e9041..78c3d571a 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -12,7 +12,6 @@ Advanced commands: module dependency graph //dump dump all defined conditionals and search paths //check checks the project for syntax and semantic - //pretty homogenizes source code style //idetools compiler support for IDEs: possible options: --track:FILE,LINE,COL track a file/cursor position --trackDirty:DIRTY_FILE,ORIG_FILE,LINE,COL @@ -28,7 +27,6 @@ Advanced commands: --server.address:HOST binds to that address, by default "" Advanced options: - -m, --mainmodule:FILE set the project main module -o, --out:FILE set the output filename --stdout output to stdout --listFullPaths list full paths in messages @@ -50,7 +48,7 @@ Advanced options: --os:SYMBOL set the target operating system (cross-compilation) --cpu:SYMBOL set the target processor (cross-compilation) --debuginfo enables debug information - --debugger:on|off turn Embedded Nimrod Debugger on|off + --debugger:on|off turn Embedded Nim Debugger on|off -t, --passC:OPTION pass an option to the C compiler -l, --passL:OPTION pass an option to the linker --cincludes:DIR modify the C compiler header search path @@ -58,7 +56,7 @@ Advanced options: --clib:LIBNAME link an additional C library (you should omit platform-specific extensions) --genMapping generate a mapping file containing - (Nimrod, mangled) identifier pairs + (Nim, mangled) identifier pairs --project document the whole project (doc2) --docSeeSrcUrl:url activate 'see source' for doc and doc2 commands (see doc.item.seesrc in config/nimdoc.cfg) @@ -79,7 +77,8 @@ Advanced options: select the GC to use; default is 'refc' --index:on|off turn index file generation on|off --putenv:key=value set an environment variable - --babelPath:PATH add a path for Babel support + --NimblePath:PATH add a path for Nimble support + --noNimblePath deactivate the Nimble path --excludePath:PATH exclude a path from the list of search paths --dynlibOverride:SYMBOL marks SYMBOL so that dynlib:SYMBOL has no effect and can be statically linked instead; @@ -89,7 +88,8 @@ Advanced options: --listCmd list the commands used to execute external programs --parallelBuild=0|1|... perform a parallel build value = number of processors (0 for auto-detect) - --verbosity:0|1|2|3 set Nimrod's verbosity level (1 is default) + --verbosity:0|1|2|3 set Nim's verbosity level (1 is default) --cs:none|partial set case sensitivity level (default: none); do not use! this setting affects the whole language + --experimental enable experimental language features -v, --version show detailed version information diff --git a/doc/apis.txt b/doc/apis.txt index 9906f6bbc..e0510d85f 100644 --- a/doc/apis.txt +++ b/doc/apis.txt @@ -84,5 +84,5 @@ indentation indent Coding Guidelines ================= -For coding guidelines see the `Internals of the Nimrod Compiler +For coding guidelines see the `Internals of the Nim Compiler `_ documentation. diff --git a/doc/astspec.txt b/doc/astspec.txt index 6b6242614..5c4274093 100644 --- a/doc/astspec.txt +++ b/doc/astspec.txt @@ -1,11 +1,11 @@ -The AST in Nimrod +The AST in Nim ================= -This section describes how the AST is modelled with Nimrod's type system. +This section describes how the AST is modelled with Nim's type system. The AST consists of nodes (``PNimrodNode``) with a variable number of children. Each node has a field named ``kind`` which describes what the node contains: -.. code-block:: nimrod +.. code-block:: nim type TNimrodNodeKind = enum ## kind of a node; only explanatory @@ -39,7 +39,7 @@ contains: For the ``PNimrodNode`` type, the ``[]`` operator has been overloaded: ``n[i]`` is ``n``'s ``i``-th child. -To specify the AST for the different Nimrod constructs, the notation +To specify the AST for the different Nim constructs, the notation ``nodekind(son1, son2, ...)`` or ``nodekind(value)`` or ``nodekind(field=value)`` is used. @@ -53,7 +53,7 @@ A leaf of the AST often corresponds to a terminal symbol in the concrete syntax. ----------------- --------------------------------------------- -Nimrod expression corresponding AST +Nim expression corresponding AST ----------------- --------------------------------------------- ``42`` ``nnkIntLit(intVal = 42)`` ``42'i8`` ``nnkInt8Lit(intVal = 42)`` @@ -87,12 +87,12 @@ Command call Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim echo "abc", "xyz" AST: -.. code-block:: nimrod +.. code-block:: nim nnkCommand(nnkIdent(!"echo"), nnkStrLit("abc"), nnkStrLit("xyz")) @@ -101,12 +101,12 @@ Call with ``()`` Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim echo("abc", "xyz") AST: -.. code-block:: nimrod +.. code-block:: nim nnkCall(nnkIdent(!"echo"), nnkStrLit("abc"), nnkStrLit("xyz")) @@ -115,12 +115,12 @@ Infix operator call Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim "abc" & "xyz" AST: -.. code-block:: nimrod +.. code-block:: nim nnkInfix(nnkIdent(!"&"), nnkStrLit("abc"), nnkStrLit("xyz")) @@ -129,29 +129,29 @@ Prefix operator call Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim ? "xyz" AST: -.. code-block:: nimrod +.. code-block:: nim nnkPrefix(nnkIdent(!"?"), nnkStrLit("abc")) Postfix operator call --------------------- -**Note:** There are no postfix operators in Nimrod. However, the +**Note:** There are no postfix operators in Nim. However, the ``nnkPostfix`` node is used for the *asterisk export marker* ``*``: Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim identifier* AST: -.. code-block:: nimrod +.. code-block:: nim nnkPostfix(nnkIdent(!"*"), nnkIdent(!"identifier")) @@ -160,12 +160,12 @@ Call with named arguments Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim writeln(file=stdout, "hallo") AST: -.. code-block:: nimrod +.. code-block:: nim nnkCall(nnkIdent(!"writeln"), nnkExprEqExpr(nnkIdent(!"file"), nnkIdent(!"stdout")), nnkStrLit("hallo")) @@ -176,12 +176,12 @@ Dereference operator ``^`` Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim x^ AST: -.. code-block:: nimrod +.. code-block:: nim nnkDerefExpr(nnkIdent(!"x")) @@ -190,12 +190,12 @@ Addr operator Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim addr(x) AST: -.. code-block:: nimrod +.. code-block:: nim nnkAddr(nnkIdent(!"x")) @@ -204,12 +204,12 @@ Cast operator Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim cast[T](x) AST: -.. code-block:: nimrod +.. code-block:: nim nnkCast(nnkIdent(!"T"), nnkIdent(!"x")) @@ -218,12 +218,12 @@ Object access operator ``.`` Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim x.y AST: -.. code-block:: nimrod +.. code-block:: nim nnkDotExpr(nnkIdent(!"x"), nnkIdent(!"y")) @@ -232,12 +232,12 @@ Array access operator ``[]`` Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim x[y] AST: -.. code-block:: nimrod +.. code-block:: nim nnkBracketExpr(nnkIdent(!"x"), nnkIdent(!"y")) @@ -249,12 +249,12 @@ are built with the ``nnkPar`` node. Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim (1, 2, (3)) AST: -.. code-block:: nimrod +.. code-block:: nim nnkPar(nnkIntLit(1), nnkIntLit(2), nnkPar(nnkIntLit(3))) @@ -265,12 +265,12 @@ Curly braces are used as the set constructor. Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim {1, 2, 3} AST: -.. code-block:: nimrod +.. code-block:: nim nnkCurly(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3)) @@ -281,12 +281,12 @@ Brackets are used as the array constructor. Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim [1, 2, 3] AST: -.. code-block:: nimrod +.. code-block:: nim nnkBracket(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3)) @@ -297,12 +297,12 @@ Ranges occur in set constructors, case statement branches or array slices. Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim 1..3 AST: -.. code-block:: nimrod +.. code-block:: nim nnkRange(nnkIntLit(1), nnkIntLit(3)) @@ -313,12 +313,12 @@ The representation of the if expression is subtle, but easy to traverse. Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim if cond1: expr1 elif cond2: expr2 else: expr3 AST: -.. code-block:: nimrod +.. code-block:: nim nnkIfExpr( nnkElifExpr(cond1, expr1), nnkElifExpr(cond2, expr2), @@ -337,7 +337,7 @@ there is no ``else`` branch, no ``nnkElse`` child exists. Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim if cond1: stmt1 elif cond2: @@ -349,7 +349,7 @@ Concrete syntax: AST: -.. code-block:: nimrod +.. code-block:: nim nnkIfStmt( nnkElifBranch(cond1, stmt1), nnkElifBranch(cond2, stmt2), @@ -369,12 +369,12 @@ Assignment Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim x = 42 AST: -.. code-block:: nimrod +.. code-block:: nim nnkAsgn(nnkIdent(!"x"), nnkIntLit(42)) @@ -383,14 +383,14 @@ Statement list Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim stmt1 stmt2 stmt3 AST: -.. code-block:: nimrod +.. code-block:: nim nnkStmtList(stmt1, stmt2, stmt3) @@ -399,7 +399,7 @@ Case statement Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim case expr1 of expr2, expr3..expr4: stmt1 @@ -412,7 +412,7 @@ Concrete syntax: AST: -.. code-block:: nimrod +.. code-block:: nim nnkCaseStmt( expr1, nnkOfBranch(expr2, nnkRange(expr3, expr4), stmt1), @@ -429,13 +429,13 @@ While statement Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim while expr1: stmt1 AST: -.. code-block:: nimrod +.. code-block:: nim nnkWhileStmt(expr1, stmt1) @@ -444,13 +444,13 @@ For statement Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim for ident1, ident2 in expr1: stmt1 AST: -.. code-block:: nimrod +.. code-block:: nim nnkForStmt(ident1, ident2, expr1, stmt1) @@ -459,7 +459,7 @@ Try statement Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim try: stmt1 except e1, e2: @@ -473,7 +473,7 @@ Concrete syntax: AST: -.. code-block:: nimrod +.. code-block:: nim nnkTryStmt( stmt1, nnkExceptBranch(e1, e2, stmt2), @@ -488,12 +488,12 @@ Return statement Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim return expr1 AST: -.. code-block:: nimrod +.. code-block:: nim nnkReturnStmt(expr1) @@ -514,12 +514,12 @@ Continue statement Concrete syntax: -.. code-block:: nimrod +.. code-block:: nim continue AST: -.. code-block:: nimrod +.. code-block:: nim nnkContinueStmt() Var section diff --git a/doc/backends.txt b/doc/backends.txt index c2dbb0af6..eb16217cd 100644 --- a/doc/backends.txt +++ b/doc/backends.txt @@ -1,9 +1,9 @@ ================================ - Nimrod Backend Integration + Nim Backend Integration ================================ :Author: Puppet Master -:Version: |nimrodversion| +:Version: |nimversion| .. contents:: "Heresy grows from idleness." -- Unknown. @@ -12,20 +12,20 @@ Introduction ============ -The `Nimrod Compiler User Guide `_ documents the typical +The `Nim Compiler User Guide `_ documents the typical compiler invocation, using the ``compile`` or ``c`` command to transform a ``.nim`` file into one or more ``.c`` files which are then compiled with the platform's C compiler into a static binary. However there are other commands to compile to C++, Objective-C or JavaScript. This document tries to concentrate in a single place all the backend and interfacing options. -The Nimrod compiler supports mainly two backend families: the C, C++ and +The Nim compiler supports mainly two backend families: the C, C++ and Objective-C targets and the JavaScript target. `The C like targets`_ creates source files which can be compiled into a library or a final executable. `The JavaScript target`_ can generate a ``.js`` file which you reference from an HTML file or create a `standalone nodejs program `_. -On top of generating libraries or standalone applications, Nimrod offers +On top of generating libraries or standalone applications, Nim offers bidirectional interfacing with the backend targets through generic and specific pragmas. @@ -49,25 +49,25 @@ project. This allows you to take the generated code and place it directly into a project using any of these languages. Here are some typical command line invocations:: - $ nimrod c hallo.nim - $ nimrod cpp hallo.nim - $ nimrod objc hallo.nim + $ nim c hallo.nim + $ nim cpp hallo.nim + $ nim objc hallo.nim The compiler commands select the target backend, but if needed you can `specify additional switches for cross compilation -`_ to select the target CPU, operative system +`_ to select the target CPU, operative system or compiler/linker commands. The JavaScript target --------------------- -Nimrod can also generate `JavaScript`:idx: code through the ``js`` command. +Nim can also generate `JavaScript`:idx: code through the ``js`` command. However, the JavaScript code generator is experimental! -Nimrod targets JavaScript 1.5 which is supported by any widely used browser. +Nim targets JavaScript 1.5 which is supported by any widely used browser. Since JavaScript does not have a portable means to include another module, -Nimrod just generates a long ``.js`` file. +Nim just generates a long ``.js`` file. Features or modules that the JavaScript platform does not support are not available. This includes: @@ -83,35 +83,35 @@ However, the modules `strutils `_, `math `_, and `times `_ are available! To access the DOM, use the `dom `_ module that is only available for the JavaScript platform. -To compile a Nimrod module into a ``.js`` file use the ``js`` command; the +To compile a Nim module into a ``.js`` file use the ``js`` command; the default is a ``.js`` file that is supposed to be referenced in an ``.html`` file. However, you can also run the code with `nodejs`:idx:, a `software platform for easily building fast, scalable network applications `_:: - nimrod js -d:nodejs -r examples/hallo.nim + nim js -d:nodejs -r examples/hallo.nim Interfacing =========== -Nimrod offers bidirectional interfacing with the target backend. This means -that you can call backend code from Nimrod and Nimrod code can be called by +Nim offers bidirectional interfacing with the target backend. This means +that you can call backend code from Nim and Nim code can be called by the backend code. Usually the direction of which calls which depends on your -software architecture (is Nimrod your main program or is Nimrod providing a +software architecture (is Nim your main program or is Nim providing a component?). -Nimrod code calling the backend --------------------------------- +Nim code calling the backend +---------------------------- -Nimrod code can interface with the backend through the `Foreign function +Nim code can interface with the backend through the `Foreign function interface `_ mainly through the `importc pragma `_. The ``importc`` pragma is the -*generic* way of making backend symbols available in Nimrod and is available +*generic* way of making backend symbols available in Nim and is available in all the target backends (JavaScript too). The C++ or Objective-C backends -have their respective `ImportCpp `_ and -`ImportObjC `_ pragmas to call methods from +have their respective `ImportCpp `_ and +`ImportObjC `_ pragmas to call methods from classes. Whenever you use any of these pragmas you need to integrate native code into @@ -121,22 +121,22 @@ JavaScript functions which you are importing with ``importc``. However, for the C like targets you need to link external code either statically or dynamically. The preferred way of integrating native code is to -use dynamic linking because it allows you to compile Nimrod programs without +use dynamic linking because it allows you to compile Nim programs without the need for having the related development libraries installed. This is done through the `dynlib pragma for import `_, though more specific control can be gained using the `dynlib module `_. -The `dynlibOverride `_ command line switch allows +The `dynlibOverride `_ command line switch allows to avoid dynamic linking if you need to statically link something instead. -Nimrod wrappers designed to statically link source files can use the `compile -pragma `_ if there are few sources or providing -them along the Nimrod code is easier than using a system library. Libraries +Nim wrappers designed to statically link source files can use the `compile +pragma `_ if there are few sources or providing +them along the Nim code is easier than using a system library. Libraries installed on the host system can be linked in with the `PassL pragma -`_. +`_. To wrap native code, take a look at the `c2nim tool `_ which helps -with the process of scanning and transforming header files into a Nimrod +with the process of scanning and transforming header files into a Nim interface. C invocation example @@ -152,7 +152,7 @@ Create a ``logic.c`` file with the following content: Create a ``calculator.nim`` file with the following content: -.. code-block:: nimrod +.. code-block:: nim {.compile: "logic.c".} proc addTwoIntegers(a, b: cint): cint {.importc.} @@ -160,8 +160,8 @@ Create a ``calculator.nim`` file with the following content: when isMainModule: echo addTwoIntegers(3, 7) -With these two files in place, you can run ``nimrod c -r calculator.nim`` and -the Nimrod compiler will compile the ``logic.c`` file in addition to +With these two files in place, you can run ``nim c -r calculator.nim`` and +the Nim compiler will compile the ``logic.c`` file in addition to ``calculator.nim`` and link both into an executable, which outputs ``10`` when run. Another way to link the C file statically and get the same effect would be remove the line with the ``compile`` pragma and run the following typical @@ -169,7 +169,7 @@ Unix commands:: $ gcc -c logic.c $ ar rvs mylib.a logic.o - $ nimrod c --passL:mylib.a -r calculator.nim + $ nim c --passL:mylib.a -r calculator.nim Just like in this example we pass the path to the ``mylib.a`` library (and we could as well pass ``logic.o``) we could be passing switches to link any other @@ -196,50 +196,50 @@ Create a ``host.html`` file with the following content: Create a ``calculator.nim`` file with the following content (or reuse the one from the previous section): -.. code-block:: nimrod +.. code-block:: nim proc addTwoIntegers(a, b: int): int {.importc.} when isMainModule: echo addTwoIntegers(3, 7) -Compile the Nimrod code to JavaScript with ``nimrod js -o:calculator.js +Compile the Nim code to JavaScript with ``nim js -o:calculator.js calculator.nim`` and open ``host.html`` in a browser. If the browser supports javascript, you should see the value ``10``. In JavaScript the `echo proc `_ will modify the HTML DOM and append the string. Use the `dom module `_ for specific DOM querying and modification procs. -Backend code calling Nimrod ---------------------------- +Backend code calling Nim +------------------------ -Backend code can interface with Nimrod code exposed through the `exportc +Backend code can interface with Nim code exposed through the `exportc pragma `_. The ``exportc`` pragma is the *generic* -way of making Nimrod symbols available to the backends. By default the Nimrod -compiler will mangle all the Nimrod symbols to avoid any name collision, so -the most significant thing the ``exportc`` pragma does is maintain the Nimrod +way of making Nim symbols available to the backends. By default the Nim +compiler will mangle all the Nim symbols to avoid any name collision, so +the most significant thing the ``exportc`` pragma does is maintain the Nim symbol name, or if specified, use an alternative symbol for the backend in case the symbol rules don't match. The JavaScript target doesn't have any further interfacing considerations since it also has garbage collection, but the C targets require you to -initialize Nimrod's internals, which is done calling a ``NimMain`` function. +initialize Nim's internals, which is done calling a ``NimMain`` function. Also, C code requires you to specify a forward declaration for functions or the compiler will asume certain types for the return value and parameters which will likely make your program crash at runtime. -The Nimrod compiler can generate a C interface header through the ``--header`` +The Nim compiler can generate a C interface header through the ``--header`` command line switch. The generated header will contain all the exported symbols and the ``NimMain`` proc which you need to call before any other -Nimrod code. +Nim code. -Nimrod invocation example from C -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Nim invocation example from C +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Create a ``fib.nim`` file with the following content: -.. code-block:: nimrod +.. code-block:: nim proc fib(a: cint): cint {.exportc.} = if a <= 2: @@ -263,35 +263,35 @@ Create a ``maths.c`` file with the following content: } Now you can run the following Unix like commands to first generate C sources -form the Nimrod code, then link them into a static binary along your main C +form the Nim code, then link them into a static binary along your main C program:: - $ nimrod c --noMain --noLinking --header:fib.h fib.nim - $ gcc -o m -Inimcache -Ipath/to/nimrod/lib nimcache/*.c maths.c + $ nim c --noMain --noLinking --header:fib.h fib.nim + $ gcc -o m -Inimcache -Ipath/to/nim/lib nimcache/*.c maths.c -The first command runs the Nimrod compiler with three special options to avoid +The first command runs the Nim compiler with three special options to avoid generating a ``main()`` function in the generated files, avoid linking the object files into a final binary, and explicitly generate a header file for C integration. All the generated files are placed into the ``nimcache`` directory. That's why the next command compiles the ``maths.c`` source plus all the ``.c`` files form ``nimcache``. In addition to this path, you also -have to tell the C compiler where to find Nimrod's ``nimbase.h`` header file. +have to tell the C compiler where to find Nim's ``nimbase.h`` header file. Instead of depending on the generation of the individual ``.c`` files you can -also ask the Nimrod compiler to generate a statically linked library:: +also ask the Nim compiler to generate a statically linked library:: - $ nimrod c --app:staticLib --noMain --header fib.nim - $ gcc -o m -Inimcache -Ipath/to/nimrod/lib libfib.nim.a maths.c + $ nim c --app:staticLib --noMain --header fib.nim + $ gcc -o m -Inimcache -Ipath/to/nim/lib libfib.nim.a maths.c -The Nimrod compiler will handle linking the source files generated in the +The Nim compiler will handle linking the source files generated in the ``nimcache`` directory into the ``libfib.nim.a`` static library, which you can then link into your C program. Note that these commands are generic and will vary for each system. For instance, on Linux systems you will likely need to use ``-ldl`` too to link in required dlopen functionality. -Nimrod invocation example from JavaScript -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Nim invocation example from JavaScript +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Create a ``mhost.html`` file with the following content: @@ -307,7 +307,7 @@ Create a ``mhost.html`` file with the following content: Create a ``fib.nim`` file with the following content (or reuse the one from the previous section): -.. code-block:: nimrod +.. code-block:: nim proc fib(a: cint): cint {.exportc.} = if a <= 2: @@ -315,11 +315,11 @@ from the previous section): else: result = fib(a - 1) + fib(a - 2) -Compile the Nimrod code to JavaScript with ``nimrod js -o:fib.js fib.nim`` and +Compile the Nim code to JavaScript with ``nim js -o:fib.js fib.nim`` and open ``mhost.html`` in a browser. If the browser supports javascript, you should see an alert box displaying the text ``Fib for 9 is 34``. As mentioned earlier, JavaScript doesn't require an initialisation call to ``NimMain`` or -similar function and you can call the exported Nimrod proc directly. +similar function and you can call the exported Nim proc directly. Nimcache naming logic @@ -328,17 +328,17 @@ Nimcache naming logic The `nimcache`:idx: directory is generated during compilation and will hold either temporary or final files depending on your backend target. The default name for the directory is ``nimcache`` but you can use the ``--nimcache`` -`compiler switch `_ to change it. +`compiler switch `_ to change it. Nimcache and C like targets ~~~~~~~~~~~~~~~~~~~~~~~~~~~ The C like backends will place their temporary ``.c``, ``.cpp`` or ``.m`` files in the ``nimcache`` directory. The naming of these files follows the pattern -``babelPackageName_`` + ``nimrodSource``: +``babelPackageName_`` + ``nimSource``: * Filenames for modules imported from `Babel packages - `_ will end up with + `_ will end up with ``babelPackageName_module.c``. For example, if you import the ``argument_parser`` module from the same name Babel package you will end up with a ``argument_parser_argument_parser.c`` file @@ -375,7 +375,7 @@ Nimcache and the Javascript target Unless you explicitly use the ``-o:filename.js`` switch as mentioned in the previous examples, the compiler will create a ``filename.js`` file in the -``nimcache`` directory using the name of your input nimrod file. There are no +``nimcache`` directory using the name of your input nim file. There are no other temporary files generated, the output is always a single self contained ``.js`` file. @@ -387,38 +387,38 @@ In the previous sections the ``NimMain()`` function reared its head. Since JavaScript already provides automatic memory management, you can freely pass objects between the two language without problems. In C and derivate languages you need to be careful about what you do and how you share memory. The -previous examples only dealt with simple scalar values, but passing a Nimrod -string to C, or reading back a C string in Nimrod already requires you to be +previous examples only dealt with simple scalar values, but passing a Nim +string to C, or reading back a C string in Nim already requires you to be aware of who controls what to avoid crashing. Strings and C strings --------------------- -The manual mentions that `Nimrod strings are implicitly convertible to +The manual mentions that `Nim strings are implicitly convertible to cstrings `_ which makes interaction usually -painless. Most C functions accepting a Nimrod string converted to a +painless. Most C functions accepting a Nim string converted to a ``cstring`` will likely not need to keep this string around and by the time they return the string won't be needed any more. However, for the rare cases -where a Nimrod string has to be preserved and made available to the C backend +where a Nim string has to be preserved and made available to the C backend as a ``cstring``, you will need to manually prevent the string data from being freed with `GC_ref `_ and `GC_unref `_. -A similar thing happens with C code invoking Nimrod code which returns a +A similar thing happens with C code invoking Nim code which returns a ``cstring``. Consider the following proc: -.. code-block:: nimrod +.. code-block:: nim proc gimme(): cstring {.exportc.} = result = "Hey there C code! " & $random(100) -Since Nimrod's garbage collector is not aware of the C code, once the +Since Nim's garbage collector is not aware of the C code, once the ``gimme`` proc has finished it can reclaim the memory of the ``cstring``. However, from a practical standpoint, the C code invoking the ``gimme`` -function directly will be able to use it since Nimrod's garbage collector has +function directly will be able to use it since Nim's garbage collector has not had a chance to run *yet*. This gives you enough time to make a copy for -the C side of the program, as calling any further Nimrod procs *might* trigger +the C side of the program, as calling any further Nim procs *might* trigger garbage collection making the previously returned string garbage. Or maybe you are `triggering yourself the collection `_. @@ -426,35 +426,35 @@ are `triggering yourself the collection `_. Custom data types ----------------- -Just like strings, custom data types that are to be shared between Nimrod and +Just like strings, custom data types that are to be shared between Nim and the backend will need careful consideration of who controlls who. If you want -to hand a Nimrod reference to C code, you will need to use `GC_ref +to hand a Nim reference to C code, you will need to use `GC_ref `_ to mark the reference as used, so it does not get freed. And for the C backend you will need to expose the `GC_unref `_ proc to clean up this memory when it is not required any more. Again, if you are wrapping a library which *mallocs* and *frees* data -structures, you need to expose the appropriate *free* function to Nimrod so +structures, you need to expose the appropriate *free* function to Nim so you can clean it up. And of course, once cleaned you should avoid accessing it -from Nimrod (or C for that matter). Typically C data structures have their own +from Nim (or C for that matter). Typically C data structures have their own ``malloc_structure`` and ``free_structure`` specific functions, so wrapping -these for the Nimrod side should be enough. +these for the Nim side should be enough. Thread coordination ------------------- -When the ``NimMain()`` function is called Nimrod initializes the garbage +When the ``NimMain()`` function is called Nim initializes the garbage collector to the current thread, which is usually the main thread of your -application. If your C code later spawns a different thread and calls Nimrod +application. If your C code later spawns a different thread and calls Nim code, the garbage collector will fail to work properly and you will crash. -As long as you don't use the threadvar emulation Nimrod uses native thread +As long as you don't use the threadvar emulation Nim uses native thread variables, of which you get a fresh version whenever you create a thread. You can then attach a GC to this thread via -.. code-block:: nimrod +.. code-block:: nim setStackBottom(addr(someLocal)) initGC() diff --git a/doc/basicopt.txt b/doc/basicopt.txt index 390623828..ffb374f18 100644 --- a/doc/basicopt.txt +++ b/doc/basicopt.txt @@ -1,11 +1,12 @@ -Usage:: - nimrod command [options] [projectfile] [arguments] +:: + + nim command [options] [projectfile] [arguments] Command: //compile, c compile project with default code generator (C) //doc generate the documentation for inputfile //doc2 generate the documentation for the whole project - //i start Nimrod in interactive mode (limited) + //i start Nim in interactive mode (limited) Arguments: arguments are passed to the program being run (if --run option is selected) diff --git a/doc/docgen.txt b/doc/docgen.txt index 30d0c6ff2..554f69838 100644 --- a/doc/docgen.txt +++ b/doc/docgen.txt @@ -1,9 +1,9 @@ =================================== - Nimrod DocGen Tools Guide + Nim DocGen Tools Guide =================================== :Author: Erik O'Leary -:Version: |nimrodversion| +:Version: |nimversion| .. contents:: @@ -12,7 +12,7 @@ Introduction ============ This document describes the `documentation generation tools`:idx: built into -the `Nimrod compiler `_, which can generate HTML and JSON output +the `Nim compiler `_, which can generate HTML and JSON output from input .nim files and projects, as well as HTML and LaTeX from input RST (reStructuredText) files. The output documentation will include module dependencies (``import``), any top-level documentation comments (##), and @@ -25,12 +25,12 @@ Documentation Comments Any comments which are preceded by a double-hash (##), are interpreted as documentation. Comments are parsed as RST (see `reference `_), providing -Nimrod module authors the ability to easily generate richly formatted +Nim module authors the ability to easily generate richly formatted documentation with only their well-documented code. Example: -.. code-block:: nimrod +.. code-block:: nim type TPerson* = object ## This type contains a description of a person name: string @@ -45,7 +45,7 @@ This type contains a description of a person Field documentation comments can be added to fields like so: -.. code-block:: nimrod +.. code-block:: nim var numValues: int ## \ ## `numValues` stores the number of values @@ -54,15 +54,15 @@ this type would not be generated. Documentation will only be generated for *exported* types/procedures/etc. -Nimrod file input +Nim file input ----------------- The following examples will generate documentation for the below contrived -*Nimrod* module, aptly named 'sample.nim' +*Nim* module, aptly named 'sample.nim' sample.nim: -.. code-block:: nimrod +.. code-block:: nim ## This module is a sample. import strutils @@ -90,7 +90,7 @@ commands. These command take either a single .nim file, outputting a single multiple .html files and, optionally, an index file. The ``doc`` command:: - nimrod doc sample + nim doc sample Partial Output:: ... @@ -99,11 +99,11 @@ Partial Output:: Output can be viewed in full here: `docgen_sample.html `_. The next command, called ``doc2``, is very similar to the ``doc`` command, but -will be run after the compiler performs semantic checking on the input nimrod +will be run after the compiler performs semantic checking on the input nim module(s), which allows it to process macros. The ``doc2`` command:: - nimrod doc2 sample + nim doc2 sample Partial Output:: ... @@ -127,7 +127,7 @@ Note that this tool is built off of the ``doc`` command, and therefore is performed before semantic checking. The ``jsondoc`` command:: - nimrod jsondoc sample + nim jsondoc sample Output:: [ @@ -150,9 +150,9 @@ Project switch -------------- :: - nimrod doc2 --project filename.nim + nim doc2 --project filename.nim -This will recursively generate documentation of all nimrod modules imported +This will recursively generate documentation of all nim modules imported into the input module, including system modules. Be careful with this command, as it may end up sprinkling html files all over your filesystem! @@ -161,15 +161,15 @@ Index switch ------------ :: - nimrod doc2 --index:on filename.nim + nim doc2 --index:on filename.nim -This will generate an index of all the exported symbols in the input Nimrod +This will generate an index of all the exported symbols in the input Nim module, and put it into a neighboring file with the extension of ``.idx``. The index file is line oriented (newlines have to be escaped). Each line represents a tab separated record of several columns, the first two mandatory, the rest optional. See the `Index (idx) file format`_ section for details. -Once index files have been generated for one or more modules, the Nimrod +Once index files have been generated for one or more modules, the Nim compiler command ``buildIndex directory`` can be run to go over all the index files in the specified directory to generate a `theindex.html `_ file. @@ -178,7 +178,7 @@ See source switch ----------------- :: - nimrod doc2 --docSeeSrcUrl:txt filename.nim + nim doc2 --docSeeSrcUrl:txt filename.nim When you pass the ``docSeeSrcUrl`` switch to docgen, after each documented item in your source code the hyper link *See source* will appear pointing to the @@ -191,25 +191,25 @@ hyper link to your own code repository. As you will see by the comments in that file, the value ``txt`` passed on the command line will be used in the HTML template along others like ``$path`` and ``$line``. -In the case of Nimrod's own documentation, the ``txt`` value is just a commit -hash to append to a formatted URL to https://github.com/Araq/Nimrod. The +In the case of Nim's own documentation, the ``txt`` value is just a commit +hash to append to a formatted URL to https://github.com/Araq/Nim. The ``tools/nimweb.nim`` helper queries the current git commit hash during doc generation, but since you might be working on an unpublished repository, it -also allows specifying a ``githash`` value in ``web/nimrod.ini`` to force a +also allows specifying a ``githash`` value in ``web/nim.ini`` to force a specific commit in the output. Other Input Formats =================== -The *Nimrod compiler* also has support for RST (reStructuredText) files with +The *Nim compiler* also has support for RST (reStructuredText) files with the ``rst2html`` and ``rst2tex`` commands. Documents like this one are -initially written in a dialect of RST which adds support for nimrod sourcecode -highlighting with the ``.. code-block:: nimrod`` prefix. ``code-block`` also +initially written in a dialect of RST which adds support for nim sourcecode +highlighting with the ``.. code-block:: nim`` prefix. ``code-block`` also supports highlighting of C++ and some other c-like languages. Usage:: - nimrod rst2html docgen.txt + nim rst2html docgen.txt Output:: You're reading it! @@ -272,8 +272,8 @@ symbols in the `system module `_. `#TSignedInt `_ * ``var globalRaiseHook: proc (e: ref E_Base): bool {.nimcall.}`` **=>** `#globalRaiseHook `_ -* ``const NimrodVersion = "0.0.0"`` **=>** - `#NimrodVersion `_ +* ``const NimVersion = "0.0.0"`` **=>** + `#NimVersion `_ * ``proc getTotalMem(): int {.rtl, raises: [], tags: [].}`` **=>** `#getTotalMem, `_ * ``proc len[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.}`` **=>** @@ -304,7 +304,7 @@ but can have up to four (additional columns are ignored). The content of these columns is: 1. Mandatory term being indexed. Terms can include quoting according to - Nimrod's rules (eg. \`^\` like in `the actors module + Nim's rules (eg. \`^\` like in `the actors module `_). 2. Base filename plus anchor hyper link (eg. ``algorithm.html#*,int,TSortOrder``). @@ -342,7 +342,7 @@ final index, and TOC entries found in ``.nim`` files are discarded. Additional resources ==================== -`Nimrod Compiler User Guide `_ +`Nim Compiler User Guide `_ `RST Quick Reference `_ diff --git a/doc/docs.txt b/doc/docs.txt index 8126da86c..ecf0cc268 100644 --- a/doc/docs.txt +++ b/doc/docs.txt @@ -1,18 +1,18 @@ The documentation consists of several documents: - | `Tutorial (part I) `_ - | The Nimrod tutorial part one deals with the basics. + | The Nim tutorial part one deals with the basics. - | `Tutorial (part II) `_ - | The Nimrod tutorial part two deals with the advanced language constructs. + | The Nim tutorial part two deals with the advanced language constructs. - | `Language Manual `_ - | The Nimrod manual is a draft that will evolve into a proper specification. + | The Nim manual is a draft that will evolve into a proper specification. - | `Library documentation `_ - | This document describes Nimrod's standard library. + | This document describes Nim's standard library. -- | `Compiler user guide `_ +- | `Compiler user guide `_ | The user guide lists command line arguments, special features of the compiler, etc. @@ -20,11 +20,11 @@ The documentation consists of several documents: | Description of some tools that come with the standard distribution. - | `GC `_ - | Additional documentation about Nimrod's GC and how to operate it in a + | Additional documentation about Nim's GC and how to operate it in a | realtime setting. - | `Source code filters `_ - | The Nimrod compiler supports source code filters as a simple yet powerful + | The Nim compiler supports source code filters as a simple yet powerful builtin templating system. - | `Term rewriting macros `_ diff --git a/doc/effects.txt b/doc/effects.txt index 8084ae17a..4ed1d09f1 100644 --- a/doc/effects.txt +++ b/doc/effects.txt @@ -1,5 +1,5 @@ ===================================================================== - Side effects in Nimrod + Side effects in Nim ===================================================================== Note: Side effects are implicit produced values! Maybe they should be @@ -13,7 +13,7 @@ difficult is the ``newString`` proc: If it is simply wrapped, it should not be evaluated at compile time! On other occasions it can and should be evaluted: -.. code-block:: nimrod +.. code-block:: nim proc toUpper(s: string): string = result = newString(len(s)) for i in 0..len(s) - 1: diff --git a/doc/endb.txt b/doc/endb.txt index e9a01b583..049f9d40d 100644 --- a/doc/endb.txt +++ b/doc/endb.txt @@ -1,15 +1,17 @@ ============================================== - Embedded Nimrod Debugger (ENDB) User Guide + Embedded Nim Debugger (ENDB) User Guide ============================================== :Author: Andreas Rumpf -:Version: |nimrodversion| +:Version: |nimversion| .. contents:: +**WARNING**: ENDB is not maintained anymore! Please help if you're interested +in this tool. -Nimrod comes with a platform independent debugger - -the Embedded Nimrod Debugger (ENDB). The debugger is +Nim comes with a platform independent debugger - +the Embedded Nim Debugger (ENDB). The debugger is *embedded* into your executable if it has been compiled with the ``--debugger:on`` command line option. This also defines the conditional symbol ``ENDB`` for you. @@ -20,7 +22,7 @@ available for the debugger. If you start your program the debugger will immediately show a prompt on the console. You can now enter a command. The next sections -deal with the possible commands. As usual in Nimrod in all commands +deal with the possible commands. As usual in Nim in all commands underscores and case do not matter. Optional components of a command are listed in brackets ``[...]`` here. @@ -105,7 +107,7 @@ The ``breakpoint`` pragma The ``breakpoint`` pragma is syntactically a statement. It can be used to mark the *following line* as a breakpoint: -.. code-block:: Nimrod +.. code-block:: Nim write("1") {.breakpoint: "before_write_2".} write("2") @@ -125,7 +127,7 @@ The ``watchpoint`` pragma The ``watchpoint`` pragma is syntactically a statement. It can be used to mark a location as a watchpoint: -.. code-block:: Nimrod +.. code-block:: Nim var a: array [0..20, int] {.watchpoint: a[3].} @@ -141,7 +143,7 @@ is turned on, so you don't need to remove it from your source code after debugging. Due to the primitive implementation watchpoints are even slower than -breakpoints: After *every* executed Nimrod code line it is checked whether the +breakpoints: After *every* executed Nim code line it is checked whether the location changed. @@ -159,7 +161,7 @@ Data Display Commands in the current stack frame, use the ``up`` or ``down`` command. Unfortunately, only inspecting variables is possible at the moment. Maybe - a future version will implement a full-blown Nimrod expression evaluator, + a future version will implement a full-blown Nim expression evaluator, but this is not easy to do and would bloat the debugger's code. Since displaying the whole data structures is often not needed and diff --git a/doc/estp.txt b/doc/estp.txt index 704bc33f2..500ee52a5 100644 --- a/doc/estp.txt +++ b/doc/estp.txt @@ -3,10 +3,10 @@ =================================================== :Author: Andreas Rumpf -:Version: |nimrodversion| +:Version: |nimversion| -Nimrod comes with a platform independent profiler - +Nim comes with a platform independent profiler - the Embedded Stack Trace Profiler (ESTP). The profiler is *embedded* into your executable. To activate the profiler you need to do: @@ -49,7 +49,7 @@ Example results file The results file lists stack traces ordered by significance. -The following example file has been generated by profiling the Nimrod compiler +The following example file has been generated by profiling the Nim compiler itself: It shows that in total 5.4% of the runtime has been spent in ``crcFromRope`` or its children. @@ -74,7 +74,7 @@ often remains mysterious. CommandCompileToC MainCommand HandleCmdLine - nimrod + nim Entry: 1/3391 Calls: 46/4160 = 1.1% [sum: 130; 130/4160 = 3.1%] updateCrc32 newCrcFromRopeAux @@ -90,7 +90,7 @@ often remains mysterious. CommandCompileToC MainCommand HandleCmdLine - nimrod + nim Entry: 2/3391 Calls: 41/4160 = 0.99% [sum: 171; 171/4160 = 4.1%] updateCrc32 updateCrc32 @@ -107,7 +107,7 @@ often remains mysterious. CommandCompileToC MainCommand HandleCmdLine - nimrod + nim Entry: 3/3391 Calls: 41/4160 = 0.99% [sum: 212; 212/4160 = 5.1%] crcFromFile writeRopeIfNotEqual @@ -121,7 +121,7 @@ often remains mysterious. CommandCompileToC MainCommand HandleCmdLine - nimrod + nim Entry: 4/3391 Calls: 41/4160 = 0.99% [sum: 253; 253/4160 = 6.1%] updateCrc32 crcFromFile @@ -136,7 +136,7 @@ often remains mysterious. CommandCompileToC MainCommand HandleCmdLine - nimrod + nim Entry: 5/3391 Calls: 32/4160 = 0.77% [sum: 285; 285/4160 = 6.9%] pop newCrcFromRopeAux @@ -152,7 +152,7 @@ often remains mysterious. CommandCompileToC MainCommand HandleCmdLine - nimrod + nim Entry: 6/3391 Calls: 17/4160 = 0.41% [sum: 302; 302/4160 = 7.3%] doOperation forAllChildrenAux @@ -171,7 +171,7 @@ often remains mysterious. MainCommand HandleCmdLine ... - nimrod + nim Entry: 7/3391 Calls: 14/4160 = 0.34% [sum: 316; 316/4160 = 7.6%] Contains isAccessible diff --git a/doc/exception_hierarchy_fragment.txt b/doc/exception_hierarchy_fragment.txt index 6ca68921f..f4a419fc4 100644 --- a/doc/exception_hierarchy_fragment.txt +++ b/doc/exception_hierarchy_fragment.txt @@ -1,31 +1,28 @@ -* `E_Base `_ - * `EAccessViolation `_ - * `EArithmetic `_ - * `EDivByZero `_ - * `EOverflow `_ - * `EAssertionFailed `_ - * `EAsynch `_ - * `EControlC `_ - * `EDeadThread `_ - * `EFloatingPoint `_ - * `EFloatDivByZero `_ - * `EFloatInexact `_ - * `EFloatInvalidOp `_ - * `EFloatOverflow `_ - * `EFloatUnderflow `_ - * `EInvalidField `_ - * `EInvalidIndex `_ - * `EInvalidObjectAssignment `_ - * `EInvalidObjectConversion `_ - * `EInvalidValue `_ - * `EInvalidKey `_ - * `ENoExceptionToReraise `_ - * `EOutOfRange `_ - * `ESynch `_ - * `EOutOfMemory `_ - * `EResourceExhausted `_ - * `EStackOverflow `_ - * `ESystem `_ - * `EIO `_ - * `EOS `_ - * `EInvalidLibrary `_ +* `Exception `_ + * `AccessViolationError `_ + * `ArithmeticError `_ + * `DivByZeroError `_ + * `OverflowError `_ + * `AssertionError `_ + * `DeadThreadError `_ + * `FloatingPointError `_ + * `FloatDivByZeroError `_ + * `FloatInexactError `_ + * `FloatInvalidOpError `_ + * `FloatOverflowError `_ + * `FloatUnderflowError `_ + * `FieldError `_ + * `IndexError `_ + * `ObjectAssignmentError `_ + * `ObjectConversionError `_ + * `ValueError `_ + * `KeyError `_ + * `ReraiseError `_ + * `RangeError `_ + * `OutOfMemoryError `_ + * `ResourceExhaustedError `_ + * `StackOverflowError `_ + * `SystemError `_ + * `IOError `_ + * `OSError `_ + * `LibraryError `_ diff --git a/doc/filelist.txt b/doc/filelist.txt index f71547f86..beff8f6c8 100644 --- a/doc/filelist.txt +++ b/doc/filelist.txt @@ -1,10 +1,10 @@ -Short description of Nimrod's modules +Short description of Nim's modules ------------------------------------- ============== ========================================================== Module Description ============== ========================================================== -nimrod main module: parses the command line and calls +nim main module: parses the command line and calls ``main.MainCommand`` main implements the top-level command dispatching nimconf implements the config file reader @@ -12,8 +12,8 @@ syntaxes dispatcher for the different parsers and filters filter_tmpl standard template filter (``#! stdtempl``) lexbase buffer handling of the lexical analyser lexer lexical analyser -parser Nimrod's parser -renderer Nimrod code renderer (AST back to its textual form) +parser Nim's parser +renderer Nim code renderer (AST back to its textual form) options contains global and local compiler options ast type definitions of the abstract syntax tree (AST) and node constructors @@ -37,7 +37,7 @@ pragmas semantic checking of pragmas idents implements a general mapping from identifiers to an internal representation (``PIdent``) that is used so that a simple - id-comparison suffices to say whether two Nimrod identifiers + id-comparison suffices to say whether two Nim identifiers are equivalent ropes implements long strings represented as trees for lazy evaluation; used mainly by the code generators diff --git a/doc/filters.txt b/doc/filters.txt index 961a0ea49..22df63490 100644 --- a/doc/filters.txt +++ b/doc/filters.txt @@ -40,7 +40,7 @@ Filters can be combined with the ``|`` pipe operator:: Available filters ================= -**Hint:** With ``--verbosity:2`` (or higher) Nimrod lists the processed code +**Hint:** With ``--verbosity:2`` (or higher) Nim lists the processed code after each filter application. Replace filter @@ -79,19 +79,19 @@ Parameters and their defaults: StdTmpl filter -------------- -The stdtmpl filter provides a simple templating engine for Nimrod. The +The stdtmpl filter provides a simple templating engine for Nim. The filter uses a line based parser: Lines prefixed with a *meta character* -(default: ``#``) contain Nimrod code, other lines are verbatim. Because +(default: ``#``) contain Nim code, other lines are verbatim. Because indentation-based parsing is not suited for a templating engine, control flow statements need ``end X`` delimiters. Parameters and their defaults: ``metaChar: char = '#'`` - prefix for a line that contains Nimrod code + prefix for a line that contains Nim code ``subsChar: char = '$'`` - prefix for a Nimrod expression within a template line + prefix for a Nim expression within a template line ``conc: string = " & "`` the operation for concatenation @@ -130,7 +130,7 @@ Example:: The filter transforms this into: -.. code-block:: nimrod +.. code-block:: nim proc generateHTMLPage(title, currentTab, content: string, tabs: openArray[string]): string = result = "" @@ -158,7 +158,7 @@ The filter transforms this into: Each line that does not start with the meta character (ignoring leading whitespace) is converted to a string literal that is added to ``result``. -The substitution character introduces a Nimrod expression *e* within the +The substitution character introduces a Nim expression *e* within the string literal. *e* is converted to a string with the *toString* operation which defaults to ``$``. For strong type checking, set ``toString`` to the empty string. *e* must match this PEG pattern:: diff --git a/doc/gc.txt b/doc/gc.txt index 18fb03b6d..ac8d46cfa 100644 --- a/doc/gc.txt +++ b/doc/gc.txt @@ -1,12 +1,16 @@ ========================== -Nimrod's Garbage Collector +Nim's Garbage Collector ========================== -:Author: Andreas Rumpf -:Version: |nimrodversion| +:Author: Andreas Rumpf +:Version: |nimversion| + +.. + "The road to hell is paved with good intentions." + Introduction ============ @@ -19,7 +23,7 @@ code generation). The GC **never** scans the whole heap but it may scan the delta-subgraph of the heap that changed since its last run. -The GC is only triggered in a memory allocation operation. It it not triggered +The GC is only triggered in a memory allocation operation. It is not triggered by some timer and does not run in a background thread. To force a full collection call ``GC_fullCollect``. Note that it is generally @@ -37,7 +41,7 @@ it is necessary to help this analysis with the ``acyclic`` pragma (see You can also use the ``acyclic`` pragma for data that is cyclic in reality and then break up the cycles explicitly with ``GC_addCycleRoot``. This can be a -very valuable optimization; the Nimrod compiler itself relies on this +very valuable optimization; the Nim compiler itself relies on this optimization trick to improve performance. Note that ``GC_addCycleRoot`` is a quick operation; the root is only registered for the next run of the cycle collector. @@ -47,9 +51,10 @@ Realtime support ================ To enable realtime support, the symbol `useRealtimeGC`:idx: needs to be -defined. With this switch the GC supports the following operations: +defined via ``--define:useRealtimeGC`` (you can put this into your config +file as well). With this switch the GC supports the following operations: -.. code-block:: nimrod +.. code-block:: nim proc GC_setMaxPause*(MaxPauseInUs: int) proc GC_step*(us: int, strongAdvice = false) @@ -111,7 +116,7 @@ highly specialized environments or for older hardware. Keeping track of memory ----------------------- -If you need to pass around memory allocated by Nimrod to C, you can use the +If you need to pass around memory allocated by Nim to C, you can use the procs ``GC_ref`` and ``GC_unref`` to mark objects as referenced to avoid them being freed by the GC. Other useful procs from `system `_ you can use to keep track of memory are: diff --git a/doc/grammar.txt b/doc/grammar.txt index a54428678..e751c2d8d 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -4,7 +4,7 @@ semicolon = ';' COMMENT? colon = ':' COMMENT? colcom = ':' COMMENT? -operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 +operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 | OP10 | 'or' | 'xor' | 'and' | 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'addr' | 'static' | '..' @@ -14,16 +14,17 @@ prefixOperator = operator optInd = COMMENT? optPar = (IND{>} | IND{=})? -simpleExpr = assignExpr (OP0 optInd assignExpr)* -assignExpr = orExpr (OP1 optInd orExpr)* -orExpr = andExpr (OP2 optInd andExpr)* -andExpr = cmpExpr (OP3 optInd cmpExpr)* -cmpExpr = sliceExpr (OP4 optInd sliceExpr)* -sliceExpr = ampExpr (OP5 optInd ampExpr)* -ampExpr = plusExpr (OP6 optInd plusExpr)* -plusExpr = mulExpr (OP7 optInd mulExpr)* -mulExpr = dollarExpr (OP8 optInd dollarExpr)* -dollarExpr = primary (OP9 optInd primary)* +simpleExpr = arrowExpr (OP0 optInd arrowExpr)* +arrowExpr = assignExpr (OP1 optInd assignExpr)* +assignExpr = orExpr (OP2 optInd orExpr)* +orExpr = andExpr (OP3 optInd andExpr)* +andExpr = cmpExpr (OP4 optInd cmpExpr)* +cmpExpr = sliceExpr (OP5 optInd sliceExpr)* +sliceExpr = ampExpr (OP6 optInd ampExpr)* +ampExpr = plusExpr (OP7 optInd plusExpr)* +plusExpr = mulExpr (OP8 optInd mulExpr)* +mulExpr = dollarExpr (OP9 optInd dollarExpr)* +dollarExpr = primary (OP10 optInd primary)* symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' | IDENT indexExpr = expr @@ -142,6 +143,7 @@ exceptBlock = 'except' colcom stmt forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt blockStmt = 'block' symbol? colcom stmt staticStmt = 'static' colcom stmt +deferStmt = 'defer' colcom stmt asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLE_STR_LIT) genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)? genericParamList = '[' optInd @@ -182,7 +184,7 @@ simpleStmt = ((returnStmt | raiseStmt | yieldStmt | discardStmt | breakStmt | includeStmt | commentStmt) / exprStmt) COMMENT? complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt | tryStmt | finallyStmt | exceptStmt | forStmt - | blockStmt | staticStmt | asmStmt + | blockStmt | staticStmt | deferStmt | asmStmt | 'proc' routine | 'method' routine | 'iterator' routine diff --git a/doc/idetools.txt b/doc/idetools.txt index 97e110b1d..5429d0c07 100644 --- a/doc/idetools.txt +++ b/doc/idetools.txt @@ -1,9 +1,9 @@ ================================ - Nimrod IDE Integration Guide + Nim IDE Integration Guide ================================ :Author: Britney Spears -:Version: |nimrodversion| +:Version: |nimversion| .. contents:: @@ -13,17 +13,17 @@ "yes, I'm the creator" -- Araq, 2013-07-26 19:28:32.

      -Nimrod differs from many other compilers in that it is really fast, +Nim differs from many other compilers in that it is really fast, and being so fast makes it suited to provide external queries for text editors about the source code being written. Through the -``idetools`` command of `the compiler `_, any IDE +``idetools`` command of `the compiler `_, any IDE can query a ``.nim`` source file and obtain useful information like definition of symbols or suggestions for completion. This document will guide you through the available options. If you want to look at practical examples of idetools support you can look at the test files found in the `Test suite`_ or `various editor -integrations `_ +integrations `_ already available. @@ -37,11 +37,11 @@ All of the available idetools commands require you to specify a query location through the ``--track`` or ``--trackDirty`` switches. The general idetools invokations are:: - nimrod idetools --track:FILE,LINE,COL proj.nim + nim idetools --track:FILE,LINE,COL proj.nim Or:: - nimrod idetools --trackDirty:DIRTY_FILE,FILE,LINE,COL proj.nim + nim idetools --trackDirty:DIRTY_FILE,FILE,LINE,COL proj.nim ``proj.nim`` This is the main *project* filename. Most of the time you will @@ -97,7 +97,7 @@ provide the typical *Jump to definition* where a user puts the cursor on a symbol or uses the mouse to select it and is redirected to the place where the symbol is located. -Since Nimrod is implemented in Nimrod, one of the nice things of +Since Nim is implemented in Nim, one of the nice things of this feature is that any user with an IDE supporting it can quickly jump around the standard library implementation and see exactly what a proc does, learning about the language and seeing real life @@ -118,7 +118,7 @@ includes/imports) and when the user starts typing something a completion box with different options appears. However such features are not context sensitive and work simply on -string matching, which can be problematic in Nimrod especially due +string matching, which can be problematic in Nim especially due to the case insensitiveness of the language (plus underscores as separators!). @@ -179,16 +179,16 @@ in the millisecond range, thus being responsive enough for IDEs. If you want to start the server using stdin/stdout as communication you need to type:: - nimrod serve --server.type:stdin proj.nim + nim serve --server.type:stdin proj.nim If you want to start the server using tcp and a port, you need to type:: - nimrod serve --server.type:tcp --server.port:6000 \ + nim serve --server.type:tcp --server.port:6000 \ --server.address:hostname proj.nim In both cases the server will start up and await further commands. The syntax of the commands you can now send to the server is -practically the same as running the nimrod compiler on the commandline, +practically the same as running the nim compiler on the commandline, you only need to remove the name of the compiler since you are already talking to it. The server will answer with as many lines of text it thinks necessary plus an empty line to indicate the end @@ -242,7 +242,7 @@ skConst | **Fourth column**: the type of the const value. | **Docstring**: always the empty string. -.. code-block:: nimrod +.. code-block:: nim const SOME_SEQUENCE = @[1, 2] --> col 2: $MODULE.SOME_SEQUENCE col 3: seq[int] @@ -256,7 +256,7 @@ skEnumField | **Fourth column**: enum type grouping other enum fields. | **Docstring**: always the empty string. -.. code-block:: nimrod +.. code-block:: nim Open(filename, fmWrite) --> col 2: system.TFileMode.fmWrite col 3: TFileMode @@ -270,7 +270,7 @@ skForVar | **Fourth column**: type of the var. | **Docstring**: always the empty string. -.. code-block:: nimrod +.. code-block:: nim proc looper(filename = "tests.nim") = for letter in filename: echo letter @@ -291,7 +291,7 @@ posterior instances of the iterator. | **Fourth column**: signature of the iterator including return type. | **Docstring**: docstring if available. -.. code-block:: nimrod +.. code-block:: nim let text = "some text" letters = toSeq(runes(text)) @@ -307,7 +307,7 @@ skLabel | **Fourth column**: always the empty string. | **Docstring**: always the empty string. -.. code-block:: nimrod +.. code-block:: nim proc test(text: string) = var found = -1 block loops: @@ -323,7 +323,7 @@ skLet | **Fourth column**: the type of the let variable. | **Docstring**: always the empty string. -.. code-block:: nimrod +.. code-block:: nim let text = "some text" --> col 2: $MODULE.text @@ -343,7 +343,7 @@ posterior instances of the macro. | **Fourth column**: signature of the macro including return type. | **Docstring**: docstring if available. -.. code-block:: nimrod +.. code-block:: nim proc testMacro() = expect(EArithmetic): --> col 2: idetools_api.expect @@ -379,7 +379,7 @@ This may change in the future. | **Fourth column**: signature of the method including return type. | **Docstring**: docstring if available. -.. code-block:: nimrod +.. code-block:: nim method eval(e: PExpr): int = quit "to override!" method eval(e: PLiteral): int = e.x method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b) @@ -396,7 +396,7 @@ skParam | **Fourth column**: the type of the parameter. | **Docstring**: always the empty string. -.. code-block:: nimrod +.. code-block:: nim proc reader(filename = "tests.nim") = let text = readFile(filename) --> col 2: $MODULE.reader.filename @@ -420,7 +420,7 @@ returned by idetools returns also the pragmas for the proc. | **Fourth column**: signature of the proc including return type. | **Docstring**: docstring if available. -.. code-block:: nimrod +.. code-block:: nim Open(filename, fmWrite) --> col 2: system.Open col 3: proc (var TFile, string, TFileMode, int): bool @@ -438,7 +438,7 @@ skResult | **Fourth column**: the type of the result. | **Docstring**: always the empty string. -.. code-block:: nimrod +.. code-block:: nim proc getRandomValue() : int = return 4 --> col 2: $MODULE.getRandomValue.result @@ -458,7 +458,7 @@ posterior instances of the template. | **Fourth column**: signature of the template including return type. | **Docstring**: docstring if available. -.. code-block:: nimrod +.. code-block:: nim let text = "some text" letters = toSeq(runes(text)) @@ -469,7 +469,7 @@ posterior instances of the template. Example: - .. code-block:: nimrod + .. code-block:: nim let numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: @@ -485,7 +485,7 @@ skType | **Fourth column**: the type. | **Docstring**: always the empty string. -.. code-block:: nimrod +.. code-block:: nim proc writeTempFile() = var output: TFile --> col 2: system.TFile @@ -500,7 +500,7 @@ skVar | **Fourth column**: the type of the var. | **Docstring**: always the empty string. -.. code-block:: nimrod +.. code-block:: nim proc writeTempFile() = var output: TFile output.open("/tmp/somefile", fmWrite) @@ -527,8 +527,8 @@ At the moment idetools support is still in development so the test suite is not integrated with the main test suite and you have to run it manually. First you have to compile the tester:: - $ cd my/nimrod/checkout/tests - $ nimrod c testament/caasdriver.nim + $ cd my/nim/checkout/tests + $ nim c testament/caasdriver.nim Running the ``caasdriver`` without parameters will attempt to process all the test cases in all three operation modes. If a test succeeds @@ -540,7 +540,7 @@ parameter ``verbose`` to force all output even on successfull tests. The normal operation mode is called ``ProcRun`` and it involves starting a process for each command or query, similar to running -manually the Nimrod compiler from the commandline. The ``CaasRun`` +manually the Nim compiler from the commandline. The ``CaasRun`` mode starts a server process to answer all queries. The ``SymbolProcRun`` mode is used by compiler developers. This means that running all tests involves processing all ``*.txt`` files three times, which diff --git a/doc/intern.txt b/doc/intern.txt index 1dcf27774..26dd49444 100644 --- a/doc/intern.txt +++ b/doc/intern.txt @@ -1,10 +1,10 @@ ========================================= - Internals of the Nimrod Compiler + Internals of the Nim Compiler ========================================= :Author: Andreas Rumpf -:Version: |nimrodversion| +:Version: |nimversion| .. contents:: @@ -14,23 +14,23 @@ Directory structure =================== -The Nimrod project's directory structure is: +The Nim project's directory structure is: ============ ============================================== Path Purpose ============ ============================================== ``bin`` generated binary files ``build`` generated C code for the installation -``compiler`` the Nimrod compiler itself; note that this +``compiler`` the Nim compiler itself; note that this code has been translated from a bootstrapping version written in Pascal, so the code is **not** - a poster child of good Nimrod code -``config`` configuration files for Nimrod + a poster child of good Nim code +``config`` configuration files for Nim ``dist`` additional packages for the distribution ``doc`` the documentation; it is a bunch of reStructuredText files -``lib`` the Nimrod library -``web`` website of Nimrod; generated by ``nimweb`` +``lib`` the Nim library +``web`` website of Nim; generated by ``nimweb`` from the ``*.txt`` and ``*.tmpl`` files ============ ============================================== @@ -38,26 +38,26 @@ Path Purpose Bootstrapping the compiler ========================== -As of version 0.8.5 the compiler is maintained in Nimrod. (The first versions +As of version 0.8.5 the compiler is maintained in Nim. (The first versions have been implemented in Object Pascal.) The Python-based build system has -been rewritten in Nimrod too. +been rewritten in Nim too. Compiling the compiler is a simple matter of running:: - nimrod c koch.nim + nim c koch.nim ./koch boot For a release version use:: - nimrod c koch.nim + nim c koch.nim ./koch boot -d:release And for a debug version compatible with GDB:: - nimrod c koch.nim + nim c koch.nim ./koch boot --debuginfo --linedir:on -The ``koch`` program is Nimrod's maintenance script. It is a replacement for +The ``koch`` program is Nim's maintenance script. It is a replacement for make and shell scripting with the advantage that it is much more portable. More information about its options can be found in the `koch `_ documentation. @@ -80,13 +80,13 @@ See also the `API naming design `_ document. Porting to new platforms ======================== -Porting Nimrod to a new architecture is pretty easy, since C is the most -portable programming language (within certain limits) and Nimrod generates +Porting Nim to a new architecture is pretty easy, since C is the most +portable programming language (within certain limits) and Nim generates C code, porting the code generator is not necessary. POSIX-compliant systems on conventional hardware are usually pretty easy to port: Add the platform to ``platform`` (if it is not already listed there), -check that the OS, System modules work and recompile Nimrod. +check that the OS, System modules work and recompile Nim. The only case where things aren't as easy is when the garbage collector needs some assembler tweaking to work. The standard @@ -98,7 +98,7 @@ replace this generic code by some assembler code. Runtime type information ======================== -*Runtime type information* (RTTI) is needed for several aspects of the Nimrod +*Runtime type information* (RTTI) is needed for several aspects of the Nim programming language: Garbage collection @@ -108,7 +108,7 @@ Garbage collection Complex assignments Sequences and strings are implemented as - pointers to resizeable buffers, but Nimrod requires copying for + pointers to resizeable buffers, but Nim requires copying for assignments. Apart from RTTI the compiler could generate copy procedures for any type that needs one. However, this would make the code bigger and the RTTI is likely already there for the GC. @@ -118,15 +118,43 @@ Thus we need to serialize this graph as RTTI for C code generation. Look at the file ``lib/system/hti.nim`` for more information. +Debugging the compiler +====================== + +You can of course use GDB or Visual Studio to debug the +compiler (via ``--debuginfo --lineDir:on``). However, there +are also lots of procs that aid in debugging: + + +.. code-block:: nim + # pretty prints the Nim AST + echo renderTree(someNode) + # outputs some JSON representation + debug(someNode) + # pretty prints some type + echo typeToString(someType) + debug(someType) + echo symbol.name.s + debug(symbol) + # pretty prints the nimrod ast, but annotates symbol IDs: + echo renderTree(someNode, {renderIds}) + if n.info ?? "temp.nim": + # only output when it comes from "temp.nim" + echo renderTree(n) + if n.info ?? "temp.nim": + # why does it process temp.nim here? + writeStackTrace() + + The compiler's architecture =========================== -Nimrod uses the classic compiler architecture: A lexer/scanner feds tokens to a +Nim uses the classic compiler architecture: A lexer/scanner feds tokens to a parser. The parser builds a syntax tree that is used by the code generator. This syntax tree is the interface between the parser and the code generator. It is essential to understand most of the compiler's code. -In order to compile Nimrod correctly, type-checking has to be separated from +In order to compile Nim correctly, type-checking has to be separated from parsing. Otherwise generics cannot work. .. include:: filelist.txt @@ -172,7 +200,7 @@ Frontend issues Methods and type converters ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Nimrod contains language features that are *global*. The best example for that +Nim contains language features that are *global*. The best example for that are multi methods: Introducing a new method with the same name and some compatible object parameter means that the method's dispatcher needs to take the new method into account. So the dispatching logic is only completely known @@ -181,12 +209,12 @@ after the whole program has been translated! Other features that are *implicitly* triggered cause problems for modularity too. Type converters fall into this category: -.. code-block:: nimrod +.. code-block:: nim # module A converter toBool(x: int): bool = result = x != 0 -.. code-block:: nimrod +.. code-block:: nim # module B import A @@ -254,17 +282,15 @@ turned on". -Debugging Nimrod's memory management -==================================== +Debugging Nim's memory management +================================= The following paragraphs are mostly a reminder for myself. Things to keep in mind: -* Segmentation faults can have multiple reasons: One that is frequently - forgotten is that *stack overflow* can trigger one! -* If an assertion in Nimrod's memory manager or GC fails, the stack trace +* If an assertion in Nim's memory manager or GC fails, the stack trace keeps allocating memory! Thus a stack overflow may happen, hiding the - real issue. + real issue. * What seem to be C code generation problems is often a bug resulting from not producing prototypes, so that some types default to ``cint``. Testing without the ``-w`` option helps! @@ -300,12 +326,12 @@ set of pointers - this is called a ``TCellSet`` in the source code. Inserting, deleting and searching are done in constant time. However, modifying a ``TCellSet`` during traversation leads to undefined behaviour. -.. code-block:: Nimrod +.. code-block:: Nim type TCellSet # hidden - proc CellSetInit(s: var TCellSet) # initialize a new set - proc CellSetDeinit(s: var TCellSet) # empty the set and free its memory + proc cellSetInit(s: var TCellSet) # initialize a new set + proc cellSetDeinit(s: var TCellSet) # empty the set and free its memory proc incl(s: var TCellSet, elem: PCell) # include an element proc excl(s: var TCellSet, elem: PCell) # exclude an element @@ -340,11 +366,11 @@ Complete traversal is done in this way:: Further complications --------------------- -In Nimrod the compiler cannot always know if a reference +In Nim the compiler cannot always know if a reference is stored on the stack or not. This is caused by var parameters. Consider this example: -.. code-block:: Nimrod +.. code-block:: Nim proc setRef(r: var ref TNode) = new(r) @@ -380,7 +406,7 @@ Code generation for closures is implemented by `lambda lifting`:idx:. Design ------ -A ``closure`` proc var can call ordinary procs of the default Nimrod calling +A ``closure`` proc var can call ordinary procs of the default Nim calling convention. But not the other way round! A closure is implemented as a ``tuple[prc, env]``. ``env`` can be nil implying a call without a closure. This means that a call through a closure generates an ``if`` but the @@ -393,7 +419,7 @@ Tests with GCC on Amd64 showed that it's really beneficical if the Proper thunk generation is harder because the proc that is to wrap could stem from a complex expression: -.. code-block:: nimrod +.. code-block:: nim receivesClosure(returnsDefaultCC[i]) A thunk would need to call 'returnsDefaultCC[i]' somehow and that would require @@ -405,7 +431,7 @@ to pass a proc pointer around via a generic ``ref`` type. Example code: -.. code-block:: nimrod +.. code-block:: nim proc add(x: int): proc (y: int): int {.closure.} = return proc (y: int): int = return x + y @@ -415,7 +441,7 @@ Example code: This should produce roughly this code: -.. code-block:: nimrod +.. code-block:: nim type PEnv = ref object x: int # data @@ -436,7 +462,7 @@ This should produce roughly this code: Beware of nesting: -.. code-block:: nimrod +.. code-block:: nim proc add(x: int): proc (y: int): proc (z: int): int {.closure.} {.closure.} = return lamba (y: int): proc (z: int): int {.closure.} = return lambda (z: int): int = @@ -447,7 +473,7 @@ Beware of nesting: This should produce roughly this code: -.. code-block:: nimrod +.. code-block:: nim type PEnvX = ref object x: int # data @@ -492,8 +518,8 @@ environments. This is however not always possible. Accumulator ----------- -.. code-block:: nimrod - proc GetAccumulator(start: int): proc (): int {.closure} = +.. code-block:: nim + proc getAccumulator(start: int): proc (): int {.closure} = var i = start return lambda: int = inc i diff --git a/doc/keywords.txt b/doc/keywords.txt index a43b35475..7a4f8c696 100644 --- a/doc/keywords.txt +++ b/doc/keywords.txt @@ -1,9 +1,9 @@ addr and as asm atomic bind block break case cast const continue converter -discard distinct div do +defer discard distinct div do elif else end enum except export -finally for from +finally for from func generic if import in include interface is isnot iterator let diff --git a/doc/koch.txt b/doc/koch.txt index 7be2be6d4..7da137458 100644 --- a/doc/koch.txt +++ b/doc/koch.txt @@ -1,8 +1,8 @@ =============================== - Nimrod maintenance script + Nim maintenance script =============================== -:Version: |nimrodversion| +:Version: |nimversion| .. contents:: @@ -15,10 +15,10 @@ Introduction ============ -The `koch`:idx: program is Nimrod's maintenance script. It is a replacement +The `koch`:idx: program is Nim's maintenance script. It is a replacement for make and shell scripting with the advantage that it is much more portable. The word *koch* means *cook* in German. ``koch`` is used mainly to build the -Nimrod compiler, but it can also be used for other tasks. This document +Nim compiler, but it can also be used for other tasks. This document describes the supported commands and their options. @@ -40,21 +40,21 @@ options: option is not supported on Windows. -d:useGnuReadline Includes the `rdstdin module `_ for `interactive - mode `_ (aka ``nimrod i``). + mode `_ (aka ``nim i``). This is not needed on Windows. On other platforms this may incorporate the GNU readline library. -d:nativeStacktrace Use native stack traces (only for Mac OS X or Linux). -d:noCaas - Builds Nimrod without compiler as a service (CAAS) support. CAAS - support is required for functionality like Nimrod's `idetool + Builds Nim without compiler as a service (CAAS) support. CAAS + support is required for functionality like Nim's `idetool `_ command used to integrate the compiler with - `external IDEs `_. + `external IDEs `_. -d:avoidTimeMachine Only for Mac OS X, activating this switch will force excluding the generated ``nimcache`` directories from Time Machine backups. By default ``nimcache`` directories will be included in backups, - and just for the Nimrod compiler itself it means backing up 20MB + and just for the Nim compiler itself it means backing up 20MB of generated files each time you update the compiler. Using this option will make the compiler invoke the `tmutil `_ @@ -66,18 +66,18 @@ options: $ find . -type d -name nimcache -exec tmutil isexcluded \{\} \; -d:useFFI - Nimrod code can use the `foreign function interface (FFI) + Nim code can use the `foreign function interface (FFI) `_ at runtime, but macros - are limited to pure Nimrod code at compilation time. Enabling - this switch will allow macros to execute non-nimrod code at + are limited to pure Nim code at compilation time. Enabling + this switch will allow macros to execute non-nim code at compilation time (eg. open a file and write to it). --gc:refc|v2|markAndSweep|boehm|none Selects which garbage collection strategy to use for the compiler - and generated code. See the `Nimrod's Garbage Collector `_ + and generated code. See the `Nim's Garbage Collector `_ documentation for more information. -After compilation is finished you will hopefully end up with the nimrod -compiler in the ``bin`` directory. You can add Nimrod's ``bin`` directory to +After compilation is finished you will hopefully end up with the nim +compiler in the ``bin`` directory. You can add Nim's ``bin`` directory to your ``$PATH`` or use the `install command`_ to place it where it will be found. @@ -101,7 +101,7 @@ The `inno`:idx: command builds the `Inno Setup installer for Windows install command --------------- -The `install`:idx: command installs Nimrod to the specified directory, which +The `install`:idx: command installs Nim to the specified directory, which is required as a parameter. For example, on Unix platforms you could run:: $ ./koch install /usr/local/bin @@ -109,8 +109,8 @@ is required as a parameter. For example, on Unix platforms you could run:: temp command ------------ -The temp command builds the Nimrod compiler but with a different final name -(``nimrod_temp``), so it doesn't overwrite your normal compiler. You can use +The temp command builds the Nim compiler but with a different final name +(``nim_temp``), so it doesn't overwrite your normal compiler. You can use this command to test different options, the same you would issue for the `boot command`_. @@ -119,14 +119,14 @@ test command The `test`:idx: command can also be invoked with the alias ``tests``. This command will compile and run ``tests/testament/tester.nim``, which is the main -driver of Nimrod's test suite. You can pass options to the ``test`` command, +driver of Nim's test suite. You can pass options to the ``test`` command, they will be forwarded to the tester. See its source code for available options. update command -------------- -The `update`:idx: command updates nimrod to the latest version from github. +The `update`:idx: command updates nim to the latest version from github. For this command to work you need to have compiled ``koch`` itself with the ``-d:withUpdate`` switch. @@ -136,7 +136,7 @@ web command The `web`:idx: command converts the documentation in the ``doc`` directory from rst to HTML. It also repeats the same operation but places the result in the ``web/upload`` which can be used to update the website at -http://nimrod-lang.org. +http://nim-lang.org. By default the documentation will be built in parallel using the number of available CPU cores. If any documentation build sub commands fail, they will diff --git a/doc/lib.txt b/doc/lib.txt index 26ea1b735..45d9dfd2a 100644 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -1,15 +1,15 @@ -======================= -Nimrod Standard Library -======================= +==================== +Nim Standard Library +==================== :Author: Andreas Rumpf -:Version: |nimrodversion| +:Version: |nimversion| .. contents:: "The good thing about reinventing the wheel is that you can get a round one." -Though the Nimrod Standard Library is still evolving, it is already quite +Though the Nim Standard Library is still evolving, it is already quite usable. It is divided into *pure libraries*, *impure libraries* and *wrappers*. Pure libraries do not depend on any external ``*.dll`` or ``lib*.so`` binary @@ -18,8 +18,8 @@ low-level interface to a C library. Read this `document `_ for a quick overview of the API design. -The `bottom <#babel>`_ of this page includes a list of 3rd party packages -created by the Nimrod community. These packages are a useful addition to the +The `bottom <#nimble>`_ of this page includes a list of 3rd party packages +created by the Nim community. These packages are a useful addition to the modules in the standard library. @@ -41,28 +41,30 @@ Core of ``system``, but an extra import. * `threads `_ - Nimrod thread support. **Note**: This is part of the system module. Do not + Nim thread support. **Note**: This is part of the system module. Do not import it explicitly. * `channels `_ - Nimrod message passing support for threads. **Note**: This is part of the + Nim message passing support for threads. **Note**: This is part of the system module. Do not import it explicitly. * `locks `_ - Locks and condition variables for Nimrod. + Locks and condition variables for Nim. * `macros `_ - Contains the AST API and documentation of Nimrod for writing macros. + Contains the AST API and documentation of Nim for writing macros. * `typeinfo `_ - Provides (unsafe) access to Nimrod's run time type information. + Provides (unsafe) access to Nim's run time type information. * `typetraits `_ This module defines compile-time reflection procs for working with types. -* `actors `_ - Actor support for Nimrod; implemented as a layer on top of the threads and - channels modules. +* `threadpool `_ + Implements Nim's `spawn `_. + +* `cpuinfo `_ + This module implements procs to determine the number of CPUs / cores. Collections and algorithms @@ -71,11 +73,11 @@ Collections and algorithms * `algorithm `_ Implements some common generic algorithms like sort or binary search. * `tables `_ - Nimrod hash table support. Contains tables, ordered tables and count tables. + Nim hash table support. Contains tables, ordered tables and count tables. * `sets `_ - Nimrod hash and bit set support. + Nim hash and bit set support. * `lists `_ - Nimrod linked list support. Contains singly and doubly linked lists and + Nim linked list support. Contains singly and doubly linked lists and circular lists ("rings"). * `queues `_ Implementation of a queue. The underlying implementation uses a ``seq``. @@ -152,12 +154,12 @@ Generic Operating System Services * `streams `_ This module provides a stream interface and two implementations thereof: - the `PFileStream` and the `PStringStream` which implement the stream - interface for Nimrod file objects (`TFile`) and strings. Other modules + the `FileStream` and the `StringStream` which implement the stream + interface for Nim file objects (`File`) and strings. Other modules may provide other implementations for this standard stream interface. * `marshal `_ - Contains procs for serialization and deseralization of arbitrary Nimrod + Contains procs for serialization and deseralization of arbitrary Nim data structures. * `terminal `_ @@ -173,6 +175,7 @@ Generic Operating System Services This module implements the ability to monitor a directory/file for changes using Posix's inotify API. + Math libraries -------------- @@ -182,6 +185,9 @@ Math libraries * `complex `_ This module implements complex numbers and their mathematical operations. +* `fenv `_ + Floating-point environment. Handling of floating-point rounding and + exceptions (overflow, zero-devide, etc.). Internet Protocols and Support @@ -273,7 +279,7 @@ Parsers parser. The configuration file's syntax is similar to the Windows ``.ini`` format, but much more powerful, as it is not a line based parser. String literals, raw string literals and triple quote string literals are supported - as in the Nimrod programming language. + as in the Nim programming language. * `parsexml `_ The ``parsexml`` module implements a simple high performance XML/HTML parser. @@ -340,7 +346,7 @@ Cryptography and Hashing * `hashes `_ This module implements efficient computations of hash values for diverse - Nimrod types. + Nim types. * `md5 `_ This module implements the MD5 checksum algorithm. @@ -353,7 +359,7 @@ Multimedia support ------------------ * `colors `_ - This module implements color handling for Nimrod. It is used by + This module implements color handling for Nim. It is used by the ``graphics`` module. @@ -426,12 +432,12 @@ Other ----- * `graphics `_ - This module implements graphical output for Nimrod; the current + This module implements graphical output for Nim; the current implementation uses SDL but the interface is meant to support multiple backends some day. * `dialogs `_ - This module implements portable dialogs for Nimrod; the implementation + This module implements portable dialogs for Nim; the implementation builds on the GTK interface. On Windows, native dialogs are shown if appropriate. @@ -444,7 +450,7 @@ Other * `ssl `_ This module provides an easy to use sockets-style - Nimrod interface to the OpenSSL library. + Nim interface to the OpenSSL library. * `rdstdin `_ This module contains code for reading from `stdin`:idx:. On UNIX the GNU @@ -532,7 +538,7 @@ Database support * `odbcsql `_ interface to the ODBC driver. * `sphinx `_ - Nimrod wrapper for ``sphinx``. + Nim wrapper for ``sphinx``. XML Processing @@ -575,36 +581,36 @@ Scientific computing * `libsvm `_ Low level wrapper for `lib svm `_. -Babel +Nimble ==================== -Babel is a package manager for the Nimrod programming language. -For instructions on how to install Babel packages see -`its README `_. +Nimble is a package manager for the Nim programming language. +For instructions on how to install Nimble packages see +`its README `_. Official packages ----------------- These packages are officially supported and will therefore be continually -maintained to ensure that they work with the latest versions of the Nimrod +maintained to ensure that they work with the latest versions of the Nim compiler. .. raw:: html
      If you are reading this you are missing - babelpkglist.js or have javascript disabled in your browser.
      + nimblepkglist.js or have javascript disabled in your browser.
      Unofficial packages ------------------- -These packages have been developed by independent Nimrod developers and as +These packages have been developed by independent Nim developers and as such may not always be up to date with the latest developments in the -Nimrod programming language. +Nim programming language. .. raw:: html
      If you are reading this you are missing - babelpkglist.js or have javascript disabled in your browser.
      + nimblepkglist.js or have javascript disabled in your browser. - - + + diff --git a/doc/manual.txt b/doc/manual.txt index c150d27e2..2dbbf0447 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -1,9 +1,9 @@ -============= -Nimrod Manual -============= +========== +Nim Manual +========== :Authors: Andreas Rumpf, Zahary Karadjov -:Version: |nimrodversion| +:Version: |nimversion| .. contents:: @@ -13,5859 +13,27 @@ Nimrod Manual pretty much constant for a given task. -- Ran - -About this document -=================== - -**Note**: This document is a draft! Several of Nimrod's features need more -precise wording. This manual will evolve into a proper specification some -day. - -This document describes the lexis, the syntax, and the semantics of Nimrod. - -The language constructs are explained using an extended BNF, in -which ``(a)*`` means 0 or more ``a``'s, ``a+`` means 1 or more ``a``'s, and -``(a)?`` means an optional *a*. Parentheses may be used to group elements. - -``&`` is the lookahead operator; ``&a`` means that an ``a`` is expected but -not consumed. It will be consumed in the following rule. - -The ``|``, ``/`` symbols are used to mark alternatives and have the lowest -precedence. ``/`` is the ordered choice that requires the parser to try the -alternatives in the given order. ``/`` is often used to ensure the grammar -is not ambiguous. - -Non-terminals start with a lowercase letter, abstract terminal symbols are in -UPPERCASE. Verbatim terminal symbols (including keywords) are quoted -with ``'``. An example:: - - ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)? - -The binary ``^*`` operator is used as a shorthand for 0 or more occurances -separated by its second argument; likewise ``^+`` means 1 or more -occurances: ``a ^+ b`` is short for ``a (b a)*`` -and ``a ^* b`` is short for ``(a (b a)*)?``. Example:: - - arrayConstructor = '[' expr ^* ',' ']' - -Other parts of Nimrod - like scoping rules or runtime semantics are only -described in an informal manner for now. - - -Definitions -=========== - -A Nimrod program specifies a computation that acts on a memory consisting of -components called `locations`:idx:. A variable is basically a name for a -location. Each variable and location is of a certain `type`:idx:. The -variable's type is called `static type`:idx:, the location's type is called -`dynamic type`:idx:. If the static type is not the same as the dynamic type, -it is a super-type or subtype of the dynamic type. - -An `identifier`:idx: is a symbol declared as a name for a variable, type, -procedure, etc. The region of the program over which a declaration applies is -called the `scope`:idx: of the declaration. Scopes can be nested. The meaning -of an identifier is determined by the smallest enclosing scope in which the -identifier is declared unless overloading resolution rules suggest otherwise. - -An expression specifies a computation that produces a value or location. -Expressions that produce locations are called `l-values`:idx:. An l-value -can denote either a location or the value the location contains, depending on -the context. Expressions whose values can be determined statically are called -`constant expressions`:idx:; they are never l-values. - -A `static error`:idx: is an error that the implementation detects before -program execution. Unless explicitly classified, an error is a static error. - -A `checked runtime error`:idx: is an error that the implementation detects -and reports at runtime. The method for reporting such errors is via *raising -exceptions* or *dying with a fatal error*. However, the implementation -provides a means to disable these runtime checks. See the section pragmas_ -for details. - -Wether a checked runtime error results in an exception or in a fatal error at -runtime is implementation specific. Thus the following program is always -invalid: - -.. code-block:: nimrod - var a: array[0..1, char] - let i = 5 - try: - a[i] = 'N' - except EInvalidIndex: - echo "invalid index" - -An `unchecked runtime error`:idx: is an error that is not guaranteed to be -detected, and can cause the subsequent behavior of the computation to -be arbitrary. Unchecked runtime errors cannot occur if only `safe`:idx: -language features are used. - - -Lexical Analysis -================ - -Encoding --------- - -All Nimrod source files are in the UTF-8 encoding (or its ASCII subset). Other -encodings are not supported. Any of the standard platform line termination -sequences can be used - the Unix form using ASCII LF (linefeed), the Windows -form using the ASCII sequence CR LF (return followed by linefeed), or the old -Macintosh form using the ASCII CR (return) character. All of these forms can be -used equally, regardless of platform. - - -Indentation ------------ - -Nimrod's standard grammar describes an `indentation sensitive`:idx: language. -This means that all the control structures are recognized by indentation. -Indentation consists only of spaces; tabulators are not allowed. - -The indentation handling is implemented as follows: The lexer annotates the -following token with the preceding number of spaces; indentation is not -a separate token. This trick allows parsing of Nimrod with only 1 token of -lookahead. - -The parser uses a stack of indentation levels: the stack consists of integers -counting the spaces. The indentation information is queried at strategic -places in the parser but ignored otherwise: The pseudo terminal ``IND{>}`` -denotes an indentation that consists of more spaces than the entry at the top -of the stack; IND{=} an indentation that has the same number of spaces. ``DED`` -is another pseudo terminal that describes the *action* of popping a value -from the stack, ``IND{>}`` then implies to push onto the stack. - -With this notation we can now easily define the core of the grammar: A block of -statements (simplified example):: - - ifStmt = 'if' expr ':' stmt - (IND{=} 'elif' expr ':' stmt)* - (IND{=} 'else' ':' stmt)? - - simpleStmt = ifStmt / ... - - stmt = IND{>} stmt ^+ IND{=} DED # list of statements - / simpleStmt # or a simple statement - - - -Comments --------- - -Comments start anywhere outside a string or character literal with the -hash character ``#``. -Comments consist of a concatenation of `comment pieces`:idx:. A comment piece -starts with ``#`` and runs until the end of the line. The end of line characters -belong to the piece. If the next line only consists of a comment piece which is -aligned to the preceding one, it does not start a new comment: - -.. code-block:: nimrod - - i = 0 # This is a single comment over multiple lines belonging to the - # assignment statement. The scanner merges these two pieces. - # This is a new comment belonging to the current block, but to no particular - # statement. - i = i + 1 # This a new comment that is NOT - echo(i) # continued here, because this comment refers to the echo statement - - -The alignment requirement does not hold if the preceding comment piece ends in -a backslash (followed by optional whitespace): - -.. code-block:: nimrod - type - TMyObject {.final, pure, acyclic.} = object # comment continues: \ - # we have lots of space here to comment 'TMyObject'. - # This line belongs to the comment as it's properly aligned. - -Comments are tokens; they are only allowed at certain places in the input file -as they belong to the syntax tree! This feature enables perfect source-to-source -transformations (such as pretty-printing) and superior documentation generators. -A nice side-effect is that the human reader of the code always knows exactly -which code snippet the comment refers to. - - -Identifiers & Keywords ----------------------- - -Identifiers in Nimrod can be any string of letters, digits -and underscores, beginning with a letter. Two immediate following -underscores ``__`` are not allowed:: - - letter ::= 'A'..'Z' | 'a'..'z' | '\x80'..'\xff' - digit ::= '0'..'9' - IDENTIFIER ::= letter ( ['_'] (letter | digit) )* - -Currently any unicode character with an ordinal value > 127 (non ASCII) is -classified as a ``letter`` and may thus be part of an identifier but later -versions of the language may assign some Unicode characters to belong to the -operator characters instead. - -The following keywords are reserved and cannot be used as identifiers: - -.. code-block:: nimrod - :file: keywords.txt - -Some keywords are unused; they are reserved for future developments of the -language. - -Nimrod is a `style-insensitive`:idx: language. This means that it is not -case-sensitive and even underscores are ignored: -**type** is a reserved word, and so is **TYPE** or **T_Y_P_E**. The idea behind -this is that this allows programmers to use their own preferred spelling style -and libraries written by different programmers cannot use incompatible -conventions. A Nimrod-aware editor or IDE can show the identifiers as -preferred. Another advantage is that it frees the programmer from remembering -the exact spelling of an identifier. - - -String literals ---------------- - -Terminal symbol in the grammar: ``STR_LIT``. - -String literals can be delimited by matching double quotes, and can -contain the following `escape sequences`:idx:\ : - -================== =================================================== - Escape sequence Meaning -================== =================================================== - ``\n`` `newline`:idx: - ``\r``, ``\c`` `carriage return`:idx: - ``\l`` `line feed`:idx: - ``\f`` `form feed`:idx: - ``\t`` `tabulator`:idx: - ``\v`` `vertical tabulator`:idx: - ``\\`` `backslash`:idx: - ``\"`` `quotation mark`:idx: - ``\'`` `apostrophe`:idx: - ``\`` '0'..'9'+ `character with decimal value d`:idx:; - all decimal digits directly - following are used for the character - ``\a`` `alert`:idx: - ``\b`` `backspace`:idx: - ``\e`` `escape`:idx: `[ESC]`:idx: - ``\x`` HH `character with hex value HH`:idx:; - exactly two hex digits are allowed -================== =================================================== - - -Strings in Nimrod may contain any 8-bit value, even embedded zeros. However -some operations may interpret the first binary zero as a terminator. - - -Triple quoted string literals ------------------------------ - -Terminal symbol in the grammar: ``TRIPLESTR_LIT``. - -String literals can also be delimited by three double quotes -``"""`` ... ``"""``. -Literals in this form may run for several lines, may contain ``"`` and do not -interpret any escape sequences. -For convenience, when the opening ``"""`` is followed by a newline (there may -be whitespace between the opening ``"""`` and the newline), -the newline (and the preceding whitespace) is not included in the string. The -ending of the string literal is defined by the pattern ``"""[^"]``, so this: - -.. code-block:: nimrod - """"long string within quotes"""" - -Produces:: - - "long string within quotes" - - -Raw string literals -------------------- - -Terminal symbol in the grammar: ``RSTR_LIT``. - -There are also raw string literals that are preceded with the -letter ``r`` (or ``R``) and are delimited by matching double quotes (just -like ordinary string literals) and do not interpret the escape sequences. -This is especially convenient for regular expressions or Windows paths: - -.. code-block:: nimrod - - var f = openFile(r"C:\texts\text.txt") # a raw string, so ``\t`` is no tab - -To produce a single ``"`` within a raw string literal, it has to be doubled: - -.. code-block:: nimrod - - r"a""b" - -Produces:: - - a"b - -``r""""`` is not possible with this notation, because the three leading -quotes introduce a triple quoted string literal. ``r"""`` is the same -as ``"""`` since triple quoted string literals do not interpret escape -sequences either. - - -Generalized raw string literals -------------------------------- - -Terminal symbols in the grammar: ``GENERALIZED_STR_LIT``, -``GENERALIZED_TRIPLESTR_LIT``. - -The construct ``identifier"string literal"`` (without whitespace between the -identifier and the opening quotation mark) is a -generalized raw string literal. It is a shortcut for the construct -``identifier(r"string literal")``, so it denotes a procedure call with a -raw string literal as its only argument. Generalized raw string literals -are especially convenient for embedding mini languages directly into Nimrod -(for example regular expressions). - -The construct ``identifier"""string literal"""`` exists too. It is a shortcut -for ``identifier("""string literal""")``. - - -Character literals ------------------- - -Character literals are enclosed in single quotes ``''`` and can contain the -same escape sequences as strings - with one exception: `newline`:idx: (``\n``) -is not allowed as it may be wider than one character (often it is the pair -CR/LF for example). Here are the valid `escape sequences`:idx: for character -literals: - -================== =================================================== - Escape sequence Meaning -================== =================================================== - ``\r``, ``\c`` `carriage return`:idx: - ``\l`` `line feed`:idx: - ``\f`` `form feed`:idx: - ``\t`` `tabulator`:idx: - ``\v`` `vertical tabulator`:idx: - ``\\`` `backslash`:idx: - ``\"`` `quotation mark`:idx: - ``\'`` `apostrophe`:idx: - ``\`` '0'..'9'+ `character with decimal value d`:idx:; - all decimal digits directly - following are used for the character - ``\a`` `alert`:idx: - ``\b`` `backspace`:idx: - ``\e`` `escape`:idx: `[ESC]`:idx: - ``\x`` HH `character with hex value HH`:idx:; - exactly two hex digits are allowed -================== =================================================== - -A character is not an Unicode character but a single byte. The reason for this -is efficiency: for the overwhelming majority of use-cases, the resulting -programs will still handle UTF-8 properly as UTF-8 was specially designed for -this. Another reason is that Nimrod can thus support ``array[char, int]`` or -``set[char]`` efficiently as many algorithms rely on this feature. The `TRune` -type is used for Unicode characters, it can represent any Unicode character. -``TRune`` is declared in the `unicode module `_. - - -Numerical constants -------------------- - -Numerical constants are of a single type and have the form:: - - hexdigit = digit | 'A'..'F' | 'a'..'f' - octdigit = '0'..'7' - bindigit = '0'..'1' - HEX_LIT = '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )* - DEC_LIT = digit ( ['_'] digit )* - OCT_LIT = '0o' octdigit ( ['_'] octdigit )* - BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )* - - INT_LIT = HEX_LIT - | DEC_LIT - | OCT_LIT - | BIN_LIT - - INT8_LIT = INT_LIT ['\''] ('i' | 'I') '8' - INT16_LIT = INT_LIT ['\''] ('i' | 'I') '16' - INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32' - INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64' - - UINT8_LIT = INT_LIT ['\''] ('u' | 'U') - UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8' - UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16' - UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32' - UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64' - - 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' - - -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 ``0x``) notation. - -There exists a literal for each numerical type that is -defined. The suffix starting with an apostrophe ('\'') is called a -`type suffix`:idx:. Literals without a type suffix are of the type ``int``, -unless the literal contains a dot or ``E|e`` in which case it is of -type ``float``. For notational convenience the apostrophe of a type suffix -is optional if it is not ambiguous (only hexadecimal floating point literals -with a type suffix can be ambiguous). - - -The type suffixes are: - -================= ========================= - Type Suffix Resulting type of literal -================= ========================= - ``'i8`` int8 - ``'i16`` int16 - ``'i32`` int32 - ``'i64`` int64 - ``'u`` uint - ``'u8`` uint8 - ``'u16`` uint16 - ``'u32`` uint32 - ``'u64`` uint64 - ``'f32`` float32 - ``'f64`` float64 -================= ========================= - -Floating point literals may also be in binary, octal or hexadecimal -notation: -``0B0_10001110100_0000101001000111101011101111111011000101001101001001'f64`` -is approximately 1.72826e35 according to the IEEE floating point standard. - - -Operators ---------- - -In Nimrod one can define his own operators. An operator is any -combination of the following characters:: - - = + - * / < > - @ $ ~ & % | - ! ? ^ . : \ - -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. - -``*:`` is as a special case the two tokens `*`:tok: and `:`:tok: -(to support ``var v*: T``). - - -Other tokens ------------- - -The following strings denote other tokens:: - - ` ( ) { } [ ] , ; [. .] {. .} (. .) - - -The `slice`:idx: operator `..`:tok: takes precedence over other tokens that -contain a dot: `{..}`:tok: are the three tokens `{`:tok:, `..`:tok:, `}`:tok: -and not the two tokens `{.`:tok:, `.}`:tok:. - - -Syntax -====== - -This section lists Nimrod's standard syntax. How the parser handles -the indentation is already described in the `Lexical Analysis`_ section. - -Nimrod allows user-definable operators. -Binary operators have 10 different levels of precedence. - -Relevant character ------------------- - -An operator symbol's *relevant character* is its first -character unless the first character is ``\`` and its length is greater than 1 -then it is the second character. - -This rule allows to escape operator symbols with ``\`` and keeps the operator's -precedence and associativity; this is useful for meta programming. - - -Associativity -------------- - -Binary operators whose relevant character is ``^`` are right-associative, all -other binary operators are left-associative. - -Precedence ----------- - -Unary operators always bind stronger than any binary -operator: ``$a + b`` is ``($a) + b`` and not ``$(a + b)``. - -If an unary operator's relevant character is ``@`` it is a `sigil-like`:idx: -operator which binds stronger than a ``primarySuffix``: ``@x.abc`` is parsed -as ``(@x).abc`` whereas ``$x.abc`` is parsed as ``$(x.abc)``. - - -For binary operators that are not keywords the precedence is determined by the -following rules: - -If the operator ends with ``=`` and its relevant character is none of -``<``, ``>``, ``!``, ``=``, ``~``, ``?``, it is an *assignment operator* which -has the lowest precedence. - -Otherwise precedence is determined by the relevant character. - -================ =============================================== ================== =============== -Precedence level Operators Relevant character Terminal symbol -================ =============================================== ================== =============== - 9 (highest) ``$ ^`` OP9 - 8 ``* / div mod shl shr %`` ``* % \ /`` OP8 - 7 ``+ -`` ``+ ~ |`` OP7 - 6 ``&`` ``&`` OP6 - 5 ``..`` ``.`` OP5 - 4 ``== <= < >= > != in notin is isnot not of`` ``= < > !`` OP4 - 3 ``and`` OP3 - 2 ``or xor`` OP2 - 1 ``@ : ?`` OP1 - 0 (lowest) *assignment operator* (like ``+=``, ``*=``) OP0 -================ =============================================== ================== =============== - - -Strong spaces -------------- - -The number of spaces preceeding a non-keyword operator affects precedence -if the experimental parser directive ``#!strongSpaces`` is used. Indentation -is not used to determine the number of spaces. If 2 or more operators have the -same number of preceding spaces the precedence table applies, so ``1 + 3 * 4`` -is still parsed as ``1 + (3 * 4)``, but ``1+3 * 4`` is parsed as ``(1+3) * 4``: - -.. code-block:: nimrod - #! strongSpaces - if foo+4 * 4 == 8 and b&c | 9 ++ - bar: - echo "" - # is parsed as - if ((foo+4)*4 == 8) and (((b&c) | 9) ++ bar): echo "" - - -Furthermore whether an operator is used a prefix operator is affected by the -number of spaces: - -.. code-block:: nimrod - #! strongSpaces - echo $foo - # is parsed as - echo($foo) - -This also affects whether ``[]``, ``{}``, ``()`` are parsed as constructors -or as accessors: - -.. code-block:: nimrod - #! strongSpaces - echo (1,2) - # is parsed as - echo((1,2)) - -Only 0, 1, 2, 4 or 8 spaces are allowed to specify precedence and it is -enforced that infix operators have the same amount of spaces before and after -them. This rules does not apply when a newline follows after the operator, -then only the preceding spaces are considered. - - -Grammar -------- - -The grammar's start symbol is ``module``. - -.. include:: grammar.txt - :literal: - - - -Types -===== - -All expressions have a type which is known at compile time. Nimrod -is statically typed. One can declare new types, which is in essence defining -an identifier that can be used to denote this custom type. - -These are the major type classes: - -* ordinal types (consist of integer, bool, character, enumeration - (and subranges thereof) types) -* floating point types -* string type -* structured types -* reference (pointer) type -* procedural type -* generic type - - -Ordinal types -------------- -Ordinal types have the following characteristics: - -- Ordinal types are countable and ordered. This property allows - the operation of functions as ``Inc``, ``Ord``, ``Dec`` on ordinal types to - be defined. -- Ordinal values have a smallest possible value. Trying to count further - down than the smallest value gives a checked runtime or static error. -- Ordinal values have a largest possible value. Trying to count further - than the largest value gives a checked runtime or static error. - -Integers, bool, characters and enumeration types (and subranges of these -types) belong to ordinal types. For reasons of simplicity of implementation -the types ``uint`` and ``uint64`` are not ordinal types. - - -Pre-defined integer types -------------------------- -These integer types are pre-defined: - -``int`` - the generic signed integer type; its size is platform dependent and has the - same size as a pointer. This type should be used in general. An integer - literal that has no type suffix is of this type. - -intXX - additional signed integer types of XX bits use this naming scheme - (example: int16 is a 16 bit wide integer). - The current implementation supports ``int8``, ``int16``, ``int32``, ``int64``. - Literals of these types have the suffix 'iXX. - -``uint`` - the generic `unsigned integer`:idx: type; its size is platform dependent and - has the same size as a pointer. An integer literal with the type - suffix ``'u`` is of this type. - -uintXX - additional signed integer types of XX bits use this naming scheme - (example: uint16 is a 16 bit wide unsigned integer). - The current implementation supports ``uint8``, ``uint16``, ``uint32``, - ``uint64``. Literals of these types have the suffix 'uXX. - Unsigned operations all wrap around; they cannot lead to over- or - underflow errors. - - -In addition to the usual arithmetic operators for signed and unsigned integers -(``+ - *`` etc.) there are also operators that formally work on *signed* -integers but treat their arguments as *unsigned*: They are mostly provided -for backwards compatibility with older versions of the language that lacked -unsigned integer types. These unsigned operations for signed integers use -the ``%`` suffix as convention: - - -====================== ====================================================== -operation meaning -====================== ====================================================== -``a +% b`` unsigned integer addition -``a -% b`` unsigned integer subtraction -``a *% b`` unsigned integer multiplication -``a /% b`` unsigned integer division -``a %% b`` unsigned integer modulo operation -``a <% b`` treat ``a`` and ``b`` as unsigned and compare -``a <=% b`` treat ``a`` and ``b`` as unsigned and compare -``ze(a)`` extends the bits of ``a`` with zeros until it has the - width of the ``int`` type -``toU8(a)`` treats ``a`` as unsigned and converts it to an - unsigned integer of 8 bits (but still the - ``int8`` type) -``toU16(a)`` treats ``a`` as unsigned and converts it to an - unsigned integer of 16 bits (but still the - ``int16`` type) -``toU32(a)`` treats ``a`` as unsigned and converts it to an - unsigned integer of 32 bits (but still the - ``int32`` type) -====================== ====================================================== - -`Automatic type conversion`:idx: is performed in expressions where different -kinds of integer types are used: the smaller type is converted to the larger. - -A `narrowing type conversion`:idx: converts a larger to a smaller type (for -example ``int32 -> int16``. A `widening type conversion`:idx: converts a -smaller type to a larger type (for example ``int16 -> int32``). In Nimrod only -widening type conversions are *implicit*: - -.. code-block:: nimrod - var myInt16 = 5i16 - var myInt: int - myInt16 + 34 # of type ``int16`` - myInt16 + myInt # of type ``int`` - myInt16 + 2i32 # of type ``int32`` - -However, ``int`` literals are implicitly convertible to a smaller integer type -if the literal's value fits this smaller type and such a conversion is less -expensive than other implicit conversions, so ``myInt16 + 34`` produces -an ``int16`` result. - -For further details, see `Convertible relation`_. - - -Subrange types --------------- -A subrange type is a range of values from an ordinal type (the base -type). To define a subrange type, one must specify it's limiting values: the -lowest and highest value of the type: - -.. code-block:: nimrod - type - TSubrange = range[0..5] - - -``TSubrange`` is a subrange of an integer which can only hold the values 0 -to 5. Assigning any other value to a variable of type ``TSubrange`` is a -checked runtime error (or static error if it can be statically -determined). Assignments from the base type to one of its subrange types -(and vice versa) are allowed. - -A subrange type has the same size as its base type (``int`` in the example). - -Nimrod requires `interval arithmetic`:idx: for subrange types over a set -of built-in operators that involve constants: ``x %% 3`` is of -type ``range[0..2]``. The following built-in operators for integers are -affected by this rule: ``-``, ``+``, ``*``, ``min``, ``max``, ``succ``, -``pred``, ``mod``, ``div``, ``%%``, ``and`` (bitwise ``and``). - -Bitwise ``and`` only produces a ``range`` if one of its operands is a -constant *x* so that (x+1) is a number of two. -(Bitwise ``and`` is then a ``%%`` operation.) - -This means that the following code is accepted: - -.. code-block:: nimrod - case (x and 3) + 7 - of 7: echo "A" - of 8: echo "B" - of 9: echo "C" - of 10: echo "D" - # note: no ``else`` required as (x and 3) + 7 has the type: range[7..10] - - -Pre-defined floating point types --------------------------------- - -The following floating point types are pre-defined: - -``float`` - the generic floating point type; its size is platform dependent - (the compiler chooses the processor's fastest floating point type). - This type should be used in general. - -floatXX - an implementation may define additional floating point types of XX bits using - this naming scheme (example: float64 is a 64 bit wide float). The current - implementation supports ``float32`` and ``float64``. Literals of these types - have the suffix 'fXX. - - -Automatic type conversion in expressions with different kinds -of floating point types is performed: See `Convertible relation`_ for further -details. Arithmetic performed on floating point types follows the IEEE -standard. Integer types are not converted to floating point types automatically -and vice versa. - -The IEEE standard defines five types of floating-point exceptions: - -* Invalid: operations with mathematically invalid operands, - for example 0.0/0.0, sqrt(-1.0), and log(-37.8). -* Division by zero: divisor is zero and dividend is a finite nonzero number, - for example 1.0/0.0. -* Overflow: operation produces a result that exceeds the range of the exponent, - for example MAXDOUBLE+0.0000000000001e308. -* Underflow: operation produces a result that is too small to be represented - as a normal number, for example, MINDOUBLE * MINDOUBLE. -* Inexact: operation produces a result that cannot be represented with infinite - precision, for example, 2.0 / 3.0, log(1.1) and 0.1 in input. - -The IEEE exceptions are either ignored at runtime or mapped to the -Nimrod exceptions: `EFloatInvalidOp`:idx:, `EFloatDivByZero`:idx:, -`EFloatOverflow`:idx:, `EFloatUnderflow`:idx:, and `EFloatInexact`:idx:. -These exceptions inherit from the `EFloatingPoint`:idx: base class. - -Nimrod provides the pragmas `NaNChecks`:idx: and `InfChecks`:idx: to control -whether the IEEE exceptions are ignored or trap a Nimrod exception: - -.. code-block:: nimrod - {.NanChecks: on, InfChecks: on.} - var a = 1.0 - var b = 0.0 - echo b / b # raises EFloatInvalidOp - echo a / b # raises EFloatOverflow - -In the current implementation ``EFloatDivByZero`` and ``EFloatInexact`` are -never raised. ``EFloatOverflow`` is raised instead of ``EFloatDivByZero``. -There is also a `floatChecks`:idx: pragma that is a short-cut for the -combination of ``NaNChecks`` and ``InfChecks`` pragmas. ``floatChecks`` are -turned off as default. - -The only operations that are affected by the ``floatChecks`` pragma are -the ``+``, ``-``, ``*``, ``/`` operators for floating point types. - - -Boolean type ------------- -The boolean type is named `bool`:idx: in Nimrod and can be one of the two -pre-defined values ``true`` and ``false``. Conditions in while, -if, elif, when statements need to be of type bool. - -This condition holds:: - - ord(false) == 0 and ord(true) == 1 - -The operators ``not, and, or, xor, <, <=, >, >=, !=, ==`` are defined -for the bool type. The ``and`` and ``or`` operators perform short-cut -evaluation. Example: - -.. code-block:: nimrod - - while p != nil and p.name != "xyz": - # p.name is not evaluated if p == nil - p = p.next - - -The size of the bool type is one byte. - - -Character type --------------- -The character type is named ``char`` in Nimrod. Its size is one byte. -Thus it cannot represent an UTF-8 character, but a part of it. -The reason for this is efficiency: for the overwhelming majority of use-cases, -the resulting programs will still handle UTF-8 properly as UTF-8 was specially -designed for this. -Another reason is that Nimrod can support ``array[char, int]`` or -``set[char]`` efficiently as many algorithms rely on this feature. The -`TRune` type is used for Unicode characters, it can represent any Unicode -character. ``TRune`` is declared in the `unicode module `_. - - - - -Enumeration types ------------------ -Enumeration types define a new type whose values consist of the ones -specified. The values are ordered. Example: - -.. code-block:: nimrod - - type - TDirection = enum - north, east, south, west - - -Now the following holds:: - - ord(north) == 0 - ord(east) == 1 - ord(south) == 2 - ord(west) == 3 - -Thus, north < east < south < west. The comparison operators can be used -with enumeration types. - -For better interfacing to other programming languages, the fields of enum -types can be assigned an explicit ordinal value. However, the ordinal values -have to be in ascending order. A field whose ordinal value is not -explicitly given is assigned the value of the previous field + 1. - -An explicit ordered enum can have *holes*: - -.. code-block:: nimrod - type - TTokenType = enum - a = 2, b = 4, c = 89 # holes are valid - -However, it is then not an ordinal anymore, so it is not possible to use these -enums as an index type for arrays. The procedures ``inc``, ``dec``, ``succ`` -and ``pred`` are not available for them either. - - -The compiler supports the built-in stringify operator ``$`` for enumerations. -The stringify's result can be controlled by explicitly giving the string -values to use: - -.. code-block:: nimrod - - type - TMyEnum = enum - valueA = (0, "my value A"), - valueB = "value B", - valueC = 2, - valueD = (3, "abc") - -As can be seen from the example, it is possible to both specify a field's -ordinal value and its string value by using a tuple. It is also -possible to only specify one of them. - -An enum can be marked with the ``pure`` pragma so that it's fields are not -added to the current scope, so they always need to be accessed -via ``TMyEnum.value``: - -.. code-block:: nimrod - - type - TMyEnum {.pure.} = enum - valueA, valueB, valueC, valueD - - echo valueA # error: Unknown identifier - echo TMyEnum.valueA # works - - -String type ------------ -All string literals are of the type ``string``. A string in Nimrod is very -similar to a sequence of characters. However, strings in Nimrod are both -zero-terminated and have a length field. One can retrieve the length with the -builtin ``len`` procedure; the length never counts the terminating zero. -The assignment operator for strings always copies the string. -The ``&`` operator concatenates strings. - -Strings are compared by their lexicographical order. All comparison operators -are available. Strings can be indexed like arrays (lower bound is 0). Unlike -arrays, they can be used in case statements: - -.. code-block:: nimrod - - case paramStr(i) - of "-v": incl(options, optVerbose) - of "-h", "-?": incl(options, optHelp) - else: write(stdout, "invalid command line option!\n") - -Per convention, all strings are UTF-8 strings, but this is not enforced. For -example, when reading strings from binary files, they are merely a sequence of -bytes. The index operation ``s[i]`` means the i-th *char* of ``s``, not the -i-th *unichar*. The iterator ``runes`` from the `unicode module -`_ can be used for iteration over all Unicode characters. - - -CString type ------------- -The ``cstring`` type represents a pointer to a zero-terminated char array -compatible to the type ``char*`` in Ansi C. Its primary purpose lies in easy -interfacing with C. The index operation ``s[i]`` means the i-th *char* of -``s``; however no bounds checking for ``cstring`` is performed making the -index operation unsafe. - -A Nimrod ``string`` is implicitly convertible -to ``cstring`` for convenience. If a Nimrod string is passed to a C-style -variadic proc, it is implicitly converted to ``cstring`` too: - -.. code-block:: nimrod - proc printf(formatstr: cstring) {.importc: "printf", varargs, - header: "".} - - printf("This works %s", "as expected") - -Even though the conversion is implicit, it is not *safe*: The garbage collector -does not consider a ``cstring`` to be a root and may collect the underlying -memory. However in practice this almost never happens as the GC considers -stack roots conservatively. One can use the builtin procs ``GC_ref`` and -``GC_unref`` to keep the string data alive for the rare cases where it does -not work. - -A `$` proc is defined for cstrings that returns a string. Thus to get a nimrod -string from a cstring: - -.. code-block:: nimrod - var str: string = "Hello!" - var cstr: cstring = s - var newstr: string = $cstr - - -Structured types ----------------- -A variable of a structured type can hold multiple values at the same -time. Structured types can be nested to unlimited levels. Arrays, sequences, -tuples, objects and sets belong to the structured types. - -Array and sequence types ------------------------- -Arrays are a homogeneous type, meaning that each element in the array -has the same type. Arrays always have a fixed length which is specified at -compile time (except for open arrays). They can be indexed by any ordinal type. -A parameter ``A`` may be an *open array*, in which case it is indexed by -integers from 0 to ``len(A)-1``. An array expression may be constructed by the -array constructor ``[]``. - -Sequences are similar to arrays but of dynamic length which may change -during runtime (like strings). Sequences are implemented as growable arrays, -allocating pieces of memory as items are added. A sequence ``S`` is always -indexed by integers from 0 to ``len(S)-1`` and its bounds are checked. -Sequences can be constructed by the array constructor ``[]`` in conjunction -with the array to sequence operator ``@``. Another way to allocate space for a -sequence is to call the built-in ``newSeq`` procedure. - -A sequence may be passed to a parameter that is of type *open array*. - -Example: - -.. code-block:: nimrod - - type - TIntArray = array[0..5, int] # an array that is indexed with 0..5 - TIntSeq = seq[int] # a sequence of integers - var - x: TIntArray - y: TIntSeq - x = [1, 2, 3, 4, 5, 6] # [] is the array constructor - y = @[1, 2, 3, 4, 5, 6] # the @ turns the array into a sequence - -The lower bound of an array or sequence may be received by the built-in proc -``low()``, the higher bound by ``high()``. The length may be -received by ``len()``. ``low()`` for a sequence or an open array always returns -0, as this is the first valid index. -One can append elements to a sequence with the ``add()`` proc or the ``&`` -operator, and remove (and get) the last element of a sequence with the -``pop()`` proc. - -The notation ``x[i]`` can be used to access the i-th element of ``x``. - -Arrays are always bounds checked (at compile-time or at runtime). These -checks can be disabled via pragmas or invoking the compiler with the -``--boundChecks:off`` command line switch. - - -Open arrays ------------ - -Often fixed size arrays turn out to be too inflexible; procedures should -be able to deal with arrays of different sizes. The `openarray`:idx: type -allows this; it can only be used for parameters. Openarrays are always -indexed with an ``int`` starting at position 0. The ``len``, ``low`` -and ``high`` operations are available for open arrays too. Any array with -a compatible base type can be passed to an openarray parameter, the index -type does not matter. In addition to arrays sequences can also be passed -to an open array parameter. - -The openarray type cannot be nested: multidimensional openarrays are not -supported because this is seldom needed and cannot be done efficiently. - - -Varargs -------- - -A ``varargs`` parameter is an openarray parameter that additionally -allows to pass a variable number of arguments to a procedure. The compiler -converts the list of arguments to an array implicitly: - -.. code-block:: nimrod - proc myWriteln(f: TFile, a: varargs[string]) = - for s in items(a): - write(f, s) - write(f, "\n") - - myWriteln(stdout, "abc", "def", "xyz") - # is transformed to: - myWriteln(stdout, ["abc", "def", "xyz"]) - -This transformation is only done if the varargs parameter is the -last parameter in the procedure header. It is also possible to perform -type conversions in this context: - -.. code-block:: nimrod - proc myWriteln(f: TFile, a: varargs[string, `$`]) = - for s in items(a): - write(f, s) - write(f, "\n") - - myWriteln(stdout, 123, "abc", 4.0) - # is transformed to: - myWriteln(stdout, [$123, $"def", $4.0]) - -In this example ``$`` is applied to any argument that is passed to the -parameter ``a``. (Note that ``$`` applied to strings is a nop.) - - - -Tuples and object types ------------------------ -A variable of a tuple or object type is a heterogeneous storage -container. -A tuple or object defines various named *fields* of a type. A tuple also -defines an *order* of the fields. Tuples are meant for heterogeneous storage -types with no overhead and few abstraction possibilities. The constructor ``()`` -can be used to construct tuples. The order of the fields in the constructor -must match the order of the tuple's definition. Different tuple-types are -*equivalent* if they specify the same fields of the same type in the same -order. The *names* of the fields also have to be identical but this might -change in a future version of the language. - -The assignment operator for tuples copies each component. -The default assignment operator for objects copies each component. Overloading -of the assignment operator for objects is not possible, but this will change -in future versions of the compiler. - -.. code-block:: nimrod - - type - TPerson = tuple[name: string, age: int] # type representing a person: - # a person consists of a name - # and an age - var - person: TPerson - person = (name: "Peter", age: 30) - # the same, but less readable: - person = ("Peter", 30) - -The implementation aligns the fields for best access performance. The alignment -is compatible with the way the C compiler does it. - -For consistency with ``object`` declarations, tuples in a ``type`` section -can also be defined with indentation instead of ``[]``: - -.. code-block:: nimrod - type - TPerson = tuple # type representing a person - name: string # a person consists of a name - age: natural # and an age - -Objects provide many features that tuples do not. Object provide inheritance -and information hiding. Objects have access to their type at runtime, so that -the ``of`` operator can be used to determine the object's type. - -.. code-block:: nimrod - type - TPerson {.inheritable.} = object - name*: string # the * means that `name` is accessible from other modules - age: int # no * means that the field is hidden - - TStudent = object of TPerson # a student is a person - id: int # with an id field - - var - student: TStudent - person: TPerson - assert(student of TStudent) # is true - -Object fields that should be visible from outside the defining module, have to -be marked by ``*``. In contrast to tuples, different object types are -never *equivalent*. Objects that have no ancestor are implicitly ``final`` -and thus have no hidden type field. One can use the ``inheritable`` pragma to -introduce new object roots apart from ``system.TObject``. - - -Object construction -------------------- - -Objects can also be created with an `object construction expression`:idx: that -has the syntax ``T(fieldA: valueA, fieldB: valueB, ...)`` where ``T`` is -an ``object`` type or a ``ref object`` type: - -.. code-block:: nimrod - var student = TStudent(name: "Anton", age: 5, id: 3) - -For a ``ref object`` type ``system.new`` is invoked implicitly. - - -Object variants ---------------- -Often an object hierarchy is overkill in certain situations where simple -variant types are needed. - -An example: - -.. code-block:: nimrod - - # This is an example how an abstract syntax tree could be modelled in Nimrod - type - TNodeKind = enum # the different node types - nkInt, # a leaf with an integer value - nkFloat, # a leaf with a float value - nkString, # a leaf with a string value - nkAdd, # an addition - nkSub, # a subtraction - nkIf # an if statement - PNode = ref TNode - TNode = object - case kind: TNodeKind # the ``kind`` field is the discriminator - of nkInt: intVal: int - of nkFloat: floatVal: float - of nkString: strVal: string - of nkAdd, nkSub: - leftOp, rightOp: PNode - of nkIf: - condition, thenPart, elsePart: PNode - - # create a new case object: - var n = PNode(kind: nkIf, condition: nil) - # accessing n.thenPart is valid because the ``nkIf`` branch is active: - n.thenPart = PNode(kind: nkFloat, floatVal: 2.0) - - # the following statement raises an `EInvalidField` exception, because - # n.kind's value does not fit and the ``nkString`` branch is not active: - n.strVal = "" - - # invalid: would change the active object branch: - n.kind = nkInt - - var x = PNode(kind: nkAdd, leftOp: PNode(kind: nkInt, intVal: 4), - rightOp: PNode(kind: nkInt, intVal: 2)) - # valid: does not change the active object branch: - x.kind = nkSub - -As can been seen from the example, an advantage to an object hierarchy is that -no casting between different object types is needed. Yet, access to invalid -object fields raises an exception. - -The syntax of ``case`` in an object declaration follows closely the syntax of -the ``case`` statement: The branches in a ``case`` section may be indented too. - -In the example the ``kind`` field is called the `discriminator`:idx:\: For -safety its address cannot be taken and assignments to it are restricted: The -new value must not lead to a change of the active object branch. For an object -branch switch ``system.reset`` has to be used. - - -Set type --------- - -.. include:: sets_fragment.txt - -Reference and pointer types ---------------------------- -References (similar to pointers in other programming languages) are a -way to introduce many-to-one relationships. This means different references can -point to and modify the same location in memory (also called `aliasing`:idx:). - -Nimrod distinguishes between `traced`:idx: and `untraced`:idx: references. -Untraced references are also called *pointers*. Traced references point to -objects of a garbage collected heap, untraced references point to -manually allocated objects or to objects somewhere else in memory. Thus -untraced references are *unsafe*. However for certain low-level operations -(accessing the hardware) untraced references are unavoidable. - -Traced references are declared with the **ref** keyword, untraced references -are declared with the **ptr** keyword. - -An empty subscript ``[]`` notation can be used to derefer a reference, -the ``addr`` procedure returns the address of an item. An address is always -an untraced reference. -Thus the usage of ``addr`` is an *unsafe* feature. - -The ``.`` (access a tuple/object field operator) -and ``[]`` (array/string/sequence index operator) operators perform implicit -dereferencing operations for reference types: - -.. code-block:: nimrod - - type - PNode = ref TNode - TNode = object - le, ri: PNode - data: int - - var - n: PNode - new(n) - n.data = 9 - # no need to write n[].data; in fact n[].data is highly discouraged! - -As a syntactical extension ``object`` types can be anonymous if -declared in a type section via the ``ref object`` or ``ptr object`` notations. -This feature is useful if an object should only gain reference semantics: - -.. code-block:: nimrod - - type - Node = ref object - le, ri: Node - data: int - - -To allocate a new traced object, the built-in procedure ``new`` has to be used. -To deal with untraced memory, the procedures ``alloc``, ``dealloc`` and -``realloc`` can be used. The documentation of the system module contains -further information. - -If a reference points to *nothing*, it has the value ``nil``. - -Special care has to be taken if an untraced object contains traced objects like -traced references, strings or sequences: in order to free everything properly, -the built-in procedure ``GCunref`` has to be called before freeing the untraced -memory manually: - -.. code-block:: nimrod - type - TData = tuple[x, y: int, s: string] - - # allocate memory for TData on the heap: - var d = cast[ptr TData](alloc0(sizeof(TData))) - - # create a new string on the garbage collected heap: - d.s = "abc" - - # tell the GC that the string is not needed anymore: - GCunref(d.s) - - # free the memory: - dealloc(d) - -Without the ``GCunref`` call the memory allocated for the ``d.s`` string would -never be freed. The example also demonstrates two important features for low -level programming: the ``sizeof`` proc returns the size of a type or value -in bytes. The ``cast`` operator can circumvent the type system: the compiler -is forced to treat the result of the ``alloc0`` call (which returns an untyped -pointer) as if it would have the type ``ptr TData``. Casting should only be -done if it is unavoidable: it breaks type safety and bugs can lead to -mysterious crashes. - -**Note**: The example only works because the memory is initialized to zero -(``alloc0`` instead of ``alloc`` does this): ``d.s`` is thus initialized to -``nil`` which the string assignment can handle. One needs to know low level -details like this when mixing garbage collected data with unmanaged memory. - -.. XXX finalizers for traced objects - - -Not nil annotation ------------------- - -All types for that ``nil`` is a valid value can be annotated to -exclude ``nil`` as a valid value with the ``not nil`` annotation: - -.. code-block:: nimrod - type - PObject = ref TObj not nil - TProc = (proc (x, y: int)) not nil - - proc p(x: PObject) = - echo "not nil" - - # compiler catches this: - p(nil) - - # and also this: - var x: PObject - p(x) - -The compiler ensures that every code path initializes variables which contain -not nilable pointers. The details of this analysis are still to be specified -here. - - -Memory regions --------------- - -The types ``ref`` and ``ptr`` can get an optional ``region`` annotation. -A region has to be an object type. - -Regions are very useful to separate user space and kernel memory in the -development of OS kernels: - -.. code-block:: nimrod - type - Kernel = object - Userspace = object - - var a: Kernel ptr Stat - var b: Userspace ptr Stat - - # the following does not compile as the pointer types are incompatible: - a = b - -As the example shows ``ptr`` can also be used as a binary -operator, ``region ptr T`` is a shortcut for ``ptr[region, T]``. - -In order to make generic code easier to write ``ptr T`` is a subtype -of ``ptr[R, T]`` for any ``R``. - -Furthermore the subtype relation of the region object types is lifted to -the pointer types: If ``A <: B`` then ``ptr[A, T] <: ptr[B, T]``. This can be -used to model subregions of memory. As a special typing rule ``ptr[R, T]`` is -not compatible to ``pointer`` to prevent the following from compiling: - -.. code-block:: nimrod - # from system - proc dealloc(p: pointer) - - # wrap some scripting language - type - PythonsHeap = object - PyObjectHeader = object - rc: int - typ: pointer - PyObject = ptr[PythonsHeap, PyObjectHeader] - - proc createPyObject(): PyObject {.importc: "...".} - proc destroyPyObject(x: PyObject) {.importc: "...".} - - var foo = createPyObject() - # type error here, how convenient: - dealloc(foo) - - -Future directions: - -* Memory regions might become available for ``string`` and ``seq`` too. -* Builtin regions like ``private``, ``global`` and ``local`` will - prove very useful for the upcoming OpenCL target. -* Builtin "regions" can model ``lent`` and ``unique`` pointers. - - - -Procedural type ---------------- -A procedural type is internally a pointer to a procedure. ``nil`` is -an allowed value for variables of a procedural type. Nimrod uses procedural -types to achieve `functional`:idx: programming techniques. - -Examples: - -.. code-block:: nimrod - - proc printItem(x: int) = ... - - proc forEach(c: proc (x: int) {.cdecl.}) = - ... - - forEach(printItem) # this will NOT compile because calling conventions differ - - -.. code-block:: nimrod - - type - TOnMouseMove = proc (x, y: int) {.closure.} - - proc onMouseMove(mouseX, mouseY: int) = - # has default calling convention - echo "x: ", mouseX, " y: ", mouseY - - proc setOnMouseMove(mouseMoveEvent: TOnMouseMove) = discard - - # ok, 'onMouseMove' has the default calling convention, which is compatible - # to 'closure': - setOnMouseMove(onMouseMove) - - -A subtle issue with procedural types is that the calling convention of the -procedure influences the type compatibility: procedural types are only -compatible if they have the same calling convention. As a special extension, -a procedure of the calling convention ``nimcall`` can be passed to a parameter -that expects a proc of the calling convention ``closure``. - -Nimrod supports these `calling conventions`:idx:\: - -`nimcall`:idx: - is the default convention used for a Nimrod **proc**. It is the - same as ``fastcall``, but only for C compilers that support ``fastcall``. - -`closure`:idx: - is the default calling convention for a **procedural type** that lacks - any pragma annotations. It indicates that the procedure has a hidden - implicit parameter (an *environment*). Proc vars that have the calling - convention ``closure`` take up two machine words: One for the proc pointer - and another one for the pointer to implicitly passed environment. - -`stdcall`:idx: - This the stdcall convention as specified by Microsoft. The generated C - procedure is declared with the ``__stdcall`` keyword. - -`cdecl`:idx: - The cdecl convention means that a procedure shall use the same convention - as the C compiler. Under windows the generated C procedure is declared with - the ``__cdecl`` keyword. - -`safecall`:idx: - This is the safecall convention as specified by Microsoft. The generated C - procedure is declared with the ``__safecall`` keyword. The word *safe* - refers to the fact that all hardware registers shall be pushed to the - hardware stack. - -`inline`:idx: - The inline convention means the the caller should not call the procedure, - but inline its code directly. Note that Nimrod does not inline, but leaves - this to the C compiler; it generates ``__inline`` procedures. This is - only a hint for the compiler: it may completely ignore it and - it may inline procedures that are not marked as ``inline``. - -`fastcall`:idx: - Fastcall means different things to different C compilers. One gets whatever - the C ``__fastcall`` means. - -`syscall`:idx: - The syscall convention is the same as ``__syscall`` in C. It is used for - interrupts. - -`noconv`:idx: - The generated C code will not have any explicit calling convention and thus - use the C compiler's default calling convention. This is needed because - Nimrod's default calling convention for procedures is ``fastcall`` to - improve speed. - -Most calling conventions exist only for the Windows 32-bit platform. - -Assigning/passing a procedure to a procedural variable is only allowed if one -of the following conditions hold: -1) The procedure that is accessed resides in the current module. -2) The procedure is marked with the ``procvar`` pragma (see `procvar pragma`_). -3) The procedure has a calling convention that differs from ``nimcall``. -4) The procedure is anonymous. - -The rules' purpose is to prevent the case that extending a non-``procvar`` -procedure with default parameters breaks client code. - -The default calling convention is ``nimcall``, unless it is an inner proc (a -proc inside of a proc). For an inner proc an analysis is performed whether it -accesses its environment. If it does so, it has the calling convention -``closure``, otherwise it has the calling convention ``nimcall``. - - -Distinct type -------------- - -A ``distinct`` type is new type derived from a `base type`:idx: that is -incompatible with its base type. In particular, it is an essential property -of a distinct type that it **does not** imply a subtype relation between it -and its base type. Explicit type conversions from a distinct type to its -base type and vice versa are allowed. - - -Modelling currencies -~~~~~~~~~~~~~~~~~~~~ - -A distinct type can be used to model different physical `units`:idx: with a -numerical base type, for example. The following example models currencies. - -Different currencies should not be mixed in monetary calculations. Distinct -types are a perfect tool to model different currencies: - -.. code-block:: nimrod - type - TDollar = distinct int - TEuro = distinct int - - var - d: TDollar - e: TEuro - - echo d + 12 - # Error: cannot add a number with no unit and a ``TDollar`` - -Unfortunately, ``d + 12.TDollar`` is not allowed either, -because ``+`` is defined for ``int`` (among others), not for ``TDollar``. So -a ``+`` for dollars needs to be defined: - -.. code-block:: - proc `+` (x, y: TDollar): TDollar = - result = TDollar(int(x) + int(y)) - -It does not make sense to multiply a dollar with a dollar, but with a -number without unit; and the same holds for division: - -.. code-block:: - proc `*` (x: TDollar, y: int): TDollar = - result = TDollar(int(x) * y) - - proc `*` (x: int, y: TDollar): TDollar = - result = TDollar(x * int(y)) - - proc `div` ... - -This quickly gets tedious. The implementations are trivial and the compiler -should not generate all this code only to optimize it away later - after all -``+`` for dollars should produce the same binary code as ``+`` for ints. -The pragma `borrow`:idx: has been designed to solve this problem; in principle -it generates the above trivial implementations: - -.. code-block:: nimrod - proc `*` (x: TDollar, y: int): TDollar {.borrow.} - proc `*` (x: int, y: TDollar): TDollar {.borrow.} - proc `div` (x: TDollar, y: int): TDollar {.borrow.} - -The ``borrow`` pragma makes the compiler use the same implementation as -the proc that deals with the distinct type's base type, so no code is -generated. - -But it seems all this boilerplate code needs to be repeated for the ``TEuro`` -currency. This can be solved with templates_. - -.. code-block:: nimrod - template additive(typ: typedesc): stmt = - proc `+` *(x, y: typ): typ {.borrow.} - proc `-` *(x, y: typ): typ {.borrow.} - - # unary operators: - proc `+` *(x: typ): typ {.borrow.} - proc `-` *(x: typ): typ {.borrow.} - - template multiplicative(typ, base: typedesc): stmt = - proc `*` *(x: typ, y: base): typ {.borrow.} - proc `*` *(x: base, y: typ): typ {.borrow.} - proc `div` *(x: typ, y: base): typ {.borrow.} - proc `mod` *(x: typ, y: base): typ {.borrow.} - - template comparable(typ: typedesc): stmt = - proc `<` * (x, y: typ): bool {.borrow.} - proc `<=` * (x, y: typ): bool {.borrow.} - proc `==` * (x, y: typ): bool {.borrow.} - - template defineCurrency(typ, base: expr): stmt = - type - typ* = distinct base - additive(typ) - multiplicative(typ, base) - comparable(typ) - - defineCurrency(TDollar, int) - defineCurrency(TEuro, int) - - -The borrow pragma can also be used to annotate the distinct type to allow -certain builtin operations to be lifted: - -.. code-block:: nimrod - type - Foo = object - a, b: int - s: string - - Bar {.borrow: `.`.} = distinct Foo - - var bb: ref Bar - new bb - # field access now valid - bb.a = 90 - bb.s = "abc" - -Currently only the dot accessor can be borrowed in this way. - - -Avoiding SQL injection attacks -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An SQL statement that is passed from Nimrod to an SQL database might be -modelled as a string. However, using string templates and filling in the -values is vulnerable to the famous `SQL injection attack`:idx:\: - -.. code-block:: nimrod - import strutils - - proc query(db: TDbHandle, statement: string) = ... - - var - username: string - - db.query("SELECT FROM users WHERE name = '$1'" % username) - # Horrible security hole, but the compiler does not mind! - -This can be avoided by distinguishing strings that contain SQL from strings -that don't. Distinct types provide a means to introduce a new string type -``TSQL`` that is incompatible with ``string``: - -.. code-block:: nimrod - type - TSQL = distinct string - - proc query(db: TDbHandle, statement: TSQL) = ... - - var - username: string - - db.query("SELECT FROM users WHERE name = '$1'" % username) - # Error at compile time: `query` expects an SQL string! - - -It is an essential property of abstract types that they **do not** imply a -subtype relation between the abtract type and its base type. Explict type -conversions from ``string`` to ``TSQL`` are allowed: - -.. code-block:: nimrod - import strutils, sequtils - - proc properQuote(s: string): TSQL = - # quotes a string properly for an SQL statement - return TSQL(s) - - proc `%` (frmt: TSQL, values: openarray[string]): TSQL = - # quote each argument: - let v = values.mapIt(TSQL, properQuote(it)) - # we need a temporary type for the type conversion :-( - type TStrSeq = seq[string] - # call strutils.`%`: - result = TSQL(string(frmt) % TStrSeq(v)) - - db.query("SELECT FROM users WHERE name = '$1'".TSQL % [username]) - -Now we have compile-time checking against SQL injection attacks. Since -``"".TSQL`` is transformed to ``TSQL("")`` no new syntax is needed for nice -looking ``TSQL`` string literals. The hypothetical ``TSQL`` type actually -exists in the library as the `TSqlQuery type `_ of -modules like `db_sqlite `_. - - -Void type ---------- - -The ``void`` type denotes the absense of any type. Parameters of -type ``void`` are treated as non-existent, ``void`` as a return type means that -the procedure does not return a value: - -.. code-block:: nimrod - proc nothing(x, y: void): void = - echo "ha" - - nothing() # writes "ha" to stdout - -The ``void`` type is particularly useful for generic code: - -.. code-block:: nimrod - proc callProc[T](p: proc (x: T), x: T) = - when T is void: - p() - else: - p(x) - - proc intProc(x: int) = discard - proc emptyProc() = discard - - callProc[int](intProc, 12) - callProc[void](emptyProc) - -However, a ``void`` type cannot be inferred in generic code: - -.. code-block:: nimrod - callProc(emptyProc) - # Error: type mismatch: got (proc ()) - # but expected one of: - # callProc(p: proc (T), x: T) - -The ``void`` type is only valid for parameters and return types; other symbols -cannot have the type ``void``. - - -Type relations -============== - -The following section defines several relations on types that are needed to -describe the type checking done by the compiler. - - -Type equality -------------- -Nimrod uses structural type equivalence for most types. Only for objects, -enumerations and distinct types name equivalence is used. The following -algorithm (in pseudo-code) determines type equality: - -.. code-block:: nimrod - proc typeEqualsAux(a, b: PType, - s: var set[PType * PType]): bool = - if (a,b) in s: return true - incl(s, (a,b)) - if a.kind == b.kind: - case a.kind - of int, intXX, float, floatXX, char, string, cstring, pointer, - bool, nil, void: - # leaf type: kinds identical; nothing more to check - result = true - of ref, ptr, var, set, seq, openarray: - result = typeEqualsAux(a.baseType, b.baseType, s) - of range: - result = typeEqualsAux(a.baseType, b.baseType, s) and - (a.rangeA == b.rangeA) and (a.rangeB == b.rangeB) - of array: - result = typeEqualsAux(a.baseType, b.baseType, s) and - typeEqualsAux(a.indexType, b.indexType, s) - of tuple: - if a.tupleLen == b.tupleLen: - for i in 0..a.tupleLen-1: - if not typeEqualsAux(a[i], b[i], s): return false - result = true - of object, enum, distinct: - result = a == b - of proc: - result = typeEqualsAux(a.parameterTuple, b.parameterTuple, s) and - typeEqualsAux(a.resultType, b.resultType, s) and - a.callingConvention == b.callingConvention - - proc typeEquals(a, b: PType): bool = - var s: set[PType * PType] = {} - result = typeEqualsAux(a, b, s) - -Since types are graphs which can have cycles, the above algorithm needs an -auxiliary set ``s`` to detect this case. - - -Type equality modulo type distinction -------------------------------------- - -The following algorithm (in pseudo-code) determines whether two types -are equal with no respect to ``distinct`` types. For brevity the cycle check -with an auxiliary set ``s`` is omitted: - -.. code-block:: nimrod - proc typeEqualsOrDistinct(a, b: PType): bool = - if a.kind == b.kind: - case a.kind - of int, intXX, float, floatXX, char, string, cstring, pointer, - bool, nil, void: - # leaf type: kinds identical; nothing more to check - result = true - of ref, ptr, var, set, seq, openarray: - result = typeEqualsOrDistinct(a.baseType, b.baseType) - of range: - result = typeEqualsOrDistinct(a.baseType, b.baseType) and - (a.rangeA == b.rangeA) and (a.rangeB == b.rangeB) - of array: - result = typeEqualsOrDistinct(a.baseType, b.baseType) and - typeEqualsOrDistinct(a.indexType, b.indexType) - of tuple: - if a.tupleLen == b.tupleLen: - for i in 0..a.tupleLen-1: - if not typeEqualsOrDistinct(a[i], b[i]): return false - result = true - of distinct: - result = typeEqualsOrDistinct(a.baseType, b.baseType) - of object, enum: - result = a == b - of proc: - result = typeEqualsOrDistinct(a.parameterTuple, b.parameterTuple) and - typeEqualsOrDistinct(a.resultType, b.resultType) and - a.callingConvention == b.callingConvention - elif a.kind == distinct: - result = typeEqualsOrDistinct(a.baseType, b) - elif b.kind == distinct: - result = typeEqualsOrDistinct(a, b.baseType) - - -Subtype relation ----------------- -If object ``a`` inherits from ``b``, ``a`` is a subtype of ``b``. This subtype -relation is extended to the types ``var``, ``ref``, ``ptr``: - -.. code-block:: nimrod - proc isSubtype(a, b: PType): bool = - if a.kind == b.kind: - case a.kind - of object: - var aa = a.baseType - while aa != nil and aa != b: aa = aa.baseType - result = aa == b - of var, ref, ptr: - result = isSubtype(a.baseType, b.baseType) - -.. XXX nil is a special value! - - -Convertible relation --------------------- -A type ``a`` is **implicitly** convertible to type ``b`` iff the following -algorithm returns true: - -.. code-block:: nimrod - # XXX range types? - proc isImplicitlyConvertible(a, b: PType): bool = - case a.kind - of int: result = b in {int8, int16, int32, int64, uint, uint8, uint16, - uint32, uint64, float, float32, float64} - of int8: result = b in {int16, int32, int64, int} - of int16: result = b in {int32, int64, int} - of int32: result = b in {int64, int} - of uint: result = b in {uint32, uint64} - of uint8: result = b in {uint16, uint32, uint64} - of uint16: result = b in {uint32, uint64} - of uint32: result = b in {uint64} - of float: result = b in {float32, float64} - of float32: result = b in {float64, float} - of float64: result = b in {float32, float} - of seq: - result = b == openArray and typeEquals(a.baseType, b.baseType) - of array: - result = b == openArray and typeEquals(a.baseType, b.baseType) - if a.baseType == char and a.indexType.rangeA == 0: - result = b = cstring - of cstring, ptr: - result = b == pointer - of string: - result = b == cstring - -A type ``a`` is **explicitly** convertible to type ``b`` iff the following -algorithm returns true: - -.. code-block:: nimrod - proc isIntegralType(t: PType): bool = - result = isOrdinal(t) or t.kind in {float, float32, float64} - - proc isExplicitlyConvertible(a, b: PType): bool = - result = false - if isImplicitlyConvertible(a, b): return true - if typeEqualsOrDistinct(a, b): return true - if isIntegralType(a) and isIntegralType(b): return true - if isSubtype(a, b) or isSubtype(b, a): return true - -The convertible relation can be relaxed by a user-defined type -`converter`:idx:. - -.. code-block:: nimrod - converter toInt(x: char): int = result = ord(x) - - var - x: int - chr: char = 'a' - - # implicit conversion magic happens here - x = chr - echo x # => 97 - # you can use the explicit form too - x = chr.toInt - echo x # => 97 - -The type conversion ``T(a)`` is an L-value if ``a`` is an L-value and -``typeEqualsOrDistinct(T, type(a))`` holds. - - -Assignment compatibility ------------------------- - -An expression ``b`` can be assigned to an expression ``a`` iff ``a`` is an -`l-value` and ``isImplicitlyConvertible(b.typ, a.typ)`` holds. - - -Overloading resolution ----------------------- - -To be written. - - -Statements and expressions -========================== - -Nimrod uses the common statement/expression paradigm: Statements do not -produce a value in contrast to expressions. However, some expressions are -statements. - -Statements are separated into `simple statements`:idx: and -`complex statements`:idx:. -Simple statements are statements that cannot contain other statements like -assignments, calls or the ``return`` statement; complex statements can -contain other statements. To avoid the `dangling else problem`:idx:, complex -statements always have to be intended. The details can be found in the grammar. - - -Statement list expression -------------------------- - -Statements can also occur in an expression context that looks -like ``(stmt1; stmt2; ...; ex)``. This is called -an statement list expression or ``(;)``. The type -of ``(stmt1; stmt2; ...; ex)`` is the type of ``ex``. All the other statements -must be of type ``void``. (One can use ``discard`` to produce a ``void`` type.) -``(;)`` does not introduce a new scope. - - -Discard statement ------------------ - -Example: - -.. code-block:: nimrod - proc p(x, y: int): int = - result = x + y - - discard p(3, 4) # discard the return value of `p` - -The ``discard`` statement evaluates its expression for side-effects and -throws the expression's resulting value away. - -Ignoring the return value of a procedure without using a discard statement is -a static error. - -The return value can be ignored implicitly if the called proc/iterator has -been declared with the `discardable`:idx: pragma: - -.. code-block:: nimrod - proc p(x, y: int): int {.discardable.} = - result = x + y - - p(3, 4) # now valid - -An empty ``discard`` statement is often used as a null statement: - -.. code-block:: nimrod - proc classify(s: string) = - case s[0] - of SymChars, '_': echo "an identifier" - of '0'..'9': echo "a number" - else: discard - - -Var statement -------------- - -Var statements declare new local and global variables and -initialize them. A comma separated list of variables can be used to specify -variables of the same type: - -.. code-block:: nimrod - - var - a: int = 0 - x, y, z: int - -If an initializer is given the type can be omitted: the variable is then of the -same type as the initializing expression. Variables are always initialized -with a default value if there is no initializing expression. The default -value depends on the type and is always a zero in binary. - -============================ ============================================== -Type default value -============================ ============================================== -any integer type 0 -any float 0.0 -char '\\0' -bool false -ref or pointer type nil -procedural type nil -sequence nil (*not* ``@[]``) -string nil (*not* "") -tuple[x: A, y: B, ...] (default(A), default(B), ...) - (analogous for objects) -array[0..., T] [default(T), ...] -range[T] default(T); this may be out of the valid range -T = enum cast[T](0); this may be an invalid value -============================ ============================================== - - -The implicit initialization can be avoided for optimization reasons with the -`noinit`:idx: pragma: - -.. code-block:: nimrod - var - a {.noInit.}: array [0..1023, char] - -If a proc is annotated with the ``noinit`` pragma this refers to its implicit -``result`` variable: - -.. code-block:: nimrod - proc returnUndefinedValue: int {.noinit.} = discard - - -The implicit initialization can be also prevented by the `requiresInit`:idx: -type pragma. The compiler requires an explicit initialization then. However -it does a `control flow analysis`:idx: to prove the variable has been -initialized and does not rely on syntactic properties: - -.. code-block:: nimrod - type - TMyObject = object {.requiresInit.} - - proc p() = - # the following is valid: - var x: TMyObject - if someCondition(): - x = a() - else: - x = a() - use x - -let statement -------------- - -A ``let`` statement declares new local and global `single assignment`:idx: -variables and binds a value to them. The syntax is the of the ``var`` -statement, except that the keyword ``var`` is replaced by the keyword ``let``. -Let variables are not l-values and can thus not be passed to ``var`` parameters -nor can their address be taken. They cannot be assigned new values. - -For let variables the same pragmas are available as for ordinary variables. - - -Const section -------------- - -`Constants`:idx: are symbols which are bound to a value. The constant's value -cannot change. The compiler must be able to evaluate the expression in a -constant declaration at compile time. - -Nimrod contains a sophisticated compile-time evaluator, so procedures which -have no side-effect can be used in constant expressions too: - -.. code-block:: nimrod - import strutils - const - constEval = contains("abc", 'b') # computed at compile time! - - -The rules for compile-time computability are: - -1. Literals are compile-time computable. -2. Type conversions are compile-time computable. -3. Procedure calls of the form ``p(X)`` are compile-time computable if - ``p`` is a proc without side-effects (see the `noSideEffect pragma`_ - for details) and if ``X`` is a (possibly empty) list of compile-time - computable arguments. - - -Constants cannot be of type ``ptr``, ``ref``, ``var`` or ``object``, nor can -they contain such a type. - - -Static statement/expression ---------------------------- - -A static statement/expression can be used to enforce compile -time evaluation explicitly. Enforced compile time evaluation can even evaluate -code that has side effects: - -.. code-block:: - - static: - echo "echo at compile time" - -It's a static error if the compiler cannot perform the evaluation at compile -time. - -The current implementation poses some restrictions for compile time -evaluation: Code which contains ``cast`` or makes use of the foreign function -interface cannot be evaluated at compile time. Later versions of Nimrod will -support the FFI at compile time. - - -If statement ------------- - -Example: - -.. code-block:: nimrod - - var name = readLine(stdin) - - if name == "Andreas": - echo("What a nice name!") - elif name == "": - echo("Don't you have a name?") - else: - echo("Boring name...") - -The ``if`` statement is a simple way to make a branch in the control flow: -The expression after the keyword ``if`` is evaluated, if it is true -the corresponding statements after the ``:`` are executed. Otherwise -the expression after the ``elif`` is evaluated (if there is an -``elif`` branch), if it is true the corresponding statements after -the ``:`` are executed. This goes on until the last ``elif``. If all -conditions fail, the ``else`` part is executed. If there is no ``else`` -part, execution continues with the statement after the ``if`` statement. - -The scoping for an ``if`` statement is slightly subtle to support an important -use case. A new scope starts for the ``if``/``elif`` condition and ends after -the corresponding *then* block: - -.. code-block:: nimrod - if {| (let m = input =~ re"(\w+)=\w+"; m.isMatch): - echo "key ", m[0], " value ", m[1] |} - elif {| (let m = input =~ re""; m.isMatch): - echo "new m in this scope" |} - else: - # 'm' not declared here - -In the example the scopes have been enclosed in ``{| |}``. - - -Case statement --------------- - -Example: - -.. code-block:: nimrod - - case readline(stdin) - of "delete-everything", "restart-computer": - echo("permission denied") - of "go-for-a-walk": echo("please yourself") - else: echo("unknown command") - - # indentation of the branches is also allowed; and so is an optional colon - # after the selecting expression: - case readline(stdin): - of "delete-everything", "restart-computer": - echo("permission denied") - of "go-for-a-walk": echo("please yourself") - else: echo("unknown command") - - -The ``case`` statement is similar to the if statement, but it represents -a multi-branch selection. The expression after the keyword ``case`` is -evaluated and if its value is in a *slicelist* the corresponding statements -(after the ``of`` keyword) are executed. If the value is not in any -given *slicelist* the ``else`` part is executed. If there is no ``else`` -part and not all possible values that ``expr`` can hold occur in a -``slicelist``, a static error occurs. This holds only for expressions of -ordinal types. "All possible values" of ``expr`` are determined by ``expr``'s -type. - -If the expression is not of an ordinal type, and no ``else`` part is -given, control passes after the ``case`` statement. - -To suppress the static error in the ordinal case an ``else`` part with an -empty ``discard`` statement can be used. - -As a special semantic extension, an expression in an ``of`` branch of a case -statement may evaluate to a set or array constructor; the set or array is then -expanded into a list of its elements: - -.. code-block:: nimrod - const - SymChars: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'} - - proc classify(s: string) = - case s[0] - of SymChars, '_': echo "an identifier" - of '0'..'9': echo "a number" - else: echo "other" - - # is equivalent to: - proc classify(s: string) = - case s[0] - of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '_': echo "an identifier" - of '0'..'9': echo "a number" - else: echo "other" - - -When statement --------------- - -Example: - -.. code-block:: nimrod - - when sizeof(int) == 2: - echo("running on a 16 bit system!") - elif sizeof(int) == 4: - echo("running on a 32 bit system!") - elif sizeof(int) == 8: - echo("running on a 64 bit system!") - else: - echo("cannot happen!") - -The ``when`` statement is almost identical to the ``if`` statement with some -exceptions: - -* Each condition (``expr``) has to be a constant expression (of type ``bool``). -* The statements do not open a new scope. -* The statements that belong to the expression that evaluated to true are - translated by the compiler, the other statements are not checked for - semantics! However, each condition is checked for semantics. - -The ``when`` statement enables conditional compilation techniques. As -a special syntactic extension, the ``when`` construct is also available -within ``object`` definitions. - - -Return statement ----------------- - -Example: - -.. code-block:: nimrod - return 40+2 - -The ``return`` statement ends the execution of the current procedure. -It is only allowed in procedures. If there is an ``expr``, this is syntactic -sugar for: - -.. code-block:: nimrod - result = expr - return result - - -``return`` without an expression is a short notation for ``return result`` if -the proc has a return type. The `result`:idx: variable is always the return -value of the procedure. It is automatically declared by the compiler. As all -variables, ``result`` is initialized to (binary) zero: - -.. code-block:: nimrod - proc returnZero(): int = - # implicitly returns 0 - - -Yield statement ---------------- - -Example: - -.. code-block:: nimrod - yield (1, 2, 3) - -The ``yield`` statement is used instead of the ``return`` statement in -iterators. It is only valid in iterators. Execution is returned to the body -of the for loop that called the iterator. Yield does not end the iteration -process, but execution is passed back to the iterator if the next iteration -starts. See the section about iterators (`Iterators and the for statement`_) -for further information. - - -Block statement ---------------- - -Example: - -.. code-block:: nimrod - var found = false - block myblock: - for i in 0..3: - for j in 0..3: - if a[j][i] == 7: - found = true - break myblock # leave the block, in this case both for-loops - echo(found) - -The block statement is a means to group statements to a (named) ``block``. -Inside the block, the ``break`` statement is allowed to leave the block -immediately. A ``break`` statement can contain a name of a surrounding -block to specify which block is to leave. - - -Break statement ---------------- - -Example: - -.. code-block:: nimrod - break - -The ``break`` statement is used to leave a block immediately. If ``symbol`` -is given, it is the name of the enclosing block that is to leave. If it is -absent, the innermost block is left. - - -While statement ---------------- - -Example: - -.. code-block:: nimrod - echo("Please tell me your password: \n") - var pw = readLine(stdin) - while pw != "12345": - echo("Wrong password! Next try: \n") - pw = readLine(stdin) - - -The ``while`` statement is executed until the ``expr`` evaluates to false. -Endless loops are no error. ``while`` statements open an `implicit block`, -so that they can be left with a ``break`` statement. - - -Continue statement ------------------- - -A ``continue`` statement leads to the immediate next iteration of the -surrounding loop construct. It is only allowed within a loop. A continue -statement is syntactic sugar for a nested block: - -.. code-block:: nimrod - while expr1: - stmt1 - continue - stmt2 - -Is equivalent to: - -.. code-block:: nimrod - while expr1: - block myBlockName: - stmt1 - break myBlockName - stmt2 - - -Assembler statement -------------------- - -The direct embedding of assembler code into Nimrod code is supported -by the unsafe ``asm`` statement. Identifiers in the assembler code that refer to -Nimrod identifiers shall be enclosed in a special character which can be -specified in the statement's pragmas. The default special character is ``'`'``: - -.. code-block:: nimrod - {.push stackTrace:off.} - proc addInt(a, b: int): int = - # a in eax, and b in edx - asm """ - mov eax, `a` - add eax, `b` - jno theEnd - call `raiseOverflow` - theEnd: - """ - {.pop.} - -If the GNU assembler is used, quotes and newlines are inserted automatically: - -.. code-block:: nimrod - proc addInt(a, b: int): int = - asm """ - addl %%ecx, %%eax - jno 1 - call `raiseOverflow` - 1: - :"=a"(`result`) - :"a"(`a`), "c"(`b`) - """ - -Instead of: - -.. code-block:: nimrod - proc addInt(a, b: int): int = - asm """ - "addl %%ecx, %%eax\n" - "jno 1\n" - "call `raiseOverflow`\n" - "1: \n" - :"=a"(`result`) - :"a"(`a`), "c"(`b`) - """ - -Using statement ---------------- - -**Warning**: The ``using`` statement is highly experimental! - -The using statement provides syntactic convenience for procs that -heavily use a single contextual parameter. When applied to a variable or a -constant, it will instruct Nimrod to automatically consider the used symbol as -a hidden leading parameter for any procedure calls, following the using -statement in the current scope. Thus, it behaves much like the hidden `this` -parameter available in some object-oriented programming languages. - -.. code-block:: nimrod - - var s = socket() - using s - - connect(host, port) - send(data) - - while true: - let line = readLine(timeout) - ... - - -When applied to a callable symbol, it brings the designated symbol in the -current scope. Thus, it can be used to disambiguate between imported symbols -from different modules having the same name. - -.. code-block:: nimrod - import windows, sdl - using sdl.SetTimer - -Note that ``using`` only *adds* to the current context, it doesn't remove or -replace, **neither** does it create a new scope. What this means is that if one -applies this to multiple variables the compiler will find conflicts in what -variable to use: - -.. code-block:: nimrod - var a, b = "kill it" - using a - add(" with fire") - using b - add(" with water") - echo a - echo b - -When the compiler reaches the second ``add`` call, both ``a`` and ``b`` could -be used with the proc, so one gets ``Error: expression '(a|b)' has no type (or -is ambiguous)``. To solve this one would need to nest ``using`` with a -``block`` statement so as to control the reach of the ``using`` statement. - -If expression -------------- - -An `if expression` is almost like an if statement, but it is an expression. -Example: - -.. code-block:: nimrod - var y = if x > 8: 9 else: 10 - -An if expression always results in a value, so the ``else`` part is -required. ``Elif`` parts are also allowed. - -When expression ---------------- - -Just like an `if expression`, but corresponding to the when statement. - -Case expression ---------------- - -The `case expression` is again very similar to the case statement: - -.. code-block:: nimrod - var favoriteFood = case animal - of "dog": "bones" - of "cat": "mice" - elif animal.endsWith"whale": "plankton" - else: - echo "I'm not sure what to serve, but everybody loves ice cream" - "ice cream" - -As seen in the above example, the case expression can also introduce side -effects. When multiple statements are given for a branch, Nimrod will use -the last expression as the result value, much like in an `expr` template. - -Table constructor ------------------ - -A table constructor is syntactic sugar for an array constructor: - -.. code-block:: nimrod - {"key1": "value1", "key2", "key3": "value2"} - - # is the same as: - [("key1", "value1"), ("key2", "value2"), ("key3", "value2")] - - -The empty table can be written ``{:}`` (in contrast to the empty set -which is ``{}``) which is thus another way to write as the empty array -constructor ``[]``. This slightly unusal way of supporting tables -has lots of advantages: - -* The order of the (key,value)-pairs is preserved, thus it is easy to - support ordered dicts with for example ``{key: val}.newOrderedTable``. -* A table literal can be put into a ``const`` section and the compiler - can easily put it into the executable's data section just like it can - for arrays and the generated data section requires a minimal amount - of memory. -* Every table implementation is treated equal syntactically. -* Apart from the minimal syntactic sugar the language core does not need to - know about tables. - - -Type conversions ----------------- -Syntactically a `type conversion` is like a procedure call, but a -type name replaces the procedure name. A type conversion is always -safe in the sense that a failure to convert a type to another -results in an exception (if it cannot be determined statically). - - -Type casts ----------- -Example: - -.. code-block:: nimrod - cast[int](x) - -Type casts are a crude mechanism to interpret the bit pattern of -an expression as if it would be of another type. Type casts are -only needed for low-level programming and are inherently unsafe. - - -The addr operator ------------------ -The ``addr`` operator returns the address of an l-value. If the type of the -location is ``T``, the `addr` operator result is of the type ``ptr T``. An -address is always an untraced reference. Taking the address of an object that -resides on the stack is **unsafe**, as the pointer may live longer than the -object on the stack and can thus reference a non-existing object. One can get -the address of variables, but one can't use it on variables declared through -``let`` statements: - -.. code-block:: nimrod - - let t1 = "Hello" - var - t2 = t1 - t3 : pointer = addr(t2) - echo repr(addr(t2)) - # --> ref 0x7fff6b71b670 --> 0x10bb81050"Hello" - echo cast[ptr string](t3)[] - # --> Hello - # The following line doesn't compile: - echo repr(addr(t1)) - # Error: expression has no address - - -Procedures -========== - -What most programming languages call `methods`:idx: or `functions`:idx: are -called `procedures`:idx: in Nimrod (which is the correct terminology). A -procedure declaration defines an identifier and associates it with a block -of code. -A procedure may call itself recursively. A parameter may be given a default -value that is used if the caller does not provide a value for this parameter. - -If the proc declaration has no body, it is a `forward`:idx: declaration. If -the proc returns a value, the procedure body can access an implicitly declared -variable named `result`:idx: that represents the return value. Procs can be -overloaded. The overloading resolution algorithm tries to find the proc that is -the best match for the arguments. Example: - -.. code-block:: nimrod - - proc toLower(c: Char): Char = # toLower for characters - if c in {'A'..'Z'}: - result = chr(ord(c) + (ord('a') - ord('A'))) - else: - result = c - - proc toLower(s: string): string = # toLower for strings - result = newString(len(s)) - for i in 0..len(s) - 1: - result[i] = toLower(s[i]) # calls toLower for characters; no recursion! - -Calling a procedure can be done in many different ways: - -.. code-block:: nimrod - proc callme(x, y: int, s: string = "", c: char, b: bool = false) = ... - - # call with positional arguments # parameter bindings: - callme(0, 1, "abc", '\t', true) # (x=0, y=1, s="abc", c='\t', b=true) - # call with named and positional arguments: - callme(y=1, x=0, "abd", '\t') # (x=0, y=1, s="abd", c='\t', b=false) - # call with named arguments (order is not relevant): - callme(c='\t', y=1, x=0) # (x=0, y=1, s="", c='\t', b=false) - # call as a command statement: no () needed: - callme 0, 1, "abc", '\t' - - -A procedure cannot modify its parameters (unless the parameters have the type -`var`). - -`Operators`:idx: are procedures with a special operator symbol as identifier: - -.. code-block:: nimrod - proc `$` (x: int): string = - # converts an integer to a string; this is a prefix operator. - result = intToStr(x) - -Operators with one parameter are prefix operators, operators with two -parameters are infix operators. (However, the parser distinguishes these from -the operator's position within an expression.) There is no way to declare -postfix operators: all postfix operators are built-in and handled by the -grammar explicitly. - -Any operator can be called like an ordinary proc with the '`opr`' -notation. (Thus an operator can have more than two parameters): - -.. code-block:: nimrod - proc `*+` (a, b, c: int): int = - # Multiply and add - result = a * b + c - - assert `*+`(3, 4, 6) == `*`(a, `+`(b, c)) - - -Method call syntax ------------------- - -For object oriented programming, the syntax ``obj.method(args)`` can be used -instead of ``method(obj, args)``. The parentheses can be omitted if there are no -remaining arguments: ``obj.len`` (instead of ``len(obj)``). - -This method call syntax is not restricted to objects, it can be used -to supply any type of first argument for procedures: - -.. code-block:: nimrod - - echo("abc".len) # is the same as echo(len("abc")) - echo("abc".toUpper()) - echo({'a', 'b', 'c'}.card) - stdout.writeln("Hallo") # the same as writeln(stdout, "Hallo") - -Another way to look at the method call syntax is that it provides the missing -postfix notation. - - -Properties ----------- -Nimrod has no need for *get-properties*: Ordinary get-procedures that are called -with the *method call syntax* achieve the same. But setting a value is -different; for this a special setter syntax is needed: - -.. code-block:: nimrod - - type - TSocket* = object of TObject - FHost: int # cannot be accessed from the outside of the module - # the `F` prefix is a convention to avoid clashes since - # the accessors are named `host` - - proc `host=`*(s: var TSocket, value: int) {.inline.} = - ## setter of hostAddr - s.FHost = value - - proc host*(s: TSocket): int {.inline.} = - ## getter of hostAddr - s.FHost - - var - s: TSocket - s.host = 34 # same as `host=`(s, 34) - - -Command invocation syntax -------------------------- - -Routines can be invoked without the ``()`` if the call is syntatically -a statement. This command invocation syntax also works for -expressions, but then only a single argument may follow. This restriction -means ``echo f 1, f 2`` is parsed as ``echo(f(1), f(2))`` and not as -``echo(f(1, f(2)))``. The method call syntax may be used to provide one -more argument in this case: - -.. code-block:: nimrod - proc optarg(x:int, y:int = 0):int = x + y - proc singlearg(x:int):int = 20*x - - echo optarg 1, " ", singlearg 2 # prints "1 40" - - let fail = optarg 1, optarg 8 # Wrong. Too many arguments for a command call - let x = optarg(1, optarg 8) # traditional procedure call with 2 arguments - let y = 1.optarg optarg 8 # same thing as above, w/o the parenthesis - assert x == y - -The command invocation syntax also can't have complex expressions as arguments. -For example: (`anonymous procs`_), ``if``, ``case`` or ``try``. The (`do -notation`_) is limited, but usable for a single proc (see the example in the -corresponding section). Function calls with no arguments still needs () to -distinguish between a call and the function itself as a first class value. - - -Closures --------- - -Procedures can appear at the top level in a module as well as inside other -scopes, in which case they are called nested procs. A nested proc can access -local variables from its enclosing scope and if it does so it becomes a -closure. Any captured variables are stored in a hidden additional argument -to the closure (its environment) and they are accessed by reference by both -the closure and its enclosing scope (i.e. any modifications made to them are -visible in both places). The closure environment may be allocated on the heap -or on the stack if the compiler determines that this would be safe. - - -Anonymous Procs ---------------- - -Procs can also be treated as expressions, in which case it's allowed to omit -the proc's name. - -.. code-block:: nimrod - var cities = @["Frankfurt", "Tokyo", "New York"] - - cities.sort(proc (x,y: string): int = - cmp(x.len, y.len)) - - -Procs as expressions can appear both as nested procs and inside top level -executable code. - - -Do notation ------------ - -As a special more convenient notation, proc expressions involved in procedure -calls can use the ``do`` keyword: - -.. code-block:: nimrod - sort(cities) do (x,y: string) -> int: - cmp(x.len, y.len) - # Less parenthesis using the method plus command syntax: - cities = cities.map do (x:string) -> string: - "City of " & x - -``do`` is written after the parentheses enclosing the regular proc params. -The proc expression represented by the do block is appended to them. - -More than one ``do`` block can appear in a single call: - -.. code-block:: nimrod - proc performWithUndo(task: proc(), undo: proc()) = ... - - performWithUndo do: - # multiple-line block of code - # to perform the task - do: - # code to undo it - -For compatibility with ``stmt`` templates and macros, the ``do`` keyword can be -omitted if the supplied proc doesn't have any parameters and return value. -The compatibility works in the other direction too as the ``do`` syntax can be -used with macros and templates expecting ``stmt`` blocks. - - -Nonoverloadable builtins ------------------------- - -The following builtin procs cannot be overloaded for reasons of implementation -simplicity (they require specialized semantic checking):: - - defined, definedInScope, compiles, low, high, sizeOf, - is, of, echo, shallowCopy, getAst, spawn - -Thus they act more like keywords than like ordinary identifiers; unlike a -keyword however, a redefinition may `shadow`:idx: the definition in -the ``system`` module. - - -Var parameters --------------- -The type of a parameter may be prefixed with the ``var`` keyword: - -.. code-block:: nimrod - proc divmod(a, b: int; res, remainder: var int) = - res = a div b - remainder = a mod b - - var - x, y: int - - divmod(8, 5, x, y) # modifies x and y - assert x == 1 - assert y == 3 - -In the example, ``res`` and ``remainder`` are `var parameters`. -Var parameters can be modified by the procedure and the changes are -visible to the caller. The argument passed to a var parameter has to be -an l-value. Var parameters are implemented as hidden pointers. The -above example is equivalent to: - -.. code-block:: nimrod - proc divmod(a, b: int; res, remainder: ptr int) = - res[] = a div b - remainder[] = a mod b - - var - x, y: int - divmod(8, 5, addr(x), addr(y)) - assert x == 1 - assert y == 3 - -In the examples, var parameters or pointers are used to provide two -return values. This can be done in a cleaner way by returning a tuple: - -.. code-block:: nimrod - proc divmod(a, b: int): tuple[res, remainder: int] = - (a div b, a mod b) - - var t = divmod(8, 5) - - assert t.res == 1 - assert t.remainder == 3 - -One can use `tuple unpacking`:idx: to access the tuple's fields: - -.. code-block:: nimrod - var (x, y) = divmod(8, 5) # tuple unpacking - assert x == 1 - assert y == 3 - - -Var return type ---------------- - -A proc, converter or iterator may return a ``var`` type which means that the -returned value is an l-value and can be modified by the caller: - -.. code-block:: nimrod - var g = 0 - - proc WriteAccessToG(): var int = - result = g - - WriteAccessToG() = 6 - assert g == 6 - -It is a compile time error if the implicitly introduced pointer could be -used to access a location beyond its lifetime: - -.. code-block:: nimrod - proc WriteAccessToG(): var int = - var g = 0 - result = g # Error! - -For iterators, a component of a tuple return type can have a ``var`` type too: - -.. code-block:: nimrod - iterator mpairs(a: var seq[string]): tuple[key: int, val: var string] = - for i in 0..a.high: - yield (i, a[i]) - -In the standard library every name of a routine that returns a ``var`` type -starts with the prefix ``m`` per convention. - - -Overloading of the subscript operator -------------------------------------- - -The ``[]`` subscript operator for arrays/openarrays/sequences can be overloaded. - - -Multi-methods -============= - -Procedures always use static dispatch. Multi-methods use dynamic -dispatch. - -.. code-block:: nimrod - type - TExpr = object ## abstract base class for an expression - TLiteral = object of TExpr - x: int - TPlusExpr = object of TExpr - a, b: ref TExpr - - method eval(e: ref TExpr): int = - # override this base method - quit "to override!" - - method eval(e: ref TLiteral): int = return e.x - - method eval(e: ref TPlusExpr): int = - # watch out: relies on dynamic binding - result = eval(e.a) + eval(e.b) - - proc newLit(x: int): ref TLiteral = - new(result) - result.x = x - - proc newPlus(a, b: ref TExpr): ref TPlusExpr = - new(result) - result.a = a - result.b = b - - 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 -requires dynamic binding. - -In a multi-method all parameters that have an object type are used for the -dispatching: - -.. code-block:: nimrod - type - TThing = object - TUnit = object of TThing - x: int - - method collide(a, b: TThing) {.inline.} = - quit "to override!" - - method collide(a: TThing, b: TUnit) {.inline.} = - echo "1" - - method collide(a: TUnit, b: TThing) {.inline.} = - echo "2" - - var - a, b: TUnit - collide(a, b) # output: 2 - - -Invocation of a multi-method cannot be ambiguous: collide 2 is preferred over -collide 1 because the resolution works from left to right. -In the example ``TUnit, TThing`` is preferred over ``TThing, TUnit``. - -**Performance note**: Nimrod does not produce a virtual method table, but -generates dispatch trees. This avoids the expensive indirect branch for method -calls and enables inlining. However, other optimizations like compile time -evaluation or dead code elimination do not work with methods. - - -Iterators and the for statement -=============================== - -The `for`:idx: statement is an abstract mechanism to iterate over the elements -of a container. It relies on an `iterator`:idx: to do so. Like ``while`` -statements, ``for`` statements open an `implicit block`:idx:, so that they -can be left with a ``break`` statement. - -The ``for`` loop declares iteration variables - their scope reaches until the -end of the loop body. The iteration variables' types are inferred by the -return type of the iterator. - -An iterator is similar to a procedure, except that it can be called in the -context of a ``for`` loop. Iterators provide a way to specify the iteration over -an abstract type. A key role in the execution of a ``for`` loop plays the -``yield`` statement in the called iterator. Whenever a ``yield`` statement is -reached the data is bound to the ``for`` loop variables and control continues -in the body of the ``for`` loop. The iterator's local variables and execution -state are automatically saved between calls. Example: - -.. code-block:: nimrod - # this definition exists in the system module - iterator items*(a: string): char {.inline.} = - var i = 0 - while i < len(a): - yield a[i] - inc(i) - - for ch in items("hello world"): # `ch` is an iteration variable - echo(ch) - -The compiler generates code as if the programmer would have written this: - -.. code-block:: nimrod - var i = 0 - while i < len(a): - var ch = a[i] - echo(ch) - inc(i) - -If the iterator yields a tuple, there can be as many iteration variables -as there are components in the tuple. The i'th iteration variable's type is -the type of the i'th component. In other words, implicit tuple unpacking in a -for loop context is supported. - -Implict items/pairs invocations -------------------------------- - -If the for loop expression ``e`` does not denote an iterator and the for loop -has exactly 1 variable, the for loop expression is rewritten to ``items(e)``; -ie. an ``items`` iterator is implicitly invoked: - -.. code-block:: nimrod - for x in [1,2,3]: echo x - -If the for loop has exactly 2 variables, a ``pairs`` iterator is implicitly -invoked. - -Symbol lookup of the identifiers ``items``/``pairs`` is performed after -the rewriting step, so that all overloadings of ``items``/``pairs`` are taken -into account. - - -First class iterators ---------------------- - -There are 2 kinds of iterators in Nimrod: *inline* and *closure* iterators. -An `inline iterator`:idx: is an iterator that's always inlined by the compiler -leading to zero overhead for the abstraction, but may result in a heavy -increase in code size. Inline iterators are second class citizens; -They can be passed as parameters only to other inlining code facilities like -templates, macros and other inline iterators. - -In contrast to that, a `closure iterator`:idx: can be passed around more freely: - -.. code-block:: nimrod - iterator count0(): int {.closure.} = - yield 0 - - iterator count2(): int {.closure.} = - var x = 1 - yield x - inc x - yield x - - proc invoke(iter: iterator(): int {.closure.}) = - for x in iter(): echo x - - invoke(count0) - invoke(count2) - -Closure iterators have other restrictions than inline iterators: - -1. ``yield`` in a closure iterator can not occur in a ``try`` statement. -2. For now, a closure iterator cannot be evaluated at compile time. -3. ``return`` is allowed in a closure iterator (but rarely useful). -4. Both inline and closure iterators cannot be recursive. - -Iterators that are neither marked ``{.closure.}`` nor ``{.inline.}`` explicitly -default to being inline, but that this may change in future versions of the -implementation. - -The ``iterator`` type is always of the calling convention ``closure`` -implicitly; the following example shows how to use iterators to implement -a `collaborative tasking`:idx: system: - -.. code-block:: nimrod - # simple tasking: - type - TTask = iterator (ticker: int) - - iterator a1(ticker: int) {.closure.} = - echo "a1: A" - yield - echo "a1: B" - yield - echo "a1: C" - yield - echo "a1: D" - - iterator a2(ticker: int) {.closure.} = - echo "a2: A" - yield - echo "a2: B" - yield - echo "a2: C" - - proc runTasks(t: varargs[TTask]) = - var ticker = 0 - while true: - let x = t[ticker mod t.len] - if finished(x): break - x(ticker) - inc ticker - - runTasks(a1, a2) - -The builtin ``system.finished`` can be used to determine if an iterator has -finished its operation; no exception is raised on an attempt to invoke an -iterator that has already finished its work. - -Closure iterators are *resumable functions* and so one has to provide the -arguments to every call. To get around this limitation one can capture -parameters of an outer factory proc: - -.. code-block:: nimrod - proc mycount(a, b: int): iterator (): int = - result = iterator (): int = - var x = a - while x <= b: - yield x - inc x - - let foo = mycount(1, 4) - - for f in foo(): - echo f - -Implicit return type --------------------- - -Since inline interators must always produce values that will be consumed in -a for loop, the compiler will implicity use the ``auto`` return type if no -type is given by the user. In contrast, since closure iterators can be used -as a collaborative tasking system, ``void`` is a valid return type for them. - - -Type sections -============= - -Example: - -.. code-block:: nimrod - type # example demonstrating mutually recursive types - PNode = ref TNode # a traced pointer to a TNode - TNode = object - le, ri: PNode # left and right subtrees - sym: ref TSym # leaves contain a reference to a TSym - - TSym = object # a symbol - name: string # the symbol's name - line: int # the line the symbol was declared in - code: PNode # the symbol's abstract syntax tree - -A type section begins with the ``type`` keyword. It contains multiple -type definitions. A type definition binds a type to a name. Type definitions -can be recursive or even mutually recursive. Mutually recursive types are only -possible within a single ``type`` section. Nominal types like ``objects`` -or ``enums`` can only be defined in a ``type`` section. - - -Exception handling -================== - -Try statement -------------- - -Example: - -.. code-block:: nimrod - # read the first two lines of a text file that should contain numbers - # and tries to add them - var - f: TFile - if open(f, "numbers.txt"): - try: - var a = readLine(f) - var b = readLine(f) - echo("sum: " & $(parseInt(a) + parseInt(b))) - except EOverflow: - echo("overflow!") - except EInvalidValue: - echo("could not convert string to integer") - except EIO: - echo("IO error!") - except: - echo("Unknown exception!") - finally: - close(f) - - -The statements after the ``try`` are executed in sequential order unless -an exception ``e`` is raised. If the exception type of ``e`` matches any -listed in an ``except`` clause the corresponding statements are executed. -The statements following the ``except`` clauses are called -`exception handlers`:idx:. - -The empty `except`:idx: clause is executed if there is an exception that is -not listed otherwise. It is similar to an ``else`` clause in ``if`` statements. - -If there is a `finally`:idx: clause, it is always executed after the -exception handlers. - -The exception is *consumed* in an exception handler. However, an -exception handler may raise another exception. If the exception is not -handled, it is propagated through the call stack. This means that often -the rest of the procedure - that is not within a ``finally`` clause - -is not executed (if an exception occurs). - - -Except and finally statements ------------------------------ - -``except`` and ``finally`` can also be used as a stand-alone statements. -Any statements following them in the current block will be considered to be -in an implicit try block: - -.. code-block:: nimrod - var f = open("numbers.txt") - finally: close(f) - ... - -The ``except`` statement has a limitation in this form: one can't specify the -type of the exception, one has to catch everything. Also, if one wants to use -both ``finally`` and ``except`` one needs to reverse the usual sequence of the -statements. Example: - -.. code-block:: nimrod - proc test() = - raise newException(E_base, "Hey ho") - - proc tester() = - finally: echo "3. Finally block" - except: echo "2. Except block" - echo "1. Pre exception" - test() - echo "4. Post exception" - # --> 1, 2, 3 is printed, 4 is never reached - - -Raise statement ---------------- - -Example: - -.. code-block:: nimrod - raise newEOS("operating system failed") - -Apart from built-in operations like array indexing, memory allocation, etc. -the ``raise`` statement is the only way to raise an exception. - -.. XXX document this better! - -If no exception name is given, the current exception is `re-raised`:idx:. The -`ENoExceptionToReraise`:idx: exception is raised if there is no exception to -re-raise. It follows that the ``raise`` statement *always* raises an -exception (unless a raise hook has been provided). - - -OnRaise builtin ---------------- - -`system.onRaise() `_ can be used to override the -behaviour of ``raise`` for a single ``try`` statement. ``onRaise`` has to be -called within the ``try`` statement that should be affected. - -This allows for a Lisp-like `condition system`:idx:\: - -.. code-block:: nimrod - var myFile = open("broken.txt", fmWrite) - try: - onRaise do (e: ref E_Base)-> bool: - if e of EIO: - stdout.writeln "ok, writing to stdout instead" - else: - # do raise other exceptions: - result = true - myFile.writeln "writing to broken file" - finally: - myFile.close() - -``OnRaise`` can only *filter* raised exceptions, it cannot transform one -exception into another. (Nor should ``onRaise`` raise an exception though -this is currently not enforced.) This restriction keeps the exception tracking -analysis sound. - - -Effect system -============= - -Exception tracking ------------------- - -Nimrod supports exception tracking. The `raises`:idx: pragma can be used -to explicitly define which exceptions a proc/iterator/method/converter is -allowed to raise. The compiler verifies this: - -.. code-block:: nimrod - proc p(what: bool) {.raises: [EIO, EOS].} = - if what: raise newException(EIO, "IO") - else: raise newException(EOS, "OS") - -An empty ``raises`` list (``raises: []``) means that no exception may be raised: - -.. code-block:: nimrod - proc p(): bool {.raises: [].} = - try: - unsafeCall() - result = true - except: - result = false - - -A ``raises`` list can also be attached to a proc type. This affects type -compatibility: - -.. code-block:: nimrod - type - TCallback = proc (s: string) {.raises: [EIO].} - var - c: TCallback - - proc p(x: string) = - raise newException(EOS, "OS") - - c = p # type error - - -For a routine ``p`` the compiler uses inference rules to determine the set of -possibly raised exceptions; the algorithm operates on ``p``'s call graph: - -1. Every indirect call via some proc type ``T`` is assumed to - raise ``system.E_Base`` (the base type of the exception hierarchy) and - thus any exception unless ``T`` has an explicit ``raises`` list. - However if the call is of the form ``f(...)`` where ``f`` is a parameter - of the currently analysed routine it is ignored. The call is optimistically - assumed to have no effect. Rule 2 compensates for this case. -2. Every expression of some proc type wihtin a call that is not a call - itself (and not nil) is assumed to be called indirectly somehow and thus - its raises list is added to ``p``'s raises list. -3. Every call to a proc ``q`` which has an unknown body (due to a forward - declaration or an ``importc`` pragma) is assumed to - raise ``system.E_Base`` unless ``q`` has an explicit ``raises`` list. -4. Every call to a method ``m`` is assumed to - raise ``system.E_Base`` unless ``m`` has an explicit ``raises`` list. -5. For every other call the analysis can determine an exact ``raises`` list. -6. For determining a ``raises`` list, the ``raise`` and ``try`` statements - of ``p`` are taken into consideration. - -Rules 1-2 ensure the following works: - -.. code-block:: nimrod - proc noRaise(x: proc()) {.raises: [].} = - # unknown call that might raise anything, but valid: - x() - - proc doRaise() {.raises: [EIO].} = - raise newException(EIO, "IO") - - proc use() {.raises: [].} = - # doesn't compile! Can raise EIO! - noRaise(doRaise) - -So in many cases a callback does not cause the compiler to be overly -conservative in its effect analysis. - - -Tag tracking ------------- - -The exception tracking is part of Nimrod's `effect system`:idx:. Raising an -exception is an *effect*. Other effects can also be defined. A user defined -effect is a means to *tag* a routine and to perform checks against this tag: - -.. code-block:: nimrod - type IO = object ## input/output effect - proc readLine(): string {.tags: [IO].} - - proc no_IO_please() {.tags: [].} = - # the compiler prevents this: - let x = readLine() - -A tag has to be a type name. A ``tags`` list - like a ``raises`` list - can -also be attached to a proc type. This affects type compatibility. - -The inference for tag tracking is analogous to the inference for -exception tracking. - - -Read/Write tracking -------------------- - -**Note**: Read/write tracking is not yet implemented! - -The inference for read/write tracking is analogous to the inference for -exception tracking. - - -Effects pragma --------------- - -The ``effects`` pragma has been designed to assist the programmer with the -effects analysis. It is a statement that makes the compiler output all inferred -effects up to the ``effects``'s position: - -.. code-block:: nimrod - proc p(what: bool) = - if what: - raise newException(EIO, "IO") - {.effects.} - else: - raise newException(EOS, "OS") - -The compiler produces a hint message that ``EIO`` can be raised. ``EOS`` is not -listed as it cannot be raised in the branch the ``effects`` pragma appears in. - - -Generics -======== - -Example: - -.. code-block:: nimrod - type - TBinaryTree[T] = object # TBinaryTree is a generic type with - # with generic param ``T`` - le, ri: ref TBinaryTree[T] # left and right subtrees; may be nil - data: T # the data stored in a node - PBinaryTree[T] = ref TBinaryTree[T] # a shorthand for notational convenience - - proc newNode[T](data: T): PBinaryTree[T] = # constructor for a node - new(result) - result.data = data - - proc add[T](root: var PBinaryTree[T], n: PBinaryTree[T]) = - if root == nil: - root = n - else: - var it = root - while it != nil: - var c = cmp(it.data, n.data) # compare the data items; uses - # the generic ``cmp`` proc that works for - # any type that has a ``==`` and ``<`` - # operator - if c < 0: - if it.le == nil: - it.le = n - return - it = it.le - else: - if it.ri == nil: - it.ri = n - return - it = it.ri - - iterator inorder[T](root: PBinaryTree[T]): T = - # inorder traversal of a binary tree - # recursive iterators are not yet implemented, so this does not work in - # the current compiler! - if root.le != nil: yield inorder(root.le) - yield root.data - if root.ri != nil: yield inorder(root.ri) - - var - root: PBinaryTree[string] # instantiate a PBinaryTree with the type string - add(root, newNode("hallo")) # instantiates generic procs ``newNode`` and - add(root, newNode("world")) # ``add`` - for str in inorder(root): - writeln(stdout, str) - -Generics are Nimrod's means to parametrize procs, iterators or types with -`type parameters`:idx:. Depending on context, the brackets are used either to -introduce type parameters or to instantiate a generic proc, iterator or type. - - -Is operator ------------ - -The ``is`` operator checks for type equivalence at compile time. It is -therefore very useful for type specialization within generic code: - -.. code-block:: nimrod - type - TTable[TKey, TValue] = object - keys: seq[TKey] - values: seq[TValue] - when not (TKey is string): # nil value for strings used for optimization - deletedKeys: seq[bool] - - -Type operator -------------- - -The ``type`` (in many other languages called `typeof`:idx:) operator can -be used to get the type of an expression: - -.. code-block:: nimrod - var x = 0 - var y: type(x) # y has type int - -If ``type`` is used to determine the result type of a proc/iterator/converter -call ``c(X)`` (where ``X`` stands for a possibly empty list of arguments), the -interpretation where ``c`` is an iterator is preferred over the -other interpretations: - -.. code-block:: nimrod - import strutils - - # strutils contains both a ``split`` proc and iterator, but since an - # an iterator is the preferred interpretation, `y` has the type ``string``: - var y: type("a b c".split) - - -Type Classes ------------- - -A type class is a special pseudo-type that can be used to match against -types in the context of overload resolution or the ``is`` operator. -Nimrod supports the following built-in type classes: - -================== =================================================== -type class matches -================== =================================================== -``object`` any object type -``tuple`` any tuple type - -``enum`` any enumeration -``proc`` any proc type -``ref`` any ``ref`` type -``ptr`` any ``ptr`` type -``var`` any ``var`` type -``distinct`` any distinct type -``array`` any array type -``set`` any set type -``seq`` any seq type -``auto`` any type -================== =================================================== - -Furthermore, every generic type automatically creates a type class of the same -name that will match any instantiation of the generic type. - -Type classes can be combined using the standard boolean operators to form -more complex type classes: - -.. code-block:: nimrod - # create a type class that will match all tuple and object types - type TRecordType = tuple or object - - proc printFields(rec: TRecordType) = - for key, value in fieldPairs(rec): - echo key, " = ", value - -Procedures utilizing type classes in such manner are considered to be -`implicitly generic`:idx:. They will be instantiated once for each unique -combination of param types used within the program. - -Nimrod also allows for type classes and regular types to be specified -as `type constraints`:idx: of the generic type parameter: - -.. code-block:: nimrod - proc onlyIntOrString[T: int|string](x, y: T) = discard - - onlyIntOrString(450, 616) # valid - onlyIntOrString(5.0, 0.0) # type mismatch - onlyIntOrString("xy", 50) # invalid as 'T' cannot be both at the same time - -By default, during overload resolution each named type class will bind to -exactly one concrete type. Here is an example taken directly from the system -module to illustrate this: - -.. code-block:: nimrod - proc `==`*(x, y: tuple): bool = - ## requires `x` and `y` to be of the same tuple type - ## generic ``==`` operator for tuples that is lifted from the components - ## of `x` and `y`. - result = true - for a, b in fields(x, y): - if a != b: result = false - -Alternatively, the ``distinct`` type modifier can be applied to the type class -to allow each param matching the type class to bind to a different type. - -If a proc param doesn't have a type specified, Nimrod will use the -``distinct auto`` type class (also known as ``any``): - -.. code-block:: nimrod - # allow any combination of param types - proc concat(a, b): string = $a & $b - -Procs written with the implicitly generic style will often need to refer to the -type parameters of the matched generic type. They can be easily accessed using -the dot syntax: - -.. code-block:: nimrod - type TMatrix[T, Rows, Columns] = object - ... - - proc `[]`(m: TMatrix, row, col: int): TMatrix.T = - m.data[col * high(TMatrix.Columns) + row] - -Alternatively, the `type` operator can be used over the proc params for similar -effect when anonymous or distinct type classes are used. - -When a generic type is instantiated with a type class instead of a concrete -type, this results in another more specific type class: - -.. code-block:: nimrod - seq[ref object] # Any sequence storing references to any object type - - type T1 = auto - proc foo(s: seq[T1], e: T1) - # seq[T1] is the same as just `seq`, but T1 will be allowed to bind - # to a single type, while the signature is being matched - - TMatrix[Ordinal] # Any TMatrix instantiation using integer values - -As seen in the previous example, in such instantiations, it's not necessary to -supply all type parameters of the generic type, because any missing ones will -be inferred to have the equivalent of the `any` type class and thus they will -match anything without discrimination. - - -User defined type classes -------------------------- - -**Note**: User defined type classes are still in development. - -The user-defined type classes are available in two flavours - declarative and -imperative. Both are used to specify an arbitrary set of requirements that the -matched type must satisfy. - -Declarative type classes are written in the following form: - -.. code-block:: nimrod - type - Comparable = generic x, y - (x < y) is bool - - Container[T] = generic c - c.len is ordinal - items(c) is iterator - for value in c: - type(value) is T - -The type class will be matched if: - -a) all of the expressions within the body can be compiled for the tested type -b) all statically evaluatable boolean expressions in the body must be true - -The identifiers following the `generic` keyword represent instances of the -currently matched type. These instances can act both as variables of the type, -when used in contexts where a value is expected, and as the type itself when -used in contexts where a type is expected. - -Please note that the ``is`` operator allows one to easily verify the precise -type signatures of the required operations, but since type inference and -default parameters are still applied in the provided block, it's also possible -to encode usage protocols that do not reveal implementation details. - -As a special rule providing further convenience when writing type classes, any -type value appearing in a callable expression will be treated as a variable of -the designated type for overload resolution purposes, unless the type value was -passed in its explicit ``typedesc[T]`` form: - -.. code-block:: nimrod - type - OutputStream = generic S - write(var S, string) - -Much like generics, the user defined type classes will be instantiated exactly -once for each tested type and any static code included within them will also be -executed once. - - -Return Type Inference ---------------------- - -If a type class is used as the return type of a proc and it won't be bound to -a concrete type by some of the proc params, Nimrod will infer the return type -from the proc body. This is usually used with the ``auto`` type class: - -.. code-block:: nimrod - proc makePair(a, b): auto = (first: a, second: b) - -The return type will be treated as additional generic param and can be -explicitly specified at call sites as any other generic param. - -Future versions of Nimrod may also support overloading based on the return type -of the overloads. In such settings, the expected result type at call sites may -also influence the inferred return type. - - -Symbol lookup in generics -------------------------- - -The symbol binding rules in generics are slightly subtle: There are "open" and -"closed" symbols. A "closed" symbol cannot be re-bound in the instantiation -context, an "open" symbol can. Per default overloaded symbols are open -and every other symbol is closed. - -Open symbols are looked up in two different contexts: Both the context -at definition and the context at instantiation are considered: - -.. code-block:: nimrod - type - TIndex = distinct int - - proc `==` (a, b: TIndex): bool {.borrow.} - - var a = (0, 0.TIndex) - var b = (0, 0.TIndex) - - echo a == b # works! - -In the example the generic ``==`` for tuples (as defined in the system module) -uses the ``==`` operators of the tuple's components. However, the ``==`` for -the ``TIndex`` type is defined *after* the ``==`` for tuples; yet the example -compiles as the instantiation takes the currently defined symbols into account -too. - -A symbol can be forced to be open by a `mixin`:idx: declaration: - -.. code-block:: nimrod - proc create*[T](): ref T = - # there is no overloaded 'init' here, so we need to state that it's an - # open symbol explicitly: - mixin init - new result - init result - - -Bind statement --------------- - -The ``bind`` statement is the counterpart to the ``mixin`` statement. It -can be used to explicitly declare identifiers that should be bound early (i.e. -the identifiers should be looked up in the scope of the template/generic -definition): - -.. code-block:: nimrod - # Module A - var - lastId = 0 - - template genId*: expr = - bind lastId - inc(lastId) - lastId - -.. code-block:: nimrod - # Module B - import A - - echo genId() - -But a ``bind`` is rarely useful because symbol binding from the definition -scope is the default. - - -Templates -========= - -A template is a simple form of a macro: It is a simple substitution -mechanism that operates on Nimrod's abstract syntax trees. It is processed in -the semantic pass of the compiler. - -The syntax to *invoke* a template is the same as calling a procedure. - -Example: - -.. code-block:: nimrod - template `!=` (a, b: expr): expr = - # this definition exists in the System module - not (a == b) - - assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6)) - -The ``!=``, ``>``, ``>=``, ``in``, ``notin``, ``isnot`` operators are in fact -templates: - -| ``a > b`` is transformed into ``b < a``. -| ``a in b`` is transformed into ``contains(b, a)``. -| ``notin`` and ``isnot`` have the obvious meanings. - -The "types" of templates can be the symbols ``expr`` (stands for *expression*), -``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type -description*). These are "meta types", they can only be used in certain -contexts. Real types can be used too; this implies that expressions are -expected. - - -Ordinary vs immediate templates -------------------------------- - -There are two different kinds of templates: immediate templates and -ordinary templates. Ordinary templates take part in overloading resolution. As -such their arguments need to be type checked before the template is invoked. -So ordinary templates cannot receive undeclared identifiers: - -.. code-block:: nimrod - - template declareInt(x: expr) = - var x: int - - declareInt(x) # error: unknown identifier: 'x' - -An ``immediate`` template does not participate in overload resolution and so -its arguments are not checked for semantics before invocation. So they can -receive undeclared identifiers: - -.. code-block:: nimrod - - template declareInt(x: expr) {.immediate.} = - var x: int - - declareInt(x) # valid - - -Passing a code block to a template ----------------------------------- - -If there is a ``stmt`` parameter it should be the last in the template -declaration, because statements are passed to a template via a -special ``:`` syntax: - -.. code-block:: nimrod - - template withFile(f, fn, mode: expr, actions: stmt): stmt {.immediate.} = - var f: TFile - if open(f, fn, mode): - try: - actions - finally: - close(f) - else: - quit("cannot open: " & fn) - - withFile(txt, "ttempl3.txt", fmWrite): - txt.writeln("line 1") - txt.writeln("line 2") - -In the example the two ``writeln`` statements are bound to the ``actions`` -parameter. - - -Symbol binding in templates ---------------------------- - -A template is a `hygienic`:idx: macro and so opens a new scope. Most symbols are -bound from the definition scope of the template: - -.. code-block:: nimrod - # Module A - var - lastId = 0 - - template genId*: expr = - inc(lastId) - lastId - -.. code-block:: nimrod - # Module B - import A - - echo genId() # Works as 'lastId' has been bound in 'genId's defining scope - -As in generics symbol binding can be influenced via ``mixin`` or ``bind`` -statements. - - - -Identifier construction ------------------------ - -In templates identifiers can be constructed with the backticks notation: - -.. code-block:: nimrod - - template typedef(name: expr, typ: typedesc) {.immediate.} = - type - `T name`* {.inject.} = typ - `P name`* {.inject.} = ref `T name` - - typedef(myint, int) - var x: PMyInt - -In the example ``name`` is instantiated with ``myint``, so \`T name\` becomes -``Tmyint``. - - -Lookup rules for template parameters ------------------------------------- - -A parameter ``p`` in a template is even substituted in the expression ``x.p``. -Thus template arguments can be used as field names and a global symbol can be -shadowed by the same argument name even when fully qualified: - -.. code-block:: nimrod - # module 'm' - - type - TLev = enum - levA, levB - - var abclev = levB - - template tstLev(abclev: TLev) = - echo abclev, " ", m.abclev - - tstLev(levA) - # produces: 'levA levA' - -But the global symbol can properly be captured by a ``bind`` statement: - -.. code-block:: nimrod - # module 'm' - - type - TLev = enum - levA, levB - - var abclev = levB - - template tstLev(abclev: TLev) = - bind m.abclev - echo abclev, " ", m.abclev - - tstLev(levA) - # produces: 'levA levB' - - -Hygiene in templates --------------------- - -Per default templates are `hygienic`:idx:\: Local identifiers declared in a -template cannot be accessed in the instantiation context: - -.. code-block:: nimrod - - template newException*(exceptn: typedesc, message: string): expr = - var - e: ref exceptn # e is implicitly gensym'ed here - new(e) - e.msg = message - e - - # so this works: - let e = "message" - raise newException(EIO, e) - - -Whether a symbol that is declared in a template is exposed to the instantiation -scope is controlled by the `inject`:idx: and `gensym`:idx: pragmas: gensym'ed -symbols are not exposed but inject'ed are. - -The default for symbols of entity ``type``, ``var``, ``let`` and ``const`` -is ``gensym`` and for ``proc``, ``iterator``, ``converter``, ``template``, -``macro`` is ``inject``. However, if the name of the entity is passed as a -template parameter, it is an inject'ed symbol: - -.. code-block:: nimrod - template withFile(f, fn, mode: expr, actions: stmt): stmt {.immediate.} = - block: - var f: TFile # since 'f' is a template param, it's injected implicitly - ... - - withFile(txt, "ttempl3.txt", fmWrite): - txt.writeln("line 1") - txt.writeln("line 2") - - -The ``inject`` and ``gensym`` pragmas are second class annotations; they have -no semantics outside of a template definition and cannot be abstracted over: - -.. code-block:: nimrod - {.pragma myInject: inject.} - - template t() = - var x {.myInject.}: int # does NOT work - - -To get rid of hygiene in templates, one can use the `dirty`:idx: pragma for -a template. ``inject`` and ``gensym`` have no effect in ``dirty`` templates. - - - -Macros -====== - -A macro is a special kind of low level template. Macros can be used -to implement `domain specific languages`:idx:. Like templates, macros come in -the 2 flavors *immediate* and *ordinary*. - -While macros enable advanced compile-time code transformations, they -cannot change Nimrod's syntax. However, this is no real restriction because -Nimrod's syntax is flexible enough anyway. - -To write macros, one needs to know how the Nimrod concrete syntax is converted -to an abstract syntax tree. - -There are two ways to invoke a macro: -(1) invoking a macro like a procedure call (`expression macros`) -(2) invoking a macro with the special ``macrostmt`` syntax (`statement macros`) - - -Expression Macros ------------------ - -The following example implements a powerful ``debug`` command that accepts a -variable number of arguments: - -.. code-block:: nimrod - # to work with Nimrod syntax trees, we need an API that is defined in the - # ``macros`` module: - import macros - - macro debug(n: varargs[expr]): stmt = - # `n` is a Nimrod AST that contains the whole macro invocation - # this macro returns a list of statements: - result = newNimNode(nnkStmtList, n) - # iterate over any argument that is passed to this macro: - for i in 0..n.len-1: - # add a call to the statement list that writes the expression; - # `toStrLit` converts an AST to its string representation: - add(result, newCall("write", newIdentNode("stdout"), toStrLit(n[i]))) - # add a call to the statement list that writes ": " - add(result, newCall("write", newIdentNode("stdout"), newStrLitNode(": "))) - # add a call to the statement list that writes the expressions value: - add(result, newCall("writeln", newIdentNode("stdout"), n[i])) - - var - a: array [0..10, int] - x = "some string" - a[0] = 42 - a[1] = 45 - - debug(a[0], a[1], x) - -The macro call expands to: - -.. code-block:: nimrod - write(stdout, "a[0]") - write(stdout, ": ") - writeln(stdout, a[0]) - - write(stdout, "a[1]") - write(stdout, ": ") - writeln(stdout, a[1]) - - write(stdout, "x") - write(stdout, ": ") - writeln(stdout, x) - - -Arguments that are passed to a ``varargs`` parameter are wrapped in an array -constructor expression. This is why ``debug`` iterates over all of ``n``'s -children. - - -BindSym -------- - -The above ``debug`` macro relies on the fact that ``write``, ``writeln`` and -``stdout`` are declared in the system module and thus visible in the -instantiating context. There is a way to use bound identifiers -(aka `symbols`:idx:) instead of using unbound identifiers. The ``bindSym`` -builtin can be used for that: - -.. code-block:: nimrod - import macros - - macro debug(n: varargs[expr]): stmt = - result = newNimNode(nnkStmtList, n) - for i in 0..n.len-1: - # we can bind symbols in scope via 'bindSym': - add(result, newCall(bindSym"write", bindSym"stdout", toStrLit(n[i]))) - add(result, newCall(bindSym"write", bindSym"stdout", newStrLitNode(": "))) - add(result, newCall(bindSym"writeln", bindSym"stdout", n[i])) - - var - a: array [0..10, int] - x = "some string" - a[0] = 42 - a[1] = 45 - - debug(a[0], a[1], x) - -The macro call expands to: - -.. code-block:: nimrod - write(stdout, "a[0]") - write(stdout, ": ") - writeln(stdout, a[0]) - - write(stdout, "a[1]") - write(stdout, ": ") - writeln(stdout, a[1]) - - write(stdout, "x") - write(stdout, ": ") - writeln(stdout, x) - -However, the symbols ``write``, ``writeln`` and ``stdout`` are already bound -and are not looked up again. As the example shows, ``bindSym`` does work with -overloaded symbols implicitly. - - -Statement Macros ----------------- - -Statement macros are defined just as expression macros. However, they are -invoked by an expression following a colon. - -The following example outlines a macro that generates a lexical analyzer from -regular expressions: - -.. code-block:: nimrod - import macros - - macro case_token(n: stmt): stmt = - # creates a lexical analyzer from regular expressions - # ... (implementation is an exercise for the reader :-) - discard - - case_token: # this colon tells the parser it is a macro statement - of r"[A-Za-z_]+[A-Za-z_0-9]*": - return tkIdentifier - of r"0-9+": - return tkInteger - of r"[\+\-\*\?]+": - return tkOperator - else: - return tkUnknown - - -**Style note**: For code readability, it is the best idea to use the least -powerful programming construct that still suffices. So the "check list" is: - -(1) Use an ordinary proc/iterator, if possible. -(2) Else: Use a generic proc/iterator, if possible. -(3) Else: Use a template, if possible. -(4) Else: Use a macro. - - -Macros as pragmas ------------------ - -Whole routines (procs, iterators etc.) can also be passed to a template or -a macro via the pragma notation: - -.. code-block:: nimrod - template m(s: stmt) = discard - - proc p() {.m.} = discard - -This is a simple syntactic transformation into: - -.. code-block:: nimrod - template m(s: stmt) = discard - - m: - proc p() = discard - - -Special Types -============= - -static[T] ---------- - -**Note**: static[T] is still in development. - -As their name suggests, static params must be known at compile-time: - -.. code-block:: nimrod - - proc precompiledRegex(pattern: static[string]): TRegEx = - var res {.global.} = re(pattern) - return res - - precompiledRegex("/d+") # Replaces the call with a precompiled - # regex, stored in a global variable - - precompiledRegex(paramStr(1)) # Error, command-line options - # are not known at compile-time - - -For the purposes of code generation, all static params are treated as -generic params - the proc will be compiled separately for each unique -supplied value (or combination of values). - -Furthermore, the system module defines a `semistatic[T]` type than can be -used to declare procs accepting both static and run-time values, which can -optimize their body according to the supplied param using the `isStatic(p)` -predicate: - -.. code-block:: nimrod - - # The following proc will be compiled once for each unique static - # value and also once for the case handling all run-time values: - - proc re(pattern: semistatic[string]): TRegEx = - when isStatic(pattern): - result = precompiledRegex(pattern) - else: - result = compile(pattern) - -Static params can also appear in the signatures of generic types: - -.. code-block:: nimrod - - type - Matrix[M,N: static[int]; T: Number] = array[0..(M*N - 1), T] - # Note how `Number` is just a type constraint here, while - # `static[int]` requires us to supply a compile-time int value - - AffineTransform2D[T] = Matrix[3, 3, T] - AffineTransform3D[T] = Matrix[4, 4, T] - - var m1: AffineTransform3D[float] # OK - var m2: AffineTransform2D[string] # Error, `string` is not a `Number` - - -typedesc --------- - -`typedesc` is a special type allowing one to treat types as compile-time values -(i.e. if types are compile-time values and all values have a type, then -typedesc must be their type). - -When used as a regular proc param, typedesc acts as a type class. The proc -will be instantiated for each unique type parameter and one can refer to the -instantiation type using the param name: - -.. code-block:: nimrod - - proc new(T: typedesc): ref T = - echo "allocating ", T.name - new(result) - - var n = TNode.new - var tree = new(TBinaryTree[int]) - -When multiple typedesc params are present, they act like a distinct type class -(i.e. they will bind freely to different types). To force a bind-once behavior -one can use a named alias or an explicit `typedesc` generic param: - -.. code-block:: nimrod - - # `type1` and `type2` are aliases for typedesc available from system.nim - proc acceptOnlyTypePairs(A, B: type1; C, D: type2) - proc acceptOnlyTypePairs[T: typedesc, U: typedesc](A, B: T; C, D: U) - -Once bound, typedesc params can appear in the rest of the proc signature: - -.. code-block:: nimrod - - template declareVariableWithType(T: typedesc, value: T) = - var x: T = value - - declareVariableWithType int, 42 - -When used with macros and .compileTime. procs on the other hand, the compiler -does not need to instantiate the code multiple times, because types then can be -manipulated using the unified internal symbol representation. In such context -typedesc acts as any other type. One can create variables, store typedesc -values inside containers and so on. For example, here is how one can create -a type-safe wrapper for the unsafe `printf` function from C: - -.. code-block:: nimrod - macro safePrintF(formatString: string{lit}, args: varargs[expr]): expr = - var i = 0 - for c in formatChars(formatString): - var expectedType = case c - of 'c': char - of 'd', 'i', 'x', 'X': int - of 'f', 'e', 'E', 'g', 'G': float - of 's': string - of 'p': pointer - else: EOutOfRange - - var actualType = args[i].getType - inc i - - if expectedType == EOutOfRange: - error c & " is not a valid format character" - elif expectedType != actualType: - error "type mismatch for argument ", i, ". expected type: ", - expectedType.name, ", actual type: ", actualType.name - - # keep the original callsite, but use cprintf instead - result = callsite() - result[0] = newIdentNode(!"cprintf") - - -Overload resolution can be further influenced by constraining the set of -types that will match the typedesc param: - -.. code-block:: nimrod - - template maxval(T: typedesc[int]): int = high(int) - template maxval(T: typedesc[float]): float = Inf - - var i = int.maxval - var f = float.maxval - var s = string.maxval # error, maxval is not implemented for string - -The constraint can be a concrete type or a type class. - - -Special Operators -================= - -dot operators -------------- - -Nimrod offers a special family of dot operators that can be used to -intercept and rewrite proc call and field access attempts, referring -to previously undeclared symbol names. They can be used to provide a -fluent interface to objects lying outside the static confines of the -type system such as values from dynamic scripting languages -or dynamic file formats such as JSON or XML. - -When Nimrod encounters an expression that cannot be resolved by the -standard overload resolution rules, the current scope will be searched -for a dot operator that can be matched against a re-written form of -the expression, where the unknown field or proc name is converted to -an additional static string parameter: - -.. code-block:: nimrod - a.b # becomes `.`(a, "b") - a.b(c, d) # becomes `.`(a, "b", c, d) - -The matched dot operators can be symbols of any callable kind (procs, -templates and macros), depending on the desired effect: - -.. code-block:: nimrod - proc `.` (js: PJsonNode, field: string): JSON = js[field] - - var js = parseJson("{ x: 1, y: 2}") - echo js.x # outputs 1 - echo js.y # outputs 2 - -The following dot operators are available: - -operator `.` ------------- -This operator will be matched against both field accesses and method calls. - -operator `.()` ---------------- -This operator will be matched exclusively against method calls. It has higher -precedence than the `.` operator and this allows one to handle expressions like -`x.y` and `x.y()` differently if one is interfacing with a scripting language -for example. - -operator `.=` -------------- -This operator will be matched against assignments to missing fields. - -.. code-block:: nimrod - a.b = c # becomes `.=`(a, "b", c) - - -Type bound operations -===================== - -There are 3 operations that are bound to a type: - -1. Assignment -2. Destruction -3. Deep copying for communication between threads - -These operations can be *overriden* instead of *overloaded*. This means the -implementation is automatically lifted to structured types. For instance if type -``T`` has an overriden assignment operator ``=`` this operator is also used -for assignments of the type ``seq[T]``. Since these operations are bound to a -type they have to be bound to a nominal type for reasons of simplicity of -implementation: This means an overriden ``deepCopy`` for ``ref T`` is really -bound to ``T`` and not to ``ref T``. This also means that one cannot override -``deepCopy`` for both ``ptr T`` and ``ref T`` at the same time; instead a -helper distinct or object type has to be used for one pointer type. - - -operator `=` ------------- - -**Note**: Overriding the assignment operator has not yet been implemented. - -This operator is the assignment operator. Note that in the contexts -like ``let v = expr``, ``var v = expr``, ``parameter = defaultValue`` or for -parameter passing no assignment is performed. The ``override`` pragma is -optional for overriding ``=``. - - -destructors ------------ - -A destructor must have a single parameter with a concrete type (the name of a -generic type is allowed too). The name of the destructor has to be ``destroy`` -and it need to be annotated with the ``override`` pragma. - -``destroy(v)`` will be automatically invoked for every local stack -variable ``v`` that goes out of scope. - -If a structured type features a field with destructable type and -the user has not provided an explicit implementation, a destructor for the -structured type will be automatically generated. Calls to any base class -destructors in both user-defined and generated destructors will be inserted. - -A destructor is attached to the type it destructs; expressions of this type -can then only be used in *destructible contexts* and as parameters: - -.. code-block:: nimrod - type - TMyObj = object - x, y: int - p: pointer - - proc destroy(o: var TMyObj) {.override.} = - if o.p != nil: dealloc o.p - - proc open: TMyObj = - result = TMyObj(x: 1, y: 2, p: alloc(3)) - - proc work(o: TMyObj) = - echo o.x - # No destructor invoked here for 'o' as 'o' is a parameter. - - proc main() = - # destructor automatically invoked at the end of the scope: - var x = open() - # valid: pass 'x' to some other proc: - work(x) - - # Error: usage of a type with a destructor in a non destructible context - echo open() - -A destructible context is currently only the following: - -1. The ``expr`` in ``var x = expr``. -2. The ``expr`` in ``let x = expr``. -3. The ``expr`` in ``return expr``. -4. The ``expr`` in ``result = expr`` where ``result`` is the special symbol - introduced by the compiler. - -These rules ensure that the construction is tied to a variable and can easily -be destructed at its scope exit. Later versions of the language will improve -the support of destructors. - -Be aware that destructors are not called for objects allocated with ``new``. -This may change in future versions of language, but for now the ``finalizer`` -parameter to ``new`` has to be used. - - -deepCopy --------- - -``deepCopy`` is a builtin that is invoked whenever data is passed to -a ``spawn``'ed proc to ensure memory safety. The programmer can override its -behaviour for a specific ``ref`` or ``ptr`` type ``T``. (Later versions of the -language may weaken this restriction.) - -The signature has to be: - -.. code-block:: nimrod - proc deepCopy(x: T): T {.override.} - -This mechanism is used by most data structures that support shared memory like -channels to implement thread safe automatic memory management. - -The builtin ``deepCopy`` can even clone closures and their environments. See -the documentation of `spawn`_ for details. - - -Term rewriting macros -===================== - -Term rewriting macros are macros or templates that have not only -a *name* but also a *pattern* that is searched for after the semantic checking -phase of the compiler: This means they provide an easy way to enhance the -compilation pipeline with user defined optimizations: - -.. code-block:: nimrod - template optMul{`*`(a, 2)}(a: int): int = a+a - - let x = 3 - echo x * 2 - -The compiler now rewrites ``x * 2`` as ``x + x``. The code inside the -curlies is the pattern to match against. The operators ``*``, ``**``, -``|``, ``~`` have a special meaning in patterns if they are written in infix -notation, so to match verbatim against ``*`` the ordinary function call syntax -needs to be used. - - -Unfortunately optimizations are hard to get right and even the tiny example -is **wrong**: - -.. code-block:: nimrod - template optMul{`*`(a, 2)}(a: int): int = a+a - - proc f(): int = - echo "side effect!" - result = 55 - - echo f() * 2 - -We cannot duplicate 'a' if it denotes an expression that has a side effect! -Fortunately Nimrod supports side effect analysis: - -.. code-block:: nimrod - template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a - - proc f(): int = - echo "side effect!" - result = 55 - - echo f() * 2 # not optimized ;-) - -So what about ``2 * a``? We should tell the compiler ``*`` is commutative. We -cannot really do that however as the following code only swaps arguments -blindly: - -.. code-block:: nimrod - template mulIsCommutative{`*`(a, b)}(a, b: int): int = b*a - -What optimizers really need to do is a *canonicalization*: - -.. code-block:: nimrod - template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = b*a - -The ``int{lit}`` parameter pattern matches against an expression of -type ``int``, but only if it's a literal. - - - -Parameter constraints ---------------------- - -The `parameter constraint`:idx: expression can use the operators ``|`` (or), -``&`` (and) and ``~`` (not) and the following predicates: - -=================== ===================================================== -Predicate Meaning -=================== ===================================================== -``atom`` The matching node has no children. -``lit`` The matching node is a literal like "abc", 12. -``sym`` The matching node must be a symbol (a bound - identifier). -``ident`` The matching node must be an identifier (an unbound - identifier). -``call`` The matching AST must be a call/apply expression. -``lvalue`` The matching AST must be an lvalue. -``sideeffect`` The matching AST must have a side effect. -``nosideeffect`` The matching AST must have no side effect. -``param`` A symbol which is a parameter. -``genericparam`` A symbol which is a generic parameter. -``module`` A symbol which is a module. -``type`` A symbol which is a type. -``var`` A symbol which is a variable. -``let`` A symbol which is a ``let`` variable. -``const`` A symbol which is a constant. -``result`` The special ``result`` variable. -``proc`` A symbol which is a proc. -``method`` A symbol which is a method. -``iterator`` A symbol which is an iterator. -``converter`` A symbol which is a converter. -``macro`` A symbol which is a macro. -``template`` A symbol which is a template. -``field`` A symbol which is a field in a tuple or an object. -``enumfield`` A symbol which is a field in an enumeration. -``forvar`` A for loop variable. -``label`` A label (used in ``block`` statements). -``nk*`` The matching AST must have the specified kind. - (Example: ``nkIfStmt`` denotes an ``if`` statement.) -``alias`` States that the marked parameter needs to alias - with *some* other parameter. -``noalias`` States that *every* other parameter must not alias - with the marked parameter. -=================== ===================================================== - -The ``alias`` and ``noalias`` predicates refer not only to the matching AST, -but also to every other bound parameter; syntactially they need to occur after -the ordinary AST predicates: - -.. code-block:: nimrod - template ex{a = b + c}(a: int{noalias}, b, c: int) = - # this transformation is only valid if 'b' and 'c' do not alias 'a': - a = b - inc a, c - - -Pattern operators ------------------ - -The operators ``*``, ``**``, ``|``, ``~`` have a special meaning in patterns -if they are written in infix notation. - - -The ``|`` operator -~~~~~~~~~~~~~~~~~~ - -The ``|`` operator if used as infix operator creates an ordered choice: - -.. code-block:: nimrod - template t{0|1}(): expr = 3 - let a = 1 - # outputs 3: - echo a - -The matching is performed after the compiler performed some optimizations like -constant folding, so the following does not work: - -.. code-block:: nimrod - template t{0|1}(): expr = 3 - # outputs 1: - echo 1 - -The reason is that the compiler already transformed the 1 into "1" for -the ``echo`` statement. However, a term rewriting macro should not change the -semantics anyway. In fact they can be deactived with the ``--patterns:off`` -command line option or temporarily with the ``patterns`` pragma. - - -The ``{}`` operator -~~~~~~~~~~~~~~~~~~~ - -A pattern expression can be bound to a pattern parameter via the ``expr{param}`` -notation: - -.. code-block:: nimrod - template t{(0|1|2){x}}(x: expr): expr = x+1 - let a = 1 - # outputs 2: - echo a - - -The ``~`` operator -~~~~~~~~~~~~~~~~~~ - -The ``~`` operator is the **not** operator in patterns: - -.. code-block:: nimrod - template t{x = (~x){y} and (~x){z}}(x, y, z: bool): stmt = - x = y - if x: x = z - - var - a = false - b = true - c = false - a = b and c - echo a - - -The ``*`` operator -~~~~~~~~~~~~~~~~~~ - -The ``*`` operator can *flatten* a nested binary expression like ``a & b & c`` -to ``&(a, b, c)``: - -.. code-block:: nimrod - var - calls = 0 - - proc `&&`(s: varargs[string]): string = - result = s[0] - for i in 1..len(s)-1: result.add s[i] - inc calls - - template optConc{ `&&` * a }(a: string): expr = &&a - - let space = " " - echo "my" && (space & "awe" && "some " ) && "concat" - - # check that it's been optimized properly: - doAssert calls == 1 - - -The second operator of `*` must be a parameter; it is used to gather all the -arguments. The expression ``"my" && (space & "awe" && "some " ) && "concat"`` -is passed to ``optConc`` in ``a`` as a special list (of kind ``nkArgList``) -which is flattened into a call expression; thus the invocation of ``optConc`` -produces: - -.. code-block:: nimrod - `&&`("my", space & "awe", "some ", "concat") - - -The ``**`` operator -~~~~~~~~~~~~~~~~~~~ - -The ``**`` is much like the ``*`` operator, except that it gathers not only -all the arguments, but also the matched operators in reverse polish notation: - -.. code-block:: nimrod - import macros - - type - TMatrix = object - dummy: int - - proc `*`(a, b: TMatrix): TMatrix = discard - proc `+`(a, b: TMatrix): TMatrix = discard - proc `-`(a, b: TMatrix): TMatrix = discard - proc `$`(a: TMatrix): string = result = $a.dummy - proc mat21(): TMatrix = - result.dummy = 21 - - macro optM{ (`+`|`-`|`*`) ** a }(a: TMatrix): expr = - echo treeRepr(a) - result = newCall(bindSym"mat21") - - var x, y, z: TMatrix - - echo x + y * z - x - -This passes the expression ``x + y * z - x`` to the ``optM`` macro as -an ``nnkArgList`` node containing:: - - Arglist - Sym "x" - Sym "y" - Sym "z" - Sym "*" - Sym "+" - Sym "x" - Sym "-" - -(Which is the reverse polish notation of ``x + y * z - x``.) - - -Parameters ----------- - -Parameters in a pattern are type checked in the matching process. If a -parameter is of the type ``varargs`` it is treated specially and it can match -0 or more arguments in the AST to be matched against: - -.. code-block:: nimrod - template optWrite{ - write(f, x) - ((write|writeln){w})(f, y) - }(x, y: varargs[expr], f: TFile, w: expr) = - w(f, x, y) - - - -Example: Partial evaluation ---------------------------- - -The following example shows how some simple partial evaluation can be -implemented with term rewriting: - -.. code-block:: nimrod - proc p(x, y: int; cond: bool): int = - result = if cond: x + y else: x - y - - template optP1{p(x, y, true)}(x, y: expr): expr = x + y - template optP2{p(x, y, false)}(x, y: expr): expr = x - y - - -Example: Hoisting ------------------ - -The following example shows how some form of hoisting can be implemented: - -.. code-block:: nimrod - import pegs - - template optPeg{peg(pattern)}(pattern: string{lit}): TPeg = - var gl {.global, gensym.} = peg(pattern) - gl - - for i in 0 .. 3: - echo match("(a b c)", peg"'(' @ ')'") - echo match("W_HI_Le", peg"\y 'while'") - -The ``optPeg`` template optimizes the case of a peg constructor with a string -literal, so that the pattern will only be parsed once at program startup and -stored in a global ``gl`` which is then re-used. This optimization is called -hoisting because it is comparable to classical loop hoisting. - - -AST based overloading -===================== - -Parameter constraints can also be used for ordinary routine parameters; these -constraints affect ordinary overloading resolution then: - -.. code-block:: nimrod - proc optLit(a: string{lit|`const`}) = - echo "string literal" - proc optLit(a: string) = - echo "no string literal" - - const - constant = "abc" - - var - variable = "xyz" - - optLit("literal") - optLit(constant) - optLit(variable) - -However, the constraints ``alias`` and ``noalias`` are not available in -ordinary routines. - - -Move optimization ------------------ - -The ``call`` constraint is particularly useful to implement a move -optimization for types that have copying semantics: - -.. code-block:: nimrod - proc `[]=`*(t: var TTable, key: string, val: string) = - ## puts a (key, value)-pair into `t`. The semantics of string require - ## a copy here: - let idx = findInsertionPosition(key) - t[idx] = key - t[idx] = val - - proc `[]=`*(t: var TTable, key: string{call}, val: string{call}) = - ## puts a (key, value)-pair into `t`. Optimized version that knows that - ## the strings are unique and thus don't need to be copied: - let idx = findInsertionPosition(key) - shallowCopy t[idx], key - shallowCopy t[idx], val - - var t: TTable - # overloading resolution ensures that the optimized []= is called here: - t[f()] = g() - - - -Modules -======= -Nimrod supports splitting a program into pieces by a module concept. -Each module needs to be in its own file and has its own `namespace`:idx:. -Modules enable `information hiding`:idx: and `separate compilation`:idx:. -A module may gain access to symbols of another module by the `import`:idx: -statement. `Recursive module dependencies`:idx: are allowed, but slightly -subtle. Only top-level symbols that are marked with an asterisk (``*``) are -exported. - -The algorithm for compiling modules is: - -- compile the whole module as usual, following import statements recursively - -- if there is a cycle only import the already parsed symbols (that are - exported); if an unknown identifier occurs then abort - -This is best illustrated by an example: - -.. code-block:: nimrod - # Module A - type - T1* = int # Module A exports the type ``T1`` - import B # the compiler starts parsing B - - proc main() = - var i = p(3) # works because B has been parsed completely here - - main() - - -.. code-block:: nimrod - # Module B - import A # A is not parsed here! Only the already known symbols - # of A are imported. - - proc p*(x: A.T1): A.T1 = - # this works because the compiler has already - # added T1 to A's interface symbol table - result = x + 1 - - -Import statement -~~~~~~~~~~~~~~~~ - -After the ``import`` statement a list of module names can follow or a single -module name followed by an ``except`` to prevent some symbols to be imported: - -.. code-block:: nimrod - import strutils except `%` - - # doesn't work then: - echo "$1" % "abc" - - -Module names in imports -~~~~~~~~~~~~~~~~~~~~~~~ - -A module alias can be introduced via the ``as`` keyword: - -.. code-block:: nimrod - import strutils as su, sequtils as qu - - echo su.format("$1", "lalelu") - -The original module name is then not accessible. The -notations ``path/to/module`` or ``path.to.module`` or ``"path/to/module"`` -can be used to refer to a module in subdirectories: - -.. code-block:: nimrod - import lib.pure.strutils, lib/pure/os, "lib/pure/times" - -Note that the module name is still ``strutils`` and not ``lib.pure.strutils`` -and so one **cannot** do: - -.. code-block:: nimrod - import lib.pure.strutils - echo lib.pure.strutils - -Likewise the following does not make sense as the name is ``strutils`` already: - -.. code-block:: nimrod - import lib.pure.strutils as strutils - - -From import statement -~~~~~~~~~~~~~~~~~~~~~ - -After the ``from`` statement a module name follows followed by -an ``import`` to list the symbols one likes to use without explict -full qualification: - -.. code-block:: nimrod - from strutils import `%` - - echo "$1" % "abc" - # always possible: full qualification: - echo strutils.replace("abc", "a", "z") - -It's also possible to use ``from module import nil`` if one wants to import -the module but wants to enforce fully qualified access to every symbol -in ``module``. - - -Export statement -~~~~~~~~~~~~~~~~ - -An ``export`` statement can be used for symbol fowarding so that client -modules don't need to import a module's dependencies: - -.. code-block:: nimrod - # module B - type TMyObject* = object - -.. code-block:: nimrod - # module A - import B - export B.TMyObject - - proc `$`*(x: TMyObject): string = "my object" - - -.. code-block:: nimrod - # module C - import A - - # B.TMyObject has been imported implicitly here: - var x: TMyObject - echo($x) - - -Scope rules ------------ -Identifiers are valid from the point of their declaration until the end of -the block in which the declaration occurred. The range where the identifier -is known is the scope of the identifier. The exact scope of an -identifier depends on the way it was declared. - -Block scope -~~~~~~~~~~~ -The *scope* of a variable declared in the declaration part of a block -is valid from the point of declaration until the end of the block. If a -block contains a second block, in which the identifier is redeclared, -then inside this block, the second declaration will be valid. Upon -leaving the inner block, the first declaration is valid again. An -identifier cannot be redefined in the same block, except if valid for -procedure or iterator overloading purposes. - - -Tuple or object scope -~~~~~~~~~~~~~~~~~~~~~ -The field identifiers inside a tuple or object definition are valid in the -following places: - -* To the end of the tuple/object definition. -* Field designators of a variable of the given tuple/object type. -* In all descendant types of the object type. - -Module scope -~~~~~~~~~~~~ -All identifiers of a module are valid from the point of declaration until -the end of the module. Identifiers from indirectly dependent modules are *not* -available. The `system`:idx: module is automatically imported in every other -module. - -If a module imports an identifier by two different modules, each occurrence of -the identifier has to be qualified, unless it is an overloaded procedure or -iterator in which case the overloading resolution takes place: - -.. code-block:: nimrod - # Module A - var x*: string - -.. code-block:: nimrod - # Module B - var x*: int - -.. code-block:: nimrod - # Module C - import A, B - write(stdout, x) # error: x is ambiguous - write(stdout, A.x) # no error: qualifier used - - var x = 4 - write(stdout, x) # not ambiguous: uses the module C's x - - -Compiler Messages -================= - -The Nimrod compiler emits different kinds of messages: `hint`:idx:, -`warning`:idx:, and `error`:idx: messages. An *error* message is emitted if -the compiler encounters any static error. - - -Pragmas -======= - -Pragmas are Nimrod's method to give the compiler additional information / -commands without introducing a massive number of new keywords. Pragmas are -processed on the fly during semantic checking. Pragmas are enclosed in the -special ``{.`` and ``.}`` curly brackets. Pragmas are also often used as a -first implementation to play with a language feature before a nicer syntax -to access the feature becomes available. - - -deprecated pragma ------------------ - -The deprecated pragma is used to mark a symbol as deprecated: - -.. code-block:: nimrod - proc p() {.deprecated.} - var x {.deprecated.}: char - -It can also be used as a statement. Then it takes a list of *renamings*. The -upcoming ``nimfix`` tool can automatically update the code and perform these -renamings: - -.. code-block:: nimrod - type - File = object - Stream = ref object - {.deprecated: [TFile: File, PStream: Stream].} - - - -noSideEffect pragma -------------------- -The ``noSideEffect`` pragma is used to mark a proc/iterator to have no side -effects. This means that the proc/iterator only changes locations that are -reachable from its parameters and the return value only depends on the -arguments. If none of its parameters have the type ``var T`` -or ``ref T`` or ``ptr T`` this means no locations are modified. It is a static -error to mark a proc/iterator to have no side effect if the compiler cannot -verify this. - -As a special semantic rule, the built-in `debugEcho `_ -pretends to be free of side effects, so that it can be used for debugging -routines marked as ``noSideEffect``. - -**Future directions**: ``func`` may become a keyword and syntactic sugar for a -proc with no side effects: - -.. code-block:: nimrod - func `+` (x, y: int): int - - -destructor pragma ------------------ - -The ``destructor`` pragma is used to mark a proc to act as a type destructor. -Its usage is deprecated, use the ``override`` pragma instead. -See `type bound operations`_. - - -override pragma ---------------- - -See `type bound operations`_ instead. - -procvar pragma --------------- -The ``procvar`` pragma is used to mark a proc that it can be passed to a -procedural variable. - - -compileTime pragma ------------------- -The ``compileTime`` pragma is used to mark a proc to be used at compile -time only. No code will be generated for it. Compile time procs are useful -as helpers for macros. - - -noReturn pragma ---------------- -The ``noreturn`` pragma is used to mark a proc that never returns. - - -Acyclic pragma --------------- -The ``acyclic`` pragma can be used for object types to mark them as acyclic -even though they seem to be cyclic. This is an **optimization** for the garbage -collector to not consider objects of this type as part of a cycle: - -.. code-block:: nimrod - type - PNode = ref TNode - TNode {.acyclic, final.} = object - left, right: PNode - data: string - -In the example a tree structure is declared with the ``TNode`` type. Note that -the type definition is recursive and the GC has to assume that objects of -this type may form a cyclic graph. The ``acyclic`` pragma passes the -information that this cannot happen to the GC. If the programmer uses the -``acyclic`` pragma for data types that are in reality cyclic, the GC may leak -memory, but nothing worse happens. - -**Future directions**: The ``acyclic`` pragma may become a property of a -``ref`` type: - -.. code-block:: nimrod - type - PNode = acyclic ref TNode - TNode = object - left, right: PNode - data: string - - -Final pragma ------------- -The ``final`` pragma can be used for an object type to specify that it -cannot be inherited from. - - -shallow pragma --------------- -The ``shallow`` pragma affects the semantics of a type: The compiler is -allowed to make a shallow copy. This can cause serious semantic issues and -break memory safety! However, it can speed up assignments considerably, -because the semantics of Nimrod require deep copying of sequences and strings. -This can be expensive, especially if sequences are used to build a tree -structure: - -.. code-block:: nimrod - type - TNodeKind = enum nkLeaf, nkInner - TNode {.final, shallow.} = object - case kind: TNodeKind - of nkLeaf: - strVal: string - of nkInner: - children: seq[TNode] - - -Pure pragma ------------ -An object type can be marked with the ``pure`` pragma so that its type -field which is used for runtime type identification is omitted. This is -necessary for binary compatibility with other compiled languages. - - -AsmNoStackFrame pragma ----------------------- -A proc can be marked with the ``AsmNoStackFrame`` pragma to tell the compiler -it should not generate a stack frame for the proc. There are also no exit -statements like ``return result;`` generated and the generated C function is -declared as ``__declspec(naked)`` or ``__attribute__((naked))`` (depending on -the used C compiler). - -**Note**: This pragma should only be used by procs which consist solely of -assembler statements. - -error pragma ------------- -The ``error`` pragma is used to make the compiler output an error message -with the given content. Compilation does not necessarily abort after an error -though. - -The ``error`` pragma can also be used to -annotate a symbol (like an iterator or proc). The *usage* of the symbol then -triggers a compile-time error. This is especially useful to rule out that some -operation is valid due to overloading and type conversions: - -.. code-block:: nimrod - ## check that underlying int values are compared and not the pointers: - proc `==`(x, y: ptr int): bool {.error.} - - -fatal pragma ------------- -The ``fatal`` pragma is used to make the compiler output an error message -with the given content. In contrast to the ``error`` pragma, compilation -is guaranteed to be aborted by this pragma. Example: - -.. code-block:: nimrod - when not defined(objc): - {.fatal: "Compile this program with the objc command!".} - -warning pragma --------------- -The ``warning`` pragma is used to make the compiler output a warning message -with the given content. Compilation continues after the warning. - -hint pragma ------------ -The ``hint`` pragma is used to make the compiler output a hint message with -the given content. Compilation continues after the hint. - -line pragma ------------ -The ``line`` pragma can be used to affect line information of the annotated -statement as seen in stack backtraces: - -.. code-block:: nimrod - - template myassert*(cond: expr, msg = "") = - if not cond: - # change run-time line information of the 'raise' statement: - {.line: InstantiationInfo().}: - raise newException(EAssertionFailed, msg) - -If the ``line`` pragma is used with a parameter, the parameter needs be a -``tuple[filename: string, line: int]``. If it is used without a parameter, -``system.InstantiationInfo()`` is used. - - -linearScanEnd pragma --------------------- -The ``linearScanEnd`` pragma can be used to tell the compiler how to -compile a Nimrod `case`:idx: statement. Syntactically it has to be used as a -statement: - -.. code-block:: nimrod - case myInt - of 0: - echo "most common case" - of 1: - {.linearScanEnd.} - echo "second most common case" - of 2: echo "unlikely: use branch table" - else: echo "unlikely too: use branch table for ", myInt - -In the example, the case branches ``0`` and ``1`` are much more common than -the other cases. Therefore the generated assembler code should test for these -values first, so that the CPU's branch predictor has a good chance to succeed -(avoiding an expensive CPU pipeline stall). The other cases might be put into a -jump table for O(1) overhead, but at the cost of a (very likely) pipeline -stall. - -The ``linearScanEnd`` pragma should be put into the last branch that should be -tested against via linear scanning. If put into the last branch of the -whole ``case`` statement, the whole ``case`` statement uses linear scanning. - - -computedGoto pragma -------------------- -The ``computedGoto`` pragma can be used to tell the compiler how to -compile a Nimrod `case`:idx: in a ``while true`` statement. -Syntactically it has to be used as a statement inside the loop: - -.. code-block:: nimrod - - type - MyEnum = enum - enumA, enumB, enumC, enumD, enumE - - proc vm() = - var instructions: array [0..100, MyEnum] - instructions[2] = enumC - instructions[3] = enumD - instructions[4] = enumA - instructions[5] = enumD - instructions[6] = enumC - instructions[7] = enumA - instructions[8] = enumB - - instructions[12] = enumE - var pc = 0 - while true: - {.computedGoto.} - let instr = instructions[pc] - case instr - of enumA: - echo "yeah A" - of enumC, enumD: - echo "yeah CD" - of enumB: - echo "yeah B" - of enumE: - break - inc(pc) - - vm() - -As the example shows ``computedGoto`` is mostly useful for interpreters. If -the underlying backend (C compiler) does not support the computed goto -extension the pragma is simply ignored. - - -unroll pragma -------------- -The ``unroll`` pragma can be used to tell the compiler that it should unroll -a `for`:idx: or `while`:idx: loop for runtime efficiency: - -.. code-block:: nimrod - proc searchChar(s: string, c: char): int = - for i in 0 .. s.high: - {.unroll: 4.} - if s[i] == c: return i - result = -1 - -In the above example, the search loop is unrolled by a factor 4. The unroll -factor can be left out too; the compiler then chooses an appropriate unroll -factor. - -**Note**: Currently the compiler recognizes but ignores this pragma. - - -immediate pragma ----------------- - -See `Ordinary vs immediate templates`_. - - -compilation option pragmas --------------------------- -The listed pragmas here can be used to override the code generation options -for a proc/method/converter. - -The implementation currently provides the following possible options (various -others may be added later). - -=============== =============== ============================================ -pragma allowed values description -=============== =============== ============================================ -checks on|off Turns the code generation for all runtime - checks on or off. -boundChecks on|off Turns the code generation for array bound - checks on or off. -overflowChecks on|off Turns the code generation for over- or - underflow checks on or off. -nilChecks on|off Turns the code generation for nil pointer - checks on or off. -assertions on|off Turns the code generation for assertions - on or off. -warnings on|off Turns the warning messages of the compiler - on or off. -hints on|off Turns the hint messages of the compiler - on or off. -optimization none|speed|size Optimize the code for speed or size, or - disable optimization. -patterns on|off Turns the term rewriting templates/macros - on or off. -callconv cdecl|... Specifies the default calling convention for - all procedures (and procedure types) that - follow. -=============== =============== ============================================ - -Example: - -.. code-block:: nimrod - {.checks: off, optimization: speed.} - # compile without runtime checks and optimize for speed - - -push and pop pragmas --------------------- -The `push/pop`:idx: pragmas are very similar to the option directive, -but are used to override the settings temporarily. Example: - -.. code-block:: nimrod - {.push checks: off.} - # compile this section without runtime checks as it is - # speed critical - # ... some code ... - {.pop.} # restore old settings - - -register pragma ---------------- -The ``register`` pragma is for variables only. It declares the variable as -``register``, giving the compiler a hint that the variable should be placed -in a hardware register for faster access. C compilers usually ignore this -though and for good reasons: Often they do a better job without it anyway. - -In highly specific cases (a dispatch loop of an bytecode interpreter for -example) it may provide benefits, though. - - -global pragma -------------- -The ``global`` pragma can be applied to a variable within a proc to instruct -the compiler to store it in a global location and initialize it once at program -startup. - -.. code-block:: nimrod - proc isHexNumber(s: string): bool = - var pattern {.global.} = re"[0-9a-fA-F]+" - result = s.match(pattern) - -When used within a generic proc, a separate unique global variable will be -created for each instantiation of the proc. The order of initialization of -the created global variables within a module is not defined, but all of them -will be initialized after any top-level variables in their originating module -and before any variable in a module that imports it. - -DeadCodeElim pragma -------------------- -The ``deadCodeElim`` pragma only applies to whole modules: It tells the -compiler to activate (or deactivate) dead code elimination for the module the -pragma appears in. - -The ``--deadCodeElim:on`` command line switch has the same effect as marking -every module with ``{.deadCodeElim:on}``. However, for some modules such as -the GTK wrapper it makes sense to *always* turn on dead code elimination - -no matter if it is globally active or not. - -Example: - -.. code-block:: nimrod - {.deadCodeElim: on.} - - -.. - NoForward pragma - ---------------- - The ``noforward`` pragma can be used to turn on and off a special compilation - mode that to large extent eliminates the need for forward declarations. In this - mode, the proc definitions may appear out of order and the compiler will postpone - their semantic analysis and compilation until it actually needs to generate code - using the definitions. In this regard, this mode is similar to the modus operandi - of dynamic scripting languages, where the function calls are not resolved until - the code is executed. Here is the detailed algorithm taken by the compiler: - - 1. When a callable symbol is first encountered, the compiler will only note the - symbol callable name and it will add it to the appropriate overload set in the - current scope. At this step, it won't try to resolve any of the type expressions - used in the signature of the symbol (so they can refer to other not yet defined - symbols). - - 2. When a top level call is encountered (usually at the very end of the module), - the compiler will try to determine the actual types of all of the symbols in the - matching overload set. This is a potentially recursive process as the signatures - of the symbols may include other call expressions, whoose types will be resolved - at this point too. - - 3. Finally, after the best overload is picked, the compiler will start compiling - the body of the respective symbol. This in turn will lead the compiler to discover - more call expresions that need to be resolved and steps 2 and 3 will be repeated - as necessary. - - Please note that if a callable symbol is never used in this scenario, its body - will never be compiled. This is the default behavior leading to best compilation - times, but if exhaustive compilation of all definitions is required, using - ``nimrod check`` provides this option as well. - - Example: - - .. code-block:: nimrod - - {.noforward: on.} - - proc foo(x: int) = - bar x - - proc bar(x: int) = - echo x - - foo(10) - - -Pragma pragma -------------- - -The ``pragma`` pragma can be used to declare user defined pragmas. This is -useful because Nimrod's templates and macros do not affect pragmas. User -defined pragmas are in a different module-wide scope than all other symbols. -They cannot be imported from a module. - -Example: - -.. code-block:: nimrod - when appType == "lib": - {.pragma: rtl, exportc, dynlib, cdecl.} - else: - {.pragma: rtl, importc, dynlib: "client.dll", cdecl.} - - proc p*(a, b: int): int {.rtl.} = - result = a+b - -In the example a new pragma named ``rtl`` is introduced that either imports -a symbol from a dynamic library or exports the symbol for dynamic library -generation. - - -Disabling certain messages --------------------------- -Nimrod generates some warnings and hints ("line too long") that may annoy the -user. A mechanism for disabling certain messages is provided: Each hint -and warning message contains a symbol in brackets. This is the message's -identifier that can be used to enable or disable it: - -.. code-block:: Nimrod - {.hint[LineTooLong]: off.} # turn off the hint about too long lines - -This is often better than disabling all warnings at once. - - -Foreign function interface -========================== - -Nimrod's `FFI`:idx: (foreign function interface) is extensive and only the -parts that scale to other future backends (like the LLVM/JavaScript backends) -are documented here. - - -Importc pragma --------------- -The ``importc`` pragma provides a means to import a proc or a variable -from C. The optional argument is a string containing the C identifier. If -the argument is missing, the C name is the Nimrod identifier *exactly as -spelled*: - -.. code-block:: - proc printf(formatstr: cstring) {.header: "", importc: "printf", varargs.} - -Note that this pragma is somewhat of a misnomer: Other backends will provide -the same feature under the same name. Also, if one is interfacing with C++ -the `ImportCpp pragma `_ and -interfacing with Objective-C the `ImportObjC pragma -`_ can be used. - - -Exportc pragma --------------- -The ``exportc`` pragma provides a means to export a type, a variable, or a -procedure to C. Enums and constants can't be exported. The optional argument -is a string containing the C identifier. If the argument is missing, the C -name is the Nimrod identifier *exactly as spelled*: - -.. code-block:: Nimrod - proc callme(formatstr: cstring) {.exportc: "callMe", varargs.} - -Note that this pragma is somewhat of a misnomer: Other backends will provide -the same feature under the same name. - - -Extern pragma -------------- -Like ``exportc`` or ``importc``, the ``extern`` pragma affects name -mangling. The string literal passed to ``extern`` can be a format string: - -.. code-block:: Nimrod - proc p(s: string) {.extern: "prefix$1".} = - echo s - -In the example the external name of ``p`` is set to ``prefixp``. - - -Bycopy pragma -------------- - -The ``bycopy`` pragma can be applied to an object or tuple type and -instructs the compiler to pass the type by value to procs: - -.. code-block:: nimrod - type - TVector {.bycopy, pure.} = object - x, y, z: float - - -Byref pragma ------------- - -The ``byref`` pragma can be applied to an object or tuple type and instructs -the compiler to pass the type by reference (hidden pointer) to procs. - - -Varargs pragma --------------- -The ``varargs`` pragma can be applied to procedures only (and procedure -types). It tells Nimrod that the proc can take a variable number of parameters -after the last specified parameter. Nimrod string values will be converted to C -strings automatically: - -.. code-block:: Nimrod - proc printf(formatstr: cstring) {.nodecl, varargs.} - - printf("hallo %s", "world") # "world" will be passed as C string - - -Union pragma ------------- -The ``union`` pragma can be applied to any ``object`` type. It means all -of the object's fields are overlaid in memory. This produces a ``union`` -instead of a ``struct`` in the generated C/C++ code. The object declaration -then must not use inheritance or any GC'ed memory but this is currently not -checked. - -**Future directions**: GC'ed memory should be allowed in unions and the GC -should scan unions conservatively. - -Packed pragma -------------- -The ``packed`` pragma can be applied to any ``object`` type. It ensures -that the fields of an object are packed back-to-back in memory. It is useful -to store packets or messages from/to network or hardware drivers, and for -interoperability with C. Combining packed pragma with inheritance is not -defined, and it should not be used with GC'ed memory (ref's). - -**Future directions**: Using GC'ed memory in packed pragma will result in -compile-time error. Usage with inheritance should be defined and documented. - -Unchecked pragma ----------------- -The ``unchecked`` pragma can be used to mark a named array as ``unchecked`` -meaning its bounds are not checked. This is often useful when one wishes to -implement his own flexibly sized arrays. Additionally an unchecked array is -translated into a C array of undetermined size: - -.. code-block:: nimrod - type - ArrayPart{.unchecked.} = array[0..0, int] - MySeq = object - len, cap: int - data: ArrayPart - -Produces roughly this C code: - -.. code-block:: C - typedef struct { - NI len; - NI cap; - NI data[]; - } MySeq; - -The bounds checking done at compile time is not disabled for now, so to access -``s.data[C]`` (where ``C`` is a constant) the array's index needs needs to -include ``C``. - -The base type of the unchecked array may not contain any GC'ed memory but this -is currently not checked. - -**Future directions**: GC'ed memory should be allowed in unchecked arrays and -there should be an explicit annotation of how the GC is to determine the -runtime size of the array. - - -Dynlib pragma for import ------------------------- -With the ``dynlib`` pragma a procedure or a variable can be imported from -a dynamic library (``.dll`` files for Windows, ``lib*.so`` files for UNIX). -The non-optional argument has to be the name of the dynamic library: - -.. code-block:: Nimrod - proc gtk_image_new(): PGtkWidget - {.cdecl, dynlib: "libgtk-x11-2.0.so", importc.} - -In general, importing a dynamic library does not require any special linker -options or linking with import libraries. This also implies that no *devel* -packages need to be installed. - -The ``dynlib`` import mechanism supports a versioning scheme: - -.. code-block:: nimrod - proc Tcl_Eval(interp: pTcl_Interp, script: cstring): int {.cdecl, - importc, dynlib: "libtcl(|8.5|8.4|8.3).so.(1|0)".} - -At runtime the dynamic library is searched for (in this order):: - - libtcl.so.1 - libtcl.so.0 - libtcl8.5.so.1 - libtcl8.5.so.0 - libtcl8.4.so.1 - libtcl8.4.so.0 - libtcl8.3.so.1 - libtcl8.3.so.0 - -The ``dynlib`` pragma supports not only constant strings as argument but also -string expressions in general: - -.. code-block:: nimrod - import os - - proc getDllName: string = - result = "mylib.dll" - if existsFile(result): return - result = "mylib2.dll" - if existsFile(result): return - quit("could not load dynamic library") - - proc myImport(s: cstring) {.cdecl, importc, dynlib: getDllName().} - -**Note**: Patterns like ``libtcl(|8.5|8.4).so`` are only supported in constant -strings, because they are precompiled. - -**Note**: Passing variables to the ``dynlib`` pragma will fail at runtime -because of order of initialization problems. - -**Note**: A ``dynlib`` import can be overriden with -the ``--dynlibOverride:name`` command line option. The Compiler User Guide -contains further information. - - -Dynlib pragma for export ------------------------- - -With the ``dynlib`` pragma a procedure can also be exported to -a dynamic library. The pragma then has no argument and has to be used in -conjunction with the ``exportc`` pragma: - -.. code-block:: Nimrod - proc exportme(): int {.cdecl, exportc, dynlib.} - -This is only useful if the program is compiled as a dynamic library via the -``--app:lib`` command line option. - - -Threads -======= - -To enable thread support the ``--threads:on`` command line switch needs to -be used. The ``system`` module then contains several threading primitives. -See the `threads `_ and `channels `_ modules -for the thread API. - -Nimrod's memory model for threads is quite different than that of other common -programming languages (C, Pascal, Java): Each thread has its own (garbage -collected) heap and sharing of memory is restricted to global variables. This -helps to prevent race conditions. GC efficiency is improved quite a lot, -because the GC never has to stop other threads and see what they reference. -Memory allocation requires no lock at all! This design easily scales to massive -multicore processors that will become the norm in the future. - - -Thread pragma -------------- - -A proc that is executed as a new thread of execution should be marked by the -``thread`` pragma. The compiler checks procedures marked as ``thread`` for -violations of the `no heap sharing restriction`:idx:\: This restriction implies -that it is invalid to construct a data structure that consists of memory -allocated from different (thread local) heaps. - -A thread proc is passed to ``createThread`` or ``spawn`` and invoked -indirectly; so the ``thread`` pragma implies ``procvar``. - - -GC safety ---------- - -We call a proc ``p`` `GC safe`:idx: when it doesn't access any global variable -that contains GC'ed memory (``string``, ``seq``, ``ref`` or a closure) either -directly or indirectly through a call to a GC unsafe proc. - -The `gcsafe`:idx: annotation can be used to mark a proc to be gcsafe -otherwise this property is inferred by the compiler. Note that ``noSideEfect`` -implies ``gcsafe``. The only way to create a thread is via ``spawn`` or -``createThead``. ``spawn`` is usually the preferable method. Either way -the invoked proc must not use ``var`` parameters nor must any of its parameters -contain a ``ref`` or ``closure`` type. This enforces -the *no heap sharing restriction*. - -Routines that are imported from C are always assumed to be ``gcsafe``. -To enable the GC-safety checking the ``--threadAnalysis:on`` command line -switch must be used. This is a temporary workaround to ease the porting effort -from old code to the new threading model. In the future the thread analysis -will always be performed. - - -Future directions: - -- For structured fork&join parallelism more efficient parameter passing can - be performed and much more can be proven safe. -- A shared GC'ed heap is planned. - - -Threadvar pragma ----------------- - -A global variable can be marked with the ``threadvar`` pragma; it is -a `thread-local`:idx: variable then: - -.. code-block:: nimrod - var checkpoints* {.threadvar.}: seq[string] - -Due to implementation restrictions thread local variables cannot be -initialized within the ``var`` section. (Every thread local variable needs to -be replicated at thread creation.) - - -Threads and exceptions ----------------------- - -The interaction between threads and exceptions is simple: A *handled* exception -in one thread cannot affect any other thread. However, an *unhandled* -exception in one thread terminates the whole *process*! - - -Spawn ------ - -Nimrod has a builtin thread pool that can be used for CPU intensive tasks. For -IO intensive tasks the upcoming ``async`` and ``await`` features should be -used instead. `spawn`:idx: is used to pass a task to the thread pool: - -.. code-block:: nimrod - proc processLine(line: string) = - # do some heavy lifting here: - discard - - for x in lines("myinput.txt"): - spawn processLine(x) - sync() - -Currently the expression that ``spawn`` takes is however quite restricted: - -* It must be a call expression ``f(a, ...)``. -* ``f`` must be ``gcsafe``. -* ``f`` must not have the calling convention ``closure``. -* ``f``'s parameters may not be of type ``var``. - This means one has to use raw ``ptr``'s for data passing reminding the - programmer to be careful. -* ``ref`` parameters are deeply copied which is a subtle semantic change and - can cause performance problems but ensures memory safety. -* For *safe* data exchange between ``f`` and the caller a global ``TChannel`` - needs to be used. Other means will be provided soon. - - - -Taint mode -========== - -The Nimrod compiler and most parts of the standard library support -a taint mode. Input strings are declared with the `TaintedString`:idx: -string type declared in the ``system`` module. - -If the taint mode is turned on (via the ``--taintMode:on`` command line -option) it is a distinct string type which helps to detect input -validation errors: - -.. code-block:: nimrod - echo "your name: " - var name: TaintedString = stdin.readline - # it is safe here to output the name without any input validation, so - # we simply convert `name` to string to make the compiler happy: - echo "hi, ", name.string - -If the taint mode is turned off, ``TaintedString`` is simply an alias for -``string``. - +.. include:: manual/about.txt +.. include:: manual/definitions.txt +.. include:: manual/lexing.txt +.. include:: manual/syntax.txt +.. include:: manual/types.txt +.. include:: manual/type_rel.txt +.. include:: manual/stmts.txt +.. include:: manual/procs.txt +.. include:: manual/type_sections.txt +.. include:: manual/exceptions.txt +.. include:: manual/effects.txt +.. include:: manual/generics.txt +.. include:: manual/templates.txt +.. include:: manual/typedesc.txt +.. include:: manual/special_ops.txt +.. include:: manual/type_bound_ops.txt +.. include:: manual/trmacros.txt +.. include:: manual/modules.txt +.. include:: manual/compiler_msgs.txt +.. include:: manual/pragmas.txt +.. include:: manual/ffi.txt +.. include:: manual/threads.txt +.. include:: manual/locking.txt +.. include:: manual/taint.txt diff --git a/doc/manual/about.txt b/doc/manual/about.txt new file mode 100644 index 000000000..13307279b --- /dev/null +++ b/doc/manual/about.txt @@ -0,0 +1,37 @@ +About this document +=================== + +**Note**: This document is a draft! Several of Nim's features need more +precise wording. This manual will evolve into a proper specification some +day. + +This document describes the lexis, the syntax, and the semantics of Nim. + +The language constructs are explained using an extended BNF, in +which ``(a)*`` means 0 or more ``a``'s, ``a+`` means 1 or more ``a``'s, and +``(a)?`` means an optional *a*. Parentheses may be used to group elements. + +``&`` is the lookahead operator; ``&a`` means that an ``a`` is expected but +not consumed. It will be consumed in the following rule. + +The ``|``, ``/`` symbols are used to mark alternatives and have the lowest +precedence. ``/`` is the ordered choice that requires the parser to try the +alternatives in the given order. ``/`` is often used to ensure the grammar +is not ambiguous. + +Non-terminals start with a lowercase letter, abstract terminal symbols are in +UPPERCASE. Verbatim terminal symbols (including keywords) are quoted +with ``'``. An example:: + + ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)? + +The binary ``^*`` operator is used as a shorthand for 0 or more occurances +separated by its second argument; likewise ``^+`` means 1 or more +occurances: ``a ^+ b`` is short for ``a (b a)*`` +and ``a ^* b`` is short for ``(a (b a)*)?``. Example:: + + arrayConstructor = '[' expr ^* ',' ']' + +Other parts of Nim - like scoping rules or runtime semantics are only +described in an informal manner for now. + diff --git a/doc/manual/compiler_msgs.txt b/doc/manual/compiler_msgs.txt new file mode 100644 index 000000000..3cf8417b0 --- /dev/null +++ b/doc/manual/compiler_msgs.txt @@ -0,0 +1,7 @@ +Compiler Messages +================= + +The Nim compiler emits different kinds of messages: `hint`:idx:, +`warning`:idx:, and `error`:idx: messages. An *error* message is emitted if +the compiler encounters any static error. + diff --git a/doc/manual/definitions.txt b/doc/manual/definitions.txt new file mode 100644 index 000000000..9eb20f70f --- /dev/null +++ b/doc/manual/definitions.txt @@ -0,0 +1,49 @@ + +Definitions +=========== + +A Nim program specifies a computation that acts on a memory consisting of +components called `locations`:idx:. A variable is basically a name for a +location. Each variable and location is of a certain `type`:idx:. The +variable's type is called `static type`:idx:, the location's type is called +`dynamic type`:idx:. If the static type is not the same as the dynamic type, +it is a super-type or subtype of the dynamic type. + +An `identifier`:idx: is a symbol declared as a name for a variable, type, +procedure, etc. The region of the program over which a declaration applies is +called the `scope`:idx: of the declaration. Scopes can be nested. The meaning +of an identifier is determined by the smallest enclosing scope in which the +identifier is declared unless overloading resolution rules suggest otherwise. + +An expression specifies a computation that produces a value or location. +Expressions that produce locations are called `l-values`:idx:. An l-value +can denote either a location or the value the location contains, depending on +the context. Expressions whose values can be determined statically are called +`constant expressions`:idx:; they are never l-values. + +A `static error`:idx: is an error that the implementation detects before +program execution. Unless explicitly classified, an error is a static error. + +A `checked runtime error`:idx: is an error that the implementation detects +and reports at runtime. The method for reporting such errors is via *raising +exceptions* or *dying with a fatal error*. However, the implementation +provides a means to disable these runtime checks. See the section pragmas_ +for details. + +Wether a checked runtime error results in an exception or in a fatal error at +runtime is implementation specific. Thus the following program is always +invalid: + +.. code-block:: nim + var a: array[0..1, char] + let i = 5 + try: + a[i] = 'N' + except IndexError: + echo "invalid index" + +An `unchecked runtime error`:idx: is an error that is not guaranteed to be +detected, and can cause the subsequent behavior of the computation to +be arbitrary. Unchecked runtime errors cannot occur if only `safe`:idx: +language features are used. + diff --git a/doc/manual/effects.txt b/doc/manual/effects.txt new file mode 100644 index 000000000..73934ab34 --- /dev/null +++ b/doc/manual/effects.txt @@ -0,0 +1,129 @@ +Effect system +============= + +Exception tracking +------------------ + +Nim supports exception tracking. The `raises`:idx: pragma can be used +to explicitly define which exceptions a proc/iterator/method/converter is +allowed to raise. The compiler verifies this: + +.. code-block:: nim + proc p(what: bool) {.raises: [IOError, OSError].} = + if what: raise newException(IOError, "IO") + else: raise newException(OSError, "OS") + +An empty ``raises`` list (``raises: []``) means that no exception may be raised: + +.. code-block:: nim + proc p(): bool {.raises: [].} = + try: + unsafeCall() + result = true + except: + result = false + + +A ``raises`` list can also be attached to a proc type. This affects type +compatibility: + +.. code-block:: nim + type + TCallback = proc (s: string) {.raises: [IOError].} + var + c: TCallback + + proc p(x: string) = + raise newException(OSError, "OS") + + c = p # type error + + +For a routine ``p`` the compiler uses inference rules to determine the set of +possibly raised exceptions; the algorithm operates on ``p``'s call graph: + +1. Every indirect call via some proc type ``T`` is assumed to + raise ``system.Exception`` (the base type of the exception hierarchy) and + thus any exception unless ``T`` has an explicit ``raises`` list. + However if the call is of the form ``f(...)`` where ``f`` is a parameter + of the currently analysed routine it is ignored. The call is optimistically + assumed to have no effect. Rule 2 compensates for this case. +2. Every expression of some proc type wihtin a call that is not a call + itself (and not nil) is assumed to be called indirectly somehow and thus + its raises list is added to ``p``'s raises list. +3. Every call to a proc ``q`` which has an unknown body (due to a forward + declaration or an ``importc`` pragma) is assumed to + raise ``system.Exception`` unless ``q`` has an explicit ``raises`` list. +4. Every call to a method ``m`` is assumed to + raise ``system.Exception`` unless ``m`` has an explicit ``raises`` list. +5. For every other call the analysis can determine an exact ``raises`` list. +6. For determining a ``raises`` list, the ``raise`` and ``try`` statements + of ``p`` are taken into consideration. + +Rules 1-2 ensure the following works: + +.. code-block:: nim + proc noRaise(x: proc()) {.raises: [].} = + # unknown call that might raise anything, but valid: + x() + + proc doRaise() {.raises: [IOError].} = + raise newException(IOError, "IO") + + proc use() {.raises: [].} = + # doesn't compile! Can raise IOError! + noRaise(doRaise) + +So in many cases a callback does not cause the compiler to be overly +conservative in its effect analysis. + + +Tag tracking +------------ + +The exception tracking is part of Nim's `effect system`:idx:. Raising an +exception is an *effect*. Other effects can also be defined. A user defined +effect is a means to *tag* a routine and to perform checks against this tag: + +.. code-block:: nim + type IO = object ## input/output effect + proc readLine(): string {.tags: [IO].} + + proc no_IO_please() {.tags: [].} = + # the compiler prevents this: + let x = readLine() + +A tag has to be a type name. A ``tags`` list - like a ``raises`` list - can +also be attached to a proc type. This affects type compatibility. + +The inference for tag tracking is analogous to the inference for +exception tracking. + + +Read/Write tracking +------------------- + +**Note**: Read/write tracking is not yet implemented! + +The inference for read/write tracking is analogous to the inference for +exception tracking. + + +Effects pragma +-------------- + +The ``effects`` pragma has been designed to assist the programmer with the +effects analysis. It is a statement that makes the compiler output all inferred +effects up to the ``effects``'s position: + +.. code-block:: nim + proc p(what: bool) = + if what: + raise newException(IOError, "IO") + {.effects.} + else: + raise newException(OSError, "OS") + +The compiler produces a hint message that ``IOError`` can be raised. ``OSError`` +is not listed as it cannot be raised in the branch the ``effects`` pragma +appears in. diff --git a/doc/manual/exceptions.txt b/doc/manual/exceptions.txt new file mode 100644 index 000000000..e40742e0a --- /dev/null +++ b/doc/manual/exceptions.txt @@ -0,0 +1,123 @@ +Exception handling +================== + +Try statement +------------- + +Example: + +.. code-block:: nim + # read the first two lines of a text file that should contain numbers + # and tries to add them + var + f: File + if open(f, "numbers.txt"): + try: + var a = readLine(f) + var b = readLine(f) + echo("sum: " & $(parseInt(a) + parseInt(b))) + except OverflowError: + echo("overflow!") + except ValueError: + echo("could not convert string to integer") + except IOError: + echo("IO error!") + except: + echo("Unknown exception!") + finally: + close(f) + + +The statements after the ``try`` are executed in sequential order unless +an exception ``e`` is raised. If the exception type of ``e`` matches any +listed in an ``except`` clause the corresponding statements are executed. +The statements following the ``except`` clauses are called +`exception handlers`:idx:. + +The empty `except`:idx: clause is executed if there is an exception that is +not listed otherwise. It is similar to an ``else`` clause in ``if`` statements. + +If there is a `finally`:idx: clause, it is always executed after the +exception handlers. + +The exception is *consumed* in an exception handler. However, an +exception handler may raise another exception. If the exception is not +handled, it is propagated through the call stack. This means that often +the rest of the procedure - that is not within a ``finally`` clause - +is not executed (if an exception occurs). + + +Try expression +-------------- + +Try can also be used as an expression; the type of the ``try`` branch then +needs to fit the types of ``except`` branches, but the type of the ``finally`` +branch always has to be ``void``: + +.. code-block:: nim + let x = try: parseInt("133a") + except: -1 + finally: echo "hi" + + +To prevent confusing code there is a parsing limitation; if the ``try`` +follows a ``(`` it has to be written as a one liner: + +.. code-block:: nim + let x = (try: parseInt("133a") except: -1) + + + +Defer statement +--------------- + +Instead of a ``try finally`` statement a ``defer`` statement can be used. + +Any statements following the ``defer`` in the current block will be considered +to be in an implicit try block: + +.. code-block:: nim + var f = open("numbers.txt") + defer: close(f) + f.write "abc" + f.write "def" + +Is rewritten to: + +.. code-block:: nim + var f = open("numbers.txt") + try: + f.write "abc" + f.write "def" + finally: + close(f) + +Top level ``defer`` statements are not supported +since it's unclear what such a statement should refer to. + + +Raise statement +--------------- + +Example: + +.. code-block:: nim + raise newEOS("operating system failed") + +Apart from built-in operations like array indexing, memory allocation, etc. +the ``raise`` statement is the only way to raise an exception. + +.. XXX document this better! + +If no exception name is given, the current exception is `re-raised`:idx:. The +`ReraiseError`:idx: exception is raised if there is no exception to +re-raise. It follows that the ``raise`` statement *always* raises an +exception (unless a raise hook has been provided). + + +Exception hierarchy +------------------- + +The exception tree is defined in the `system `_ module: + +.. include:: exception_hierarchy_fragment.txt diff --git a/doc/manual/ffi.txt b/doc/manual/ffi.txt new file mode 100644 index 000000000..0ad4ebba9 --- /dev/null +++ b/doc/manual/ffi.txt @@ -0,0 +1,209 @@ +Foreign function interface +========================== + +Nim's `FFI`:idx: (foreign function interface) is extensive and only the +parts that scale to other future backends (like the LLVM/JavaScript backends) +are documented here. + + +Importc pragma +-------------- +The ``importc`` pragma provides a means to import a proc or a variable +from C. The optional argument is a string containing the C identifier. If +the argument is missing, the C name is the Nim identifier *exactly as +spelled*: + +.. code-block:: + proc printf(formatstr: cstring) {.header: "", importc: "printf", varargs.} + +Note that this pragma is somewhat of a misnomer: Other backends will provide +the same feature under the same name. Also, if one is interfacing with C++ +the `ImportCpp pragma `_ and +interfacing with Objective-C the `ImportObjC pragma +`_ can be used. + + +Exportc pragma +-------------- +The ``exportc`` pragma provides a means to export a type, a variable, or a +procedure to C. Enums and constants can't be exported. The optional argument +is a string containing the C identifier. If the argument is missing, the C +name is the Nim identifier *exactly as spelled*: + +.. code-block:: Nim + proc callme(formatstr: cstring) {.exportc: "callMe", varargs.} + +Note that this pragma is somewhat of a misnomer: Other backends will provide +the same feature under the same name. + + +Extern pragma +------------- +Like ``exportc`` or ``importc``, the ``extern`` pragma affects name +mangling. The string literal passed to ``extern`` can be a format string: + +.. code-block:: Nim + proc p(s: string) {.extern: "prefix$1".} = + echo s + +In the example the external name of ``p`` is set to ``prefixp``. + + +Bycopy pragma +------------- + +The ``bycopy`` pragma can be applied to an object or tuple type and +instructs the compiler to pass the type by value to procs: + +.. code-block:: nim + type + TVector {.bycopy, pure.} = object + x, y, z: float + + +Byref pragma +------------ + +The ``byref`` pragma can be applied to an object or tuple type and instructs +the compiler to pass the type by reference (hidden pointer) to procs. + + +Varargs pragma +-------------- +The ``varargs`` pragma can be applied to procedures only (and procedure +types). It tells Nim that the proc can take a variable number of parameters +after the last specified parameter. Nim string values will be converted to C +strings automatically: + +.. code-block:: Nim + proc printf(formatstr: cstring) {.nodecl, varargs.} + + printf("hallo %s", "world") # "world" will be passed as C string + + +Union pragma +------------ +The ``union`` pragma can be applied to any ``object`` type. It means all +of the object's fields are overlaid in memory. This produces a ``union`` +instead of a ``struct`` in the generated C/C++ code. The object declaration +then must not use inheritance or any GC'ed memory but this is currently not +checked. + +**Future directions**: GC'ed memory should be allowed in unions and the GC +should scan unions conservatively. + +Packed pragma +------------- +The ``packed`` pragma can be applied to any ``object`` type. It ensures +that the fields of an object are packed back-to-back in memory. It is useful +to store packets or messages from/to network or hardware drivers, and for +interoperability with C. Combining packed pragma with inheritance is not +defined, and it should not be used with GC'ed memory (ref's). + +**Future directions**: Using GC'ed memory in packed pragma will result in +compile-time error. Usage with inheritance should be defined and documented. + +Unchecked pragma +---------------- +The ``unchecked`` pragma can be used to mark a named array as ``unchecked`` +meaning its bounds are not checked. This is often useful when one wishes to +implement his own flexibly sized arrays. Additionally an unchecked array is +translated into a C array of undetermined size: + +.. code-block:: nim + type + ArrayPart{.unchecked.} = array[0..0, int] + MySeq = object + len, cap: int + data: ArrayPart + +Produces roughly this C code: + +.. code-block:: C + typedef struct { + NI len; + NI cap; + NI data[]; + } MySeq; + +The bounds checking done at compile time is not disabled for now, so to access +``s.data[C]`` (where ``C`` is a constant) the array's index needs needs to +include ``C``. + +The base type of the unchecked array may not contain any GC'ed memory but this +is currently not checked. + +**Future directions**: GC'ed memory should be allowed in unchecked arrays and +there should be an explicit annotation of how the GC is to determine the +runtime size of the array. + + +Dynlib pragma for import +------------------------ +With the ``dynlib`` pragma a procedure or a variable can be imported from +a dynamic library (``.dll`` files for Windows, ``lib*.so`` files for UNIX). +The non-optional argument has to be the name of the dynamic library: + +.. code-block:: Nim + proc gtk_image_new(): PGtkWidget + {.cdecl, dynlib: "libgtk-x11-2.0.so", importc.} + +In general, importing a dynamic library does not require any special linker +options or linking with import libraries. This also implies that no *devel* +packages need to be installed. + +The ``dynlib`` import mechanism supports a versioning scheme: + +.. code-block:: nim + proc Tcl_Eval(interp: pTcl_Interp, script: cstring): int {.cdecl, + importc, dynlib: "libtcl(|8.5|8.4|8.3).so.(1|0)".} + +At runtime the dynamic library is searched for (in this order):: + + libtcl.so.1 + libtcl.so.0 + libtcl8.5.so.1 + libtcl8.5.so.0 + libtcl8.4.so.1 + libtcl8.4.so.0 + libtcl8.3.so.1 + libtcl8.3.so.0 + +The ``dynlib`` pragma supports not only constant strings as argument but also +string expressions in general: + +.. code-block:: nim + import os + + proc getDllName: string = + result = "mylib.dll" + if existsFile(result): return + result = "mylib2.dll" + if existsFile(result): return + quit("could not load dynamic library") + + proc myImport(s: cstring) {.cdecl, importc, dynlib: getDllName().} + +**Note**: Patterns like ``libtcl(|8.5|8.4).so`` are only supported in constant +strings, because they are precompiled. + +**Note**: Passing variables to the ``dynlib`` pragma will fail at runtime +because of order of initialization problems. + +**Note**: A ``dynlib`` import can be overriden with +the ``--dynlibOverride:name`` command line option. The Compiler User Guide +contains further information. + + +Dynlib pragma for export +------------------------ + +With the ``dynlib`` pragma a procedure can also be exported to +a dynamic library. The pragma then has no argument and has to be used in +conjunction with the ``exportc`` pragma: + +.. code-block:: Nim + proc exportme(): int {.cdecl, exportc, dynlib.} + +This is only useful if the program is compiled as a dynamic library via the +``--app:lib`` command line option. diff --git a/doc/manual/generics.txt b/doc/manual/generics.txt new file mode 100644 index 000000000..6f1b2ee0d --- /dev/null +++ b/doc/manual/generics.txt @@ -0,0 +1,346 @@ +Generics +======== + +Generics are Nim's means to parametrize procs, iterators or types with +`type parameters`:idx:. Depending on context, the brackets are used either to +introduce type parameters or to instantiate a generic proc, iterator or type. + +The following example shows a generic binary tree can be modelled: + +.. code-block:: nim + type + TBinaryTree[T] = object # TBinaryTree is a generic type with + # with generic param ``T`` + le, ri: ref TBinaryTree[T] # left and right subtrees; may be nil + data: T # the data stored in a node + PBinaryTree[T] = ref TBinaryTree[T] # a shorthand for notational convenience + + proc newNode[T](data: T): PBinaryTree[T] = # constructor for a node + new(result) + result.data = data + + proc add[T](root: var PBinaryTree[T], n: PBinaryTree[T]) = + if root == nil: + root = n + else: + var it = root + while it != nil: + var c = cmp(it.data, n.data) # compare the data items; uses + # the generic ``cmp`` proc that works for + # any type that has a ``==`` and ``<`` + # operator + if c < 0: + if it.le == nil: + it.le = n + return + it = it.le + else: + if it.ri == nil: + it.ri = n + return + it = it.ri + + iterator inorder[T](root: PBinaryTree[T]): T = + # inorder traversal of a binary tree + # recursive iterators are not yet implemented, so this does not work in + # the current compiler! + if root.le != nil: yield inorder(root.le) + yield root.data + if root.ri != nil: yield inorder(root.ri) + + var + root: PBinaryTree[string] # instantiate a PBinaryTree with the type string + add(root, newNode("hallo")) # instantiates generic procs ``newNode`` and + add(root, newNode("world")) # ``add`` + for str in inorder(root): + writeln(stdout, str) + + +Is operator +----------- + +The ``is`` operator checks for type equivalence at compile time. It is +therefore very useful for type specialization within generic code: + +.. code-block:: nim + type + TTable[TKey, TValue] = object + keys: seq[TKey] + values: seq[TValue] + when not (TKey is string): # nil value for strings used for optimization + deletedKeys: seq[bool] + + +Type operator +------------- + +The ``type`` (in many other languages called `typeof`:idx:) operator can +be used to get the type of an expression: + +.. code-block:: nim + var x = 0 + var y: type(x) # y has type int + +If ``type`` is used to determine the result type of a proc/iterator/converter +call ``c(X)`` (where ``X`` stands for a possibly empty list of arguments), the +interpretation where ``c`` is an iterator is preferred over the +other interpretations: + +.. code-block:: nim + import strutils + + # strutils contains both a ``split`` proc and iterator, but since an + # an iterator is the preferred interpretation, `y` has the type ``string``: + var y: type("a b c".split) + + +Type Classes +------------ + +A type class is a special pseudo-type that can be used to match against +types in the context of overload resolution or the ``is`` operator. +Nim supports the following built-in type classes: + +================== =================================================== +type class matches +================== =================================================== +``object`` any object type +``tuple`` any tuple type + +``enum`` any enumeration +``proc`` any proc type +``ref`` any ``ref`` type +``ptr`` any ``ptr`` type +``var`` any ``var`` type +``distinct`` any distinct type +``array`` any array type +``set`` any set type +``seq`` any seq type +``auto`` any type +================== =================================================== + +Furthermore, every generic type automatically creates a type class of the same +name that will match any instantiation of the generic type. + +Type classes can be combined using the standard boolean operators to form +more complex type classes: + +.. code-block:: nim + # create a type class that will match all tuple and object types + type TRecordType = tuple or object + + proc printFields(rec: TRecordType) = + for key, value in fieldPairs(rec): + echo key, " = ", value + +Procedures utilizing type classes in such manner are considered to be +`implicitly generic`:idx:. They will be instantiated once for each unique +combination of param types used within the program. + +Nim also allows for type classes and regular types to be specified +as `type constraints`:idx: of the generic type parameter: + +.. code-block:: nim + proc onlyIntOrString[T: int|string](x, y: T) = discard + + onlyIntOrString(450, 616) # valid + onlyIntOrString(5.0, 0.0) # type mismatch + onlyIntOrString("xy", 50) # invalid as 'T' cannot be both at the same time + +By default, during overload resolution each named type class will bind to +exactly one concrete type. Here is an example taken directly from the system +module to illustrate this: + +.. code-block:: nim + proc `==`*(x, y: tuple): bool = + ## requires `x` and `y` to be of the same tuple type + ## generic ``==`` operator for tuples that is lifted from the components + ## of `x` and `y`. + result = true + for a, b in fields(x, y): + if a != b: result = false + +Alternatively, the ``distinct`` type modifier can be applied to the type class +to allow each param matching the type class to bind to a different type. + +If a proc param doesn't have a type specified, Nim will use the +``distinct auto`` type class (also known as ``any``): + +.. code-block:: nim + # allow any combination of param types + proc concat(a, b): string = $a & $b + +Procs written with the implicitly generic style will often need to refer to the +type parameters of the matched generic type. They can be easily accessed using +the dot syntax: + +.. code-block:: nim + type TMatrix[T, Rows, Columns] = object + ... + + proc `[]`(m: TMatrix, row, col: int): TMatrix.T = + m.data[col * high(TMatrix.Columns) + row] + +Alternatively, the `type` operator can be used over the proc params for similar +effect when anonymous or distinct type classes are used. + +When a generic type is instantiated with a type class instead of a concrete +type, this results in another more specific type class: + +.. code-block:: nim + seq[ref object] # Any sequence storing references to any object type + + type T1 = auto + proc foo(s: seq[T1], e: T1) + # seq[T1] is the same as just `seq`, but T1 will be allowed to bind + # to a single type, while the signature is being matched + + TMatrix[Ordinal] # Any TMatrix instantiation using integer values + +As seen in the previous example, in such instantiations, it's not necessary to +supply all type parameters of the generic type, because any missing ones will +be inferred to have the equivalent of the `any` type class and thus they will +match anything without discrimination. + + +User defined type classes +------------------------- + +**Note**: User defined type classes are still in development. + +The user-defined type classes are available in two flavours - declarative and +imperative. Both are used to specify an arbitrary set of requirements that the +matched type must satisfy. + +Declarative type classes are written in the following form: + +.. code-block:: nim + type + Comparable = generic x, y + (x < y) is bool + + Container[T] = generic c + c.len is ordinal + items(c) is iterator + for value in c: + type(value) is T + +The type class will be matched if: + +a) all of the expressions within the body can be compiled for the tested type +b) all statically evaluatable boolean expressions in the body must be true + +The identifiers following the `generic` keyword represent instances of the +currently matched type. These instances can act both as variables of the type, +when used in contexts where a value is expected, and as the type itself when +used in contexts where a type is expected. + +Please note that the ``is`` operator allows one to easily verify the precise +type signatures of the required operations, but since type inference and +default parameters are still applied in the provided block, it's also possible +to encode usage protocols that do not reveal implementation details. + +As a special rule providing further convenience when writing type classes, any +type value appearing in a callable expression will be treated as a variable of +the designated type for overload resolution purposes, unless the type value was +passed in its explicit ``typedesc[T]`` form: + +.. code-block:: nim + type + OutputStream = generic S + write(var S, string) + +Much like generics, the user defined type classes will be instantiated exactly +once for each tested type and any static code included within them will also be +executed once. + + +Type inference with type classes +-------------------------------- + +If a type class is used as the return type of a proc and it won't be bound to +a concrete type by some of the proc params, Nim will infer the return type +from the proc body. This is usually used with the ``auto`` type class: + +.. code-block:: nim + proc makePair(a, b): auto = (first: a, second: b) + +The return type will be treated as an additional generic param and can be +explicitly specified at call sites as any other generic param. + +Future versions of Nim may also support overloading based on the return type +of the overloads. In such settings, the expected result type at call sites may +also influence the inferred return type. + +.. + Likewise, if a type class is used in another position where Nim expects a + concrete type (e.g. a variable declaration or a type coercion), Nim will try + to infer the concrete type by applying the matching algorithm that also used + in overload resolution. + + +Symbol lookup in generics +------------------------- + +The symbol binding rules in generics are slightly subtle: There are "open" and +"closed" symbols. A "closed" symbol cannot be re-bound in the instantiation +context, an "open" symbol can. Per default overloaded symbols are open +and every other symbol is closed. + +Open symbols are looked up in two different contexts: Both the context +at definition and the context at instantiation are considered: + +.. code-block:: nim + type + TIndex = distinct int + + proc `==` (a, b: TIndex): bool {.borrow.} + + var a = (0, 0.TIndex) + var b = (0, 0.TIndex) + + echo a == b # works! + +In the example the generic ``==`` for tuples (as defined in the system module) +uses the ``==`` operators of the tuple's components. However, the ``==`` for +the ``TIndex`` type is defined *after* the ``==`` for tuples; yet the example +compiles as the instantiation takes the currently defined symbols into account +too. + +A symbol can be forced to be open by a `mixin`:idx: declaration: + +.. code-block:: nim + proc create*[T](): ref T = + # there is no overloaded 'init' here, so we need to state that it's an + # open symbol explicitly: + mixin init + new result + init result + + +Bind statement +-------------- + +The ``bind`` statement is the counterpart to the ``mixin`` statement. It +can be used to explicitly declare identifiers that should be bound early (i.e. +the identifiers should be looked up in the scope of the template/generic +definition): + +.. code-block:: nim + # Module A + var + lastId = 0 + + template genId*: expr = + bind lastId + inc(lastId) + lastId + +.. code-block:: nim + # Module B + import A + + echo genId() + +But a ``bind`` is rarely useful because symbol binding from the definition +scope is the default. diff --git a/doc/manual/lexing.txt b/doc/manual/lexing.txt new file mode 100644 index 000000000..4de7936a3 --- /dev/null +++ b/doc/manual/lexing.txt @@ -0,0 +1,375 @@ +Lexical Analysis +================ + +Encoding +-------- + +All Nim source files are in the UTF-8 encoding (or its ASCII subset). Other +encodings are not supported. Any of the standard platform line termination +sequences can be used - the Unix form using ASCII LF (linefeed), the Windows +form using the ASCII sequence CR LF (return followed by linefeed), or the old +Macintosh form using the ASCII CR (return) character. All of these forms can be +used equally, regardless of platform. + + +Indentation +----------- + +Nim's standard grammar describes an `indentation sensitive`:idx: language. +This means that all the control structures are recognized by indentation. +Indentation consists only of spaces; tabulators are not allowed. + +The indentation handling is implemented as follows: The lexer annotates the +following token with the preceding number of spaces; indentation is not +a separate token. This trick allows parsing of Nim with only 1 token of +lookahead. + +The parser uses a stack of indentation levels: the stack consists of integers +counting the spaces. The indentation information is queried at strategic +places in the parser but ignored otherwise: The pseudo terminal ``IND{>}`` +denotes an indentation that consists of more spaces than the entry at the top +of the stack; IND{=} an indentation that has the same number of spaces. ``DED`` +is another pseudo terminal that describes the *action* of popping a value +from the stack, ``IND{>}`` then implies to push onto the stack. + +With this notation we can now easily define the core of the grammar: A block of +statements (simplified example):: + + ifStmt = 'if' expr ':' stmt + (IND{=} 'elif' expr ':' stmt)* + (IND{=} 'else' ':' stmt)? + + simpleStmt = ifStmt / ... + + stmt = IND{>} stmt ^+ IND{=} DED # list of statements + / simpleStmt # or a simple statement + + + +Comments +-------- + +Comments start anywhere outside a string or character literal with the +hash character ``#``. +Comments consist of a concatenation of `comment pieces`:idx:. A comment piece +starts with ``#`` and runs until the end of the line. The end of line characters +belong to the piece. If the next line only consists of a comment piece with +no other tokens between it and the preceding one, it does not start a new +comment: + + +.. code-block:: nim + i = 0 # This is a single comment over multiple lines. + # The scanner merges these two pieces. + # The comment continues here. + + +`Documentation comments`:idx: are comments that start with two ``##``. +Documentation comments are tokens; they are only allowed at certain places in +the input file as they belong to the syntax tree! + + +Identifiers & Keywords +---------------------- + +Identifiers in Nim can be any string of letters, digits +and underscores, beginning with a letter. Two immediate following +underscores ``__`` are not allowed:: + + letter ::= 'A'..'Z' | 'a'..'z' | '\x80'..'\xff' + digit ::= '0'..'9' + IDENTIFIER ::= letter ( ['_'] (letter | digit) )* + +Currently any unicode character with an ordinal value > 127 (non ASCII) is +classified as a ``letter`` and may thus be part of an identifier but later +versions of the language may assign some Unicode characters to belong to the +operator characters instead. + +The following keywords are reserved and cannot be used as identifiers: + +.. code-block:: nim + :file: keywords.txt + +Some keywords are unused; they are reserved for future developments of the +language. + + +Identifier equality +------------------- + +Two identifiers are considered equal if the following algorithm returns true: + +.. code-block:: nim + proc sameIdentifier(a, b: string): bool = + a[0] == b[0] and a.replace("_", "").toLower == b.replace("_", "").toLower + +That means only the first letters are compared in a case sensitive manner. Other +letters are compared case insensitively and underscores are ignored. + +This rather strange way to do identifier comparisons is called +`partial case insensitivity`:idx: and has some advantages over the conventional +case sensitivity: + +It allows programmers to mostly use their own preferred +spelling style and libraries written by different programmers cannot use +incompatible conventions. A Nim-aware editor or IDE can show the identifiers as +preferred. Another advantage is that it frees the programmer from remembering +the exact spelling of an identifier. The exception with respect to the first +letter allows common code like ``var foo: Foo`` to be parsed unambiguously. + +Historically, Nim was a `style-insensitive`:idx: language. This means that it +was not case-sensitive and underscores were ignored and there was no distinction +between ``foo`` and ``Foo``. + + +String literals +--------------- + +Terminal symbol in the grammar: ``STR_LIT``. + +String literals can be delimited by matching double quotes, and can +contain the following `escape sequences`:idx:\ : + +================== =================================================== + Escape sequence Meaning +================== =================================================== + ``\n`` `newline`:idx: + ``\r``, ``\c`` `carriage return`:idx: + ``\l`` `line feed`:idx: + ``\f`` `form feed`:idx: + ``\t`` `tabulator`:idx: + ``\v`` `vertical tabulator`:idx: + ``\\`` `backslash`:idx: + ``\"`` `quotation mark`:idx: + ``\'`` `apostrophe`:idx: + ``\`` '0'..'9'+ `character with decimal value d`:idx:; + all decimal digits directly + following are used for the character + ``\a`` `alert`:idx: + ``\b`` `backspace`:idx: + ``\e`` `escape`:idx: `[ESC]`:idx: + ``\x`` HH `character with hex value HH`:idx:; + exactly two hex digits are allowed +================== =================================================== + + +Strings in Nim may contain any 8-bit value, even embedded zeros. However +some operations may interpret the first binary zero as a terminator. + + +Triple quoted string literals +----------------------------- + +Terminal symbol in the grammar: ``TRIPLESTR_LIT``. + +String literals can also be delimited by three double quotes +``"""`` ... ``"""``. +Literals in this form may run for several lines, may contain ``"`` and do not +interpret any escape sequences. +For convenience, when the opening ``"""`` is followed by a newline (there may +be whitespace between the opening ``"""`` and the newline), +the newline (and the preceding whitespace) is not included in the string. The +ending of the string literal is defined by the pattern ``"""[^"]``, so this: + +.. code-block:: nim + """"long string within quotes"""" + +Produces:: + + "long string within quotes" + + +Raw string literals +------------------- + +Terminal symbol in the grammar: ``RSTR_LIT``. + +There are also raw string literals that are preceded with the +letter ``r`` (or ``R``) and are delimited by matching double quotes (just +like ordinary string literals) and do not interpret the escape sequences. +This is especially convenient for regular expressions or Windows paths: + +.. code-block:: nim + + var f = openFile(r"C:\texts\text.txt") # a raw string, so ``\t`` is no tab + +To produce a single ``"`` within a raw string literal, it has to be doubled: + +.. code-block:: nim + + r"a""b" + +Produces:: + + a"b + +``r""""`` is not possible with this notation, because the three leading +quotes introduce a triple quoted string literal. ``r"""`` is the same +as ``"""`` since triple quoted string literals do not interpret escape +sequences either. + + +Generalized raw string literals +------------------------------- + +Terminal symbols in the grammar: ``GENERALIZED_STR_LIT``, +``GENERALIZED_TRIPLESTR_LIT``. + +The construct ``identifier"string literal"`` (without whitespace between the +identifier and the opening quotation mark) is a +generalized raw string literal. It is a shortcut for the construct +``identifier(r"string literal")``, so it denotes a procedure call with a +raw string literal as its only argument. Generalized raw string literals +are especially convenient for embedding mini languages directly into Nim +(for example regular expressions). + +The construct ``identifier"""string literal"""`` exists too. It is a shortcut +for ``identifier("""string literal""")``. + + +Character literals +------------------ + +Character literals are enclosed in single quotes ``''`` and can contain the +same escape sequences as strings - with one exception: `newline`:idx: (``\n``) +is not allowed as it may be wider than one character (often it is the pair +CR/LF for example). Here are the valid `escape sequences`:idx: for character +literals: + +================== =================================================== + Escape sequence Meaning +================== =================================================== + ``\r``, ``\c`` `carriage return`:idx: + ``\l`` `line feed`:idx: + ``\f`` `form feed`:idx: + ``\t`` `tabulator`:idx: + ``\v`` `vertical tabulator`:idx: + ``\\`` `backslash`:idx: + ``\"`` `quotation mark`:idx: + ``\'`` `apostrophe`:idx: + ``\`` '0'..'9'+ `character with decimal value d`:idx:; + all decimal digits directly + following are used for the character + ``\a`` `alert`:idx: + ``\b`` `backspace`:idx: + ``\e`` `escape`:idx: `[ESC]`:idx: + ``\x`` HH `character with hex value HH`:idx:; + exactly two hex digits are allowed +================== =================================================== + +A character is not an Unicode character but a single byte. The reason for this +is efficiency: for the overwhelming majority of use-cases, the resulting +programs will still handle UTF-8 properly as UTF-8 was specially designed for +this. Another reason is that Nim can thus support ``array[char, int]`` or +``set[char]`` efficiently as many algorithms rely on this feature. The `TRune` +type is used for Unicode characters, it can represent any Unicode character. +``TRune`` is declared in the `unicode module `_. + + +Numerical constants +------------------- + +Numerical constants are of a single type and have the form:: + + hexdigit = digit | 'A'..'F' | 'a'..'f' + octdigit = '0'..'7' + bindigit = '0'..'1' + HEX_LIT = '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )* + DEC_LIT = digit ( ['_'] digit )* + OCT_LIT = '0o' octdigit ( ['_'] octdigit )* + BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )* + + INT_LIT = HEX_LIT + | DEC_LIT + | OCT_LIT + | BIN_LIT + + INT8_LIT = INT_LIT ['\''] ('i' | 'I') '8' + INT16_LIT = INT_LIT ['\''] ('i' | 'I') '16' + INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32' + INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64' + + UINT8_LIT = INT_LIT ['\''] ('u' | 'U') + UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8' + UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16' + UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32' + UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64' + + 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' + + +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 ``0x``) notation. + +There exists a literal for each numerical type that is +defined. The suffix starting with an apostrophe ('\'') is called a +`type suffix`:idx:. Literals without a type suffix are of the type ``int``, +unless the literal contains a dot or ``E|e`` in which case it is of +type ``float``. For notational convenience the apostrophe of a type suffix +is optional if it is not ambiguous (only hexadecimal floating point literals +with a type suffix can be ambiguous). + + +The type suffixes are: + +================= ========================= + Type Suffix Resulting type of literal +================= ========================= + ``'i8`` int8 + ``'i16`` int16 + ``'i32`` int32 + ``'i64`` int64 + ``'u`` uint + ``'u8`` uint8 + ``'u16`` uint16 + ``'u32`` uint32 + ``'u64`` uint64 + ``'f32`` float32 + ``'f64`` float64 +================= ========================= + +Floating point literals may also be in binary, octal or hexadecimal +notation: +``0B0_10001110100_0000101001000111101011101111111011000101001101001001'f64`` +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:: + + = + - * / < > + @ $ ~ & % | + ! ? ^ . : \ + +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. + +``*:`` is as a special case the two tokens `*`:tok: and `:`:tok: +(to support ``var v*: T``). + + +Other tokens +------------ + +The following strings denote other tokens:: + + ` ( ) { } [ ] , ; [. .] {. .} (. .) + + +The `slice`:idx: operator `..`:tok: takes precedence over other tokens that +contain a dot: `{..}`:tok: are the three tokens `{`:tok:, `..`:tok:, `}`:tok: +and not the two tokens `{.`:tok:, `.}`:tok:. + diff --git a/doc/manual/locking.txt b/doc/manual/locking.txt new file mode 100644 index 000000000..864eeccb5 --- /dev/null +++ b/doc/manual/locking.txt @@ -0,0 +1,200 @@ +Guards and locks +================ + +Apart from ``spawn`` and ``parallel`` Nim also provides all the common low level +concurrency mechanisms like locks, atomic intristics or condition variables. + +Nim significantly improves on the safety of these features via additional +pragmas: + +1) A `guard`:idx: annotation is introduced to prevent data races. +2) Every access of a guarded memory location needs to happen in an + appropriate `locks`:idx: statement. +3) Locks and routines can be annotated with `lock levels`:idx: to prevent + deadlocks at compile time. + + +Guards and the locks section +---------------------------- + +Protecting global variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Object fields and global variables can be annotated via a ``guard`` pragma: + +.. code-block:: nim + var glock: TLock + var gdata {.guard: glock.}: int + +The compiler then ensures that every access of ``gdata`` is within a ``locks`` +section: + +.. code-block:: nim + proc invalid = + # invalid: unguarded access: + echo gdata + + proc valid = + # valid access: + {.locks: [glock].}: + echo gdata + +Top level accesses to ``gdata`` are always allowed so that it can be initialized +conveniently. It is *assumed* (but not enforced) that every top level statement +is executed before any concurrent action happens. + +The ``locks`` section deliberately looks ugly because it has no runtime +semantics and should not be used directly! It should only be used in templates +that also implement some form of locking at runtime: + +.. code-block:: nim + template lock(a: TLock; body: stmt) = + pthread_mutex_lock(a) + {.locks: [a].}: + try: + body + finally: + pthread_mutex_unlock(a) + + +The guard does not need to be of any particular type. It is flexible enough to +model low level lockfree mechanisms: + +.. code-block:: nim + var dummyLock {.compileTime.}: int + var atomicCounter {.guard: dummyLock.}: int + + template atomicRead(x): expr = + {.locks: [dummyLock].}: + memoryReadBarrier() + x + + echo atomicRead(atomicCounter) + + +The ``locks`` pragma takes a list of lock expressions ``locks: [a, b, ...]`` +in order to support *multi lock* statements. Why these are essential is +explained in the `lock levels`_ section. + + +Protecting general locations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``guard`` annotation can also be used to protect fields within an object. +The guard then needs to be another field within the same object or a +global variable. + +Since objects can reside on the heap or on the stack this greatly enhances the +expressivity of the language: + +.. code-block:: nim + type + ProtectedCounter = object + v {.guard: L.}: int + L: TLock + + proc incCounters(counters: var openArray[ProtectedCounter]) = + for i in 0..counters.high: + lock counters[i].L: + inc counters[i].v + +The access to field ``x.v`` is allowed since its guard ``x.L`` is active. +After template expansion, this amounts to: + +.. code-block:: nim + proc incCounters(counters: var openArray[ProtectedCounter]) = + for i in 0..counters.high: + pthread_mutex_lock(counters[i].L) + {.locks: [counters[i].L].}: + try: + inc counters[i].v + finally: + pthread_mutex_unlock(counters[i].L) + +There is an analysis that checks that ``counters[i].L`` is the lock that +corresponds to the protected location ``counters[i].v``. This analysis is called +`path analysis`:idx: because it deals with paths to locations +like ``obj.field[i].fieldB[j]``. + +The path analysis is **currently unsound**, but that doesn't make it useless. +Two paths are considered equivalent if they are syntactically the same. + +This means the following compiles (for now) even though it really should not: + +.. code-block:: nim + {.locks: [a[i].L].}: + inc i + access a[i].v + + + +Lock levels +----------- + +Lock levels are used to enforce a global locking order in order to prevent +deadlocks at compile-time. A lock level is an constant integer in the range +0..1_000. Lock level 0 means that no lock is acquired at all. + +If a section of code holds a lock of level ``M`` than it can also acquire any +lock of level ``N < M``. Another lock of level ``M`` cannot be acquired. Locks +of the same level can only be acquired *at the same time* within a +single ``locks`` section: + +.. code-block:: nim + var a, b: TLock[2] + var x: TLock[1] + # invalid locking order: TLock[1] cannot be acquired before TLock[2]: + {.locks: [x].}: + {.locks: [a].}: + ... + # valid locking order: TLock[2] acquired before TLock[1]: + {.locks: [a].}: + {.locks: [x].}: + ... + + # invalid locking order: TLock[2] acquired before TLock[2]: + {.locks: [a].}: + {.locks: [b].}: + ... + + # valid locking order, locks of the same level acquired at the same time: + {.locks: [a, b].}: + ... + + +Here is how a typical multilock statement can be implemented in Nim. Note how +the runtime check is required to ensure a global ordering for two locks ``a`` +and ``b`` of the same lock level: + +.. code-block:: nim + template multilock(a, b: ptr TLock; body: stmt) = + if cast[ByteAddress](a) < cast[ByteAddress](b): + pthread_mutex_lock(a) + pthread_mutex_lock(b) + else: + pthread_mutex_lock(b) + pthread_mutex_lock(a) + {.locks: [a, b].}: + try: + body + finally: + pthread_mutex_unlock(a) + pthread_mutex_unlock(b) + + +Whole routines can also be annotated with a ``locks`` pragma that takes a lock +level. This then means that the routine may acquire locks of up to this level. +This is essential so that procs can be called within a ``locks`` section: + +.. code-block:: nim + proc p() {.locks: 3.} = discard + + var a: TLock[4] + {.locks: [a].}: + # p's locklevel (3) is strictly less than a's (4) so the call is allowed: + p() + + +As usual ``locks`` is an inferred effect and there is a subtype +relation: ``proc () {.locks: N.}`` is a subtype of ``proc () {.locks: M.}`` +iff (M <= N). diff --git a/doc/manual/modules.txt b/doc/manual/modules.txt new file mode 100644 index 000000000..4000597d0 --- /dev/null +++ b/doc/manual/modules.txt @@ -0,0 +1,203 @@ +Modules +======= +Nim supports splitting a program into pieces by a module concept. +Each module needs to be in its own file and has its own `namespace`:idx:. +Modules enable `information hiding`:idx: and `separate compilation`:idx:. +A module may gain access to symbols of another module by the `import`:idx: +statement. `Recursive module dependencies`:idx: are allowed, but slightly +subtle. Only top-level symbols that are marked with an asterisk (``*``) are +exported. A valid module name can only be a valid Nim identifier (and thus its +filename is ``identifier.nim``). + +The algorithm for compiling modules is: + +- compile the whole module as usual, following import statements recursively + +- if there is a cycle only import the already parsed symbols (that are + exported); if an unknown identifier occurs then abort + +This is best illustrated by an example: + +.. code-block:: nim + # Module A + type + T1* = int # Module A exports the type ``T1`` + import B # the compiler starts parsing B + + proc main() = + var i = p(3) # works because B has been parsed completely here + + main() + + +.. code-block:: nim + # Module B + import A # A is not parsed here! Only the already known symbols + # of A are imported. + + proc p*(x: A.T1): A.T1 = + # this works because the compiler has already + # added T1 to A's interface symbol table + result = x + 1 + + +Import statement +~~~~~~~~~~~~~~~~ + +After the ``import`` statement a list of module names can follow or a single +module name followed by an ``except`` list to prevent some symbols to be +imported: + +.. code-block:: nim + import strutils except `%`, toUpper + + # doesn't work then: + echo "$1" % "abc".toUpper + + +It is not checked that the ``except`` list is really exported from the module. +This feature allows to compile against an older version of the module that +does not export these identifiers. + + +Include statement +~~~~~~~~~~~~~~~~~ +The ``include`` statement does something fundamentally different than +importing a module: it merely includes the contents of a file. The ``include`` +statement is useful to split up a large module into several files: + +.. code-block:: nim + include fileA, fileB, fileC + + + +Module names in imports +~~~~~~~~~~~~~~~~~~~~~~~ + +A module alias can be introduced via the ``as`` keyword: + +.. code-block:: nim + import strutils as su, sequtils as qu + + echo su.format("$1", "lalelu") + +The original module name is then not accessible. The +notations ``path/to/module`` or ``path.to.module`` or ``"path/to/module"`` +can be used to refer to a module in subdirectories: + +.. code-block:: nim + import lib.pure.strutils, lib/pure/os, "lib/pure/times" + +Note that the module name is still ``strutils`` and not ``lib.pure.strutils`` +and so one **cannot** do: + +.. code-block:: nim + import lib.pure.strutils + echo lib.pure.strutils + +Likewise the following does not make sense as the name is ``strutils`` already: + +.. code-block:: nim + import lib.pure.strutils as strutils + + +From import statement +~~~~~~~~~~~~~~~~~~~~~ + +After the ``from`` statement a module name follows followed by +an ``import`` to list the symbols one likes to use without explict +full qualification: + +.. code-block:: nim + from strutils import `%` + + echo "$1" % "abc" + # always possible: full qualification: + echo strutils.replace("abc", "a", "z") + +It's also possible to use ``from module import nil`` if one wants to import +the module but wants to enforce fully qualified access to every symbol +in ``module``. + + +Export statement +~~~~~~~~~~~~~~~~ + +An ``export`` statement can be used for symbol fowarding so that client +modules don't need to import a module's dependencies: + +.. code-block:: nim + # module B + type TMyObject* = object + +.. code-block:: nim + # module A + import B + export B.TMyObject + + proc `$`*(x: TMyObject): string = "my object" + + +.. code-block:: nim + # module C + import A + + # B.TMyObject has been imported implicitly here: + var x: TMyObject + echo($x) + + +Scope rules +----------- +Identifiers are valid from the point of their declaration until the end of +the block in which the declaration occurred. The range where the identifier +is known is the scope of the identifier. The exact scope of an +identifier depends on the way it was declared. + +Block scope +~~~~~~~~~~~ +The *scope* of a variable declared in the declaration part of a block +is valid from the point of declaration until the end of the block. If a +block contains a second block, in which the identifier is redeclared, +then inside this block, the second declaration will be valid. Upon +leaving the inner block, the first declaration is valid again. An +identifier cannot be redefined in the same block, except if valid for +procedure or iterator overloading purposes. + + +Tuple or object scope +~~~~~~~~~~~~~~~~~~~~~ +The field identifiers inside a tuple or object definition are valid in the +following places: + +* To the end of the tuple/object definition. +* Field designators of a variable of the given tuple/object type. +* In all descendant types of the object type. + +Module scope +~~~~~~~~~~~~ +All identifiers of a module are valid from the point of declaration until +the end of the module. Identifiers from indirectly dependent modules are *not* +available. The `system`:idx: module is automatically imported in every other +module. + +If a module imports an identifier by two different modules, each occurrence of +the identifier has to be qualified, unless it is an overloaded procedure or +iterator in which case the overloading resolution takes place: + +.. code-block:: nim + # Module A + var x*: string + +.. code-block:: nim + # Module B + var x*: int + +.. code-block:: nim + # Module C + import A, B + write(stdout, x) # error: x is ambiguous + write(stdout, A.x) # no error: qualifier used + + var x = 4 + write(stdout, x) # not ambiguous: uses the module C's x diff --git a/doc/manual/pragmas.txt b/doc/manual/pragmas.txt new file mode 100644 index 000000000..6bb1314cd --- /dev/null +++ b/doc/manual/pragmas.txt @@ -0,0 +1,515 @@ +Pragmas +======= + +Pragmas are Nim's method to give the compiler additional information / +commands without introducing a massive number of new keywords. Pragmas are +processed on the fly during semantic checking. Pragmas are enclosed in the +special ``{.`` and ``.}`` curly brackets. Pragmas are also often used as a +first implementation to play with a language feature before a nicer syntax +to access the feature becomes available. + + +deprecated pragma +----------------- + +The deprecated pragma is used to mark a symbol as deprecated: + +.. code-block:: nimrod + proc p() {.deprecated.} + var x {.deprecated.}: char + +It can also be used as a statement. Then it takes a list of *renamings*. The +upcoming ``nimfix`` tool can automatically update the code and perform these +renamings: + +.. code-block:: nimrod + type + File = object + Stream = ref object + {.deprecated: [TFile: File, PStream: Stream].} + + +noSideEffect pragma +------------------- +The ``noSideEffect`` pragma is used to mark a proc/iterator to have no side +effects. This means that the proc/iterator only changes locations that are +reachable from its parameters and the return value only depends on the +arguments. If none of its parameters have the type ``var T`` +or ``ref T`` or ``ptr T`` this means no locations are modified. It is a static +error to mark a proc/iterator to have no side effect if the compiler cannot +verify this. + +As a special semantic rule, the built-in `debugEcho `_ +pretends to be free of side effects, so that it can be used for debugging +routines marked as ``noSideEffect``. + +**Future directions**: ``func`` may become a keyword and syntactic sugar for a +proc with no side effects: + +.. code-block:: nim + func `+` (x, y: int): int + + +destructor pragma +----------------- + +The ``destructor`` pragma is used to mark a proc to act as a type destructor. +Its usage is deprecated, use the ``override`` pragma instead. +See `type bound operations`_. + + +override pragma +--------------- + +See `type bound operations`_ instead. + +procvar pragma +-------------- +The ``procvar`` pragma is used to mark a proc that it can be passed to a +procedural variable. + + +compileTime pragma +------------------ +The ``compileTime`` pragma is used to mark a proc to be used at compile +time only. No code will be generated for it. Compile time procs are useful +as helpers for macros. + + +noReturn pragma +--------------- +The ``noreturn`` pragma is used to mark a proc that never returns. + + +acyclic pragma +-------------- +The ``acyclic`` pragma can be used for object types to mark them as acyclic +even though they seem to be cyclic. This is an **optimization** for the garbage +collector to not consider objects of this type as part of a cycle: + +.. code-block:: nim + type + PNode = ref TNode + TNode {.acyclic, final.} = object + left, right: PNode + data: string + +In the example a tree structure is declared with the ``TNode`` type. Note that +the type definition is recursive and the GC has to assume that objects of +this type may form a cyclic graph. The ``acyclic`` pragma passes the +information that this cannot happen to the GC. If the programmer uses the +``acyclic`` pragma for data types that are in reality cyclic, the GC may leak +memory, but nothing worse happens. + +**Future directions**: The ``acyclic`` pragma may become a property of a +``ref`` type: + +.. code-block:: nim + type + PNode = acyclic ref TNode + TNode = object + left, right: PNode + data: string + + +final pragma +------------ +The ``final`` pragma can be used for an object type to specify that it +cannot be inherited from. + + +shallow pragma +-------------- +The ``shallow`` pragma affects the semantics of a type: The compiler is +allowed to make a shallow copy. This can cause serious semantic issues and +break memory safety! However, it can speed up assignments considerably, +because the semantics of Nim require deep copying of sequences and strings. +This can be expensive, especially if sequences are used to build a tree +structure: + +.. code-block:: nim + type + TNodeKind = enum nkLeaf, nkInner + TNode {.final, shallow.} = object + case kind: TNodeKind + of nkLeaf: + strVal: string + of nkInner: + children: seq[TNode] + + +pure pragma +----------- +An object type can be marked with the ``pure`` pragma so that its type +field which is used for runtime type identification is omitted. This used to be +necessary for binary compatibility with other compiled languages. + +An enum type can be marked as ``pure``. Then access of its fields always +requires full qualification. + + +asmNoStackFrame pragma +---------------------- +A proc can be marked with the ``AsmNoStackFrame`` pragma to tell the compiler +it should not generate a stack frame for the proc. There are also no exit +statements like ``return result;`` generated and the generated C function is +declared as ``__declspec(naked)`` or ``__attribute__((naked))`` (depending on +the used C compiler). + +**Note**: This pragma should only be used by procs which consist solely of +assembler statements. + +error pragma +------------ +The ``error`` pragma is used to make the compiler output an error message +with the given content. Compilation does not necessarily abort after an error +though. + +The ``error`` pragma can also be used to +annotate a symbol (like an iterator or proc). The *usage* of the symbol then +triggers a compile-time error. This is especially useful to rule out that some +operation is valid due to overloading and type conversions: + +.. code-block:: nim + ## check that underlying int values are compared and not the pointers: + proc `==`(x, y: ptr int): bool {.error.} + + +fatal pragma +------------ +The ``fatal`` pragma is used to make the compiler output an error message +with the given content. In contrast to the ``error`` pragma, compilation +is guaranteed to be aborted by this pragma. Example: + +.. code-block:: nim + when not defined(objc): + {.fatal: "Compile this program with the objc command!".} + +warning pragma +-------------- +The ``warning`` pragma is used to make the compiler output a warning message +with the given content. Compilation continues after the warning. + +hint pragma +----------- +The ``hint`` pragma is used to make the compiler output a hint message with +the given content. Compilation continues after the hint. + +line pragma +----------- +The ``line`` pragma can be used to affect line information of the annotated +statement as seen in stack backtraces: + +.. code-block:: nim + + template myassert*(cond: expr, msg = "") = + if not cond: + # change run-time line information of the 'raise' statement: + {.line: InstantiationInfo().}: + raise newException(EAssertionFailed, msg) + +If the ``line`` pragma is used with a parameter, the parameter needs be a +``tuple[filename: string, line: int]``. If it is used without a parameter, +``system.InstantiationInfo()`` is used. + + +linearScanEnd pragma +-------------------- +The ``linearScanEnd`` pragma can be used to tell the compiler how to +compile a Nim `case`:idx: statement. Syntactically it has to be used as a +statement: + +.. code-block:: nim + case myInt + of 0: + echo "most common case" + of 1: + {.linearScanEnd.} + echo "second most common case" + of 2: echo "unlikely: use branch table" + else: echo "unlikely too: use branch table for ", myInt + +In the example, the case branches ``0`` and ``1`` are much more common than +the other cases. Therefore the generated assembler code should test for these +values first, so that the CPU's branch predictor has a good chance to succeed +(avoiding an expensive CPU pipeline stall). The other cases might be put into a +jump table for O(1) overhead, but at the cost of a (very likely) pipeline +stall. + +The ``linearScanEnd`` pragma should be put into the last branch that should be +tested against via linear scanning. If put into the last branch of the +whole ``case`` statement, the whole ``case`` statement uses linear scanning. + + +computedGoto pragma +------------------- +The ``computedGoto`` pragma can be used to tell the compiler how to +compile a Nim `case`:idx: in a ``while true`` statement. +Syntactically it has to be used as a statement inside the loop: + +.. code-block:: nim + + type + MyEnum = enum + enumA, enumB, enumC, enumD, enumE + + proc vm() = + var instructions: array [0..100, MyEnum] + instructions[2] = enumC + instructions[3] = enumD + instructions[4] = enumA + instructions[5] = enumD + instructions[6] = enumC + instructions[7] = enumA + instructions[8] = enumB + + instructions[12] = enumE + var pc = 0 + while true: + {.computedGoto.} + let instr = instructions[pc] + case instr + of enumA: + echo "yeah A" + of enumC, enumD: + echo "yeah CD" + of enumB: + echo "yeah B" + of enumE: + break + inc(pc) + + vm() + +As the example shows ``computedGoto`` is mostly useful for interpreters. If +the underlying backend (C compiler) does not support the computed goto +extension the pragma is simply ignored. + + +unroll pragma +------------- +The ``unroll`` pragma can be used to tell the compiler that it should unroll +a `for`:idx: or `while`:idx: loop for runtime efficiency: + +.. code-block:: nim + proc searchChar(s: string, c: char): int = + for i in 0 .. s.high: + {.unroll: 4.} + if s[i] == c: return i + result = -1 + +In the above example, the search loop is unrolled by a factor 4. The unroll +factor can be left out too; the compiler then chooses an appropriate unroll +factor. + +**Note**: Currently the compiler recognizes but ignores this pragma. + + +immediate pragma +---------------- + +See `Ordinary vs immediate templates`_. + + +compilation option pragmas +-------------------------- +The listed pragmas here can be used to override the code generation options +for a proc/method/converter. + +The implementation currently provides the following possible options (various +others may be added later). + +=============== =============== ============================================ +pragma allowed values description +=============== =============== ============================================ +checks on|off Turns the code generation for all runtime + checks on or off. +boundChecks on|off Turns the code generation for array bound + checks on or off. +overflowChecks on|off Turns the code generation for over- or + underflow checks on or off. +nilChecks on|off Turns the code generation for nil pointer + checks on or off. +assertions on|off Turns the code generation for assertions + on or off. +warnings on|off Turns the warning messages of the compiler + on or off. +hints on|off Turns the hint messages of the compiler + on or off. +optimization none|speed|size Optimize the code for speed or size, or + disable optimization. +patterns on|off Turns the term rewriting templates/macros + on or off. +callconv cdecl|... Specifies the default calling convention for + all procedures (and procedure types) that + follow. +=============== =============== ============================================ + +Example: + +.. code-block:: nim + {.checks: off, optimization: speed.} + # compile without runtime checks and optimize for speed + + +push and pop pragmas +-------------------- +The `push/pop`:idx: pragmas are very similar to the option directive, +but are used to override the settings temporarily. Example: + +.. code-block:: nim + {.push checks: off.} + # compile this section without runtime checks as it is + # speed critical + # ... some code ... + {.pop.} # restore old settings + + +register pragma +--------------- +The ``register`` pragma is for variables only. It declares the variable as +``register``, giving the compiler a hint that the variable should be placed +in a hardware register for faster access. C compilers usually ignore this +though and for good reasons: Often they do a better job without it anyway. + +In highly specific cases (a dispatch loop of an bytecode interpreter for +example) it may provide benefits, though. + + +global pragma +------------- +The ``global`` pragma can be applied to a variable within a proc to instruct +the compiler to store it in a global location and initialize it once at program +startup. + +.. code-block:: nim + proc isHexNumber(s: string): bool = + var pattern {.global.} = re"[0-9a-fA-F]+" + result = s.match(pattern) + +When used within a generic proc, a separate unique global variable will be +created for each instantiation of the proc. The order of initialization of +the created global variables within a module is not defined, but all of them +will be initialized after any top-level variables in their originating module +and before any variable in a module that imports it. + +deadCodeElim pragma +------------------- +The ``deadCodeElim`` pragma only applies to whole modules: It tells the +compiler to activate (or deactivate) dead code elimination for the module the +pragma appears in. + +The ``--deadCodeElim:on`` command line switch has the same effect as marking +every module with ``{.deadCodeElim:on}``. However, for some modules such as +the GTK wrapper it makes sense to *always* turn on dead code elimination - +no matter if it is globally active or not. + +Example: + +.. code-block:: nim + {.deadCodeElim: on.} + + +.. + NoForward pragma + ---------------- + The ``noforward`` pragma can be used to turn on and off a special compilation + mode that to large extent eliminates the need for forward declarations. In this + mode, the proc definitions may appear out of order and the compiler will postpone + their semantic analysis and compilation until it actually needs to generate code + using the definitions. In this regard, this mode is similar to the modus operandi + of dynamic scripting languages, where the function calls are not resolved until + the code is executed. Here is the detailed algorithm taken by the compiler: + + 1. When a callable symbol is first encountered, the compiler will only note the + symbol callable name and it will add it to the appropriate overload set in the + current scope. At this step, it won't try to resolve any of the type expressions + used in the signature of the symbol (so they can refer to other not yet defined + symbols). + + 2. When a top level call is encountered (usually at the very end of the module), + the compiler will try to determine the actual types of all of the symbols in the + matching overload set. This is a potentially recursive process as the signatures + of the symbols may include other call expressions, whoose types will be resolved + at this point too. + + 3. Finally, after the best overload is picked, the compiler will start compiling + the body of the respective symbol. This in turn will lead the compiler to discover + more call expresions that need to be resolved and steps 2 and 3 will be repeated + as necessary. + + Please note that if a callable symbol is never used in this scenario, its body + will never be compiled. This is the default behavior leading to best compilation + times, but if exhaustive compilation of all definitions is required, using + ``nim check`` provides this option as well. + + Example: + + .. code-block:: nim + + {.noforward: on.} + + proc foo(x: int) = + bar x + + proc bar(x: int) = + echo x + + foo(10) + + +pragma pragma +------------- + +The ``pragma`` pragma can be used to declare user defined pragmas. This is +useful because Nim's templates and macros do not affect pragmas. User +defined pragmas are in a different module-wide scope than all other symbols. +They cannot be imported from a module. + +Example: + +.. code-block:: nim + when appType == "lib": + {.pragma: rtl, exportc, dynlib, cdecl.} + else: + {.pragma: rtl, importc, dynlib: "client.dll", cdecl.} + + proc p*(a, b: int): int {.rtl.} = + result = a+b + +In the example a new pragma named ``rtl`` is introduced that either imports +a symbol from a dynamic library or exports the symbol for dynamic library +generation. + + +Disabling certain messages +-------------------------- +Nim generates some warnings and hints ("line too long") that may annoy the +user. A mechanism for disabling certain messages is provided: Each hint +and warning message contains a symbol in brackets. This is the message's +identifier that can be used to enable or disable it: + +.. code-block:: Nim + {.hint[LineTooLong]: off.} # turn off the hint about too long lines + +This is often better than disabling all warnings at once. + + +experimental pragma +------------------- + +The ``experimental`` pragma enables experimental language features. Depending +on the concrete feature this means that the feature is either considered +too unstable for an otherwise stable release or that the future of the feature +is uncertain (it may be removed any time). + +Example: + +.. code-block:: nim + {.experimental.} + + proc useUsing(dest: var string) = + using dest + add "foo" + add "bar" + diff --git a/doc/manual/procs.txt b/doc/manual/procs.txt new file mode 100644 index 000000000..2ebbe494d --- /dev/null +++ b/doc/manual/procs.txt @@ -0,0 +1,603 @@ +Procedures +========== + +What most programming languages call `methods`:idx: or `functions`:idx: are +called `procedures`:idx: in Nim (which is the correct terminology). A +procedure declaration defines an identifier and associates it with a block +of code. +A procedure may call itself recursively. A parameter may be given a default +value that is used if the caller does not provide a value for this parameter. + +If the proc declaration has no body, it is a `forward`:idx: declaration. If +the proc returns a value, the procedure body can access an implicitly declared +variable named `result`:idx: that represents the return value. Procs can be +overloaded. The overloading resolution algorithm tries to find the proc that is +the best match for the arguments. Example: + +.. code-block:: nim + + proc toLower(c: char): char = # toLower for characters + if c in {'A'..'Z'}: + result = chr(ord(c) + (ord('a') - ord('A'))) + else: + result = c + + proc toLower(s: string): string = # toLower for strings + result = newString(len(s)) + for i in 0..len(s) - 1: + result[i] = toLower(s[i]) # calls toLower for characters; no recursion! + +Calling a procedure can be done in many different ways: + +.. code-block:: nim + proc callme(x, y: int, s: string = "", c: char, b: bool = false) = ... + + # call with positional arguments # parameter bindings: + callme(0, 1, "abc", '\t', true) # (x=0, y=1, s="abc", c='\t', b=true) + # call with named and positional arguments: + callme(y=1, x=0, "abd", '\t') # (x=0, y=1, s="abd", c='\t', b=false) + # call with named arguments (order is not relevant): + callme(c='\t', y=1, x=0) # (x=0, y=1, s="", c='\t', b=false) + # call as a command statement: no () needed: + callme 0, 1, "abc", '\t' + + +A procedure cannot modify its parameters (unless the parameters have the type +`var`). + +`Operators`:idx: are procedures with a special operator symbol as identifier: + +.. code-block:: nim + proc `$` (x: int): string = + # converts an integer to a string; this is a prefix operator. + result = intToStr(x) + +Operators with one parameter are prefix operators, operators with two +parameters are infix operators. (However, the parser distinguishes these from +the operator's position within an expression.) There is no way to declare +postfix operators: all postfix operators are built-in and handled by the +grammar explicitly. + +Any operator can be called like an ordinary proc with the '`opr`' +notation. (Thus an operator can have more than two parameters): + +.. code-block:: nim + proc `*+` (a, b, c: int): int = + # Multiply and add + result = a * b + c + + assert `*+`(3, 4, 6) == `*`(a, `+`(b, c)) + + +Export marker +------------- + +If a declared symbol is marked with an `asterisk`:idx: it is exported from the +current module: + +.. code-block:: nim + + proc exportedEcho*(s: string) = echo s + proc `*`*(a: string; b: int): string = + result = newStringOfCap(a.len * b) + for i in 1..b: result.add a + + var exportedVar*: int + const exportedConst* = 78 + type + ExportedType* = object + exportedField*: int + + +Method call syntax +------------------ + +For object oriented programming, the syntax ``obj.method(args)`` can be used +instead of ``method(obj, args)``. The parentheses can be omitted if there are no +remaining arguments: ``obj.len`` (instead of ``len(obj)``). + +This method call syntax is not restricted to objects, it can be used +to supply any type of first argument for procedures: + +.. code-block:: nim + + echo("abc".len) # is the same as echo(len("abc")) + echo("abc".toUpper()) + echo({'a', 'b', 'c'}.card) + stdout.writeln("Hallo") # the same as writeln(stdout, "Hallo") + +Another way to look at the method call syntax is that it provides the missing +postfix notation. + +See also: `Limitations of the method call syntax`_. + + +Properties +---------- +Nim has no need for *get-properties*: Ordinary get-procedures that are called +with the *method call syntax* achieve the same. But setting a value is +different; for this a special setter syntax is needed: + +.. code-block:: nim + + type + TSocket* = object of TObject + FHost: int # cannot be accessed from the outside of the module + # the `F` prefix is a convention to avoid clashes since + # the accessors are named `host` + + proc `host=`*(s: var TSocket, value: int) {.inline.} = + ## setter of hostAddr + s.FHost = value + + proc host*(s: TSocket): int {.inline.} = + ## getter of hostAddr + s.FHost + + var + s: TSocket + s.host = 34 # same as `host=`(s, 34) + + +Command invocation syntax +------------------------- + +Routines can be invoked without the ``()`` if the call is syntatically +a statement. This command invocation syntax also works for +expressions, but then only a single argument may follow. This restriction +means ``echo f 1, f 2`` is parsed as ``echo(f(1), f(2))`` and not as +``echo(f(1, f(2)))``. The method call syntax may be used to provide one +more argument in this case: + +.. code-block:: nim + proc optarg(x: int, y: int = 0): int = x + y + proc singlearg(x: int): int = 20*x + + echo optarg 1, " ", singlearg 2 # prints "1 40" + + let fail = optarg 1, optarg 8 # Wrong. Too many arguments for a command call + let x = optarg(1, optarg 8) # traditional procedure call with 2 arguments + let y = 1.optarg optarg 8 # same thing as above, w/o the parenthesis + assert x == y + +The command invocation syntax also can't have complex expressions as arguments. +For example: (`anonymous procs`_), ``if``, ``case`` or ``try``. The (`do +notation`_) is limited, but usable for a single proc (see the example in the +corresponding section). Function calls with no arguments still needs () to +distinguish between a call and the function itself as a first class value. + + +Closures +-------- + +Procedures can appear at the top level in a module as well as inside other +scopes, in which case they are called nested procs. A nested proc can access +local variables from its enclosing scope and if it does so it becomes a +closure. Any captured variables are stored in a hidden additional argument +to the closure (its environment) and they are accessed by reference by both +the closure and its enclosing scope (i.e. any modifications made to them are +visible in both places). The closure environment may be allocated on the heap +or on the stack if the compiler determines that this would be safe. + + +Anonymous Procs +--------------- + +Procs can also be treated as expressions, in which case it's allowed to omit +the proc's name. + +.. code-block:: nim + var cities = @["Frankfurt", "Tokyo", "New York"] + + cities.sort(proc (x,y: string): int = + cmp(x.len, y.len)) + + +Procs as expressions can appear both as nested procs and inside top level +executable code. + + +Do notation +----------- + +**Note:** The future of the ``do`` notation is uncertain. + +As a special more convenient notation, proc expressions involved in procedure +calls can use the ``do`` keyword: + +.. code-block:: nim + sort(cities) do (x,y: string) -> int: + cmp(x.len, y.len) + # Less parenthesis using the method plus command syntax: + cities = cities.map do (x:string) -> string: + "City of " & x + +``do`` is written after the parentheses enclosing the regular proc params. +The proc expression represented by the do block is appended to them. + +More than one ``do`` block can appear in a single call: + +.. code-block:: nim + proc performWithUndo(task: proc(), undo: proc()) = ... + + performWithUndo do: + # multiple-line block of code + # to perform the task + do: + # code to undo it + + +Nonoverloadable builtins +------------------------ + +The following builtin procs cannot be overloaded for reasons of implementation +simplicity (they require specialized semantic checking):: + + declared, defined, definedInScope, compiles, low, high, sizeOf, + is, of, shallowCopy, getAst, astToStr, spawn, procCall + +Thus they act more like keywords than like ordinary identifiers; unlike a +keyword however, a redefinition may `shadow`:idx: the definition in +the ``system`` module. From this list the following should not be written in dot +notation ``x.f`` since ``x`` cannot be type checked before it gets passed +to ``f``:: + + declared, defined, definedInScope, compiles, getAst, astToStr + + +Var parameters +-------------- +The type of a parameter may be prefixed with the ``var`` keyword: + +.. code-block:: nim + proc divmod(a, b: int; res, remainder: var int) = + res = a div b + remainder = a mod b + + var + x, y: int + + divmod(8, 5, x, y) # modifies x and y + assert x == 1 + assert y == 3 + +In the example, ``res`` and ``remainder`` are `var parameters`. +Var parameters can be modified by the procedure and the changes are +visible to the caller. The argument passed to a var parameter has to be +an l-value. Var parameters are implemented as hidden pointers. The +above example is equivalent to: + +.. code-block:: nim + proc divmod(a, b: int; res, remainder: ptr int) = + res[] = a div b + remainder[] = a mod b + + var + x, y: int + divmod(8, 5, addr(x), addr(y)) + assert x == 1 + assert y == 3 + +In the examples, var parameters or pointers are used to provide two +return values. This can be done in a cleaner way by returning a tuple: + +.. code-block:: nim + proc divmod(a, b: int): tuple[res, remainder: int] = + (a div b, a mod b) + + var t = divmod(8, 5) + + assert t.res == 1 + assert t.remainder == 3 + +One can use `tuple unpacking`:idx: to access the tuple's fields: + +.. code-block:: nim + var (x, y) = divmod(8, 5) # tuple unpacking + assert x == 1 + assert y == 3 + + +**Note**: ``var`` parameters are never necessary for efficient parameter +passing. Since non-var parameters cannot be modified the compiler is always +free to pass arguments by reference if it considers it can speed up execution. + + +Var return type +--------------- + +A proc, converter or iterator may return a ``var`` type which means that the +returned value is an l-value and can be modified by the caller: + +.. code-block:: nim + var g = 0 + + proc WriteAccessToG(): var int = + result = g + + WriteAccessToG() = 6 + assert g == 6 + +It is a compile time error if the implicitly introduced pointer could be +used to access a location beyond its lifetime: + +.. code-block:: nim + proc WriteAccessToG(): var int = + var g = 0 + result = g # Error! + +For iterators, a component of a tuple return type can have a ``var`` type too: + +.. code-block:: nim + iterator mpairs(a: var seq[string]): tuple[key: int, val: var string] = + for i in 0..a.high: + yield (i, a[i]) + +In the standard library every name of a routine that returns a ``var`` type +starts with the prefix ``m`` per convention. + + +Overloading of the subscript operator +------------------------------------- + +The ``[]`` subscript operator for arrays/openarrays/sequences can be overloaded. + + +Multi-methods +============= + +Procedures always use static dispatch. Multi-methods use dynamic +dispatch. + +.. code-block:: nim + type + Expression = object ## abstract base class for an expression + Literal = object of Expression + x: int + PlusExpr = object of Expression + a, b: ref Expression + + method eval(e: ref Expression): int = + # override this base method + quit "to override!" + + method eval(e: ref Literal): int = return e.x + + method eval(e: ref PlusExpr): int = + # watch out: relies on dynamic binding + result = eval(e.a) + eval(e.b) + + proc newLit(x: int): ref Literal = + new(result) + result.x = x + + proc newPlus(a, b: ref Expression): ref PlusExpr = + new(result) + result.a = a + result.b = b + + 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 +requires dynamic binding. + +In a multi-method all parameters that have an object type are used for the +dispatching: + +.. code-block:: nim + type + Thing = object + Unit = object of Thing + x: int + + method collide(a, b: Thing) {.inline.} = + quit "to override!" + + method collide(a: Thing, b: Unit) {.inline.} = + echo "1" + + method collide(a: Unit, b: Thing) {.inline.} = + echo "2" + + var + a, b: Unit + collide(a, b) # output: 2 + + +Invocation of a multi-method cannot be ambiguous: collide 2 is preferred over +collide 1 because the resolution works from left to right. +In the example ``Unit, Thing`` is preferred over ``Thing, Unit``. + +**Performance note**: Nim does not produce a virtual method table, but +generates dispatch trees. This avoids the expensive indirect branch for method +calls and enables inlining. However, other optimizations like compile time +evaluation or dead code elimination do not work with methods. + + +Iterators and the for statement +=============================== + +The `for`:idx: statement is an abstract mechanism to iterate over the elements +of a container. It relies on an `iterator`:idx: to do so. Like ``while`` +statements, ``for`` statements open an `implicit block`:idx:, so that they +can be left with a ``break`` statement. + +The ``for`` loop declares iteration variables - their scope reaches until the +end of the loop body. The iteration variables' types are inferred by the +return type of the iterator. + +An iterator is similar to a procedure, except that it can be called in the +context of a ``for`` loop. Iterators provide a way to specify the iteration over +an abstract type. A key role in the execution of a ``for`` loop plays the +``yield`` statement in the called iterator. Whenever a ``yield`` statement is +reached the data is bound to the ``for`` loop variables and control continues +in the body of the ``for`` loop. The iterator's local variables and execution +state are automatically saved between calls. Example: + +.. code-block:: nim + # this definition exists in the system module + iterator items*(a: string): char {.inline.} = + var i = 0 + while i < len(a): + yield a[i] + inc(i) + + for ch in items("hello world"): # `ch` is an iteration variable + echo(ch) + +The compiler generates code as if the programmer would have written this: + +.. code-block:: nim + var i = 0 + while i < len(a): + var ch = a[i] + echo(ch) + inc(i) + +If the iterator yields a tuple, there can be as many iteration variables +as there are components in the tuple. The i'th iteration variable's type is +the type of the i'th component. In other words, implicit tuple unpacking in a +for loop context is supported. + +Implict items/pairs invocations +------------------------------- + +If the for loop expression ``e`` does not denote an iterator and the for loop +has exactly 1 variable, the for loop expression is rewritten to ``items(e)``; +ie. an ``items`` iterator is implicitly invoked: + +.. code-block:: nim + for x in [1,2,3]: echo x + +If the for loop has exactly 2 variables, a ``pairs`` iterator is implicitly +invoked. + +Symbol lookup of the identifiers ``items``/``pairs`` is performed after +the rewriting step, so that all overloadings of ``items``/``pairs`` are taken +into account. + + +First class iterators +--------------------- + +There are 2 kinds of iterators in Nim: *inline* and *closure* iterators. +An `inline iterator`:idx: is an iterator that's always inlined by the compiler +leading to zero overhead for the abstraction, but may result in a heavy +increase in code size. Inline iterators are second class citizens; +They can be passed as parameters only to other inlining code facilities like +templates, macros and other inline iterators. + +In contrast to that, a `closure iterator`:idx: can be passed around more freely: + +.. code-block:: nim + iterator count0(): int {.closure.} = + yield 0 + + iterator count2(): int {.closure.} = + var x = 1 + yield x + inc x + yield x + + proc invoke(iter: iterator(): int {.closure.}) = + for x in iter(): echo x + + invoke(count0) + invoke(count2) + +Closure iterators have other restrictions than inline iterators: + +1. ``yield`` in a closure iterator can not occur in a ``try`` statement. +2. For now, a closure iterator cannot be evaluated at compile time. +3. ``return`` is allowed in a closure iterator (but rarely useful). +4. Both inline and closure iterators cannot be recursive. + +Iterators that are neither marked ``{.closure.}`` nor ``{.inline.}`` explicitly +default to being inline, but that this may change in future versions of the +implementation. + +The ``iterator`` type is always of the calling convention ``closure`` +implicitly; the following example shows how to use iterators to implement +a `collaborative tasking`:idx: system: + +.. code-block:: nim + # simple tasking: + type + Task = iterator (ticker: int) + + iterator a1(ticker: int) {.closure.} = + echo "a1: A" + yield + echo "a1: B" + yield + echo "a1: C" + yield + echo "a1: D" + + iterator a2(ticker: int) {.closure.} = + echo "a2: A" + yield + echo "a2: B" + yield + echo "a2: C" + + proc runTasks(t: varargs[Task]) = + var ticker = 0 + while true: + let x = t[ticker mod t.len] + if finished(x): break + x(ticker) + inc ticker + + runTasks(a1, a2) + +The builtin ``system.finished`` can be used to determine if an iterator has +finished its operation; no exception is raised on an attempt to invoke an +iterator that has already finished its work. + +Closure iterators are *resumable functions* and so one has to provide the +arguments to every call. To get around this limitation one can capture +parameters of an outer factory proc: + +.. code-block:: nim + proc mycount(a, b: int): iterator (): int = + result = iterator (): int = + var x = a + while x <= b: + yield x + inc x + + let foo = mycount(1, 4) + + for f in foo(): + echo f + +.. + Implicit return type + -------------------- + + Since inline interators must always produce values that will be consumed in + a for loop, the compiler will implicity use the ``auto`` return type if no + type is given by the user. In contrast, since closure iterators can be used + as a collaborative tasking system, ``void`` is a valid return type for them. + + +Converters +========== + +A converter is like an ordinary proc except that it enhances +the "implicitly convertible" type relation (see `Convertible relation`_): + +.. code-block:: nim + # bad style ahead: Nim is not C. + converter toBool(x: int): bool = x != 0 + + if 4: + echo "compiles" + + +A converter can also be explicitly invoked for improved readability. Note that +implicit converter chaining is not supported: If there is a converter from +type A to type B and from type B to type C the implicit conversion from A to C +is not provided. diff --git a/doc/manual/special_ops.txt b/doc/manual/special_ops.txt new file mode 100644 index 000000000..46135f129 --- /dev/null +++ b/doc/manual/special_ops.txt @@ -0,0 +1,54 @@ +Special Operators +================= + +dot operators +------------- + +Nim offers a special family of dot operators that can be used to +intercept and rewrite proc call and field access attempts, referring +to previously undeclared symbol names. They can be used to provide a +fluent interface to objects lying outside the static confines of the +type system such as values from dynamic scripting languages +or dynamic file formats such as JSON or XML. + +When Nim encounters an expression that cannot be resolved by the +standard overload resolution rules, the current scope will be searched +for a dot operator that can be matched against a re-written form of +the expression, where the unknown field or proc name is converted to +an additional static string parameter: + +.. code-block:: nim + a.b # becomes `.`(a, "b") + a.b(c, d) # becomes `.`(a, "b", c, d) + +The matched dot operators can be symbols of any callable kind (procs, +templates and macros), depending on the desired effect: + +.. code-block:: nim + proc `.` (js: PJsonNode, field: string): JSON = js[field] + + var js = parseJson("{ x: 1, y: 2}") + echo js.x # outputs 1 + echo js.y # outputs 2 + +The following dot operators are available: + +operator `.` +------------ +This operator will be matched against both field accesses and method calls. + +operator `.()` +--------------- +This operator will be matched exclusively against method calls. It has higher +precedence than the `.` operator and this allows one to handle expressions like +`x.y` and `x.y()` differently if one is interfacing with a scripting language +for example. + +operator `.=` +------------- +This operator will be matched against assignments to missing fields. + +.. code-block:: nim + a.b = c # becomes `.=`(a, "b", c) + + diff --git a/doc/manual/stmts.txt b/doc/manual/stmts.txt new file mode 100644 index 000000000..e31be7121 --- /dev/null +++ b/doc/manual/stmts.txt @@ -0,0 +1,650 @@ +Statements and expressions +========================== + +Nim uses the common statement/expression paradigm: Statements do not +produce a value in contrast to expressions. However, some expressions are +statements. + +Statements are separated into `simple statements`:idx: and +`complex statements`:idx:. +Simple statements are statements that cannot contain other statements like +assignments, calls or the ``return`` statement; complex statements can +contain other statements. To avoid the `dangling else problem`:idx:, complex +statements always have to be intended. The details can be found in the grammar. + + +Statement list expression +------------------------- + +Statements can also occur in an expression context that looks +like ``(stmt1; stmt2; ...; ex)``. This is called +an statement list expression or ``(;)``. The type +of ``(stmt1; stmt2; ...; ex)`` is the type of ``ex``. All the other statements +must be of type ``void``. (One can use ``discard`` to produce a ``void`` type.) +``(;)`` does not introduce a new scope. + + +Discard statement +----------------- + +Example: + +.. code-block:: nim + proc p(x, y: int): int = + result = x + y + + discard p(3, 4) # discard the return value of `p` + +The ``discard`` statement evaluates its expression for side-effects and +throws the expression's resulting value away. + +Ignoring the return value of a procedure without using a discard statement is +a static error. + +The return value can be ignored implicitly if the called proc/iterator has +been declared with the `discardable`:idx: pragma: + +.. code-block:: nim + proc p(x, y: int): int {.discardable.} = + result = x + y + + p(3, 4) # now valid + +An empty ``discard`` statement is often used as a null statement: + +.. code-block:: nim + proc classify(s: string) = + case s[0] + of SymChars, '_': echo "an identifier" + of '0'..'9': echo "a number" + else: discard + + +Var statement +------------- + +Var statements declare new local and global variables and +initialize them. A comma separated list of variables can be used to specify +variables of the same type: + +.. code-block:: nim + + var + a: int = 0 + x, y, z: int + +If an initializer is given the type can be omitted: the variable is then of the +same type as the initializing expression. Variables are always initialized +with a default value if there is no initializing expression. The default +value depends on the type and is always a zero in binary. + +============================ ============================================== +Type default value +============================ ============================================== +any integer type 0 +any float 0.0 +char '\\0' +bool false +ref or pointer type nil +procedural type nil +sequence nil (*not* ``@[]``) +string nil (*not* "") +tuple[x: A, y: B, ...] (default(A), default(B), ...) + (analogous for objects) +array[0..., T] [default(T), ...] +range[T] default(T); this may be out of the valid range +T = enum cast[T](0); this may be an invalid value +============================ ============================================== + + +The implicit initialization can be avoided for optimization reasons with the +`noinit`:idx: pragma: + +.. code-block:: nim + var + a {.noInit.}: array [0..1023, char] + +If a proc is annotated with the ``noinit`` pragma this refers to its implicit +``result`` variable: + +.. code-block:: nim + proc returnUndefinedValue: int {.noinit.} = discard + + +The implicit initialization can be also prevented by the `requiresInit`:idx: +type pragma. The compiler requires an explicit initialization then. However +it does a `control flow analysis`:idx: to prove the variable has been +initialized and does not rely on syntactic properties: + +.. code-block:: nim + type + TMyObject = object {.requiresInit.} + + proc p() = + # the following is valid: + var x: TMyObject + if someCondition(): + x = a() + else: + x = a() + use x + +let statement +------------- + +A ``let`` statement declares new local and global `single assignment`:idx: +variables and binds a value to them. The syntax is the of the ``var`` +statement, except that the keyword ``var`` is replaced by the keyword ``let``. +Let variables are not l-values and can thus not be passed to ``var`` parameters +nor can their address be taken. They cannot be assigned new values. + +For let variables the same pragmas are available as for ordinary variables. + + +Const section +------------- + +`Constants`:idx: are symbols which are bound to a value. The constant's value +cannot change. The compiler must be able to evaluate the expression in a +constant declaration at compile time. + +Nim contains a sophisticated compile-time evaluator, so procedures which +have no side-effect can be used in constant expressions too: + +.. code-block:: nim + import strutils + const + constEval = contains("abc", 'b') # computed at compile time! + + +The rules for compile-time computability are: + +1. Literals are compile-time computable. +2. Type conversions are compile-time computable. +3. Procedure calls of the form ``p(X)`` are compile-time computable if + ``p`` is a proc without side-effects (see the `noSideEffect pragma`_ + for details) and if ``X`` is a (possibly empty) list of compile-time + computable arguments. + + +Constants cannot be of type ``ptr``, ``ref``, ``var`` or ``object``, nor can +they contain such a type. + + +Static statement/expression +--------------------------- + +A static statement/expression can be used to enforce compile +time evaluation explicitly. Enforced compile time evaluation can even evaluate +code that has side effects: + +.. code-block:: + + static: + echo "echo at compile time" + +It's a static error if the compiler cannot perform the evaluation at compile +time. + +The current implementation poses some restrictions for compile time +evaluation: Code which contains ``cast`` or makes use of the foreign function +interface cannot be evaluated at compile time. Later versions of Nim will +support the FFI at compile time. + + +If statement +------------ + +Example: + +.. code-block:: nim + + var name = readLine(stdin) + + if name == "Andreas": + echo("What a nice name!") + elif name == "": + echo("Don't you have a name?") + else: + echo("Boring name...") + +The ``if`` statement is a simple way to make a branch in the control flow: +The expression after the keyword ``if`` is evaluated, if it is true +the corresponding statements after the ``:`` are executed. Otherwise +the expression after the ``elif`` is evaluated (if there is an +``elif`` branch), if it is true the corresponding statements after +the ``:`` are executed. This goes on until the last ``elif``. If all +conditions fail, the ``else`` part is executed. If there is no ``else`` +part, execution continues with the statement after the ``if`` statement. + +The scoping for an ``if`` statement is slightly subtle to support an important +use case. A new scope starts for the ``if``/``elif`` condition and ends after +the corresponding *then* block: + +.. code-block:: nim + if {| (let m = input =~ re"(\w+)=\w+"; m.isMatch): + echo "key ", m[0], " value ", m[1] |} + elif {| (let m = input =~ re""; m.isMatch): + echo "new m in this scope" |} + else: + # 'm' not declared here + +In the example the scopes have been enclosed in ``{| |}``. + + +Case statement +-------------- + +Example: + +.. code-block:: nim + + case readline(stdin) + of "delete-everything", "restart-computer": + echo("permission denied") + of "go-for-a-walk": echo("please yourself") + else: echo("unknown command") + + # indentation of the branches is also allowed; and so is an optional colon + # after the selecting expression: + case readline(stdin): + of "delete-everything", "restart-computer": + echo("permission denied") + of "go-for-a-walk": echo("please yourself") + else: echo("unknown command") + + +The ``case`` statement is similar to the if statement, but it represents +a multi-branch selection. The expression after the keyword ``case`` is +evaluated and if its value is in a *slicelist* the corresponding statements +(after the ``of`` keyword) are executed. If the value is not in any +given *slicelist* the ``else`` part is executed. If there is no ``else`` +part and not all possible values that ``expr`` can hold occur in a +``slicelist``, a static error occurs. This holds only for expressions of +ordinal types. "All possible values" of ``expr`` are determined by ``expr``'s +type. To suppress the static error an ``else`` part with an +empty ``discard`` statement should be used. + +For non ordinal types it is not possible to list every possible value and so +these always require an ``else`` part. + +As a special semantic extension, an expression in an ``of`` branch of a case +statement may evaluate to a set or array constructor; the set or array is then +expanded into a list of its elements: + +.. code-block:: nim + const + SymChars: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'} + + proc classify(s: string) = + case s[0] + of SymChars, '_': echo "an identifier" + of '0'..'9': echo "a number" + else: echo "other" + + # is equivalent to: + proc classify(s: string) = + case s[0] + of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '_': echo "an identifier" + of '0'..'9': echo "a number" + else: echo "other" + + +When statement +-------------- + +Example: + +.. code-block:: nim + + when sizeof(int) == 2: + echo("running on a 16 bit system!") + elif sizeof(int) == 4: + echo("running on a 32 bit system!") + elif sizeof(int) == 8: + echo("running on a 64 bit system!") + else: + echo("cannot happen!") + +The ``when`` statement is almost identical to the ``if`` statement with some +exceptions: + +* Each condition (``expr``) has to be a constant expression (of type ``bool``). +* The statements do not open a new scope. +* The statements that belong to the expression that evaluated to true are + translated by the compiler, the other statements are not checked for + semantics! However, each condition is checked for semantics. + +The ``when`` statement enables conditional compilation techniques. As +a special syntactic extension, the ``when`` construct is also available +within ``object`` definitions. + + +Return statement +---------------- + +Example: + +.. code-block:: nim + return 40+2 + +The ``return`` statement ends the execution of the current procedure. +It is only allowed in procedures. If there is an ``expr``, this is syntactic +sugar for: + +.. code-block:: nim + result = expr + return result + + +``return`` without an expression is a short notation for ``return result`` if +the proc has a return type. The `result`:idx: variable is always the return +value of the procedure. It is automatically declared by the compiler. As all +variables, ``result`` is initialized to (binary) zero: + +.. code-block:: nim + proc returnZero(): int = + # implicitly returns 0 + + +Yield statement +--------------- + +Example: + +.. code-block:: nim + yield (1, 2, 3) + +The ``yield`` statement is used instead of the ``return`` statement in +iterators. It is only valid in iterators. Execution is returned to the body +of the for loop that called the iterator. Yield does not end the iteration +process, but execution is passed back to the iterator if the next iteration +starts. See the section about iterators (`Iterators and the for statement`_) +for further information. + + +Block statement +--------------- + +Example: + +.. code-block:: nim + var found = false + block myblock: + for i in 0..3: + for j in 0..3: + if a[j][i] == 7: + found = true + break myblock # leave the block, in this case both for-loops + echo(found) + +The block statement is a means to group statements to a (named) ``block``. +Inside the block, the ``break`` statement is allowed to leave the block +immediately. A ``break`` statement can contain a name of a surrounding +block to specify which block is to leave. + + +Break statement +--------------- + +Example: + +.. code-block:: nim + break + +The ``break`` statement is used to leave a block immediately. If ``symbol`` +is given, it is the name of the enclosing block that is to leave. If it is +absent, the innermost block is left. + + +While statement +--------------- + +Example: + +.. code-block:: nim + echo("Please tell me your password: \n") + var pw = readLine(stdin) + while pw != "12345": + echo("Wrong password! Next try: \n") + pw = readLine(stdin) + + +The ``while`` statement is executed until the ``expr`` evaluates to false. +Endless loops are no error. ``while`` statements open an `implicit block`, +so that they can be left with a ``break`` statement. + + +Continue statement +------------------ + +A ``continue`` statement leads to the immediate next iteration of the +surrounding loop construct. It is only allowed within a loop. A continue +statement is syntactic sugar for a nested block: + +.. code-block:: nim + while expr1: + stmt1 + continue + stmt2 + +Is equivalent to: + +.. code-block:: nim + while expr1: + block myBlockName: + stmt1 + break myBlockName + stmt2 + + +Assembler statement +------------------- + +The direct embedding of assembler code into Nim code is supported +by the unsafe ``asm`` statement. Identifiers in the assembler code that refer to +Nim identifiers shall be enclosed in a special character which can be +specified in the statement's pragmas. The default special character is ``'`'``: + +.. code-block:: nim + {.push stackTrace:off.} + proc addInt(a, b: int): int = + # a in eax, and b in edx + asm """ + mov eax, `a` + add eax, `b` + jno theEnd + call `raiseOverflow` + theEnd: + """ + {.pop.} + +If the GNU assembler is used, quotes and newlines are inserted automatically: + +.. code-block:: nim + proc addInt(a, b: int): int = + asm """ + addl %%ecx, %%eax + jno 1 + call `raiseOverflow` + 1: + :"=a"(`result`) + :"a"(`a`), "c"(`b`) + """ + +Instead of: + +.. code-block:: nim + proc addInt(a, b: int): int = + asm """ + "addl %%ecx, %%eax\n" + "jno 1\n" + "call `raiseOverflow`\n" + "1: \n" + :"=a"(`result`) + :"a"(`a`), "c"(`b`) + """ + +Using statement +--------------- + +**Warning**: The ``using`` statement is highly experimental and has to be +explicitly enabled with the `experimental`:idx: pragma or command line option! + +The using statement provides syntactic convenience for procs that +heavily use a single contextual parameter. When applied to a variable or a +constant, it will instruct Nim to automatically consider the used symbol as +a hidden leading parameter for any procedure calls, following the using +statement in the current scope. Thus, it behaves much like the hidden `this` +parameter available in some object-oriented programming languages. + +.. code-block:: nim + + var s = socket() + using s + + connect(host, port) + send(data) + + while true: + let line = readLine(timeout) + ... + + +When applied to a callable symbol, it brings the designated symbol in the +current scope. Thus, it can be used to disambiguate between imported symbols +from different modules having the same name. + +.. code-block:: nim + import windows, sdl + using sdl.SetTimer + +Note that ``using`` only *adds* to the current context, it doesn't remove or +replace, **neither** does it create a new scope. What this means is that if one +applies this to multiple variables the compiler will find conflicts in what +variable to use: + +.. code-block:: nim + var a, b = "kill it" + using a + add(" with fire") + using b + add(" with water") + echo a + echo b + +When the compiler reaches the second ``add`` call, both ``a`` and ``b`` could +be used with the proc, so one gets ``Error: expression '(a|b)' has no type (or +is ambiguous)``. To solve this one would need to nest ``using`` with a +``block`` statement so as to control the reach of the ``using`` statement. + +If expression +------------- + +An `if expression` is almost like an if statement, but it is an expression. +Example: + +.. code-block:: nim + var y = if x > 8: 9 else: 10 + +An if expression always results in a value, so the ``else`` part is +required. ``Elif`` parts are also allowed. + +When expression +--------------- + +Just like an `if expression`, but corresponding to the when statement. + +Case expression +--------------- + +The `case expression` is again very similar to the case statement: + +.. code-block:: nim + var favoriteFood = case animal + of "dog": "bones" + of "cat": "mice" + elif animal.endsWith"whale": "plankton" + else: + echo "I'm not sure what to serve, but everybody loves ice cream" + "ice cream" + +As seen in the above example, the case expression can also introduce side +effects. When multiple statements are given for a branch, Nim will use +the last expression as the result value, much like in an `expr` template. + +Table constructor +----------------- + +A table constructor is syntactic sugar for an array constructor: + +.. code-block:: nim + {"key1": "value1", "key2", "key3": "value2"} + + # is the same as: + [("key1", "value1"), ("key2", "value2"), ("key3", "value2")] + + +The empty table can be written ``{:}`` (in contrast to the empty set +which is ``{}``) which is thus another way to write as the empty array +constructor ``[]``. This slightly unusal way of supporting tables +has lots of advantages: + +* The order of the (key,value)-pairs is preserved, thus it is easy to + support ordered dicts with for example ``{key: val}.newOrderedTable``. +* A table literal can be put into a ``const`` section and the compiler + can easily put it into the executable's data section just like it can + for arrays and the generated data section requires a minimal amount + of memory. +* Every table implementation is treated equal syntactically. +* Apart from the minimal syntactic sugar the language core does not need to + know about tables. + + +Type conversions +---------------- +Syntactically a `type conversion` is like a procedure call, but a +type name replaces the procedure name. A type conversion is always +safe in the sense that a failure to convert a type to another +results in an exception (if it cannot be determined statically). + +Ordinary procs are often preferred over type conversions in Nim: For instance, +``$`` is the ``toString`` operator by convention and ``toFloat`` and ``toInt`` +can be used to convert from floating point to integer or vice versa. + + +Type casts +---------- +Example: + +.. code-block:: nim + cast[int](x) + +Type casts are a crude mechanism to interpret the bit pattern of +an expression as if it would be of another type. Type casts are +only needed for low-level programming and are inherently unsafe. + + +The addr operator +----------------- +The ``addr`` operator returns the address of an l-value. If the type of the +location is ``T``, the `addr` operator result is of the type ``ptr T``. An +address is always an untraced reference. Taking the address of an object that +resides on the stack is **unsafe**, as the pointer may live longer than the +object on the stack and can thus reference a non-existing object. One can get +the address of variables, but one can't use it on variables declared through +``let`` statements: + +.. code-block:: nim + + let t1 = "Hello" + var + t2 = t1 + t3 : pointer = addr(t2) + echo repr(addr(t2)) + # --> ref 0x7fff6b71b670 --> 0x10bb81050"Hello" + echo cast[ptr string](t3)[] + # --> Hello + # The following line doesn't compile: + echo repr(addr(t1)) + # Error: expression has no address diff --git a/doc/manual/syntax.txt b/doc/manual/syntax.txt new file mode 100644 index 000000000..c975e7f48 --- /dev/null +++ b/doc/manual/syntax.txt @@ -0,0 +1,110 @@ +Syntax +====== + +This section lists Nim's standard syntax. How the parser handles +the indentation is already described in the `Lexical Analysis`_ section. + +Nim allows user-definable operators. +Binary operators have 11 different levels of precedence. + + + +Associativity +------------- + +Binary operators whose first character is ``^`` or its last character +is ``>`` are right-associative, all other binary operators are left-associative. + +Exception: The single "greater than" ``>`` operator is left-associative too. + +Operators ending in ``>`` but longer than a single character are +called `arrow like`:idx:. + + +Precedence +---------- + +Unary operators always bind stronger than any binary +operator: ``$a + b`` is ``($a) + b`` and not ``$(a + b)``. + +If an unary operator's first character is ``@`` it is a `sigil-like`:idx: +operator which binds stronger than a ``primarySuffix``: ``@x.abc`` is parsed +as ``(@x).abc`` whereas ``$x.abc`` is parsed as ``$(x.abc)``. + + +For binary operators that are not keywords the precedence is determined by the +following rules: + +If the operator ends with ``=`` and its first character is none of +``<``, ``>``, ``!``, ``=``, ``~``, ``?``, it is an *assignment operator* which +has the lowest precedence. + +Otherwise precedence is determined by the first character. + +================ =============================================== ================== =============== +Precedence level Operators First character Terminal symbol +================ =============================================== ================== =============== + 10 (highest) ``$ ^`` OP10 + 9 ``* / div mod shl shr %`` ``* % \ /`` OP9 + 8 ``+ -`` ``+ ~ |`` OP8 + 7 ``&`` ``&`` OP7 + 6 ``..`` ``.`` OP6 + 5 ``== <= < >= > != in notin is isnot not of`` ``= < > !`` OP5 + 4 ``and`` OP4 + 3 ``or xor`` OP3 + 2 ``@ : ?`` OP2 + 1 *assignment operator* (like ``+=``, ``*=``) OP1 + 0 (lowest) *arrow like operator* (like ``->``, ``=>``) OP0 +================ =============================================== ================== =============== + + +Strong spaces +------------- + +The number of spaces preceeding a non-keyword operator affects precedence +if the experimental parser directive ``#!strongSpaces`` is used. Indentation +is not used to determine the number of spaces. If 2 or more operators have the +same number of preceding spaces the precedence table applies, so ``1 + 3 * 4`` +is still parsed as ``1 + (3 * 4)``, but ``1+3 * 4`` is parsed as ``(1+3) * 4``: + +.. code-block:: nim + #! strongSpaces + if foo+4 * 4 == 8 and b&c | 9 ++ + bar: + echo "" + # is parsed as + if ((foo+4)*4 == 8) and (((b&c) | 9) ++ bar): echo "" + + +Furthermore whether an operator is used a prefix operator is affected by the +number of spaces: + +.. code-block:: nim + #! strongSpaces + echo $foo + # is parsed as + echo($foo) + +This also affects whether ``[]``, ``{}``, ``()`` are parsed as constructors +or as accessors: + +.. code-block:: nim + #! strongSpaces + echo (1,2) + # is parsed as + echo((1,2)) + +Only 0, 1, 2, 4 or 8 spaces are allowed to specify precedence and it is +enforced that infix operators have the same amount of spaces before and after +them. This rules does not apply when a newline follows after the operator, +then only the preceding spaces are considered. + + +Grammar +------- + +The grammar's start symbol is ``module``. + +.. include:: grammar.txt + :literal: + diff --git a/doc/manual/taint.txt b/doc/manual/taint.txt new file mode 100644 index 000000000..84f0c68b1 --- /dev/null +++ b/doc/manual/taint.txt @@ -0,0 +1,20 @@ +Taint mode +========== + +The Nim compiler and most parts of the standard library support +a taint mode. Input strings are declared with the `TaintedString`:idx: +string type declared in the ``system`` module. + +If the taint mode is turned on (via the ``--taintMode:on`` command line +option) it is a distinct string type which helps to detect input +validation errors: + +.. code-block:: nim + echo "your name: " + var name: TaintedString = stdin.readline + # it is safe here to output the name without any input validation, so + # we simply convert `name` to string to make the compiler happy: + echo "hi, ", name.string + +If the taint mode is turned off, ``TaintedString`` is simply an alias for +``string``. diff --git a/doc/manual/templates.txt b/doc/manual/templates.txt new file mode 100644 index 000000000..64bf71a96 --- /dev/null +++ b/doc/manual/templates.txt @@ -0,0 +1,436 @@ +Templates +========= + +A template is a simple form of a macro: It is a simple substitution +mechanism that operates on Nim's abstract syntax trees. It is processed in +the semantic pass of the compiler. + +The syntax to *invoke* a template is the same as calling a procedure. + +Example: + +.. code-block:: nim + template `!=` (a, b: expr): expr = + # this definition exists in the System module + not (a == b) + + assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6)) + +The ``!=``, ``>``, ``>=``, ``in``, ``notin``, ``isnot`` operators are in fact +templates: + +| ``a > b`` is transformed into ``b < a``. +| ``a in b`` is transformed into ``contains(b, a)``. +| ``notin`` and ``isnot`` have the obvious meanings. + +The "types" of templates can be the symbols ``expr`` (stands for *expression*), +``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type +description*). These are "meta types", they can only be used in certain +contexts. Real types can be used too; this implies that expressions are +expected. + + +Ordinary vs immediate templates +------------------------------- + +There are two different kinds of templates: immediate templates and +ordinary templates. Ordinary templates take part in overloading resolution. As +such their arguments need to be type checked before the template is invoked. +So ordinary templates cannot receive undeclared identifiers: + +.. code-block:: nim + + template declareInt(x: expr) = + var x: int + + declareInt(x) # error: unknown identifier: 'x' + +An ``immediate`` template does not participate in overload resolution and so +its arguments are not checked for semantics before invocation. So they can +receive undeclared identifiers: + +.. code-block:: nim + + template declareInt(x: expr) {.immediate.} = + var x: int + + declareInt(x) # valid + + +Passing a code block to a template +---------------------------------- + +If there is a ``stmt`` parameter it should be the last in the template +declaration, because statements are passed to a template via a +special ``:`` syntax: + +.. code-block:: nim + + template withFile(f, fn, mode: expr, actions: stmt): stmt {.immediate.} = + var f: TFile + if open(f, fn, mode): + try: + actions + finally: + close(f) + else: + quit("cannot open: " & fn) + + withFile(txt, "ttempl3.txt", fmWrite): + txt.writeln("line 1") + txt.writeln("line 2") + +In the example the two ``writeln`` statements are bound to the ``actions`` +parameter. + + +Symbol binding in templates +--------------------------- + +A template is a `hygienic`:idx: macro and so opens a new scope. Most symbols are +bound from the definition scope of the template: + +.. code-block:: nim + # Module A + var + lastId = 0 + + template genId*: expr = + inc(lastId) + lastId + +.. code-block:: nim + # Module B + import A + + echo genId() # Works as 'lastId' has been bound in 'genId's defining scope + +As in generics symbol binding can be influenced via ``mixin`` or ``bind`` +statements. + + + +Identifier construction +----------------------- + +In templates identifiers can be constructed with the backticks notation: + +.. code-block:: nim + + template typedef(name: expr, typ: typedesc) {.immediate.} = + type + `T name`* {.inject.} = typ + `P name`* {.inject.} = ref `T name` + + typedef(myint, int) + var x: PMyInt + +In the example ``name`` is instantiated with ``myint``, so \`T name\` becomes +``Tmyint``. + + +Lookup rules for template parameters +------------------------------------ + +A parameter ``p`` in a template is even substituted in the expression ``x.p``. +Thus template arguments can be used as field names and a global symbol can be +shadowed by the same argument name even when fully qualified: + +.. code-block:: nim + # module 'm' + + type + TLev = enum + levA, levB + + var abclev = levB + + template tstLev(abclev: TLev) = + echo abclev, " ", m.abclev + + tstLev(levA) + # produces: 'levA levA' + +But the global symbol can properly be captured by a ``bind`` statement: + +.. code-block:: nim + # module 'm' + + type + TLev = enum + levA, levB + + var abclev = levB + + template tstLev(abclev: TLev) = + bind m.abclev + echo abclev, " ", m.abclev + + tstLev(levA) + # produces: 'levA levB' + + +Hygiene in templates +-------------------- + +Per default templates are `hygienic`:idx:\: Local identifiers declared in a +template cannot be accessed in the instantiation context: + +.. code-block:: nim + + template newException*(exceptn: typedesc, message: string): expr = + var + e: ref exceptn # e is implicitly gensym'ed here + new(e) + e.msg = message + e + + # so this works: + let e = "message" + raise newException(EIO, e) + + +Whether a symbol that is declared in a template is exposed to the instantiation +scope is controlled by the `inject`:idx: and `gensym`:idx: pragmas: gensym'ed +symbols are not exposed but inject'ed are. + +The default for symbols of entity ``type``, ``var``, ``let`` and ``const`` +is ``gensym`` and for ``proc``, ``iterator``, ``converter``, ``template``, +``macro`` is ``inject``. However, if the name of the entity is passed as a +template parameter, it is an inject'ed symbol: + +.. code-block:: nim + template withFile(f, fn, mode: expr, actions: stmt): stmt {.immediate.} = + block: + var f: TFile # since 'f' is a template param, it's injected implicitly + ... + + withFile(txt, "ttempl3.txt", fmWrite): + txt.writeln("line 1") + txt.writeln("line 2") + + +The ``inject`` and ``gensym`` pragmas are second class annotations; they have +no semantics outside of a template definition and cannot be abstracted over: + +.. code-block:: nim + {.pragma myInject: inject.} + + template t() = + var x {.myInject.}: int # does NOT work + + +To get rid of hygiene in templates, one can use the `dirty`:idx: pragma for +a template. ``inject`` and ``gensym`` have no effect in ``dirty`` templates. + + + +Limitations of the method call syntax +------------------------------------- + +The expression ``x`` in ``x.f`` needs to be semantically checked (that means +symbol lookup and type checking) before it can be decided that it needs to be +rewritten to ``f(x)``. Therefore the dot syntax has some limiations when it +is used to invoke templates/macros: + +.. code-block:: nim + template declareVar(name: expr): stmt = + const name {.inject.} = 45 + + # Doesn't compile: + unknownIdentifier.declareVar + + +Another common example is this: + +.. code-block:: nim + from sequtils import toSeq + + iterator something: string = + yield "Hello" + yield "World" + + var info = toSeq(something()) + +The problem here is that the compiler already decided that ``something()`` as +an iterator is not callable in this context before ``toSeq`` gets its +chance to convert it into a sequence. + + +Macros +====== + +A macro is a special kind of low level template. Macros can be used +to implement `domain specific languages`:idx:. Like templates, macros come in +the 2 flavors *immediate* and *ordinary*. + +While macros enable advanced compile-time code transformations, they +cannot change Nim's syntax. However, this is no real restriction because +Nim's syntax is flexible enough anyway. + +To write macros, one needs to know how the Nim concrete syntax is converted +to an abstract syntax tree. + +There are two ways to invoke a macro: +(1) invoking a macro like a procedure call (`expression macros`) +(2) invoking a macro with the special ``macrostmt`` syntax (`statement macros`) + + +Expression Macros +----------------- + +The following example implements a powerful ``debug`` command that accepts a +variable number of arguments: + +.. code-block:: nim + # to work with Nim syntax trees, we need an API that is defined in the + # ``macros`` module: + import macros + + macro debug(n: varargs[expr]): stmt = + # `n` is a Nim AST that contains the whole macro invocation + # this macro returns a list of statements: + result = newNimNode(nnkStmtList, n) + # iterate over any argument that is passed to this macro: + for i in 0..n.len-1: + # add a call to the statement list that writes the expression; + # `toStrLit` converts an AST to its string representation: + add(result, newCall("write", newIdentNode("stdout"), toStrLit(n[i]))) + # add a call to the statement list that writes ": " + add(result, newCall("write", newIdentNode("stdout"), newStrLitNode(": "))) + # add a call to the statement list that writes the expressions value: + add(result, newCall("writeln", newIdentNode("stdout"), n[i])) + + var + a: array [0..10, int] + x = "some string" + a[0] = 42 + a[1] = 45 + + debug(a[0], a[1], x) + +The macro call expands to: + +.. code-block:: nim + write(stdout, "a[0]") + write(stdout, ": ") + writeln(stdout, a[0]) + + write(stdout, "a[1]") + write(stdout, ": ") + writeln(stdout, a[1]) + + write(stdout, "x") + write(stdout, ": ") + writeln(stdout, x) + + +Arguments that are passed to a ``varargs`` parameter are wrapped in an array +constructor expression. This is why ``debug`` iterates over all of ``n``'s +children. + + +BindSym +------- + +The above ``debug`` macro relies on the fact that ``write``, ``writeln`` and +``stdout`` are declared in the system module and thus visible in the +instantiating context. There is a way to use bound identifiers +(aka `symbols`:idx:) instead of using unbound identifiers. The ``bindSym`` +builtin can be used for that: + +.. code-block:: nim + import macros + + macro debug(n: varargs[expr]): stmt = + result = newNimNode(nnkStmtList, n) + for i in 0..n.len-1: + # we can bind symbols in scope via 'bindSym': + add(result, newCall(bindSym"write", bindSym"stdout", toStrLit(n[i]))) + add(result, newCall(bindSym"write", bindSym"stdout", newStrLitNode(": "))) + add(result, newCall(bindSym"writeln", bindSym"stdout", n[i])) + + var + a: array [0..10, int] + x = "some string" + a[0] = 42 + a[1] = 45 + + debug(a[0], a[1], x) + +The macro call expands to: + +.. code-block:: nim + write(stdout, "a[0]") + write(stdout, ": ") + writeln(stdout, a[0]) + + write(stdout, "a[1]") + write(stdout, ": ") + writeln(stdout, a[1]) + + write(stdout, "x") + write(stdout, ": ") + writeln(stdout, x) + +However, the symbols ``write``, ``writeln`` and ``stdout`` are already bound +and are not looked up again. As the example shows, ``bindSym`` does work with +overloaded symbols implicitly. + + +Statement Macros +---------------- + +Statement macros are defined just as expression macros. However, they are +invoked by an expression following a colon. + +The following example outlines a macro that generates a lexical analyzer from +regular expressions: + +.. code-block:: nim + import macros + + macro case_token(n: stmt): stmt = + # creates a lexical analyzer from regular expressions + # ... (implementation is an exercise for the reader :-) + discard + + case_token: # this colon tells the parser it is a macro statement + of r"[A-Za-z_]+[A-Za-z_0-9]*": + return tkIdentifier + of r"0-9+": + return tkInteger + of r"[\+\-\*\?]+": + return tkOperator + else: + return tkUnknown + + +**Style note**: For code readability, it is the best idea to use the least +powerful programming construct that still suffices. So the "check list" is: + +(1) Use an ordinary proc/iterator, if possible. +(2) Else: Use a generic proc/iterator, if possible. +(3) Else: Use a template, if possible. +(4) Else: Use a macro. + + +Macros as pragmas +----------------- + +Whole routines (procs, iterators etc.) can also be passed to a template or +a macro via the pragma notation: + +.. code-block:: nim + template m(s: stmt) = discard + + proc p() {.m.} = discard + +This is a simple syntactic transformation into: + +.. code-block:: nim + template m(s: stmt) = discard + + m: + proc p() = discard + diff --git a/doc/manual/threads.txt b/doc/manual/threads.txt new file mode 100644 index 000000000..245545f6f --- /dev/null +++ b/doc/manual/threads.txt @@ -0,0 +1,210 @@ +Threads +======= + +To enable thread support the ``--threads:on`` command line switch needs to +be used. The ``system`` module then contains several threading primitives. +See the `threads `_ and `channels `_ modules +for the low level thread API. There are also high level parallelism constructs +available. See `spawn`_ for further details. + +Nim's memory model for threads is quite different than that of other common +programming languages (C, Pascal, Java): Each thread has its own (garbage +collected) heap and sharing of memory is restricted to global variables. This +helps to prevent race conditions. GC efficiency is improved quite a lot, +because the GC never has to stop other threads and see what they reference. +Memory allocation requires no lock at all! This design easily scales to massive +multicore processors that are becoming the norm. + + +Thread pragma +------------- + +A proc that is executed as a new thread of execution should be marked by the +``thread`` pragma for reasons of readability. The compiler checks for +violations of the `no heap sharing restriction`:idx:\: This restriction implies +that it is invalid to construct a data structure that consists of memory +allocated from different (thread local) heaps. + +A thread proc is passed to ``createThread`` or ``spawn`` and invoked +indirectly; so the ``thread`` pragma implies ``procvar``. + + +GC safety +--------- + +We call a proc ``p`` `GC safe`:idx: when it doesn't access any global variable +that contains GC'ed memory (``string``, ``seq``, ``ref`` or a closure) either +directly or indirectly through a call to a GC unsafe proc. + +The `gcsafe`:idx: annotation can be used to mark a proc to be gcsafe, +otherwise this property is inferred by the compiler. Note that ``noSideEfect`` +implies ``gcsafe``. The only way to create a thread is via ``spawn`` or +``createThead``. ``spawn`` is usually the preferable method. Either way +the invoked proc must not use ``var`` parameters nor must any of its parameters +contain a ``ref`` or ``closure`` type. This enforces +the *no heap sharing restriction*. + +Routines that are imported from C are always assumed to be ``gcsafe``. +To enable the GC-safety checking the ``--threadAnalysis:on`` command line +switch must be used. This is a temporary workaround to ease the porting effort +from old code to the new threading model. In the future the thread analysis +will always be performed. + + +Future directions: + +- A shared GC'ed heap might be provided. + + +Threadvar pragma +---------------- + +A global variable can be marked with the ``threadvar`` pragma; it is +a `thread-local`:idx: variable then: + +.. code-block:: nim + var checkpoints* {.threadvar.}: seq[string] + +Due to implementation restrictions thread local variables cannot be +initialized within the ``var`` section. (Every thread local variable needs to +be replicated at thread creation.) + + +Threads and exceptions +---------------------- + +The interaction between threads and exceptions is simple: A *handled* exception +in one thread cannot affect any other thread. However, an *unhandled* exception +in one thread terminates the whole *process*! + + + +Parallel & Spawn +================ + +Nim has two flavors of parallelism: +1) `Structured`:idx: parallelism via the ``parallel`` statement. +2) `Unstructured`:idx: parallelism via the standalone ``spawn`` statement. + +Nim has a builtin thread pool that can be used for CPU intensive tasks. For +IO intensive tasks the ``async`` and ``await`` features should be +used instead. Both parallel and spawn need the `threadpool `_ +module to work. + +Somewhat confusingly, ``spawn`` is also used in the ``parallel`` statement +with slightly different semantics. ``spawn`` always takes a call expression of +the form ``f(a, ...)``. Let ``T`` be ``f``'s return type. If ``T`` is ``void`` +then ``spawn``'s return type is also ``void`` otherwise it is ``FlowVar[T]``. + +Within a ``parallel`` section sometimes the ``FlowVar[T]`` is eliminated +to ``T``. This happens when ``T`` does not contain any GC'ed memory. +The compiler can ensure the location in ``location = spawn f(...)`` is not +read prematurely within a ``parallel`` section and so there is no need for +the overhead of an indirection via ``FlowVar[T]`` to ensure correctness. + +**Note**: Currently exceptions are not propagated between ``spawn``'ed tasks! + + +Spawn statement +--------------- + +`spawn`:idx: can be used to pass a task to the thread pool: + +.. code-block:: nim + import threadpool + + proc processLine(line: string) = + discard "do some heavy lifting here" + + for x in lines("myinput.txt"): + spawn processLine(x) + sync() + +For reasons of type safety and implementation simplicity the expression +that ``spawn`` takes is restricted: + +* It must be a call expression ``f(a, ...)``. +* ``f`` must be ``gcsafe``. +* ``f`` must not have the calling convention ``closure``. +* ``f``'s parameters may not be of type ``var``. + This means one has to use raw ``ptr``'s for data passing reminding the + programmer to be careful. +* ``ref`` parameters are deeply copied which is a subtle semantic change and + can cause performance problems but ensures memory safety. This deep copy + is performed via ``system.deepCopy`` and so can be overriden. +* For *safe* data exchange between ``f`` and the caller a global ``TChannel`` + needs to be used. However, since spawn can return a result, often no further + communication is required. + + +``spawn`` executes the passed expression on the thread pool and returns +a `data flow variable`:idx: ``FlowVar[T]`` that can be read from. The reading +with the ``^`` operator is **blocking**. However, one can use ``awaitAny`` to +wait on multiple flow variables at the same time: + +.. code-block:: nim + import threadpool, ... + + # wait until 2 out of 3 servers received the update: + proc main = + var responses = newSeq[RawFlowVar](3) + for i in 0..2: + responses[i] = spawn tellServer(Update, "key", "value") + var index = awaitAny(responses) + assert index >= 0 + responses.del(index) + discard awaitAny(responses) + +Data flow variables ensure that no data races +are possible. Due to technical limitations not every type ``T`` is possible in +a data flow variable: ``T`` has to be of the type ``ref``, ``string``, ``seq`` +or of a type that doesn't contain a type that is garbage collected. This +restriction is not hard to work-around in practice. + + + +Parallel statement +------------------ + +Example: + +.. code-block:: nim + # Compute PI in an inefficient way + import strutils, math, threadpool + + proc term(k: float): float = 4 * math.pow(-1, k) / (2*k + 1) + + proc pi(n: int): float = + var ch = newSeq[float](n+1) + parallel: + for k in 0..ch.high: + ch[k] = spawn term(float(k)) + for k in 0..ch.high: + result += ch[k] + + echo formatFloat(pi(5000)) + + +The parallel statement is the preferred mechanism to introduce parallelism +in a Nim program. A subset of the Nim language is valid within a +``parallel`` section. This subset is checked to be free of data races at +compile time. A sophisticated `disjoint checker`:idx: ensures that no data +races are possible even though shared memory is extensively supported! + +The subset is in fact the full language with the following +restrictions / changes: + +* ``spawn`` within a ``parallel`` section has special semantics. +* Every location of the form ``a[i]`` and ``a[i..j]`` and ``dest`` where + ``dest`` is part of the pattern ``dest = spawn f(...)`` has to be + provably disjoint. This is called the *disjoint check*. +* Every other complex location ``loc`` that is used in a spawned + proc (``spawn f(loc)``) has to be immutable for the duration of + the ``parallel`` section. This is called the *immutability check*. Currently + it is not specified what exactly "complex location" means. We need to make + this an optimization! +* Every array access has to be provably within bounds. This is called + the *bounds check*. +* Slices are optimized so that no copy is performed. This optimization is not + yet performed for ordinary slices outside of a ``parallel`` section. Slices + are also special in that they currently do not support negative indexes! diff --git a/doc/manual/trmacros.txt b/doc/manual/trmacros.txt new file mode 100644 index 000000000..715c9f850 --- /dev/null +++ b/doc/manual/trmacros.txt @@ -0,0 +1,361 @@ +Term rewriting macros +===================== + +Term rewriting macros are macros or templates that have not only +a *name* but also a *pattern* that is searched for after the semantic checking +phase of the compiler: This means they provide an easy way to enhance the +compilation pipeline with user defined optimizations: + +.. code-block:: nim + template optMul{`*`(a, 2)}(a: int): int = a+a + + let x = 3 + echo x * 2 + +The compiler now rewrites ``x * 2`` as ``x + x``. The code inside the +curlies is the pattern to match against. The operators ``*``, ``**``, +``|``, ``~`` have a special meaning in patterns if they are written in infix +notation, so to match verbatim against ``*`` the ordinary function call syntax +needs to be used. + + +Unfortunately optimizations are hard to get right and even the tiny example +is **wrong**: + +.. code-block:: nim + template optMul{`*`(a, 2)}(a: int): int = a+a + + proc f(): int = + echo "side effect!" + result = 55 + + echo f() * 2 + +We cannot duplicate 'a' if it denotes an expression that has a side effect! +Fortunately Nim supports side effect analysis: + +.. code-block:: nim + template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a + + proc f(): int = + echo "side effect!" + result = 55 + + echo f() * 2 # not optimized ;-) + +So what about ``2 * a``? We should tell the compiler ``*`` is commutative. We +cannot really do that however as the following code only swaps arguments +blindly: + +.. code-block:: nim + template mulIsCommutative{`*`(a, b)}(a, b: int): int = b*a + +What optimizers really need to do is a *canonicalization*: + +.. code-block:: nim + template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = b*a + +The ``int{lit}`` parameter pattern matches against an expression of +type ``int``, but only if it's a literal. + + + +Parameter constraints +--------------------- + +The `parameter constraint`:idx: expression can use the operators ``|`` (or), +``&`` (and) and ``~`` (not) and the following predicates: + +=================== ===================================================== +Predicate Meaning +=================== ===================================================== +``atom`` The matching node has no children. +``lit`` The matching node is a literal like "abc", 12. +``sym`` The matching node must be a symbol (a bound + identifier). +``ident`` The matching node must be an identifier (an unbound + identifier). +``call`` The matching AST must be a call/apply expression. +``lvalue`` The matching AST must be an lvalue. +``sideeffect`` The matching AST must have a side effect. +``nosideeffect`` The matching AST must have no side effect. +``param`` A symbol which is a parameter. +``genericparam`` A symbol which is a generic parameter. +``module`` A symbol which is a module. +``type`` A symbol which is a type. +``var`` A symbol which is a variable. +``let`` A symbol which is a ``let`` variable. +``const`` A symbol which is a constant. +``result`` The special ``result`` variable. +``proc`` A symbol which is a proc. +``method`` A symbol which is a method. +``iterator`` A symbol which is an iterator. +``converter`` A symbol which is a converter. +``macro`` A symbol which is a macro. +``template`` A symbol which is a template. +``field`` A symbol which is a field in a tuple or an object. +``enumfield`` A symbol which is a field in an enumeration. +``forvar`` A for loop variable. +``label`` A label (used in ``block`` statements). +``nk*`` The matching AST must have the specified kind. + (Example: ``nkIfStmt`` denotes an ``if`` statement.) +``alias`` States that the marked parameter needs to alias + with *some* other parameter. +``noalias`` States that *every* other parameter must not alias + with the marked parameter. +=================== ===================================================== + +The ``alias`` and ``noalias`` predicates refer not only to the matching AST, +but also to every other bound parameter; syntactially they need to occur after +the ordinary AST predicates: + +.. code-block:: nim + template ex{a = b + c}(a: int{noalias}, b, c: int) = + # this transformation is only valid if 'b' and 'c' do not alias 'a': + a = b + inc a, c + + +Pattern operators +----------------- + +The operators ``*``, ``**``, ``|``, ``~`` have a special meaning in patterns +if they are written in infix notation. + + +The ``|`` operator +~~~~~~~~~~~~~~~~~~ + +The ``|`` operator if used as infix operator creates an ordered choice: + +.. code-block:: nim + template t{0|1}(): expr = 3 + let a = 1 + # outputs 3: + echo a + +The matching is performed after the compiler performed some optimizations like +constant folding, so the following does not work: + +.. code-block:: nim + template t{0|1}(): expr = 3 + # outputs 1: + echo 1 + +The reason is that the compiler already transformed the 1 into "1" for +the ``echo`` statement. However, a term rewriting macro should not change the +semantics anyway. In fact they can be deactived with the ``--patterns:off`` +command line option or temporarily with the ``patterns`` pragma. + + +The ``{}`` operator +~~~~~~~~~~~~~~~~~~~ + +A pattern expression can be bound to a pattern parameter via the ``expr{param}`` +notation: + +.. code-block:: nim + template t{(0|1|2){x}}(x: expr): expr = x+1 + let a = 1 + # outputs 2: + echo a + + +The ``~`` operator +~~~~~~~~~~~~~~~~~~ + +The ``~`` operator is the **not** operator in patterns: + +.. code-block:: nim + template t{x = (~x){y} and (~x){z}}(x, y, z: bool): stmt = + x = y + if x: x = z + + var + a = false + b = true + c = false + a = b and c + echo a + + +The ``*`` operator +~~~~~~~~~~~~~~~~~~ + +The ``*`` operator can *flatten* a nested binary expression like ``a & b & c`` +to ``&(a, b, c)``: + +.. code-block:: nim + var + calls = 0 + + proc `&&`(s: varargs[string]): string = + result = s[0] + for i in 1..len(s)-1: result.add s[i] + inc calls + + template optConc{ `&&` * a }(a: string): expr = &&a + + let space = " " + echo "my" && (space & "awe" && "some " ) && "concat" + + # check that it's been optimized properly: + doAssert calls == 1 + + +The second operator of `*` must be a parameter; it is used to gather all the +arguments. The expression ``"my" && (space & "awe" && "some " ) && "concat"`` +is passed to ``optConc`` in ``a`` as a special list (of kind ``nkArgList``) +which is flattened into a call expression; thus the invocation of ``optConc`` +produces: + +.. code-block:: nim + `&&`("my", space & "awe", "some ", "concat") + + +The ``**`` operator +~~~~~~~~~~~~~~~~~~~ + +The ``**`` is much like the ``*`` operator, except that it gathers not only +all the arguments, but also the matched operators in reverse polish notation: + +.. code-block:: nim + import macros + + type + TMatrix = object + dummy: int + + proc `*`(a, b: TMatrix): TMatrix = discard + proc `+`(a, b: TMatrix): TMatrix = discard + proc `-`(a, b: TMatrix): TMatrix = discard + proc `$`(a: TMatrix): string = result = $a.dummy + proc mat21(): TMatrix = + result.dummy = 21 + + macro optM{ (`+`|`-`|`*`) ** a }(a: TMatrix): expr = + echo treeRepr(a) + result = newCall(bindSym"mat21") + + var x, y, z: TMatrix + + echo x + y * z - x + +This passes the expression ``x + y * z - x`` to the ``optM`` macro as +an ``nnkArgList`` node containing:: + + Arglist + Sym "x" + Sym "y" + Sym "z" + Sym "*" + Sym "+" + Sym "x" + Sym "-" + +(Which is the reverse polish notation of ``x + y * z - x``.) + + +Parameters +---------- + +Parameters in a pattern are type checked in the matching process. If a +parameter is of the type ``varargs`` it is treated specially and it can match +0 or more arguments in the AST to be matched against: + +.. code-block:: nim + template optWrite{ + write(f, x) + ((write|writeln){w})(f, y) + }(x, y: varargs[expr], f: TFile, w: expr) = + w(f, x, y) + + + +Example: Partial evaluation +--------------------------- + +The following example shows how some simple partial evaluation can be +implemented with term rewriting: + +.. code-block:: nim + proc p(x, y: int; cond: bool): int = + result = if cond: x + y else: x - y + + template optP1{p(x, y, true)}(x, y: expr): expr = x + y + template optP2{p(x, y, false)}(x, y: expr): expr = x - y + + +Example: Hoisting +----------------- + +The following example shows how some form of hoisting can be implemented: + +.. code-block:: nim + import pegs + + template optPeg{peg(pattern)}(pattern: string{lit}): TPeg = + var gl {.global, gensym.} = peg(pattern) + gl + + for i in 0 .. 3: + echo match("(a b c)", peg"'(' @ ')'") + echo match("W_HI_Le", peg"\y 'while'") + +The ``optPeg`` template optimizes the case of a peg constructor with a string +literal, so that the pattern will only be parsed once at program startup and +stored in a global ``gl`` which is then re-used. This optimization is called +hoisting because it is comparable to classical loop hoisting. + + +AST based overloading +===================== + +Parameter constraints can also be used for ordinary routine parameters; these +constraints affect ordinary overloading resolution then: + +.. code-block:: nim + proc optLit(a: string{lit|`const`}) = + echo "string literal" + proc optLit(a: string) = + echo "no string literal" + + const + constant = "abc" + + var + variable = "xyz" + + optLit("literal") + optLit(constant) + optLit(variable) + +However, the constraints ``alias`` and ``noalias`` are not available in +ordinary routines. + + +Move optimization +----------------- + +The ``call`` constraint is particularly useful to implement a move +optimization for types that have copying semantics: + +.. code-block:: nim + proc `[]=`*(t: var TTable, key: string, val: string) = + ## puts a (key, value)-pair into `t`. The semantics of string require + ## a copy here: + let idx = findInsertionPosition(key) + t[idx] = key + t[idx] = val + + proc `[]=`*(t: var TTable, key: string{call}, val: string{call}) = + ## puts a (key, value)-pair into `t`. Optimized version that knows that + ## the strings are unique and thus don't need to be copied: + let idx = findInsertionPosition(key) + shallowCopy t[idx], key + shallowCopy t[idx], val + + var t: TTable + # overloading resolution ensures that the optimized []= is called here: + t[f()] = g() + diff --git a/doc/manual/type_bound_ops.txt b/doc/manual/type_bound_ops.txt new file mode 100644 index 000000000..64c6c325d --- /dev/null +++ b/doc/manual/type_bound_ops.txt @@ -0,0 +1,112 @@ +Type bound operations +===================== + +There are 3 operations that are bound to a type: + +1. Assignment +2. Destruction +3. Deep copying for communication between threads + +These operations can be *overriden* instead of *overloaded*. This means the +implementation is automatically lifted to structured types. For instance if type +``T`` has an overriden assignment operator ``=`` this operator is also used +for assignments of the type ``seq[T]``. Since these operations are bound to a +type they have to be bound to a nominal type for reasons of simplicity of +implementation: This means an overriden ``deepCopy`` for ``ref T`` is really +bound to ``T`` and not to ``ref T``. This also means that one cannot override +``deepCopy`` for both ``ptr T`` and ``ref T`` at the same time; instead a +helper distinct or object type has to be used for one pointer type. + + +operator `=` +------------ + +This operator is the assignment operator. Note that in the contexts +like ``let v = expr``, ``var v = expr``, ``parameter = defaultValue`` or for +parameter passing no assignment is performed. The ``override`` pragma is +optional for overriding ``=``. + +**Note**: Overriding of operator ``=`` is not yet implemented. + + +destructors +----------- + +A destructor must have a single parameter with a concrete type (the name of a +generic type is allowed too). The name of the destructor has to be ``destroy`` +and it need to be annotated with the ``override`` pragma. + +``destroy(v)`` will be automatically invoked for every local stack +variable ``v`` that goes out of scope. + +If a structured type features a field with destructable type and +the user has not provided an explicit implementation, a destructor for the +structured type will be automatically generated. Calls to any base class +destructors in both user-defined and generated destructors will be inserted. + +A destructor is attached to the type it destructs; expressions of this type +can then only be used in *destructible contexts* and as parameters: + +.. code-block:: nim + type + TMyObj = object + x, y: int + p: pointer + + proc destroy(o: var TMyObj) {.override.} = + if o.p != nil: dealloc o.p + + proc open: TMyObj = + result = TMyObj(x: 1, y: 2, p: alloc(3)) + + proc work(o: TMyObj) = + echo o.x + # No destructor invoked here for 'o' as 'o' is a parameter. + + proc main() = + # destructor automatically invoked at the end of the scope: + var x = open() + # valid: pass 'x' to some other proc: + work(x) + + # Error: usage of a type with a destructor in a non destructible context + echo open() + +A destructible context is currently only the following: + +1. The ``expr`` in ``var x = expr``. +2. The ``expr`` in ``let x = expr``. +3. The ``expr`` in ``return expr``. +4. The ``expr`` in ``result = expr`` where ``result`` is the special symbol + introduced by the compiler. + +These rules ensure that the construction is tied to a variable and can easily +be destructed at its scope exit. Later versions of the language will improve +the support of destructors. + +Be aware that destructors are not called for objects allocated with ``new``. +This may change in future versions of language, but for now the ``finalizer`` +parameter to ``new`` has to be used. + +**Note**: Destructors are still experimental and the spec might change +significantly in order to incorporate an escape analysis. + + +deepCopy +-------- + +``deepCopy`` is a builtin that is invoked whenever data is passed to +a ``spawn``'ed proc to ensure memory safety. The programmer can override its +behaviour for a specific ``ref`` or ``ptr`` type ``T``. (Later versions of the +language may weaken this restriction.) + +The signature has to be: + +.. code-block:: nim + proc deepCopy(x: T): T {.override.} + +This mechanism is used by most data structures that support shared memory like +channels to implement thread safe automatic memory management. + +The builtin ``deepCopy`` can even clone closures and their environments. See +the documentation of `spawn`_ for details. diff --git a/doc/manual/type_rel.txt b/doc/manual/type_rel.txt new file mode 100644 index 000000000..805b8637f --- /dev/null +++ b/doc/manual/type_rel.txt @@ -0,0 +1,192 @@ +Type relations +============== + +The following section defines several relations on types that are needed to +describe the type checking done by the compiler. + + +Type equality +------------- +Nim uses structural type equivalence for most types. Only for objects, +enumerations and distinct types name equivalence is used. The following +algorithm (in pseudo-code) determines type equality: + +.. code-block:: nim + proc typeEqualsAux(a, b: PType, + s: var set[PType * PType]): bool = + if (a,b) in s: return true + incl(s, (a,b)) + if a.kind == b.kind: + case a.kind + of int, intXX, float, floatXX, char, string, cstring, pointer, + bool, nil, void: + # leaf type: kinds identical; nothing more to check + result = true + of ref, ptr, var, set, seq, openarray: + result = typeEqualsAux(a.baseType, b.baseType, s) + of range: + result = typeEqualsAux(a.baseType, b.baseType, s) and + (a.rangeA == b.rangeA) and (a.rangeB == b.rangeB) + of array: + result = typeEqualsAux(a.baseType, b.baseType, s) and + typeEqualsAux(a.indexType, b.indexType, s) + of tuple: + # Note: tuple field names are ignored + if a.tupleLen == b.tupleLen: + for i in 0..a.tupleLen-1: + if not typeEqualsAux(a[i], b[i], s): return false + result = true + of object, enum, distinct: + result = a == b + of proc: + result = typeEqualsAux(a.parameterTuple, b.parameterTuple, s) and + typeEqualsAux(a.resultType, b.resultType, s) and + a.callingConvention == b.callingConvention + + proc typeEquals(a, b: PType): bool = + var s: set[PType * PType] = {} + result = typeEqualsAux(a, b, s) + +Since types are graphs which can have cycles, the above algorithm needs an +auxiliary set ``s`` to detect this case. + + +Type equality modulo type distinction +------------------------------------- + +The following algorithm (in pseudo-code) determines whether two types +are equal with no respect to ``distinct`` types. For brevity the cycle check +with an auxiliary set ``s`` is omitted: + +.. code-block:: nim + proc typeEqualsOrDistinct(a, b: PType): bool = + if a.kind == b.kind: + case a.kind + of int, intXX, float, floatXX, char, string, cstring, pointer, + bool, nil, void: + # leaf type: kinds identical; nothing more to check + result = true + of ref, ptr, var, set, seq, openarray: + result = typeEqualsOrDistinct(a.baseType, b.baseType) + of range: + result = typeEqualsOrDistinct(a.baseType, b.baseType) and + (a.rangeA == b.rangeA) and (a.rangeB == b.rangeB) + of array: + result = typeEqualsOrDistinct(a.baseType, b.baseType) and + typeEqualsOrDistinct(a.indexType, b.indexType) + of tuple: + if a.tupleLen == b.tupleLen: + for i in 0..a.tupleLen-1: + if not typeEqualsOrDistinct(a[i], b[i]): return false + result = true + of distinct: + result = typeEqualsOrDistinct(a.baseType, b.baseType) + of object, enum: + result = a == b + of proc: + result = typeEqualsOrDistinct(a.parameterTuple, b.parameterTuple) and + typeEqualsOrDistinct(a.resultType, b.resultType) and + a.callingConvention == b.callingConvention + elif a.kind == distinct: + result = typeEqualsOrDistinct(a.baseType, b) + elif b.kind == distinct: + result = typeEqualsOrDistinct(a, b.baseType) + + +Subtype relation +---------------- +If object ``a`` inherits from ``b``, ``a`` is a subtype of ``b``. This subtype +relation is extended to the types ``var``, ``ref``, ``ptr``: + +.. code-block:: nim + proc isSubtype(a, b: PType): bool = + if a.kind == b.kind: + case a.kind + of object: + var aa = a.baseType + while aa != nil and aa != b: aa = aa.baseType + result = aa == b + of var, ref, ptr: + result = isSubtype(a.baseType, b.baseType) + +.. XXX nil is a special value! + + +Convertible relation +-------------------- +A type ``a`` is **implicitly** convertible to type ``b`` iff the following +algorithm returns true: + +.. code-block:: nim + # XXX range types? + proc isImplicitlyConvertible(a, b: PType): bool = + case a.kind + of int: result = b in {int8, int16, int32, int64, uint, uint8, uint16, + uint32, uint64, float, float32, float64} + of int8: result = b in {int16, int32, int64, int} + of int16: result = b in {int32, int64, int} + of int32: result = b in {int64, int} + of uint: result = b in {uint32, uint64} + of uint8: result = b in {uint16, uint32, uint64} + of uint16: result = b in {uint32, uint64} + of uint32: result = b in {uint64} + of float: result = b in {float32, float64} + of float32: result = b in {float64, float} + of float64: result = b in {float32, float} + of seq: + result = b == openArray and typeEquals(a.baseType, b.baseType) + of array: + result = b == openArray and typeEquals(a.baseType, b.baseType) + if a.baseType == char and a.indexType.rangeA == 0: + result = b = cstring + of cstring, ptr: + result = b == pointer + of string: + result = b == cstring + +A type ``a`` is **explicitly** convertible to type ``b`` iff the following +algorithm returns true: + +.. code-block:: nim + proc isIntegralType(t: PType): bool = + result = isOrdinal(t) or t.kind in {float, float32, float64} + + proc isExplicitlyConvertible(a, b: PType): bool = + result = false + if isImplicitlyConvertible(a, b): return true + if typeEqualsOrDistinct(a, b): return true + if isIntegralType(a) and isIntegralType(b): return true + if isSubtype(a, b) or isSubtype(b, a): return true + +The convertible relation can be relaxed by a user-defined type +`converter`:idx:. + +.. code-block:: nim + converter toInt(x: char): int = result = ord(x) + + var + x: int + chr: char = 'a' + + # implicit conversion magic happens here + x = chr + echo x # => 97 + # you can use the explicit form too + x = chr.toInt + echo x # => 97 + +The type conversion ``T(a)`` is an L-value if ``a`` is an L-value and +``typeEqualsOrDistinct(T, type(a))`` holds. + + +Assignment compatibility +------------------------ + +An expression ``b`` can be assigned to an expression ``a`` iff ``a`` is an +`l-value` and ``isImplicitlyConvertible(b.typ, a.typ)`` holds. + + +Overloading resolution +---------------------- + +To be written. diff --git a/doc/manual/type_sections.txt b/doc/manual/type_sections.txt new file mode 100644 index 000000000..5413e896e --- /dev/null +++ b/doc/manual/type_sections.txt @@ -0,0 +1,23 @@ +Type sections +============= + +Example: + +.. code-block:: nim + type # example demonstrating mutually recursive types + PNode = ref TNode # a traced pointer to a TNode + TNode = object + le, ri: PNode # left and right subtrees + sym: ref TSym # leaves contain a reference to a TSym + + TSym = object # a symbol + name: string # the symbol's name + line: int # the line the symbol was declared in + code: PNode # the symbol's abstract syntax tree + +A type section begins with the ``type`` keyword. It contains multiple +type definitions. A type definition binds a type to a name. Type definitions +can be recursive or even mutually recursive. Mutually recursive types are only +possible within a single ``type`` section. Nominal types like ``objects`` +or ``enums`` can only be defined in a ``type`` section. + diff --git a/doc/manual/typedesc.txt b/doc/manual/typedesc.txt new file mode 100644 index 000000000..d1d8bc87a --- /dev/null +++ b/doc/manual/typedesc.txt @@ -0,0 +1,146 @@ +Special Types +============= + +static[T] +--------- + +**Note**: static[T] is still in development. + +As their name suggests, static params must be known at compile-time: + +.. code-block:: nim + + proc precompiledRegex(pattern: static[string]): TRegEx = + var res {.global.} = re(pattern) + return res + + precompiledRegex("/d+") # Replaces the call with a precompiled + # regex, stored in a global variable + + precompiledRegex(paramStr(1)) # Error, command-line options + # are not known at compile-time + + +For the purposes of code generation, all static params are treated as +generic params - the proc will be compiled separately for each unique +supplied value (or combination of values). + +Furthermore, the system module defines a `semistatic[T]` type than can be +used to declare procs accepting both static and run-time values, which can +optimize their body according to the supplied param using the `isStatic(p)` +predicate: + +.. code-block:: nim + + # The following proc will be compiled once for each unique static + # value and also once for the case handling all run-time values: + + proc re(pattern: semistatic[string]): TRegEx = + when isStatic(pattern): + result = precompiledRegex(pattern) + else: + result = compile(pattern) + +Static params can also appear in the signatures of generic types: + +.. code-block:: nim + + type + Matrix[M,N: static[int]; T: Number] = array[0..(M*N - 1), T] + # Note how `Number` is just a type constraint here, while + # `static[int]` requires us to supply a compile-time int value + + AffineTransform2D[T] = Matrix[3, 3, T] + AffineTransform3D[T] = Matrix[4, 4, T] + + var m1: AffineTransform3D[float] # OK + var m2: AffineTransform2D[string] # Error, `string` is not a `Number` + + +typedesc +-------- + +`typedesc` is a special type allowing one to treat types as compile-time values +(i.e. if types are compile-time values and all values have a type, then +typedesc must be their type). + +When used as a regular proc param, typedesc acts as a type class. The proc +will be instantiated for each unique type parameter and one can refer to the +instantiation type using the param name: + +.. code-block:: nim + + proc new(T: typedesc): ref T = + echo "allocating ", T.name + new(result) + + var n = TNode.new + var tree = new(TBinaryTree[int]) + +When multiple typedesc params are present, they act like a distinct type class +(i.e. they will bind freely to different types). To force a bind-once behavior +one can use a named alias or an explicit `typedesc` generic param: + +.. code-block:: nim + + # `type1` and `type2` are aliases for typedesc available from system.nim + proc acceptOnlyTypePairs(A, B: type1; C, D: type2) + proc acceptOnlyTypePairs[T: typedesc, U: typedesc](A, B: T; C, D: U) + +Once bound, typedesc params can appear in the rest of the proc signature: + +.. code-block:: nim + + template declareVariableWithType(T: typedesc, value: T) = + var x: T = value + + declareVariableWithType int, 42 + +When used with macros and .compileTime. procs on the other hand, the compiler +does not need to instantiate the code multiple times, because types then can be +manipulated using the unified internal symbol representation. In such context +typedesc acts as any other type. One can create variables, store typedesc +values inside containers and so on. For example, here is how one can create +a type-safe wrapper for the unsafe `printf` function from C: + +.. code-block:: nim + macro safePrintF(formatString: string{lit}, args: varargs[expr]): expr = + var i = 0 + for c in formatChars(formatString): + var expectedType = case c + of 'c': char + of 'd', 'i', 'x', 'X': int + of 'f', 'e', 'E', 'g', 'G': float + of 's': string + of 'p': pointer + else: EOutOfRange + + var actualType = args[i].getType + inc i + + if expectedType == EOutOfRange: + error c & " is not a valid format character" + elif expectedType != actualType: + error "type mismatch for argument ", i, ". expected type: ", + expectedType.name, ", actual type: ", actualType.name + + # keep the original callsite, but use cprintf instead + result = callsite() + result[0] = newIdentNode(!"cprintf") + + +Overload resolution can be further influenced by constraining the set of +types that will match the typedesc param: + +.. code-block:: nim + + template maxval(T: typedesc[int]): int = high(int) + template maxval(T: typedesc[float]): float = Inf + + var i = int.maxval + var f = float.maxval + var s = string.maxval # error, maxval is not implemented for string + +The constraint can be a concrete type or a type class. + + diff --git a/doc/manual/types.txt b/doc/manual/types.txt new file mode 100644 index 000000000..b028752e8 --- /dev/null +++ b/doc/manual/types.txt @@ -0,0 +1,1164 @@ +Types +===== + +All expressions have a type which is known at compile time. Nim +is statically typed. One can declare new types, which is in essence defining +an identifier that can be used to denote this custom type. + +These are the major type classes: + +* ordinal types (consist of integer, bool, character, enumeration + (and subranges thereof) types) +* floating point types +* string type +* structured types +* reference (pointer) type +* procedural type +* generic type + + +Ordinal types +------------- +Ordinal types have the following characteristics: + +- Ordinal types are countable and ordered. This property allows + the operation of functions as ``inc``, ``ord``, ``dec`` on ordinal types to + be defined. +- Ordinal values have a smallest possible value. Trying to count further + down than the smallest value gives a checked runtime or static error. +- Ordinal values have a largest possible value. Trying to count further + than the largest value gives a checked runtime or static error. + +Integers, bool, characters and enumeration types (and subranges of these +types) belong to ordinal types. For reasons of simplicity of implementation +the types ``uint`` and ``uint64`` are not ordinal types. + + +Pre-defined integer types +------------------------- +These integer types are pre-defined: + +``int`` + the generic signed integer type; its size is platform dependent and has the + same size as a pointer. This type should be used in general. An integer + literal that has no type suffix is of this type. + +intXX + additional signed integer types of XX bits use this naming scheme + (example: int16 is a 16 bit wide integer). + The current implementation supports ``int8``, ``int16``, ``int32``, ``int64``. + Literals of these types have the suffix 'iXX. + +``uint`` + the generic `unsigned integer`:idx: type; its size is platform dependent and + has the same size as a pointer. An integer literal with the type + suffix ``'u`` is of this type. + +uintXX + additional signed integer types of XX bits use this naming scheme + (example: uint16 is a 16 bit wide unsigned integer). + The current implementation supports ``uint8``, ``uint16``, ``uint32``, + ``uint64``. Literals of these types have the suffix 'uXX. + Unsigned operations all wrap around; they cannot lead to over- or + underflow errors. + + +In addition to the usual arithmetic operators for signed and unsigned integers +(``+ - *`` etc.) there are also operators that formally work on *signed* +integers but treat their arguments as *unsigned*: They are mostly provided +for backwards compatibility with older versions of the language that lacked +unsigned integer types. These unsigned operations for signed integers use +the ``%`` suffix as convention: + + +====================== ====================================================== +operation meaning +====================== ====================================================== +``a +% b`` unsigned integer addition +``a -% b`` unsigned integer subtraction +``a *% b`` unsigned integer multiplication +``a /% b`` unsigned integer division +``a %% b`` unsigned integer modulo operation +``a <% b`` treat ``a`` and ``b`` as unsigned and compare +``a <=% b`` treat ``a`` and ``b`` as unsigned and compare +``ze(a)`` extends the bits of ``a`` with zeros until it has the + width of the ``int`` type +``toU8(a)`` treats ``a`` as unsigned and converts it to an + unsigned integer of 8 bits (but still the + ``int8`` type) +``toU16(a)`` treats ``a`` as unsigned and converts it to an + unsigned integer of 16 bits (but still the + ``int16`` type) +``toU32(a)`` treats ``a`` as unsigned and converts it to an + unsigned integer of 32 bits (but still the + ``int32`` type) +====================== ====================================================== + +`Automatic type conversion`:idx: is performed in expressions where different +kinds of integer types are used: the smaller type is converted to the larger. + +A `narrowing type conversion`:idx: converts a larger to a smaller type (for +example ``int32 -> int16``. A `widening type conversion`:idx: converts a +smaller type to a larger type (for example ``int16 -> int32``). In Nim only +widening type conversions are *implicit*: + +.. code-block:: nim + var myInt16 = 5i16 + var myInt: int + myInt16 + 34 # of type ``int16`` + myInt16 + myInt # of type ``int`` + myInt16 + 2i32 # of type ``int32`` + +However, ``int`` literals are implicitly convertible to a smaller integer type +if the literal's value fits this smaller type and such a conversion is less +expensive than other implicit conversions, so ``myInt16 + 34`` produces +an ``int16`` result. + +For further details, see `Convertible relation`_. + + +Subrange types +-------------- +A subrange type is a range of values from an ordinal type (the base +type). To define a subrange type, one must specify it's limiting values: the +lowest and highest value of the type: + +.. code-block:: nim + type + Subrange = range[0..5] + + +``Subrange`` is a subrange of an integer which can only hold the values 0 +to 5. Assigning any other value to a variable of type ``Subrange`` is a +checked runtime error (or static error if it can be statically +determined). Assignments from the base type to one of its subrange types +(and vice versa) are allowed. + +A subrange type has the same size as its base type (``int`` in the example). + +Nim requires `interval arithmetic`:idx: for subrange types over a set +of built-in operators that involve constants: ``x %% 3`` is of +type ``range[0..2]``. The following built-in operators for integers are +affected by this rule: ``-``, ``+``, ``*``, ``min``, ``max``, ``succ``, +``pred``, ``mod``, ``div``, ``%%``, ``and`` (bitwise ``and``). + +Bitwise ``and`` only produces a ``range`` if one of its operands is a +constant *x* so that (x+1) is a number of two. +(Bitwise ``and`` is then a ``%%`` operation.) + +This means that the following code is accepted: + +.. code-block:: nim + case (x and 3) + 7 + of 7: echo "A" + of 8: echo "B" + of 9: echo "C" + of 10: echo "D" + # note: no ``else`` required as (x and 3) + 7 has the type: range[7..10] + + +Pre-defined floating point types +-------------------------------- + +The following floating point types are pre-defined: + +``float`` + the generic floating point type; its size is platform dependent + (the compiler chooses the processor's fastest floating point type). + This type should be used in general. + +floatXX + an implementation may define additional floating point types of XX bits using + this naming scheme (example: float64 is a 64 bit wide float). The current + implementation supports ``float32`` and ``float64``. Literals of these types + have the suffix 'fXX. + + +Automatic type conversion in expressions with different kinds +of floating point types is performed: See `Convertible relation`_ for further +details. Arithmetic performed on floating point types follows the IEEE +standard. Integer types are not converted to floating point types automatically +and vice versa. + +The IEEE standard defines five types of floating-point exceptions: + +* Invalid: operations with mathematically invalid operands, + for example 0.0/0.0, sqrt(-1.0), and log(-37.8). +* Division by zero: divisor is zero and dividend is a finite nonzero number, + for example 1.0/0.0. +* Overflow: operation produces a result that exceeds the range of the exponent, + for example MAXDOUBLE+0.0000000000001e308. +* Underflow: operation produces a result that is too small to be represented + as a normal number, for example, MINDOUBLE * MINDOUBLE. +* Inexact: operation produces a result that cannot be represented with infinite + precision, for example, 2.0 / 3.0, log(1.1) and 0.1 in input. + +The IEEE exceptions are either ignored at runtime or mapped to the +Nim exceptions: `FloatInvalidOpError`:idx:, `FloatDivByZeroError`:idx:, +`FloatOverflowError`:idx:, `FloatUnderflowError`:idx:, +and `FloatInexactError`:idx:. +These exceptions inherit from the `FloatingPointError`:idx: base class. + +Nim provides the pragmas `NaNChecks`:idx: and `InfChecks`:idx: to control +whether the IEEE exceptions are ignored or trap a Nim exception: + +.. code-block:: nim + {.NanChecks: on, InfChecks: on.} + var a = 1.0 + var b = 0.0 + echo b / b # raises FloatInvalidOpError + echo a / b # raises FloatOverflowError + +In the current implementation ``FloatDivByZeroError`` and ``FloatInexactError`` +are never raised. ``FloatOverflowError`` is raised instead of +``FloatDivByZeroError``. +There is also a `floatChecks`:idx: pragma that is a short-cut for the +combination of ``NaNChecks`` and ``InfChecks`` pragmas. ``floatChecks`` are +turned off as default. + +The only operations that are affected by the ``floatChecks`` pragma are +the ``+``, ``-``, ``*``, ``/`` operators for floating point types. + +An implementation should always use the maximum precision available to evaluate +floating pointer values at compile time; this means expressions like +``0.09'f32 + 0.01'f32 == 0.09'f64 + 0.01'f64`` are true. + + +Boolean type +------------ +The boolean type is named `bool`:idx: in Nim and can be one of the two +pre-defined values ``true`` and ``false``. Conditions in while, +if, elif, when statements need to be of type bool. + +This condition holds:: + + ord(false) == 0 and ord(true) == 1 + +The operators ``not, and, or, xor, <, <=, >, >=, !=, ==`` are defined +for the bool type. The ``and`` and ``or`` operators perform short-cut +evaluation. Example: + +.. code-block:: nim + + while p != nil and p.name != "xyz": + # p.name is not evaluated if p == nil + p = p.next + + +The size of the bool type is one byte. + + +Character type +-------------- +The character type is named ``char`` in Nim. Its size is one byte. +Thus it cannot represent an UTF-8 character, but a part of it. +The reason for this is efficiency: for the overwhelming majority of use-cases, +the resulting programs will still handle UTF-8 properly as UTF-8 was specially +designed for this. +Another reason is that Nim can support ``array[char, int]`` or +``set[char]`` efficiently as many algorithms rely on this feature. The +`TRune` type is used for Unicode characters, it can represent any Unicode +character. ``TRune`` is declared in the `unicode module `_. + + + + +Enumeration types +----------------- +Enumeration types define a new type whose values consist of the ones +specified. The values are ordered. Example: + +.. code-block:: nim + + type + Direction = enum + north, east, south, west + + +Now the following holds:: + + ord(north) == 0 + ord(east) == 1 + ord(south) == 2 + ord(west) == 3 + +Thus, north < east < south < west. The comparison operators can be used +with enumeration types. + +For better interfacing to other programming languages, the fields of enum +types can be assigned an explicit ordinal value. However, the ordinal values +have to be in ascending order. A field whose ordinal value is not +explicitly given is assigned the value of the previous field + 1. + +An explicit ordered enum can have *holes*: + +.. code-block:: nim + type + TokenType = enum + a = 2, b = 4, c = 89 # holes are valid + +However, it is then not an ordinal anymore, so it is not possible to use these +enums as an index type for arrays. The procedures ``inc``, ``dec``, ``succ`` +and ``pred`` are not available for them either. + + +The compiler supports the built-in stringify operator ``$`` for enumerations. +The stringify's result can be controlled by explicitly giving the string +values to use: + +.. code-block:: nim + + type + MyEnum = enum + valueA = (0, "my value A"), + valueB = "value B", + valueC = 2, + valueD = (3, "abc") + +As can be seen from the example, it is possible to both specify a field's +ordinal value and its string value by using a tuple. It is also +possible to only specify one of them. + +An enum can be marked with the ``pure`` pragma so that it's fields are not +added to the current scope, so they always need to be accessed +via ``MyEnum.value``: + +.. code-block:: nim + + type + MyEnum {.pure.} = enum + valueA, valueB, valueC, valueD + + echo valueA # error: Unknown identifier + echo MyEnum.valueA # works + + +String type +----------- +All string literals are of the type ``string``. A string in Nim is very +similar to a sequence of characters. However, strings in Nim are both +zero-terminated and have a length field. One can retrieve the length with the +builtin ``len`` procedure; the length never counts the terminating zero. +The assignment operator for strings always copies the string. +The ``&`` operator concatenates strings. + +Strings are compared by their lexicographical order. All comparison operators +are available. Strings can be indexed like arrays (lower bound is 0). Unlike +arrays, they can be used in case statements: + +.. code-block:: nim + + case paramStr(i) + of "-v": incl(options, optVerbose) + of "-h", "-?": incl(options, optHelp) + else: write(stdout, "invalid command line option!\n") + +Per convention, all strings are UTF-8 strings, but this is not enforced. For +example, when reading strings from binary files, they are merely a sequence of +bytes. The index operation ``s[i]`` means the i-th *char* of ``s``, not the +i-th *unichar*. The iterator ``runes`` from the `unicode module +`_ can be used for iteration over all Unicode characters. + + +cstring type +------------ +The ``cstring`` type represents a pointer to a zero-terminated char array +compatible to the type ``char*`` in Ansi C. Its primary purpose lies in easy +interfacing with C. The index operation ``s[i]`` means the i-th *char* of +``s``; however no bounds checking for ``cstring`` is performed making the +index operation unsafe. + +A Nim ``string`` is implicitly convertible +to ``cstring`` for convenience. If a Nim string is passed to a C-style +variadic proc, it is implicitly converted to ``cstring`` too: + +.. code-block:: nim + proc printf(formatstr: cstring) {.importc: "printf", varargs, + header: "".} + + printf("This works %s", "as expected") + +Even though the conversion is implicit, it is not *safe*: The garbage collector +does not consider a ``cstring`` to be a root and may collect the underlying +memory. However in practice this almost never happens as the GC considers +stack roots conservatively. One can use the builtin procs ``GC_ref`` and +``GC_unref`` to keep the string data alive for the rare cases where it does +not work. + +A `$` proc is defined for cstrings that returns a string. Thus to get a nim +string from a cstring: + +.. code-block:: nim + var str: string = "Hello!" + var cstr: cstring = s + var newstr: string = $cstr + + +Structured types +---------------- +A variable of a structured type can hold multiple values at the same +time. Structured types can be nested to unlimited levels. Arrays, sequences, +tuples, objects and sets belong to the structured types. + +Array and sequence types +------------------------ +Arrays are a homogeneous type, meaning that each element in the array +has the same type. Arrays always have a fixed length which is specified at +compile time (except for open arrays). They can be indexed by any ordinal type. +A parameter ``A`` may be an *open array*, in which case it is indexed by +integers from 0 to ``len(A)-1``. An array expression may be constructed by the +array constructor ``[]``. + +Sequences are similar to arrays but of dynamic length which may change +during runtime (like strings). Sequences are implemented as growable arrays, +allocating pieces of memory as items are added. A sequence ``S`` is always +indexed by integers from 0 to ``len(S)-1`` and its bounds are checked. +Sequences can be constructed by the array constructor ``[]`` in conjunction +with the array to sequence operator ``@``. Another way to allocate space for a +sequence is to call the built-in ``newSeq`` procedure. + +A sequence may be passed to a parameter that is of type *open array*. + +Example: + +.. code-block:: nim + + type + IntArray = array[0..5, int] # an array that is indexed with 0..5 + IntSeq = seq[int] # a sequence of integers + var + x: IntArray + y: IntSeq + x = [1, 2, 3, 4, 5, 6] # [] is the array constructor + y = @[1, 2, 3, 4, 5, 6] # the @ turns the array into a sequence + +The lower bound of an array or sequence may be received by the built-in proc +``low()``, the higher bound by ``high()``. The length may be +received by ``len()``. ``low()`` for a sequence or an open array always returns +0, as this is the first valid index. +One can append elements to a sequence with the ``add()`` proc or the ``&`` +operator, and remove (and get) the last element of a sequence with the +``pop()`` proc. + +The notation ``x[i]`` can be used to access the i-th element of ``x``. + +Arrays are always bounds checked (at compile-time or at runtime). These +checks can be disabled via pragmas or invoking the compiler with the +``--boundChecks:off`` command line switch. + + +Open arrays +----------- + +Often fixed size arrays turn out to be too inflexible; procedures should +be able to deal with arrays of different sizes. The `openarray`:idx: type +allows this; it can only be used for parameters. Openarrays are always +indexed with an ``int`` starting at position 0. The ``len``, ``low`` +and ``high`` operations are available for open arrays too. Any array with +a compatible base type can be passed to an openarray parameter, the index +type does not matter. In addition to arrays sequences can also be passed +to an open array parameter. + +The openarray type cannot be nested: multidimensional openarrays are not +supported because this is seldom needed and cannot be done efficiently. + + +Varargs +------- + +A ``varargs`` parameter is an openarray parameter that additionally +allows to pass a variable number of arguments to a procedure. The compiler +converts the list of arguments to an array implicitly: + +.. code-block:: nim + proc myWriteln(f: File, a: varargs[string]) = + for s in items(a): + write(f, s) + write(f, "\n") + + myWriteln(stdout, "abc", "def", "xyz") + # is transformed to: + myWriteln(stdout, ["abc", "def", "xyz"]) + +This transformation is only done if the varargs parameter is the +last parameter in the procedure header. It is also possible to perform +type conversions in this context: + +.. code-block:: nim + proc myWriteln(f: File, a: varargs[string, `$`]) = + for s in items(a): + write(f, s) + write(f, "\n") + + myWriteln(stdout, 123, "abc", 4.0) + # is transformed to: + myWriteln(stdout, [$123, $"def", $4.0]) + +In this example ``$`` is applied to any argument that is passed to the +parameter ``a``. (Note that ``$`` applied to strings is a nop.) + + + +Tuples and object types +----------------------- +A variable of a tuple or object type is a heterogeneous storage +container. +A tuple or object defines various named *fields* of a type. A tuple also +defines an *order* of the fields. Tuples are meant for heterogeneous storage +types with no overhead and few abstraction possibilities. The constructor ``()`` +can be used to construct tuples. The order of the fields in the constructor +must match the order of the tuple's definition. Different tuple-types are +*equivalent* if they specify the same types in the same +order. The *names* of the fields are ignored. + +The assignment operator for tuples copies each component. +The default assignment operator for objects copies each component. Overloading +of the assignment operator for objects is not possible, but this will change +in future versions of the compiler. + +.. code-block:: nim + + type + Person = tuple[name: string, age: int] # type representing a person: + # a person consists of a name + # and an age + var + person: Person + person = (name: "Peter", age: 30) + # the same, but less readable: + person = ("Peter", 30) + # the same, but with confusing tuple field names: + person = (creditCard: "Peter", id: 20) + +The implementation aligns the fields for best access performance. The alignment +is compatible with the way the C compiler does it. + +For consistency with ``object`` declarations, tuples in a ``type`` section +can also be defined with indentation instead of ``[]``: + +.. code-block:: nim + type + Person = tuple # type representing a person + name: string # a person consists of a name + age: natural # and an age + +Objects provide many features that tuples do not. Object provide inheritance +and information hiding. Objects have access to their type at runtime, so that +the ``of`` operator can be used to determine the object's type. + +.. code-block:: nim + type + Person {.inheritable.} = object + name*: string # the * means that `name` is accessible from other modules + age: int # no * means that the field is hidden + + Student = object of Person # a student is a person + id: int # with an id field + + var + student: Student + person: Person + assert(student of Student) # is true + +Object fields that should be visible from outside the defining module, have to +be marked by ``*``. In contrast to tuples, different object types are +never *equivalent*. Objects that have no ancestor are implicitly ``final`` +and thus have no hidden type field. One can use the ``inheritable`` pragma to +introduce new object roots apart from ``system.RootObj``. + + +Object construction +------------------- + +Objects can also be created with an `object construction expression`:idx: that +has the syntax ``T(fieldA: valueA, fieldB: valueB, ...)`` where ``T`` is +an ``object`` type or a ``ref object`` type: + +.. code-block:: nim + var student = Student(name: "Anton", age: 5, id: 3) + +For a ``ref object`` type ``system.new`` is invoked implicitly. + + +Object variants +--------------- +Often an object hierarchy is overkill in certain situations where simple +variant types are needed. + +An example: + +.. code-block:: nim + + # This is an example how an abstract syntax tree could be modelled in Nim + type + TNodeKind = enum # the different node types + nkInt, # a leaf with an integer value + nkFloat, # a leaf with a float value + nkString, # a leaf with a string value + nkAdd, # an addition + nkSub, # a subtraction + nkIf # an if statement + PNode = ref TNode + TNode = object + case kind: TNodeKind # the ``kind`` field is the discriminator + of nkInt: intVal: int + of nkFloat: floatVal: float + of nkString: strVal: string + of nkAdd, nkSub: + leftOp, rightOp: PNode + of nkIf: + condition, thenPart, elsePart: PNode + + # create a new case object: + var n = PNode(kind: nkIf, condition: nil) + # accessing n.thenPart is valid because the ``nkIf`` branch is active: + n.thenPart = PNode(kind: nkFloat, floatVal: 2.0) + + # the following statement raises an `EInvalidField` exception, because + # n.kind's value does not fit and the ``nkString`` branch is not active: + n.strVal = "" + + # invalid: would change the active object branch: + n.kind = nkInt + + var x = PNode(kind: nkAdd, leftOp: PNode(kind: nkInt, intVal: 4), + rightOp: PNode(kind: nkInt, intVal: 2)) + # valid: does not change the active object branch: + x.kind = nkSub + +As can been seen from the example, an advantage to an object hierarchy is that +no casting between different object types is needed. Yet, access to invalid +object fields raises an exception. + +The syntax of ``case`` in an object declaration follows closely the syntax of +the ``case`` statement: The branches in a ``case`` section may be indented too. + +In the example the ``kind`` field is called the `discriminator`:idx:\: For +safety its address cannot be taken and assignments to it are restricted: The +new value must not lead to a change of the active object branch. For an object +branch switch ``system.reset`` has to be used. + + +Set type +-------- + +.. include:: sets_fragment.txt + +Reference and pointer types +--------------------------- +References (similar to pointers in other programming languages) are a +way to introduce many-to-one relationships. This means different references can +point to and modify the same location in memory (also called `aliasing`:idx:). + +Nim distinguishes between `traced`:idx: and `untraced`:idx: references. +Untraced references are also called *pointers*. Traced references point to +objects of a garbage collected heap, untraced references point to +manually allocated objects or to objects somewhere else in memory. Thus +untraced references are *unsafe*. However for certain low-level operations +(accessing the hardware) untraced references are unavoidable. + +Traced references are declared with the **ref** keyword, untraced references +are declared with the **ptr** keyword. + +An empty subscript ``[]`` notation can be used to derefer a reference, +the ``addr`` procedure returns the address of an item. An address is always +an untraced reference. +Thus the usage of ``addr`` is an *unsafe* feature. + +The ``.`` (access a tuple/object field operator) +and ``[]`` (array/string/sequence index operator) operators perform implicit +dereferencing operations for reference types: + +.. code-block:: nim + + type + PNode = ref TNode + TNode = object + le, ri: PNode + data: int + + var + n: PNode + new(n) + n.data = 9 + # no need to write n[].data; in fact n[].data is highly discouraged! + +In order to simplify structural type checking, recursive tuples are not valid: + +.. code-block:: nim + # invalid recursion + type MyTuple = tuple[a: ref MyTuple] + +Likewise ``T = ref T`` is an invalid type. + +As a syntactical extension ``object`` types can be anonymous if +declared in a type section via the ``ref object`` or ``ptr object`` notations. +This feature is useful if an object should only gain reference semantics: + +.. code-block:: nim + + type + Node = ref object + le, ri: Node + data: int + + +To allocate a new traced object, the built-in procedure ``new`` has to be used. +To deal with untraced memory, the procedures ``alloc``, ``dealloc`` and +``realloc`` can be used. The documentation of the system module contains +further information. + +If a reference points to *nothing*, it has the value ``nil``. + +Special care has to be taken if an untraced object contains traced objects like +traced references, strings or sequences: in order to free everything properly, +the built-in procedure ``GCunref`` has to be called before freeing the untraced +memory manually: + +.. code-block:: nim + type + TData = tuple[x, y: int, s: string] + + # allocate memory for TData on the heap: + var d = cast[ptr TData](alloc0(sizeof(TData))) + + # create a new string on the garbage collected heap: + d.s = "abc" + + # tell the GC that the string is not needed anymore: + GCunref(d.s) + + # free the memory: + dealloc(d) + +Without the ``GCunref`` call the memory allocated for the ``d.s`` string would +never be freed. The example also demonstrates two important features for low +level programming: the ``sizeof`` proc returns the size of a type or value +in bytes. The ``cast`` operator can circumvent the type system: the compiler +is forced to treat the result of the ``alloc0`` call (which returns an untyped +pointer) as if it would have the type ``ptr TData``. Casting should only be +done if it is unavoidable: it breaks type safety and bugs can lead to +mysterious crashes. + +**Note**: The example only works because the memory is initialized to zero +(``alloc0`` instead of ``alloc`` does this): ``d.s`` is thus initialized to +``nil`` which the string assignment can handle. One needs to know low level +details like this when mixing garbage collected data with unmanaged memory. + +.. XXX finalizers for traced objects + + +Not nil annotation +------------------ + +All types for that ``nil`` is a valid value can be annotated to +exclude ``nil`` as a valid value with the ``not nil`` annotation: + +.. code-block:: nim + type + PObject = ref TObj not nil + TProc = (proc (x, y: int)) not nil + + proc p(x: PObject) = + echo "not nil" + + # compiler catches this: + p(nil) + + # and also this: + var x: PObject + p(x) + +The compiler ensures that every code path initializes variables which contain +not nilable pointers. The details of this analysis are still to be specified +here. + + +Memory regions +-------------- + +The types ``ref`` and ``ptr`` can get an optional ``region`` annotation. +A region has to be an object type. + +Regions are very useful to separate user space and kernel memory in the +development of OS kernels: + +.. code-block:: nim + type + Kernel = object + Userspace = object + + var a: Kernel ptr Stat + var b: Userspace ptr Stat + + # the following does not compile as the pointer types are incompatible: + a = b + +As the example shows ``ptr`` can also be used as a binary +operator, ``region ptr T`` is a shortcut for ``ptr[region, T]``. + +In order to make generic code easier to write ``ptr T`` is a subtype +of ``ptr[R, T]`` for any ``R``. + +Furthermore the subtype relation of the region object types is lifted to +the pointer types: If ``A <: B`` then ``ptr[A, T] <: ptr[B, T]``. This can be +used to model subregions of memory. As a special typing rule ``ptr[R, T]`` is +not compatible to ``pointer`` to prevent the following from compiling: + +.. code-block:: nim + # from system + proc dealloc(p: pointer) + + # wrap some scripting language + type + PythonsHeap = object + PyObjectHeader = object + rc: int + typ: pointer + PyObject = ptr[PythonsHeap, PyObjectHeader] + + proc createPyObject(): PyObject {.importc: "...".} + proc destroyPyObject(x: PyObject) {.importc: "...".} + + var foo = createPyObject() + # type error here, how convenient: + dealloc(foo) + + +Future directions: + +* Memory regions might become available for ``string`` and ``seq`` too. +* Builtin regions like ``private``, ``global`` and ``local`` will + prove very useful for the upcoming OpenCL target. +* Builtin "regions" can model ``lent`` and ``unique`` pointers. + + + +Procedural type +--------------- +A procedural type is internally a pointer to a procedure. ``nil`` is +an allowed value for variables of a procedural type. Nim uses procedural +types to achieve `functional`:idx: programming techniques. + +Examples: + +.. code-block:: nim + + proc printItem(x: int) = ... + + proc forEach(c: proc (x: int) {.cdecl.}) = + ... + + forEach(printItem) # this will NOT compile because calling conventions differ + + +.. code-block:: nim + + type + TOnMouseMove = proc (x, y: int) {.closure.} + + proc onMouseMove(mouseX, mouseY: int) = + # has default calling convention + echo "x: ", mouseX, " y: ", mouseY + + proc setOnMouseMove(mouseMoveEvent: TOnMouseMove) = discard + + # ok, 'onMouseMove' has the default calling convention, which is compatible + # to 'closure': + setOnMouseMove(onMouseMove) + + +A subtle issue with procedural types is that the calling convention of the +procedure influences the type compatibility: procedural types are only +compatible if they have the same calling convention. As a special extension, +a procedure of the calling convention ``nimcall`` can be passed to a parameter +that expects a proc of the calling convention ``closure``. + +Nim supports these `calling conventions`:idx:\: + +`nimcall`:idx: + is the default convention used for a Nim **proc**. It is the + same as ``fastcall``, but only for C compilers that support ``fastcall``. + +`closure`:idx: + is the default calling convention for a **procedural type** that lacks + any pragma annotations. It indicates that the procedure has a hidden + implicit parameter (an *environment*). Proc vars that have the calling + convention ``closure`` take up two machine words: One for the proc pointer + and another one for the pointer to implicitly passed environment. + +`stdcall`:idx: + This the stdcall convention as specified by Microsoft. The generated C + procedure is declared with the ``__stdcall`` keyword. + +`cdecl`:idx: + The cdecl convention means that a procedure shall use the same convention + as the C compiler. Under windows the generated C procedure is declared with + the ``__cdecl`` keyword. + +`safecall`:idx: + This is the safecall convention as specified by Microsoft. The generated C + procedure is declared with the ``__safecall`` keyword. The word *safe* + refers to the fact that all hardware registers shall be pushed to the + hardware stack. + +`inline`:idx: + The inline convention means the the caller should not call the procedure, + but inline its code directly. Note that Nim does not inline, but leaves + this to the C compiler; it generates ``__inline`` procedures. This is + only a hint for the compiler: it may completely ignore it and + it may inline procedures that are not marked as ``inline``. + +`fastcall`:idx: + Fastcall means different things to different C compilers. One gets whatever + the C ``__fastcall`` means. + +`syscall`:idx: + The syscall convention is the same as ``__syscall`` in C. It is used for + interrupts. + +`noconv`:idx: + The generated C code will not have any explicit calling convention and thus + use the C compiler's default calling convention. This is needed because + Nim's default calling convention for procedures is ``fastcall`` to + improve speed. + +Most calling conventions exist only for the Windows 32-bit platform. + +Assigning/passing a procedure to a procedural variable is only allowed if one +of the following conditions hold: +1) The procedure that is accessed resides in the current module. +2) The procedure is marked with the ``procvar`` pragma (see `procvar pragma`_). +3) The procedure has a calling convention that differs from ``nimcall``. +4) The procedure is anonymous. + +The rules' purpose is to prevent the case that extending a non-``procvar`` +procedure with default parameters breaks client code. + +The default calling convention is ``nimcall``, unless it is an inner proc (a +proc inside of a proc). For an inner proc an analysis is performed whether it +accesses its environment. If it does so, it has the calling convention +``closure``, otherwise it has the calling convention ``nimcall``. + + +Distinct type +------------- + +A ``distinct`` type is new type derived from a `base type`:idx: that is +incompatible with its base type. In particular, it is an essential property +of a distinct type that it **does not** imply a subtype relation between it +and its base type. Explicit type conversions from a distinct type to its +base type and vice versa are allowed. + + +Modelling currencies +~~~~~~~~~~~~~~~~~~~~ + +A distinct type can be used to model different physical `units`:idx: with a +numerical base type, for example. The following example models currencies. + +Different currencies should not be mixed in monetary calculations. Distinct +types are a perfect tool to model different currencies: + +.. code-block:: nim + type + TDollar = distinct int + TEuro = distinct int + + var + d: TDollar + e: TEuro + + echo d + 12 + # Error: cannot add a number with no unit and a ``TDollar`` + +Unfortunately, ``d + 12.TDollar`` is not allowed either, +because ``+`` is defined for ``int`` (among others), not for ``TDollar``. So +a ``+`` for dollars needs to be defined: + +.. code-block:: + proc `+` (x, y: TDollar): TDollar = + result = TDollar(int(x) + int(y)) + +It does not make sense to multiply a dollar with a dollar, but with a +number without unit; and the same holds for division: + +.. code-block:: + proc `*` (x: TDollar, y: int): TDollar = + result = TDollar(int(x) * y) + + proc `*` (x: int, y: TDollar): TDollar = + result = TDollar(x * int(y)) + + proc `div` ... + +This quickly gets tedious. The implementations are trivial and the compiler +should not generate all this code only to optimize it away later - after all +``+`` for dollars should produce the same binary code as ``+`` for ints. +The pragma `borrow`:idx: has been designed to solve this problem; in principle +it generates the above trivial implementations: + +.. code-block:: nim + proc `*` (x: TDollar, y: int): TDollar {.borrow.} + proc `*` (x: int, y: TDollar): TDollar {.borrow.} + proc `div` (x: TDollar, y: int): TDollar {.borrow.} + +The ``borrow`` pragma makes the compiler use the same implementation as +the proc that deals with the distinct type's base type, so no code is +generated. + +But it seems all this boilerplate code needs to be repeated for the ``TEuro`` +currency. This can be solved with templates_. + +.. code-block:: nim + template additive(typ: typedesc): stmt = + proc `+` *(x, y: typ): typ {.borrow.} + proc `-` *(x, y: typ): typ {.borrow.} + + # unary operators: + proc `+` *(x: typ): typ {.borrow.} + proc `-` *(x: typ): typ {.borrow.} + + template multiplicative(typ, base: typedesc): stmt = + proc `*` *(x: typ, y: base): typ {.borrow.} + proc `*` *(x: base, y: typ): typ {.borrow.} + proc `div` *(x: typ, y: base): typ {.borrow.} + proc `mod` *(x: typ, y: base): typ {.borrow.} + + template comparable(typ: typedesc): stmt = + proc `<` * (x, y: typ): bool {.borrow.} + proc `<=` * (x, y: typ): bool {.borrow.} + proc `==` * (x, y: typ): bool {.borrow.} + + template defineCurrency(typ, base: expr): stmt = + type + typ* = distinct base + additive(typ) + multiplicative(typ, base) + comparable(typ) + + defineCurrency(TDollar, int) + defineCurrency(TEuro, int) + + +The borrow pragma can also be used to annotate the distinct type to allow +certain builtin operations to be lifted: + +.. code-block:: nim + type + Foo = object + a, b: int + s: string + + Bar {.borrow: `.`.} = distinct Foo + + var bb: ref Bar + new bb + # field access now valid + bb.a = 90 + bb.s = "abc" + +Currently only the dot accessor can be borrowed in this way. + + +Avoiding SQL injection attacks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An SQL statement that is passed from Nim to an SQL database might be +modelled as a string. However, using string templates and filling in the +values is vulnerable to the famous `SQL injection attack`:idx:\: + +.. code-block:: nim + import strutils + + proc query(db: TDbHandle, statement: string) = ... + + var + username: string + + db.query("SELECT FROM users WHERE name = '$1'" % username) + # Horrible security hole, but the compiler does not mind! + +This can be avoided by distinguishing strings that contain SQL from strings +that don't. Distinct types provide a means to introduce a new string type +``TSQL`` that is incompatible with ``string``: + +.. code-block:: nim + type + TSQL = distinct string + + proc query(db: TDbHandle, statement: TSQL) = ... + + var + username: string + + db.query("SELECT FROM users WHERE name = '$1'" % username) + # Error at compile time: `query` expects an SQL string! + + +It is an essential property of abstract types that they **do not** imply a +subtype relation between the abtract type and its base type. Explict type +conversions from ``string`` to ``TSQL`` are allowed: + +.. code-block:: nim + import strutils, sequtils + + proc properQuote(s: string): TSQL = + # quotes a string properly for an SQL statement + return TSQL(s) + + proc `%` (frmt: TSQL, values: openarray[string]): TSQL = + # quote each argument: + let v = values.mapIt(TSQL, properQuote(it)) + # we need a temporary type for the type conversion :-( + type TStrSeq = seq[string] + # call strutils.`%`: + result = TSQL(string(frmt) % TStrSeq(v)) + + db.query("SELECT FROM users WHERE name = '$1'".TSQL % [username]) + +Now we have compile-time checking against SQL injection attacks. Since +``"".TSQL`` is transformed to ``TSQL("")`` no new syntax is needed for nice +looking ``TSQL`` string literals. The hypothetical ``TSQL`` type actually +exists in the library as the `TSqlQuery type `_ of +modules like `db_sqlite `_. + + +Void type +--------- + +The ``void`` type denotes the absense of any type. Parameters of +type ``void`` are treated as non-existent, ``void`` as a return type means that +the procedure does not return a value: + +.. code-block:: nim + proc nothing(x, y: void): void = + echo "ha" + + nothing() # writes "ha" to stdout + +The ``void`` type is particularly useful for generic code: + +.. code-block:: nim + proc callProc[T](p: proc (x: T), x: T) = + when T is void: + p() + else: + p(x) + + proc intProc(x: int) = discard + proc emptyProc() = discard + + callProc[int](intProc, 12) + callProc[void](emptyProc) + +However, a ``void`` type cannot be inferred in generic code: + +.. code-block:: nim + callProc(emptyProc) + # Error: type mismatch: got (proc ()) + # but expected one of: + # callProc(p: proc (T), x: T) + +The ``void`` type is only valid for parameters and return types; other symbols +cannot have the type ``void``. diff --git a/doc/nimrodc.txt b/doc/nimc.txt similarity index 79% rename from doc/nimrodc.txt rename to doc/nimc.txt index 016f0ed50..92acd3979 100644 --- a/doc/nimrodc.txt +++ b/doc/nimc.txt @@ -1,9 +1,9 @@ =================================== - Nimrod Compiler User Guide + Nim Compiler User Guide =================================== :Author: Andreas Rumpf -:Version: |nimrodversion| +:Version: |nimversion| .. contents:: @@ -15,11 +15,11 @@ Introduction ============ -This document describes the usage of the *Nimrod compiler* -on the different supported platforms. It is not a definition of the Nimrod +This document describes the usage of the *Nim compiler* +on the different supported platforms. It is not a definition of the Nim programming language (therefore is the `manual `_). -Nimrod is free software; it is licensed under the +Nim is free software; it is licensed under the `MIT License `_. @@ -28,10 +28,14 @@ Compiler Usage Command line switches --------------------- -Basic command line switches are: +Basic command line switches are: + +Usage: .. include:: basicopt.txt +---- + Advanced command line switches are: .. include:: advopt.txt @@ -42,7 +46,7 @@ List of warnings ---------------- Each warning can be activated individually with ``--warning[NAME]:on|off`` or -in a ``push`` pragma. +in a ``push`` pragma. ========================== ============================================ Name Description @@ -107,14 +111,14 @@ Configuration files passed as a command line argument to the compiler. -The ``nimrod`` executable processes configuration files in the following +The ``nim`` executable processes configuration files in the following directories (in this order; later files overwrite previous settings): -1) ``$nimrod/config/nimrod.cfg``, ``/etc/nimrod.cfg`` (UNIX) or ``%NIMROD%/config/nimrod.cfg`` (Windows). This file can be skipped with the ``--skipCfg`` command line option. -2) ``/home/$user/.config/nimrod.cfg`` (UNIX) or ``%APPDATA%/nimrod.cfg`` (Windows). This file can be skipped with the ``--skipUserCfg`` command line option. -3) ``$parentDir/nimrod.cfg`` where ``$parentDir`` stands for any parent directory of the project file's path. These files can be skipped with the ``--skipParentCfg`` command line option. -4) ``$projectDir/nimrod.cfg`` where ``$projectDir`` stands for the project file's path. This file can be skipped with the ``--skipProjCfg`` command line option. -5) A project can also have a project specific configuration file named ``$project.nimrod.cfg`` that resides in the same directory as ``$project.nim``. This file can be skipped with the ``--skipProjCfg`` command line option. +1) ``$nim/config/nim.cfg``, ``/etc/nim.cfg`` (UNIX) or ``%NIMROD%/config/nim.cfg`` (Windows). This file can be skipped with the ``--skipCfg`` command line option. +2) ``/home/$user/.config/nim.cfg`` (UNIX) or ``%APPDATA%/nim.cfg`` (Windows). This file can be skipped with the ``--skipUserCfg`` command line option. +3) ``$parentDir/nim.cfg`` where ``$parentDir`` stands for any parent directory of the project file's path. These files can be skipped with the ``--skipParentCfg`` command line option. +4) ``$projectDir/nim.cfg`` where ``$projectDir`` stands for the project file's path. This file can be skipped with the ``--skipProjCfg`` command line option. +5) A project can also have a project specific configuration file named ``$project.nim.cfg`` that resides in the same directory as ``$project.nim``. This file can be skipped with the ``--skipProjCfg`` command line option. Command line settings have priority over configuration file settings. @@ -122,17 +126,17 @@ Command line settings have priority over configuration file settings. The default build of a project is a `debug build`:idx:. To compile a `release build`:idx: define the ``release`` symbol:: - nimrod c -d:release myproject.nim + nim c -d:release myproject.nim Search path handling -------------------- -Nimrod has the concept of a global search path (PATH) that is queried to +Nim has the concept of a global search path (PATH) that is queried to determine where to find imported modules or include files. If multiple files are found an ambiguity error is produced. -``nimrod dump`` shows the contents of the PATH. +``nim dump`` shows the contents of the PATH. However before the PATH is used the current directory is checked for the file's existance. So if PATH contains ``$lib`` and ``$lib/bar`` and the @@ -152,10 +156,10 @@ the first matching file is used. Generated C code directory -------------------------- -The generated files that Nimrod produces all go into a subdirectory called +The generated files that Nim produces all go into a subdirectory called ``nimcache`` in your project directory. This makes it easy to delete all generated files. Files generated in this directory follow a naming logic which -you can read about in the `Nimrod Backend Integration document +you can read about in the `Nim Backend Integration document `_. However, the generated C code is not platform independent. C code generated for @@ -190,14 +194,14 @@ Cross compilation To cross compile, use for example:: - nimrod c --cpu:i386 --os:linux --compile_only --gen_script myproject.nim + nim c --cpu:i386 --os:linux --compile_only --gen_script myproject.nim Then move the C code and the compile script ``compile_myproject.sh`` to your Linux i386 machine and run the script. -Another way is to make Nimrod invoke a cross compiler toolchain:: +Another way is to make Nim invoke a cross compiler toolchain:: - nimrod c --cpu:arm --os:linux myproject.nim + nim c --cpu:arm --os:linux myproject.nim For cross compilation, the compiler invokes a C compiler named like ``$cpu.$os.$cc`` (for example arm.linux.gcc) and the configuration @@ -212,16 +216,16 @@ configuration file should contain something like:: DLL generation ============== -Nimrod supports the generation of DLLs. However, there must be only one +Nim supports the generation of DLLs. However, there must be only one instance of the GC per process/address space. This instance is contained in -``nimrtl.dll``. This means that every generated Nimrod DLL depends +``nimrtl.dll``. This means that every generated Nim DLL depends on ``nimrtl.dll``. To generate the "nimrtl.dll" file, use the command:: - nimrod c -d:release lib/nimrtl.nim + nim c -d:release lib/nimrtl.nim To link against ``nimrtl.dll`` use the command:: - nimrod c -d:useNimRtl myprog.nim + nim c -d:useNimRtl myprog.nim **Note**: Currently the creation of ``nimrtl.dll`` with thread support has never been tested and is unlikely to work! @@ -243,14 +247,15 @@ Define Effect version. ``useFork`` Makes ``osproc`` use ``fork`` instead of ``posix_spawn``. ``useNimRtl`` Compile and link against ``nimrtl.dll``. -``useMalloc`` Makes Nimrod use C's `malloc`:idx: instead of Nimrod's +``useMalloc`` Makes Nim use C's `malloc`:idx: instead of Nim's own memory manager. This only works with ``gc:none``. -``useRealtimeGC`` Enables support of Nimrod's GC for *soft* realtime +``useRealtimeGC`` Enables support of Nim's GC for *soft* realtime systems. See the documentation of the `gc `_ for further information. ``nodejs`` The JS target is actually ``node.js``. ``ssl`` Enables OpenSSL support for the sockets module. ``memProfiler`` Enables memory profiling for the native GC. +``uClibc`` Use uClibc instead of libc. (Relevant for Unix-like OSes) ================== ========================================================= @@ -258,8 +263,8 @@ Define Effect Additional Features =================== -This section describes Nimrod's additional features that are not listed in the -Nimrod manual. Some of the features here only make sense for the C code +This section describes Nim's additional features that are not listed in the +Nim manual. Some of the features here only make sense for the C code generator and are subject to change. @@ -267,13 +272,13 @@ NoDecl pragma ------------- The ``noDecl`` pragma can be applied to almost any symbol (variable, proc, type, etc.) and is sometimes useful for interoperability with C: -It tells Nimrod that it should not generate a declaration for the symbol in +It tells Nim that it should not generate a declaration for the symbol in the C code. For example: -.. code-block:: Nimrod +.. code-block:: Nim var EACCES {.importc, noDecl.}: cint # pretend EACCES was a variable, as - # Nimrod does not know its value + # Nim does not know its value However, the ``header`` pragma is often the better alternative. @@ -286,14 +291,14 @@ The ``header`` pragma is very similar to the ``noDecl`` pragma: It can be applied to almost any symbol and specifies that it should not be declared and instead the generated code should contain an ``#include``: -.. code-block:: Nimrod +.. code-block:: Nim type PFile {.importc: "FILE*", header: "".} = distinct pointer - # import C's FILE* type; Nimrod will treat it as a new pointer type + # import C's FILE* type; Nim will treat it as a new pointer type The ``header`` pragma always expects a string constant. The string contant contains the header file: As usual for C, a system header file is enclosed -in angle brackets: ``<>``. If no angle brackets are given, Nimrod +in angle brackets: ``<>``. If no angle brackets are given, Nim encloses the header file in ``""`` in the generated C code. **Note**: This will not work for the LLVM backend. @@ -304,7 +309,7 @@ IncompleteStruct pragma The ``incompleteStruct`` pragma tells the compiler to not use the underlying C ``struct`` in a ``sizeof`` expression: -.. code-block:: Nimrod +.. code-block:: Nim type TDIR* {.importc: "DIR", header: "", final, pure, incompleteStruct.} = object @@ -315,10 +320,10 @@ Compile pragma The ``compile`` pragma can be used to compile and link a C/C++ source file with the project: -.. code-block:: Nimrod +.. code-block:: Nim {.compile: "myfile.cpp".} -**Note**: Nimrod computes a CRC checksum and only recompiles the file if it +**Note**: Nim computes a CRC checksum and only recompiles the file if it has changed. You can use the ``-f`` command line option to force recompilation of the file. @@ -327,7 +332,7 @@ Link pragma ----------- The ``link`` pragma can be used to link an additional file with the project: -.. code-block:: Nimrod +.. code-block:: Nim {.link: "myfile.o".} @@ -336,13 +341,13 @@ PassC pragma The ``passC`` pragma can be used to pass additional parameters to the C compiler like you would using the commandline switch ``--passC``: -.. code-block:: Nimrod +.. code-block:: Nim {.passC: "-Wall -Werror".} Note that you can use ``gorge`` from the `system module `_ to embed parameters from an external command at compile time: -.. code-block:: Nimrod +.. code-block:: Nim {.passC: gorge("pkg-config --cflags sdl").} PassL pragma @@ -350,13 +355,13 @@ PassL pragma The ``passL`` pragma can be used to pass additional parameters to the linker like you would using the commandline switch ``--passL``: -.. code-block:: Nimrod +.. code-block:: Nim {.passL: "-lSDLmain -lSDL".} Note that you can use ``gorge`` from the `system module `_ to embed parameters from an external command at compile time: -.. code-block:: Nimrod +.. code-block:: Nim {.passL: gorge("pkg-config --libs sdl").} @@ -369,20 +374,23 @@ extremely useful for interfacing with `C++`:idx: or `Objective C`:idx: code. Example: -.. code-block:: Nimrod +.. code-block:: Nim {.emit: """ static int cvariable = 420; """.} {.push stackTrace:off.} proc embedsC() = - var nimrodVar = 89 - # use backticks to access Nimrod symbols within an emit section: - {.emit: """fprintf(stdout, "%d\n", cvariable + (int)`nimrodVar`);""".} + var nimVar = 89 + # use backticks to access Nim symbols within an emit section: + {.emit: """fprintf(stdout, "%d\n", cvariable + (int)`nimVar`);""".} {.pop.} embedsC() - + +As can be seen from the example, to Nim symbols can be referred via backticks. +Use two backticks to produce a single verbatim backtick. + ImportCpp pragma ---------------- @@ -392,7 +400,7 @@ code then uses the C++ method calling syntax: ``obj->method(arg)``. In addition with the ``header`` and ``emit`` pragmas this allows *sloppy* interfacing with libraries written in C++: -.. code-block:: Nimrod +.. code-block:: Nim # Horrible example of how to interface with a C++ engine ... ;-) {.link: "/usr/lib/libIrrlicht.so".} @@ -431,7 +439,7 @@ generated code then uses the Objective C method calling syntax: ``[obj method param1: arg]``. In addition with the ``header`` and ``emit`` pragmas this allows *sloppy* interfacing with libraries written in Objective C: -.. code-block:: Nimrod +.. code-block:: Nim # horrible example of how to interface with GNUStep ... {.passL: "-lobjc".} @@ -475,11 +483,11 @@ emits Objective C code. CodegenDecl pragma ------------------ -The ``codegenDecl`` pragma can be used to directly influence Nimrod's code +The ``codegenDecl`` pragma can be used to directly influence Nim's code generator. It receives a format string that determines how the variable or proc is declared in the generated code: -.. code-block:: nimrod +.. code-block:: nim var a {.codegenDecl: "$# progmem $#".}: int @@ -494,7 +502,7 @@ The ``injectStmt`` pragma can be used to inject a statement before every other statement in the current module. It is only supposed to be used for debugging: -.. code-block:: nimrod +.. code-block:: nim {.injectStmt: gcInvariants().} # ... complex code here that produces crashes ... @@ -523,7 +531,7 @@ is raised. Debugger option --------------- -The ``debugger`` option enables or disables the *Embedded Nimrod Debugger*. +The ``debugger`` option enables or disables the *Embedded Nim Debugger*. See the documentation of endb_ for further information. @@ -545,11 +553,11 @@ in C/C++). Source code style ================= -Nimrod allows you to `mix freely case and underscores as identifier separators +Nim allows you to `mix freely case and underscores as identifier separators `_, so variables named ``MyPrecioussInt`` and ``my_preciouss_int`` are equivalent: -.. code-block:: Nimrod +.. code-block:: Nim var MyPrecioussInt = 3 # Following line compiles fine! echo my_preciouss_int @@ -557,16 +565,16 @@ Nimrod allows you to `mix freely case and underscores as identifier separators Since this can lead to many variants of the same source code (you can use `nimgrep `_ instead of your typical ``grep`` to ignore style problems) the compiler provides the command ``pretty`` to help unifying the -style of source code. Running ``nimrod pretty ugly_test.nim`` with this +style of source code. Running ``nim pretty ugly_test.nim`` with this example will generate a secondary file named ``ugly_test.pretty.nim`` with the following content: -.. code-block:: Nimrod +.. code-block:: Nim var MyPrecioussInt = 3 # Following line compiles fine! echo MyPrecioussInt -During execution the ``pretty`` command will also run on Nimrod's standard +During execution the ``pretty`` command will also run on Nim's standard library, since it doesn't differentiate the standard library as something special, and hence will warn of many *errors* which are out of your hand to fix, creating respective ``.pretty.nim`` files all the way. You can ignore @@ -583,11 +591,11 @@ important changes for you to review. In this case the command is warning that a variable name should not start with a capital letter, which is usually reserved to `object types `_. To learn about the accepted `camel case style `_ read `Coding Guidelines in -the Internals of Nimrod Compiler `_ or `Coding -Guidelines `_ and `NEP 1 -: Style Guide for Nimrod Code -`_ -from the Nimrod `GitHub wiki`_. +the Internals of Nim Compiler `_ or `Coding +Guidelines `_ and `NEP 1 +: Style Guide for Nim Code +`_ +from the Nim `GitHub wiki`_. This command is safe to run because it will never attempt to overwrite your existing sources, but the respective ``.pretty.nim`` files **will** be @@ -597,14 +605,14 @@ overwritten without notice. DynlibOverride ============== -By default Nimrod's ``dynlib`` pragma causes the compiler to generate +By default Nim's ``dynlib`` pragma causes the compiler to generate ``GetProcAddress`` (or their Unix counterparts) calls to bind to a DLL. With the ``dynlibOverride`` command line switch this can be prevented and then via ``--passL`` the static library can be linked against. For instance, to link statically against Lua this command might work on Linux:: - nimrod c --dynlibOverride:lua --passL:liblua.lib program.nim + nim c --dynlibOverride:lua --passL:liblua.lib program.nim Backend language options @@ -614,32 +622,32 @@ The typical compiler usage involves using the ``compile`` or ``c`` command to transform a ``.nim`` file into one or more ``.c`` files which are then compiled with the platform's C compiler into a static binary. However there are other commands to compile to C++, Objective-C or Javascript. More details -can be read in the `Nimrod Backend Integration document `_. +can be read in the `Nim Backend Integration document `_. -Nimrod documentation tools -========================== +Nim documentation tools +======================= -Nimrod provides the `doc`:idx: and `doc2`:idx: commands to generate HTML +Nim provides the `doc`:idx: and `doc2`:idx: commands to generate HTML documentation from ``.nim`` source files. Only exported symbols will appear in the output. For more details `see the docgen documentation `_. -Nimrod idetools integration -=========================== +Nim idetools integration +======================== -Nimrod provides language integration with external IDEs through the +Nim provides language integration with external IDEs through the idetools command. See the documentation of `idetools `_ for further information. -Nimrod interactive mode -======================= +Nim interactive mode +==================== -The Nimrod compiler supports an interactive mode. This is also known as -a `REPL`:idx: (*read eval print loop*). If Nimrod has been built with the +The Nim compiler supports an interactive mode. This is also known as +a `REPL`:idx: (*read eval print loop*). If Nim has been built with the ``-d:useGnuReadline`` switch, it uses the GNU readline library for terminal -input management. To start Nimrod in interactive mode use the command -``nimrod i``. To quit use the ``quit()`` command. To determine whether an input +input management. To start Nim in interactive mode use the command +``nim i``. To quit use the ``quit()`` command. To determine whether an input line is an incomplete statement to be continued these rules are used: 1. The line ends with ``[-+*/\\<>!\?\|%&$@~,;:=#^]\s*$`` (operator symbol followed by optional whitespace). @@ -648,8 +656,8 @@ line is an incomplete statement to be continued these rules are used: does not work if the line contains more than one ``"""``. -Nimrod for embedded systems -=========================== +Nim for embedded systems +======================== The standard library can be avoided to a point where C code generation for 16bit micro controllers is feasible. Use the `standalone`:idx: target @@ -661,7 +669,7 @@ target. For example, to generate code for an `AVR`:idx: processor use this command:: - nimrod c --cpu:avr --os:standalone --deadCodeElim:on --genScript x.nim + nim c --cpu:avr --os:standalone --deadCodeElim:on --genScript x.nim For the ``standalone`` target you need to provide a file ``panicoverride.nim``. @@ -669,27 +677,27 @@ See ``tests/manyloc/standalone/panicoverride.nim`` for an example implementation. -Nimrod for realtime systems -=========================== +Nim for realtime systems +======================== -See the documentation of Nimrod's soft realtime `GC `_ for further +See the documentation of Nim's soft realtime `GC `_ for further information. -Debugging with Nimrod -===================== +Debugging with Nim +================== -Nimrod comes with its own *Embedded Nimrod Debugger*. See +Nim comes with its own *Embedded Nim Debugger*. See the documentation of endb_ for further information. -Optimizing for Nimrod -===================== +Optimizing for Nim +================== -Nimrod has no separate optimizer, but the C code that is produced is very +Nim has no separate optimizer, but the C code that is produced is very efficient. Most C compilers have excellent optimizers, so usually it is -not needed to optimize one's code. Nimrod has been designed to encourage -efficient code: The most readable code in Nimrod is often the most efficient +not needed to optimize one's code. Nim has been designed to encourage +efficient code: The most readable code in Nim is often the most efficient too. However, sometimes one has to optimize. Do it in the following order: @@ -706,32 +714,32 @@ This section can only help you with the last item. Optimizing string handling -------------------------- -String assignments are sometimes expensive in Nimrod: They are required to +String assignments are sometimes expensive in Nim: They are required to copy the whole string. However, the compiler is often smart enough to not copy strings. Due to the argument passing semantics, strings are never copied when passed to subroutines. The compiler does not copy strings that are a result from a procedure call, because the callee returns a new string anyway. Thus it is efficient to do: -.. code-block:: Nimrod +.. code-block:: Nim var s = procA() # assignment will not copy the string; procA allocates a new # string already However it is not efficient to do: -.. code-block:: Nimrod +.. code-block:: Nim var s = varA # assignment has to copy the whole string into a new buffer! For ``let`` symbols a copy is not always necessary: -.. code-block:: Nimrod +.. code-block:: Nim let s = varA # may only copy a pointer if it safe to do so If you know what you're doing, you can also mark single string (or sequence) objects as `shallow`:idx:\: -.. code-block:: Nimrod +.. code-block:: Nim var s = "abc" shallow(s) # mark 's' as shallow string var x = s # now might not copy the string! @@ -744,7 +752,7 @@ The compiler optimizes string case statements: A hashing scheme is used for them if several different string constants are used. So code like this is reasonably efficient: -.. code-block:: Nimrod +.. code-block:: Nim case normalize(k.key) of "name": c.name = v of "displayname": c.displayName = v diff --git a/doc/nimfix.txt b/doc/nimfix.txt new file mode 100644 index 000000000..6d78f3e3f --- /dev/null +++ b/doc/nimfix.txt @@ -0,0 +1,56 @@ +===================== + Nimfix User Guide +===================== + +:Author: Andreas Rumpf +:Version: |nimversion| + +**WARNING**: Nimfix is currently beta-quality. + +Nimfix is a tool to help you upgrade from Nimrod (<= version 0.9.6) to +Nim (=> version 0.10.0). + +It performs 3 different actions: + +1. It makes your code case consistent. +2. It renames every symbol that has a deprecation rule. So if a module has a + rule ``{.deprecated: [TFoo: Foo].}`` then ``TFoo`` is replaced by ``Foo``. +3. It can also check that your identifiers adhere to the official style guide + and optionally modify them to do so (via ``--styleCheck:auto``). + +Note that ``nimfix`` defaults to **overwrite** your code unless you +use ``--overwriteFiles:off``! But hey, if you do not use a version control +system by this day and age, your project is already in big trouble. + + +Installation +------------ + +Nimfix is part of the compiler distribution. Compile via:: + + nim c compiler/nimfix/nimfix.nim + mv compiler/nimfix/nimfix bin + +Or on windows:: + + nim c compiler\nimfix\nimfix.nim + move compiler\nimfix\nimfix.exe bin + +Usage +----- + +Usage: + nimfix [options] projectfile.nim + +Options: + + --overwriteFiles:on|off overwrite the original nim files. DEFAULT is ON! + --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; + 'auto' corrects the spelling. + +In addition, all command line options of Nim are supported. + + diff --git a/doc/nimgrep.txt b/doc/nimgrep.txt index 67aaa427e..e2f7b228f 100644 --- a/doc/nimgrep.txt +++ b/doc/nimgrep.txt @@ -10,7 +10,7 @@ Nimgrep is a command line tool for search&replace tasks. It can search for regex or peg patterns and can search whole directories at once. User confirmation for every single replace operation can be requested. -Nimgrep has particularly good support for Nimrod's +Nimgrep has particularly good support for Nim's eccentric *style insensitivity*. Apart from that it is a generic text manipulation tool. @@ -20,7 +20,7 @@ Installation Compile nimgrep with the command:: - nimrod c -d:release tools/nimgrep.nim + nim c -d:release tools/nimgrep.nim And copy the executable somewhere in your ``$PATH``. diff --git a/doc/niminst.txt b/doc/niminst.txt index 8d95d01c9..d743c5187 100644 --- a/doc/niminst.txt +++ b/doc/niminst.txt @@ -3,14 +3,14 @@ ========================= :Author: Andreas Rumpf -:Version: |nimrodversion| +:Version: |nimversion| .. contents:: Introduction ============ -niminst is a tool to generate an installer for a Nimrod program. Currently +niminst is a tool to generate an installer for a Nim program. Currently it can create an installer for Windows via `Inno Setup `_ as well as installation/deinstallation scripts for UNIX. Later versions will support @@ -24,7 +24,7 @@ systems. Configuration file ================== -niminst uses the Nimrod `parsecfg `_ module to parse the +niminst uses the Nim `parsecfg `_ module to parse the configuration file. Here's an example of how the syntax looks like: .. include:: doc/mytest.cfg @@ -187,9 +187,9 @@ Key description Real world example ================== -The installers for the Nimrod compiler itself are generated by niminst. Have a +The installers for the Nim compiler itself are generated by niminst. Have a look at its configuration file: -.. include:: compiler/nimrod.ini +.. include:: compiler/nim.ini :literal: diff --git a/doc/overview.txt b/doc/overview.txt index 242039086..5b41752ae 100644 --- a/doc/overview.txt +++ b/doc/overview.txt @@ -1,9 +1,9 @@ ============================= -Nimrod Documentation Overview +Nim Documentation Overview ============================= :Author: Andreas Rumpf -:Version: |nimrodversion| +:Version: |nimversion| .. include:: ../doc/docs.txt diff --git a/doc/pegdocs.txt b/doc/pegdocs.txt index eb7f4562f..d22d81a5a 100644 --- a/doc/pegdocs.txt +++ b/doc/pegdocs.txt @@ -171,7 +171,7 @@ The PEG parser implements this grammar (written in PEG syntax):: expression, identifiers are not interpreted as non-terminals, but are interpreted as verbatim string: -.. code-block:: nimrod +.. code-block:: nim abc =~ peg"abc" # is true So it is not necessary to write ``peg" 'abc' "`` in the above example. @@ -180,19 +180,19 @@ So it is not necessary to write ``peg" 'abc' "`` in the above example. Examples -------- -Check if `s` matches Nimrod's "while" keyword: +Check if `s` matches Nim's "while" keyword: -.. code-block:: nimrod +.. code-block:: nim s =~ peg" y'while'" Exchange (key, val)-pairs: -.. code-block:: nimrod +.. code-block:: nim "key: val; key2: val2".replace(peg"{\ident} \s* ':' \s* {\ident}", "$2: $1") Determine the ``#include``'ed files of a C file: -.. code-block:: nimrod +.. code-block:: nim for line in lines("myfile.c"): if line =~ peg"""s <- ws '#include' ws '"' {[^"]+} '"' ws comment <- '/*' @ '*/' / '//' .* @@ -212,7 +212,7 @@ example ``*`` should not be greedy, so ``\[.*?\]`` should be used instead. PEG construction ---------------- -There are two ways to construct a PEG in Nimrod code: +There are two ways to construct a PEG in Nim code: (1) Parsing a string into an AST which consists of `TPeg` nodes with the `peg` proc. (2) Constructing the AST directly with proc calls. This method does not diff --git a/doc/readme.txt b/doc/readme.txt index 7f509bd39..e5d9e2c27 100644 --- a/doc/readme.txt +++ b/doc/readme.txt @@ -1,7 +1,7 @@ ============================ -Nimrod's documenation system +Nim's documenation system ============================ -This folder contains Nimrod's documentation. The documentation +This folder contains Nim's documentation. The documentation is written in a format called *reStructuredText*, a markup language that reads like ASCII and can be converted to HTML automatically! diff --git a/doc/regexprs.txt b/doc/regexprs.txt index 930352948..d912c623e 100644 --- a/doc/regexprs.txt +++ b/doc/regexprs.txt @@ -44,9 +44,9 @@ As the regular expressions supported by this module are enormous, the reader is referred to http://perldoc.perl.org/perlre.html for the full documentation of Perl's regular expressions. -Because the backslash ``\`` is a meta character both in the Nimrod +Because the backslash ``\`` is a meta character both in the Nim programming language and in regular expressions, it is strongly -recommended that one uses the *raw* strings of Nimrod, so that +recommended that one uses the *raw* strings of Nim, so that backslashes are interpreted by the regular expression engine:: r"\S" # matches any character that is not whitespace diff --git a/doc/rst.txt b/doc/rst.txt deleted file mode 100644 index 4199598d1..000000000 --- a/doc/rst.txt +++ /dev/null @@ -1,110 +0,0 @@ -=========================================================================== - Nimrod's implementation of |rst| -=========================================================================== - -:Author: Andreas Rumpf -:Version: |nimrodversion| - -.. contents:: - -Introduction -============ - -This document describes the subset of `Docutils`_' `reStructuredText`_ as it -has been implemented in the Nimrod compiler for generating documentation. -Elements of |rst| that are not listed here have not been implemented. -Unfortunately, the specification of |rst| is quite vague, so Nimrod is not as -compatible to the original implementation as one would like. - -Even though Nimrod's |rst| parser does not parse all constructs, it is pretty -usable. The missing features can easily be circumvented. An indication of this -fact is that Nimrod's *whole* documentation itself (including this document) is -processed by Nimrod's |rst| parser. (Which is an order of magnitude faster than -Docutils' parser.) - - -Inline elements -=============== - -Ordinary text may contain *inline elements*. - - -Bullet lists -============ - -*Bullet lists* look like this:: - - * Item 1 - * Item 2 that - spans over multiple lines - * Item 3 - * Item 4 - - bullet lists may nest - - item 3b - - valid bullet characters are ``+``, ``*`` and ``-`` - -This results in: -* Item 1 -* Item 2 that - spans over multiple lines -* Item 3 -* Item 4 - - bullet lists may nest - - item 3b - - valid bullet characters are ``+``, ``*`` and ``-`` - - -Enumerated lists -================ - -*Enumerated lists* - - -Definition lists -================ - -Save this code to the file "greeting.nim". Now compile and run it: - - ``nimrod run greeting.nim`` - -As you see, with the ``run`` command Nimrod executes the file automatically -after compilation. You can even give your program command line arguments by -appending them after the filename that is to be compiled and run: - - ``nimrod run greeting.nim arg1 arg2`` - - -Tables -====== - -Nimrod only implements simple tables of the form:: - - ================== =============== =================== - header 1 header 2 header n - ================== =============== =================== - Cell 1 Cell 2 Cell 3 - Cell 4 Cell 5; any Cell 6 - cell that is - not in column 1 - may span over - multiple lines - Cell 7 Cell 8 Cell 9 - ================== =============== =================== - -This results in: -================== =============== =================== -header 1 header 2 header n -================== =============== =================== -Cell 1 Cell 2 Cell 3 -Cell 4 Cell 5; any Cell 6 - cell that is - not in column 1 - may span over - multiple lines -Cell 7 Cell 8 Cell 9 -================== =============== =================== - - -.. |rst| replace:: reStructuredText -.. _reStructuredText: http://docutils.sourceforge.net/rst.html#reference-documentation -.. _docutils: http://docutils.sourceforge.net/ diff --git a/doc/sets_fragment.txt b/doc/sets_fragment.txt index fba355269..59d434807 100644 --- a/doc/sets_fragment.txt +++ b/doc/sets_fragment.txt @@ -6,7 +6,7 @@ Sets can be constructed via the set constructor: ``{}`` is the empty set. The empty set is type compatible with any concrete set type. The constructor can also be used to include elements (and ranges of elements): -.. code-block:: nimrod +.. code-block:: nim type TCharSet = set[char] var @@ -29,7 +29,6 @@ operation meaning ``e in A`` set membership (A contains element e) ``e notin A`` A does not contain element e ``contains(A, e)`` A contains element e -``A -+- B`` symmetric set difference (= (A - B) + (B - A)) ``card(A)`` the cardinality of A (number of elements in A) ``incl(A, elem)`` same as ``A = A + {elem}`` ``excl(A, elem)`` same as ``A = A - {elem}`` diff --git a/doc/spawn.txt b/doc/spawn.txt index c5c96ecf8..fb2f851c7 100644 --- a/doc/spawn.txt +++ b/doc/spawn.txt @@ -2,7 +2,7 @@ Parallel & Spawn ========================================================== -Nimrod has two flavors of parallelism: +Nim has two flavors of parallelism: 1) `Structured`:idx parallelism via the ``parallel`` statement. 2) `Unstructured`:idx: parallelism via the standalone ``spawn`` statement. @@ -19,12 +19,42 @@ read prematurely within a ``parallel`` section and so there is no need for the overhead of an indirection via ``FlowVar[T]`` to ensure correctness. +Spawn statement +=============== + +A standalone ``spawn`` statement is a simple construct. It executes +the passed expression on the thread pool and returns a `data flow variable`:idx: +``FlowVar[T]`` that can be read from. The reading with the ``^`` operator is +**blocking**. However, one can use ``awaitAny`` to wait on multiple flow +variables at the same time: + +.. code-block:: nim + import threadpool, ... + + # wait until 2 out of 3 servers received the update: + proc main = + var responses = newSeq[RawFlowVar](3) + for i in 0..2: + responses[i] = spawn tellServer(Update, "key", "value") + var index = awaitAny(responses) + assert index >= 0 + responses.del(index) + discard awaitAny(responses) + +Data flow variables ensure that no data races +are possible. Due to technical limitations not every type ``T`` is possible in +a data flow variable: ``T`` has to be of the type ``ref``, ``string``, ``seq`` +or of a type that doesn't contain a type that is garbage collected. This +restriction will be removed in the future. + + + Parallel statement ================== Example: -.. code-block:: nimrod +.. code-block:: nim # Compute PI in an inefficient way import strutils, math, threadpool @@ -42,7 +72,7 @@ Example: The parallel statement is the preferred mechanism to introduce parallelism -in a Nimrod program. A subset of the Nimrod language is valid within a +in a Nim program. A subset of the Nim language is valid within a ``parallel`` section. This subset is checked to be free of data races at compile time. A sophisticated `disjoint checker`:idx: ensures that no data races are possible even though shared memory is extensively supported! @@ -64,35 +94,3 @@ restrictions / changes: * Slices are optimized so that no copy is performed. This optimization is not yet performed for ordinary slices outside of a ``parallel`` section. Slices are also special in that they currently do not support negative indexes! - - - - -Spawn statement -=============== - -A standalone ``spawn`` statement is a simple construct. It executes -the passed expression on the thread pool and returns a `data flow variable`:idx: -``FlowVar[T]`` that can be read from. The reading with the ``^`` operator is -**blocking**. However, one can use ``awaitAny`` to wait on multiple flow -variables at the same time: - -.. code-block:: nimrod - import threadpool, ... - - # wait until 2 out of 3 servers received the update: - proc main = - var responses = newSeq[RawFlowVar](3) - for i in 0..2: - responses[i] = spawn tellServer(Update, "key", "value") - var index = awaitAny(responses) - assert index >= 0 - responses.del(index) - discard awaitAny(responses) - -Like the ``parallel`` statement data flow variables ensure that no data races -are possible. Due to technical limitations not every type ``T`` is possible in -a data flow variable: ``T`` has to be of the type ``ref``, ``string``, ``seq`` -or of a type that doesn't contain a type that is garbage collected. This -restriction will be removed in the future. - diff --git a/doc/subexes.txt b/doc/subexes.txt index 3b688fd0a..decf30eef 100644 --- a/doc/subexes.txt +++ b/doc/subexes.txt @@ -16,10 +16,12 @@ Notation meaning ``$name`` use named argument, you can wrap the named argument in curly braces (eg. ``${name}``) to separate it from the next characters. +``$$`` produces a single ``$`` ``$1`` use first argument ``$-1`` use last argument ``${1..3}`` use arguments 1 to 3 ``${..}`` use all arguments +``$*`` use all arguments (same as ``${..}``) ``${#..}`` use all remaining arguments ``${..-2}`` use all arguments except the last argument ``${$1}`` use argument X where ``X = parseInt(arg[1])`` @@ -45,7 +47,7 @@ Notation meaning Examples ======== -.. code-block:: nimrod +.. code-block:: nim subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)" diff --git a/doc/tools.txt b/doc/tools.txt index 0127bc4a8..7f2830879 100644 --- a/doc/tools.txt +++ b/doc/tools.txt @@ -1,20 +1,24 @@ -=========================== -Tools available with Nimrod -=========================== +======================== +Tools available with Nim +======================== The standard distribution ships with the following tools: -- | `Nimrod Installation Generator `_ - | How to generate a nice installer for your Nimrod program. +- | `Nim Installation Generator `_ + | How to generate a nice installer for your Nim program. - | `C2nim `_ - | C to Nimrod source converter. Translates C header files to Nimrod. + | C to Nim source converter. Translates C header files to Nim. - | `nimgrep `_ - | Nimrod search and replace utility. + | Nim search and replace utility. - | `endb `_ - | Nimrod's slow platform independent embedded debugger. + | Nim's slow platform independent embedded debugger. - | `estp `_ - | Nimrod's slow platform independent embedded stack trace profiler. + | Nim's slow platform independent embedded stack trace profiler. + +- | `nimfix `_ + | Nimfix is a tool to help you upgrade from Nimrod (<= version 0.9.6) to + Nim (=> version 0.10.0). diff --git a/doc/tut1.txt b/doc/tut1.txt index ee642ce17..9d75b1ea2 100644 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -1,9 +1,9 @@ -======================== -Nimrod Tutorial (Part I) -======================== +===================== +Nim Tutorial (Part I) +===================== :Author: Andreas Rumpf -:Version: |nimrodversion| +:Version: |nimversion| .. contents:: @@ -16,7 +16,7 @@ Introduction

      -This document is a tutorial for the programming language *Nimrod*. +This document is a tutorial for the programming language *Nim*. This tutorial assumes that you are familiar with basic programming concepts like variables, types or statements but is kept very basic. The `manual `_ contains many more examples of the advanced language features. @@ -28,7 +28,7 @@ The first program We start the tour with a modified "hello world" program: -.. code-block:: Nimrod +.. code-block:: Nim # This is a comment echo("What's your name? ") var name: string = readLine(stdin) @@ -37,47 +37,47 @@ We start the tour with a modified "hello world" program: Save this code to the file "greetings.nim". Now compile and run it:: - nimrod compile --run greetings.nim + nim compile --run greetings.nim -With the ``--run`` `switch `_ Nimrod +With the ``--run`` `switch `_ Nim executes the file automatically after compilation. You can give your program command line arguments by appending them after the filename:: - nimrod compile --run greetings.nim arg1 arg2 + nim compile --run greetings.nim arg1 arg2 Commonly used commands and switches have abbreviations, so you can also use:: - nimrod c -r greetings.nim + nim c -r greetings.nim To compile a release version use:: - nimrod c -d:release greetings.nim + nim c -d:release greetings.nim -By default the Nimrod compiler generates a large amount of runtime checks +By default the Nim compiler generates a large amount of runtime checks aiming for your debugging pleasure. With ``-d:release`` these checks are `turned off and optimizations are turned on -`_. +`_. Though it should be pretty obvious what the program does, I will explain the syntax: statements which are not indented are executed when the program -starts. Indentation is Nimrod's way of grouping statements. Indentation is +starts. Indentation is Nim's way of grouping statements. Indentation is done with spaces only, tabulators are not allowed. String literals are enclosed in double quotes. The ``var`` statement declares a new variable named ``name`` of type ``string`` with the value that is -returned by the `readLine `_ procedure. Since the -compiler knows that `readLine `_ returns a string, +returned by the `readLine `_ procedure. Since the +compiler knows that `readLine `_ returns a string, you can leave out the type in the declaration (this is called `local type inference`:idx:). So this will work too: -.. code-block:: Nimrod +.. code-block:: Nim var name = readLine(stdin) Note that this is basically the only form of type inference that exists in -Nimrod: it is a good compromise between brevity and readability. +Nim: it is a good compromise between brevity and readability. The "hello world" program contains several identifiers that are already known -to the compiler: ``echo``, `readLine `_, etc. +to the compiler: ``echo``, `readLine `_, etc. These built-ins are declared in the system_ module which is implicitly imported by any other module. @@ -85,8 +85,8 @@ imported by any other module. Lexical elements ================ -Let us look at Nimrod's lexical elements in more detail: like other -programming languages Nimrod consists of (string) literals, identifiers, +Let us look at Nim's lexical elements in more detail: like other +programming languages Nim consists of (string) literals, identifiers, keywords, comments, operators, and other punctuation marks. @@ -97,7 +97,7 @@ String literals are enclosed in double quotes; character literals in single quotes. Special characters are escaped with ``\``: ``\n`` means newline, ``\t`` means tabulator, etc. There are also *raw* string literals: -.. code-block:: Nimrod +.. code-block:: Nim r"C:\program files\nim" In raw literals the backslash is not an escape character. @@ -112,54 +112,23 @@ Comments -------- Comments start anywhere outside a string or character literal with the -hash character ``#``. Documentation comments start with ``##``. Multiline -comments need to be aligned at the same column: +hash character ``#``. Documentation comments start with ``##``: -.. code-block:: nimrod - - i = 0 # This is a single comment over multiple lines belonging to the - # assignment statement. - # This is a new comment belonging to the current block, but to no particular - # statement. - i = i + 1 # This a new comment that is NOT - echo(i) # continued here, because this comment refers to the echo statement +.. code-block:: nim + # A comment. + + var myVariable: int ## a documentation comment -The alignment requirement does not hold if the preceding comment piece ends in -a backslash: +Documentation comments are tokens; they are only allowed at certain places in +the input file as they belong to the syntax tree! This feature enables simpler +documentation generators. -.. code-block:: nimrod - type - TMyObject {.final, pure, acyclic.} = object # comment continues: \ - # we have lots of space here to comment 'TMyObject'. - # This line belongs to the comment as it's properly aligned. - - -Comments are tokens; they are only allowed at certain places in the input file -as they belong to the syntax tree! This feature enables perfect source-to-source -transformations (such as pretty-printing) and simpler documentation generators. -A nice side-effect is that the human reader of the code always knows exactly -which code snippet the comment refers to. Since comments are a proper part of -the syntax, watch their indentation: - -.. code-block:: - echo("Hello!") - # comment has the same indentation as above statement -> fine - echo("Hi!") - # comment has not the correct indentation level -> syntax error! - -**Note**: To comment out a large piece of code, it is often better to use a -``when false:`` statement. - -.. code-block:: nimrod - when false: - brokenCode() - -Another option is to use the `discard statement`_ together with *long string +You can also use the `discard statement`_ together with *long string literals* to create block comments: -.. code-block:: nimrod - discard """ You can have any Nimrod code text commented +.. code-block:: nim + discard """ You can have any Nim code text commented out inside this with no indentation restrictions. yes("May I ask a pointless question?") """ @@ -204,7 +173,7 @@ to a storage location: ``=`` is the *assignment operator*. The assignment operator cannot be overloaded, overwritten or forbidden, but this might change in a future version -of Nimrod. You can declare multiple variables with a single assignment +of Nim. You can declare multiple variables with a single assignment statement and all the variables will have the same value: .. code-block:: @@ -229,7 +198,7 @@ Constants are symbols which are bound to a value. The constant's value cannot change. The compiler must be able to evaluate the expression in a constant declaration at compile time: -.. code-block:: nimrod +.. code-block:: nim const x = "abc" # the constant x contains the string "abc" Indentation can be used after the ``const`` keyword to list a whole section of @@ -277,7 +246,7 @@ If statement The if statement is one way to branch the control flow: -.. code-block:: nimrod +.. code-block:: nim let name = readLine(stdin) if name == "": echo("Poor soul, you lost your name?") @@ -298,7 +267,7 @@ Case statement Another way to branch is provided by the case statement. A case statement is a multi-branch: -.. code-block:: nimrod +.. code-block:: nim let name = readLine(stdin) case name of "": @@ -317,7 +286,7 @@ The case statement can deal with integers, other ordinal types and strings. (What an ordinal type is will be explained soon.) For integers or other ordinal types value ranges are also possible: -.. code-block:: nimrod +.. code-block:: nim # this statement will be explained later: from strutils import parseInt @@ -333,7 +302,7 @@ every value that ``n`` may contain, but the code only handles the values (though it is possible thanks to the range notation), we fix this by telling the compiler that for every other value nothing should be done: -.. code-block:: nimrod +.. code-block:: nim ... case n of 0..2, 4..7: echo("The number is in the set: {0, 1, 2, 4, 5, 6, 7}") @@ -343,7 +312,7 @@ the compiler that for every other value nothing should be done: The empty `discard statement`_ is a *do nothing* statement. The compiler knows that a case statement with an else part cannot fail and thus the error disappears. Note that it is impossible to cover all possible string values: -that is why there is no such check for string cases. +that is why string cases always need an ``else`` branch. In general the case statement is used for subrange types or enumerations where it is of great help that the compiler checks that you covered any possible @@ -355,7 +324,7 @@ While statement The while statement is a simple looping construct: -.. code-block:: nimrod +.. code-block:: nim echo("What's your name? ") var name = readLine(stdin) @@ -375,7 +344,7 @@ The ``for`` statement is a construct to loop over any element an *iterator* provides. The example uses the built-in `countup `_ iterator: -.. code-block:: nimrod +.. code-block:: nim echo("Counting to ten: ") for i in countup(1, 10): echo($i) @@ -387,7 +356,7 @@ other types into a string. The variable ``i`` is implicitly declared by the `_ returns. ``i`` runs through the values 1, 2, .., 10. Each value is ``echo``-ed. This code does the same: -.. code-block:: nimrod +.. code-block:: nim echo("Counting to 10: ") var i = 1 while i <= 10: @@ -397,16 +366,16 @@ Each value is ``echo``-ed. This code does the same: Counting down can be achieved as easily (but is less often needed): -.. code-block:: nimrod +.. code-block:: nim echo("Counting down from 10 to 1: ") for i in countdown(10, 1): echo($i) # --> Outputs 10 9 8 7 6 5 4 3 2 1 on different lines -Since counting up occurs so often in programs, Nimrod also has a `.. +Since counting up occurs so often in programs, Nim also has a `.. `_ iterator that does the same: -.. code-block:: nimrod +.. code-block:: nim for i in 1..10: ... @@ -417,7 +386,7 @@ Control flow statements have a feature not covered yet: they open a new scope. This means that in the following example, ``x`` is not accessible outside the loop: -.. code-block:: nimrod +.. code-block:: nim while false: var x = "hi" echo(x) # does not work @@ -426,7 +395,7 @@ A while (for) statement introduces an implicit block. Identifiers are only visible within the block they have been declared. The ``block`` statement can be used to open a new block explicitly: -.. code-block:: nimrod +.. code-block:: nim block myblock: var x = "hi" echo(x) # does not work either @@ -440,7 +409,7 @@ A block can be left prematurely with a ``break`` statement. The break statement can leave a ``while``, ``for``, or a ``block`` statement. It leaves the innermost construct, unless a label of a block is given: -.. code-block:: nimrod +.. code-block:: nim block myblock: echo("entering block") while true: @@ -461,7 +430,7 @@ Continue statement Like in many other programming languages, a ``continue`` statement starts the next iteration immediately: -.. code-block:: nimrod +.. code-block:: nim while true: let x = readLine(stdin) if x == "": continue @@ -473,7 +442,7 @@ When statement Example: -.. code-block:: nimrod +.. code-block:: nim when system.hostOS == "windows": echo("running on Windows!") @@ -504,17 +473,17 @@ possible. Statements and indentation ========================== -Now that we covered the basic control flow statements, let's return to Nimrod +Now that we covered the basic control flow statements, let's return to Nim indentation rules. -In Nimrod there is a distinction between *simple statements* and *complex +In Nim there is a distinction between *simple statements* and *complex statements*. *Simple statements* cannot contain other statements: Assignment, procedure calls or the ``return`` statement belong to the simple statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can contain other statements. To avoid ambiguities, complex statements always have to be indented, but single simple statements do not: -.. code-block:: nimrod +.. code-block:: nim # no indentation needed for single assignment statement: if x: x = false @@ -535,7 +504,7 @@ to be indented, but single simple statements do not: condition in an if statement is an example for an expression. Expressions can contain indentation at certain places for better readability: -.. code-block:: nimrod +.. code-block:: nim if thisIsaLongCondition() and thisIsAnotherLongCondition(1, @@ -548,7 +517,7 @@ an open parenthesis and after commas. With parenthesis and semicolons ``(;)`` you can use statements where only an expression is allowed: -.. code-block:: nimrod +.. code-block:: nim # computes fac(4) at compile time: const fac4 = (var x = 1; for i in 1..4: x *= i; x) @@ -557,11 +526,11 @@ Procedures ========== To define new commands like `echo `_ and `readLine -`_ in the examples, the concept of a `procedure` -is needed. (Some languages call them *methods* or *functions*.) In Nimrod new +`_ in the examples, the concept of a `procedure` +is needed. (Some languages call them *methods* or *functions*.) In Nim new procedures are defined with the ``proc`` keyword: -.. code-block:: nimrod +.. code-block:: nim proc yes(question: string): bool = echo(question, " (y/n)") while true: @@ -597,7 +566,7 @@ shorthand for ``return result``. The ``result`` value is always returned automatically at the end a procedure if there is no ``return`` statement at the exit. -.. code-block:: nimrod +.. code-block:: nim proc sumTillNegative(x: varargs[int]): int = for i in x: if i < 0: @@ -624,7 +593,7 @@ most efficient way. If a mutable variable is needed inside the procedure, it has to be declared with ``var`` in the procedure body. Shadowing the parameter name is possible, and actually an idiom: -.. code-block:: nimrod +.. code-block:: nim proc printSeq(s: seq, nprinted: int = -1) = var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len) for i in 0 .. `_ operator in -Nimrod.) The compiler chooses the most appropriate proc for the ``toString`` +Nim.) The compiler chooses the most appropriate proc for the ``toString`` calls. How this overloading resolution algorithm works exactly is not discussed here (it will be specified in the manual soon). However, it does not lead to nasty surprises and is based on a quite simple unification @@ -744,13 +713,13 @@ algorithm. Ambiguous calls are reported as errors. Operators --------- -The Nimrod library makes heavy use of overloading - one reason for this is that +The Nim library makes heavy use of overloading - one reason for this is that each operator like ``+`` is a just an overloaded proc. The parser lets you use operators in `infix notation` (``a + b``) or `prefix notation` (``+ a``). An infix operator always receives two arguments, a prefix operator always one. Postfix operators are not possible, because this would be ambiguous: does ``a @ @ b`` mean ``(a) @ (@b)`` or ``(a@) @ (b)``? It always means -``(a) @ (@b)``, because there are no postfix operators in Nimrod. +``(a) @ (@b)``, because there are no postfix operators in Nim. Apart from a few built-in keyword operators such as ``and``, ``or``, ``not``, operators always consist of these characters: @@ -764,7 +733,7 @@ can be found in the manual. To define a new operator enclose the operator in backticks "``": -.. code-block:: nimrod +.. code-block:: nim proc `$` (x: myDataType): string = ... # now the $ operator also works with myDataType, overloading resolution # ensures that $ works for built-in types just like before @@ -772,7 +741,7 @@ To define a new operator enclose the operator in backticks "``": The "``" notation can also be used to call an operator just like any other procedure: -.. code-block:: nimrod +.. code-block:: nim if `==`( `+`(3, 4), 7): echo("True") @@ -783,7 +752,7 @@ Every variable, procedure, etc. needs to be declared before it can be used. (The reason for this is compilation efficiency.) However, this cannot be done for mutually recursive procedures: -.. code-block:: nimrod +.. code-block:: nim # forward declaration: proc even(n: int): bool @@ -810,7 +779,7 @@ Iterators Let's return to the boring counting example: -.. code-block:: nimrod +.. code-block:: nim echo("Counting to ten: ") for i in countup(1, 10): echo($i) @@ -818,7 +787,7 @@ Let's return to the boring counting example: Can a `countup `_ proc be written that supports this loop? Lets try: -.. code-block:: nimrod +.. code-block:: nim proc countup(a, b: int): int = var res = a while res <= b: @@ -831,7 +800,7 @@ finished. This *return and continue* is called a `yield` statement. Now the only thing left to do is to replace the ``proc`` keyword by ``iterator`` and there it is - our first iterator: -.. code-block:: nimrod +.. code-block:: nim iterator countup(a, b: int): int = var res = a while res <= b: @@ -868,7 +837,7 @@ that are available for them in detail. Booleans -------- -The boolean type is named ``bool`` in Nimrod and consists of the two +The boolean type is named ``bool`` in Nim and consists of the two pre-defined values ``true`` and ``false``. Conditions in while, if, elif, when statements need to be of type bool. @@ -876,7 +845,7 @@ The operators ``not, and, or, xor, <, <=, >, >=, !=, ==`` are defined for the bool type. The ``and`` and ``or`` operators perform short-cut evaluation. Example: -.. code-block:: nimrod +.. code-block:: nim while p != nil and p.name != "xyz": # p.name is not evaluated if p == nil @@ -885,7 +854,7 @@ evaluation. Example: Characters ---------- -The `character type` is named ``char`` in Nimrod. Its size is one byte. +The `character type` is named ``char`` in Nim. Its size is one byte. Thus it cannot represent an UTF-8 character, but a part of it. The reason for this is efficiency: for the overwhelming majority of use-cases, the resulting programs will still handle UTF-8 properly as UTF-8 was specially @@ -900,13 +869,13 @@ Converting from an integer to a ``char`` is done with the ``chr`` proc. Strings ------- -String variables in Nimrod are **mutable**, so appending to a string -is quite efficient. Strings in Nimrod are both zero-terminated and have a +String variables in Nim are **mutable**, so appending to a string +is quite efficient. Strings in Nim are both zero-terminated and have a length field. One can retrieve a string's length with the builtin ``len`` procedure; the length never counts the terminating zero. Accessing the terminating zero is no error and often leads to simpler code: -.. code-block:: nimrod +.. code-block:: nim if s[i] == 'a' and s[i+1] == 'b': # no need to check whether ``i < len(s)``! ... @@ -929,14 +898,14 @@ object on the heap, so there is a trade-off to be made here. Integers -------- -Nimrod has these integer types built-in: +Nim has these integer types built-in: ``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``. The default integer type is ``int``. Integer literals can have a *type suffix* to mark them to be of another integer type: -.. code-block:: nimrod +.. code-block:: nim let x = 0 # x is of type ``int`` y = 0'i8 # y is of type ``int8`` @@ -964,7 +933,7 @@ cannot be detected at compile time). Floats ------ -Nimrod has these floating point types built-in: ``float float32 float64``. +Nim has these floating point types built-in: ``float float32 float64``. The default float type is ``float``. In the current implementation, ``float`` is always 64 bit wide. @@ -972,7 +941,7 @@ The default float type is ``float``. In the current implementation, Float literals can have a *type suffix* to mark them to be of another float type: -.. code-block:: nimrod +.. code-block:: nim var x = 0.0 # x is of type ``float`` y = 0.0'f32 # y is of type ``float32`` @@ -988,6 +957,19 @@ versa. The `toInt `_ and `toFloat `_ procs can be used for these conversions. +Type Conversion +--------------- +Conversion between basic types in nim is performed by using the +type as a function: + +.. code-block:: nim + var + x: int32 = 1.int32 # same as calling int32(1) + y: int8 = int8('a') # 'a' == 97'i8 + z: float = 2.5 # int(2.5) rounds down to 2 + sum: int = int(x) + int(y) + int(z) # sum == 100 + + Internal type representation ============================ @@ -1001,11 +983,11 @@ having to write its ``$`` operator. You can use then the `repr graphs with cycles. The following example shows that even for basic types there is a difference between the ``$`` and ``repr`` outputs: -.. code-block:: nimrod +.. code-block:: nim var myBool = true myCharacter = 'n' - myString = "nimrod" + myString = "nim" myInteger = 42 myFloat = 3.14 echo($myBool, ":", repr(myBool)) @@ -1013,7 +995,7 @@ there is a difference between the ``$`` and ``repr`` outputs: echo($myCharacter, ":", repr(myCharacter)) # --> n:'n' echo($myString, ":", repr(myString)) - # --> nimrod:0x10fa8c050"nimrod" + # --> nim:0x10fa8c050"nim" echo($myInteger, ":", repr(myInteger)) # --> 42:42 echo($myFloat, ":", repr(myFloat)) @@ -1023,9 +1005,9 @@ there is a difference between the ``$`` and ``repr`` outputs: Advanced types ============== -In Nimrod new types can be defined within a ``type`` statement: +In Nim new types can be defined within a ``type`` statement: -.. code-block:: nimrod +.. code-block:: nim type biggestInt = int64 # biggest integer type that is available biggestFloat = float64 # biggest float type that is available @@ -1041,20 +1023,19 @@ limited set. This set consists of ordered symbols. Each symbol is mapped to an integer value internally. The first symbol is represented at runtime by 0, the second by 1 and so on. Example: -.. code-block:: nimrod +.. code-block:: nim type - TDirection = enum + Direction = enum north, east, south, west - var x = south # `x` is of type `TDirection`; its value is `south` + var x = south # `x` is of type `Direction`; its value is `south` echo($x) # writes "south" to `stdout` -(To prefix a new type with the letter ``T`` is a convention in Nimrod.) All comparison operators can be used with enumeration types. An enumeration's symbol can be qualified to avoid ambiguities: -``TDirection.south``. +``Direction.south``. The ``$`` operator can convert any enumeration value to its name, the ``ord`` proc to its underlying integer value. @@ -1066,9 +1047,9 @@ explicitly given is assigned the value of the previous symbol + 1. An explicit ordered enum can have *holes*: -.. code-block:: nimrod +.. code-block:: nim type - TMyEnum = enum + MyEnum = enum a = 2, b = 4, c = 89 @@ -1104,13 +1085,13 @@ Subranges A subrange type is a range of values from an integer or enumeration type (the base type). Example: -.. code-block:: nimrod +.. code-block:: nim type - TSubrange = range[0..5] + Subrange = range[0..5] -``TSubrange`` is a subrange of ``int`` which can only hold the values 0 -to 5. Assigning any other value to a variable of type ``TSubrange`` is a +``Subrange`` is a subrange of ``int`` which can only hold the values 0 +to 5. Assigning any other value to a variable of type ``Subrange`` is a compile-time or runtime error. Assignments from the base type to one of its subrange types (and vice versa) are allowed. @@ -1119,7 +1100,7 @@ type as ``range[0..high(int)]`` (`high `_ returns the maximal value). Other programming languages mandate the usage of unsigned integers for natural numbers. This is often **wrong**: you don't want unsigned arithmetic (which wraps around) just because the numbers cannot be negative. -Nimrod's ``Natural`` type helps to avoid this common programming error. +Nim's ``Natural`` type helps to avoid this common programming error. Sets @@ -1134,12 +1115,12 @@ the array has the same type. The array's index type can be any ordinal type. Arrays can be constructed via ``[]``: -.. code-block:: nimrod +.. code-block:: nim type - TIntArray = array[0..5, int] # an array that is indexed with 0..5 + IntArray = array[0..5, int] # an array that is indexed with 0..5 var - x: TIntArray + x: IntArray x = [1, 2, 3, 4, 5, 6] for i in low(x)..high(x): echo(x[i]) @@ -1149,22 +1130,22 @@ Array access is always bounds checked (at compile-time or at runtime). These checks can be disabled via pragmas or invoking the compiler with the ``--bound_checks:off`` command line switch. -Arrays are value types, like any other Nimrod type. The assignment operator +Arrays are value types, like any other Nim type. The assignment operator copies the whole array contents. The built-in `len `_ proc returns the array's length. `low(a) `_ returns the lowest valid index for the array `a` and `high(a) `_ the highest valid index. -.. code-block:: nimrod +.. code-block:: nim type - TDirection = enum + Direction = enum north, east, south, west - TBlinkLights = enum + BlinkLights = enum off, on, slowBlink, mediumBlink, fastBlink - TLevelSetting = array[north..west, TBlinkLights] + LevelSetting = array[north..west, BlinkLights] var - level : TLevelSetting + level: LevelSetting level[north] = on level[south] = slowBlink level[east] = fastBlink @@ -1175,17 +1156,17 @@ array `a` and `high(a) `_ the highest valid index. The syntax for nested arrays (multidimensional) in other languages is a matter of appending more brackets because usually each dimension is restricted to the -same index type as the others. In nimrod you can have different dimensions with +same index type as the others. In Nim you can have different dimensions with different index types, so the nesting syntax is slightly different. Building on the previous example where a level is defined as an array of enums indexed by yet another enum, we can add the following lines to add a light tower type subdivided in height levels accessed through their integer index: -.. code-block:: nimrod +.. code-block:: nim type - TLightTower = array[1..10, TLevelSetting] + LightTower = array[1..10, LevelSetting] var - tower: TLightTower + tower: LightTower tower[1][north] = slowBlink tower[1][east] = mediumBlink echo len(tower) # --> 10 @@ -1196,24 +1177,24 @@ subdivided in height levels accessed through their integer index: #tower[0][1] = on Note how the built-in ``len`` proc returns only the array's first dimension -length. Another way of defining the ``TLightTower`` to show better its -nested nature would be to omit the previous definition of the ``TLevelSetting`` +length. Another way of defining the ``LightTower`` to show better its +nested nature would be to omit the previous definition of the ``LevelSetting`` type and instead write it embedded directly as the type of the first dimension: -.. code-block:: nimrod +.. code-block:: nim type - TLightTower = array[1..10, array[north..west, TBlinkLights]] + LightTower = array[1..10, array[north..west, BlinkLights]] It is quite frequent to have arrays start at zero, so there's a shortcut syntax to specify a range from zero to the specified index minus one: -.. code-block:: nimrod +.. code-block:: nim type - TIntArray = array[0..5, int] # an array that is indexed with 0..5 - TQuickArray = array[6, int] # an array that is indexed with 0..5 + IntArray = array[0..5, int] # an array that is indexed with 0..5 + QuickArray = array[6, int] # an array that is indexed with 0..5 var - x: TIntArray - y: TQuickArray + x: IntArray + y: QuickArray x = [1, 2, 3, 4, 5, 6] y = x for i in low(x)..high(x): @@ -1239,7 +1220,7 @@ A sequence may be passed to an openarray parameter. Example: -.. code-block:: nimrod +.. code-block:: nim var x: seq[int] # a sequence of integers @@ -1261,7 +1242,7 @@ value. Here the ``for`` statement is looping over the results from the `pairs() `_ iterator from the `system `_ module. Examples: -.. code-block:: nimrod +.. code-block:: nim for i in @[3, 4, 5]: echo($i) # --> 3 @@ -1299,8 +1280,8 @@ also a means to implement passing a variable number of arguments to a procedure. The compiler converts the list of arguments to an array automatically: -.. code-block:: nimrod - proc myWriteln(f: TFile, a: varargs[string]) = +.. code-block:: nim + proc myWriteln(f: File, a: varargs[string]) = for s in items(a): write(f, s) write(f, "\n") @@ -1313,8 +1294,8 @@ This transformation is only done if the varargs parameter is the last parameter in the procedure header. It is also possible to perform type conversions in this context: -.. code-block:: nimrod - proc myWriteln(f: TFile, a: varargs[string, `$`]) = +.. code-block:: nim + proc myWriteln(f: File, a: varargs[string, `$`]) = for s in items(a): write(f, s) write(f, "\n") @@ -1336,10 +1317,10 @@ context. A slice is just an object of type TSlice which contains two bounds, `a` and `b`. By itself a slice is not very useful, but other collection types define operators which accept TSlice objects to define ranges. -.. code-block:: nimrod +.. code-block:: nim var - a = "Nimrod is a progamming language" + a = "Nim is a progamming language" b = "Slices are useless." echo a[10..15] # --> 'a prog' @@ -1366,7 +1347,7 @@ The assignment operator for tuples copies each component. The notation ``t[i]`` to access the ``i``'th field. Here ``i`` needs to be a constant integer. -.. code-block:: nimrod +.. code-block:: nim type TPerson = tuple[name: string, age: int] # type representing a person: @@ -1411,19 +1392,19 @@ use parenthesis around the values you want to assign the unpacking to, otherwise you will be assigning the same value to all the individual variables! Example: -.. code-block:: nimrod +.. code-block:: nim import os let - path = "usr/local/nimrodc.html" + path = "usr/local/nimc.html" (dir, name, ext) = splitFile(path) baddir, badname, badext = splitFile(path) echo dir # outputs `usr/local` - echo name # outputs `nimrodc` + echo name # outputs `nimc` echo ext # outputs `.html` # All the following output the same line: - # `(dir: usr/local, name: nimrodc, ext: .html)` + # `(dir: usr/local, name: nimc, ext: .html)` echo baddir echo badname echo badext @@ -1431,12 +1412,12 @@ variables! Example: Tuple unpacking **only** works in ``var`` or ``let`` blocks. The following code won't compile: -.. code-block:: nimrod +.. code-block:: nim import os var - path = "usr/local/nimrodc.html" + path = "usr/local/nimc.html" dir, name, ext = "" (dir, name, ext) = splitFile(path) @@ -1449,7 +1430,7 @@ References (similar to pointers in other programming languages) are a way to introduce many-to-one relationships. This means different references can point to and modify the same location in memory. -Nimrod distinguishes between `traced`:idx: and `untraced`:idx: references. +Nim distinguishes between `traced`:idx: and `untraced`:idx: references. Untraced references are also called *pointers*. Traced references point to objects of a garbage collected heap, untraced references point to manually allocated objects or to objects somewhere else in memory. Thus @@ -1464,19 +1445,19 @@ meaning to retrieve the item the reference points to. The ``.`` (access a tuple/object field operator) and ``[]`` (array/string/sequence index operator) operators perform implicit dereferencing operations for reference types: -.. code-block:: nimrod +.. code-block:: nim type - PNode = ref TNode - TNode = tuple[le, ri: PNode, data: int] + Node = ref NodeObj + NodeObj = object + le, ri: PNode + data: int var - n: PNode + n: Node new(n) n.data = 9 # no need to write n[].data; in fact n[].data is highly discouraged! -(As a convention, reference types use a 'P' prefix.) - To allocate a new traced object, the built-in procedure ``new`` has to be used. To deal with untraced memory, the procedures ``alloc``, ``dealloc`` and ``realloc`` can be used. The documentation of the `system `_ @@ -1489,12 +1470,12 @@ Procedural type --------------- A procedural type is a (somewhat abstract) pointer to a procedure. ``nil`` is an allowed value for a variable of a procedural type. -Nimrod uses procedural types to achieve `functional`:idx: programming +Nim uses procedural types to achieve `functional`:idx: programming techniques. Example: -.. code-block:: nimrod +.. code-block:: nim proc echoItem(x: int) = echo(x) proc forEach(action: proc (x: int)) = @@ -1513,13 +1494,13 @@ listed in the `manual `_. Modules ======= -Nimrod supports splitting a program into pieces with a module concept. +Nim supports splitting a program into pieces with a module concept. Each module is in its own file. Modules enable `information hiding`:idx: and `separate compilation`:idx:. A module may gain access to symbols of another module by the `import`:idx: statement. Only top-level symbols that are marked with an asterisk (``*``) are exported: -.. code-block:: nimrod +.. code-block:: nim # Module A var x*, y: int @@ -1554,7 +1535,7 @@ The algorithm for compiling modules is: This is best illustrated by an example: -.. code-block:: nimrod +.. code-block:: nim # Module A type T1* = int # Module A exports the type ``T1`` @@ -1565,7 +1546,7 @@ This is best illustrated by an example: main() -.. code-block:: nimrod +.. code-block:: nim # Module B import A # A is not parsed here! Only the already known symbols # of A are imported. @@ -1581,15 +1562,15 @@ the symbol is ambiguous, it even *has* to be qualified. A symbol is ambiguous if it is defined in two (or more) different modules and both modules are imported by a third one: -.. code-block:: nimrod +.. code-block:: nim # Module A var x*: string -.. code-block:: nimrod +.. code-block:: nim # Module B var x*: int -.. code-block:: nimrod +.. code-block:: nim # Module C import A, B write(stdout, x) # error: x is ambiguous @@ -1602,15 +1583,15 @@ imported by a third one: But this rule does not apply to procedures or iterators. Here the overloading rules apply: -.. code-block:: nimrod +.. code-block:: nim # Module A - proc x*(a: int): string = result = $a + proc x*(a: int): string = $a -.. code-block:: nimrod +.. code-block:: nim # Module B - proc x*(a: string): string = result = $a + proc x*(a: string): string = $a -.. code-block:: nimrod +.. code-block:: nim # Module C import A, B write(stdout, x(3)) # no error: A.x is called @@ -1627,7 +1608,7 @@ The normal ``import`` statement will bring in all exported symbols. These can be limited by naming symbols which should be excluded with the ``except`` qualifier. -.. code-block:: nimrod +.. code-block:: nim import mymodule except y @@ -1638,19 +1619,19 @@ We have already seen the simple ``import`` statement that just imports all exported symbols. An alternative that only imports listed symbols is the ``from import`` statement: -.. code-block:: nimrod +.. code-block:: nim from mymodule import x, y, z The ``from`` statement can also force namespace qualification on symbols, thereby making symbols available, but needing to be qualified to be used. -.. code-block:: nimrod +.. code-block:: nim from mymodule import x, y, z x() # use x without any qualification -.. code-block:: nimrod +.. code-block:: nim from mymodule import nil mymodule.x() # must qualify x with the module name as prefix @@ -1660,7 +1641,7 @@ to be used. Since module names are generally long to be descriptive, you can also define a shorter alias to use when qualifying symbols. -.. code-block:: nimrod +.. code-block:: nim from mymodule as m import nil m.x() # m is aliasing mymodule @@ -1672,18 +1653,15 @@ The ``include`` statement does something fundamentally different than importing a module: it merely includes the contents of a file. The ``include`` statement is useful to split up a large module into several files: -.. code-block:: nimrod +.. code-block:: nim include fileA, fileB, fileC -**Note**: The documentation generator currently does not follow ``include`` -statements, so exported symbols in an include file will not show up in the -generated documentation. Part 2 ====== -So, now that we are done with the basics, let's see what Nimrod offers apart +So, now that we are done with the basics, let's see what Nim offers apart from a nice syntax for procedural programming: `Part II `_ diff --git a/doc/tut2.txt b/doc/tut2.txt index 582a6b3e4..6e239d681 100644 --- a/doc/tut2.txt +++ b/doc/tut2.txt @@ -1,9 +1,9 @@ -========================= -Nimrod Tutorial (Part II) -========================= +====================== +Nim Tutorial (Part II) +====================== :Author: Andreas Rumpf -:Version: |nimrodversion| +:Version: |nimversion| .. contents:: @@ -11,11 +11,10 @@ Nimrod Tutorial (Part II) Introduction ============ - "Object-oriented programming is an exceptionally bad idea which could - only have originated in California." --Edsger Dijkstra + "Repetition renders the ridiculous reasonable." -- Norman Wildberger -This document is a tutorial for the advanced constructs of the *Nimrod* +This document is a tutorial for the advanced constructs of the *Nim* programming language. **Note that this document is somewhat obsolete as the** `manual `_ **contains many more examples of the advanced language features.** @@ -24,18 +23,18 @@ features.** Pragmas ======= -Pragmas are Nimrod's method to give the compiler additional information/ +Pragmas are Nim's method to give the compiler additional information/ commands without introducing a massive number of new keywords. Pragmas are enclosed in the special ``{.`` and ``.}`` curly dot brackets. This tutorial does not cover pragmas. See the `manual `_ or `user guide -`_ for a description of the available +`_ for a description of the available pragmas. Object Oriented Programming =========================== -While Nimrod's support for object oriented programming (OOP) is minimalistic, +While Nim's support for object oriented programming (OOP) is minimalistic, powerful OOP technics can be used. OOP is seen as *one* way to design a program, not *the only* way. Often a procedural approach leads to simpler and more efficient code. In particular, prefering composition over inheritance @@ -55,7 +54,7 @@ a *constructor*). Objects have access to their type at runtime. There is an ``of`` operator that can be used to check the object's type: -.. code-block:: nimrod +.. code-block:: nim type TPerson = object of TObject name*: string # the * means that `name` is accessible from other modules @@ -86,20 +85,20 @@ in the GTK wrapper for instance.) **Note**: Composition (*has-a* relation) is often preferable to inheritance (*is-a* relation) for simple code reuse. Since objects are value types in -Nimrod, composition is as efficient as inheritance. +Nim, composition is as efficient as inheritance. Mutually recursive types ------------------------ Objects, tuples and references can model quite complex data structures which -depend on each other; they are *mutually recursive*. In Nimrod +depend on each other; they are *mutually recursive*. In Nim these types can only be declared within a single type section. (Anything else would require arbitrary symbol lookahead which slows down compilation.) Example: -.. code-block:: nimrod +.. code-block:: nim type PNode = ref TNode # a traced reference to a TNode TNode = object @@ -114,7 +113,7 @@ Example: Type conversions ---------------- -Nimrod distinguishes between `type casts`:idx: and `type conversions`:idx:. +Nim distinguishes between `type casts`:idx: and `type conversions`:idx:. Casts are done with the ``cast`` operator and force the compiler to interpret a bit pattern to be of another type. @@ -126,11 +125,11 @@ raised. The syntax for type conversions is ``destination_type(expression_to_convert)`` (like an ordinary call): -.. code-block:: nimrod +.. code-block:: nim proc getID(x: TPerson): int = TStudent(x).id -The ``EInvalidObjectConversion`` exception is raised if ``x`` is not a +The ``InvalidObjectConversionError`` exception is raised if ``x`` is not a ``TStudent``. @@ -141,9 +140,9 @@ variant types are needed. An example: -.. code-block:: nimrod +.. code-block:: nim - # This is an example how an abstract syntax tree could be modeled in Nimrod + # This is an example how an abstract syntax tree could be modeled in Nim type TNodeKind = enum # the different node types nkInt, # a leaf with an integer value @@ -164,7 +163,7 @@ An example: condition, thenPart, elsePart: PNode var n = PNode(kind: nkFloat, floatVal: 1.0) - # the following statement raises an `EInvalidField` exception, because + # the following statement raises an `FieldError` exception, because # n.kind's value does not fit: n.strVal = "" @@ -183,8 +182,8 @@ bound to a class. This has disadvantages: * Often it is unclear where the method should belong to: is ``join`` a string method or an array method? -Nimrod avoids these problems by not assigning methods to a class. All methods -in Nimrod are multi-methods. As we will see later, multi-methods are +Nim avoids these problems by not assigning methods to a class. All methods +in Nim are multi-methods. As we will see later, multi-methods are distinguished from procs only for dynamic binding purposes. @@ -199,7 +198,7 @@ If there are no remaining arguments, the parentheses can be omitted: This method call syntax is not restricted to objects, it can be used for any type: -.. code-block:: nimrod +.. code-block:: nim echo("abc".len) # is the same as echo(len("abc")) echo("abc".toUpper()) @@ -211,7 +210,7 @@ postfix notation.) So "pure object oriented" code is easy to write: -.. code-block:: nimrod +.. code-block:: nim import strutils stdout.writeln("Give a list of numbers (separated by spaces): ") @@ -221,12 +220,12 @@ So "pure object oriented" code is easy to write: Properties ---------- -As the above example shows, Nimrod has no need for *get-properties*: +As the above example shows, Nim has no need for *get-properties*: Ordinary get-procedures that are called with the *method call syntax* achieve the same. But setting a value is different; for this a special setter syntax is needed: -.. code-block:: nimrod +.. code-block:: nim type TSocket* = object of TObject @@ -252,7 +251,7 @@ is needed: The ``[]`` array access operator can be overloaded to provide `array properties`:idx:\ : -.. code-block:: nimrod +.. code-block:: nim type TVector* = object x, y, z: float @@ -283,7 +282,7 @@ Dynamic dispatch Procedures always use static dispatch. For dynamic dispatch replace the ``proc`` keyword by ``method``: -.. code-block:: nimrod +.. code-block:: nim type PExpr = ref object of TObject ## abstract base class for an expression PLiteral = ref object of PExpr @@ -311,7 +310,7 @@ requires dynamic binding. In a multi-method all parameters that have an object type are used for the dispatching: -.. code-block:: nimrod +.. code-block:: nim type TThing = object of TObject @@ -336,7 +335,7 @@ As the example demonstrates, invocation of a multi-method cannot be ambiguous: Collide 2 is preferred over collide 1 because the resolution works from left to right. Thus ``TUnit, TThing`` is preferred over ``TThing, TUnit``. -**Perfomance note**: Nimrod does not produce a virtual method table, but +**Perfomance note**: Nim does not produce a virtual method table, but generates dispatch trees. This avoids the expensive indirect branch for method calls and enables inlining. However, other optimizations like compile time evaluation or dead code elimination do not work with methods. @@ -345,10 +344,10 @@ evaluation or dead code elimination do not work with methods. Exceptions ========== -In Nimrod exceptions are objects. By convention, exception types are -prefixed with an 'E', not 'T'. The `system `_ module defines an +In Nim exceptions are objects. By convention, exception types are +suffixed with 'Error'. The `system `_ module defines an exception hierarchy that you might want to stick to. Exceptions derive from -E_Base, which provides the common interface. +``system.Exception``, which provides the common interface. Exceptions have to be allocated on the heap because their lifetime is unknown. The compiler will prevent you from raising an exception created on the stack. @@ -364,9 +363,9 @@ Raise statement --------------- Raising an exception is done with the ``raise`` statement: -.. code-block:: nimrod +.. code-block:: nim var - e: ref EOS + e: ref OSError new(e) e.msg = "the request to the OS failed" raise e @@ -375,8 +374,8 @@ If the ``raise`` keyword is not followed by an expression, the last exception is *re-raised*. For the purpose of avoiding repeating this common code pattern, the template ``newException`` in the ``system`` module can be used: -.. code-block:: nimrod - raise newException(EOS, "the request to the OS failed") +.. code-block:: nim + raise newException(OSError, "the request to the OS failed") Try statement @@ -384,21 +383,21 @@ Try statement The ``try`` statement handles exceptions: -.. code-block:: nimrod +.. code-block:: nim # read the first two lines of a text file that should contain numbers # and tries to add them var - f: TFile + f: File if open(f, "numbers.txt"): try: let a = readLine(f) let b = readLine(f) echo "sum: ", parseInt(a) + parseInt(b) - except EOverflow: + except OverflowError: echo "overflow!" - except EInvalidValue: + except ValueError: echo "could not convert string to integer" - except EIO: + except IOError: echo "IO error!" except: echo "Unknown exception!" @@ -426,9 +425,9 @@ If you need to *access* the actual exception object or message inside an ``except`` branch you can use the `getCurrentException() `_ and `getCurrentExceptionMsg() `_ procs from the `system `_ -module. Example: +module. Example: -.. code-block:: nimrod +.. code-block:: nim try: doSomethingHere() except: @@ -444,12 +443,12 @@ Annotating procs with raised exceptions Through the use of the optional ``{.raises.}`` pragma you can specify that a proc is meant to raise a specific set of exceptions, or none at all. If the ``{.raises.}`` pragma is used, the compiler will verify that this is true. For -instance, if you specify that a proc raises ``EIO``, and at some point it (or -one of the procs it calls) starts raising a new exception the compiler will +instance, if you specify that a proc raises ``IOError``, and at some point it +(or one of the procs it calls) starts raising a new exception the compiler will prevent that proc from compiling. Usage example: -.. code-block:: nimrod - proc complexProc() {.raises: [EIO, EArithmetic].} = +.. code-block:: nim + proc complexProc() {.raises: [IOError, ArithmeticError].} = ... proc simpleProc() {.raises: [].} = @@ -464,21 +463,21 @@ help you locate the offending code which has changed. If you want to add the ``{.raises.}`` pragma to existing code, the compiler can also help you. You can add the ``{.effects.}`` pragma statement to your proc and the compiler will output all inferred effects up to that point (exception -tracking is part of Nimrod's effect system). Another more roundabout way to -find out the list of exceptions raised by a proc is to use the Nimrod ``doc2`` +tracking is part of Nim's effect system). Another more roundabout way to +find out the list of exceptions raised by a proc is to use the Nim ``doc2`` command which generates documentation for a whole module and decorates all -procs with the list of raised exceptions. You can read more about Nimrod's +procs with the list of raised exceptions. You can read more about Nim's `effect system and related pragmas in the manual `_. Generics ======== -Generics are Nimrod's means to parametrize procs, iterators or types +Generics are Nim's means to parametrize procs, iterators or types with `type parameters`:idx:. They are most useful for efficient type safe containers: -.. code-block:: nimrod +.. code-block:: nim type TBinaryTree[T] = object # TBinaryTree is a generic type with # with generic param ``T`` @@ -545,7 +544,7 @@ is not hidden and is used in the ``preorder`` iterator. Templates ========= -Templates are a simple substitution mechanism that operates on Nimrod's +Templates are a simple substitution mechanism that operates on Nim's abstract syntax trees. Templates are processed in the semantic pass of the compiler. They integrate well with the rest of the language and share none of C's preprocessor macros flaws. @@ -554,7 +553,7 @@ To *invoke* a template, call it like a procedure. Example: -.. code-block:: nimrod +.. code-block:: nim template `!=` (a, b: expr): expr = # this definition exists in the System module not (a == b) @@ -573,7 +572,7 @@ for IEEE floating point numbers - NaN breaks basic boolean logic.) Templates are especially useful for lazy evaluation purposes. Consider a simple proc for logging: -.. code-block:: nimrod +.. code-block:: nim const debug = true @@ -590,7 +589,7 @@ evaluation for procedures is *eager*). Turning the ``log`` proc into a template solves this problem: -.. code-block:: nimrod +.. code-block:: nim const debug = true @@ -606,37 +605,16 @@ The parameters' types can be ordinary types or the meta types ``expr`` (stands for *type description*). If the template has no explicit return type, ``stmt`` is used for consistency with procs and methods. -The template body does not open a new scope. To open a new scope use a ``block`` -statement: - -.. code-block:: nimrod - template declareInScope(x: expr, t: typeDesc): stmt {.immediate.} = - var x: t - - template declareInNewScope(x: expr, t: typeDesc): stmt {.immediate.} = - # open a new scope: - block: - var x: t - - declareInScope(a, int) - a = 42 # works, `a` is known here - - declareInNewScope(b, int) - b = 42 # does not work, `b` is unknown - -(The `manual explains `_ why the -``immediate`` pragma is needed for these templates.) - If there is a ``stmt`` parameter it should be the last in the template declaration. The reason is that statements can be passed to a template via a special ``:`` syntax: -.. code-block:: nimrod +.. code-block:: nim - template withFile(f: expr, filename: string, mode: TFileMode, + template withFile(f: expr, filename: string, mode: FileMode, body: stmt): stmt {.immediate.} = let fn = filename - var f: TFile + var f: File if open(f, fn, mode): try: body @@ -660,18 +638,18 @@ Macros ====== Macros enable advanced compile-time code transformations, but they cannot -change Nimrod's syntax. However, this is no real restriction because Nimrod's -syntax is flexible enough anyway. Macros have to be implemented in pure Nimrod +change Nim's syntax. However, this is no real restriction because Nim's +syntax is flexible enough anyway. Macros have to be implemented in pure Nim code if `foreign function interface (FFI) `_ is not enabled in the compiler, but other than that restriction (which at some point in the future will go away) -you can write any kind of Nimrod code and the compiler will run it at compile +you can write any kind of Nim code and the compiler will run it at compile time. -There are two ways to write a macro, either *generating* Nimrod source code and +There are two ways to write a macro, either *generating* Nim source code and letting the compiler parse it, or creating manually an abstract syntax tree (AST) which you feed to the compiler. In order to build the AST one needs to -know how the Nimrod concrete syntax is converted to an abstract syntax tree +know how the Nim concrete syntax is converted to an abstract syntax tree (AST). The AST is documented in the `macros `_ module. Once your macro is finished, there are two ways to invoke it: @@ -686,13 +664,13 @@ Expression Macros The following example implements a powerful ``debug`` command that accepts a variable number of arguments: -.. code-block:: nimrod - # to work with Nimrod syntax trees, we need an API that is defined in the +.. code-block:: nim + # to work with Nim syntax trees, we need an API that is defined in the # ``macros`` module: import macros macro debug(n: varargs[expr]): stmt = - # `n` is a Nimrod AST that contains a list of expressions; + # `n` is a Nim AST that contains a list of expressions; # this macro returns a list of statements: result = newNimNode(nnkStmtList, n) # iterate over any argument that is passed to this macro: @@ -715,7 +693,7 @@ variable number of arguments: The macro call expands to: -.. code-block:: nimrod +.. code-block:: nim write(stdout, "a[0]") write(stdout, ": ") writeln(stdout, a[0]) @@ -739,7 +717,7 @@ invoked by an expression following a colon. The following example outlines a macro that generates a lexical analyzer from regular expressions: -.. code-block:: nimrod +.. code-block:: nim macro case_token(n: stmt): stmt = # creates a lexical analyzer from regular expressions @@ -772,11 +750,11 @@ To give a footstart to writing macros we will show now how to turn your typical dynamic code into something that compiles statically. For the exercise we will use the following snippet of code as the starting point: -.. code-block:: nimrod +.. code-block:: nim import strutils, tables - proc readCfgAtRuntime(cfgFilename: string): TTable[string, string] = + proc readCfgAtRuntime(cfgFilename: string): Table[string, string] = let inputString = readFile(cfgFilename) var @@ -810,11 +788,11 @@ to be included along the program containing the license information:: licenseKey,M1Tl3PjBWO2CC48m The ``readCfgAtRuntime`` proc will open the given filename and return a -``TTable`` from the `tables module `_. The parsing of the file is +``Table`` from the `tables module `_. The parsing of the file is done (without much care for handling invalid data or corner cases) using the -``splitLines`` proc from the `strutils module `_. There are many -things which can fail; mind the purpose is explaining how to make this run at -compile time, not how to properly implement a DRM scheme. +`splitLines proc from the strutils module `_. There +are many things which can fail; mind the purpose is explaining how to make +this run at compile time, not how to properly implement a DRM scheme. The reimplementation of this code as a compile time proc will allow us to get rid of the ``data.cfg`` file we would need to distribute along the binary, plus @@ -836,7 +814,9 @@ time string with the *generated source code*, which we then pass to the ``parseStmt`` proc from the `macros module `_. Here is the modified source code implementing the macro: -.. code-block:: nimrod +.. code-block:: nim + :number-lines: + import macros, strutils macro readCfgAndBuildSource(cfgFilename: string): stmt = @@ -863,58 +843,61 @@ modified source code implementing the macro: echo cfglicenseKey echo cfgversion -The good news is not much has changed! First, we need to change the handling of -the input parameter. In the dynamic version the ``readCfgAtRuntime`` proc -receives a string parameter. However, in the macro version it is also declared -as string, but this is the *outside* interface of the macro. When the macro is -run, it actually gets a ``PNimrodNode`` object instead of a string, and we have -to call the ``strVal`` proc from the `macros module `_ to obtain -the string being passed in to the macro. +The good news is not much has changed! First, we need to change the handling +of the input parameter (line 3). In the dynamic version the +``readCfgAtRuntime`` proc receives a string parameter. However, in the macro +version it is also declared as string, but this is the *outside* interface of +the macro. When the macro is run, it actually gets a ``PNimNode`` object +instead of a string, and we have to call the `strVal proc +`_ (line 5) from the `macros module `_ to +obtain the string being passed in to the macro. -Second, we cannot use the ``readFile`` proc from the `system module -`_ due to FFI restriction at compile time. If we try to use this -proc, or any other which depends on FFI, the compiler will error with the -message ``cannot evaluate`` and a dump of the macro's source code, along with a -stack trace where the compiler reached before bailing out. We can get around -this limitation by using the ``slurp`` proc from the `system module -`_, which was precisely made for compilation time (just like -``gorge`` which executes an external program and captures its output). +Second, we cannot use the `readFile proc `_ from the +`system module `_ due to FFI restriction at compile time. If we +try to use this proc, or any other which depends on FFI, the compiler will +error with the message ``cannot evaluate`` and a dump of the macro's source +code, along with a stack trace where the compiler reached before bailing out. +We can get around this limitation by using the `slurp proc +`_ from the `system module `_, which was +precisely made for compilation time (just like `gorge `_ +which executes an external program and captures its output). -The interesting thing is that our macro does not return a runtime ``TTable`` -object. Instead, it builds up Nimrod source code into the ``source`` variable. -For each line of the configuration file a ``const`` variable will be generated. -To avoid conflicts we prefix these variables with ``cfg``. In essence, what the -compiler is doing is replacing the line calling the macro with the following -snippet of code: +The interesting thing is that our macro does not return a runtime `Table +`_ object. Instead, it builds up Nim source code into +the ``source`` variable. For each line of the configuration file a ``const`` +variable will be generated (line 15). To avoid conflicts we prefix these +variables with ``cfg``. In essence, what the compiler is doing is replacing +the line calling the macro with the following snippet of code: -.. code-block:: nimrod +.. code-block:: nim const cfgversion= "1.1" const cfglicenseOwner= "Hyori Lee" const cfglicenseKey= "M1Tl3PjBWO2CC48m" You can verify this yourself adding the line ``echo source`` somewhere at the end of the macro and compiling the program. Another difference is that instead -of calling the usual ``quit`` proc to abort (which we could still call) this -version calls the ``error`` proc. The ``error`` proc has the same behavior as -``quit`` but will dump also the source and file line information where the -error happened, making it easier for the programmer to find where compilation -failed. In this situation it would point to the line invoking the macro, but -**not** the line of ``data.cfg`` we are processing, that's something the macro -itself would need to control. +of calling the usual `quit proc `_ to abort (which we could +still call) this version calls the `error proc `_ (line +14). The ``error`` proc has the same behavior as ``quit`` but will dump also +the source and file line information where the error happened, making it +easier for the programmer to find where compilation failed. In this situation +it would point to the line invoking the macro, but **not** the line of +``data.cfg`` we are processing, that's something the macro itself would need +to control. Generating AST by hand ++++++++++++++++++++++ To generate an AST we would need to intimately know the structures used by the -Nimrod compiler exposed in the `macros module `_, which at first -look seems a daunting task. But we can use as helper shortcut the ``dumpTree`` -macro, which is used as a statement macro instead of an expression macro. -Since we know that we want to generate a bunch of ``const`` symbols we can -create the following source file and compile it to see what the compiler -*expects* from us: +Nim compiler exposed in the `macros module `_, which at first +look seems a daunting task. But we can use as helper shortcut the `dumpTree +macro `_, which is used as a statement macro instead of +an expression macro. Since we know that we want to generate a bunch of +``const`` symbols we can create the following source file and compile it to +see what the compiler *expects* from us: -.. code-block:: nimrod +.. code-block:: nim import macros dumpTree: @@ -957,7 +940,9 @@ identifier, optionally a type (can be an *empty* node) and the value. Armed with this knowledge, let's look at the finished version of the AST building macro: -.. code-block:: nimrod +.. code-block:: nim + :number-lines: + import macros, strutils macro readCfgAndBuildAST(cfgFilename: string): stmt = @@ -993,19 +978,20 @@ Since we are building on the previous example generating source code, we will only mention the differences to it. Instead of creating a temporary ``string`` variable and writing into it source code as if it were written *by hand*, we use the ``result`` variable directly and create a statement list node -(``nnkStmtList``) which will hold our children. +(``nnkStmtList``) which will hold our children (line 7). For each input line we have to create a constant definition (``nnkConstDef``) and wrap it inside a constant section (``nnkConstSection``). Once these -variables are created, we fill them hierarchichally like the previous AST dump -tree showed: the constant definition is a child of the section definition, and -the constant definition has an identifier node, an empty node (we let the -compiler figure out the type), and a string literal with the value. +variables are created, we fill them hierarchichally (line 17) like the +previous AST dump tree showed: the constant definition is a child of the +section definition, and the constant definition has an identifier node, an +empty node (we let the compiler figure out the type), and a string literal +with the value. A last tip when writing a macro: if you are not sure the AST you are building looks ok, you may be tempted to use the ``dumpTree`` macro. But you can't use it *inside* the macro you are writting/debugging. Instead ``echo`` the string -generated by ``treeRepr``. If at the end of the this example you add ``echo -treeRepr(result)`` you should get the same output as using the ``dumpTree`` -macro, but of course you can call that at any point of the macro where you -might be having troubles. +generated by `treeRepr `_. If at the end of the this +example you add ``echo treeRepr(result)`` you should get the same output as +using the ``dumpTree`` macro, but of course you can call that at any point of +the macro where you might be having troubles. diff --git a/examples/curlex.nim b/examples/curlex.nim index 1d0f18ddd..9dadd9a90 100644 --- a/examples/curlex.nim +++ b/examples/curlex.nim @@ -3,7 +3,7 @@ import var hCurl = easy_init() if hCurl != nil: - discard easy_setopt(hCurl, OPT_VERBOSE, True) + discard easy_setopt(hCurl, OPT_VERBOSE, true) discard easy_setopt(hCurl, OPT_URL, "http://force7.de/nimrod") discard easy_perform(hCurl) easy_cleanup(hCurl) diff --git a/examples/htmlrefs.nim b/examples/htmlrefs.nim index 8b668325f..5364d61b6 100644 --- a/examples/htmlrefs.nim +++ b/examples/htmlrefs.nim @@ -12,10 +12,10 @@ if paramCount() < 1: quit("Usage: htmlrefs filename[.html]") var links = 0 # count the number of links -var filename = addFileExt(ParamStr(1), "html") +var filename = addFileExt(paramStr(1), "html") var s = newFileStream(filename, fmRead) if s == nil: quit("cannot open the file " & filename) -var x: TXmlParser +var x: XmlParser open(x, s, filename) next(x) # get first event block mainLoop: @@ -43,12 +43,12 @@ block mainLoop: while x.kind == xmlCharData: desc.add(x.charData) x.next() - Echo(desc & ": " & link) + echo(desc & ": " & link) else: x.next() of xmlEof: break # end of file reached of xmlError: - Echo(errorMsg(x)) + echo(errorMsg(x)) x.next() else: x.next() # skip other events diff --git a/examples/htmltitle.nim b/examples/htmltitle.nim index a3280bb13..7e66fabaa 100644 --- a/examples/htmltitle.nim +++ b/examples/htmltitle.nim @@ -10,7 +10,7 @@ if paramCount() < 1: var filename = addFileExt(paramStr(1), "html") var s = newFileStream(filename, fmRead) if s == nil: quit("cannot open the file " & filename) -var x: TXmlParser +var x: XmlParser open(x, s, filename) while true: x.next() diff --git a/examples/httpserver2.nim b/examples/httpserver2.nim index 45350ac89..13fea9e21 100644 --- a/examples/httpserver2.nim +++ b/examples/httpserver2.nim @@ -7,19 +7,19 @@ const type TRequestMethod = enum reqGet, reqPost TServer* = object ## contains the current server state - socket: TSocket + s: Socket job: seq[TJob] TJob* = object - client: TSocket - process: PProcess + client: Socket + process: Process # --------------- output messages -------------------------------------------- -proc sendTextContentType(client: TSocket) = +proc sendTextContentType(client: Socket) = send(client, "Content-type: text/html" & wwwNL) send(client, wwwNL) -proc badRequest(client: TSocket) = +proc badRequest(client: Socket) = # Inform the client that a request it has made has a problem. send(client, "HTTP/1.0 400 BAD REQUEST" & wwwNL) sendTextContentType(client) @@ -27,19 +27,19 @@ proc badRequest(client: TSocket) = "such as a POST without a Content-Length.

      " & wwwNL) -proc cannotExec(client: TSocket) = +proc cannotExec(client: Socket) = send(client, "HTTP/1.0 500 Internal Server Error" & wwwNL) sendTextContentType(client) send(client, "

      Error prohibited CGI execution.

      " & wwwNL) -proc headers(client: TSocket, filename: string) = +proc headers(client: Socket, filename: string) = # XXX could use filename to determine file type send(client, "HTTP/1.0 200 OK" & wwwNL) send(client, ServerSig) sendTextContentType(client) -proc notFound(client: TSocket, path: string) = +proc notFound(client: Socket, path: string) = send(client, "HTTP/1.0 404 NOT FOUND" & wwwNL) send(client, ServerSig) sendTextContentType(client) @@ -50,7 +50,7 @@ proc notFound(client: TSocket, path: string) = send(client, "" & wwwNL) -proc unimplemented(client: TSocket) = +proc unimplemented(client: Socket) = send(client, "HTTP/1.0 501 Method Not Implemented" & wwwNL) send(client, ServerSig) sendTextContentType(client) @@ -62,24 +62,25 @@ proc unimplemented(client: TSocket) = # ----------------- file serving --------------------------------------------- -proc discardHeaders(client: TSocket) = skip(client) +proc discardHeaders(client: Socket) = skip(client) -proc serveFile(client: TSocket, filename: string) = +proc serveFile(client: Socket, filename: string) = discardHeaders(client) - var f: TFile + var f: File if open(f, filename): headers(client, filename) const bufSize = 8000 # != 8K might be good for memory manager var buf = alloc(bufsize) - while True: + while true: var bytesread = readBuffer(f, buf, bufsize) if bytesread > 0: var byteswritten = send(client, buf, bytesread) if bytesread != bytesWritten: + let err = osLastError() dealloc(buf) close(f) - OSError() + raiseOSError(err) if bytesread != bufSize: break dealloc(buf) close(f) @@ -89,7 +90,7 @@ proc serveFile(client: TSocket, filename: string) = # ------------------ CGI execution ------------------------------------------- -proc executeCgi(server: var TServer, client: TSocket, path, query: string, +proc executeCgi(server: var TServer, client: Socket, path, query: string, meth: TRequestMethod) = var env = newStringTable(modeCaseInsensitive) var contentLength = -1 @@ -131,9 +132,10 @@ proc executeCgi(server: var TServer, client: TSocket, path, query: string, if meth == reqPost: # get from client and post to CGI program: var buf = alloc(contentLength) - if recv(client, buf, contentLength) != contentLength: + if recv(client, buf, contentLength) != contentLength: + let err = osLastError() dealloc(buf) - OSError() + raiseOSError(err) var inp = process.inputStream inp.writeData(buf, contentLength) dealloc(buf) @@ -163,7 +165,7 @@ proc animate(server: var TServer) = # --------------- Server Setup ----------------------------------------------- -proc acceptRequest(server: var TServer, client: TSocket) = +proc acceptRequest(server: var TServer, client: Socket) = var cgi = false var query = "" var buf = "" @@ -197,7 +199,7 @@ proc acceptRequest(server: var TServer, client: TSocket) = if path[path.len-1] == '/' or existsDir(path): path = path / "index.html" - if not ExistsFile(path): + if not existsFile(path): discardHeaders(client) notFound(client, path) client.close() @@ -215,26 +217,24 @@ proc acceptRequest(server: var TServer, client: TSocket) = else: executeCgi(server, client, path, query, meth) - - when isMainModule: var port = 80 var server: TServer server.job = @[] - server.socket = socket(AF_INET) - if server.socket == InvalidSocket: OSError() - server.socket.bindAddr(port=TPort(port)) - listen(server.socket) + server.s = socket(AF_INET) + if server.s == invalidSocket: raiseOSError(osLastError()) + server.s.bindAddr(port=Port(port)) + listen(server.s) echo("server up on port " & $port) while true: # check for new new connection & handle it - var list: seq[TSocket] = @[server.socket] + var list: seq[Socket] = @[server.s] if select(list, 10) > 0: - var client: TSocket + var client: Socket new(client) - accept(server.socket, client) + accept(server.s, client) try: acceptRequest(server, client) except: @@ -244,4 +244,4 @@ when isMainModule: animate(server) # some slack for CPU sleep(10) - server.socket.close() + server.s.close() diff --git a/examples/iupex1.nim b/examples/iupex1.nim index bf7ed2f1c..f768fb23f 100644 --- a/examples/iupex1.nim +++ b/examples/iupex1.nim @@ -1,37 +1,37 @@ # Example IUP program -# IupTabs: Creates a IupTabs control. +# iupTabs: Creates a iupTabs control. import iup -discard iup.Open(nil, nil) +discard iup.open(nil, nil) -var vbox1 = Iup.Vbox(Iup.Label("Inside Tab A"), Iup.Button("Button A", ""), nil) -var vbox2 = Iup.Vbox(Iup.Label("Inside Tab B"), Iup.Button("Button B", ""), nil) +var vbox1 = iup.vbox(iup.label("Inside Tab A"), iup.button("Button A", ""), nil) +var vbox2 = iup.vbox(iup.label("Inside Tab B"), iup.button("Button B", ""), nil) -Iup.SetAttribute(vbox1, "TABTITLE", "Tab A") -Iup.SetAttribute(vbox2, "TABTITLE", "Tab B") +iup.setAttribute(vbox1, "TABTITLE", "Tab A") +iup.setAttribute(vbox2, "TABTITLE", "Tab B") -var tabs1 = Iup.Tabs(vbox1, vbox2, nil) +var tabs1 = iup.tabs(vbox1, vbox2, nil) -vbox1 = Iup.Vbox(Iup.Label("Inside Tab C"), Iup.Button("Button C", ""), nil) -vbox2 = Iup.Vbox(Iup.Label("Inside Tab D"), Iup.Button("Button D", ""), nil) +vbox1 = iup.vbox(iup.label("Inside Tab C"), iup.button("Button C", ""), nil) +vbox2 = iup.vbox(iup.label("Inside Tab D"), iup.button("Button D", ""), nil) -Iup.SetAttribute(vbox1, "TABTITLE", "Tab C") -Iup.SetAttribute(vbox2, "TABTITLE", "Tab D") +iup.setAttribute(vbox1, "TABTITLE", "Tab C") +iup.setAttribute(vbox2, "TABTITLE", "Tab D") -var tabs2 = Iup.Tabs(vbox1, vbox2, nil) -Iup.SetAttribute(tabs2, "TABTYPE", "LEFT") +var tabs2 = iup.tabs(vbox1, vbox2, nil) +iup.setAttribute(tabs2, "TABTYPE", "LEFT") -var box = Iup.Hbox(tabs1, tabs2, nil) -Iup.SetAttribute(box, "MARGIN", "10x10") -Iup.SetAttribute(box, "GAP", "10") +var box = iup.hbox(tabs1, tabs2, nil) +iup.setAttribute(box, "MARGIN", "10x10") +iup.setAttribute(box, "GAP", "10") -var dlg = Iup.Dialog(box) -Iup.SetAttribute(dlg, "TITLE", "IupTabs") -Iup.SetAttribute(dlg, "SIZE", "200x100") +var dlg = iup.dialog(box) +iup.setAttribute(dlg, "TITLE", "iupTabs") +iup.setAttribute(dlg, "SIZE", "200x100") -discard ShowXY(dlg, IUP_CENTER, IUP_CENTER) -discard MainLoop() -Close() +discard showXY(dlg, IUP_CENTER, IUP_CENTER) +discard mainLoop() +close() diff --git a/examples/parsecfgex.nim b/examples/parsecfgex.nim index 618ecadd6..0f37a0378 100644 --- a/examples/parsecfgex.nim +++ b/examples/parsecfgex.nim @@ -4,7 +4,7 @@ import var f = newFileStream(paramStr(1), fmRead) if f != nil: - var p: TCfgParser + var p: CfgParser open(p, f, paramStr(1)) while true: var e = next(p) diff --git a/examples/sdlex.nim b/examples/sdlex.nim index 2c1a413e9..3dd474d8e 100644 --- a/examples/sdlex.nim +++ b/examples/sdlex.nim @@ -12,11 +12,11 @@ var if init(INIT_VIDEO) != 0: quit "SDL failed to initialize!" -screen = SetVideoMode(640, 480, 16, SWSURFACE or ANYFORMAT) +screen = setVideoMode(640, 480, 16, SWSURFACE or ANYFORMAT) if screen.isNil: quit($sdl.getError()) -greeting = IMG_load("tux.png") +greeting = imgLoad("tux.png") if greeting.isNil: echo "Failed to load tux.png" else: @@ -36,7 +36,7 @@ block game_loop: of QUITEV: break game_loop of KEYDOWN: - if EvKeyboard(addr event).keysym.sym == K_ESCAPE: + if evKeyboard(addr event).keysym.sym == K_ESCAPE: break game_loop else: discard @@ -47,6 +47,6 @@ block game_loop: greeting.freeSurface() screen.freeSurface() -sdl.Quit() +sdl.quit() ## fowl wuz here 10/2012 \ No newline at end of file diff --git a/examples/statcsv.nim b/examples/statcsv.nim index 48c4ea0cc..cd1de62af 100644 --- a/examples/statcsv.nim +++ b/examples/statcsv.nim @@ -8,14 +8,14 @@ import os, streams, parsecsv, strutils, math if paramCount() < 1: quit("Usage: statcsv filename[.csv]") -var filename = addFileExt(ParamStr(1), "csv") +var filename = addFileExt(paramStr(1), "csv") var s = newFileStream(filename, fmRead) if s == nil: quit("cannot open the file " & filename) var - x: TCsvParser + x: CsvParser header: seq[string] - res: seq[TRunningStat] + res: seq[RunningStat] open(x, s, filename, separator=';', skipInitialSpace = true) while readRow(x): if processedRows(x) == 1: diff --git a/install.sh.template b/install.sh.template new file mode 100644 index 000000000..1292abdab --- /dev/null +++ b/install.sh.template @@ -0,0 +1,9 @@ +#!/bin/sh +set -e +set -x + +if [ "$1" != "" ]; then + exec ./koch install "$1" +else + exec ./koch install +fi diff --git a/install.txt b/install.txt index 4543e4ece..deca55599 100644 --- a/install.txt +++ b/install.txt @@ -9,25 +9,25 @@ should be in your ``$PATH`` (most likely the case). Note that some few Linux distributions do not ship with a GCC compiler preinstalled - then you have to install it. -Install Nimrod by downloading the appropriate ``.zip`` file and extracting it -to a directory of your choice. The Nimrod Compiler will stay in this +Install Nim by downloading the appropriate ``.zip`` file and extracting it +to a directory of your choice. The Nim Compiler will stay in this directory (unless you copy it somewhere else). The compiler does not need -write access to its directory, so copying the nimrod folder to ``/opt`` +write access to its directory, so copying the nim folder to ``/opt`` works. Then run the following command:: sh build.sh -Unlike other software, Nimrod does not distribute its files over the whole file +Unlike other software, Nim does not distribute its files over the whole file hierarchy. This has the advantage that you can deinstall it by just deleting its folder. The disadvantage is that you have to add it to your ``PATH`` manually. An alternative is to create a symbolic link in ``/usr/bin``:: - [sudo] ln -s $your_install_dir/bin/nimrod /usr/bin/nimrod + [sudo] ln -s $your_install_dir/bin/nim /usr/bin/nim There are also ``install.sh`` and ``deinstall.sh`` scripts for distributing -the files over the UNIX hierarchy. However, updating your Nimrod installation +the files over the UNIX hierarchy. However, updating your Nim installation is more cumbersome then. @@ -43,9 +43,9 @@ You need to install Apple's developer's tools for the GNU Compiler Collection. Installation on Windows ----------------------- -Install Nimrod by downloading and running the ``nimrod_$version.exe`` file. +Install Nim by downloading and running the ``nim_$version.exe`` file. As default, the ``GCC`` compiler is used that is bundled with this installer. -You can change the configuration file ``config/nimrod.cfg`` to use +You can change the configuration file ``config/nim.cfg`` to use another C compiler or change the path to GCC. Currently, the following C compilers are supported under Windows: @@ -64,5 +64,5 @@ However, most testing is done with GCC. Bootstrapping from Github ------------------------- -Take a look at the readme file on github `here `_ +Take a look at the readme file on github `here `_ for instructions. diff --git a/koch.nim b/koch.nim index 293ffb148..79228c1a4 100644 --- a/koch.nim +++ b/koch.nim @@ -1,6 +1,6 @@ # # -# Maintenance program for Nimrod +# Maintenance program for Nim # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -18,6 +18,8 @@ when defined(gcc) and defined(windows): import os, strutils, parseopt, osproc, streams +import "compiler/nversion.nim" + when defined(withUpdate): import httpclient when defined(haveZipLib): @@ -26,7 +28,7 @@ when defined(haveZipLib): const HelpText = """ +-----------------------------------------------------------------+ -| Maintenance program for Nimrod | +| Maintenance program for Nim | | Version $1| | (c) 2014 Andreas Rumpf | +-----------------------------------------------------------------+ @@ -38,16 +40,19 @@ Options: --help, -h shows this help and quits Possible Commands: boot [options] bootstraps with given command line options - install [dir] installs to given directory + install [bindir] installs to given directory clean cleans Nimrod project; removes generated files - web generates the website + web [options] generates the website and the full documentation + website [options] generates only the website csource [options] builds the C sources for installation + pdf builds the PDF documentation zip builds the installation ZIP package nsis [options] builds the NSIS Setup installer (for Windows) tests [options] run the testsuite - update updates nimrod to the latest version from github + update updates nim to the latest version from github (compile koch with -d:withUpdate to enable) temp options creates a temporary compiler for testing + winrelease creates a release (for coredevs only) Boot options: -d:release produce a release version of the compiler -d:tinyc include the Tiny C backend (not supported on Windows) @@ -61,13 +66,13 @@ Boot options: proc exe(f: string): string = return addFileExt(f, ExeExt) proc findNim(): string = - var nimrod = "nimrod".exe - result = "bin" / nimrod + var nim = "nim".exe + result = "bin" / nim if existsFile(result): return for dir in split(getEnv("PATH"), PathSep): - if existsFile(dir / nimrod): return dir / nimrod + if existsFile(dir / nim): return dir / nim # assume there is a symlink to the exe or something: - return nimrod + return nim proc exec(cmd: string) = echo(cmd) @@ -89,14 +94,14 @@ const compileNimInst = "-d:useLibzipSrc tools/niminst/niminst" proc csource(args: string) = - exec("$4 cc $1 -r $3 --var:version=$2 csource compiler/nimrod.ini $1" % - [args, NimrodVersion, compileNimInst, findNim()]) + exec("$4 cc $1 -r $3 --var:version=$2 --var:mingw=mingw32 csource compiler/nim.ini $1" % + [args, VersionAsString, compileNimInst, findNim()]) proc zip(args: string) = - exec("$3 cc -r $2 --var:version=$1 --var:mingw=mingw32 scripts compiler/nimrod.ini" % - [NimrodVersion, compileNimInst, findNim()]) - exec("$# --var:version=$# --var:mingw=mingw32 zip compiler/nimrod.ini" % - ["tools/niminst/niminst".exe, NimrodVersion]) + exec("$3 cc -r $2 --var:version=$1 --var:mingw=mingw32 scripts compiler/nim.ini" % + [VersionAsString, compileNimInst, findNim()]) + exec("$# --var:version=$# --var:mingw=mingw32 zip compiler/nim.ini" % + ["tools/niminst/niminst".exe, VersionAsString]) proc buildTool(toolname, args: string) = exec("$# cc $# $#" % [findNim(), args, toolname]) @@ -107,58 +112,76 @@ proc nsis(args: string) = buildTool("tools/niminst/niminst", args) buildTool("tools/nimgrep", args) # produce 'nimrod_debug.exe': - exec "nimrod c compiler" / "nimrod.nim" - copyExe("compiler/nimrod".exe, "bin/nimrod_debug".exe) + exec "nim c compiler" / "nim.nim" + copyExe("compiler/nim".exe, "bin/nim_debug".exe) exec(("tools" / "niminst" / "niminst --var:version=$# --var:mingw=mingw32" & - " nsis compiler/nimrod") % NimrodVersion) + " nsis compiler/nim") % VersionAsString) proc install(args: string) = - exec("$# cc -r $# --var:version=$# --var:mingw=mingw32 scripts compiler/nimrod.ini" % - [findNim(), compileNimInst, NimrodVersion]) + exec("$# cc -r $# --var:version=$# --var:mingw=mingw32 scripts compiler/nim.ini" % + [findNim(), compileNimInst, VersionAsString]) exec("sh ./install.sh $#" % args) proc web(args: string) = - exec("$# cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" % - [findNim(), NimrodVersion]) + exec("$# cc -r tools/nimweb.nim $# web/nim --putenv:nimversion=$#" % + [findNim(), args, VersionAsString]) + +proc website(args: string) = + exec("$# cc -r tools/nimweb.nim $# --website web/nim --putenv:nimversion=$#" % + [findNim(), args, VersionAsString]) + +proc pdf(args="") = + exec("$# cc -r tools/nimweb.nim $# --pdf web/nim --putenv:nimversion=$#" % + [findNim(), args, VersionAsString]) # -------------- boot --------------------------------------------------------- const bootOptions = "" # options to pass to the bootstrap process -proc findStartNimrod: string = +proc findStartNim: string = # we try several things before giving up: + # * bin/nim + # * $PATH/nim # * bin/nimrod # * $PATH/nimrod - # If these fail, we try to build nimrod with the "build.(sh|bat)" script. + # If these fail, we try to build nim with the "build.(sh|bat)" script. + var nim = "nim".exe + result = "bin" / nim + if existsFile(result): return + for dir in split(getEnv("PATH"), PathSep): + if existsFile(dir / nim): return dir / nim + + # try the old "nimrod.exe": var nimrod = "nimrod".exe result = "bin" / nimrod if existsFile(result): return for dir in split(getEnv("PATH"), PathSep): - if existsFile(dir / nimrod): return dir / nimrod + if existsFile(dir / nim): return dir / nimrod + when defined(Posix): const buildScript = "build.sh" if existsFile(buildScript): - if tryExec("./" & buildScript): return "bin" / nimrod + if tryExec("./" & buildScript): return "bin" / nim else: const buildScript = "build.bat" if existsFile(buildScript): - if tryExec(buildScript): return "bin" / nimrod - - echo("Found no nimrod compiler and every attempt to build one failed!") + if tryExec(buildScript): return "bin" / nim + + echo("Found no nim compiler and every attempt to build one failed!") quit("FAILURE") proc thVersion(i: int): string = - result = ("compiler" / "nimrod" & $i).exe + result = ("compiler" / "nim" & $i).exe proc boot(args: string) = - var output = "compiler" / "nimrod".exe - var finalDest = "bin" / "nimrod".exe + var output = "compiler" / "nim".exe + var finalDest = "bin" / "nim".exe - copyExe(findStartNimrod(), 0.thVersion) + copyExe(findStartNim(), 0.thVersion) for i in 0..2: echo "iteration: ", i+1 - exec i.thVersion & " c $# $# compiler" / "nimrod.nim" % [bootOptions, args] + exec i.thVersion & " c $# $# compiler" / "nim.nim" % [bootOptions, args] if sameFileContent(output, i.thVersion): copyExe(output, finalDest) echo "executables are equal: SUCCESS!" @@ -176,7 +199,7 @@ const ".idx", ".ilk" ] ignore = [ - ".bzrignore", "nimrod", "nimrod.exe", "koch", "koch.exe", ".gitignore" + ".bzrignore", "nim", "nim.exe", "koch", "koch.exe", ".gitignore" ] proc cleanAux(dir: string) = @@ -273,6 +296,33 @@ when defined(withUpdate): boot(args) echo("Update complete!") +# -------------- builds a release --------------------------------------------- + +proc run7z(platform: string, patterns: varargs[string]) = + const tmpDir = "nim-" & VersionAsString + createDir tmpDir + try: + for pattern in patterns: + for f in walkFiles(pattern): + if "nimcache" notin f: + copyFile(f, tmpDir / f) + exec("7z a -tzip $1-$2.zip $1" % [tmpDir, platform]) + finally: + removeDir tmpDir + +proc winRelease() = + boot(" -d:release") + #buildTool("tools/niminst/niminst", " -d:release") + buildTool("tools/nimgrep", " -d:release") + buildTool("compiler/nimfix/nimfix", " -d:release") + + run7z("win32", "bin/nim.exe", "bin/c2nim.exe", "bin/nimgrep.exe", + "bin/nimfix.exe", + "bin/babel.exe", "bin/*.dll", + "config", "dist/*.dll", "examples", "lib", + "readme.txt", "contributors.txt", "copying.txt") + # second step: XXX build 64 bit version + # -------------- tests -------------------------------------------------------- template `|`(a, b): expr = (if a.len > 0: a else: b) @@ -280,42 +330,44 @@ template `|`(a, b): expr = (if a.len > 0: a else: b) proc tests(args: string) = # we compile the tester with taintMode:on to have a basic # taint mode test :-) - exec "nimrod cc --taintMode:on tests/testament/tester" + exec "nim cc --taintMode:on tests/testament/tester" let tester = quoteShell(getCurrentDir() / "tests/testament/tester".exe) exec tester & " " & (args|"all") exec tester & " html" proc temp(args: string) = - var output = "compiler" / "nimrod".exe - var finalDest = "bin" / "nimrod_temp".exe - exec("nimrod c compiler" / "nimrod") + var output = "compiler" / "nim".exe + var finalDest = "bin" / "nim_temp".exe + exec("nim c compiler" / "nim") copyExe(output, finalDest) if args.len > 0: exec(finalDest & " " & args) proc showHelp() = - quit(HelpText % [NimrodVersion & repeatChar(44-len(NimrodVersion)), - CompileDate, CompileTime]) + quit(HelpText % [VersionAsString & repeatChar(44-len(VersionAsString)), + CompileDate, CompileTime], QuitSuccess) var op = initOptParser() op.next() case op.kind of cmdLongOption, cmdShortOption: showHelp() of cmdArgument: - case normalize(op.key) + case normalize(op.key) of "boot": boot(op.cmdLineRest) of "clean": clean(op.cmdLineRest) of "web": web(op.cmdLineRest) + of "website": website(op.cmdLineRest) + of "pdf": pdf() of "csource", "csources": csource(op.cmdLineRest) of "zip": zip(op.cmdLineRest) of "nsis": nsis(op.cmdLineRest) of "install": install(op.cmdLineRest) of "test", "tests": tests(op.cmdLineRest) - of "update": + of "update": when defined(withUpdate): update(op.cmdLineRest) else: quit "this Koch has not been compiled with -d:withUpdate" of "temp": temp(op.cmdLineRest) + of "winrelease": winRelease() else: showHelp() of cmdEnd: showHelp() - diff --git a/lib/core/locks.nim b/lib/core/locks.nim index 894965a85..766b7b536 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -1,54 +1,32 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## This module contains Nimrod's support for locks and condition vars. -## If the symbol ``preventDeadlocks`` is defined -## (compiled with ``-d:preventDeadlocks``) special logic is added to -## every ``acquire``, ``tryAcquire`` and ``release`` action that ensures at -## runtime that no deadlock can occur. This is achieved by forcing a thread -## to release its locks should it be part of a deadlock. This thread then -## re-acquires its locks and proceeds. +## This module contains Nim's support for locks and condition vars. include "system/syslocks" type - TLock* = TSysLock ## Nimrod lock; whether this is re-entrant - ## or not is unspecified! However, compilation - ## in preventDeadlocks-mode guarantees re-entrancy. - TCond* = TSysCond ## Nimrod condition variable + TLock* = TSysLock ## Nim lock; whether this is re-entrant + ## or not is unspecified! + TCond* = TSysCond ## Nim condition variable - FLock* = object of TEffect ## effect that denotes that some lock operation - ## is performed - FAquireLock* = object of FLock ## effect that denotes that some lock is - ## aquired - FReleaseLock* = object of FLock ## effect that denotes that some lock is - ## released - -const - noDeadlocks = defined(preventDeadlocks) - maxLocksPerThread* = 10 ## max number of locks a thread can hold - ## at the same time; this limit is only relevant - ## when compiled with ``-d:preventDeadlocks``. - -var - deadlocksPrevented*: int ## counts the number of times a - ## deadlock has been prevented - -when noDeadlocks: - var - locksLen {.threadvar.}: int - locks {.threadvar.}: array [0..MaxLocksPerThread-1, pointer] - - proc orderedLocks(): bool = - for i in 0 .. locksLen-2: - if locks[i] >= locks[i+1]: return false - result = true + 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 + ## aquired. 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.} = ## Initializes the given lock. @@ -58,93 +36,16 @@ proc deinitLock*(lock: var TLock) {.inline.} = ## Frees the resources associated with the lock. deinitSys(lock) -proc tryAcquire*(lock: var TLock): bool {.tags: [FAquireLock].} = +proc tryAcquire*(lock: var TLock): bool = ## Tries to acquire the given lock. Returns `true` on success. result = tryAcquireSys(lock) - when noDeadlocks: - if not result: return - # we have to add it to the ordered list. Oh, and we might fail if - # there is no space in the array left ... - if locksLen >= len(locks): - releaseSys(lock) - raise newException(EResourceExhausted, "cannot acquire additional lock") - # find the position to add: - var p = addr(lock) - var L = locksLen-1 - var i = 0 - while i <= L: - assert locks[i] != nil - if locks[i] < p: inc(i) # in correct order - elif locks[i] == p: return # thread already holds lock - else: - # do the crazy stuff here: - while L >= i: - locks[L+1] = locks[L] - dec L - locks[i] = p - inc(locksLen) - assert orderedLocks() - return - # simply add to the end: - locks[locksLen] = p - inc(locksLen) - assert orderedLocks() -proc acquire*(lock: var TLock) {.tags: [FAquireLock].} = +proc acquire*(lock: var TLock) = ## Acquires the given lock. - when nodeadlocks: - var p = addr(lock) - var L = locksLen-1 - var i = 0 - while i <= L: - assert locks[i] != nil - if locks[i] < p: inc(i) # in correct order - elif locks[i] == p: return # thread already holds lock - else: - # do the crazy stuff here: - if locksLen >= len(locks): - raise newException(EResourceExhausted, - "cannot acquire additional lock") - while L >= i: - releaseSys(cast[ptr TSysLock](locks[L])[]) - locks[L+1] = locks[L] - dec L - # acquire the current lock: - acquireSys(lock) - locks[i] = p - inc(locksLen) - # acquire old locks in proper order again: - L = locksLen-1 - inc i - while i <= L: - acquireSys(cast[ptr TSysLock](locks[i])[]) - inc(i) - # DANGER: We can only modify this global var if we gained every lock! - # NO! We need an atomic increment. Crap. - discard system.atomicInc(deadlocksPrevented, 1) - assert orderedLocks() - return - - # simply add to the end: - if locksLen >= len(locks): - raise newException(EResourceExhausted, "cannot acquire additional lock") - acquireSys(lock) - locks[locksLen] = p - inc(locksLen) - assert orderedLocks() - else: - acquireSys(lock) + acquireSys(lock) -proc release*(lock: var TLock) {.tags: [FReleaseLock].} = +proc release*(lock: var TLock) = ## Releases the given lock. - when nodeadlocks: - var p = addr(lock) - var L = locksLen - for i in countdown(L-1, 0): - if locks[i] == p: - for j in i..L-2: locks[j] = locks[j+1] - dec locksLen - break releaseSys(lock) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 455f99c9e..353254d49 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -46,7 +46,7 @@ type nnkForStmt, nnkParForStmt, nnkWhileStmt, nnkCaseStmt, nnkTypeSection, nnkVarSection, nnkLetSection, nnkConstSection, nnkConstDef, nnkTypeDef, - nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt, + nnkYieldStmt, nnkDefer, nnkTryStmt, nnkFinally, nnkRaiseStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt, nnkDiscardStmt, nnkStmtList, nnkImportStmt, @@ -97,7 +97,7 @@ type TNimSymKinds* = set[TNimrodSymKind] type - TNimrodIdent* = object of TObject + TNimrodIdent* = object of RootObj ## represents a Nimrod identifier in the AST TNimrodSymbol {.final.} = object # hidden @@ -249,19 +249,35 @@ proc lineinfo*(n: PNimrodNode): string {.magic: "NLineInfo", noSideEffect.} ## returns the position the node appears in the original source file ## in the form filename(line, col) -proc parseExpr*(s: string): PNimrodNode {.magic: "ParseExprToAst", noSideEffect.} - ## Compiles the passed string to its AST representation. - ## Expects a single expression. +proc internalParseExpr(s: string): PNimrodNode {. + magic: "ParseExprToAst", noSideEffect.} -proc parseStmt*(s: string): PNimrodNode {.magic: "ParseStmtToAst", noSideEffect.} +proc internalParseStmt(s: string): PNimrodNode {. + magic: "ParseStmtToAst", noSideEffect.} + +proc internalErrorFlag*(): string {.magic: "NError", noSideEffect.} + ## Some builtins set an error flag. This is then turned into a proper + ## exception. **Note**: Ordinary application code should not call this. + +proc parseExpr*(s: string): PNimrodNode {.noSideEffect, compileTime.} = ## Compiles the passed string to its AST representation. - ## Expects one or more statements. + ## Expects a single expression. Raises ``ValueError`` for parsing errors. + result = internalParseExpr(s) + let x = internalErrorFlag() + if x.len > 0: raise newException(ValueError, x) + +proc parseStmt*(s: string): PNimrodNode {.noSideEffect, compileTime.} = + ## Compiles the passed string to its AST representation. + ## Expects one or more statements. Raises ``ValueError`` for parsing errors. + result = internalParseStmt(s) + let x = internalErrorFlag() + if x.len > 0: raise newException(ValueError, x) proc getAst*(macroOrTemplate: expr): PNimrodNode {.magic: "ExpandToAst", noSideEffect.} ## Obtains the AST nodes returned from a macro or template invocation. ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## macro FooMacro() = ## var ast = getAst(BarTemplate()) @@ -278,7 +294,7 @@ proc quote*(bl: stmt, op = "``"): PNimrodNode {.magic: "QuoteAst", noSideEffect. ## ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## macro check(ex: expr): stmt = ## # this is a simplified version of the check macro from the @@ -422,7 +438,7 @@ proc lispRepr*(n: PNimrodNode): string {.compileTime.} = add(result, ")") macro dumpTree*(s: stmt): stmt {.immediate.} = echo s.treeRepr - ## Accepts a block of nimrod code and prints the parsed abstract syntax + ## Accepts a block of nim code and prints the parsed abstract syntax ## tree using the `toTree` function. Printing is done *at compile time*. ## ## You can use this as a tool to explore the Nimrod's abstract syntax @@ -430,7 +446,7 @@ macro dumpTree*(s: stmt): stmt {.immediate.} = echo s.treeRepr ## a certain expression/statement. macro dumpLisp*(s: stmt): stmt {.immediate.} = echo s.lispRepr - ## Accepts a block of nimrod code and prints the parsed abstract syntax + ## Accepts a block of nim code and prints the parsed abstract syntax ## tree using the `toLisp` function. Printing is done *at compile time*. ## ## See `dumpTree`. @@ -489,7 +505,7 @@ proc newIdentDefs*(name, kind: PNimrodNode; ## ``let`` or ``var`` blocks may have an empty ``kind`` node if the ## identifier is being assigned a value. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## var varSection = newNimNode(nnkVarSection).add( ## newIdentDefs(ident("a"), ident("string")), @@ -501,7 +517,7 @@ proc newIdentDefs*(name, kind: PNimrodNode; ## If you need to create multiple identifiers you need to use the lower level ## ``newNimNode``: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## result = newNimNode(nnkIdentDefs).add( ## ident("a"), ident("b"), ident("c"), ident("string"), @@ -549,7 +565,7 @@ proc newIfStmt*(branches: varargs[tuple[cond, body: PNimrodNode]]): PNimrodNode {.compiletime.} = ## Constructor for ``if`` statements. ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## newIfStmt( ## (Ident, StmtList), @@ -596,7 +612,7 @@ proc `pragma=`*(someProc: PNimrodNode; val: PNimrodNode){.compileTime.}= someProc[4] = val -template badnodekind(k; f): stmt{.immediate.} = +template badNodeKind(k; f): stmt{.immediate.} = assert false, "Invalid node kind $# for macros.`$2`".format(k, f) proc body*(someProc: PNimrodNode): PNimrodNode {.compileTime.} = @@ -608,7 +624,7 @@ proc body*(someProc: PNimrodNode): PNimrodNode {.compileTime.} = of nnkForStmt: return someProc.last else: - badNodeKind someproc.kind, "body" + badNodeKind someProc.kind, "body" proc `body=`*(someProc: PNimrodNode, val: PNimrodNode) {.compileTime.} = case someProc.kind @@ -633,6 +649,8 @@ proc `$`*(node: PNimrodNode): string {.compileTime.} = result = $node.basename.ident & "*" of nnkStrLit..nnkTripleStrLit: result = node.strVal + of nnkSym: + result = $node.symbol else: badNodeKind node.kind, "$" @@ -647,7 +665,7 @@ template findChild*(n: PNimrodNode; cond: expr): PNimrodNode {. immediate, dirty.} = ## Find the first child node matching condition (or nil). ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## var res = findChild(n, it.kind == nnkPostfix and ## it.basename.ident == !"foo") block: @@ -742,11 +760,11 @@ proc addIdentIfAbsent*(dest: PNimrodNode, ident: string) {.compiletime.} = when not defined(booting): template emit*(e: static[string]): stmt = - ## accepts a single string argument and treats it as nimrod code + ## accepts a single string argument and treats it as nim code ## that should be inserted verbatim in the program ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## emit("echo " & '"' & "hello world".toUpper & '"') ## macro payload: stmt {.gensym.} = diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 8df1b3dfb..0046924a1 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -1,18 +1,23 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Dominik Picheta, Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## This module implements an interface to Nimrod's runtime type information. +## 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 ## details from its clients, it remains inherently unsafe! +## +## See the `marshal `_ module for what this module allows you +## to do. {.push hints: off.} +include "system/inclrtl.nim" include "system/hti.nim" {.pop.} @@ -50,7 +55,7 @@ type akUInt32 = 43, ## any represents an unsigned int32 akUInt64 = 44, ## any represents an unsigned int64 - TAny* = object {.pure.} ## can represent any nimrod value; NOTE: the wrapped + TAny* = 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 ## wrapped value and **must not** live longer than @@ -61,27 +66,27 @@ type ppointer = ptr pointer pbyteArray = ptr array[0.. 0xffff, int8] - TGenSeq {.pure.} = object + TGenSeq = object len, space: int PGenSeq = ptr TGenSeq const GenericSeqSize = (2 * sizeof(int)) -proc genericAssign(dest, src: Pointer, mt: PNimType) {.importCompilerProc.} -proc genericShallowAssign(dest, src: Pointer, mt: PNimType) {. +proc genericAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc.} +proc genericShallowAssign(dest, src: pointer, mt: PNimType) {. importCompilerProc.} proc incrSeq(seq: PGenSeq, elemSize: int): PGenSeq {.importCompilerProc.} proc newObj(typ: PNimType, size: int): pointer {.importCompilerProc.} proc newSeq(typ: PNimType, len: int): pointer {.importCompilerProc.} -proc objectInit(dest: Pointer, typ: PNimType) {.importCompilerProc.} +proc objectInit(dest: pointer, typ: PNimType) {.importCompilerProc.} -template `+!!`(a, b: expr): expr = cast[pointer](cast[TAddress](a) + b) +template `+!!`(a, b: expr): expr = cast[pointer](cast[ByteAddress](a) + b) -proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int = +proc getDiscriminant(aa: pointer, n: ptr TNimNode): int = assert(n.kind == nkCase) var d: int - var a = cast[TAddress](aa) + var a = cast[ByteAddress](aa) case n.typ.size of 1: d = ze(cast[ptr int8](a +% n.offset)[]) of 2: d = ze(cast[ptr int16](a +% n.offset)[]) @@ -89,7 +94,7 @@ proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int = else: assert(false) return d -proc selectBranch(aa: Pointer, n: ptr TNimNode): ptr TNimNode = +proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode = var discr = getDiscriminant(aa, n) if discr <% n.len: result = n.sons[discr] @@ -174,14 +179,14 @@ proc `[]`*(x: TAny, i: int): TAny = of tyArray: var bs = x.rawType.base.size if i >=% x.rawType.size div bs: - raise newException(EInvalidIndex, "index out of bounds") + raise newException(IndexError, "index out of bounds") return newAny(x.value +!! i*bs, x.rawType.base) of tySequence: var s = cast[ppointer](x.value)[] - if s == nil: raise newException(EInvalidValue, "sequence is nil") + if s == nil: raise newException(ValueError, "sequence is nil") var bs = x.rawType.base.size if i >=% cast[PGenSeq](s).len: - raise newException(EInvalidIndex, "index out of bounds") + raise newException(IndexError, "index out of bounds") return newAny(s +!! (GenericSeqSize+i*bs), x.rawType.base) else: assert false @@ -191,15 +196,15 @@ proc `[]=`*(x: TAny, i: int, y: TAny) = of tyArray: var bs = x.rawType.base.size if i >=% x.rawType.size div bs: - raise newException(EInvalidIndex, "index out of bounds") + raise newException(IndexError, "index out of bounds") assert y.rawType == x.rawType.base genericAssign(x.value +!! i*bs, y.value, y.rawType) of tySequence: var s = cast[ppointer](x.value)[] - if s == nil: raise newException(EInvalidValue, "sequence is nil") + if s == nil: raise newException(ValueError, "sequence is nil") var bs = x.rawType.base.size if i >=% cast[PGenSeq](s).len: - raise newException(EInvalidIndex, "index out of bounds") + raise newException(IndexError, "index out of bounds") assert y.rawType == x.rawType.base genericAssign(s +!! (GenericSeqSize+i*bs), y.value, y.rawType) else: assert false @@ -264,9 +269,14 @@ 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 n = t.node var ret: seq[tuple[name: cstring, any: TAny]] = @[] - fieldsAux(p, n, ret) + if t.kind == tyObject: + while true: + fieldsAux(p, t.node, ret) + t = t.base + if t.isNil: break + else: + fieldsAux(p, t.node, ret) for name, any in items(ret): yield ($name, any) @@ -276,7 +286,7 @@ proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} = else: result = c var i = 0 var j = 0 - while True: + while true: while a[i] == '_': inc(i) while b[j] == '_': inc(j) # BUGFIX: typo var aa = toLower(a[i]) @@ -311,12 +321,12 @@ proc `[]=`*(x: TAny, fieldName: string, value: TAny) = when false: if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[] assert x.rawType.kind in {tyTuple, tyObject} - var n = getFieldNode(x.value, t.node, fieldname) + var n = getFieldNode(x.value, t.node, fieldName) if n != nil: assert n.typ == value.rawType genericAssign(x.value +!! n.offset, value.value, value.rawType) else: - raise newException(EInvalidValue, "invalid field name: " & fieldName) + raise newException(ValueError, "invalid field name: " & fieldName) proc `[]`*(x: TAny, fieldName: string): TAny = ## gets a field of `x`; `x` represents an object or a tuple. @@ -325,80 +335,80 @@ proc `[]`*(x: TAny, fieldName: string): TAny = when false: if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[] assert x.rawType.kind in {tyTuple, tyObject} - var n = getFieldNode(x.value, t.node, fieldname) + var n = getFieldNode(x.value, t.node, fieldName) if n != nil: result.value = x.value +!! n.offset result.rawType = n.typ else: - raise newException(EInvalidValue, "invalid field name: " & fieldName) + raise newException(ValueError, "invalid field name: " & fieldName) proc `[]`*(x: TAny): TAny = ## dereference operation for the any `x` that represents a ptr or a ref. - assert x.rawtype.kind in {tyRef, tyPtr} + assert x.rawType.kind in {tyRef, tyPtr} result.value = cast[ppointer](x.value)[] result.rawType = x.rawType.base proc `[]=`*(x, y: TAny) = ## dereference operation for the any `x` that represents a ptr or a ref. - assert x.rawtype.kind in {tyRef, tyPtr} + 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 = ## retrieve the int value out of `x`. `x` needs to represent an int. - assert skipRange(x.rawtype).kind == tyInt + assert skipRange(x.rawType).kind == tyInt result = cast[ptr int](x.value)[] proc getInt8*(x: TAny): int8 = ## retrieve the int8 value out of `x`. `x` needs to represent an int8. - assert skipRange(x.rawtype).kind == tyInt8 + assert skipRange(x.rawType).kind == tyInt8 result = cast[ptr int8](x.value)[] proc getInt16*(x: TAny): int16 = ## retrieve the int16 value out of `x`. `x` needs to represent an int16. - assert skipRange(x.rawtype).kind == tyInt16 + assert skipRange(x.rawType).kind == tyInt16 result = cast[ptr int16](x.value)[] proc getInt32*(x: TAny): int32 = ## retrieve the int32 value out of `x`. `x` needs to represent an int32. - assert skipRange(x.rawtype).kind == tyInt32 + assert skipRange(x.rawType).kind == tyInt32 result = cast[ptr int32](x.value)[] proc getInt64*(x: TAny): int64 = ## retrieve the int64 value out of `x`. `x` needs to represent an int64. - assert skipRange(x.rawtype).kind == tyInt64 + assert skipRange(x.rawType).kind == tyInt64 result = cast[ptr int64](x.value)[] -proc getBiggestInt*(x: TAny): biggestInt = +proc getBiggestInt*(x: TAny): 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``. - var t = skipRange(x.rawtype) + ## The value might be sign-extended to ``BiggestInt``. + var t = skipRange(x.rawType) case t.kind - of tyInt: result = biggestInt(cast[ptr int](x.value)[]) - of tyInt8: result = biggestInt(cast[ptr int8](x.value)[]) - of tyInt16: result = biggestInt(cast[ptr int16](x.value)[]) - of tyInt32: result = biggestInt(cast[ptr int32](x.value)[]) - of tyInt64, tyUInt64: result = biggestInt(cast[ptr int64](x.value)[]) - of tyBool: result = biggestInt(cast[ptr bool](x.value)[]) - of tyChar: result = biggestInt(cast[ptr char](x.value)[]) + of tyInt: result = BiggestInt(cast[ptr int](x.value)[]) + of tyInt8: result = BiggestInt(cast[ptr int8](x.value)[]) + of tyInt16: result = BiggestInt(cast[ptr int16](x.value)[]) + of tyInt32: result = BiggestInt(cast[ptr int32](x.value)[]) + of tyInt64, tyUInt64: result = BiggestInt(cast[ptr int64](x.value)[]) + of tyBool: result = BiggestInt(cast[ptr bool](x.value)[]) + of tyChar: result = BiggestInt(cast[ptr char](x.value)[]) of tyEnum, tySet: case t.size of 1: result = ze64(cast[ptr int8](x.value)[]) of 2: result = ze64(cast[ptr int16](x.value)[]) - of 4: result = biggestInt(cast[ptr int32](x.value)[]) - of 8: result = biggestInt(cast[ptr int64](x.value)[]) + of 4: result = BiggestInt(cast[ptr int32](x.value)[]) + of 8: result = BiggestInt(cast[ptr int64](x.value)[]) else: assert false - of tyUInt: result = biggestInt(cast[ptr uint](x.value)[]) - of tyUInt8: result = biggestInt(cast[ptr uint8](x.value)[]) - of tyUInt16: result = biggestInt(cast[ptr uint16](x.value)[]) - of tyUInt32: result = biggestInt(cast[ptr uint32](x.value)[]) + of tyUInt: result = BiggestInt(cast[ptr uint](x.value)[]) + of tyUInt8: result = BiggestInt(cast[ptr uint8](x.value)[]) + of tyUInt16: result = BiggestInt(cast[ptr uint16](x.value)[]) + of tyUInt32: result = BiggestInt(cast[ptr uint32](x.value)[]) else: assert false -proc setBiggestInt*(x: TAny, y: biggestInt) = +proc setBiggestInt*(x: TAny, 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) + var t = skipRange(x.rawType) case t.kind of tyInt: cast[ptr int](x.value)[] = int(y) of tyInt8: cast[ptr int8](x.value)[] = int8(y) @@ -422,37 +432,37 @@ proc setBiggestInt*(x: TAny, y: biggestInt) = proc getUInt*(x: TAny): uint = ## retrieve the uint value out of `x`, `x` needs to represent an uint. - assert skipRange(x.rawtype).kind == tyUInt + assert skipRange(x.rawType).kind == tyUInt result = cast[ptr uint](x.value)[] proc getUInt8*(x: TAny): uint8 = ## retrieve the uint8 value out of `x`, `x` needs to represent an ## uint8. - assert skipRange(x.rawtype).kind == tyUInt8 + assert skipRange(x.rawType).kind == tyUInt8 result = cast[ptr uint8](x.value)[] proc getUInt16*(x: TAny): uint16 = ## retrieve the uint16 value out of `x`, `x` needs to represent an ## uint16. - assert skipRange(x.rawtype).kind == tyUInt16 + assert skipRange(x.rawType).kind == tyUInt16 result = cast[ptr uint16](x.value)[] proc getUInt32*(x: TAny): uint32 = ## retrieve the uint32 value out of `x`, `x` needs to represent an ## uint32. - assert skipRange(x.rawtype).kind == tyUInt32 + assert skipRange(x.rawType).kind == tyUInt32 result = cast[ptr uint32](x.value)[] proc getUInt64*(x: TAny): uint64 = ## retrieve the uint64 value out of `x`, `x` needs to represent an ## uint64. - assert skipRange(x.rawtype).kind == tyUInt64 + assert skipRange(x.rawType).kind == tyUInt64 result = cast[ptr uint64](x.value)[] proc getBiggestUint*(x: TAny): uint64 = ## retrieve the unsigned integer value out of `x`. `x` needs to ## represent an unsigned integer. - var t = skipRange(x.rawtype) + var t = skipRange(x.rawType) case t.kind of tyUInt: result = uint64(cast[ptr uint](x.value)[]) of tyUInt8: result = uint64(cast[ptr uint8](x.value)[]) @@ -464,7 +474,7 @@ proc getBiggestUint*(x: TAny): uint64 = proc setBiggestUint*(x: TAny; y: uint64) = ## sets the unsigned integer value of `c`. `c` needs to represent an ## unsigned integer. - var t = skipRange(x.rawtype) + var t = skipRange(x.rawType) case t.kind: of tyUInt: cast[ptr uint](x.value)[] = uint(y) of tyUInt8: cast[ptr uint8](x.value)[] = uint8(y) @@ -475,13 +485,13 @@ proc setBiggestUint*(x: TAny; y: uint64) = proc getChar*(x: TAny): char = ## retrieve the char value out of `x`. `x` needs to represent a char. - var t = skipRange(x.rawtype) + var t = skipRange(x.rawType) assert t.kind == tyChar result = cast[ptr char](x.value)[] proc getBool*(x: TAny): bool = ## retrieve the bool value out of `x`. `x` needs to represent a bool. - var t = skipRange(x.rawtype) + var t = skipRange(x.rawType) assert t.kind == tyBool result = cast[ptr bool](x.value)[] @@ -495,7 +505,7 @@ proc getEnumOrdinal*(x: TAny, 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. - var typ = skipRange(x.rawtype) + var typ = skipRange(x.rawType) assert typ.kind == tyEnum var n = typ.node var s = n.sons @@ -511,7 +521,7 @@ proc getEnumField*(x: TAny, 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. - var typ = skipRange(x.rawtype) + var typ = skipRange(x.rawType) assert typ.kind == tyEnum var e = ordinalValue if ntfEnumHole notin typ.flags: @@ -531,51 +541,51 @@ proc getEnumField*(x: TAny): string = proc getFloat*(x: TAny): float = ## retrieve the float value out of `x`. `x` needs to represent an float. - assert skipRange(x.rawtype).kind == tyFloat + assert skipRange(x.rawType).kind == tyFloat result = cast[ptr float](x.value)[] proc getFloat32*(x: TAny): float32 = ## retrieve the float32 value out of `x`. `x` needs to represent an float32. - assert skipRange(x.rawtype).kind == tyFloat32 + assert skipRange(x.rawType).kind == tyFloat32 result = cast[ptr float32](x.value)[] proc getFloat64*(x: TAny): float64 = ## retrieve the float64 value out of `x`. `x` needs to represent an float64. - assert skipRange(x.rawtype).kind == tyFloat64 + assert skipRange(x.rawType).kind == tyFloat64 result = cast[ptr float64](x.value)[] -proc getBiggestFloat*(x: TAny): biggestFloat = +proc getBiggestFloat*(x: TAny): 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 - of tyFloat: result = biggestFloat(cast[ptr Float](x.value)[]) - of tyFloat32: result = biggestFloat(cast[ptr Float32](x.value)[]) - of tyFloat64: result = biggestFloat(cast[ptr Float64](x.value)[]) + ## some float. The value is extended to ``BiggestFloat``. + case skipRange(x.rawType).kind + of tyFloat: result = BiggestFloat(cast[ptr float](x.value)[]) + of tyFloat32: result = BiggestFloat(cast[ptr float32](x.value)[]) + of tyFloat64: result = BiggestFloat(cast[ptr float64](x.value)[]) else: assert false -proc setBiggestFloat*(x: TAny, y: biggestFloat) = +proc setBiggestFloat*(x: TAny, y: BiggestFloat) = ## sets the float value of `x`. `x` needs to represent ## some float. - case skipRange(x.rawtype).kind - of tyFloat: cast[ptr Float](x.value)[] = y - of tyFloat32: cast[ptr Float32](x.value)[] = y.float32 - of tyFloat64: cast[ptr Float64](x.value)[] = y + case skipRange(x.rawType).kind + of tyFloat: cast[ptr float](x.value)[] = y + of tyFloat32: cast[ptr float32](x.value)[] = y.float32 + of tyFloat64: cast[ptr float64](x.value)[] = y else: assert false proc getString*(x: TAny): string = ## retrieve the string value out of `x`. `x` needs to represent a string. - assert x.rawtype.kind == tyString + 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) = ## sets the string value of `x`. `x` needs to represent a string. - assert x.rawtype.kind == tyString + assert x.rawType.kind == tyString cast[ptr string](x.value)[] = y proc getCString*(x: TAny): cstring = ## retrieve the cstring value out of `x`. `x` needs to represent a cstring. - assert x.rawtype.kind == tyCString + assert x.rawType.kind == tyCString result = cast[ptr cstring](x.value)[] proc assign*(x, y: TAny) = @@ -585,9 +595,9 @@ proc assign*(x, y: TAny) = genericAssign(x.value, y.value, y.rawType) iterator elements*(x: TAny): int = - ## iterates over every element of `x` that represents a Nimrod bitset. + ## iterates over every element of `x` that represents a Nim bitset. assert x.rawType.kind == tySet - var typ = x.rawtype + var typ = x.rawType var p = x.value # "typ.slots.len" field is for sets the "first" field var u: int64 @@ -607,9 +617,9 @@ iterator elements*(x: TAny): int = yield i+typ.node.len proc inclSetElement*(x: TAny, elem: int) = - ## includes an element `elem` in `x`. `x` needs to represent a Nimrod bitset. + ## includes an element `elem` in `x`. `x` needs to represent a Nim bitset. assert x.rawType.kind == tySet - var typ = x.rawtype + var typ = x.rawType var p = x.value # "typ.slots.len" field is for sets the "first" field var e = elem - typ.node.len diff --git a/lib/core/unsigned.nim b/lib/core/unsigned.nim index a3ddd4125..7acdf1439 100644 --- a/lib/core/unsigned.nim +++ b/lib/core/unsigned.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index ce48a32ed..968a2923a 100644 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -82,17 +82,17 @@ proc tryExec*(db: TDbConn, query: TSqlQuery, 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 + return mysql.realQuery(db, q, q.len) == 0'i32 proc rawExec(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) = var q = dbFormat(query, args) - if mysql.RealQuery(db, q, q.len) != 0'i32: dbError(db) + if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db) proc exec*(db: TDbConn, query: TSqlQuery, 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) + if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db) proc newRow(L: int): TRow = newSeq(result, L) @@ -100,8 +100,8 @@ proc newRow(L: int): TRow = proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) = if row != nil: - while mysql.FetchRow(sqlres) != nil: discard - mysql.FreeResult(sqlres) + while mysql.fetchRow(sqlres) != nil: discard + mysql.freeResult(sqlres) iterator fastRows*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]): TRow {.tags: [FReadDB].} = @@ -109,13 +109,13 @@ iterator fastRows*(db: TDbConn, query: TSqlQuery, ## fast, but potenially dangerous: If the for-loop-body executes another ## query, the results can be undefined. For MySQL this is the case!. rawExec(db, query, args) - var sqlres = mysql.UseResult(db) + var sqlres = mysql.useResult(db) if sqlres != nil: - var L = int(mysql.NumFields(sqlres)) + var L = int(mysql.numFields(sqlres)) var result = newRow(L) var row: cstringArray while true: - row = mysql.FetchRow(sqlres) + row = mysql.fetchRow(sqlres) if row == nil: break for i in 0..L-1: setLen(result[i], 0) @@ -131,11 +131,11 @@ proc getRow*(db: TDbConn, query: TSqlQuery, ## retrieves a single row. If the query doesn't return any rows, this proc ## will return a TRow with empty strings for each column. rawExec(db, query, args) - var sqlres = mysql.UseResult(db) + var sqlres = mysql.useResult(db) if sqlres != nil: - var L = int(mysql.NumFields(sqlres)) + var L = int(mysql.numFields(sqlres)) result = newRow(L) - var row = mysql.FetchRow(sqlres) + var row = mysql.fetchRow(sqlres) if row != nil: for i in 0..L-1: setLen(result[i], 0) @@ -150,13 +150,13 @@ proc getAllRows*(db: TDbConn, query: TSqlQuery, ## executes the query and returns the whole result dataset. result = @[] rawExec(db, query, args) - var sqlres = mysql.UseResult(db) + var sqlres = mysql.useResult(db) if sqlres != nil: - var L = int(mysql.NumFields(sqlres)) + var L = int(mysql.numFields(sqlres)) var row: cstringArray var j = 0 while true: - row = mysql.FetchRow(sqlres) + row = mysql.fetchRow(sqlres) if row == nil: break setLen(result, j+1) newSeq(result[j], L) @@ -166,12 +166,12 @@ proc getAllRows*(db: TDbConn, query: TSqlQuery, else: result[j][i] = $row[i] inc(j) - mysql.FreeResult(sqlres) + mysql.freeResult(sqlres) iterator rows*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]): TRow {.tags: [FReadDB].} = - ## same as `FastRows`, but slower and safe. - for r in items(GetAllRows(db, query, args)): yield r + ## same as `fastRows`, but slower and safe. + for r in items(getAllRows(db, query, args)): yield r proc getValue*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]): string {.tags: [FReadDB].} = @@ -179,7 +179,7 @@ proc getValue*(db: TDbConn, query: TSqlQuery, ## result dataset. Returns "" if the dataset contains no rows or the database ## value is NULL. result = "" - for row in FastRows(db, query, args): + for row in fastRows(db, query, args): result = row[0] break @@ -188,16 +188,16 @@ proc tryInsertId*(db: TDbConn, query: TSqlQuery, ## executes the query (typically "INSERT") and returns the ## generated ID for the row or -1 in case of an error. var q = dbFormat(query, args) - if mysql.RealQuery(db, q, q.len) != 0'i32: + if mysql.realQuery(db, q, q.len) != 0'i32: result = -1'i64 else: - result = mysql.InsertId(db) + result = mysql.insertId(db) proc insertId*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} = ## executes the query (typically "INSERT") and returns the ## generated ID for the row. - result = TryInsertID(db, query, args) + result = tryInsertID(db, query, args) if result < 0: dbError(db) proc execAffectedRows*(db: TDbConn, query: TSqlQuery, @@ -206,7 +206,7 @@ proc execAffectedRows*(db: TDbConn, query: TSqlQuery, ## runs the query (typically "UPDATE") and returns the ## number of affected rows rawExec(db, query, args) - result = mysql.AffectedRows(db) + result = mysql.affectedRows(db) proc close*(db: TDbConn) {.tags: [FDb].} = ## closes the database connection. @@ -216,17 +216,16 @@ proc open*(connection, user, password, database: string): TDbConn {. tags: [FDb].} = ## opens a database connection. Raises `EDb` if the connection could not ## be established. - result = mysql.Init(nil) + result = mysql.init(nil) if result == nil: dbError("could not open database connection") let colonPos = connection.find(':') - host = if colonPos < 0: connection - else: substr(connection, 0, colonPos-1) + host = if colonPos < 0: connection + else: substr(connection, 0, colonPos-1) port: int32 = if colonPos < 0: 0'i32 - else: substr(connection, colonPos+1).parseInt.int32 - if mysql.RealConnect(result, host, user, password, database, + else: substr(connection, colonPos+1).parseInt.int32 + if mysql.realConnect(result, host, user, password, database, port, nil, 0) == nil: var errmsg = $mysql.error(result) - db_mysql.Close(result) + db_mysql.close(result) dbError(errmsg) - diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim index 510cb8e45..6691c5703 100644 --- a/lib/impure/db_postgres.nim +++ b/lib/impure/db_postgres.nim @@ -1,7 +1,7 @@ # # -# Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf +# Nim's Runtime Library +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -16,16 +16,16 @@ type TDbConn* = PPGconn ## encapsulates a database connection TRow* = seq[string] ## a row of a dataset. NULL database values will be ## transformed always to the empty string. - EDb* = object of EIO ## exception that is raised if a database error occurs + 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 - FDb* = object of FIO ## 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 + 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 -proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} = +proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} = ## constructs a TSqlQuery from the string `query`. This is supposed to be ## used as a raw-string-literal modifier: ## ``sql"update user set counter = counter + 1"`` @@ -38,7 +38,7 @@ proc dbError*(db: TDbConn) {.noreturn.} = ## raises an EDb exception. var e: ref EDb new(e) - e.msg = $PQerrorMessage(db) + e.msg = $pqErrorMessage(db) raise e proc dbError*(msg: string) {.noreturn.} = @@ -73,30 +73,30 @@ proc tryExec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} = ## tries to execute the query and returns true if successful, false otherwise. var arr = allocCStringArray(args) - var res = PQexecParams(db, query.string, int32(args.len), nil, arr, + var res = pqexecParams(db, query.string, int32(args.len), nil, arr, nil, nil, 0) deallocCStringArray(arr) - result = PQresultStatus(res) == PGRES_COMMAND_OK - PQclear(res) + result = pqresultStatus(res) == PGRES_COMMAND_OK + pqclear(res) proc exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {. tags: [FReadDB, FWriteDb].} = ## executes the query and raises EDB if not successful. var arr = allocCStringArray(args) - var res = PQexecParams(db, query.string, int32(args.len), nil, arr, + var res = pqexecParams(db, query.string, int32(args.len), nil, arr, nil, nil, 0) deallocCStringArray(arr) - if PQresultStatus(res) != PGRES_COMMAND_OK: dbError(db) - PQclear(res) + if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db) + pqclear(res) proc exec*(db: TDbConn, stmtName: TSqlPrepared, args: varargs[string]) {.tags: [FReadDB, FWriteDb].} = var arr = allocCStringArray(args) - var res = PQexecPrepared(db, stmtName.string, int32(args.len), arr, + var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr, nil, nil, 0) deallocCStringArray(arr) - if PQResultStatus(res) != PGRES_COMMAND_OK: dbError(db) - PQclear(res) + if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db) + pqclear(res) proc newRow(L: int): TRow = newSeq(result, L) @@ -105,30 +105,30 @@ proc newRow(L: int): TRow = proc setupQuery(db: TDbConn, query: TSqlQuery, args: varargs[string]): PPGresult = var arr = allocCStringArray(args) - result = PQexecParams(db, query.string, int32(args.len), nil, arr, + result = pqexecParams(db, query.string, int32(args.len), nil, arr, nil, nil, 0) deallocCStringArray(arr) - if PQResultStatus(result) != PGRES_TUPLES_OK: dbError(db) + if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db) proc setupQuery(db: TDbConn, stmtName: TSqlPrepared, args: varargs[string]): PPGresult = var arr = allocCStringArray(args) - result = PQexecPrepared(db, stmtName.string, int32(args.len), arr, + result = pqexecPrepared(db, stmtName.string, int32(args.len), arr, nil, nil, 0) deallocCStringArray(arr) - if PQResultStatus(result) != PGRES_TUPLES_OK: dbError(db) + if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db) proc prepare*(db: TDbConn; stmtName: string, query: TSqlQuery; nParams: int): TSqlPrepared = - var res = PQprepare(db, stmtName, query.string, int32(nParams), nil) - if PQResultStatus(res) != PGRES_COMMAND_OK: dbError(db) + var res = pqprepare(db, stmtName, query.string, int32(nParams), nil) + if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db) return TSqlPrepared(stmtName) proc setRow(res: PPGresult, r: var TRow, line, cols: int32) = for col in 0..cols-1: setLen(r[col], 0) - var x = PQgetvalue(res, line, col) - if x == nil: + let x = pqgetvalue(res, line, col) + if x.isNil: r[col] = nil else: add(r[col], x) @@ -139,67 +139,67 @@ iterator fastRows*(db: TDbConn, query: TSqlQuery, ## fast, but potenially dangerous: If the for-loop-body executes another ## query, the results can be undefined. For Postgres it is safe though. var res = setupQuery(db, query, args) - var L = PQnfields(res) + var L = pqnfields(res) var result = newRow(L) - for i in 0..PQntuples(res)-1: + for i in 0..pqntuples(res)-1: setRow(res, result, i, L) yield result - PQclear(res) + pqclear(res) iterator fastRows*(db: TDbConn, stmtName: TSqlPrepared, args: varargs[string, `$`]): TRow {.tags: [FReadDB].} = ## executes the prepared query and iterates over the result dataset. var res = setupQuery(db, stmtName, args) - var L = PQnfields(res) + var L = pqNfields(res) var result = newRow(L) - for i in 0..PQntuples(res)-1: + for i in 0..pqNtuples(res)-1: setRow(res, result, i, L) yield result - PQclear(res) + pqClear(res) proc getRow*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]): TRow {.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. var res = setupQuery(db, query, args) - var L = PQnfields(res) + var L = pqnfields(res) result = newRow(L) setRow(res, result, 0, L) - PQclear(res) + pqclear(res) proc getRow*(db: TDbConn, stmtName: TSqlPrepared, - args: varargs[string, `$`]): TRow {.tags: [FReadDB].} = - var res = setupQuery(db, stmtName, args) - var L = PQnfields(res) - result = newRow(L) - setRow(res, result, 0, L) - PQclear(res) + args: varargs[string, `$`]): TRow {.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].} = ## executes the query and returns the whole result dataset. result = @[] - for r in FastRows(db, query, args): + for r in fastRows(db, query, args): result.add(r) proc getAllRows*(db: TDbConn, stmtName: TSqlPrepared, args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} = ## executes the prepared query and returns the whole result dataset. result = @[] - for r in FastRows(db, stmtName, args): + for r in fastRows(db, stmtName, args): result.add(r) iterator rows*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]): TRow {.tags: [FReadDB].} = - ## same as `FastRows`, but slower and safe. - for r in items(GetAllRows(db, query, args)): yield r + ## same as `fastRows`, but slower and safe. + for r in items(getAllRows(db, query, args)): yield r proc getValue*(db: TDbConn, query: TSqlQuery, 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 ## value is NULL. - var x = PQgetvalue(setupQuery(db, query, args), 0, 0) + var x = pqgetvalue(setupQuery(db, query, args), 0, 0) result = if isNil(x): "" else: $x proc tryInsertID*(db: TDbConn, query: TSqlQuery, @@ -208,10 +208,10 @@ proc tryInsertID*(db: TDbConn, query: TSqlQuery, ## 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, TSqlQuery(string(query) & " RETURNING id"), args), 0, 0) if not isNil(x): - result = ParseBiggestInt($x) + result = parseBiggestInt($x) else: result = -1 @@ -221,7 +221,7 @@ proc insertID*(db: TDbConn, query: TSqlQuery, ## generated ID for the row. For Postgre this adds ## ``RETURNING id`` to the query, so it only works if your primary key is ## named ``id``. - result = TryInsertID(db, query, args) + result = tryInsertID(db, query, args) if result < 0: dbError(db) proc execAffectedRows*(db: TDbConn, query: TSqlQuery, @@ -230,14 +230,14 @@ proc execAffectedRows*(db: TDbConn, query: TSqlQuery, ## executes the query (typically "UPDATE") and returns the ## number of affected rows. var q = dbFormat(query, args) - var res = PQExec(db, q) - if PQresultStatus(res) != PGRES_COMMAND_OK: dbError(db) - result = parseBiggestInt($PQcmdTuples(res)) - PQclear(res) + var res = pqExec(db, q) + if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db) + result = parseBiggestInt($pqcmdTuples(res)) + pqclear(res) proc close*(db: TDbConn) {.tags: [FDb].} = ## closes the database connection. - if db != nil: PQfinish(db) + if db != nil: pqfinish(db) proc open*(connection, user, password, database: string): TDbConn {. tags: [FDb].} = @@ -249,16 +249,14 @@ proc open*(connection, user, password, database: string): TDbConn {. ## ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## - ## con = Open("", "", "", "host=localhost port=5432 dbname=mydb") + ## con = open("", "", "", "host=localhost port=5432 dbname=mydb") ## ## See http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING ## for more information. ## ## Note that the connection parameter is not used but exists to maintain - ## the nimrod db api. - result = PQsetdbLogin(nil, nil, nil, nil, database, user, password) - if PQStatus(result) != CONNECTION_OK: dbError(result) # result = nil - - + ## the nim db api. + result = pqsetdbLogin(nil, nil, nil, nil, database, user, password) + if pqStatus(result) != CONNECTION_OK: dbError(result) # result = nil diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index 809ee7039..bc9e0b591 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -16,13 +16,13 @@ type TDbConn* = PSqlite3 ## encapsulates a database connection TRow* = seq[string] ## a row of a dataset. NULL database values will be ## transformed always to the empty string. - EDb* = object of EIO ## exception that is raised if a database error occurs + EDb* = object of IOError ## exception that is raised if a database error occurs TSqlQuery* = distinct string ## an SQL query string - FDb* = object of FIO ## 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 + 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 proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} = ## constructs a TSqlQuery from the string `query`. This is supposed to be @@ -65,16 +65,16 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string = add(result, c) proc tryExec*(db: TDbConn, query: TSqlQuery, - args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} = + args: varargs[string, `$`]): bool {.tags: [FReadDb, FWriteDb].} = ## tries to execute the query and returns true if successful, false otherwise. var q = dbFormat(query, args) - var stmt: sqlite3.PStmt + var stmt: sqlite3.Pstmt if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK: if step(stmt) == SQLITE_DONE: result = finalize(stmt) == SQLITE_OK proc exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {. - tags: [FReadDB, FWriteDb].} = + tags: [FReadDb, FWriteDb].} = ## executes the query and raises EDB if not successful. if not tryExec(db, query, args): dbError(db) @@ -83,11 +83,11 @@ proc newRow(L: int): TRow = for i in 0..L-1: result[i] = "" proc setupQuery(db: TDbConn, query: TSqlQuery, - args: varargs[string]): PStmt = + 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 TRow, cols: cint) = for col in 0..cols-1: setLen(r[col], column_bytes(stmt, col)) # set capacity setLen(r[col], 0) @@ -95,12 +95,12 @@ proc setRow(stmt: PStmt, r: var TRow, cols: cint) = if not isNil(x): add(r[col], x) iterator fastRows*(db: TDbConn, query: TSqlQuery, - args: varargs[string, `$`]): TRow {.tags: [FReadDB].} = + args: varargs[string, `$`]): TRow {.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. var stmt = setupQuery(db, query, args) - var L = (columnCount(stmt)) + var L = (column_count(stmt)) var result = newRow(L) while step(stmt) == SQLITE_ROW: setRow(stmt, result, L) @@ -108,30 +108,30 @@ iterator fastRows*(db: TDbConn, query: TSqlQuery, if finalize(stmt) != SQLITE_OK: dbError(db) proc getRow*(db: TDbConn, query: TSqlQuery, - args: varargs[string, `$`]): TRow {.tags: [FReadDB].} = + args: varargs[string, `$`]): TRow {.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. var stmt = setupQuery(db, query, args) - var L = (columnCount(stmt)) + var L = (column_count(stmt)) result = newRow(L) if step(stmt) == SQLITE_ROW: setRow(stmt, result, L) if finalize(stmt) != SQLITE_OK: dbError(db) proc getAllRows*(db: TDbConn, query: TSqlQuery, - args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} = + args: varargs[string, `$`]): seq[TRow] {.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].} = + args: varargs[string, `$`]): TRow {.tags: [FReadDb].} = ## same as `FastRows`, but slower and safe. for r in fastRows(db, query, args): yield r proc getValue*(db: TDbConn, query: TSqlQuery, - args: varargs[string, `$`]): string {.tags: [FReadDB].} = + args: varargs[string, `$`]): string {.tags: [FReadDb].} = ## executes the query and returns the first column of the first row of the ## result dataset. Returns "" if the dataset contains no rows or the database ## value is NULL. @@ -153,7 +153,7 @@ proc tryInsertID*(db: TDbConn, query: TSqlQuery, ## executes the query (typically "INSERT") and returns the ## generated ID for the row or -1 in case of an error. var q = dbFormat(query, args) - var stmt: sqlite3.PStmt + var stmt: sqlite3.Pstmt result = -1 if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK: if step(stmt) == SQLITE_DONE: @@ -172,18 +172,18 @@ proc insertID*(db: TDbConn, query: TSqlQuery, proc execAffectedRows*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]): int64 {. - tags: [FReadDB, FWriteDb].} = + tags: [FReadDb, FWriteDb].} = ## executes the query (typically "UPDATE") and returns the ## number of affected rows. exec(db, query, args) result = changes(db) -proc close*(db: TDbConn) {.tags: [FDB].} = +proc close*(db: TDbConn) {.tags: [FDb].} = ## closes the database connection. if sqlite3.close(db) != SQLITE_OK: dbError(db) proc open*(connection, user, password, database: string): TDbConn {. - tags: [FDB].} = + 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 diff --git a/lib/impure/dialogs.nim b/lib/impure/dialogs.nim index 00ba2663e..4ea66a6e6 100644 --- a/lib/impure/dialogs.nim +++ b/lib/impure/dialogs.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -8,7 +8,7 @@ # -## This module implements portable dialogs for Nimrod; the implementation +## This module implements portable dialogs for Nim; the implementation ## builds on the GTK interface. On Windows, native dialogs are shown instead. import @@ -200,14 +200,14 @@ proc chooseDir*(window: PWindow, root: string = ""): string = BrowseInfo: TBrowseInfo DisplayName: array [0..MAX_PATH, char] TempPath: array [0..MAX_PATH, char] - Result = "" + result = "" #BrowseInfo.hwndOwner = Application.Handle BrowseInfo.pszDisplayName = DisplayName BrowseInfo.ulFlags = 1 #BIF_RETURNONLYFSDIRS lpItemID = SHBrowseForFolder(cast[LPBrowseInfo](addr(BrowseInfo))) if lpItemId != nil: discard SHGetPathFromIDList(lpItemID, TempPath) - Result = $TempPath + result = $TempPath discard GlobalFreePtr(lpItemID) else: var chooser = file_chooser_dialog_new("Select Directory", window, diff --git a/lib/impure/graphics.nim b/lib/impure/graphics.nim index 2c8e96460..dfadb46ee 100644 --- a/lib/impure/graphics.nim +++ b/lib/impure/graphics.nim @@ -1,20 +1,20 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf, Dominik Picheta # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## This module implements graphical output for Nimrod; the current +## This module implements graphical output for Nim; the current ## implementation uses SDL but the interface is meant to support multiple ## backends some day. There is no need to init SDL as this module does that ## implicitly. import colors, math from sdl import PSurface # Bug -from sdl_ttf import OpenFont, closeFont +from sdl_ttf import openFont, closeFont type TRect* = tuple[x, y, width, height: int] @@ -25,24 +25,24 @@ type w*, h*: Natural s*: sdl.PSurface - EGraphics* = object of EIO + EGraphics* = object of IOError TFont {.pure, final.} = object f: sdl_ttf.PFont - color: SDL.TColor + color: sdl.TColor PFont* = ref TFont ## represents a font -proc toSdlColor*(c: TColor): Sdl.TColor = - ## Convert colors.TColor to SDL.TColor +proc toSdlColor*(c: Color): sdl.TColor = + ## Convert colors.TColor to sdl.TColor var x = c.extractRGB result.r = x.r and 0xff result.g = x.g and 0xff result.b = x.b and 0xff -proc createSdlColor*(sur: PSurface, c: TColor, alpha: int = 0): int32 = +proc createSdlColor*(sur: PSurface, c: Color, alpha: int = 0): int32 = ## Creates a color using ``sdl.MapRGBA``. var x = c.extractRGB - return sdl.MapRGBA(sur.s.format, x.r and 0xff, x.g and 0xff, + return sdl.mapRGBA(sur.s.format, x.r and 0xff, x.g and 0xff, x.b and 0xff, alpha and 0xff) proc toSdlRect*(r: TRect): sdl.TRect = @@ -53,7 +53,7 @@ proc toSdlRect*(r: TRect): sdl.TRect = result.h = uint16(r.height) proc raiseEGraphics = - raise newException(EGraphics, $SDL.GetError()) + raise newException(EGraphics, $sdl.getError()) proc surfaceFinalizer(s: PSurface) = sdl.freeSurface(s.s) @@ -62,21 +62,21 @@ proc newSurface*(width, height: int): PSurface = new(result, surfaceFinalizer) result.w = width result.h = height - result.s = SDL.CreateRGBSurface(SDL.SWSURFACE, width, height, + result.s = sdl.createRGBSurface(sdl.SWSURFACE, width, height, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0) if result.s == nil: raiseEGraphics() - assert(not sdl.MustLock(result.s)) + assert(not sdl.mustLock(result.s)) proc fontFinalizer(f: PFont) = closeFont(f.f) proc newFont*(name = "VeraMono.ttf", size = 9, color = colBlack): PFont = ## Creates a new font object. Raises ``EIO`` if the font cannot be loaded. new(result, fontFinalizer) - result.f = OpenFont(name, size.cint) + result.f = openFont(name, size.cint) if result.f == nil: - raise newException(EIO, "Could not open font file: " & name) + raise newException(IOError, "Could not open font file: " & name) result.color = toSdlColor(color) var @@ -92,7 +92,7 @@ proc newScreenSurface*(width, height: int): PSurface = new(result, surfaceFinalizer) result.w = width result.h = height - result.s = SDL.SetVideoMode(width, height, 0, 0) + result.s = sdl.setVideoMode(width, height, 0, 0) if result.s == nil: raiseEGraphics() @@ -100,7 +100,7 @@ proc writeToBMP*(sur: PSurface, filename: string) = ## Saves the contents of the surface `sur` to the file `filename` as a ## BMP file. if sdl.saveBMP(sur.s, filename) != 0: - raise newException(EIO, "cannot write: " & filename) + raise newException(IOError, "cannot write: " & filename) type TPixels = array[0..1000_000-1, int32] @@ -110,44 +110,44 @@ template setPix(video, pitch, x, y, col: expr): stmt = video[y * pitch + x] = int32(col) template getPix(video, pitch, x, y: expr): expr = - colors.TColor(video[y * pitch + x]) + colors.Color(video[y * pitch + x]) const ColSize = 4 -proc getPixel(sur: PSurface, x, y: Natural): colors.TColor {.inline.} = +proc getPixel(sur: PSurface, x, y: Natural): colors.Color {.inline.} = assert x <% sur.w assert y <% sur.h result = getPix(cast[PPixels](sur.s.pixels), sur.s.pitch.int div ColSize, x, y) -proc setPixel(sur: PSurface, x, y: Natural, col: colors.TColor) {.inline.} = +proc setPixel(sur: PSurface, x, y: Natural, col: colors.Color) {.inline.} = assert x <% sur.w assert y <% sur.h var pixs = cast[PPixels](sur.s.pixels) #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): TColor = +proc `[]`*(sur: PSurface, p: TPoint): Color = ## get pixel at position `p`. No range checking is done! result = getPixel(sur, p.x, p.y) -proc `[]`*(sur: PSurface, x, y: int): TColor = +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: TColor) = +proc `[]=`*(sur: PSurface, p: TPoint, col: Color) = ## set the pixel at position `p`. No range checking is done! setPixel(sur, p.x, p.y, col) -proc `[]=`*(sur: PSurface, x, y: int, col: TColor) = +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) = ## Copies ``srcSurf`` into ``destSurf`` - var destTRect, srcTRect: SDL.TRect + var destTRect, srcTRect: sdl.TRect destTRect.x = int16(destRect.x) destTRect.y = int16(destRect.y) @@ -159,12 +159,12 @@ proc blit*(destSurf: PSurface, destRect: TRect, srcSurf: PSurface, srcTRect.w = uint16(srcRect.width) srcTRect.h = uint16(srcRect.height) - if SDL.blitSurface(srcSurf.s, addr(srcTRect), destSurf.s, addr(destTRect)) != 0: + if sdl.blitSurface(srcSurf.s, addr(srcTRect), destSurf.s, addr(destTRect)) != 0: raiseEGraphics() proc textBounds*(text: string, font = defaultFont): tuple[width, height: int] = var w, h: cint - if sdl_ttf.SizeUTF8(font.f, text, w, h) < 0: raiseEGraphics() + if sdl_ttf.sizeUTF8(font.f, text, w, h) < 0: raiseEGraphics() result.width = int(w) result.height = int(h) @@ -175,21 +175,21 @@ proc drawText*(sur: PSurface, p: TPoint, text: string, font = defaultFont) = new(textSur, surfaceFinalizer) # Render the text - textSur.s = sdl_ttf.RenderTextBlended(font.f, text, font.color) + textSur.s = sdl_ttf.renderTextBlended(font.f, text, font.color) # Merge the text surface with sur 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, - bg: TColor, font = defaultFont) = + bg: Color, font = defaultFont) = ## Draws text, at location ``p`` with font ``font``. ``bg`` ## is the background color. var textSur: PSurface # This surface will have the text drawn on it new(textSur, surfaceFinalizer) - textSur.s = sdl_ttf.RenderTextShaded(font.f, text, font.color, toSdlColor(bg)) + textSur.s = sdl_ttf.renderTextShaded(font.f, text, font.color, toSdlColor(bg)) # Merge the text surface with sur 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: TColor) = +proc drawCircle*(sur: PSurface, p: TPoint, 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 +229,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: TColor) = +proc drawLine*(sur: PSurface, p1, p2: TPoint, 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 @@ -273,7 +273,7 @@ proc drawLine*(sur: PSurface, p1, p2: TPoint, color: TColor) = fraction = fraction + dx setPix(video, pitch, x0, y0, color) -proc drawHorLine*(sur: PSurface, x, y, w: Natural, Color: TColor) = +proc drawHorLine*(sur: PSurface, x, y, w: Natural, color: Color) = ## draws a horizontal line from (x,y) to (x+w-1, y). var video = cast[PPixels](sur.s.pixels) var pitch = sur.s.pitch.int div ColSize @@ -282,7 +282,7 @@ proc drawHorLine*(sur: PSurface, x, y, w: Natural, Color: TColor) = for i in 0 .. min(sur.s.w-x, w)-1: setPix(video, pitch, x + i, y, color) -proc drawVerLine*(sur: PSurface, x, y, h: Natural, Color: TColor) = +proc drawVerLine*(sur: PSurface, x, y, h: Natural, color: Color) = ## draws a vertical line from (x,y) to (x, y+h-1). var video = cast[PPixels](sur.s.pixels) var pitch = sur.s.pitch.int div ColSize @@ -291,7 +291,7 @@ proc drawVerLine*(sur: PSurface, x, y, h: Natural, Color: TColor) = 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: TColor) = +proc fillCircle*(s: PSurface, p: TPoint, 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 @@ -301,11 +301,11 @@ proc fillCircle*(s: PSurface, p: TPoint, r: Natural, color: TColor) = var y = p.y while px <= py: # Fill up the middle half of the circle - DrawVerLine(s, x + px, y, py + 1, color) - DrawVerLine(s, x + px, y - py, py, color) + drawVerLine(s, x + px, y, py + 1, color) + drawVerLine(s, x + px, y - py, py, color) if px != 0: - DrawVerLine(s, x - px, y, py + 1, color) - DrawVerLine(s, x - px, y - py, py, color) + drawVerLine(s, x - px, y, py + 1, color) + drawVerLine(s, x - px, y - py, py, color) if a < 0: a = a + (2 * px + 3) else: @@ -313,13 +313,13 @@ proc fillCircle*(s: PSurface, p: TPoint, r: Natural, color: TColor) = py = py - 1 # Fill up the left/right half of the circle if py >= px: - DrawVerLine(s, x + py + 1, y, px + 1, color) - DrawVerLine(s, x + py + 1, y - px, px, color) - DrawVerLine(s, x - py - 1, y, px + 1, color) - DrawVerLine(s, x - py - 1, y - px, px, color) + drawVerLine(s, x + py + 1, y, px + 1, color) + drawVerLine(s, x + py + 1, y - px, px, color) + drawVerLine(s, x - py - 1, y, px + 1, color) + drawVerLine(s, x - py - 1, y - px, px, color) px = px + 1 -proc drawRect*(sur: PSurface, r: TRect, color: TColor) = +proc drawRect*(sur: PSurface, r: TRect, color: Color) = ## draws a rectangle. var video = cast[PPixels](sur.s.pixels) var pitch = sur.s.pitch.int div ColSize @@ -337,78 +337,78 @@ proc drawRect*(sur: PSurface, r: TRect, color: TColor) = 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: TColor) = +proc fillRect*(sur: PSurface, r: TRect, 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: + if sdl.fillRect(sur.s, addr(rect), sur.createSdlColor(col)) == -1: raiseEGraphics() -proc plot4EllipsePoints(sur: PSurface, CX, CY, X, Y: Natural, col: TColor) = +proc plot4EllipsePoints(sur: PSurface, cx, cy, x, y: Natural, col: Color) = var video = cast[PPixels](sur.s.pixels) var pitch = sur.s.pitch.int div ColSize - if CX+X <= sur.s.w-1: - if CY+Y <= sur.s.h-1: setPix(video, pitch, CX+X, CY+Y, col) - if CY-Y <= sur.s.h-1: setPix(video, pitch, CX+X, CY-Y, col) - if CX-X <= sur.s.w-1: - if CY+Y <= sur.s.h-1: setPix(video, pitch, CX-X, CY+Y, col) - if CY-Y <= sur.s.h-1: setPix(video, pitch, CX-X, CY-Y, col) + if cx+x <= sur.s.w-1: + if cy+y <= sur.s.h-1: setPix(video, pitch, cx+x, cy+y, col) + if cy-y <= sur.s.h-1: setPix(video, pitch, cx+x, cy-y, col) + if cx-x <= sur.s.w-1: + if cy+y <= sur.s.h-1: setPix(video, pitch, cx-x, cy+y, col) + if cy-y <= sur.s.h-1: setPix(video, pitch, cx-x, cy-y, col) -proc drawEllipse*(sur: PSurface, CX, CY, XRadius, YRadius: Natural, - col: TColor) = +proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural, + col: Color) = ## Draws an ellipse, ``CX`` and ``CY`` specify the center X and Y of the ## ellipse, ``XRadius`` and ``YRadius`` specify half the width and height ## of the ellipse. var - X, Y: Natural - XChange, YChange: Int - EllipseError: Natural - TwoASquare, TwoBSquare: Natural - StoppingX, StoppingY: Natural + x, y: Natural + xChange, yChange: int + ellipseError: Natural + twoASquare, twoBSquare: Natural + stoppingX, stoppingY: Natural - TwoASquare = 2 * XRadius * XRadius - TwoBSquare = 2 * YRadius * YRadius - X = XRadius - Y = 0 - XChange = YRadius * YRadius * (1 - 2 * XRadius) - YChange = XRadius * XRadius - EllipseError = 0 - StoppingX = TwoBSquare * XRadius - StoppingY = 0 + twoASquare = 2 * xRadius * xRadius + twoBSquare = 2 * yRadius * yRadius + x = xRadius + y = 0 + xChange = yRadius * yRadius * (1 - 2 * xRadius) + yChange = xRadius * xRadius + ellipseError = 0 + stoppingX = twoBSquare * xRadius + stoppingY = 0 - while StoppingX >= StoppingY: # 1st set of points, y` > - 1 - sur.Plot4EllipsePoints(CX, CY, X, Y, col) - inc(Y) - inc(StoppingY, TwoASquare) - inc(EllipseError, YChange) - inc(YChange, TwoASquare) - if (2 * EllipseError + XChange) > 0 : + while stoppingX >= stoppingY: # 1st set of points, y` > - 1 + sur.plot4EllipsePoints(cx, cy, x, y, col) + inc(y) + inc(stoppingY, twoASquare) + inc(ellipseError, yChange) + inc(yChange, twoASquare) + if (2 * ellipseError + xChange) > 0 : dec(x) - dec(StoppingX, TwoBSquare) - inc(EllipseError, XChange) - inc(XChange, TwoBSquare) + dec(stoppingX, twoBSquare) + inc(ellipseError, xChange) + inc(xChange, twoBSquare) # 1st point set is done; start the 2nd set of points - X = 0 - Y = YRadius - XChange = YRadius * YRadius - YChange = XRadius * XRadius * (1 - 2 * YRadius) - EllipseError = 0 - StoppingX = 0 - StoppingY = TwoASquare * YRadius - while StoppingX <= StoppingY: - sur.Plot4EllipsePoints(CX, CY, X, Y, col) - inc(X) - inc(StoppingX, TwoBSquare) - inc(EllipseError, XChange) - inc(XChange,TwoBSquare) - if (2 * EllipseError + YChange) > 0: - dec(Y) - dec(StoppingY, TwoASquare) - inc(EllipseError, YChange) - inc(YChange,TwoASquare) + x = 0 + y = yRadius + xChange = yRadius * yRadius + yChange = xRadius * xRadius * (1 - 2 * yRadius) + ellipseError = 0 + stoppingX = 0 + stoppingY = twoASquare * yRadius + while stoppingX <= stoppingY: + sur.plot4EllipsePoints(cx, cy, x, y, col) + inc(x) + inc(stoppingX, twoBSquare) + inc(ellipseError, xChange) + inc(xChange,twoBSquare) + if (2 * ellipseError + yChange) > 0: + dec(y) + dec(stoppingY, twoASquare) + inc(ellipseError, yChange) + inc(yChange,twoASquare) -proc plotAA(sur: PSurface, x, y: int, c: float, color: TColor) = +proc plotAA(sur: PSurface, x, y: int, c: float, color: Color) = if (x > 0 and x < sur.s.w) and (y > 0 and y < sur.s.h): var video = cast[PPixels](sur.s.pixels) var pitch = sur.s.pitch.int div ColSize @@ -424,7 +424,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: TColor) = +proc drawLineAA*(sur: PSurface, p1, p2: TPoint, 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(), @@ -444,11 +444,11 @@ proc drawLineAA*(sur: PSurface, p1, p2: TPoint, color: TColor) = swap(x2, y2) swap(dx, dy) - template doPlot(x, y: int, c: float, color: TColor): stmt = + template doPlot(x, y: int, c: float, color: Color): stmt = if ax < ay: - sur.PlotAA(y, x, c, color) + sur.plotAA(y, x, c, color) else: - sur.PlotAA(x, y, c, color) + sur.plotAA(x, y, c, color) if x2 < x1: swap(x1, x2) @@ -482,22 +482,22 @@ proc drawLineAA*(sur: PSurface, p1, p2: TPoint, color: TColor) = intery = intery + gradient inc(x) -proc fillSurface*(sur: PSurface, color: TColor) = +proc fillSurface*(sur: PSurface, color: Color) = ## Fills the entire surface with ``color``. - if sdl.FillRect(sur.s, nil, sur.createSdlColor(color)) == -1: + if sdl.fillRect(sur.s, nil, sur.createSdlColor(color)) == -1: raiseEGraphics() 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. - while True: - var event: SDL.TEvent - if SDL.WaitEvent(addr(event)) == 1: + while true: + var event: sdl.TEvent + if sdl.waitEvent(addr(event)) == 1: actions -if sdl.Init(sdl.INIT_VIDEO) < 0: raiseEGraphics() -if sdl_ttf.Init() < 0: raiseEGraphics() +if sdl.init(sdl.INIT_VIDEO) < 0: raiseEGraphics() +if sdl_ttf.init() < 0: raiseEGraphics() when isMainModule: var surf = newScreenSurface(800, 600) @@ -539,19 +539,19 @@ when isMainModule: withEvents(surf, event): var eventp = addr(event) case event.kind: - of SDL.QUITEV: + of sdl.QUITEV: break - of SDL.KEYDOWN: - var evk = sdl.EvKeyboard(eventp) - if evk.keysym.sym == SDL.K_LEFT: + of sdl.KEYDOWN: + var evk = sdl.evKeyboard(eventp) + if evk.keysym.sym == sdl.K_LEFT: surf.drawHorLine(395, 300, 50, colBlack) echo("Drawing") - elif evk.keysym.sym == SDL.K_ESCAPE: + elif evk.keysym.sym == sdl.K_ESCAPE: break else: echo(evk.keysym.sym) - of SDL.MOUSEBUTTONDOWN: - var mbd = sdl.EvMouseButton(eventp) + of sdl.MOUSEBUTTONDOWN: + var mbd = sdl.evMouseButton(eventp) if mouseStartX == -1 or mouseStartY == -1: mouseStartX = int(mbd.x) mouseStartY = int(mbd.y) @@ -560,16 +560,16 @@ when isMainModule: mouseStartX = -1 mouseStartY = -1 - of SDL.MouseMotion: - var mm = sdl.EvMouseMotion(eventp) + of sdl.MOUSEMOTION: + var mm = sdl.evMouseMotion(eventp) if mouseStartX != -1 and mouseStartY != -1: surf.drawLineAA((mouseStartX, mouseStartY), (int(mm.x), int(mm.y)), colPurple) #echo(mm.x, " ", mm.y, " ", mm.yrel) else: - #echo(event.kind) + discard "echo(event.kind)" - SDL.UpdateRect(surf.s, 0, 0, 800, 600) + sdl.updateRect(surf.s, 0, 0, 800, 600) surf.writeToBMP("test.bmp") - SDL.Quit() + sdl.quit() diff --git a/lib/impure/osinfo_posix.nim b/lib/impure/osinfo_posix.nim index 4fde82012..1baff8c55 100644 --- a/lib/impure/osinfo_posix.nim +++ b/lib/impure/osinfo_posix.nim @@ -1,20 +1,21 @@ import posix, strutils, os -type - Tstatfs {.importc: "struct statfs64", - header: "", final, pure.} = object - f_type: int - f_bsize: int - f_blocks: int - f_bfree: int - f_bavail: int - f_files: int - f_ffree: int - f_fsid: int - f_namelen: int +when false: + type + Tstatfs {.importc: "struct statfs64", + header: "", final, pure.} = object + f_type: int + f_bsize: int + f_blocks: int + f_bfree: int + f_bavail: int + f_files: int + f_ffree: int + f_fsid: int + f_namelen: int -proc statfs(path: string, buf: var Tstatfs): int {. - importc, header: "".} + proc statfs(path: string, buf: var Tstatfs): int {. + importc, header: "".} proc getSystemVersion*(): string = @@ -23,7 +24,7 @@ proc getSystemVersion*(): string = var unix_info: TUtsname if uname(unix_info) != 0: - os.OSError() + os.raiseOSError(osLastError()) if $unix_info.sysname == "Linux": # Linux diff --git a/lib/impure/osinfo_win.nim b/lib/impure/osinfo_win.nim index 572e50273..f423a34a3 100644 --- a/lib/impure/osinfo_win.nim +++ b/lib/impure/osinfo_win.nim @@ -375,7 +375,7 @@ proc getFileSize*(file: string): BiggestInt = var hFile = findFirstFileA(file, fileData) if hFile == INVALID_HANDLE_VALUE: - raise newException(EIO, $getLastError()) + raise newException(IOError, $getLastError()) return fileData.nFileSizeLow @@ -386,10 +386,10 @@ proc getDiskFreeSpaceEx*(lpDirectoryName: cstring, lpFreeBytesAvailableToCaller, proc getPartitionInfo*(partition: string): TPartitionInfo = ## Retrieves partition info, for example ``partition`` may be ``"C:\"`` - var FreeBytes, TotalBytes, TotalFreeBytes: TFiletime - var res = getDiskFreeSpaceEx(r"C:\", FreeBytes, TotalBytes, - TotalFreeBytes) - return (FreeBytes, TotalBytes) + var freeBytes, totalBytes, totalFreeBytes: TFiletime + discard getDiskFreeSpaceEx(r"C:\", freeBytes, totalBytes, + totalFreeBytes) + return (freeBytes, totalBytes) when isMainModule: var r = getMemoryInfo() diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim index 1037d3bda..07ef13fd9 100644 --- a/lib/impure/rdstdin.nim +++ b/lib/impure/rdstdin.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -15,13 +15,13 @@ when defined(Windows): proc readLineFromStdin*(prompt: string): TaintedString {. - tags: [FReadIO, FWriteIO].} = + tags: [ReadIOEffect, WriteIOEffect].} = ## Reads a line from stdin. stdout.write(prompt) result = readLine(stdin) proc readLineFromStdin*(prompt: string, line: var TaintedString): bool {. - tags: [FReadIO, FWriteIO].} = + tags: [ReadIOEffect, WriteIOEffect].} = ## Reads a `line` from stdin. `line` must not be ## ``nil``! May throw an IO exception. ## A line of text may be delimited by ``CR``, ``LF`` or @@ -35,7 +35,7 @@ else: import readline, history proc readLineFromStdin*(prompt: string): TaintedString {. - tags: [FReadIO, FWriteIO].} = + tags: [ReadIOEffect, WriteIOEffect].} = var buffer = readline.readLine(prompt) if isNil(buffer): quit(0) result = TaintedString($buffer) @@ -44,7 +44,7 @@ else: readline.free(buffer) proc readLineFromStdin*(prompt: string, line: var TaintedString): bool {. - tags: [FReadIO, FWriteIO].} = + tags: [ReadIOEffect, WriteIOEffect].} = var buffer = readline.readLine(prompt) if isNil(buffer): quit(0) line = TaintedString($buffer) diff --git a/lib/impure/re.nim b/lib/impure/re.nim index ac07b2d6b..a7bebb81c 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -1,13 +1,13 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## Regular expression support for Nimrod. Consider using the pegs module +## Regular expression support for Nim. Consider using the pegs module ## instead. ## This module is implemented by providing a wrapper around the ## `PRCE (Perl-Compatible Regular Expressions) `_ @@ -20,15 +20,15 @@ ## import - pcre, strutils + pcre, strutils, rtarrays const - MaxSubpatterns* = 10 + MaxSubpatterns* = 20 ## defines the maximum number of subpatterns that can be captured. - ## More subpatterns cannot be captured! + ## This limit still exists for ``replacef`` and ``parallelReplace``. type - TRegexFlag* = enum ## options for regular expressions + RegexFlag* = enum ## options for regular expressions reIgnoreCase = 0, ## do caseless matching reMultiLine = 1, ## ``^`` and ``$`` match newlines within data reDotAll = 2, ## ``.`` matches anything including NL @@ -36,30 +36,33 @@ type reStudy = 4 ## study the expression (may be omitted if the ## expression will be used only once) - TRegexDesc {.pure, final.} = object + RegexDesc = object h: PPcre e: ptr TExtra - TRegex* = ref TRegexDesc ## a compiled regular expression + Regex* = ref RegexDesc ## a compiled regular expression - EInvalidRegEx* = object of EInvalidValue + RegexError* = object of ValueError ## is raised if the pattern is no valid regular expression. +{.deprecated: [TRegexFlag: RegexFlag, TRegexDesc: RegexDesc, TRegex: Regex, + EInvalidRegEx: RegexError].} + proc raiseInvalidRegex(msg: string) {.noinline, noreturn.} = - var e: ref EInvalidRegEx + var e: ref RegexError new(e) e.msg = msg raise e - + proc rawCompile(pattern: string, flags: cint): PPcre = var msg: cstring offset: cint - result = pcre.Compile(pattern, flags, addr(msg), addr(offset), nil) + result = pcre.compile(pattern, flags, addr(msg), addr(offset), nil) if result == nil: raiseInvalidRegex($msg & "\n" & pattern & "\n" & repeatChar(offset) & "^\n") -proc finalizeRegEx(x: TRegex) = +proc finalizeRegEx(x: Regex) = # XXX This is a hack, but PCRE does not export its "free" function properly. # Sigh. The hack relies on PCRE's implementation (see ``pcre_get.c``). # Fortunately the implementation is unlikely to change. @@ -67,8 +70,8 @@ proc finalizeRegEx(x: TRegex) = if not isNil(x.e): pcre.free_substring(cast[cstring](x.e)) -proc re*(s: string, flags = {reExtended, reStudy}): TRegex = - ## Constructor of regular expressions. Note that Nimrod's +proc re*(s: string, flags = {reExtended, reStudy}): Regex = + ## Constructor of regular expressions. Note that Nim's ## extended raw string literals support this syntax ``re"[abc]"`` as ## a short form for ``re(r"[abc]")``. new(result, finalizeRegEx) @@ -78,39 +81,41 @@ proc re*(s: string, flags = {reExtended, reStudy}): TRegex = result.e = pcre.study(result.h, 0, msg) if not isNil(msg): raiseInvalidRegex($msg) -proc matchOrFind(s: string, pattern: TRegex, matches: var openArray[string], +proc matchOrFind(s: string, pattern: Regex, matches: var openArray[string], start, flags: cint): cint = var - rawMatches: array[0..maxSubpatterns * 3 - 1, cint] - res = pcre.Exec(pattern.h, pattern.e, s, len(s).cint, start, flags, - cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3) + rtarray = initRtArray[cint]((matches.len+1)*3) + rawMatches = rtarray.getRawData + res = pcre.exec(pattern.h, pattern.e, s, len(s).cint, start, flags, + cast[ptr cint](rawMatches), (matches.len+1).cint*3) if res < 0'i32: return res for i in 1..int(res)-1: var a = rawMatches[i * 2] var b = rawMatches[i * 2 + 1] if a >= 0'i32: matches[i-1] = substr(s, int(a), int(b)-1) - else: matches[i-1] = "" + else: matches[i-1] = nil return rawMatches[1] - rawMatches[0] -proc findBounds*(s: string, pattern: TRegex, matches: var openArray[string], +proc findBounds*(s: string, pattern: Regex, matches: var openArray[string], start = 0): tuple[first, last: int] = ## returns the starting position and end position of `pattern` in `s` ## and the captured ## substrings in the array `matches`. If it does not match, nothing ## is written into `matches` and ``(-1,0)`` is returned. var - rawMatches: array[0..maxSubpatterns * 3 - 1, cint] - res = pcre.Exec(pattern.h, pattern.e, s, len(s).cint, start.cint, 0'i32, - cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3) + rtarray = initRtArray[cint]((matches.len+1)*3) + rawMatches = rtarray.getRawData + res = pcre.exec(pattern.h, pattern.e, s, len(s).cint, start.cint, 0'i32, + cast[ptr cint](rawMatches), (matches.len+1).cint*3) if res < 0'i32: return (-1, 0) for i in 1..int(res)-1: var a = rawMatches[i * 2] var b = rawMatches[i * 2 + 1] if a >= 0'i32: matches[i-1] = substr(s, int(a), int(b)-1) - else: matches[i-1] = "" + else: matches[i-1] = nil return (rawMatches[0].int, rawMatches[1].int - 1) -proc findBounds*(s: string, pattern: TRegex, +proc findBounds*(s: string, pattern: Regex, matches: var openArray[tuple[first, last: int]], start = 0): tuple[first, last: int] = ## returns the starting position and end position of ``pattern`` in ``s`` @@ -118,9 +123,10 @@ proc findBounds*(s: string, pattern: TRegex, ## If it does not match, nothing is written into `matches` and ## ``(-1,0)`` is returned. var - rawMatches: array[0..maxSubpatterns * 3 - 1, cint] - res = pcre.Exec(pattern.h, pattern.e, s, len(s).cint, start.cint, 0'i32, - cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3) + rtarray = initRtArray[cint]((matches.len+1)*3) + rawMatches = rtarray.getRawData + res = pcre.exec(pattern.h, pattern.e, s, len(s).cint, start.cint, 0'i32, + cast[ptr cint](rawMatches), (matches.len+1).cint*3) if res < 0'i32: return (-1, 0) for i in 1..int(res)-1: var a = rawMatches[i * 2] @@ -129,25 +135,28 @@ proc findBounds*(s: string, pattern: TRegex, else: matches[i-1] = (-1,0) return (rawMatches[0].int, rawMatches[1].int - 1) -proc findBounds*(s: string, pattern: TRegex, +proc findBounds*(s: string, pattern: Regex, start = 0): tuple[first, last: int] = ## returns the starting position of `pattern` in `s`. If it does not ## match, ``(-1,0)`` is returned. var - rawMatches: array[0..3 - 1, cint] - res = pcre.Exec(pattern.h, nil, s, len(s).cint, start.cint, 0'i32, - cast[ptr cint](addr(rawMatches)), 3) + rtarray = initRtArray[cint](3) + rawMatches = rtarray.getRawData + res = pcre.exec(pattern.h, nil, s, len(s).cint, start.cint, 0'i32, + cast[ptr cint](rawMatches), 3) if res < 0'i32: return (int(res), 0) return (int(rawMatches[0]), int(rawMatches[1]-1)) -proc matchOrFind(s: string, pattern: TRegex, start, flags: cint): cint = - var rawMatches: array [0..maxSubpatterns * 3 - 1, cint] - result = pcre.Exec(pattern.h, pattern.e, s, len(s).cint, start, flags, - cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3) +proc matchOrFind(s: string, pattern: Regex, start, flags: cint): cint = + var + rtarray = initRtArray[cint](3) + rawMatches = rtarray.getRawData + result = pcre.exec(pattern.h, pattern.e, s, len(s).cint, start, flags, + cast[ptr cint](rawMatches), 3) if result >= 0'i32: result = rawMatches[1] - rawMatches[0] -proc match*(s: string, pattern: TRegex, matches: var openArray[string], +proc match*(s: string, pattern: Regex, matches: var openArray[string], start = 0): bool = ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and ## the captured substrings in the array ``matches``. If it does not @@ -156,67 +165,71 @@ proc match*(s: string, pattern: TRegex, matches: var openArray[string], return matchOrFind(s, pattern, matches, start.cint, pcre.ANCHORED) == cint(s.len - start) -proc match*(s: string, pattern: TRegex, start = 0): bool = +proc match*(s: string, pattern: Regex, start = 0): bool = ## returns ``true`` if ``s[start..]`` matches the ``pattern``. return matchOrFind(s, pattern, start.cint, pcre.ANCHORED) == cint(s.len-start) -proc matchLen*(s: string, pattern: TRegex, matches: var openArray[string], +proc matchLen*(s: string, pattern: Regex, matches: var openArray[string], start = 0): int = ## the same as ``match``, but it returns the length of the match, ## if there is no match, -1 is returned. Note that a match length ## of zero can happen. return matchOrFind(s, pattern, matches, start.cint, pcre.ANCHORED) -proc matchLen*(s: string, pattern: TRegex, start = 0): int = +proc matchLen*(s: string, pattern: Regex, start = 0): int = ## the same as ``match``, but it returns the length of the match, ## if there is no match, -1 is returned. Note that a match length ## of zero can happen. return matchOrFind(s, pattern, start.cint, pcre.ANCHORED) -proc find*(s: string, pattern: TRegex, matches: var openArray[string], +proc find*(s: string, pattern: Regex, matches: var openArray[string], start = 0): int = ## returns the starting position of ``pattern`` in ``s`` and the captured ## substrings in the array ``matches``. If it does not match, nothing ## is written into ``matches`` and -1 is returned. var - rawMatches: array[0..maxSubpatterns * 3 - 1, cint] - res = pcre.Exec(pattern.h, pattern.e, s, len(s).cint, start.cint, 0'i32, - cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3) + rtarray = initRtArray[cint]((matches.len+1)*3) + rawMatches = rtarray.getRawData + res = pcre.exec(pattern.h, pattern.e, s, len(s).cint, start.cint, 0'i32, + cast[ptr cint](rawMatches), (matches.len+1).cint*3) if res < 0'i32: return res for i in 1..int(res)-1: var a = rawMatches[i * 2] var b = rawMatches[i * 2 + 1] if a >= 0'i32: matches[i-1] = substr(s, int(a), int(b)-1) - else: matches[i-1] = "" + else: matches[i-1] = nil return rawMatches[0] -proc find*(s: string, pattern: TRegex, start = 0): int = +proc find*(s: string, pattern: Regex, start = 0): int = ## returns the starting position of ``pattern`` in ``s``. If it does not ## match, -1 is returned. var - rawMatches: array[0..3 - 1, cint] - res = pcre.Exec(pattern.h, nil, s, len(s).cint, start.cint, 0'i32, - cast[ptr cint](addr(rawMatches)), 3) + rtarray = initRtArray[cint](3) + rawMatches = rtarray.getRawData + res = pcre.exec(pattern.h, nil, s, len(s).cint, start.cint, 0'i32, + cast[ptr cint](rawMatches), 3) if res < 0'i32: return res return rawMatches[0] - -iterator findAll*(s: string, pattern: TRegex, start = 0): string = + +iterator findAll*(s: string, pattern: Regex, start = 0): string = ## Yields all matching *substrings* of `s` that match `pattern`. ## ## Note that since this is an iterator you should not modify the string you ## are iterating over: bad things could happen. - var i = int32(start) - var rawMatches: array[0..maxSubpatterns * 3 - 1, cint] + var + i = int32(start) + rtarray = initRtArray[cint](3) + rawMatches = rtarray.getRawData while true: - let res = pcre.Exec(pattern.h, pattern.e, s, len(s).cint, i, 0'i32, - cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3) + let res = pcre.exec(pattern.h, pattern.e, s, len(s).cint, i, 0'i32, + cast[ptr cint](rawMatches), 3) if res < 0'i32: break let a = rawMatches[0] let b = rawMatches[1] yield substr(s, int(a), int(b)-1) i = b -proc findAll*(s: string, pattern: TRegex, start = 0): seq[string] = +proc findAll*(s: string, pattern: Regex, start = 0): seq[string] = ## returns all matching *substrings* of `s` that match `pattern`. ## If it does not match, @[] is returned. accumulateResult(findAll(s, pattern, start)) @@ -224,11 +237,11 @@ proc findAll*(s: string, pattern: TRegex, start = 0): seq[string] = when not defined(nimhygiene): {.pragma: inject.} -template `=~` *(s: string, pattern: TRegex): expr = +template `=~` *(s: string, pattern: Regex): expr = ## This calls ``match`` with an implicit declared ``matches`` array that ## can be used in the scope of the ``=~`` call: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## if line =~ re"\s*(\w+)\s*\=\s*(\w+)": ## # matches a key=value pair: @@ -242,41 +255,41 @@ template `=~` *(s: string, pattern: TRegex): expr = ## else: ## echo("syntax error") ## - bind maxSubPatterns + bind MaxSubPatterns when not declaredInScope(matches): var matches {.inject.}: array[0..MaxSubpatterns-1, string] match(s, pattern, matches) # ------------------------- more string handling ------------------------------ -proc contains*(s: string, pattern: TRegex, start = 0): bool = +proc contains*(s: string, pattern: Regex, start = 0): bool = ## same as ``find(s, pattern, start) >= 0`` return find(s, pattern, start) >= 0 -proc contains*(s: string, pattern: TRegex, matches: var openArray[string], +proc contains*(s: string, pattern: Regex, matches: var openArray[string], start = 0): bool = ## same as ``find(s, pattern, matches, start) >= 0`` return find(s, pattern, matches, start) >= 0 -proc startsWith*(s: string, prefix: TRegex): bool = +proc startsWith*(s: string, prefix: Regex): bool = ## returns true if `s` starts with the pattern `prefix` result = matchLen(s, prefix) >= 0 -proc endsWith*(s: string, suffix: TRegex): bool = +proc endsWith*(s: string, suffix: Regex): bool = ## returns true if `s` ends with the pattern `prefix` for i in 0 .. s.len-1: if matchLen(s, suffix, i) == s.len - i: return true -proc replace*(s: string, sub: TRegex, by = ""): string = +proc replace*(s: string, sub: Regex, by = ""): string = ## Replaces `sub` in `s` by the string `by`. Captures cannot be ## accessed in `by`. Examples: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## "var1=key; var2=key2".replace(re"(\w+)'='(\w+)") ## ## Results in: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## "; " result = "" @@ -289,50 +302,38 @@ proc replace*(s: string, sub: TRegex, by = ""): string = prev = match.last + 1 add(result, substr(s, prev)) -proc replacef*(s: string, sub: TRegex, by: string): string = +proc replacef*(s: string, sub: Regex, by: string): string = ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by` ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## "var1=key; var2=key2".replacef(re"(\w+)'='(\w+)", "$1<-$2$2") ## ## Results in: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## "var1<-keykey; val2<-key2key2" result = "" - var caps: array[0..maxSubpatterns-1, string] + var caps: array[0..MaxSubpatterns-1, string] var prev = 0 while true: var match = findBounds(s, sub, caps, prev) if match.first < 0: break + assert result != nil + assert s != nil add(result, substr(s, prev, match.first-1)) addf(result, by, caps) prev = match.last + 1 add(result, substr(s, prev)) - when false: - result = "" - var i = 0 - var caps: array[0..maxSubpatterns-1, string] - while i < s.len: - var x = matchLen(s, sub, caps, i) - if x <= 0: - add(result, s[i]) - inc(i) - else: - addf(result, by, caps) - inc(i, x) - # substr the rest: - add(result, substr(s, i)) - + proc parallelReplace*(s: string, subs: openArray[ - tuple[pattern: TRegex, repl: string]]): string = + tuple[pattern: Regex, repl: string]]): string = ## Returns a modified copy of `s` with the substitutions in `subs` ## applied in parallel. result = "" var i = 0 - var caps: array[0..maxSubpatterns-1, string] + var caps: array[0..MaxSubpatterns-1, string] while i < s.len: block searchSubs: for j in 0..high(subs): @@ -344,29 +345,29 @@ proc parallelReplace*(s: string, subs: openArray[ add(result, s[i]) inc(i) # copy the rest: - add(result, substr(s, i)) - + add(result, substr(s, i)) + proc transformFile*(infile, outfile: string, - subs: openArray[tuple[pattern: TRegex, repl: string]]) = + subs: openArray[tuple[pattern: Regex, repl: string]]) = ## reads in the file `infile`, performs a parallel replacement (calls ## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an ## error occurs. This is supposed to be used for quick scripting. var x = readFile(infile).string writeFile(outfile, x.parallelReplace(subs)) -iterator split*(s: string, sep: TRegex): string = +iterator split*(s: string, sep: Regex): string = ## Splits the string `s` into substrings. ## ## Substrings are separated by the regular expression `sep`. ## Examples: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## for word in split("00232this02939is39an22example111", re"\d+"): ## writeln(stdout, word) ## ## Results in: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## "this" ## "is" ## "an" @@ -386,7 +387,7 @@ iterator split*(s: string, sep: TRegex): string = if first < last: yield substr(s, first, last-1) -proc split*(s: string, sep: TRegex): seq[string] = +proc split*(s: string, sep: Regex): seq[string] = ## Splits the string `s` into substrings. accumulateResult(split(s, sep)) @@ -447,6 +448,14 @@ when isMainModule: assert matches[1] == "abc" else: assert false + + if "abc" =~ re"(cba)?.*": + assert matches[0] == nil + else: assert false + + if "abc" =~ re"().*": + assert matches[0] == "" + else: assert false assert "var1=key; var2=key2".endsWith(re"\w+=\w+") assert("var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2") == @@ -461,3 +470,11 @@ when isMainModule: assert x == "d" for x in findAll("abcdef", re".", 3): echo x + + block: + var matches: array[0..15, string] + if match("abcdefghijklmnop", re"(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)(m)(n)(o)(p)", matches): + for i in 0..matches.high: + assert matches[i] == $chr(i + 'a'.ord) + else: + assert false diff --git a/lib/impure/ssl.nim b/lib/impure/ssl.nim index 54d524c7b..bb7cfc0d3 100644 --- a/lib/impure/ssl.nim +++ b/lib/impure/ssl.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Dominik Picheta # # See the file "copying.txt", included in this @@ -8,31 +8,31 @@ # ## This module provides an easy to use sockets-style -## nimrod interface to the OpenSSL library. +## nim interface to the OpenSSL library. {.deprecated.} import openssl, strutils, os type - TSecureSocket* {.final.} = object - ssl: PSSL - bio: PBIO + TSecureSocket* = object + ssl: SslPtr + bio: BIO proc connect*(sock: var TSecureSocket, address: string, - port: int): Int = + port: int): int = ## Connects to the specified `address` on the specified `port`. ## Returns the result of the certificate validation. SslLoadErrorStrings() ERR_load_BIO_strings() if SSL_library_init() != 1: - OSError() + raiseOSError(osLastError()) var ctx = SSL_CTX_new(SSLv23_client_method()) if ctx == nil: ERR_print_errors_fp(stderr) - OSError() + raiseOSError(osLastError()) #if SSL_CTX_load_verify_locations(ctx, # "/tmp/openssl-0.9.8e/certs/vsign1.pem", NIL) == 0: @@ -41,14 +41,14 @@ proc connect*(sock: var TSecureSocket, address: string, sock.bio = BIO_new_ssl_connect(ctx) if BIO_get_ssl(sock.bio, addr(sock.ssl)) == 0: - OSError() + raiseOSError(osLastError()) if BIO_set_conn_hostname(sock.bio, address & ":" & $port) != 1: - OSError() + raiseOSError(osLastError()) if BIO_do_connect(sock.bio) <= 0: ERR_print_errors_fp(stderr) - OSError() + raiseOSError(osLastError()) result = SSL_get_verify_result(sock.ssl) @@ -57,30 +57,30 @@ proc recvLine*(sock: TSecureSocket, line: var TaintedString): bool = ## Returns false when no data is available to be read. ## `Line` must be initialized and not nil! setLen(line.string, 0) - while True: + while true: var c: array[0..0, char] var n = BIO_read(sock.bio, c, c.len.cint) - if n <= 0: return False + if n <= 0: return false if c[0] == '\r': n = BIO_read(sock.bio, c, c.len.cint) if n > 0 and c[0] == '\L': - return True + return true elif n <= 0: - return False - elif c[0] == '\L': return True + return false + elif c[0] == '\L': return true add(line.string, c) proc send*(sock: TSecureSocket, data: string) = ## Writes `data` to the socket. if BIO_write(sock.bio, data, data.len.cint) <= 0: - OSError() + raiseOSError(osLastError()) proc close*(sock: TSecureSocket) = ## Closes the socket if BIO_free(sock.bio) <= 0: ERR_print_errors_fp(stderr) - OSError() + raiseOSError(osLastError()) when isMainModule: var s: TSecureSocket diff --git a/lib/impure/web.nim b/lib/impure/web.nim deleted file mode 100644 index 5f04422d1..000000000 --- a/lib/impure/web.nim +++ /dev/null @@ -1,63 +0,0 @@ -# -# -# Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module contains simple high-level procedures for dealing with the -## web. Use cases: -## -## * requesting URLs -## * sending and retrieving emails -## * sending and retrieving files from an FTP server -## -## Currently only requesting URLs is implemented. The implementation depends -## on the libcurl library! -## -## **Deprecated since version 0.8.8:** Use the -## `httpclient `_ module instead. -## - -{.deprecated.} - -import libcurl, streams - -proc curlwrapperWrite(p: pointer, size, nmemb: int, - data: pointer): int {.cdecl.} = - var stream = cast[PStream](data) - stream.writeData(p, size*nmemb) - return size*nmemb - -proc URLretrieveStream*(url: string): PStream = - ## retrieves the given `url` and returns a stream which one can read from to - ## obtain the contents. Returns nil if an error occurs. - result = newStringStream() - var hCurl = easy_init() - if hCurl == nil: return nil - if easy_setopt(hCurl, OPT_URL, url) != E_OK: return nil - if easy_setopt(hCurl, OPT_WRITEFUNCTION, - curlwrapperWrite) != E_OK: return nil - if easy_setopt(hCurl, OPT_WRITEDATA, result) != E_OK: return nil - if easy_perform(hCurl) != E_OK: return nil - easy_cleanup(hCurl) - -proc URLretrieveString*(url: string): TaintedString = - ## retrieves the given `url` and returns the contents. Returns nil if an - ## error occurs. - var stream = newStringStream() - var hCurl = easy_init() - if hCurl == nil: return - if easy_setopt(hCurl, OPT_URL, url) != E_OK: return - if easy_setopt(hCurl, OPT_WRITEFUNCTION, - curlwrapperWrite) != E_OK: return - if easy_setopt(hCurl, OPT_WRITEDATA, stream) != E_OK: return - if easy_perform(hCurl) != E_OK: return - easy_cleanup(hCurl) - result = stream.data.TaintedString - -when isMainModule: - echo URLretrieveString("http://nimrod-code.org/") - diff --git a/lib/impure/zipfiles.nim b/lib/impure/zipfiles.nim index b9f89dda0..c22294061 100644 --- a/lib/impure/zipfiles.nim +++ b/lib/impure/zipfiles.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -13,18 +13,18 @@ import streams, libzip, times, os type - TZipArchive* = object of TObject ## represents a zip archive - mode: TFileMode + TZipArchive* = object of RootObj ## represents a zip archive + mode: FileMode w: PZip proc zipError(z: var TZipArchive) = - var e: ref EIO + var e: ref IOError new(e) e.msg = $zip_strerror(z.w) raise e -proc open*(z: var TZipArchive, filename: string, mode: TFileMode = fmRead): bool = +proc open*(z: var TZipArchive, 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 @@ -57,7 +57,7 @@ proc addFile*(z: var TZipArchive, dest, src: string) = ## may contain a path that will be created. assert(z.mode != fmRead) if not fileExists(src): - raise newException(EIO, "File '" & src & "' does not exist") + raise newException(IOError, "File '" & src & "' does not exist") var zipsrc = zip_source_file(z.w, src, 0, -1) if zipsrc == nil: #echo("Dest: " & dest) @@ -74,7 +74,7 @@ proc addFile*(z: var TZipArchive, file: string) = proc mySourceCallback(state, data: pointer, len: int, cmd: TZipSourceCmd): int {.cdecl.} = - var src = cast[PStream](state) + var src = cast[Stream](state) case cmd of ZIP_SOURCE_OPEN: if src.setPositionImpl != nil: setPosition(src, 0) # reset @@ -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: PStream) = +proc addFile*(z: var TZipArchive, 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) @@ -109,14 +109,14 @@ proc addFile*(z: var TZipArchive, dest: string, src: PStream) = # -------------- zip file stream --------------------------------------------- type - TZipFileStream = object of TStream + TZipFileStream = object of StreamObj f: PZipFile PZipFileStream* = ref TZipFileStream ## a reader stream of a file within a zip archive -proc fsClose(s: PStream) = zip_fclose(PZipFileStream(s).f) -proc fsReadData(s: PStream, buffer: pointer, bufLen: int): int = +proc fsClose(s: Stream) = zip_fclose(PZipFileStream(s).f) +proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int = result = zip_fread(PZipFileStream(s).f, buffer, bufLen) proc newZipFileStream(f: PZipFile): PZipFileStream = @@ -146,7 +146,7 @@ iterator walkFiles*(z: var TZipArchive): string = inc(i) -proc extractFile*(z: var TZipArchive, srcFile: string, dest: PStream) = +proc extractFile*(z: var TZipArchive, srcFile: string, dest: Stream) = ## extracts a file from the zip archive `z` to the destination stream. var strm = getStream(z, srcFile) while true: diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 951d8e835..91b260a64 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -14,7 +14,7 @@ when not defined(js) and not defined(Nimdoc): {.error: "This module only works on the JavaScript platform".} type - TEventHandlers* {.importc.} = object of TObject + TEventHandlers* {.importc.} = object of RootObj onabort*: proc (event: ref TEvent) {.nimcall.} onblur*: proc (event: ref TEvent) {.nimcall.} onchange*: proc (event: ref TEvent) {.nimcall.} @@ -124,14 +124,14 @@ type embeds*: seq[ref TEmbed] links*: seq[ref TLink] - TLink* {.importc.} = object of TObject + TLink* {.importc.} = object of RootObj name*: cstring target*: cstring text*: cstring x*: int y*: int - TEmbed* {.importc.} = object of TObject + TEmbed* {.importc.} = object of RootObj height*: int hspace*: int name*: cstring @@ -142,12 +142,12 @@ type play*: proc () {.nimcall.} stop*: proc () {.nimcall.} - TAnchor* {.importc.} = object of TObject + TAnchor* {.importc.} = object of RootObj name*: cstring text*: cstring x*, y*: int - TApplet* {.importc.} = object of TObject + TApplet* {.importc.} = object of RootObj TElement* {.importc.} = object of TEventHandlers checked*: bool @@ -166,7 +166,7 @@ type select*: proc () {.nimcall.} options*: seq[ref TOption] - TOption* {.importc.} = object of TObject + TOption* {.importc.} = object of RootObj defaultSelected*: bool selected*: bool selectedIndex*: int @@ -209,7 +209,7 @@ type DocumentTypeNode, DocumentFragmentNode, NotationNode - TNode* {.importc.} = object of TObject + TNode* {.importc.} = object of RootObj attributes*: seq[ref TNode] childNodes*: seq[ref TNode] data*: cstring @@ -241,7 +241,7 @@ type setAttributeNode*: proc (attr: ref TNode) {.nimcall.} style*: ref TStyle - TStyle* {.importc.} = object of TObject + TStyle* {.importc.} = object of RootObj background*: cstring backgroundAttachment*: cstring backgroundColor*: cstring @@ -335,7 +335,7 @@ type removeAttribute*: proc (attr: cstring, caseSensitive=false) {.nimcall.} setAttribute*: proc (attr, value: cstring, caseSensitive=false) {.nimcall.} - TEvent* {.importc.} = object of TObject + TEvent* {.importc.} = object of RootObj altKey*, ctrlKey*, shiftKey*: bool button*: int clientX*, clientY*: int @@ -373,7 +373,7 @@ type SUBMIT*: int UNLOAD*: int - TLocation* {.importc.} = object of TObject + TLocation* {.importc.} = object of RootObj hash*: cstring host*: cstring hostname*: cstring @@ -385,13 +385,13 @@ type reload*: proc () {.nimcall.} replace*: proc (s: cstring) {.nimcall.} - THistory* {.importc.} = object of TObject + THistory* {.importc.} = object of RootObj length*: int back*: proc () {.nimcall.} forward*: proc () {.nimcall.} go*: proc (pagesToJump: int) {.nimcall.} - TNavigator* {.importc.} = object of TObject + TNavigator* {.importc.} = object of RootObj appCodeName*: cstring appName*: cstring appVersion*: cstring @@ -402,18 +402,18 @@ type javaEnabled*: proc (): bool {.nimcall.} mimeTypes*: seq[ref TMimeType] - TPlugin* {.importc.} = object of TObject + TPlugin* {.importc.} = object of RootObj description*: cstring filename*: cstring name*: cstring - TMimeType* {.importc.} = object of TObject + TMimeType* {.importc.} = object of RootObj description*: cstring enabledPlugin*: ref TPlugin suffixes*: seq[cstring] `type`*: cstring - TLocationBar* {.importc.} = object of TObject + TLocationBar* {.importc.} = object of RootObj visible*: bool TMenuBar* = TLocationBar TPersonalBar* = TLocationBar @@ -421,7 +421,7 @@ type TToolBar* = TLocationBar TStatusBar* = TLocationBar - TScreen* {.importc.} = object of TObject + TScreen* {.importc.} = object of RootObj availHeight*: int availWidth*: int colorDepth*: int @@ -429,8 +429,8 @@ type pixelDepth*: int width*: int - TTimeOut* {.importc.} = object of TObject - TInterval* {.importc.} = object of TObject + TTimeOut* {.importc.} = object of RootObj + TInterval* {.importc.} = object of RootObj var window* {.importc, nodecl.}: ref TWindow @@ -446,7 +446,7 @@ proc unescape*(uri: cstring): cstring {.importc, nodecl.} proc decodeURIComponent*(uri: cstring): cstring {.importc, nodecl.} proc encodeURIComponent*(uri: cstring): cstring {.importc, nodecl.} -proc isFinite*(x: biggestFloat): bool {.importc, nodecl.} -proc isNaN*(x: biggestFloat): bool {.importc, nodecl.} -proc parseFloat*(s: cstring): biggestFloat {.importc, nodecl.} +proc isFinite*(x: BiggestFloat): bool {.importc, nodecl.} +proc isNaN*(x: BiggestFloat): bool {.importc, nodecl.} +proc parseFloat*(s: cstring): BiggestFloat {.importc, nodecl.} proc parseInt*(s: cstring): int {.importc, nodecl.} diff --git a/lib/nimbase.h b/lib/nimbase.h index b43094227..ac90081d8 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -1,7 +1,7 @@ /* - Nimrod's Runtime Library - (c) Copyright 2013 Andreas Rumpf + Nim's Runtime Library + (c) Copyright 2014 Andreas Rumpf See the file "copying.txt", included in this distribution, for details about the copyright. @@ -141,9 +141,11 @@ __clang__ /* these compilers have a fastcall so use it: */ # define N_NIMCALL(rettype, name) rettype __fastcall name # define N_NIMCALL_PTR(rettype, name) rettype (__fastcall *name) +# define N_RAW_NIMCALL __fastcall #else # define N_NIMCALL(rettype, name) rettype name /* no modifier */ # define N_NIMCALL_PTR(rettype, name) rettype (*name) +# define N_RAW_NIMCALL #endif #define N_CLOSURE(rettype, name) N_NIMCALL(rettype, name) @@ -175,9 +177,9 @@ __clang__ # define NIM_NIL 0 struct NimException { - NimException(struct E_Base* exp, const char* msg): exp(exp), msg(msg) {} + NimException(struct Exception* exp, const char* msg): exp(exp), msg(msg) {} - struct E_Base* exp; + struct Exception* exp; const char* msg; }; #else @@ -382,3 +384,9 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); } "error: 'assert_numbits' declared as an array with a negative size" */ typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1]; #endif + +#ifdef __cplusplus +# define NIM_EXTERNC extern "C" +#else +# define NIM_EXTERNC +#endif diff --git a/lib/nimrtl.nim b/lib/nimrtl.nim index 68b7d7bd9..96dab1284 100644 --- a/lib/nimrtl.nim +++ b/lib/nimrtl.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2010 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index ff371f4e1..7fc2e1aa3 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -23,7 +23,7 @@ type gtTagStart, gtTagEnd, gtKey, gtValue, gtRawData, gtAssembler, gtPreprocessor, gtDirective, gtCommand, gtRule, gtHyperlink, gtLabel, gtReference, gtOther - TGeneralTokenizer* = object of TObject + TGeneralTokenizer* = object of RootObj kind*: TTokenClass start*, length*: int buf: cstring @@ -31,11 +31,11 @@ type state: TTokenClass TSourceLanguage* = enum - langNone, langNimrod, langCpp, langCsharp, langC, langJava + langNone, langNim, langNimrod, langCpp, langCsharp, langC, langJava const - sourceLanguageToStr*: array[TSourceLanguage, string] = ["none", "Nimrod", - "C++", "C#", "C", "Java"] + sourceLanguageToStr*: array[TSourceLanguage, string] = ["none", + "Nim", "Nimrod", "C++", "C#", "C", "Java"] tokenClassToStr*: array[TTokenClass, string] = ["Eof", "None", "Whitespace", "DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber", "Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit", @@ -46,10 +46,12 @@ const # The following list comes from doc/keywords.txt, make sure it is # synchronized with this array by running the module itself as a test case. - nimrodKeywords = ["addr", "and", "as", "asm", "atomic", "bind", "block", - "break", "case", "cast", "const", "continue", "converter", "discard", - "distinct", "div", "do", "elif", "else", "end", "enum", "except", "export", - "finally", "for", "from", "generic", "if", "import", "in", "include", + nimKeywords = ["addr", "and", "as", "asm", "atomic", "bind", "block", + "break", "case", "cast", "const", "continue", "converter", + "defer", "discard", "distinct", "div", "do", + "elif", "else", "end", "enum", "except", "export", + "finally", "for", "from", "func", + "generic", "if", "import", "in", "include", "interface", "is", "isnot", "iterator", "let", "macro", "method", "mixin", "mod", "nil", "not", "notin", "object", "of", "or", "out", "proc", "ptr", "raise", "ref", "return", "shl", "shr", "static", @@ -79,7 +81,7 @@ proc deinitGeneralTokenizer*(g: var TGeneralTokenizer) = discard proc nimGetKeyword(id: string): TTokenClass = - for k in nimrodKeywords: + for k in nimKeywords: if cmpIgnoreStyle(id, k) == 0: return gtKeyword result = gtIdentifier when false: @@ -542,7 +544,7 @@ proc javaNextToken(g: var TGeneralTokenizer) = proc getNextToken*(g: var TGeneralTokenizer, lang: TSourceLanguage) = case lang of langNone: assert false - of langNimrod: nimNextToken(g) + of langNim, langNimrod: nimNextToken(g) of langCpp: cppNextToken(g) of langCsharp: csharpNextToken(g) of langC: cNextToken(g) @@ -557,7 +559,7 @@ when isMainModule: keywords = input.split() break doAssert(not keywords.isNil, "Couldn't read any keywords.txt file!") - doAssert keywords.len == nimrodKeywords.len, "No matching lengths" + doAssert keywords.len == nimKeywords.len, "No matching lengths" for i in 0..keywords.len-1: - #echo keywords[i], " == ", nimrodKeywords[i] - doAssert keywords[i] == nimrodKeywords[i], "Unexpected keyword" + #echo keywords[i], " == ", nimKeywords[i] + doAssert keywords[i] == nimKeywords[i], "Unexpected keyword" diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 23459ade6..95d49bad7 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -17,7 +17,7 @@ import type TRstParseOption* = enum ## options for the RST parser roSkipPounds, ## skip ``#`` at line beginning (documentation - ## embedded in Nimrod comments) + ## embedded in Nim comments) roSupportSmilies, ## make the RST parser support smilies like ``:)`` roSupportRawDirective, ## support the ``raw`` directive (don't support ## it for sandboxing) @@ -39,7 +39,8 @@ type meInvalidDirective, mwRedefinitionOfLabel, mwUnknownSubstitution, - mwUnsupportedLanguage + mwUnsupportedLanguage, + mwUnsupportedField TMsgHandler* = proc (filename: string, line, col: int, msgKind: TMsgKind, arg: string) {.nimcall.} ## what to do in case of an error @@ -55,7 +56,8 @@ const meInvalidDirective: "invalid directive: '$1'", mwRedefinitionOfLabel: "redefinition of label '$1'", mwUnknownSubstitution: "unknown substitution '$1'", - mwUnsupportedLanguage: "language '$1' not supported" + mwUnsupportedLanguage: "language '$1' not supported", + mwUnsupportedField: "field '$1' not supported" ] proc rstnodeToRefname*(n: PRstNode): string @@ -66,8 +68,8 @@ proc getArgument*(n: PRstNode): string # ----------------------------- scanner part -------------------------------- const - SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'} - SmileyStartChars: TCharSet = {':', ';', '8'} + SymChars: set[char] = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'} + SmileyStartChars: set[char] = {':', ';', '8'} Smilies = { ":D": "icon_e_biggrin", ":-D": "icon_e_biggrin", @@ -111,21 +113,21 @@ const type TTokType = enum tkEof, tkIndent, tkWhite, tkWord, tkAdornment, tkPunct, tkOther - TToken{.final.} = object # a RST token + TToken = object # a RST token kind*: TTokType # 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 TObject + TLexer = object of RootObj buf*: cstring bufpos*: int line*, col*, baseIndent*: int skipPounds*: bool -proc getThing(L: var TLexer, tok: var TToken, s: TCharSet) = +proc getThing(L: var TLexer, tok: var TToken, s: set[char]) = tok.kind = tkWord tok.line = L.line tok.col = L.col @@ -252,11 +254,11 @@ proc getTokens(buffer: string, skipPounds: bool, tokens: var TTokenSeq): int = type TLevelMap = array[char, int] - TSubstitution{.final.} = object + TSubstitution = object key*: string value*: PRstNode - TSharedState {.final.} = object + TSharedState = object options: TRstParseOptions # parsing options uLevel, oLevel: int # counters for the section levels subs: seq[TSubstitution] # substitutions @@ -271,9 +273,9 @@ type # This is for over-underline adornments. msgHandler: TMsgHandler # How to handle errors. findFile: TFindFileHandler # How to find files. - + PSharedState = ref TSharedState - TRstParser = object of TObject + TRstParser = object of RootObj idx*: int tok*: TTokenSeq s*: PSharedState @@ -282,7 +284,7 @@ type line*, col*: int hasToc*: bool - EParseError* = object of EInvalidValue + EParseError* = object of ValueError proc whichMsgClass*(k: TMsgKind): TMsgClass = ## returns which message class `k` belongs to. @@ -850,13 +852,13 @@ proc parseComment(p: var TRstParser): PRstNode = type TDirKind = enum # must be ordered alphabetically! - dkNone, dkAuthor, dkAuthors, dkCodeBlock, dkContainer, dkContents, + dkNone, dkAuthor, dkAuthors, dkCode, dkCodeBlock, dkContainer, dkContents, dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle const - DirIds: array[0..11, string] = ["", "author", "authors", "code-block", - "container", "contents", "figure", "image", "include", "index", "raw", - "title"] + DirIds: array[0..12, string] = ["", "author", "authors", "code", + "code-block", "container", "contents", "figure", "image", "include", + "index", "raw", "title"] proc getDirKind(s: string): TDirKind = let i = find(DirIds, s) @@ -876,7 +878,10 @@ proc parseUntilNewline(p: var TRstParser, father: PRstNode) = of tkEof, tkIndent: break proc parseSection(p: var TRstParser, result: PRstNode) -proc parseField(p: var TRstParser): PRstNode = +proc parseField(p: var TRstParser): PRstNode = + ## Returns a parsed rnField node. + ## + ## rnField nodes have two children nodes, a rnFieldName and a rnFieldBody. result = newRstNode(rnField) var col = p.tok[p.idx].col var fieldname = newRstNode(rnFieldName) @@ -892,7 +897,11 @@ proc parseField(p: var TRstParser): PRstNode = add(result, fieldname) add(result, fieldbody) -proc parseFields(p: var TRstParser): PRstNode = +proc parseFields(p: var TRstParser): PRstNode = + ## Parses fields for a section or directive block. + ## + ## This proc may return nil if the parsing doesn't find anything of value, + ## otherwise it will return a node of rnFieldList type with children. result = nil var atStart = p.idx == 0 and p.tok[0].symbol == ":" if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx + 1].symbol == ":") or @@ -908,6 +917,18 @@ proc parseFields(p: var TRstParser): PRstNode = else: break +proc getFieldValue*(n: PRstNode): string = + ## Returns the value of a specific ``rnField`` node. + ## + ## This proc will assert if the node is not of the expected type. The empty + ## string will be returned as a minimum. Any value in the rst will be + ## stripped form leading/trailing whitespace. + assert n.kind == rnField + assert n.len == 2 + assert n.sons[0].kind == rnFieldName + assert n.sons[1].kind == rnFieldBody + result = addNodes(n.sons[1]).strip + proc getFieldValue(n: PRstNode, fieldname: string): string = result = "" if n.sons[1] == nil: return @@ -1387,7 +1408,16 @@ type TDirFlags = set[TDirFlag] TSectionParser = proc (p: var TRstParser): PRstNode {.nimcall.} -proc parseDirective(p: var TRstParser, flags: TDirFlags): PRstNode = +proc parseDirective(p: var TRstParser, flags: TDirFlags): PRstNode = + ## Parses arguments and options for a directive block. + ## + ## A directive block will always have three sons: the arguments for the + ## directive (rnDirArg), the options (rnFieldList) and the block + ## (rnLineBlock). This proc parses the two first nodes, the block is left to + ## the outer `parseDirective` call. + ## + ## Both rnDirArg and rnFieldList children nodes might be nil, so you need to + ## check them before accessing. result = newRstNode(rnDirective) var args: PRstNode = nil var options: PRstNode = nil @@ -1421,6 +1451,9 @@ proc indFollows(p: TRstParser): bool = proc parseDirective(p: var TRstParser, flags: TDirFlags, contentParser: TSectionParser): PRstNode = + ## Returns a generic rnDirective tree. + ## + ## The children are rnDirArg, rnFieldList and rnLineBlock. Any might be nil. result = parseDirective(p, flags) if not isNil(contentParser) and indFollows(p): pushInd(p, p.tok[p.idx].ival) @@ -1474,7 +1507,23 @@ proc dirInclude(p: var TRstParser): PRstNode = # InternalError("Too many binary zeros in include file") result = parseDoc(q) -proc dirCodeBlock(p: var TRstParser): PRstNode = +proc dirCodeBlock(p: var TRstParser, nimrodExtension = false): PRstNode = + ## Parses a code block. + ## + ## Code blocks are rnDirective trees with a `kind` of rnCodeBlock. See the + ## description of ``parseDirective`` for further structure information. + ## + ## Code blocks can come in two forms, the standard `code directive + ## `_ and + ## the nimrod extension ``.. code-block::``. If the block is an extension, we + ## want the default language syntax highlighting to be Nimrod, so we create a + ## fake internal field to comminicate with the generator. The field is named + ## ``default-language``, which is unlikely to collide with a field specified + ## by any random rst input file. + ## + ## As an extension this proc will process the ``file`` extension field and if + ## present will replace the code block with the contents of the referenced + ## file. result = parseDirective(p, {hasArg, hasOptions}, parseLiteralBlock) var filename = strip(getFieldValue(result, "file")) if filename != "": @@ -1483,6 +1532,20 @@ proc dirCodeBlock(p: var TRstParser): PRstNode = var n = newRstNode(rnLiteralBlock) add(n, newRstNode(rnLeaf, readFile(path))) result.sons[2] = n + + # Extend the field block if we are using our custom extension. + if nimrodExtension: + # Create a field block if the input block didn't have any. + if result.sons[1].isNil: result.sons[1] = newRstNode(rnFieldList) + assert result.sons[1].kind == rnFieldList + # Hook the extra field and specify the Nimrod language as value. + var extraNode = newRstNode(rnField) + extraNode.add(newRstNode(rnFieldName)) + extraNode.add(newRstNode(rnFieldBody)) + extraNode.sons[0].add(newRstNode(rnLeaf, "default-language")) + extraNode.sons[1].add(newRstNode(rnLeaf, "Nimrod")) + result.sons[1].add(extraNode) + result.kind = rnCodeBlock proc dirContainer(p: var TRstParser): PRstNode = @@ -1566,7 +1629,8 @@ proc parseDotDot(p: var TRstParser): PRstNode = result = dirRaw(p) else: rstMessage(p, meInvalidDirective, d) - of dkCodeBlock: result = dirCodeBlock(p) + of dkCode: result = dirCodeBlock(p) + of dkCodeBlock: result = dirCodeBlock(p, nimrodExtension = true) of dkIndex: result = dirIndex(p) else: rstMessage(p, meInvalidDirective, d) popInd(p) diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 8f946d973..52af672df 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -286,7 +286,7 @@ proc renderRstToRst*(n: PRstNode, result: var string) = var d: TRenderContext renderRstToRst(d, n, result) -proc renderRstToJsonNode(node: PRstNode): PJsonNode = +proc renderRstToJsonNode(node: PRstNode): JsonNode = result = %[ (key: "kind", val: %($node.kind)), @@ -295,7 +295,7 @@ proc renderRstToJsonNode(node: PRstNode): PJsonNode = if node.text != nil: result.add("text", %node.text) if node.sons != nil and len(node.sons) > 0: - var accm = newSeq[PJsonNode](len(node.sons)) + var accm = newSeq[JsonNode](len(node.sons)) for i, son in node.sons: accm[i] = renderRstToJsonNode(son) result.add("sons", %accm) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index e9bae69b5..09373ac9e 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -24,7 +24,7 @@ ## generate `LaTeX documents `_ too. import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils, - algorithm + algorithm, parseutils const HtmlExt = "html" @@ -35,16 +35,16 @@ type outHtml, # output is HTML outLatex # output is Latex - TTocEntry{.final.} = object + TTocEntry = object n*: PRstNode refname*, header*: string TMetaEnum* = enum metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion - TRstGenerator* = object of TObject + TRstGenerator* = object of RootObj target*: TOutputTarget - config*: PStringTable + config*: StringTableRef splitAfter*: int # split too long entries in the TOC tocPart*: seq[TTocEntry] hasToc*: bool @@ -57,21 +57,34 @@ type 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. - seenIndexTerms: TTable[string, int] ## \ + seenIndexTerms: Table[string, int] ## \ ## 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. + 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. + + +proc init(p: var CodeBlockParams) = + ## Default initialisation of CodeBlockParams to sane values. + p.startLine = 1 + p.lang = langNone + p.langStr = "" + proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget, - config: PStringTable, filename: string, + config: StringTableRef, filename: string, options: TRstParseOptions, - findFile: TFindFileHandler, - msgHandler: TMsgHandler) = + findFile: TFindFileHandler=nil, + msgHandler: TMsgHandler=nil) = ## Initializes a ``TRstGenerator``. ## ## You need to call this before using a ``TRstGenerator`` with any other - ## procs in this module. Pass a non ``nil`` ``PStringTable`` value as + ## 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() ## <#defaultConfig>_` proc. @@ -103,15 +116,12 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget, ## ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## import packages/docutils/rstgen ## ## var gen: TRstGenerator - ## - ## gen.initRstGenerator(outHtml, defaultConfig(), - ## "filename", {}, nil, nil) - + ## gen.initRstGenerator(outHtml, defaultConfig(), "filename", {}) g.config = config g.target = target g.tocPart = @[] @@ -231,7 +241,7 @@ proc renderRstToOut*(d: var TRstGenerator, n: PRstNode, result: var string) ## ``initRstGenerator`` and parse a rst file with ``rstParse`` from the ## `packages/docutils/rst module `_. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## # ...configure gen and rst vars... ## var generatedHTML = "" @@ -341,13 +351,13 @@ proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) = [id, term]) type - TIndexEntry {.pure, final.} = object + TIndexEntry = 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 {.pure, final.} = TTable[TIndexEntry, seq[TIndexEntry]] ## \ + TIndexedDocs = Table[TIndexEntry, seq[TIndexEntry]] ## \ ## Contains the index sequences for doc types. ## ## The key is a *fake* TIndexEntry which will contain the title of the @@ -597,7 +607,7 @@ proc mergeIndexes*(dir: string): string = ## Merges all index files in `dir` and returns the generated index as HTML. ## ## This proc will first scan `dir` for index files with the ``.idx`` - ## extension previously created by commands like ``nimrod doc|rst2html`` + ## extension previously created by commands like ``nim doc|rst2html`` ## which use the ``--index:on`` switch. These index files are the result of ## calls to `setIndexTerm() <#setIndexTerm>`_ and `writeIndexFile() ## <#writeIndexFile>`_, so they are simple tab separated files. @@ -678,7 +688,7 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = d.tocPart[length].header = tmp dispA(d.target, result, "\n$3", "\\rsth$4{$3}\\label{$2}\n", + "id=\"$2\" href=\"#$2\">$3", "\\rsth$4{$3}\\label{$2}\n", [$n.level, d.tocPart[length].refname, tmp, $chr(n.level - 1 + ord('A'))]) else: dispA(d.target, result, "\n$3", @@ -715,9 +725,9 @@ proc renderTocEntry(d: PDoc, e: TTocEntry, result: var string) = "\\item\\label{$1_toc} $2\\ref{$1}\n", [e.refname, e.header]) proc renderTocEntries*(d: var TRstGenerator, j: var int, lvl: int, - result: var string) = + result: var string) = var tmp = "" - while j <= high(d.tocPart): + while j <= high(d.tocPart): var a = abs(d.tocPart[j].n.level) if a == lvl: renderTocEntry(d, d.tocPart[j], tmp) @@ -727,60 +737,142 @@ proc renderTocEntries*(d: var TRstGenerator, j: var int, lvl: int, else: break if lvl > 1: - dispA(d.target, result, "
        $1
      ", + dispA(d.target, result, "
        $1
      ", "\\begin{enumerate}$1\\end{enumerate}", [tmp]) else: result.add(tmp) - -proc renderImage(d: PDoc, n: PRstNode, result: var string) = + +proc renderImage(d: PDoc, n: PRstNode, result: var string) = + template valid(s): expr = + s.len > 0 and allCharsInSet(s, {'/',':','%','_','\\','\128'..'\xFF'} + + Digits + Letters + WhiteSpace) + var options = "" var s = getFieldValue(n, "scale") - if s != "": dispA(d.target, options, " scale=\"$1\"", " scale=$1", [strip(s)]) + if s.valid: dispA(d.target, options, " scale=\"$1\"", " scale=$1", [strip(s)]) s = getFieldValue(n, "height") - if s != "": dispA(d.target, options, " height=\"$1\"", " height=$1", [strip(s)]) + if s.valid: dispA(d.target, options, " height=\"$1\"", " height=$1", [strip(s)]) s = getFieldValue(n, "width") - if s != "": dispA(d.target, options, " width=\"$1\"", " width=$1", [strip(s)]) + if s.valid: dispA(d.target, options, " width=\"$1\"", " width=$1", [strip(s)]) s = getFieldValue(n, "alt") - if s != "": dispA(d.target, options, " alt=\"$1\"", "", [strip(s)]) + if s.valid: dispA(d.target, options, " alt=\"$1\"", "", [strip(s)]) s = getFieldValue(n, "align") - if s != "": dispA(d.target, options, " align=\"$1\"", "", [strip(s)]) + if s.valid: dispA(d.target, options, " align=\"$1\"", "", [strip(s)]) if options.len > 0: options = dispF(d.target, "$1", "[$1]", [options]) - - dispA(d.target, result, "", "\\includegraphics$2{$1}", - [getArgument(n), options]) + + let arg = getArgument(n) + if arg.valid: + dispA(d.target, result, "", "\\includegraphics$2{$1}", + [arg, options]) if len(n) >= 3: renderRstToOut(d, n.sons[2], result) proc renderSmiley(d: PDoc, n: PRstNode, result: var string) = dispA(d.target, result, - """""", - "\\includegraphics{$1}", [n.text]) + """""", + "\\includegraphics{$1}", [d.config["doc.smiley_format"] % n.text]) +proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) = + ## Parses useful fields which can appear before a code block. + ## + ## This supports the special ``default-language`` internal string generated + ## by the ``rst`` module to communicate a specific default language. + case n.getArgument.toLower + of "number-lines": + params.numberLines = true + # See if the field has a parameter specifying a different line than 1. + var number: int + if parseInt(n.getFieldValue, number) > 0: + params.startLine = number + of "file": + # The ``file`` option is a Nimrod extension to the official spec, it acts + # like it would for other directives like ``raw`` or ``cvs-table``. This + # field is dealt with in ``rst.nim`` which replaces the existing block with + # the referenced file, so we only need to ignore it here to avoid incorrect + # warning messages. + discard + of "default-language": + params.langStr = n.getFieldValue.strip + params.lang = params.langStr.getSourceLanguage + else: + d.msgHandler(d.filename, 1, 0, mwUnsupportedField, n.getArgument) + +proc parseCodeBlockParams(d: PDoc, n: PRstNode): CodeBlockParams = + ## Iterates over all code block fields and returns processed params. + ## + ## Also processes the argument of the directive as the default language. This + ## is done last so as to override any internal communication field variables. + result.init + if n.isNil: + return + assert n.kind == rnCodeBlock + assert(not n.sons[2].isNil) + + # Parse the field list for rendering parameters if there are any. + if not n.sons[1].isNil: + for son in n.sons[1].sons: d.parseCodeBlockField(son, result) + + # Parse the argument and override the language. + result.langStr = strip(getArgument(n)) + if result.langStr != "": + result.lang = getSourceLanguage(result.langStr) + +proc buildLinesHTMLTable(params: CodeBlockParams, code: string): + tuple[beginTable, endTable: string] = + ## Returns the necessary tags to start/end a code block in HTML. + ## + ## If the numberLines has not been used, the tags will default to a simple + ##
       pair. Otherwise it will build a table and insert an initial column
      +  ## with all the line numbers, which requires you to pass the `code` to detect
      +  ## how many lines have to be generated (and starting at which point!).
      +  if not params.numberLines:
      +    result = ("
      ", "
      ") + return + + var codeLines = 1 + code.strip.countLines + assert codeLines > 0 + result.beginTable = """
      """
      +  var line = params.startLine
      +  while codeLines > 0:
      +    result.beginTable.add($line & "\n")
      +    line.inc
      +    codeLines.dec
      +  result.beginTable.add("
      ")
      +  result.endTable = "
      " + proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) = + ## Renders a code block, appending it to `result`. + ## + ## If the code block uses the ``number-lines`` option, a table will be + ## generated with two columns, the first being a list of numbers and the + ## second the code block itself. The code block can use syntax highlighting, + ## which depends on the directive argument specified by the rst input, and + ## may also come from the parser through the internal ``default-language`` + ## option to differentiate between a plain code block and nimrod's code block + ## extension. + assert n.kind == rnCodeBlock if n.sons[2] == nil: return + var params = d.parseCodeBlockParams(n) var m = n.sons[2].sons[0] assert m.kind == rnLeaf - var langstr = strip(getArgument(n)) - var lang: TSourceLanguage - if langstr == "": - lang = langNimrod # default language - else: - lang = getSourceLanguage(langstr) - - dispA(d.target, result, "
      ", "\\begin{rstpre}\n", [])
      -  if lang == langNone:
      -    d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, langstr)
      +
      +  let (blockStart, blockEnd) = params.buildLinesHTMLTable(m.text)
      +
      +  dispA(d.target, result, blockStart, "\\begin{rstpre}\n", [])
      +  if params.lang == langNone:
      +    if len(params.langStr) > 0:
      +      d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, params.langStr)
           for letter in m.text: escChar(d.target, result, letter)
         else:
           var g: TGeneralTokenizer
           initGeneralTokenizer(g, m.text)
           while true: 
      -      getNextToken(g, lang)
      +      getNextToken(g, params.lang)
             case g.kind
             of gtEof: break 
             of gtNone, gtWhitespace: 
      @@ -790,8 +882,8 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
                 esc(d.target, substr(m.text, g.start, g.length+g.start-1)),
                 tokenClassToStr[g.kind]])
           deinitGeneralTokenizer(g)
      -  dispA(d.target, result, "
      ", "\n\\end{rstpre}\n") - + dispA(d.target, result, blockEnd, "\n\\end{rstpre}\n") + proc renderContainer(d: PDoc, n: PRstNode, result: var string) = var tmp = "" renderRstToOut(d, n.sons[2], tmp) @@ -1011,7 +1103,7 @@ proc formatNamedVars*(frmt: string, varnames: openArray[string], inc(i) if i > L-1 or frmt[i] notin {'0'..'9'}: break if j > high(varvalues) + 1: - raise newException(EInvalidValue, "invalid index: " & $j) + raise newException(ValueError, "invalid index: " & $j) num = j add(result, varvalues[j - 1]) of 'A'..'Z', 'a'..'z', '\x80'..'\xFF': @@ -1024,13 +1116,13 @@ proc formatNamedVars*(frmt: string, varnames: openArray[string], if idx >= 0: add(result, varvalues[idx]) else: - raise newException(EInvalidValue, "unknown substitution var: " & id) + raise newException(ValueError, "unknown substitution var: " & id) of '{': var id = "" inc(i) while frmt[i] != '}': if frmt[i] == '\0': - raise newException(EInvalidValue, "'}' expected") + raise newException(ValueError, "'}' expected") add(id, frmt[i]) inc(i) inc(i) # skip } @@ -1038,9 +1130,9 @@ proc formatNamedVars*(frmt: string, varnames: openArray[string], var idx = getVarIdx(varnames, id) if idx >= 0: add(result, varvalues[idx]) else: - raise newException(EInvalidValue, "unknown substitution var: " & id) + raise newException(ValueError, "unknown substitution var: " & id) else: - raise newException(EInvalidValue, "unknown substitution: $" & $frmt[i]) + raise newException(ValueError, "unknown substitution: $" & $frmt[i]) var start = i while i < L: if frmt[i] != '$': inc(i) @@ -1048,10 +1140,10 @@ proc formatNamedVars*(frmt: string, varnames: openArray[string], if i-1 >= start: add(result, substr(frmt, start, i - 1)) -proc defaultConfig*(): PStringTable = +proc defaultConfig*(): StringTableRef = ## Returns a default configuration for embedded HTML generation. ## - ## The returned ``PStringTable`` contains the paramters used by the HTML + ## The returned ``StringTableRef`` contains the paramters used by the HTML ## engine to build the final output. For information on what these parameters ## are and their purpose, please look up the file ``config/nimdoc.cfg`` ## bundled with the compiler. @@ -1109,11 +1201,12 @@ $content """) setConfigVar("doc.body_no_toc", "$moduledesc $content") setConfigVar("doc.file", "$content") + setConfigVar("doc.smiley_format", "/images/smilies/$1.gif") # ---------- forum --------------------------------------------------------- proc rstToHtml*(s: string, options: TRstParseOptions, - config: PStringTable): string = + config: StringTableRef): string = ## Converts an input rst string into embeddable HTML. ## ## This convenience proc parses any input string using rst markup (it doesn't @@ -1123,7 +1216,7 @@ proc rstToHtml*(s: string, options: TRstParseOptions, ## work. For an explanation of the ``config`` parameter see the ## ``initRstGenerator`` proc. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## import packages/docutils/rstgen, strtabs ## ## echo rstToHtml("*Hello* **world**!", {}, diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim index ee04348e8..bc84611a6 100644 --- a/lib/posix/epoll.nim +++ b/lib/posix/epoll.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Dominik Picheta # # See the file "copying.txt", included in this @@ -9,7 +9,7 @@ {.deadCodeElim:on.} -from posix import TSocketHandle +from posix import SocketHandle const EPOLLIN* = 0x00000001 @@ -58,7 +58,7 @@ proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1", ## Same as epoll_create but with an FLAGS parameter. The unused SIZE ## parameter has been dropped. -proc epoll_ctl*(epfd: cint; op: cint; fd: cint | TSocketHandle; event: ptr epoll_event): cint {. +proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr epoll_event): cint {. importc: "epoll_ctl", header: "".} ## Manipulate an epoll instance "epfd". Returns 0 in case of success, ## -1 in case of error ( the "errno" variable will contain the diff --git a/lib/posix/inotify.nim b/lib/posix/inotify.nim index 852eb12fa..c6f0633ff 100644 --- a/lib/posix/inotify.nim +++ b/lib/posix/inotify.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Dominik Picheta # # See the file "copying.txt", included in this diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 9334ceeae..deb120372 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -13,14 +13,14 @@ # net/if, sys/socket, sys/uio, netinet/in, netinet/tcp, netdb ## This is a raw POSIX interface module. It does not not provide any -## convenience: cstrings are used instead of proper Nimrod strings and +## convenience: cstrings are used instead of proper Nim strings and ## return codes indicate errors. If you want exceptions -## and a proper Nimrod-like interface, use the OS module or write a wrapper. +## and a proper Nim-like interface, use the OS module or write a wrapper. ## Coding conventions: ## ALL types are named the same as in the POSIX standard except that they start ## with 'T' or 'P' (if they are pointers) and without the '_t' suffix to be -## consistent with Nimrod conventions. If an identifier is a Nimrod keyword +## consistent with Nim conventions. If an identifier is a Nim keyword ## the \`identifier\` notation is used. ## ## This library relies on the header files of your C compiler. The @@ -29,7 +29,7 @@ {.deadCodeElim:on.} -from times import TTime +from times import Time const hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays @@ -83,8 +83,11 @@ else: ## A type representing a directory stream. type - TSocketHandle* = distinct cint # The type used to represent socket descriptors + SocketHandle* = distinct cint # The type used to represent socket descriptors +{.deprecated: [TSocketHandle: SocketHandle].} + +type Tdirent* {.importc: "struct dirent", header: "", final, pure.} = object ## dirent_t struct d_ino*: Tino ## File serial number. @@ -248,9 +251,9 @@ type ## For a typed memory object, the length in bytes. ## For other file types, the use of this field is ## unspecified. - st_atime*: TTime ## Time of last access. - st_mtime*: TTime ## Time of last data modification. - st_ctime*: TTime ## Time of last status change. + st_atime*: Time ## Time of last access. + st_mtime*: Time ## Time of last data modification. + st_ctime*: Time ## Time of last status change. st_blksize*: Tblksize ## A file system-specific preferred I/O block size ## for this object. In some file system types, this ## may vary from file to file. @@ -291,7 +294,7 @@ type tm_isdst*: cint ## Daylight Savings flag. Ttimespec* {.importc: "struct timespec", header: "", final, pure.} = object ## struct timespec - tv_sec*: TTime ## Seconds. + tv_sec*: Time ## Seconds. tv_nsec*: int ## Nanoseconds. titimerspec* {.importc: "struct itimerspec", header: "", final, pure.} = object ## struct itimerspec @@ -365,12 +368,12 @@ type sched_ss_max_repl*: cint ## Maximum pending replenishments for ## sporadic server. - Ttimeval* {.importc: "struct timeval", header: "", - final, pure.} = object ## struct timeval + Timeval* {.importc: "struct timeval", header: "", + final, pure.} = object ## struct timeval tv_sec*: int ## Seconds. tv_usec*: int ## Microseconds. TFdSet* {.importc: "fd_set", header: "", - final, pure.} = object + final, pure.} = object Tmcontext* {.importc: "mcontext_t", header: "", final, pure.} = object Tucontext* {.importc: "ucontext_t", header: "", @@ -403,11 +406,11 @@ when hasSpawnH: header: "", final, pure.} = object type - TSocklen* {.importc: "socklen_t", header: "".} = cuint + Socklen* {.importc: "socklen_t", header: "".} = cuint TSa_Family* {.importc: "sa_family_t", header: "".} = cint - TSockAddr* {.importc: "struct sockaddr", header: "", - pure, final.} = object ## struct sockaddr + SockAddr* {.importc: "struct sockaddr", header: "", + pure, final.} = object ## struct sockaddr sa_family*: TSa_Family ## Address family. sa_data*: array [0..255, char] ## Socket address (variable-length data). @@ -430,17 +433,17 @@ type Tmsghdr* {.importc: "struct msghdr", pure, final, header: "".} = object ## struct msghdr msg_name*: pointer ## Optional address. - msg_namelen*: TSocklen ## Size of address. + msg_namelen*: Socklen ## Size of address. msg_iov*: ptr TIOVec ## Scatter/gather array. msg_iovlen*: cint ## Members in msg_iov. msg_control*: pointer ## Ancillary data; see below. - msg_controllen*: TSocklen ## Ancillary data buffer len. + msg_controllen*: Socklen ## Ancillary data buffer len. msg_flags*: cint ## Flags on received message. Tcmsghdr* {.importc: "struct cmsghdr", pure, final, header: "".} = object ## struct cmsghdr - cmsg_len*: TSocklen ## Data byte count, including the cmsghdr. + cmsg_len*: Socklen ## Data byte count, including the cmsghdr. cmsg_level*: cint ## Originating protocol. cmsg_type*: cint ## Protocol-specific type. @@ -455,15 +458,15 @@ type TInAddrT* {.importc: "in_addr_t", pure, final, header: "".} = int32 ## unsigned! - TInAddr* {.importc: "struct in_addr", pure, final, + InAddr* {.importc: "struct in_addr", pure, final, header: "".} = object ## struct in_addr s_addr*: TInAddrScalar - Tsockaddr_in* {.importc: "struct sockaddr_in", pure, final, + Sockaddr_in* {.importc: "struct sockaddr_in", pure, final, header: "".} = object ## struct sockaddr_in sin_family*: TSa_Family ## AF_INET. sin_port*: TInPort ## Port number. - sin_addr*: TInAddr ## IP address. + sin_addr*: InAddr ## IP address. TIn6Addr* {.importc: "struct in6_addr", pure, final, header: "".} = object ## struct in6_addr @@ -482,7 +485,7 @@ type ipv6mr_multiaddr*: TIn6Addr ## IPv6 multicast address. ipv6mr_interface*: cint ## Interface index. - Thostent* {.importc: "struct hostent", pure, final, + Hostent* {.importc: "struct hostent", pure, final, header: "".} = object ## struct hostent h_name*: cstring ## Official name of the host. h_aliases*: cstringArray ## A pointer to an array of pointers to @@ -512,8 +515,8 @@ type ## a null pointer. p_proto*: cint ## The protocol number. - TServent* {.importc: "struct servent", pure, final, - header: "".} = object ## struct servent + Servent* {.importc: "struct servent", pure, final, + header: "".} = object ## struct servent s_name*: cstring ## Official name of the service. s_aliases*: cstringArray ## A pointer to an array of pointers to ## alternative service names, terminated by @@ -523,16 +526,16 @@ type s_proto*: cstring ## The name of the protocol to use when ## contacting the service. - Taddrinfo* {.importc: "struct addrinfo", pure, final, + AddrInfo* {.importc: "struct addrinfo", pure, final, header: "".} = object ## struct addrinfo ai_flags*: cint ## Input flags. ai_family*: cint ## Address family of socket. ai_socktype*: cint ## Socket type. ai_protocol*: cint ## Protocol of socket. - ai_addrlen*: TSocklen ## Length of socket address. - ai_addr*: ptr TSockAddr ## Socket address of socket. + ai_addrlen*: Socklen ## Length of socket address. + ai_addr*: ptr SockAddr ## Socket address of socket. ai_canonname*: cstring ## Canonical name of service location. - ai_next*: ptr Taddrinfo ## Pointer to next in list. + ai_next*: ptr AddrInfo ## Pointer to next in list. TPollfd* {.importc: "struct pollfd", pure, final, header: "".} = object ## struct pollfd @@ -542,6 +545,11 @@ type Tnfds* {.importc: "nfds_t", header: "".} = cint +{.deprecated: [TSockaddr_in: Sockaddr_in, TAddrinfo: AddrInfo, + TSockAddr: SockAddr, TSockLen: SockLen, TTimeval: Timeval, + Thostent: Hostent, TServent: Servent, + TInAddr: InAddr].} + var errno* {.importc, header: "".}: cint ## error variable h_errno* {.importc, header: "".}: cint @@ -1756,7 +1764,7 @@ proc ntohl*(a1: int32): int32 {.importc, header: "".} proc ntohs*(a1: int16): int16 {.importc, header: "".} proc inet_addr*(a1: cstring): TInAddrT {.importc, header: "".} -proc inet_ntoa*(a1: TInAddr): cstring {.importc, header: "".} +proc inet_ntoa*(a1: InAddr): cstring {.importc, header: "".} proc inet_ntop*(a1: cint, a2: pointer, a3: cstring, a4: int32): cstring {. importc, header: "".} proc inet_pton*(a1: cint, a2: cstring, a3: pointer): cint {. @@ -1786,7 +1794,7 @@ proc dlopen*(a1: cstring, a2: cint): pointer {.importc, header: "".} proc dlsym*(a1: pointer, a2: cstring): pointer {.importc, header: "".} proc creat*(a1: cstring, a2: TMode): cint {.importc, header: "".} -proc fcntl*(a1: cint | TSocketHandle, a2: cint): cint {.varargs, importc, header: "".} +proc fcntl*(a1: cint | SocketHandle, a2: cint): cint {.varargs, importc, header: "".} proc open*(a1: cstring, a2: cint): cint {.varargs, importc, header: "".} proc posix_fadvise*(a1: cint, a2, a3: TOff, a4: cint): cint {. importc, header: "".} @@ -2050,7 +2058,7 @@ proc access*(a1: cstring, a2: cint): cint {.importc, header: "".} proc alarm*(a1: cint): cint {.importc, header: "".} proc chdir*(a1: cstring): cint {.importc, header: "".} proc chown*(a1: cstring, a2: Tuid, a3: TGid): cint {.importc, header: "".} -proc close*(a1: cint | TSocketHandle): cint {.importc, header: "".} +proc close*(a1: cint | SocketHandle): cint {.importc, header: "".} proc confstr*(a1: cint, a2: cstring, a3: int): int {.importc, header: "".} proc crypt*(a1, a2: cstring): cstring {.importc, header: "".} proc ctermid*(a1: cstring): cstring {.importc, header: "".} @@ -2235,22 +2243,22 @@ proc clock_nanosleep*(a1: TClockId, a2: cint, a3: var Ttimespec, proc clock_settime*(a1: TClockId, a2: var Ttimespec): cint {. importc, header: "".} -proc ctime*(a1: var TTime): cstring {.importc, header: "".} -proc ctime_r*(a1: var TTime, a2: cstring): cstring {.importc, header: "".} -proc difftime*(a1, a2: TTime): cdouble {.importc, header: "".} +proc ctime*(a1: var Time): cstring {.importc, header: "".} +proc ctime_r*(a1: var Time, a2: cstring): cstring {.importc, header: "".} +proc difftime*(a1, a2: Time): cdouble {.importc, header: "".} proc getdate*(a1: cstring): ptr Ttm {.importc, header: "".} -proc gmtime*(a1: var TTime): ptr Ttm {.importc, header: "".} -proc gmtime_r*(a1: var TTime, a2: var Ttm): ptr Ttm {.importc, header: "".} -proc localtime*(a1: var TTime): ptr Ttm {.importc, header: "".} -proc localtime_r*(a1: var TTime, a2: var Ttm): ptr Ttm {.importc, header: "".} -proc mktime*(a1: var Ttm): TTime {.importc, header: "".} -proc timegm*(a1: var Ttm): TTime {.importc, header: "".} +proc gmtime*(a1: var Time): ptr Ttm {.importc, header: "".} +proc gmtime_r*(a1: var Time, a2: var Ttm): ptr Ttm {.importc, header: "".} +proc localtime*(a1: var Time): ptr Ttm {.importc, header: "".} +proc localtime_r*(a1: var Time, a2: var Ttm): ptr Ttm {.importc, header: "".} +proc mktime*(a1: var Ttm): Time {.importc, header: "".} +proc timegm*(a1: var Ttm): Time {.importc, header: "".} proc nanosleep*(a1, a2: var Ttimespec): cint {.importc, header: "".} proc strftime*(a1: cstring, a2: int, a3: cstring, a4: var Ttm): int {.importc, header: "".} proc strptime*(a1, a2: cstring, a3: var Ttm): cstring {.importc, header: "".} -proc time*(a1: var TTime): TTime {.importc, header: "".} +proc time*(a1: var Time): Time {.importc, header: "".} proc timer_create*(a1: var TClockId, a2: var TsigEvent, a3: var Ttimer): cint {.importc, header: "".} proc timer_delete*(a1: var Ttimer): cint {.importc, header: "".} @@ -2330,14 +2338,15 @@ proc strerror*(errnum: cint): cstring {.importc, header: "".} proc hstrerror*(herrnum: cint): cstring {.importc, header: "".} proc FD_CLR*(a1: cint, a2: var TFdSet) {.importc, header: "".} -proc FD_ISSET*(a1: cint | TSocketHandle, a2: var TFdSet): cint {. +proc FD_ISSET*(a1: cint | SocketHandle, a2: var TFdSet): cint {. importc, header: "".} -proc FD_SET*(a1: cint | TSocketHandle, a2: var TFdSet) {.importc, header: "".} +proc FD_SET*(a1: cint | SocketHandle, a2: var TFdSet) {. + importc: "FD_SET", header: "".} proc FD_ZERO*(a1: var TFdSet) {.importc, header: "".} proc pselect*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimespec, a6: var Tsigset): cint {.importc, header: "".} -proc select*(a1: cint | TSocketHandle, a2, a3, a4: ptr TFdSet, a5: ptr Ttimeval): cint {. +proc select*(a1: cint | SocketHandle, a2, a3, a4: ptr TFdSet, a5: ptr Timeval): cint {. importc, header: "".} when hasSpawnH: @@ -2413,48 +2422,48 @@ proc CMSG_FIRSTHDR*(mhdr: ptr Tmsghdr): ptr Tcmsghdr {. importc, header: "".} const - INVALID_SOCKET* = TSocketHandle(-1) + INVALID_SOCKET* = SocketHandle(-1) -proc `==`*(x, y: TSocketHandle): bool {.borrow.} +proc `==`*(x, y: SocketHandle): bool {.borrow.} -proc accept*(a1: TSocketHandle, a2: ptr TSockAddr, a3: ptr TSocklen): TSocketHandle {. +proc accept*(a1: SocketHandle, a2: ptr SockAddr, a3: ptr Socklen): SocketHandle {. importc, header: "".} -proc bindSocket*(a1: TSocketHandle, a2: ptr TSockAddr, a3: TSocklen): cint {. +proc bindSocket*(a1: SocketHandle, a2: ptr SockAddr, a3: Socklen): cint {. importc: "bind", header: "".} ## is Posix's ``bind``, because ``bind`` is a reserved word -proc connect*(a1: TSocketHandle, a2: ptr TSockAddr, a3: TSocklen): cint {. +proc connect*(a1: SocketHandle, a2: ptr SockAddr, a3: Socklen): cint {. importc, header: "".} -proc getpeername*(a1: TSocketHandle, a2: ptr TSockAddr, a3: ptr TSocklen): cint {. +proc getpeername*(a1: SocketHandle, a2: ptr SockAddr, a3: ptr Socklen): cint {. importc, header: "".} -proc getsockname*(a1: TSocketHandle, a2: ptr TSockAddr, a3: ptr TSocklen): cint {. +proc getsockname*(a1: SocketHandle, a2: ptr SockAddr, a3: ptr Socklen): cint {. importc, header: "".} -proc getsockopt*(a1: TSocketHandle, a2, a3: cint, a4: pointer, a5: ptr TSocklen): cint {. +proc getsockopt*(a1: SocketHandle, a2, a3: cint, a4: pointer, a5: ptr Socklen): cint {. importc, header: "".} -proc listen*(a1: TSocketHandle, a2: cint): cint {. +proc listen*(a1: SocketHandle, a2: cint): cint {. importc, header: "".} -proc recv*(a1: TSocketHandle, a2: pointer, a3: int, a4: cint): int {. +proc recv*(a1: SocketHandle, a2: pointer, a3: int, a4: cint): int {. importc, header: "".} -proc recvfrom*(a1: TSocketHandle, a2: pointer, a3: int, a4: cint, - a5: ptr TSockAddr, a6: ptr TSocklen): int {. +proc recvfrom*(a1: SocketHandle, a2: pointer, a3: int, a4: cint, + a5: ptr SockAddr, a6: ptr Socklen): int {. importc, header: "".} -proc recvmsg*(a1: TSocketHandle, a2: ptr Tmsghdr, a3: cint): int {. +proc recvmsg*(a1: SocketHandle, a2: ptr Tmsghdr, a3: cint): int {. importc, header: "".} -proc send*(a1: TSocketHandle, a2: pointer, a3: int, a4: cint): int {. +proc send*(a1: SocketHandle, a2: pointer, a3: int, a4: cint): int {. importc, header: "".} -proc sendmsg*(a1: TSocketHandle, a2: ptr Tmsghdr, a3: cint): int {. +proc sendmsg*(a1: SocketHandle, a2: ptr Tmsghdr, a3: cint): int {. importc, header: "".} -proc sendto*(a1: TSocketHandle, a2: pointer, a3: int, a4: cint, a5: ptr TSockAddr, - a6: TSocklen): int {. +proc sendto*(a1: SocketHandle, a2: pointer, a3: int, a4: cint, a5: ptr SockAddr, + a6: Socklen): int {. importc, header: "".} -proc setsockopt*(a1: TSocketHandle, a2, a3: cint, a4: pointer, a5: TSocklen): cint {. +proc setsockopt*(a1: SocketHandle, a2, a3: cint, a4: pointer, a5: Socklen): cint {. importc, header: "".} -proc shutdown*(a1: TSocketHandle, a2: cint): cint {. +proc shutdown*(a1: SocketHandle, a2: cint): cint {. importc, header: "".} -proc socket*(a1, a2, a3: cint): TSocketHandle {. +proc socket*(a1, a2, a3: cint): SocketHandle {. importc, header: "".} proc sockatmark*(a1: cint): cint {. importc, header: "".} @@ -2508,21 +2517,21 @@ proc endhostent*() {.importc, header: "".} proc endnetent*() {.importc, header: "".} proc endprotoent*() {.importc, header: "".} proc endservent*() {.importc, header: "".} -proc freeaddrinfo*(a1: ptr Taddrinfo) {.importc, header: "".} +proc freeaddrinfo*(a1: ptr AddrInfo) {.importc, header: "".} proc gai_strerror*(a1: cint): cstring {.importc, header: "".} -proc getaddrinfo*(a1, a2: cstring, a3: ptr Taddrinfo, - a4: var ptr Taddrinfo): cint {.importc, header: "".} +proc getaddrinfo*(a1, a2: cstring, a3: ptr AddrInfo, + a4: var ptr AddrInfo): cint {.importc, header: "".} -proc gethostbyaddr*(a1: pointer, a2: TSocklen, a3: cint): ptr Thostent {. +proc gethostbyaddr*(a1: pointer, a2: Socklen, a3: cint): ptr Hostent {. importc, header: "".} -proc gethostbyname*(a1: cstring): ptr Thostent {.importc, header: "".} -proc gethostent*(): ptr Thostent {.importc, header: "".} +proc gethostbyname*(a1: cstring): ptr Hostent {.importc, header: "".} +proc gethostent*(): ptr Hostent {.importc, header: "".} -proc getnameinfo*(a1: ptr TSockAddr, a2: TSocklen, - a3: cstring, a4: TSocklen, a5: cstring, - a6: TSocklen, a7: cint): cint {.importc, header: "".} +proc getnameinfo*(a1: ptr SockAddr, a2: Socklen, + a3: cstring, a4: Socklen, a5: cstring, + a6: Socklen, a7: cint): cint {.importc, header: "".} proc getnetbyaddr*(a1: int32, a2: cint): ptr Tnetent {.importc, header: "".} proc getnetbyname*(a1: cstring): ptr Tnetent {.importc, header: "".} @@ -2532,10 +2541,10 @@ proc getprotobyname*(a1: cstring): ptr TProtoent {.importc, header: "". proc getprotobynumber*(a1: cint): ptr TProtoent {.importc, header: "".} proc getprotoent*(): ptr TProtoent {.importc, header: "".} -proc getservbyname*(a1, a2: cstring): ptr TServent {.importc, header: "".} -proc getservbyport*(a1: cint, a2: cstring): ptr TServent {. +proc getservbyname*(a1, a2: cstring): ptr Servent {.importc, header: "".} +proc getservbyport*(a1: cint, a2: cstring): ptr Servent {. importc, header: "".} -proc getservent*(): ptr TServent {.importc, header: "".} +proc getservent*(): ptr Servent {.importc, header: "".} proc sethostent*(a1: cint) {.importc, header: "".} proc setnetent*(a1: cint) {.importc, header: "".} @@ -2548,7 +2557,7 @@ proc poll*(a1: ptr TPollfd, a2: Tnfds, a3: int): cint {. proc realpath*(name, resolved: cstring): cstring {. importc: "realpath", header: "".} -proc utimes*(path: cstring, times: ptr array [2, Ttimeval]): int {. +proc utimes*(path: cstring, times: ptr array [2, Timeval]): int {. importc: "utimes", header: "".} ## Sets file access and modification times. ## diff --git a/lib/prelude.nim b/lib/prelude.nim index 50b4d4092..940864207 100644 --- a/lib/prelude.nim +++ b/lib/prelude.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -10,12 +10,12 @@ ## This is an include file that simply imports common modules for your ## convenience: ## -## .. code-block:: nimrod +## .. code-block:: nim ## include prelude ## ## Same as: ## -## .. code-block:: nimrod +## .. code-block:: nim ## import os, strutils, times, parseutils, parseopt, hashes, tables, sets import os, strutils, times, parseutils, parseopt, hashes, tables, sets diff --git a/lib/pure/actors.nim b/lib/pure/actors.nim index 2d902debe..f2c50ce4c 100644 --- a/lib/pure/actors.nim +++ b/lib/pure/actors.nim @@ -1,19 +1,19 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## `Actor`:idx: support for Nimrod. An actor is implemented as a thread with +## `Actor`:idx: support for Nim. An actor is implemented as a thread with ## a channel as its inbox. This module requires the ``--threads:on`` ## command line switch. ## ## Example: ## -## .. code-block:: nimrod +## .. code-block:: nim ## ## var ## a: TActorPool[int, void] @@ -21,6 +21,11 @@ ## for i in 0 .. < 300: ## a.spawn(i, proc (x: int) {.thread.} = echo x) ## a.join() +## +## **Note**: This whole module is deprecated. Use `threadpool` and ``spawn`` +## instead. + +{.deprecated.} from os import sleep diff --git a/lib/pure/actors.nimrod.cfg b/lib/pure/actors.nim.cfg similarity index 100% rename from lib/pure/actors.nimrod.cfg rename to lib/pure/actors.nim.cfg diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 89c83a8a4..0358a9a81 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -10,10 +10,13 @@ ## This module implements some common generic algorithms. type - TSortOrder* = enum ## sort order + SortOrder* = enum ## sort order Descending, Ascending -proc `*`*(x: int, order: TSortOrder): int {.inline.} = +{.deprecated: [TSortOrder: SortOrder].} + + +proc `*`*(x: int, order: SortOrder): int {.inline.} = ## flips `x` if ``order == Descending``; ## if ``order == Ascending`` then `x` is returned. ## `x` is supposed to be the result of a comparator, ie ``< 0`` for @@ -69,7 +72,7 @@ proc smartBinarySearch*[T](a: openArray[T], key: T): int = const onlySafeCode = true -proc lowerBound*[T](a: openarray[T], key: T, cmp: proc(x,y: T): int {.closure.}): int = +proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.}): int = ## same as binarySearch except that if key is not in `a` then this ## returns the location where `key` would be if it were. In other ## words if you have a sorted sequence and you call insert(thing, elm, lowerBound(thing, elm)) @@ -98,9 +101,9 @@ proc lowerBound*[T](a: openarray[T], key: T, cmp: proc(x,y: T): int {.closure.}) else: count = step -proc lowerBound*[T](a: openarray[T], key: T): int = lowerBound(a, key, cmp[T]) +proc lowerBound*[T](a: openArray[T], key: T): int = lowerBound(a, key, cmp[T]) proc merge[T](a, b: var openArray[T], lo, m, hi: int, - cmp: proc (x, y: T): int {.closure.}, order: TSortOrder) = + cmp: proc (x, y: T): int {.closure.}, order: SortOrder) = template `<-` (a, b: expr) = when false: a = b @@ -147,16 +150,16 @@ proc merge[T](a, b: var openArray[T], lo, m, hi: int, proc sort*[T](a: var openArray[T], cmp: proc (x, y: T): int {.closure.}, - order = TSortOrder.Ascending) = - ## Default Nimrod sort. The sorting is guaranteed to be stable and + order = SortOrder.Ascending) = + ## Default Nim sort. The sorting is guaranteed to be stable and ## the worst case is guaranteed to be O(n log n). ## The current implementation uses an iterative ## mergesort to achieve this. It uses a temporary sequence of - ## length ``a.len div 2``. Currently Nimrod does not support a + ## length ``a.len div 2``. Currently Nim does not support a ## sensible default argument for ``cmp``, so you have to provide one ## of your own. However, the ``system.cmp`` procs can be used: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## sort(myIntArray, system.cmp[int]) ## @@ -167,7 +170,7 @@ proc sort*[T](a: var openArray[T], ## You can inline adhoc comparison procs with the `do notation ## `_. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## people.sort do (x, y: Person) -> int: ## result = cmp(x.surname, y.surname) @@ -184,7 +187,7 @@ proc sort*[T](a: var openArray[T], dec(m, s*2) s = s*2 -proc product*[T](x: openarray[seq[T]]): seq[seq[T]] = +proc product*[T](x: openArray[seq[T]]): seq[seq[T]] = ## produces the Cartesian product of the array. Warning: complexity ## may explode. result = @[] diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 0ea8ef43b..4bdce9cfb 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Dominik Picheta # # See the file "copying.txt", included in this @@ -13,45 +13,143 @@ import os, oids, tables, strutils, macros, times import rawsockets, net -export TPort, TSocketFlags +export Port, SocketFlag #{.injectStmt: newGcInvariant().} ## AsyncDispatch -## ------------- +## ************* ## -## This module implements a brand new dispatcher based on Futures. -## On Windows IOCP is used and on other operating systems the ``selectors`` -## module is used instead. +## This module implements asynchronous IO. This includes a dispatcher, +## a ``Future`` type implementation, and an ``async`` macro which allows +## asynchronous code to be written in a synchronous style with the ``await`` +## keyword. ## -## **Note:** This module is still largely experimental. +## The dispatcher acts as a kind of event loop. You must call ``poll`` on it +## (or a function which does so for you such as ``waitFor`` or ``runForever``) +## in order to poll for any outstanding events. The underlying implementation +## is based on epoll on Linux, IO Completion Ports on Windows and select on +## other operating systems. +## +## The ``poll`` function will not, on its own, return any events. Instead +## an appropriate ``Future`` object will be completed. A ``Future`` is a +## type which holds a value which is not yet available, but which *may* be +## available in the future. You can check whether a future is finished +## by using the ``finished`` function. When a future is finished it means that +## either the value that it holds is now available or it holds an error instead. +## The latter situation occurs when the operation to complete a future fails +## with an exception. You can distinguish between the two situations with the +## ``failed`` function. +## +## Future objects can also store a callback procedure which will be called +## automatically once the future completes. +## +## Futures therefore can be thought of as an implementation of the proactor +## pattern. In this +## pattern you make a request for an action, and once that action is fulfilled +## a future is completed with the result of that action. Requests can be +## made by calling the appropriate functions. For example: calling the ``recv`` +## function will create a request for some data to be read from a socket. The +## future which the ``recv`` function returns will then complete once the +## requested amount of data is read **or** an exception occurs. +## +## Code to read some data from a socket may look something like this: +## +## .. code-block::nim +## var future = socket.recv(100) +## future.callback = +## proc () = +## echo(future.read) +## +## All asynchronous functions returning a ``Future`` will not block. They +## will not however return immediately. An asynchronous function will have +## code which will be executed before an asynchronous request is made, in most +## cases this code sets up the request. +## +## In the above example, the ``recv`` function will return a brand new +## ``Future`` instance once the request for data to be read from the socket +## is made. This ``Future`` instance will complete once the requested amount +## of data is read, in this case it is 100 bytes. The second line sets a +## callback on this future which will be called once the future completes. +## All the callback does is write the data stored in the future to ``stdout``. +## The ``read`` function is used for this and it checks whether the future +## completes with an error for you (if it did it will simply raise the +## error), if there is no error however it returns the value of the future. +## +## Asynchronous procedures +## ----------------------- +## +## Asynchronous procedures remove the pain of working with callbacks. They do +## this by allowing you to write asynchronous code the same way as you would +## write synchronous code. +## +## An asynchronous procedure is marked using the ``{.async.}`` pragma. +## When marking a procedure with the ``{.async.}`` pragma it must have a +## ``Future[T]`` return type or no return type at all. If you do not specify +## a return type then ``Future[void]`` is assumed. +## +## Inside asynchronous procedures ``await`` can be used to call any +## procedures which return a +## ``Future``; this includes asynchronous procedures. When a procedure is +## "awaited", the asynchronous procedure it is awaited in will +## suspend its execution +## until the awaited procedure's Future completes. At which point the +## asynchronous procedure will resume its execution. During the period +## when an asynchronous procedure is suspended other asynchronous procedures +## will be run by the dispatcher. +## +## The ``await`` call may be used in many contexts. It can be used on the right +## hand side of a variable declaration: ``var data = await socket.recv(100)``, +## in which case the variable will be set to the value of the future +## automatically. It can be used to await a ``Future`` object, and it can +## be used to await a procedure returning a ``Future[void]``: +## ``await socket.send("foobar")``. +## +## Discarding futures +## ------------------ +## +## Futures should **never** be discarded. This is because they may contain +## errors. If you do not care for the result of a Future then you should +## use the ``asyncCheck`` procedure instead of the ``discard`` keyword. +## +## Examples +## -------- +## +## For examples take a look at the documentation for the modules implementing +## asynchronous IO. A good place to start is the +## `asyncnet module `_. +## +## Limitations/Bugs +## ---------------- +## +## * ``except`` statement (without `try`) does not work inside async procedures. +## * The effect system (``raises: []``) does not work with async procedures. +## * Can't await in a ``except`` body -# TODO: ``except`` statement (without `try`) does not work. -# TODO: Multiple exception names in a ``except`` don't work. -# TODO: The effect system (raises: []) has trouble with my try transformation. -# TODO: Can't await in a 'except' body -# TODO: getCurrentException(Msg) don't work # TODO: Check if yielded future is nil and throw a more meaningful exception # -- Futures type - PFutureBase* = ref object of PObject + FutureBase* = ref object of RootObj ## Untyped future. cb: proc () {.closure,gcsafe.} finished: bool - error*: ref EBase + error*: ref Exception ## Stored exception errorStackTrace*: string when not defined(release): stackTrace: string ## For debugging purposes only. id: int fromProc: string - PFuture*[T] = ref object of PFutureBase - value: T + Future*[T] = ref object of FutureBase ## Typed future. + value: T ## Stored value -var currentID* = 0 -proc newFuture*[T](fromProc: string = "unspecified"): PFuture[T] = +{.deprecated: [PFutureBase: FutureBase, PFuture: Future].} + + +var currentID = 0 +proc newFuture*[T](fromProc: string = "unspecified"): Future[T] = ## Creates a new future. ## ## Specifying ``fromProc``, which is a string specifying the name of the proc @@ -64,7 +162,7 @@ proc newFuture*[T](fromProc: string = "unspecified"): PFuture[T] = result.fromProc = fromProc currentID.inc() -proc checkFinished[T](future: PFuture[T]) = +proc checkFinished[T](future: Future[T]) = when not defined(release): if future.finished: echo("<-----> ", future.id, " ", future.fromProc) @@ -77,7 +175,7 @@ proc checkFinished[T](future: PFuture[T]) = echo getStackTrace() assert false -proc complete*[T](future: PFuture[T], val: T) = +proc complete*[T](future: Future[T], val: T) = ## Completes ``future`` with value ``val``. #assert(not future.finished, "Future already finished, cannot finish twice.") checkFinished(future) @@ -87,7 +185,7 @@ proc complete*[T](future: PFuture[T], val: T) = if future.cb != nil: future.cb() -proc complete*(future: PFuture[void]) = +proc complete*(future: Future[void]) = ## Completes a void ``future``. #assert(not future.finished, "Future already finished, cannot finish twice.") checkFinished(future) @@ -96,7 +194,7 @@ proc complete*(future: PFuture[void]) = if future.cb != nil: future.cb() -proc fail*[T](future: PFuture[T], error: ref EBase) = +proc fail*[T](future: Future[T], error: ref Exception) = ## Completes ``future`` with ``error``. #assert(not future.finished, "Future already finished, cannot finish twice.") checkFinished(future) @@ -112,8 +210,9 @@ proc fail*[T](future: PFuture[T], error: ref EBase) = # TODO: This may turn out to be a bad idea. # Turns out this is a bad idea. #raise error + discard -proc `callback=`*(future: PFutureBase, cb: proc () {.closure,gcsafe.}) = +proc `callback=`*(future: FutureBase, cb: proc () {.closure,gcsafe.}) = ## Sets the callback proc to be called when the future completes. ## ## If future has already completed then ``cb`` will be called immediately. @@ -124,25 +223,26 @@ proc `callback=`*(future: PFutureBase, cb: proc () {.closure,gcsafe.}) = if future.finished: future.cb() -proc `callback=`*[T](future: PFuture[T], - cb: proc (future: PFuture[T]) {.closure,gcsafe.}) = +proc `callback=`*[T](future: Future[T], + cb: proc (future: Future[T]) {.closure,gcsafe.}) = ## Sets the callback proc to be called when the future completes. ## ## If future has already completed then ``cb`` will be called immediately. future.callback = proc () = cb(future) -proc echoOriginalStackTrace[T](future: PFuture[T]) = +proc echoOriginalStackTrace[T](future: Future[T]) = # TODO: Come up with something better. when not defined(release): echo("Original stack trace in ", future.fromProc, ":") - if not future.errorStackTrace.isNil() and future.errorStackTrace != "": + if not future.errorStackTrace.isNil and future.errorStackTrace != "": echo(future.errorStackTrace) else: echo("Empty or nil stack trace.") + echo("Continuing...") -proc read*[T](future: PFuture[T]): T = +proc read*[T](future: Future[T]): T = ## Retrieves the value of ``future``. Future must be finished otherwise - ## this function will fail with a ``EInvalidValue`` exception. + ## this function will fail with a ``ValueError`` exception. ## ## If the result of the future is an error then that error will be raised. if future.finished: @@ -153,24 +253,28 @@ proc read*[T](future: PFuture[T]): T = return future.value else: # TODO: Make a custom exception type for this? - raise newException(EInvalidValue, "Future still in progress.") + raise newException(ValueError, "Future still in progress.") -proc readError*[T](future: PFuture[T]): ref EBase = +proc readError*[T](future: Future[T]): ref Exception = + ## Retrieves the exception stored in ``future``. + ## + ## An ``ValueError`` exception will be thrown if no exception exists + ## in the specified Future. if future.error != nil: return future.error else: - raise newException(EInvalidValue, "No error in future.") + raise newException(ValueError, "No error in future.") -proc finished*[T](future: PFuture[T]): bool = +proc finished*[T](future: Future[T]): bool = ## Determines whether ``future`` has completed. ## ## ``True`` may indicate an error or a value. Use ``failed`` to distinguish. future.finished -proc failed*(future: PFutureBase): bool = +proc failed*(future: FutureBase): bool = ## Determines whether ``future`` completed with an error. - future.error != nil + return future.error != nil -proc asyncCheck*[T](future: PFuture[T]) = +proc asyncCheck*[T](future: Future[T]) = ## Sets a callback on ``future`` which raises an exception if the future ## finished with an error. ## @@ -181,7 +285,7 @@ proc asyncCheck*[T](future: PFuture[T]) = echoOriginalStackTrace(future) raise future.error -proc `and`*[T, Y](fut1: PFuture[T], fut2: PFuture[Y]): PFuture[void] = +proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = ## Returns a future which will complete once both ``fut1`` and ``fut2`` ## complete. var retFuture = newFuture[void]("asyncdispatch.`and`") @@ -193,7 +297,7 @@ proc `and`*[T, Y](fut1: PFuture[T], fut2: PFuture[Y]): PFuture[void] = if fut1.finished: retFuture.complete() return retFuture -proc `or`*[T, Y](fut1: PFuture[T], fut2: PFuture[Y]): PFuture[void] = +proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = ## Returns a future which will complete once either ``fut1`` or ``fut2`` ## complete. var retFuture = newFuture[void]("asyncdispatch.`or`") @@ -204,8 +308,8 @@ proc `or`*[T, Y](fut1: PFuture[T], fut2: PFuture[Y]): PFuture[void] = return retFuture type - PDispatcherBase = ref object of PObject - timers: seq[tuple[finishAt: float, fut: PFuture[void]]] + PDispatcherBase = ref object of RootRef + timers: seq[tuple[finishAt: float, fut: Future[void]]] proc processTimers(p: PDispatcherBase) = var oldTimers = p.timers @@ -219,21 +323,21 @@ proc processTimers(p: PDispatcherBase) = when defined(windows) or defined(nimdoc): import winlean, sets, hashes type - TCompletionKey = dword + TCompletionKey = Dword TCompletionData* = object - sock: TAsyncFD - cb: proc (sock: TAsyncFD, bytesTransferred: DWORD, - errcode: TOSErrorCode) {.closure,gcsafe.} + fd*: TAsyncFD # TODO: Rename this. + cb*: proc (fd: TAsyncFD, bytesTransferred: Dword, + errcode: OSErrorCode) {.closure,gcsafe.} PDispatcher* = ref object of PDispatcherBase ioPort: THandle - handles: TSet[TAsyncFD] + handles: HashSet[TAsyncFD] TCustomOverlapped = object of TOVERLAPPED data*: TCompletionData - PCustomOverlapped = ref TCustomOverlapped + PCustomOverlapped* = ref TCustomOverlapped TAsyncFD* = distinct int @@ -243,7 +347,7 @@ when defined(windows) or defined(nimdoc): proc newDispatcher*(): PDispatcher = ## Creates a new Dispatcher instance. new result - result.ioPort = CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1) + result.ioPort = createIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1) result.handles = initSet[TAsyncFD]() result.timers = @[] @@ -253,19 +357,19 @@ when defined(windows) or defined(nimdoc): if gDisp.isNil: gDisp = newDispatcher() result = gDisp - proc register*(sock: TAsyncFD) = - ## Registers ``sock`` with the dispatcher. + proc register*(fd: TAsyncFD) = + ## Registers ``fd`` with the dispatcher. let p = getGlobalDispatcher() - if CreateIOCompletionPort(sock.THandle, p.ioPort, - cast[TCompletionKey](sock), 1) == 0: - osError(osLastError()) - p.handles.incl(sock) + if createIoCompletionPort(fd.THandle, p.ioPort, + cast[TCompletionKey](fd), 1) == 0: + raiseOSError(osLastError()) + p.handles.incl(fd) - proc verifyPresence(sock: TAsyncFD) = - ## Ensures that socket has been registered with the dispatcher. + proc verifyPresence(fd: TAsyncFD) = + ## Ensures that file descriptor has been registered with the dispatcher. let p = getGlobalDispatcher() - if sock notin p.handles: - raise newException(EInvalidValue, + if fd notin p.handles: + raise newException(ValueError, "Operation performed on a socket which has not been registered with" & " the dispatcher yet.") @@ -273,40 +377,40 @@ when defined(windows) or defined(nimdoc): ## Waits for completion events and processes them. let p = getGlobalDispatcher() if p.handles.len == 0 and p.timers.len == 0: - raise newException(EInvalidValue, + raise newException(ValueError, "No handles or timers registered in dispatcher.") let llTimeout = if timeout == -1: winlean.INFINITE else: timeout.int32 - var lpNumberOfBytesTransferred: DWORD + var lpNumberOfBytesTransferred: Dword var lpCompletionKey: ULONG var customOverlapped: PCustomOverlapped - let res = GetQueuedCompletionStatus(p.ioPort, + let res = getQueuedCompletionStatus(p.ioPort, addr lpNumberOfBytesTransferred, addr lpCompletionKey, - cast[ptr POverlapped](addr customOverlapped), llTimeout).bool + cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool # http://stackoverflow.com/a/12277264/492186 # 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.sock == lpCompletionKey.TAsyncFD + assert customOverlapped.data.fd == lpCompletionKey.TAsyncFD - customOverlapped.data.cb(customOverlapped.data.sock, - lpNumberOfBytesTransferred, TOSErrorCode(-1)) + customOverlapped.data.cb(customOverlapped.data.fd, + lpNumberOfBytesTransferred, OSErrorCode(-1)) GC_unref(customOverlapped) else: let errCode = osLastError() if customOverlapped != nil: - assert customOverlapped.data.sock == lpCompletionKey.TAsyncFD - customOverlapped.data.cb(customOverlapped.data.sock, + assert customOverlapped.data.fd == lpCompletionKey.TAsyncFD + customOverlapped.data.cb(customOverlapped.data.fd, lpNumberOfBytesTransferred, errCode) GC_unref(customOverlapped) else: if errCode.int32 == WAIT_TIMEOUT: # Timed out discard - else: osError(errCode) + else: raiseOSError(errCode) # Timer processing. processTimers(p) @@ -315,105 +419,105 @@ when defined(windows) or defined(nimdoc): var acceptExPtr: pointer = nil var getAcceptExSockAddrsPtr: pointer = nil - proc initPointer(s: TSocketHandle, func: var pointer, guid: var TGUID): bool = + proc initPointer(s: SocketHandle, fun: var pointer, guid: var TGUID): bool = # Ref: https://github.com/powdahound/twisted/blob/master/twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c - var bytesRet: DWord - func = nil + var bytesRet: Dword + fun = nil result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid, - sizeof(TGUID).dword, addr func, sizeof(pointer).DWORD, + sizeof(TGUID).Dword, addr fun, sizeof(pointer).Dword, addr bytesRet, nil, nil) == 0 proc initAll() = let dummySock = newRawSocket() if not initPointer(dummySock, connectExPtr, WSAID_CONNECTEX): - osError(osLastError()) + raiseOSError(osLastError()) if not initPointer(dummySock, acceptExPtr, WSAID_ACCEPTEX): - osError(osLastError()) + raiseOSError(osLastError()) if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS): - osError(osLastError()) + raiseOSError(osLastError()) - proc connectEx(s: TSocketHandle, name: ptr TSockAddr, namelen: cint, - lpSendBuffer: pointer, dwSendDataLength: dword, - lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool = - if connectExPtr.isNil: raise newException(EInvalidValue, "Need to initialise ConnectEx().") - let func = - cast[proc (s: TSocketHandle, name: ptr TSockAddr, namelen: cint, - lpSendBuffer: pointer, dwSendDataLength: dword, - lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool {.stdcall,gcsafe.}](connectExPtr) + proc connectEx(s: SocketHandle, name: ptr SockAddr, namelen: cint, + lpSendBuffer: pointer, dwSendDataLength: Dword, + lpdwBytesSent: PDword, lpOverlapped: POVERLAPPED): bool = + if connectExPtr.isNil: raise newException(ValueError, "Need to initialise ConnectEx().") + let fun = + cast[proc (s: SocketHandle, name: ptr SockAddr, namelen: cint, + lpSendBuffer: pointer, dwSendDataLength: Dword, + lpdwBytesSent: PDword, lpOverlapped: POVERLAPPED): bool {.stdcall,gcsafe.}](connectExPtr) - result = func(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent, + result = fun(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent, lpOverlapped) - proc acceptEx(listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer, + proc acceptEx(listenSock, acceptSock: SocketHandle, lpOutputBuffer: pointer, dwReceiveDataLength, dwLocalAddressLength, - dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD, - lpOverlapped: POverlapped): bool = - if acceptExPtr.isNil: raise newException(EInvalidValue, "Need to initialise AcceptEx().") - let func = - cast[proc (listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer, + dwRemoteAddressLength: Dword, lpdwBytesReceived: PDword, + lpOverlapped: POVERLAPPED): bool = + if acceptExPtr.isNil: raise newException(ValueError, "Need to initialise AcceptEx().") + let fun = + cast[proc (listenSock, acceptSock: SocketHandle, lpOutputBuffer: pointer, dwReceiveDataLength, dwLocalAddressLength, - dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD, - lpOverlapped: POverlapped): bool {.stdcall,gcsafe.}](acceptExPtr) - result = func(listenSock, acceptSock, lpOutputBuffer, dwReceiveDataLength, + dwRemoteAddressLength: Dword, lpdwBytesReceived: PDword, + lpOverlapped: POVERLAPPED): bool {.stdcall,gcsafe.}](acceptExPtr) + result = fun(listenSock, acceptSock, lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived, lpOverlapped) proc getAcceptExSockaddrs(lpOutputBuffer: pointer, - dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength: DWORD, - LocalSockaddr: ptr ptr TSockAddr, LocalSockaddrLength: lpint, - RemoteSockaddr: ptr ptr TSockAddr, RemoteSockaddrLength: lpint) = + dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength: Dword, + LocalSockaddr: ptr ptr SockAddr, LocalSockaddrLength: LPInt, + RemoteSockaddr: ptr ptr SockAddr, RemoteSockaddrLength: LPInt) = if getAcceptExSockAddrsPtr.isNil: - raise newException(EInvalidValue, "Need to initialise getAcceptExSockAddrs().") + raise newException(ValueError, "Need to initialise getAcceptExSockAddrs().") - let func = + let fun = cast[proc (lpOutputBuffer: pointer, dwReceiveDataLength, dwLocalAddressLength, - dwRemoteAddressLength: DWORD, LocalSockaddr: ptr ptr TSockAddr, - LocalSockaddrLength: lpint, RemoteSockaddr: ptr ptr TSockAddr, - RemoteSockaddrLength: lpint) {.stdcall,gcsafe.}](getAcceptExSockAddrsPtr) + dwRemoteAddressLength: Dword, LocalSockaddr: ptr ptr SockAddr, + LocalSockaddrLength: LPInt, RemoteSockaddr: ptr ptr SockAddr, + RemoteSockaddrLength: LPInt) {.stdcall,gcsafe.}](getAcceptExSockAddrsPtr) - func(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength, + fun(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength, RemoteSockaddr, RemoteSockaddrLength) - proc connect*(socket: TAsyncFD, address: string, port: TPort, - af = AF_INET): PFuture[void] = + proc connect*(socket: TAsyncFD, address: string, port: Port, + af = AF_INET): Future[void] = ## Connects ``socket`` to server at ``address:port``. ## - ## Returns a ``PFuture`` which will complete when the connection succeeds + ## Returns a ``Future`` which will complete when the connection succeeds ## or an error occurs. verifyPresence(socket) var retFuture = newFuture[void]("connect") # Apparently ``ConnectEx`` expects the socket to be initially bound: - var saddr: Tsockaddr_in + var saddr: Sockaddr_in saddr.sin_family = int16(toInt(af)) saddr.sin_port = 0 saddr.sin_addr.s_addr = INADDR_ANY - if bindAddr(socket.TSocketHandle, cast[ptr TSockAddr](addr(saddr)), - sizeof(saddr).TSockLen) < 0'i32: - osError(osLastError()) + if bindAddr(socket.SocketHandle, cast[ptr SockAddr](addr(saddr)), + sizeof(saddr).SockLen) < 0'i32: + raiseOSError(osLastError()) var aiList = getAddrInfo(address, port, af) var success = false - var lastError: TOSErrorCode + var lastError: OSErrorCode var it = aiList while it != nil: # "the OVERLAPPED structure must remain valid until the I/O completes" # http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx var ol = PCustomOverlapped() GC_ref(ol) - ol.data = TCompletionData(sock: socket, cb: - proc (sock: TAsyncFD, bytesCount: DWord, errcode: TOSErrorCode) = + ol.data = TCompletionData(fd: socket, cb: + proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) = if not retFuture.finished: - if errcode == TOSErrorCode(-1): + if errcode == OSErrorCode(-1): retFuture.complete() else: - retFuture.fail(newException(EOS, osErrorMsg(errcode))) + retFuture.fail(newException(OSError, osErrorMsg(errcode))) ) - var ret = connectEx(socket.TSocketHandle, it.ai_addr, - sizeof(TSockAddrIn).cint, nil, 0, nil, - cast[POverlapped](ol)) + var ret = connectEx(socket.SocketHandle, it.ai_addr, + sizeof(Sockaddr_in).cint, nil, 0, nil, + cast[POVERLAPPED](ol)) if ret: # Request to connect completed immediately. success = true @@ -435,15 +539,17 @@ when defined(windows) or defined(nimdoc): dealloc(aiList) if not success: - retFuture.fail(newException(EOS, osErrorMsg(lastError))) + retFuture.fail(newException(OSError, osErrorMsg(lastError))) return retFuture proc recv*(socket: TAsyncFD, size: int, - flags = {TSocketFlags.SafeDisconn}): PFuture[string] = + 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 ## read, or the socket has disconnected in which case the future will ## complete with a value of ``""``. + ## + ## **Warning**: The ``Peek`` socket flag is not supported on Windows. # Things to note: @@ -453,19 +559,21 @@ when defined(windows) or defined(nimdoc): # '\0' in the message currently signifies a socket disconnect. Who # knows what will happen when someone sends that to our socket. verifyPresence(socket) + assert SocketFlag.Peek notin flags, "Peek not supported on Windows." + var retFuture = newFuture[string]("recv") var dataBuf: TWSABuf dataBuf.buf = cast[cstring](alloc0(size)) dataBuf.len = size - var bytesReceived: DWord - var flagsio = flags.toOSFlags().DWord + var bytesReceived: Dword + var flagsio = flags.toOSFlags().Dword var ol = PCustomOverlapped() GC_ref(ol) - ol.data = TCompletionData(sock: socket, cb: - proc (sock: TAsyncFD, bytesCount: DWord, errcode: TOSErrorCode) = + ol.data = TCompletionData(fd: socket, cb: + proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) = if not retFuture.finished: - if errcode == TOSErrorCode(-1): + if errcode == OSErrorCode(-1): if bytesCount == 0 and dataBuf.buf[0] == '\0': retFuture.complete("") else: @@ -477,14 +585,14 @@ when defined(windows) or defined(nimdoc): if flags.isDisconnectionError(errcode): retFuture.complete("") else: - retFuture.fail(newException(EOS, osErrorMsg(errcode))) + retFuture.fail(newException(OSError, osErrorMsg(errcode))) if dataBuf.buf != nil: dealloc dataBuf.buf dataBuf.buf = nil ) - let ret = WSARecv(socket.TSocketHandle, addr dataBuf, 1, addr bytesReceived, - addr flagsio, cast[POverlapped](ol), nil) + let ret = WSARecv(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived, + addr flagsio, cast[POVERLAPPED](ol), nil) if ret == -1: let err = osLastError() if err.int32 != ERROR_IO_PENDING: @@ -495,7 +603,7 @@ when defined(windows) or defined(nimdoc): if flags.isDisconnectionError(err): retFuture.complete("") else: - retFuture.fail(newException(EOS, osErrorMsg(err))) + retFuture.fail(newException(OSError, osErrorMsg(err))) elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0': # We have to ensure that the buffer is empty because WSARecv will tell # us immediatelly when it was disconnected, even when there is still @@ -527,7 +635,7 @@ when defined(windows) or defined(nimdoc): return retFuture proc send*(socket: TAsyncFD, data: string, - flags = {TSocketFlags.SafeDisconn}): PFuture[void] = + flags = {SocketFlag.SafeDisconn}): Future[void] = ## Sends ``data`` to ``socket``. The returned future will complete once all ## data has been sent. verifyPresence(socket) @@ -537,23 +645,23 @@ when defined(windows) or defined(nimdoc): dataBuf.buf = data # since this is not used in a callback, this is fine dataBuf.len = data.len - var bytesReceived, lowFlags: DWord + var bytesReceived, lowFlags: Dword var ol = PCustomOverlapped() GC_ref(ol) - ol.data = TCompletionData(sock: socket, cb: - proc (sock: TAsyncFD, bytesCount: DWord, errcode: TOSErrorCode) = + ol.data = TCompletionData(fd: socket, cb: + proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) = if not retFuture.finished: - if errcode == TOSErrorCode(-1): + if errcode == OSErrorCode(-1): retFuture.complete() else: if flags.isDisconnectionError(errcode): retFuture.complete() else: - retFuture.fail(newException(EOS, osErrorMsg(errcode))) + retFuture.fail(newException(OSError, osErrorMsg(errcode))) ) - let ret = WSASend(socket.TSocketHandle, addr dataBuf, 1, addr bytesReceived, - lowFlags, cast[POverlapped](ol), nil) + let ret = WSASend(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived, + lowFlags, cast[POVERLAPPED](ol), nil) if ret == -1: let err = osLastError() if err.int32 != ERROR_IO_PENDING: @@ -561,7 +669,7 @@ when defined(windows) or defined(nimdoc): if flags.isDisconnectionError(err): retFuture.complete() else: - retFuture.fail(newException(EOS, osErrorMsg(err))) + retFuture.fail(newException(OSError, osErrorMsg(err))) else: retFuture.complete() # We don't deallocate ``ol`` here because even though this completed @@ -569,8 +677,8 @@ when defined(windows) or defined(nimdoc): # free ``ol``. return retFuture - proc acceptAddr*(socket: TAsyncFD, flags = {TSocketFlags.SafeDisconn}): - PFuture[tuple[address: string, client: TAsyncFD]] = + proc acceptAddr*(socket: TAsyncFD, flags = {SocketFlag.SafeDisconn}): + Future[tuple[address: string, client: TAsyncFD]] = ## 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. @@ -586,32 +694,32 @@ when defined(windows) or defined(nimdoc): var retFuture = newFuture[tuple[address: string, client: TAsyncFD]]("acceptAddr") var clientSock = newRawSocket() - if clientSock == osInvalidSocket: osError(osLastError()) + if clientSock == osInvalidSocket: raiseOSError(osLastError()) const lpOutputLen = 1024 var lpOutputBuf = newString(lpOutputLen) - var dwBytesReceived: DWORD - let dwReceiveDataLength = 0.DWORD # We don't want any data to be read. - let dwLocalAddressLength = DWORD(sizeof (TSockaddr_in) + 16) - let dwRemoteAddressLength = DWORD(sizeof(TSockaddr_in) + 16) + var dwBytesReceived: Dword + let dwReceiveDataLength = 0.Dword # We don't want any data to be read. + let dwLocalAddressLength = Dword(sizeof (Sockaddr_in) + 16) + let dwRemoteAddressLength = Dword(sizeof(Sockaddr_in) + 16) template completeAccept(): stmt {.immediate, dirty.} = var listenSock = socket let setoptRet = setsockopt(clientSock, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, addr listenSock, - sizeof(listenSock).TSockLen) - if setoptRet != 0: osError(osLastError()) + sizeof(listenSock).SockLen) + if setoptRet != 0: raiseOSError(osLastError()) - var LocalSockaddr, RemoteSockaddr: ptr TSockAddr + var localSockaddr, remoteSockaddr: ptr SockAddr var localLen, remoteLen: int32 getAcceptExSockaddrs(addr lpOutputBuf[0], dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength, - addr LocalSockaddr, addr localLen, - addr RemoteSockaddr, addr remoteLen) + addr localSockaddr, addr localLen, + addr remoteSockaddr, addr remoteLen) register(clientSock.TAsyncFD) # TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186 retFuture.complete( - (address: $inet_ntoa(cast[ptr Tsockaddr_in](remoteSockAddr).sin_addr), + (address: $inet_ntoa(cast[ptr Sockaddr_in](remoteSockAddr).sin_addr), client: clientSock.TAsyncFD) ) @@ -625,25 +733,25 @@ when defined(windows) or defined(nimdoc): else: retFuture.complete(newAcceptFut.read) else: - retFuture.fail(newException(EOS, osErrorMsg(errcode))) + retFuture.fail(newException(OSError, osErrorMsg(errcode))) var ol = PCustomOverlapped() GC_ref(ol) - ol.data = TCompletionData(sock: socket, cb: - proc (sock: TAsyncFD, bytesCount: DWord, errcode: TOSErrorCode) = + ol.data = TCompletionData(fd: socket, cb: + proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) = if not retFuture.finished: - if errcode == TOSErrorCode(-1): + if errcode == OSErrorCode(-1): completeAccept() else: failAccept(errcode) ) # http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx - let ret = acceptEx(socket.TSocketHandle, clientSock, addr lpOutputBuf[0], + let ret = acceptEx(socket.SocketHandle, clientSock, addr lpOutputBuf[0], dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength, - addr dwBytesReceived, cast[POverlapped](ol)) + addr dwBytesReceived, cast[POVERLAPPED](ol)) if not ret: let err = osLastError() @@ -658,17 +766,23 @@ when defined(windows) or defined(nimdoc): return retFuture - proc newAsyncRawSocket*(domain: TDomain = AF_INET, - typ: TType = SOCK_STREAM, - protocol: TProtocol = IPPROTO_TCP): TAsyncFD = + proc newAsyncRawSocket*(domain, typ, protocol: cint): TAsyncFD = ## Creates a new socket and registers it with the dispatcher implicitly. result = newRawSocket(domain, typ, protocol).TAsyncFD - result.TSocketHandle.setBlocking(false) + result.SocketHandle.setBlocking(false) + register(result) + + proc newAsyncRawSocket*(domain: Domain = AF_INET, + typ: SockType = SOCK_STREAM, + protocol: Protocol = IPPROTO_TCP): TAsyncFD = + ## Creates a new socket and registers it with the dispatcher implicitly. + result = newRawSocket(domain, typ, protocol).TAsyncFD + result.SocketHandle.setBlocking(false) register(result) proc closeSocket*(socket: TAsyncFD) = ## Closes a socket and ensures that it is unregistered. - socket.TSocketHandle.close() + socket.SocketHandle.close() getGlobalDispatcher().handles.excl(socket) proc unregister*(fd: TAsyncFD) = @@ -692,15 +806,15 @@ else: type TAsyncFD* = distinct cint - TCallback = proc (sock: TAsyncFD): bool {.closure,gcsafe.} + TCallback = proc (fd: TAsyncFD): bool {.closure,gcsafe.} - PData* = ref object of PObject - sock: TAsyncFD + PData* = ref object of RootRef + fd: TAsyncFD readCBs: seq[TCallback] writeCBs: seq[TCallback] PDispatcher* = ref object of PDispatcherBase - selector: PSelector + selector: Selector proc `==`*(x, y: TAsyncFD): bool {.borrow.} @@ -714,51 +828,56 @@ else: if gDisp.isNil: gDisp = newDispatcher() result = gDisp - proc update(sock: TAsyncFD, events: set[TEvent]) = + proc update(fd: TAsyncFD, events: set[Event]) = let p = getGlobalDispatcher() - assert sock.TSocketHandle in p.selector - discard p.selector.update(sock.TSocketHandle, events) + assert fd.SocketHandle in p.selector + discard p.selector.update(fd.SocketHandle, events) - proc register(sock: TAsyncFD) = + proc register*(fd: TAsyncFD) = let p = getGlobalDispatcher() - var data = PData(sock: sock, readCBs: @[], writeCBs: @[]) - p.selector.register(sock.TSocketHandle, {}, data.PObject) + var data = PData(fd: fd, readCBs: @[], writeCBs: @[]) + p.selector.register(fd.SocketHandle, {}, data.RootRef) - proc newAsyncRawSocket*(domain: TDomain = AF_INET, - typ: TType = SOCK_STREAM, - protocol: TProtocol = IPPROTO_TCP): TAsyncFD = + proc newAsyncRawSocket*(domain: cint, typ: cint, protocol: cint): TAsyncFD = result = newRawSocket(domain, typ, protocol).TAsyncFD - result.TSocketHandle.setBlocking(false) + result.SocketHandle.setBlocking(false) + register(result) + + proc newAsyncRawSocket*(domain: Domain = AF_INET, + typ: SockType = SOCK_STREAM, + protocol: Protocol = IPPROTO_TCP): TAsyncFD = + result = newRawSocket(domain, typ, protocol).TAsyncFD + result.SocketHandle.setBlocking(false) register(result) proc closeSocket*(sock: TAsyncFD) = let disp = getGlobalDispatcher() - sock.TSocketHandle.close() - disp.selector.unregister(sock.TSocketHandle) + sock.SocketHandle.close() + disp.selector.unregister(sock.SocketHandle) proc unregister*(fd: TAsyncFD) = - getGlobalDispatcher().selector.unregister(fd.TSocketHandle) + getGlobalDispatcher().selector.unregister(fd.SocketHandle) - proc addRead(sock: TAsyncFD, cb: TCallback) = + proc addRead*(fd: TAsyncFD, cb: TCallback) = let p = getGlobalDispatcher() - if sock.TSocketHandle notin p.selector: - raise newException(EInvalidValue, "File descriptor not registered.") - p.selector[sock.TSocketHandle].data.PData.readCBs.add(cb) - update(sock, p.selector[sock.TSocketHandle].events + {EvRead}) + 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(sock: TAsyncFD, cb: TCallback) = + proc addWrite*(fd: TAsyncFD, cb: TCallback) = let p = getGlobalDispatcher() - if sock.TSocketHandle notin p.selector: - raise newException(EInvalidValue, "File descriptor not registered.") - p.selector[sock.TSocketHandle].data.PData.writeCBs.add(cb) - update(sock, p.selector[sock.TSocketHandle].events + {EvWrite}) + if fd.SocketHandle notin p.selector: + raise newException(ValueError, "File descriptor not registered.") + p.selector[fd.SocketHandle].data.PData.writeCBs.add(cb) + update(fd, p.selector[fd.SocketHandle].events + {EvWrite}) proc poll*(timeout = 500) = let p = getGlobalDispatcher() for info in p.selector.select(timeout): let data = PData(info.key.data) - assert data.sock == info.key.fd.TAsyncFD - #echo("In poll ", data.sock.cint) + assert data.fd == info.key.fd.TAsyncFD + #echo("In poll ", data.fd.cint) if EvRead in info.events: # Callback may add items to ``data.readCBs`` which causes issues if # we are iterating over ``data.readCBs`` at the same time. We therefore @@ -766,7 +885,7 @@ else: let currentCBs = data.readCBs data.readCBs = @[] for cb in currentCBs: - if not cb(data.sock): + if not cb(data.fd): # Callback wants to be called again. data.readCBs.add(cb) @@ -774,37 +893,38 @@ else: let currentCBs = data.writeCBs data.writeCBs = @[] for cb in currentCBs: - if not cb(data.sock): + if not cb(data.fd): # Callback wants to be called again. data.writeCBs.add(cb) if info.key in p.selector: - var newEvents: set[TEvent] + var newEvents: set[Event] if data.readCBs.len != 0: newEvents = {EvRead} if data.writeCBs.len != 0: newEvents = newEvents + {EvWrite} if newEvents != info.key.events: - update(data.sock, newEvents) + update(data.fd, newEvents) else: # FD no longer a part of the selector. Likely been closed # (e.g. socket disconnected). + discard processTimers(p) - proc connect*(socket: TAsyncFD, address: string, port: TPort, - af = AF_INET): PFuture[void] = + proc connect*(socket: TAsyncFD, address: string, port: Port, + af = AF_INET): Future[void] = var retFuture = newFuture[void]("connect") - proc cb(sock: TAsyncFD): bool = + proc cb(fd: TAsyncFD): bool = # We have connected. retFuture.complete() return true var aiList = getAddrInfo(address, port, af) var success = false - var lastError: TOSErrorCode + var lastError: OSErrorCode var it = aiList while it != nil: - var ret = connect(socket.TSocketHandle, it.ai_addr, it.ai_addrlen.TSocklen) + var ret = connect(socket.SocketHandle, it.ai_addr, it.ai_addrlen.Socklen) if ret == 0: # Request to connect completed immediately. success = true @@ -822,18 +942,18 @@ else: dealloc(aiList) if not success: - retFuture.fail(newException(EOS, osErrorMsg(lastError))) + retFuture.fail(newException(OSError, osErrorMsg(lastError))) return retFuture proc recv*(socket: TAsyncFD, size: int, - flags = {TSocketFlags.SafeDisconn}): PFuture[string] = + flags = {SocketFlag.SafeDisconn}): Future[string] = var retFuture = newFuture[string]("recv") var readBuffer = newString(size) proc cb(sock: TAsyncFD): bool = result = true - let res = recv(sock.TSocketHandle, addr readBuffer[0], size.cint, + let res = recv(sock.SocketHandle, addr readBuffer[0], size.cint, flags.toOSFlags()) #echo("recv cb res: ", res) if res < 0: @@ -842,7 +962,7 @@ else: if flags.isDisconnectionError(lastError): retFuture.complete("") else: - retFuture.fail(newException(EOS, osErrorMsg(lastError))) + retFuture.fail(newException(OSError, osErrorMsg(lastError))) else: result = false # We still want this callback to be called. elif res == 0: @@ -857,7 +977,7 @@ else: return retFuture proc send*(socket: TAsyncFD, data: string, - flags = {TSocketFlags.SafeDisconn}): PFuture[void] = + flags = {SocketFlag.SafeDisconn}): Future[void] = var retFuture = newFuture[void]("send") var written = 0 @@ -866,7 +986,7 @@ else: result = true let netSize = data.len-written var d = data.cstring - let res = send(sock.TSocketHandle, addr d[written], netSize.cint, + let res = send(sock.SocketHandle, addr d[written], netSize.cint, MSG_NOSIGNAL) if res < 0: let lastError = osLastError() @@ -874,7 +994,7 @@ else: if flags.isDisconnectionError(lastError): retFuture.complete() else: - retFuture.fail(newException(EOS, osErrorMsg(lastError))) + retFuture.fail(newException(OSError, osErrorMsg(lastError))) else: result = false # We still want this callback to be called. else: @@ -888,16 +1008,16 @@ else: addWrite(socket, cb) return retFuture - proc acceptAddr*(socket: TAsyncFD, flags = {TSocketFlags.SafeDisconn}): - PFuture[tuple[address: string, client: TAsyncFD]] = + proc acceptAddr*(socket: TAsyncFD, flags = {SocketFlag.SafeDisconn}): + Future[tuple[address: string, client: TAsyncFD]] = var retFuture = newFuture[tuple[address: string, client: TAsyncFD]]("acceptAddr") proc cb(sock: TAsyncFD): bool = result = true - var sockAddress: Tsockaddr_in - var addrLen = sizeof(sockAddress).TSocklen - var client = accept(sock.TSocketHandle, - cast[ptr TSockAddr](addr(sockAddress)), addr(addrLen)) + var sockAddress: SockAddr_in + var addrLen = sizeof(sockAddress).Socklen + var client = accept(sock.SocketHandle, + cast[ptr SockAddr](addr(sockAddress)), addr(addrLen)) if client == osInvalidSocket: let lastError = osLastError() assert lastError.int32 notin {EWOULDBLOCK, EAGAIN} @@ -907,14 +1027,14 @@ else: if flags.isDisconnectionError(lastError): return false else: - retFuture.fail(newException(EOS, osErrorMsg(lastError))) + retFuture.fail(newException(OSError, osErrorMsg(lastError))) else: register(client.TAsyncFD) retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client.TAsyncFD)) addRead(socket, cb) return retFuture -proc sleepAsync*(ms: int): PFuture[void] = +proc sleepAsync*(ms: int): Future[void] = ## Suspends the execution of the current async procedure for the next ## ``ms`` miliseconds. var retFuture = newFuture[void]("sleepAsync") @@ -923,14 +1043,14 @@ proc sleepAsync*(ms: int): PFuture[void] = return retFuture proc accept*(socket: TAsyncFD, - flags = {TSocketFlags.SafeDisconn}): PFuture[TAsyncFD] = + flags = {SocketFlag.SafeDisconn}): Future[TAsyncFD] = ## 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 fut = acceptAddr(socket, flags) fut.callback = - proc (future: PFuture[tuple[address: string, client: TAsyncFD]]) = + proc (future: Future[tuple[address: string, client: TAsyncFD]]) = assert future.finished if future.failed: retFut.fail(future.error) @@ -940,7 +1060,7 @@ proc accept*(socket: TAsyncFD, # -- Await Macro -template createCb*(retFutureSym, iteratorNameSym, +template createCb(retFutureSym, iteratorNameSym, name: expr): stmt {.immediate.} = var nameIterVar = iteratorNameSym #{.push stackTrace: off.} @@ -963,29 +1083,50 @@ template createCb*(retFutureSym, iteratorNameSym, cb() #{.pop.} proc generateExceptionCheck(futSym, - exceptBranch, rootReceiver, fromNode: PNimrodNode): PNimrodNode {.compileTime.} = - if exceptBranch == nil: + tryStmt, rootReceiver, fromNode: PNimrodNode): PNimrodNode {.compileTime.} = + if tryStmt.kind == nnkNilLit: result = rootReceiver else: - if exceptBranch[0].kind == nnkStmtList: - result = newIfStmt( - (newDotExpr(futSym, newIdentNode("failed")), - exceptBranch[0] - ) - ) - else: - expectKind(exceptBranch[1], nnkStmtList) - result = newIfStmt( - (newDotExpr(futSym, newIdentNode("failed")), - newIfStmt( - (infix(newDotExpr(futSym, newIdentNode("error")), "of", exceptBranch[0]), - exceptBranch[1]) - ) - ) - ) + var exceptionChecks: seq[tuple[cond, body: PNimrodNode]] = @[] + let errorNode = newDotExpr(futSym, newIdentNode("error")) + for i in 1 .. -> else: raise futSym.error + exceptionChecks.add((newIdentNode("true"), + newNimNode(nnkRaiseStmt).add(errorNode))) + # Read the future if there is no error. + # -> else: futSym.read let elseNode = newNimNode(nnkElse, fromNode) elseNode.add newNimNode(nnkStmtList, fromNode) elseNode[0].add rootReceiver + + let ifBody = newStmtList() + ifBody.add newCall(newIdentNode("setCurrentException"), errorNode) + ifBody.add newIfStmt(exceptionChecks) + ifBody.add newCall(newIdentNode("setCurrentException"), newNilLit()) + + result = newIfStmt( + (newDotExpr(futSym, newIdentNode("failed")), ifBody) + ) result.add elseNode template createVar(result: var PNimrodNode, futSymName: string, @@ -997,25 +1138,25 @@ template createVar(result: var PNimrodNode, futSymName: string, result.add newVarStmt(futSym, asyncProc) # -> var future = y result.add newNimNode(nnkYieldStmt, fromNode).add(futSym) # -> yield future valueReceiver = newDotExpr(futSym, newIdentNode("read")) # -> future.read - result.add generateExceptionCheck(futSym, exceptBranch, rootReceiver, fromNode) + result.add generateExceptionCheck(futSym, tryStmt, rootReceiver, fromNode) proc processBody(node, retFutureSym: PNimrodNode, subTypeIsVoid: bool, - exceptBranch: PNimrodNode): PNimrodNode {.compileTime.} = + tryStmt: PNimrodNode): PNimrodNode {.compileTime.} = #echo(node.treeRepr) result = node case node.kind of nnkReturnStmt: result = newNimNode(nnkStmtList, node) if node[0].kind == nnkEmpty: - if not subtypeIsVoid: + if not subTypeIsVoid: result.add newCall(newIdentNode("complete"), retFutureSym, newIdentNode("result")) else: result.add newCall(newIdentNode("complete"), retFutureSym) else: result.add newCall(newIdentNode("complete"), retFutureSym, - node[0].processBody(retFutureSym, subtypeIsVoid, exceptBranch)) + node[0].processBody(retFutureSym, subTypeIsVoid, tryStmt)) result.add newNimNode(nnkReturnStmt, node).add(newNilLit()) return # Don't process the children of this return stmt @@ -1070,7 +1211,7 @@ proc processBody(node, retFutureSym: PNimrodNode, res: PNimrodNode): bool {.compileTime.} = result = false while i < n[0].len: - var processed = processBody(n[0][i], retFutureSym, subtypeIsVoid, n[1]) + var processed = processBody(n[0][i], retFutureSym, subTypeIsVoid, n) if processed.kind != n[0][i].kind or processed.len != n[0][i].len: expectKind(processed, nnkStmtList) expectKind(processed[2][1], nnkElse) @@ -1090,7 +1231,7 @@ proc processBody(node, retFutureSym: PNimrodNode, else: discard for i in 0 .. iterator nameIter(): PFutureBase {.closure.} = + # -> iterator nameIter(): FutureBase {.closure.} = # -> var result: T # -> # -> complete(retFuture, result) @@ -1155,14 +1296,14 @@ macro async*(prc: stmt): stmt {.immediate.} = # -> complete(retFuture) procBody.add(newCall(newIdentNode("complete"), retFutureSym)) - var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")], + var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")], procBody, nnkIteratorDef) closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure")) outerProcBody.add(closureIterator) # -> createCb(retFuture) - var cbName = newIdentNode("cb") - var procCb = newCall("createCb", retFutureSym, iteratorNameSym, + #var cbName = newIdentNode("cb") + var procCb = newCall(bindSym"createCb", retFutureSym, iteratorNameSym, newStrLitNode(prc[0].getName)) outerProcBody.add procCb @@ -1178,16 +1319,16 @@ macro async*(prc: stmt): stmt {.immediate.} = if subtypeIsVoid: # Add discardable pragma. if returnType.kind == nnkEmpty: - # Add PFuture[void] - result[3][0] = parseExpr("PFuture[void]") + # Add Future[void] + result[3][0] = parseExpr("Future[void]") result[6] = outerProcBody #echo(treeRepr(result)) - #if prc[0].getName == "getFile": + #if prc[0].getName == "catch": # echo(toStrLit(result)) -proc recvLine*(socket: TAsyncFD): PFuture[string] {.async.} = +proc recvLine*(socket: TAsyncFD): Future[string] {.async.} = ## Reads a line of data from ``socket``. Returned future will complete once ## a full line is read or an error occurs. ## @@ -1200,6 +1341,11 @@ proc recvLine*(socket: TAsyncFD): PFuture[string] {.async.} = ## If the socket is disconnected in the middle of a line (before ``\r\L`` ## is read) then line will be set to ``""``. ## The partial line **will be lost**. + ## + ## **Warning**: This assumes that lines are delimited by ``\r\L``. + ## + ## **Note**: This procedure is mostly used for testing. You likely want to + ## use ``asyncnet.recvLine`` instead. template addNLIfEmpty(): stmt = if result.len == 0: @@ -1212,9 +1358,8 @@ proc recvLine*(socket: TAsyncFD): PFuture[string] {.async.} = if c.len == 0: return "" if c == "\r": - c = await recv(socket, 1, {TSocketFlags.SafeDisconn, TSocketFlags.Peek}) - if c.len > 0 and c == "\L": - discard await recv(socket, 1) + c = await recv(socket, 1) + assert c == "\l" addNLIfEmpty() return elif c == "\L": @@ -1227,10 +1372,9 @@ proc runForever*() = while true: poll() -proc waitFor*[T](fut: PFuture[T]) = +proc waitFor*[T](fut: Future[T]): T = ## **Blocks** the current thread until the specified future completes. while not fut.finished: poll() - if fut.failed: - raise fut.error + fut.read diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim new file mode 100644 index 000000000..c09cb5a35 --- /dev/null +++ b/lib/pure/asyncfile.nim @@ -0,0 +1,325 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2014 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements asynchronous file handling. +## +## .. code-block:: Nim +## import asyncfile, asyncdispatch, os +## +## proc main() {.async.} = +## var file = openAsync(getTempDir() / "foobar.txt", fmReadWrite) +## await file.write("test") +## file.setFilePos(0) +## let data = await file.readAll() +## doAssert data == "test" +## file.close() +## +## waitFor main() + +import asyncdispatch, os + +when defined(windows): + import winlean +else: + import posix + +type + AsyncFile = ref object + fd: TAsyncFd + offset: int64 + +when defined(windows): + proc getDesiredAccess(mode: TFileMode): int32 = + case mode + of fmRead: + result = GENERIC_READ + of fmWrite, fmAppend: + result = GENERIC_WRITE + of fmReadWrite, fmReadWriteExisting: + result = GENERIC_READ or GENERIC_WRITE + + proc getCreationDisposition(mode: TFileMode, filename: string): int32 = + case mode + of fmRead, fmReadWriteExisting: + OPEN_EXISTING + of fmAppend, fmReadWrite, fmWrite: + if fileExists(filename): + OPEN_EXISTING + else: + CREATE_NEW +else: + proc getPosixFlags(mode: TFileMode): cint = + case mode + of fmRead: + result = O_RDONLY + of fmWrite: + result = O_WRONLY or O_CREAT + of fmAppend: + result = O_WRONLY or O_CREAT or O_APPEND + of fmReadWrite: + result = O_RDWR or O_CREAT + of fmReadWriteExisting: + result = O_RDWR + result = result or O_NONBLOCK + +proc getFileSize(f: AsyncFile): int64 = + ## Retrieves the specified file's size. + when defined(windows): + var high: DWord + let low = getFileSize(f.fd.THandle, addr high) + if low == INVALID_FILE_SIZE: + raiseOSError() + return (high shl 32) or low + +proc openAsync*(filename: string, mode = fmRead): AsyncFile = + ## Opens a file specified by the path in ``filename`` using + ## the specified ``mode`` asynchronously. + new result + when defined(windows): + let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL + let desiredAccess = getDesiredAccess(mode) + let creationDisposition = getCreationDisposition(mode, filename) + when useWinUnicode: + result.fd = createFileW(newWideCString(filename), desiredAccess, + FILE_SHARE_READ, + nil, creationDisposition, flags, 0).TAsyncFd + else: + result.fd = createFileA(filename, desiredAccess, + FILE_SHARE_READ, + nil, creationDisposition, flags, 0).TAsyncFd + + if result.fd.THandle == INVALID_HANDLE_VALUE: + raiseOSError() + + register(result.fd) + + if mode == fmAppend: + result.offset = getFileSize(result) + + else: + 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 + if result.fd.cint == -1: + raiseOSError() + + register(result.fd) + +proc read*(f: AsyncFile, size: int): Future[string] = + ## Read ``size`` bytes from the specified file asynchronously starting at + ## the current position of the file pointer. + ## + ## If the file pointer is past the end of the file then an empty string is + ## returned. + var retFuture = newFuture[string]("asyncfile.read") + + when defined(windows): + var buffer = alloc0(size) + + var ol = PCustomOverlapped() + GC_ref(ol) + ol.data = TCompletionData(fd: f.fd, cb: + proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) = + if not retFuture.finished: + if errcode == OSErrorCode(-1): + assert bytesCount > 0 + assert bytesCount <= size + var data = newString(bytesCount) + copyMem(addr data[0], buffer, bytesCount) + f.offset.inc bytesCount + retFuture.complete($data) + else: + if errcode.int32 == ERROR_HANDLE_EOF: + retFuture.complete("") + else: + retFuture.fail(newException(OSError, osErrorMsg(errcode))) + if buffer != nil: + dealloc buffer + buffer = nil + ) + ol.offset = DWord(f.offset and 0xffffffff) + 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, + cast[POVERLAPPED](ol)) + if not ret.bool: + let err = osLastError() + if err.int32 != ERROR_IO_PENDING: + if buffer != nil: + dealloc buffer + buffer = nil + GC_unref(ol) + retFuture.fail(newException(OSError, osErrorMsg(err))) + else: + # Request completed immediately. + var bytesRead: DWord + let overlappedRes = getOverlappedResult(f.fd.THandle, + cast[POverlapped](ol)[], bytesRead, false.WinBool) + if not overlappedRes.bool: + let err = osLastError() + if err.int32 == ERROR_HANDLE_EOF: + retFuture.complete("") + else: + retFuture.fail(newException(OSError, osErrorMsg(osLastError()))) + else: + assert bytesRead > 0 + assert bytesRead <= size + var data = newString(bytesRead) + copyMem(addr data[0], buffer, bytesRead) + f.offset.inc bytesRead + retFuture.complete($data) + else: + var readBuffer = newString(size) + + proc cb(fd: TAsyncFD): bool = + result = true + let res = read(fd.cint, addr readBuffer[0], size.cint) + if res < 0: + let lastError = osLastError() + if lastError.int32 != EAGAIN: + retFuture.fail(newException(EOS, osErrorMsg(lastError))) + else: + result = false # We still want this callback to be called. + elif res == 0: + # EOF + retFuture.complete("") + else: + readBuffer.setLen(res) + f.offset.inc(res) + retFuture.complete(readBuffer) + + if not cb(f.fd): + addRead(f.fd, cb) + + return retFuture + +proc readLine*(f: AsyncFile): Future[string] {.async.} = + ## Reads a single line from the specified file asynchronously. + result = "" + while true: + var c = await read(f, 1) + if c[0] == '\c': + c = await read(f, 1) + break + if c[0] == '\L' or c == "": + break + else: + result.add(c) + +proc getFilePos*(f: AsyncFile): int64 = + ## Retrieves the current position of the file pointer that is + ## used to read from the specified file. The file's first byte has the + ## index zero. + f.offset + +proc setFilePos*(f: AsyncFile, pos: int64) = + ## Sets the position of the file pointer that is used for read/write + ## operations. The file's first byte has the index zero. + f.offset = pos + when not defined(windows): + let ret = lseek(f.fd.cint, pos, SEEK_SET) + if ret == -1: + raiseOSError() + +proc readAll*(f: AsyncFile): Future[string] {.async.} = + ## Reads all data from the specified file. + result = "" + while true: + let data = await read(f, 4000) + if data.len == 0: + return + result.add data + +proc write*(f: AsyncFile, data: string): Future[void] = + ## Writes ``data`` to the file specified asynchronously. + ## + ## The returned Future will complete once all data has been written to the + ## specified file. + var retFuture = newFuture[void]("asyncfile.write") + var copy = data + when defined(windows): + var buffer = alloc0(data.len) + copyMem(buffer, addr copy[0], data.len) + + var ol = PCustomOverlapped() + GC_ref(ol) + ol.data = TCompletionData(fd: f.fd, cb: + proc (fd: TAsyncFD, bytesCount: DWord, errcode: OSErrorCode) = + if not retFuture.finished: + if errcode == OSErrorCode(-1): + assert bytesCount == data.len.int32 + f.offset.inc(data.len) + retFuture.complete() + else: + retFuture.fail(newException(OSError, osErrorMsg(errcode))) + if buffer != nil: + dealloc buffer + buffer = nil + ) + ol.offset = DWord(f.offset and 0xffffffff) + 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, + cast[POVERLAPPED](ol)) + if not ret.bool: + let err = osLastError() + if err.int32 != ERROR_IO_PENDING: + if buffer != nil: + dealloc buffer + buffer = nil + GC_unref(ol) + retFuture.fail(newException(OSError, osErrorMsg(err))) + else: + # Request completed immediately. + var bytesWritten: DWord + let overlappedRes = getOverlappedResult(f.fd.THandle, + cast[POverlapped](ol)[], bytesWritten, false.WinBool) + if not overlappedRes.bool: + retFuture.fail(newException(OSError, osErrorMsg(osLastError()))) + else: + assert bytesWritten == data.len.int32 + f.offset.inc(data.len) + retFuture.complete() + else: + var written = 0 + + proc cb(fd: TAsyncFD): bool = + result = true + let remainderSize = data.len-written + let res = write(fd.cint, addr copy[written], remainderSize.cint) + if res < 0: + let lastError = osLastError() + if lastError.int32 != EAGAIN: + retFuture.fail(newException(EOS, osErrorMsg(lastError))) + else: + result = false # We still want this callback to be called. + else: + written.inc res + f.offset.inc res + if res != remainderSize: + result = false # We still have data to write. + else: + retFuture.complete() + + if not cb(f.fd): + addWrite(f.fd, cb) + return retFuture + +proc close*(f: AsyncFile) = + ## Closes the file specified. + when defined(windows): + if not closeHandle(f.fd.THandle).bool: + raiseOSError() + else: + if close(f.fd.cint) == -1: + raiseOSError() + diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim index f1b1d1400..fc38dc31a 100644 --- a/lib/pure/asyncftpclient.nim +++ b/lib/pure/asyncftpclient.nim @@ -1,28 +1,46 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Dominik Picheta # See the file "copying.txt", included in this # distribution, for details about the copyright. # +## This module implement an asynchronous FTP client. +## +## Examples +## -------- +## +## .. code-block::nim +## +## var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test") +## proc main(ftp: AsyncFtpClient) {.async.} = +## await ftp.connect() +## echo await ftp.pwd() +## echo await ftp.listDirs() +## await ftp.store("payload.jpg", "payload.jpg") +## await ftp.retrFile("payload.jpg", "payload2.jpg") +## echo("Finished") +## +## waitFor main(ftp) + import asyncdispatch, asyncnet, strutils, parseutils, os, times -from ftpclient import TFtpBase, EInvalidReply, TFtpEvent -from net import bufferSize +from ftpclient import FtpBaseObj, ReplyError, FtpEvent +from net import BufferSize type - TAsyncFtpClient* = TFtpBase[PAsyncSocket] - PAsyncFtpClient* = ref TAsyncFtpClient + AsyncFtpClientObj* = FtpBaseObj[AsyncSocket] + AsyncFtpClient* = ref AsyncFtpClientObj ProgressChangedProc* = proc (total, progress: BiggestInt, speed: float): - PFuture[void] {.closure, gcsafe.} + Future[void] {.closure, gcsafe.} -proc expectReply(ftp: PAsyncFtpClient): PFuture[TaintedString] = +proc expectReply(ftp: AsyncFtpClient): Future[TaintedString] = result = ftp.csock.recvLine() -proc send*(ftp: PAsyncFtpClient, m: string): PFuture[TaintedString] {.async.} = +proc send*(ftp: AsyncFtpClient, m: string): Future[TaintedString] {.async.} = ## Send a message to the server, and wait for a primary reply. ## ``\c\L`` is added for you. await ftp.csock.send(m & "\c\L") @@ -31,11 +49,11 @@ proc send*(ftp: PAsyncFtpClient, m: string): PFuture[TaintedString] {.async.} = proc assertReply(received: TaintedString, expected: varargs[string]) = for i in items(expected): if received.string.startsWith(i): return - raise newException(EInvalidReply, + raise newException(ReplyError, "Expected reply '$1' got: $2" % [expected.join("' or '"), received.string]) -proc pasv(ftp: PAsyncFtpClient) {.async.} = +proc pasv(ftp: AsyncFtpClient) {.async.} = ## Negotiate a data connection. ftp.dsock = newAsyncSocket() @@ -46,13 +64,13 @@ proc pasv(ftp: PAsyncFtpClient) {.async.} = var ip = nums[0.. -3] var port = nums[-2.. -1] var properPort = port[0].parseInt()*256+port[1].parseInt() - await ftp.dsock.connect(ip.join("."), TPort(properPort.toU16)) - ftp.dsockConnected = True + await ftp.dsock.connect(ip.join("."), Port(properPort.toU16)) + ftp.dsockConnected = true proc normalizePathSep(path: string): string = return replace(path, '\\', '/') -proc connect*(ftp: PAsyncFtpClient) {.async.} = +proc connect*(ftp: AsyncFtpClient) {.async.} = ## Connect to the FTP server specified by ``ftp``. await ftp.csock.connect(ftp.address, ftp.port) @@ -69,21 +87,21 @@ proc connect*(ftp: PAsyncFtpClient) {.async.} = if ftp.pass != "": assertReply(await(ftp.send("PASS " & ftp.pass)), "230") -proc pwd*(ftp: PAsyncFtpClient): PFuture[TaintedString] {.async.} = +proc pwd*(ftp: AsyncFtpClient): Future[TaintedString] {.async.} = ## Returns the current working directory. let wd = await ftp.send("PWD") assertReply wd, "257" return wd.string.captureBetween('"').TaintedString # " -proc cd*(ftp: PAsyncFtpClient, dir: string) {.async.} = +proc cd*(ftp: AsyncFtpClient, dir: string) {.async.} = ## Changes the current directory on the remote FTP server to ``dir``. assertReply(await(ftp.send("CWD " & dir.normalizePathSep)), "250") -proc cdup*(ftp: PAsyncFtpClient) {.async.} = +proc cdup*(ftp: AsyncFtpClient) {.async.} = ## Changes the current directory to the parent of the current directory. assertReply(await(ftp.send("CDUP")), "200") -proc getLines(ftp: PAsyncFtpClient): PFuture[string] {.async.} = +proc getLines(ftp: AsyncFtpClient): Future[string] {.async.} = ## Downloads text data in ASCII mode result = "" assert ftp.dsockConnected @@ -96,7 +114,7 @@ proc getLines(ftp: PAsyncFtpClient): PFuture[string] {.async.} = assertReply(await(ftp.expectReply()), "226") -proc listDirs*(ftp: PAsyncFtpClient, dir = ""): PFuture[seq[string]] {.async.} = +proc listDirs*(ftp: AsyncFtpClient, dir = ""): Future[seq[string]] {.async.} = ## Returns a list of filenames in the given directory. If ``dir`` is "", ## the current directory is used. If ``async`` is true, this ## function will return immediately and it will be your job to @@ -107,13 +125,13 @@ proc listDirs*(ftp: PAsyncFtpClient, dir = ""): PFuture[seq[string]] {.async.} = result = splitLines(await ftp.getLines()) -proc existsFile*(ftp: PAsyncFtpClient, file: string): PFuture[bool] {.async.} = +proc existsFile*(ftp: AsyncFtpClient, file: string): Future[bool] {.async.} = ## Determines whether ``file`` exists. var files = await ftp.listDirs() for f in items(files): if f.normalizePathSep == file.normalizePathSep: return true -proc createDir*(ftp: PAsyncFtpClient, dir: string, recursive = false){.async.} = +proc createDir*(ftp: AsyncFtpClient, dir: string, recursive = false){.async.} = ## Creates a directory ``dir``. If ``recursive`` is true, the topmost ## subdirectory of ``dir`` will be created first, following the secondmost... ## etc. this allows you to give a full path as the ``dir`` without worrying @@ -123,14 +141,14 @@ proc createDir*(ftp: PAsyncFtpClient, dir: string, recursive = false){.async.} = else: var reply = TaintedString"" var previousDirs = "" - for p in split(dir, {os.dirSep, os.altSep}): + for p in split(dir, {os.DirSep, os.AltSep}): if p != "": previousDirs.add(p) reply = await ftp.send("MKD " & previousDirs) previousDirs.add('/') assertReply reply, "257" -proc chmod*(ftp: PAsyncFtpClient, path: string, +proc chmod*(ftp: AsyncFtpClient, path: string, permissions: set[TFilePermission]) {.async.} = ## Changes permission of ``path`` to ``permissions``. var userOctal = 0 @@ -152,7 +170,7 @@ proc chmod*(ftp: PAsyncFtpClient, path: string, assertReply(await(ftp.send("SITE CHMOD " & perm & " " & path.normalizePathSep)), "200") -proc list*(ftp: PAsyncFtpClient, dir = ""): PFuture[string] {.async.} = +proc list*(ftp: AsyncFtpClient, dir = ""): Future[string] {.async.} = ## Lists all files in ``dir``. If ``dir`` is ``""``, uses the current ## working directory. await ftp.pasv() @@ -162,7 +180,7 @@ proc list*(ftp: PAsyncFtpClient, dir = ""): PFuture[string] {.async.} = result = await ftp.getLines() -proc retrText*(ftp: PAsyncFtpClient, file: string): PFuture[string] {.async.} = +proc retrText*(ftp: AsyncFtpClient, file: string): Future[string] {.async.} = ## Retrieves ``file``. File must be ASCII text. await ftp.pasv() let reply = await ftp.send("RETR " & file.normalizePathSep) @@ -170,13 +188,13 @@ proc retrText*(ftp: PAsyncFtpClient, file: string): PFuture[string] {.async.} = result = await ftp.getLines() -proc getFile(ftp: PAsyncFtpClient, file: TFile, total: BiggestInt, +proc getFile(ftp: AsyncFtpClient, file: TFile, total: BiggestInt, onProgressChanged: ProgressChangedProc) {.async.} = assert ftp.dsockConnected var progress = 0 var progressInSecond = 0 var countdownFut = sleepAsync(1000) - var dataFut = ftp.dsock.recv(bufferSize) + var dataFut = ftp.dsock.recv(BufferSize) while ftp.dsockConnected: await dataFut or countdownFut if countdownFut.finished: @@ -191,20 +209,20 @@ proc getFile(ftp: PAsyncFtpClient, file: TFile, total: BiggestInt, progress.inc(data.len) progressInSecond.inc(data.len) file.write(data) - dataFut = ftp.dsock.recv(bufferSize) + dataFut = ftp.dsock.recv(BufferSize) else: - ftp.dsockConnected = False + ftp.dsockConnected = false assertReply(await(ftp.expectReply()), "226") proc defaultOnProgressChanged*(total, progress: BiggestInt, - speed: float): PFuture[void] {.nimcall,gcsafe.} = + speed: float): Future[void] {.nimcall,gcsafe.} = ## Default FTP ``onProgressChanged`` handler. Does nothing. result = newFuture[void]() #echo(total, " ", progress, " ", speed) result.complete() -proc retrFile*(ftp: PAsyncFtpClient, file, dest: string, +proc retrFile*(ftp: AsyncFtpClient, file, dest: string, onProgressChanged = defaultOnProgressChanged) {.async.} = ## Downloads ``file`` and saves it to ``dest``. ## The ``EvRetr`` event is passed to the specified ``handleEvent`` function @@ -215,14 +233,14 @@ proc retrFile*(ftp: PAsyncFtpClient, file, dest: string, var reply = await ftp.send("RETR " & file.normalizePathSep) assertReply reply, ["125", "150"] if {'(', ')'} notin reply.string: - raise newException(EInvalidReply, "Reply has no file size.") - var fileSize: biggestInt + raise newException(ReplyError, "Reply has no file size.") + var fileSize: BiggestInt if reply.string.captureBetween('(', ')').parseBiggestInt(fileSize) == 0: - raise newException(EInvalidReply, "Reply has no file size.") + raise newException(ReplyError, "Reply has no file size.") await getFile(ftp, destFile, fileSize, onProgressChanged) -proc doUpload(ftp: PAsyncFtpClient, file: TFile, +proc doUpload(ftp: AsyncFtpClient, file: TFile, onProgressChanged: ProgressChangedProc) {.async.} = assert ftp.dsockConnected @@ -231,7 +249,7 @@ proc doUpload(ftp: PAsyncFtpClient, file: TFile, var progress = 0 var progressInSecond = 0 var countdownFut = sleepAsync(1000) - var sendFut: PFuture[void] = nil + var sendFut: Future[void] = nil while ftp.dsockConnected: if sendFut == nil or sendFut.finished: progress.inc(data.len) @@ -255,7 +273,7 @@ proc doUpload(ftp: PAsyncFtpClient, file: TFile, await countdownFut or sendFut -proc storeFile*(ftp: PAsyncFtpClient, file, dest: string, +proc store*(ftp: AsyncFtpClient, file, dest: string, onProgressChanged = defaultOnProgressChanged) {.async.} = ## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this ## function asynchronously is recommended to view the progress of @@ -271,9 +289,9 @@ proc storeFile*(ftp: PAsyncFtpClient, file, dest: string, await doUpload(ftp, destFile, onProgressChanged) -proc newAsyncFtpClient*(address: string, port = TPort(21), - user, pass = ""): PAsyncFtpClient = - ## Creates a new ``PAsyncFtpClient`` object. +proc newAsyncFtpClient*(address: string, port = Port(21), + user, pass = ""): AsyncFtpClient = + ## Creates a new ``AsyncFtpClient`` object. new result result.user = user result.pass = pass @@ -284,11 +302,11 @@ proc newAsyncFtpClient*(address: string, port = TPort(21), when isMainModule: var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test") - proc main(ftp: PAsyncFtpClient) {.async.} = + proc main(ftp: AsyncFtpClient) {.async.} = await ftp.connect() echo await ftp.pwd() echo await ftp.listDirs() - await ftp.storeFile("payload.jpg", "payload.jpg") + await ftp.store("payload.jpg", "payload.jpg") await ftp.retrFile("payload.jpg", "payload2.jpg") echo("Finished") diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index c8bd5cfc1..0b18e6bcc 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Dominik Picheta # # See the file "copying.txt", included in this @@ -9,36 +9,80 @@ ## This module implements a high performance asynchronous HTTP server. ## -## **Note:** This module is still largely experimental. +## Examples +## -------- +## +## This example will create an HTTP server on port 8080. The server will +## respond to all requests with a ``200 OK`` response code and "Hello World" +## as the response body. +## +## .. code-block::nim +## var server = newAsyncHttpServer() +## proc cb(req: Request) {.async.} = +## await req.respond(Http200, "Hello World") +## +## asyncCheck server.serve(Port(8080), cb) +## runForever() import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils type - TRequest* = object - client*: PAsyncSocket # TODO: Separate this into a Response object? + Request* = object + client*: AsyncSocket # TODO: Separate this into a Response object? reqMethod*: string - headers*: PStringTable + headers*: StringTableRef protocol*: tuple[orig: string, major, minor: int] - url*: TUri + url*: Uri hostname*: string ## The hostname of the client that made the request. body*: string - PAsyncHttpServer* = ref object - socket: PAsyncSocket + AsyncHttpServer* = ref object + socket: AsyncSocket + reuseAddr: bool - THttpCode* = enum + HttpCode* = enum + Http100 = "100 Continue", + Http101 = "101 Switching Protocols", Http200 = "200 OK", - Http303 = "303 Moved", + Http201 = "201 Created", + Http202 = "202 Accepted", + Http204 = "204 No Content", + Http205 = "205 Reset Content", + Http206 = "206 Partial Content", + Http300 = "300 Multiple Choices", + Http301 = "301 Moved Permanently", + Http302 = "302 Found", + Http303 = "303 See Other", + Http304 = "304 Not Modified", + Http305 = "305 Use Proxy", + Http307 = "307 Temporary Redirect", Http400 = "400 Bad Request", + Http401 = "401 Unauthorized", + Http403 = "403 Forbidden", Http404 = "404 Not Found", + Http405 = "405 Method Not Allowed", + Http406 = "406 Not Acceptable", + Http407 = "407 Proxy Authentication Required", + Http408 = "408 Request Timeout", + Http409 = "409 Conflict", + Http410 = "410 Gone", + Http411 = "411 Length Required", + Http418 = "418 I'm a teapot", Http500 = "500 Internal Server Error", - Http502 = "502 Bad Gateway" + Http501 = "501 Not Implemented", + Http502 = "502 Bad Gateway", + Http503 = "503 Service Unavailable", + Http504 = "504 Gateway Timeout", + Http505 = "505 HTTP Version Not Supported" - THttpVersion* = enum + HttpVersion* = enum HttpVer11, HttpVer10 +{.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer, + THttpCode: HttpCode, THttpVersion: HttpVersion].} + proc `==`*(protocol: tuple[orig: string, major, minor: int], - ver: THttpVersion): bool = + ver: HttpVersion): bool = let major = case ver of HttpVer11, HttpVer10: 1 @@ -48,21 +92,23 @@ proc `==`*(protocol: tuple[orig: string, major, minor: int], of HttpVer10: 0 result = protocol.major == major and protocol.minor == minor -proc newAsyncHttpServer*(): PAsyncHttpServer = +proc newAsyncHttpServer*(reuseAddr = true): AsyncHttpServer = + ## Creates a new ``AsyncHttpServer`` instance. new result + result.reuseAddr = reuseAddr -proc addHeaders(msg: var string, headers: PStringTable) = +proc addHeaders(msg: var string, headers: StringTableRef) = for k, v in headers: msg.add(k & ": " & v & "\c\L") -proc sendHeaders*(req: TRequest, headers: PStringTable): PFuture[void] = +proc sendHeaders*(req: Request, headers: StringTableRef): Future[void] = ## Sends the specified headers to the requesting client. var msg = "" addHeaders(msg, headers) return req.client.send(msg) -proc respond*(req: TRequest, code: THttpCode, - content: string, headers: PStringTable = newStringTable()) {.async.} = +proc respond*(req: Request, code: HttpCode, + content: string, headers = newStringTable()) {.async.} = ## Responds to the request with the specified ``HttpCode``, headers and ## content. ## @@ -73,8 +119,10 @@ proc respond*(req: TRequest, code: THttpCode, msg.addHeaders(customHeaders) await req.client.send(msg & "\c\L" & content) -proc newRequest(): TRequest = +proc newRequest(): Request = result.headers = newStringTable(modeCaseInsensitive) + result.hostname = "" + result.body = "" proc parseHeader(line: string): tuple[key, value: string] = var i = 0 @@ -86,20 +134,20 @@ proc parseHeader(line: string): tuple[key, value: string] = proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] = var i = protocol.skipIgnoreCase("HTTP/") if i != 5: - raise newException(EInvalidValue, "Invalid request protocol. Got: " & + raise newException(ValueError, "Invalid request protocol. Got: " & protocol) result.orig = protocol i.inc protocol.parseInt(result.major, i) i.inc # Skip . i.inc protocol.parseInt(result.minor, i) -proc sendStatus(client: PAsyncSocket, status: string): PFuture[void] = +proc sendStatus(client: AsyncSocket, status: string): Future[void] = client.send("HTTP/1.1 " & status & "\c\L") -proc processClient(client: PAsyncSocket, address: string, - callback: proc (request: TRequest): - PFuture[void] {.closure, gcsafe.}) {.async.} = - while true: +proc processClient(client: AsyncSocket, address: string, + callback: proc (request: Request): + Future[void] {.closure, gcsafe.}) {.async.} = + while not client.isClosed: # GET /path HTTP/1.1 # Header: val # \n @@ -139,7 +187,7 @@ proc processClient(client: PAsyncSocket, address: string, request.url = parseUri(path) try: request.protocol = protocol.parseProtocol() - except EInvalidValue: + except ValueError: asyncCheck request.respond(Http400, "Invalid request protocol. Got: " & protocol) continue @@ -180,18 +228,21 @@ proc processClient(client: PAsyncSocket, address: string, # header states otherwise. # In HTTP 1.0 we assume that the connection should not be persistent. # Unless the connection header states otherwise. + discard else: request.client.close() break -proc serve*(server: PAsyncHttpServer, port: TPort, - callback: proc (request: TRequest): PFuture[void] {.closure,gcsafe.}, +proc serve*(server: AsyncHttpServer, port: Port, + callback: proc (request: Request): Future[void] {.closure,gcsafe.}, address = "") {.async.} = ## Starts the process of listening for incoming HTTP connections on the ## specified address and port. ## ## When a request is made by a client the specified callback will be called. server.socket = newAsyncSocket() + if server.reuseAddr: + server.socket.setSockOpt(OptReuseAddr, true) server.socket.bindAddr(port, address) server.socket.listen() @@ -203,20 +254,20 @@ proc serve*(server: PAsyncHttpServer, port: TPort, #echo(f.isNil) #echo(f.repr) -proc close*(server: PAsyncHttpServer) = +proc close*(server: AsyncHttpServer) = ## Terminates the async http server instance. server.socket.close() when isMainModule: proc main = var server = newAsyncHttpServer() - proc cb(req: TRequest) {.async.} = + proc cb(req: Request) {.async.} = #echo(req.reqMethod, " ", req.url) #echo(req.headers) let headers = {"Date": "Tue, 29 Apr 2014 23:40:08 GMT", "Content-type": "text/plain; charset=utf-8"} await req.respond(Http200, "Hello World", headers.newStringTable()) - asyncCheck server.serve(TPort(5555), cb) + asyncCheck server.serve(Port(5555), cb) runForever() main() diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim index 6b67bf4b5..d40c3849e 100644 --- a/lib/pure/asyncio.nim +++ b/lib/pure/asyncio.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf, Dominik Picheta # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -10,8 +10,13 @@ include "system/inclrtl" import sockets, os -## This module implements an asynchronous event loop together with asynchronous sockets -## which use this event loop. +## +## **Warning:** This module is deprecated since version 0.10.2. +## Use the brand new `asyncdispatch `_ module together +## with the `asyncnet `_ module. + +## This module implements an asynchronous event loop together with asynchronous +## sockets which use this event loop. ## It is akin to Python's asyncore module. Many modules that use sockets ## have an implementation for this module, those modules should all have a ## ``register`` function which you should use to add the desired objects to a @@ -31,10 +36,10 @@ import sockets, os ## ## Most (if not all) modules that use asyncio provide a userArg which is passed ## on with the events. The type that you set userArg to must be inheriting from -## TObject! +## ``RootObj``! ## ## **Note:** If you want to provide async ability to your module please do not -## use the ``TDelegate`` object, instead use ``PAsyncSocket``. It is possible +## use the ``Delegate`` object, instead use ``AsyncSocket``. It is possible ## that in the future this type's fields will not be exported therefore breaking ## your code. ## @@ -44,10 +49,10 @@ import sockets, os ## Asynchronous sockets ## ==================== ## -## For most purposes you do not need to worry about the ``TDelegate`` type. The -## ``PAsyncSocket`` is what you are after. It's a reference to the ``TAsyncSocket`` -## object. This object defines events which you should overwrite by your own -## procedures. +## For most purposes you do not need to worry about the ``Delegate`` type. The +## ``AsyncSocket`` is what you are after. It's a reference to +## the ``AsyncSocketObj`` object. This object defines events which you should +## overwrite by your own procedures. ## ## For server sockets the only event you need to worry about is the ``handleAccept`` ## event, in your handleAccept proc you should call ``accept`` on the server @@ -57,13 +62,13 @@ import sockets, os ## ## An example ``handleAccept`` follows: ## -## .. code-block:: nimrod +## .. code-block:: nim ## -## var disp: PDispatcher = newDispatcher() +## var disp = newDispatcher() ## ... -## proc handleAccept(s: PAsyncSocket) = +## proc handleAccept(s: AsyncSocket) = ## echo("Accepted client.") -## var client: PAsyncSocket +## var client: AsyncSocket ## new(client) ## s.accept(client) ## client.handleRead = ... @@ -76,106 +81,116 @@ import sockets, os ## the socket has established a connection to a server socket; from that point ## it can be safely written to. ## -## Getting a blocking client from a PAsyncSocket +## Getting a blocking client from an AsyncSocket ## ============================================= ## ## If you need a asynchronous server socket but you wish to process the clients -## synchronously then you can use the ``getSocket`` converter to get a TSocket -## object from the PAsyncSocket object, this can then be combined with ``accept`` -## like so: +## synchronously then you can use the ``getSocket`` converter to get +## a ``Socket`` from the ``AsyncSocket`` object, this can then be combined +## with ``accept`` like so: ## -## .. code-block:: nimrod +## .. code-block:: nim ## -## proc handleAccept(s: PAsyncSocket) = -## var client: TSocket +## proc handleAccept(s: AsyncSocket) = +## var client: Socket ## getSocket(s).accept(client) +{.deprecated.} + when defined(windows): - from winlean import TTimeVal, TSocketHandle, TFdSet, FD_ZERO, FD_SET, FD_ISSET, select + from winlean import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet, + FD_ISSET, select else: - from posix import TTimeVal, TSocketHandle, TFdSet, FD_ZERO, FD_SET, FD_ISSET, select + from posix import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet, + FD_ISSET, select type - TDelegate* = object - fd*: TSocketHandle - deleVal*: PObject + DelegateObj* = object + fd*: SocketHandle + deleVal*: RootRef - handleRead*: proc (h: PObject) {.nimcall, gcsafe.} - handleWrite*: proc (h: PObject) {.nimcall, gcsafe.} - handleError*: proc (h: PObject) {.nimcall, gcsafe.} - hasDataBuffered*: proc (h: PObject): bool {.nimcall, gcsafe.} + handleRead*: proc (h: RootRef) {.nimcall, gcsafe.} + handleWrite*: proc (h: RootRef) {.nimcall, gcsafe.} + handleError*: proc (h: RootRef) {.nimcall, gcsafe.} + hasDataBuffered*: proc (h: RootRef): bool {.nimcall, gcsafe.} open*: bool - task*: proc (h: PObject) {.nimcall, gcsafe.} - mode*: TFileMode + task*: proc (h: RootRef) {.nimcall, gcsafe.} + mode*: FileMode - PDelegate* = ref TDelegate + Delegate* = ref DelegateObj - PDispatcher* = ref TDispatcher - TDispatcher = object - delegates: seq[PDelegate] + Dispatcher* = ref DispatcherObj + DispatcherObj = object + delegates: seq[Delegate] - PAsyncSocket* = ref TAsyncSocket - TAsyncSocket* = object of TObject - socket: TSocket - info: TInfo + AsyncSocket* = ref AsyncSocketObj + AsyncSocketObj* = object of RootObj + socket: Socket + info: SocketStatus - handleRead*: proc (s: PAsyncSocket) {.closure, gcsafe.} - handleWrite: proc (s: PAsyncSocket) {.closure, gcsafe.} - handleConnect*: proc (s: PAsyncSocket) {.closure, gcsafe.} + handleRead*: proc (s: AsyncSocket) {.closure, gcsafe.} + handleWrite: proc (s: AsyncSocket) {.closure, gcsafe.} + handleConnect*: proc (s: AsyncSocket) {.closure, gcsafe.} - handleAccept*: proc (s: PAsyncSocket) {.closure, gcsafe.} + handleAccept*: proc (s: AsyncSocket) {.closure, gcsafe.} - handleTask*: proc (s: PAsyncSocket) {.closure, gcsafe.} + handleTask*: proc (s: AsyncSocket) {.closure, gcsafe.} lineBuffer: TaintedString ## Temporary storage for ``readLine`` sendBuffer: string ## Temporary storage for ``send`` sslNeedAccept: bool - proto: TProtocol - deleg: PDelegate + proto: Protocol + deleg: Delegate - TInfo* = enum + SocketStatus* = enum SockIdle, SockConnecting, SockConnected, SockListening, SockClosed, SockUDPBound -proc newDelegate*(): PDelegate = +{.deprecated: [TDelegate: DelegateObj, PDelegate: Delegate, + TInfo: SocketStatus, PAsyncSocket: AsyncSocket, TAsyncSocket: AsyncSocketObj, + TDispatcher: DispatcherObj, PDispatcher: Dispatcher, + ].} + + +proc newDelegate*(): Delegate = ## Creates a new delegate. new(result) - result.handleRead = (proc (h: PObject) = discard) - result.handleWrite = (proc (h: PObject) = discard) - result.handleError = (proc (h: PObject) = discard) - result.hasDataBuffered = (proc (h: PObject): bool = return false) - result.task = (proc (h: PObject) = discard) + result.handleRead = (proc (h: RootRef) = discard) + result.handleWrite = (proc (h: RootRef) = discard) + result.handleError = (proc (h: RootRef) = discard) + result.hasDataBuffered = (proc (h: RootRef): bool = return false) + result.task = (proc (h: RootRef) = discard) result.mode = fmRead -proc newAsyncSocket(): PAsyncSocket = +proc newAsyncSocket(): AsyncSocket = new(result) result.info = SockIdle - result.handleRead = (proc (s: PAsyncSocket) = discard) + result.handleRead = (proc (s: AsyncSocket) = discard) result.handleWrite = nil - result.handleConnect = (proc (s: PAsyncSocket) = discard) - result.handleAccept = (proc (s: PAsyncSocket) = discard) - result.handleTask = (proc (s: PAsyncSocket) = discard) + result.handleConnect = (proc (s: AsyncSocket) = discard) + result.handleAccept = (proc (s: AsyncSocket) = discard) + result.handleTask = (proc (s: AsyncSocket) = discard) result.lineBuffer = "".TaintedString result.sendBuffer = "" -proc asyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, - protocol: TProtocol = IPPROTO_TCP, - buffered = true): PAsyncSocket = +proc asyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM, + protocol: Protocol = IPPROTO_TCP, + buffered = true): AsyncSocket = ## Initialises an AsyncSocket object. If a socket cannot be initialised ## EOS is raised. result = newAsyncSocket() result.socket = socket(domain, typ, protocol, buffered) result.proto = protocol - if result.socket == invalidSocket: osError(osLastError()) + if result.socket == invalidSocket: raiseOSError(osLastError()) result.socket.setBlocking(false) -proc toAsyncSocket*(sock: TSocket, state: TInfo = SockConnected): PAsyncSocket = - ## Wraps an already initialized ``TSocket`` into a PAsyncSocket. +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 - ## asynchronous PAsyncSocket in asyncio's event loop. + ## asynchronous AsyncSocket in asyncio's event loop. ## ## ``state`` may be overriden, i.e. if ``sock`` is not connected it should be ## adjusted properly. By default it will be assumed that the socket is @@ -193,7 +208,7 @@ proc toAsyncSocket*(sock: TSocket, state: TInfo = SockConnected): PAsyncSocket = ## SockUDPBound Socket is a UDP socket which is listening for data. ## ================ ================================================================ ## - ## **Warning**: If ``state`` is set incorrectly the resulting ``PAsyncSocket`` + ## **Warning**: If ``state`` is set incorrectly the resulting ``AsyncSocket`` ## object may not work properly. ## ## **Note**: This will set ``sock`` to be non-blocking. @@ -203,80 +218,81 @@ proc toAsyncSocket*(sock: TSocket, state: TInfo = SockConnected): PAsyncSocket = result.socket.setBlocking(false) result.info = state -proc asyncSockHandleRead(h: PObject) = +proc asyncSockHandleRead(h: RootRef) = when defined(ssl): - if PAsyncSocket(h).socket.isSSL and not - PAsyncSocket(h).socket.gotHandshake: + if AsyncSocket(h).socket.isSSL and not + AsyncSocket(h).socket.gotHandshake: return - if PAsyncSocket(h).info != SockListening: - if PAsyncSocket(h).info != SockConnecting: - PAsyncSocket(h).handleRead(PAsyncSocket(h)) + if AsyncSocket(h).info != SockListening: + if AsyncSocket(h).info != SockConnecting: + AsyncSocket(h).handleRead(AsyncSocket(h)) else: - PAsyncSocket(h).handleAccept(PAsyncSocket(h)) + AsyncSocket(h).handleAccept(AsyncSocket(h)) -proc close*(sock: PAsyncSocket) {.gcsafe.} -proc asyncSockHandleWrite(h: PObject) = +proc close*(sock: AsyncSocket) {.gcsafe.} +proc asyncSockHandleWrite(h: RootRef) = when defined(ssl): - if PAsyncSocket(h).socket.isSSL and not - PAsyncSocket(h).socket.gotHandshake: + if AsyncSocket(h).socket.isSSL and not + AsyncSocket(h).socket.gotHandshake: return - if PAsyncSocket(h).info == SockConnecting: - PAsyncSocket(h).handleConnect(PAsyncSocket(h)) - PAsyncSocket(h).info = SockConnected + if AsyncSocket(h).info == SockConnecting: + AsyncSocket(h).handleConnect(AsyncSocket(h)) + AsyncSocket(h).info = SockConnected # Stop receiving write events if there is no handleWrite event. - if PAsyncSocket(h).handleWrite == nil: - PAsyncSocket(h).deleg.mode = fmRead + if AsyncSocket(h).handleWrite == nil: + AsyncSocket(h).deleg.mode = fmRead else: - PAsyncSocket(h).deleg.mode = fmReadWrite + AsyncSocket(h).deleg.mode = fmReadWrite else: - if PAsyncSocket(h).sendBuffer != "": - let sock = PAsyncSocket(h) + if AsyncSocket(h).sendBuffer != "": + let sock = AsyncSocket(h) try: let bytesSent = sock.socket.sendAsync(sock.sendBuffer) if bytesSent == 0: # Apparently the socket cannot be written to. Even though select # just told us that it can be... This used to be an assert. Just # do nothing instead. + discard elif bytesSent != sock.sendBuffer.len: sock.sendBuffer = sock.sendBuffer[bytesSent .. -1] elif bytesSent == sock.sendBuffer.len: sock.sendBuffer = "" - if PAsyncSocket(h).handleWrite != nil: - PAsyncSocket(h).handleWrite(PAsyncSocket(h)) - except EOS: + if AsyncSocket(h).handleWrite != nil: + AsyncSocket(h).handleWrite(AsyncSocket(h)) + except OSError: # Most likely the socket closed before the full buffer could be sent to it. sock.close() # TODO: Provide a handleError for users? else: - if PAsyncSocket(h).handleWrite != nil: - PAsyncSocket(h).handleWrite(PAsyncSocket(h)) + if AsyncSocket(h).handleWrite != nil: + AsyncSocket(h).handleWrite(AsyncSocket(h)) else: - PAsyncSocket(h).deleg.mode = fmRead + AsyncSocket(h).deleg.mode = fmRead when defined(ssl): proc asyncSockDoHandshake(h: PObject) {.gcsafe.} = - if PAsyncSocket(h).socket.isSSL and not - PAsyncSocket(h).socket.gotHandshake: - if PAsyncSocket(h).sslNeedAccept: + if AsyncSocket(h).socket.isSSL and not + AsyncSocket(h).socket.gotHandshake: + if AsyncSocket(h).sslNeedAccept: var d = "" - let ret = PAsyncSocket(h).socket.acceptAddrSSL(PAsyncSocket(h).socket, d) + let ret = AsyncSocket(h).socket.acceptAddrSSL(AsyncSocket(h).socket, d) assert ret != AcceptNoClient if ret == AcceptSuccess: - PAsyncSocket(h).info = SockConnected + AsyncSocket(h).info = SockConnected else: # handshake will set socket's ``sslNoHandshake`` field. - discard PAsyncSocket(h).socket.handshake() + discard AsyncSocket(h).socket.handshake() -proc asyncSockTask(h: PObject) = +proc asyncSockTask(h: RootRef) = when defined(ssl): h.asyncSockDoHandshake() - PAsyncSocket(h).handleTask(PAsyncSocket(h)) + AsyncSocket(h).handleTask(AsyncSocket(h)) -proc toDelegate(sock: PAsyncSocket): PDelegate = +proc toDelegate(sock: AsyncSocket): Delegate = result = newDelegate() result.deleVal = sock result.fd = getFD(sock.socket) @@ -289,8 +305,8 @@ proc toDelegate(sock: PAsyncSocket): PDelegate = #result.handleError = (proc (h: PObject) = assert(false)) result.hasDataBuffered = - proc (h: PObject): bool {.nimcall.} = - return PAsyncSocket(h).socket.hasDataBuffered() + proc (h: RootRef): bool {.nimcall.} = + return AsyncSocket(h).socket.hasDataBuffered() sock.deleg = result if sock.info notin {SockIdle, SockClosed}: @@ -298,22 +314,22 @@ proc toDelegate(sock: PAsyncSocket): PDelegate = else: sock.deleg.open = false -proc connect*(sock: PAsyncSocket, name: string, port = TPort(0), - af: TDomain = AF_INET) = +proc connect*(sock: AsyncSocket, name: string, port = Port(0), + af: Domain = AF_INET) = ## Begins connecting ``sock`` to ``name``:``port``. sock.socket.connectAsync(name, port, af) sock.info = SockConnecting if sock.deleg != nil: sock.deleg.open = true -proc close*(sock: PAsyncSocket) = +proc close*(sock: AsyncSocket) = ## Closes ``sock``. Terminates any current connections. sock.socket.close() sock.info = SockClosed if sock.deleg != nil: sock.deleg.open = false -proc bindAddr*(sock: PAsyncSocket, port = TPort(0), address = "") = +proc bindAddr*(sock: AsyncSocket, port = Port(0), address = "") = ## Equivalent to ``sockets.bindAddr``. sock.socket.bindAddr(port, address) if sock.proto == IPPROTO_UDP: @@ -321,14 +337,14 @@ proc bindAddr*(sock: PAsyncSocket, port = TPort(0), address = "") = if sock.deleg != nil: sock.deleg.open = true -proc listen*(sock: PAsyncSocket) = +proc listen*(sock: AsyncSocket) = ## Equivalent to ``sockets.listen``. sock.socket.listen() sock.info = SockListening if sock.deleg != nil: sock.deleg.open = true -proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket, +proc acceptAddr*(server: AsyncSocket, client: var AsyncSocket, address: var string) = ## Equivalent to ``sockets.acceptAddr``. This procedure should be called in ## a ``handleAccept`` event handler **only** once. @@ -336,7 +352,7 @@ proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket, ## **Note**: ``client`` needs to be initialised. assert(client != nil) client = newAsyncSocket() - var c: TSocket + var c: Socket new(c) when defined(ssl): if server.socket.isSSL: @@ -359,7 +375,7 @@ proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket, client.sslNeedAccept = false client.info = SockConnected - if c == invalidSocket: socketError(server.socket) + if c == invalidSocket: raiseSocketError(server.socket) c.setBlocking(false) # TODO: Needs to be tested. # deleg.open is set in ``toDelegate``. @@ -369,12 +385,12 @@ proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket, client.sendBuffer = "" client.info = SockConnected -proc accept*(server: PAsyncSocket, client: var PAsyncSocket) = +proc accept*(server: AsyncSocket, client: var AsyncSocket) = ## Equivalent to ``sockets.accept``. var dummyAddr = "" server.acceptAddr(client, dummyAddr) -proc acceptAddr*(server: PAsyncSocket): tuple[sock: PAsyncSocket, +proc acceptAddr*(server: AsyncSocket): tuple[sock: AsyncSocket, address: string] {.deprecated.} = ## Equivalent to ``sockets.acceptAddr``. ## @@ -384,7 +400,7 @@ proc acceptAddr*(server: PAsyncSocket): tuple[sock: PAsyncSocket, acceptAddr(server, client, address) return (client, address) -proc accept*(server: PAsyncSocket): PAsyncSocket {.deprecated.} = +proc accept*(server: AsyncSocket): AsyncSocket {.deprecated.} = ## Equivalent to ``sockets.accept``. ## ## **Deprecated since version 0.9.0:** Please use the function above. @@ -392,69 +408,69 @@ proc accept*(server: PAsyncSocket): PAsyncSocket {.deprecated.} = var address = "" server.acceptAddr(result, address) -proc newDispatcher*(): PDispatcher = +proc newDispatcher*(): Dispatcher = new(result) result.delegates = @[] -proc register*(d: PDispatcher, deleg: PDelegate) = +proc register*(d: Dispatcher, deleg: Delegate) = ## Registers delegate ``deleg`` with dispatcher ``d``. d.delegates.add(deleg) -proc register*(d: PDispatcher, sock: PAsyncSocket): PDelegate {.discardable.} = +proc register*(d: Dispatcher, sock: AsyncSocket): Delegate {.discardable.} = ## Registers async socket ``sock`` with dispatcher ``d``. result = sock.toDelegate() d.register(result) -proc unregister*(d: PDispatcher, deleg: PDelegate) = +proc unregister*(d: Dispatcher, deleg: Delegate) = ## Unregisters deleg ``deleg`` from dispatcher ``d``. for i in 0..len(d.delegates)-1: if d.delegates[i] == deleg: d.delegates.del(i) return - raise newException(EInvalidIndex, "Could not find delegate.") + raise newException(IndexError, "Could not find delegate.") -proc isWriteable*(s: PAsyncSocket): bool = +proc isWriteable*(s: AsyncSocket): bool = ## Determines whether socket ``s`` is ready to be written to. var writeSock = @[s.socket] return selectWrite(writeSock, 1) != 0 and s.socket notin writeSock -converter getSocket*(s: PAsyncSocket): TSocket = +converter getSocket*(s: AsyncSocket): Socket = return s.socket -proc isConnected*(s: PAsyncSocket): bool = +proc isConnected*(s: AsyncSocket): bool = ## Determines whether ``s`` is connected. return s.info == SockConnected -proc isListening*(s: PAsyncSocket): bool = +proc isListening*(s: AsyncSocket): bool = ## Determines whether ``s`` is listening for incoming connections. return s.info == SockListening -proc isConnecting*(s: PAsyncSocket): bool = +proc isConnecting*(s: AsyncSocket): bool = ## Determines whether ``s`` is connecting. return s.info == SockConnecting -proc isClosed*(s: PAsyncSocket): bool = +proc isClosed*(s: AsyncSocket): bool = ## Determines whether ``s`` has been closed. return s.info == SockClosed -proc isSendDataBuffered*(s: PAsyncSocket): bool = +proc isSendDataBuffered*(s: AsyncSocket): bool = ## Determines whether ``s`` has data waiting to be sent, i.e. whether this ## socket's sendBuffer contains data. return s.sendBuffer.len != 0 -proc setHandleWrite*(s: PAsyncSocket, - handleWrite: proc (s: PAsyncSocket) {.closure, gcsafe.}) = +proc setHandleWrite*(s: AsyncSocket, + handleWrite: proc (s: AsyncSocket) {.closure, gcsafe.}) = ## Setter for the ``handleWrite`` event. ## ## To remove this event you should use the ``delHandleWrite`` function. ## It is advised to use that function instead of just setting the event to - ## ``proc (s: PAsyncSocket) = nil`` as that would mean that that function + ## ``proc (s: AsyncSocket) = nil`` as that would mean that that function ## would be called constantly. s.deleg.mode = fmReadWrite s.handleWrite = handleWrite -proc delHandleWrite*(s: PAsyncSocket) = +proc delHandleWrite*(s: AsyncSocket) = ## Removes the ``handleWrite`` event handler on ``s``. s.handleWrite = nil {.push warning[deprecated]: off.} -proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool {.deprecated.} = +proc recvLine*(s: AsyncSocket, line: var TaintedString): bool {.deprecated.} = ## Behaves similar to ``sockets.recvLine``, however it handles non-blocking ## sockets properly. This function guarantees that ``line`` is a full line, ## if this function can only retrieve some data; it will save this data and @@ -483,11 +499,11 @@ proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool {.deprecated.} = of RecvDisconnected: result = true of RecvFail: - s.socketError(async = true) + s.raiseSocketError(async = true) result = false {.pop.} -proc readLine*(s: PAsyncSocket, line: var TaintedString): bool = +proc readLine*(s: AsyncSocket, line: var TaintedString): bool = ## Behaves similar to ``sockets.readLine``, however it handles non-blocking ## sockets properly. This function guarantees that ``line`` is a full line, ## if this function can only retrieve some data; it will save this data and @@ -517,7 +533,7 @@ proc readLine*(s: PAsyncSocket, line: var TaintedString): bool = of ReadDisconnected: result = true -proc send*(sock: PAsyncSocket, data: string) = +proc send*(sock: AsyncSocket, data: string) = ## Sends ``data`` to socket ``sock``. This is basically a nicer implementation ## of ``sockets.sendAsync``. ## @@ -537,19 +553,19 @@ proc send*(sock: PAsyncSocket, data: string) = sock.sendBuffer.add(data[bytesSent .. -1]) sock.deleg.mode = fmReadWrite -proc timeValFromMilliseconds(timeout = 500): TTimeVal = +proc timeValFromMilliseconds(timeout = 500): Timeval = if timeout != -1: var seconds = timeout div 1000 result.tv_sec = seconds.int32 result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 -proc createFdSet(fd: var TFdSet, s: seq[PDelegate], m: var int) = +proc createFdSet(fd: var TFdSet, s: seq[Delegate], m: var int) = FD_ZERO(fd) for i in items(s): m = max(m, int(i.fd)) FD_SET(i.fd, fd) -proc pruneSocketSet(s: var seq[PDelegate], fd: var TFdSet) = +proc pruneSocketSet(s: var seq[Delegate], fd: var TFdSet) = var i = 0 var L = s.len while i < L: @@ -560,9 +576,9 @@ proc pruneSocketSet(s: var seq[PDelegate], fd: var TFdSet) = inc(i) setLen(s, L) -proc select(readfds, writefds, exceptfds: var seq[PDelegate], +proc select(readfds, writefds, exceptfds: var seq[Delegate], timeout = 500): int = - var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout) + var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) var rd, wr, ex: TFdSet var m = 0 @@ -579,7 +595,7 @@ proc select(readfds, writefds, exceptfds: var seq[PDelegate], pruneSocketSet(writefds, (wr)) pruneSocketSet(exceptfds, (ex)) -proc poll*(d: PDispatcher, timeout: int = 500): bool = +proc poll*(d: Dispatcher, timeout: int = 500): bool = ## This function checks for events on all the delegates in the `PDispatcher`. ## It then proceeds to call the correct event handler. ## @@ -592,7 +608,7 @@ proc poll*(d: PDispatcher, timeout: int = 500): bool = ## only be executed after one or more file descriptors becomes readable or ## writeable. result = true - var readDg, writeDg, errorDg: seq[PDelegate] = @[] + var readDg, writeDg, errorDg: seq[Delegate] = @[] var len = d.delegates.len var dc = 0 @@ -640,16 +656,16 @@ proc poll*(d: PDispatcher, timeout: int = 500): bool = for i in items(d.delegates): i.task(i.deleVal) -proc len*(disp: PDispatcher): int = +proc len*(disp: Dispatcher): int = ## Retrieves the amount of delegates in ``disp``. return disp.delegates.len when isMainModule: - proc testConnect(s: PAsyncSocket, no: int) = + proc testConnect(s: AsyncSocket, no: int) = echo("Connected! " & $no) - proc testRead(s: PAsyncSocket, no: int) = + proc testRead(s: AsyncSocket, no: int) = echo("Reading! " & $no) var data = "" if not s.readLine(data): return @@ -659,34 +675,34 @@ when isMainModule: echo(data) echo("Finished reading! " & $no) - proc testAccept(s: PAsyncSocket, disp: PDispatcher, no: int) = + proc testAccept(s: AsyncSocket, disp: PDispatcher, no: int) = echo("Accepting client! " & $no) - var client: PAsyncSocket + var client: AsyncSocket new(client) var address = "" s.acceptAddr(client, address) echo("Accepted ", address) client.handleRead = - proc (s: PAsyncSocket) = + proc (s: AsyncSocket) = testRead(s, 2) disp.register(client) proc main = var d = newDispatcher() - var s = AsyncSocket() + var s = asyncSocket() s.connect("amber.tenthbit.net", TPort(6667)) s.handleConnect = - proc (s: PAsyncSocket) = + proc (s: AsyncSocket) = testConnect(s, 1) s.handleRead = - proc (s: PAsyncSocket) = + proc (s: AsyncSocket) = testRead(s, 1) d.register(s) - var server = AsyncSocket() + var server = asyncSocket() server.handleAccept = - proc (s: PAsyncSocket) = + proc (s: AsyncSocket) = testAccept(s, d, 78) server.bindAddr(TPort(5555)) server.listen() diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 5095d9461..76b2bc46c 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Dominik Picheta # # See the file "copying.txt", included in this @@ -10,26 +10,39 @@ ## This module implements a high-level asynchronous sockets API based on the ## asynchronous dispatcher defined in the ``asyncdispatch`` module. ## -## Example -## ======= +## SSL +## --- +## +## SSL can be enabled by compiling with the ``-d:ssl`` flag. +## +## You must create a new SSL context with the ``newContext`` function defined +## in the ``net`` module. You may then call ``wrapSocket`` on your socket using +## the newly created SSL context to get an SSL socket. +## +## Examples +## -------- +## +## Chat server +## ^^^^^^^^^^^ ## ## The following example demonstrates a simple chat server. ## -## .. code-block::nimrod +## .. code-block::nim ## ## import asyncnet, asyncdispatch ## -## var clients: seq[PAsyncSocket] = @[] +## var clients {.threadvar.}: seq[AsyncSocket] ## -## proc processClient(client: PAsyncSocket) {.async.} = +## proc processClient(client: AsyncSocket) {.async.} = ## while true: ## let line = await client.recvLine() ## for c in clients: ## await c.send(line & "\c\L") ## ## proc serve() {.async.} = +## clients = @[] ## var server = newAsyncSocket() -## server.bindAddr(TPort(12345)) +## server.bindAddr(Port(12345)) ## server.listen() ## ## while true: @@ -41,67 +54,178 @@ ## asyncCheck serve() ## runForever() ## -## -## **Note:** This module is still largely experimental. import asyncdispatch import rawsockets import net +import os + +export SOBool when defined(ssl): import openssl type # TODO: I would prefer to just do: - # PAsyncSocket* {.borrow: `.`.} = distinct PSocket. But that doesn't work. - TAsyncSocket {.borrow: `.`.} = distinct TSocketImpl - PAsyncSocket* = ref TAsyncSocket + # AsyncSocket* {.borrow: `.`.} = distinct Socket. But that doesn't work. + AsyncSocketDesc = object + fd: SocketHandle + closed: bool ## determines whether this socket has been closed + case isBuffered: bool ## determines whether this socket is buffered. + of true: + buffer: array[0..BufferSize, char] + currPos: int # current index in buffer + bufLen: int # current length of buffer + of false: nil + case isSsl: bool + of true: + when defined(ssl): + sslHandle: SslPtr + sslContext: SslContext + bioIn: BIO + bioOut: BIO + of false: nil + AsyncSocket* = ref AsyncSocketDesc + +{.deprecated: [PAsyncSocket: AsyncSocket].} # TODO: Save AF, domain etc info and reuse it in procs which need it like connect. -proc newSocket(fd: TAsyncFD, isBuff: bool): PAsyncSocket = +proc newAsyncSocket*(fd: TAsyncFD, isBuff: bool): AsyncSocket = + ## Creates a new ``AsyncSocket`` based on the supplied params. assert fd != osInvalidSocket.TAsyncFD - new(result.PSocket) - result.fd = fd.TSocketHandle + new(result) + result.fd = fd.SocketHandle result.isBuffered = isBuff if isBuff: result.currPos = 0 -proc newAsyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, - protocol: TProtocol = IPPROTO_TCP, buffered = true): PAsyncSocket = +proc newAsyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM, + protocol: Protocol = IPPROTO_TCP, buffered = true): AsyncSocket = ## Creates a new asynchronous socket. - result = newSocket(newAsyncRawSocket(domain, typ, protocol), buffered) + ## + ## This procedure will also create a brand new file descriptor for + ## this socket. + result = newAsyncSocket(newAsyncRawSocket(domain, typ, protocol), buffered) -proc connect*(socket: PAsyncSocket, address: string, port: TPort, - af = AF_INET): PFuture[void] = +proc newAsyncSocket*(domain, typ, protocol: cint, buffered = true): AsyncSocket = + ## Creates a new asynchronous socket. + ## + ## This procedure will also create a brand new file descriptor for + ## this socket. + result = newAsyncSocket(newAsyncRawSocket(domain, typ, protocol), buffered) + +when defined(ssl): + proc getSslError(handle: SslPtr, err: cint): cint = + assert err < 0 + var ret = SSLGetError(handle, err.cint) + case ret + of SSL_ERROR_ZERO_RETURN: + raiseSSLError("TLS/SSL connection failed to initiate, socket closed prematurely.") + of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: + return ret + of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ: + return ret + of SSL_ERROR_WANT_X509_LOOKUP: + raiseSSLError("Function for x509 lookup has been called.") + of SSL_ERROR_SYSCALL, SSL_ERROR_SSL: + raiseSSLError() + else: raiseSSLError("Unknown Error") + + proc sendPendingSslData(socket: AsyncSocket, + flags: set[SocketFlag]) {.async.} = + let len = bioCtrlPending(socket.bioOut) + if len > 0: + var data = newStringOfCap(len) + let read = bioRead(socket.bioOut, addr data[0], len) + assert read != 0 + if read < 0: + raiseSslError() + data.setLen(read) + await socket.fd.TAsyncFd.send(data, flags) + + proc appeaseSsl(socket: AsyncSocket, flags: set[SocketFlag], + sslError: cint) {.async.} = + case sslError + of SSL_ERROR_WANT_WRITE: + await sendPendingSslData(socket, flags) + of SSL_ERROR_WANT_READ: + var data = await recv(socket.fd.TAsyncFD, BufferSize, flags) + let ret = bioWrite(socket.bioIn, addr data[0], data.len.cint) + if ret < 0: + raiseSSLError() + else: + raiseSSLError("Cannot appease SSL.") + + template sslLoop(socket: AsyncSocket, flags: set[SocketFlag], + op: expr) = + var opResult {.inject.} = -1.cint + while opResult < 0: + opResult = op + # Bit hackish here. + # TODO: Introduce an async template transformation pragma? + yield sendPendingSslData(socket, flags) + if opResult < 0: + let err = getSslError(socket.sslHandle, opResult.cint) + yield appeaseSsl(socket, flags, err.cint) + +proc connect*(socket: AsyncSocket, address: string, port: Port, + af = AF_INET) {.async.} = ## Connects ``socket`` to server at ``address:port``. ## - ## Returns a ``PFuture`` which will complete when the connection succeeds + ## Returns a ``Future`` which will complete when the connection succeeds ## or an error occurs. - result = connect(socket.fd.TAsyncFD, address, port, af) + await connect(socket.fd.TAsyncFD, address, port, af) + if socket.isSsl: + when defined(ssl): + let flags = {SocketFlag.SafeDisconn} + sslSetConnectState(socket.sslHandle) + sslLoop(socket, flags, sslDoHandshake(socket.sslHandle)) -proc readIntoBuf(socket: PAsyncSocket, - flags: set[TSocketFlags]): PFuture[int] {.async.} = - var data = await recv(socket.fd.TAsyncFD, BufferSize, flags) - if data.len != 0: - copyMem(addr socket.buffer[0], addr data[0], data.len) - socket.bufLen = data.len +proc readInto(buf: cstring, size: int, socket: AsyncSocket, + flags: set[SocketFlag]): Future[int] {.async.} = + if socket.isSsl: + when defined(ssl): + # SSL mode. + sslLoop(socket, flags, + sslRead(socket.sslHandle, buf, size.cint)) + result = opResult + else: + var data = await recv(socket.fd.TAsyncFD, size, flags) + if data.len != 0: + copyMem(buf, addr data[0], data.len) + # Not in SSL mode. + result = data.len + +proc readIntoBuf(socket: AsyncSocket, + flags: set[SocketFlag]): Future[int] {.async.} = + result = await readInto(addr socket.buffer[0], BufferSize, socket, flags) socket.currPos = 0 - result = data.len + socket.bufLen = result -proc recv*(socket: PAsyncSocket, size: int, - flags = {TSocketFlags.SafeDisconn}): PFuture[string] {.async.} = - ## Reads ``size`` bytes from ``socket``. Returned future will complete once - ## all of the requested data is read. If socket is disconnected during the +proc recv*(socket: AsyncSocket, size: int, + flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} = + ## Reads **up to** ``size`` bytes from ``socket``. + ## + ## For buffered sockets this function will attempt to read all the requested + ## data. It will read this data in ``BufferSize`` chunks. + ## + ## For unbuffered sockets this function makes no effort to read + ## all the data requested. It will return as much data as the operating system + ## gives it. + ## + ## If socket is disconnected during the ## recv operation then the future may complete with only a part of the - ## requested data read. If socket is disconnected and no data is available + ## requested data. + ## + ## If socket is disconnected and no data is available ## to be read then the future will complete with a value of ``""``. if socket.isBuffered: result = newString(size) let originalBufPos = socket.currPos if socket.bufLen == 0: - let res = await socket.readIntoBuf(flags - {TSocketFlags.Peek}) + let res = await socket.readIntoBuf(flags - {SocketFlag.Peek}) if res == 0: result.setLen(0) return @@ -109,10 +233,10 @@ proc recv*(socket: PAsyncSocket, size: int, var read = 0 while read < size: if socket.currPos >= socket.bufLen: - if TSocketFlags.Peek in flags: + if SocketFlag.Peek in flags: # We don't want to get another buffer if we're peeking. break - let res = await socket.readIntoBuf(flags - {TSocketFlags.Peek}) + let res = await socket.readIntoBuf(flags - {SocketFlag.Peek}) if res == 0: break @@ -121,47 +245,56 @@ proc recv*(socket: PAsyncSocket, size: int, read.inc(chunk) socket.currPos.inc(chunk) - if TSocketFlags.Peek in flags: + if SocketFlag.Peek in flags: # Restore old buffer cursor position. socket.currPos = originalBufPos result.setLen(read) else: - result = await recv(socket.fd.TAsyncFD, size, flags) + result = newString(size) + let read = await readInto(addr result[0], size, socket, flags) + result.setLen(read) -proc send*(socket: PAsyncSocket, data: string, - flags = {TSocketFlags.SafeDisconn}): PFuture[void] = +proc send*(socket: AsyncSocket, data: string, + flags = {SocketFlag.SafeDisconn}) {.async.} = ## Sends ``data`` to ``socket``. The returned future will complete once all ## data has been sent. assert socket != nil - result = send(socket.fd.TAsyncFD, data, flags) + if socket.isSsl: + when defined(ssl): + var copy = data + sslLoop(socket, flags, + sslWrite(socket.sslHandle, addr copy[0], copy.len.cint)) + await sendPendingSslData(socket, flags) + else: + await send(socket.fd.TAsyncFD, data, flags) -proc acceptAddr*(socket: PAsyncSocket, flags = {TSocketFlags.SafeDisconn}): - PFuture[tuple[address: string, client: PAsyncSocket]] = +proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}): + Future[tuple[address: string, client: AsyncSocket]] = ## 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. - var retFuture = newFuture[tuple[address: string, client: PAsyncSocket]]("asyncnet.acceptAddr") + var retFuture = newFuture[tuple[address: string, client: AsyncSocket]]("asyncnet.acceptAddr") var fut = acceptAddr(socket.fd.TAsyncFD, flags) fut.callback = - proc (future: PFuture[tuple[address: string, client: TAsyncFD]]) = + proc (future: Future[tuple[address: string, client: TAsyncFD]]) = assert future.finished if future.failed: retFuture.fail(future.readError) else: let resultTup = (future.read.address, - newSocket(future.read.client, socket.isBuffered)) + newAsyncSocket(future.read.client, socket.isBuffered)) retFuture.complete(resultTup) return retFuture -proc accept*(socket: PAsyncSocket, - flags = {TSocketFlags.SafeDisconn}): PFuture[PAsyncSocket] = +proc accept*(socket: AsyncSocket, + flags = {SocketFlag.SafeDisconn}): Future[AsyncSocket] = ## 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[PAsyncSocket]("asyncnet.accept") + var retFut = newFuture[AsyncSocket]("asyncnet.accept") var fut = acceptAddr(socket, flags) fut.callback = - proc (future: PFuture[tuple[address: string, client: PAsyncSocket]]) = + proc (future: Future[tuple[address: string, client: AsyncSocket]]) = assert future.finished if future.failed: retFut.fail(future.readError) @@ -169,8 +302,8 @@ proc accept*(socket: PAsyncSocket, retFut.complete(future.read.client) return retFut -proc recvLine*(socket: PAsyncSocket, - flags = {TSocketFlags.SafeDisconn}): PFuture[string] {.async.} = +proc recvLine*(socket: AsyncSocket, + flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} = ## Reads a line of data from ``socket``. Returned future will complete once ## a full line is read or an error occurs. ## @@ -185,10 +318,13 @@ proc recvLine*(socket: PAsyncSocket, ## The partial line **will be lost**. ## ## **Warning**: The ``Peek`` flag is not yet implemented. + ## + ## **Warning**: ``recvLine`` on unbuffered sockets assumes that the protocol + ## uses ``\r\L`` to delimit a new line. template addNLIfEmpty(): stmt = if result.len == 0: result.add("\c\L") - assert TSocketFlags.Peek notin flags ## TODO: + assert SocketFlag.Peek notin flags ## TODO: if socket.isBuffered: result = "" if socket.bufLen == 0: @@ -227,10 +363,8 @@ proc recvLine*(socket: PAsyncSocket, if c.len == 0: return "" if c == "\r": - c = await recv(socket, 1, flags + {TSocketFlags.Peek}) - if c.len > 0 and c == "\L": - let dummy = await recv(socket, 1, flags) - assert dummy == "\L" + c = await recv(socket, 1, flags) # Skip \L + assert c == "\L" addNLIfEmpty() return elif c == "\L": @@ -238,24 +372,91 @@ proc recvLine*(socket: PAsyncSocket, return add(result.string, c) -proc bindAddr*(socket: PAsyncSocket, port = TPort(0), address = "") = - ## Binds ``address``:``port`` to the socket. - ## - ## If ``address`` is "" then ADDR_ANY will be bound. - socket.PSocket.bindAddr(port, address) - -proc listen*(socket: PAsyncSocket, backlog = SOMAXCONN) = +proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} = ## Marks ``socket`` as accepting connections. ## ``Backlog`` specifies the maximum length of the ## queue of pending connections. ## ## Raises an EOS error upon failure. - socket.PSocket.listen(backlog) + if listen(socket.fd, backlog) < 0'i32: raiseOSError(osLastError()) -proc close*(socket: PAsyncSocket) = +proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {. + tags: [ReadIOEffect].} = + ## Binds ``address``:``port`` to the socket. + ## + ## If ``address`` is "" then ADDR_ANY will be bound. + + if address == "": + var name: Sockaddr_in + when defined(Windows) or defined(nimdoc): + name.sin_family = toInt(AF_INET).int16 + else: + name.sin_family = toInt(AF_INET) + name.sin_port = htons(int16(port)) + name.sin_addr.s_addr = htonl(INADDR_ANY) + if bindAddr(socket.fd, cast[ptr SockAddr](addr(name)), + sizeof(name).Socklen) < 0'i32: + raiseOSError(osLastError()) + else: + var aiList = getAddrInfo(address, port, AF_INET) + if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32: + dealloc(aiList) + raiseOSError(osLastError()) + dealloc(aiList) + +proc close*(socket: AsyncSocket) = ## Closes the socket. - socket.fd.TAsyncFD.closeSocket() - # TODO SSL + defer: + socket.fd.TAsyncFD.closeSocket() + when defined(ssl): + if socket.isSSL: + let res = SslShutdown(socket.sslHandle) + if res == 0: + discard + elif res != 1: + raiseSslError() + socket.closed = true # TODO: Add extra debugging checks for this. + +when defined(ssl): + proc wrapSocket*(ctx: SslContext, socket: AsyncSocket) = + ## Wraps a socket in an SSL context. This function effectively turns + ## ``socket`` into an SSL socket. + ## + ## **Disclaimer**: This code is not well tested, may be very unsafe and + ## prone to security vulnerabilities. + socket.isSsl = true + socket.sslContext = ctx + socket.sslHandle = SSLNew(SSLCTX(socket.sslContext)) + if socket.sslHandle == nil: + raiseSslError() + + socket.bioIn = bioNew(bio_s_mem()) + socket.bioOut = bioNew(bio_s_mem()) + sslSetBio(socket.sslHandle, socket.bioIn, socket.bioOut) + +proc getSockOpt*(socket: AsyncSocket, opt: SOBool, level = SOL_SOCKET): bool {. + tags: [ReadIOEffect].} = + ## Retrieves option ``opt`` as a boolean value. + var res = getSockOptInt(socket.fd, cint(level), toCInt(opt)) + result = res != 0 + +proc setSockOpt*(socket: AsyncSocket, opt: SOBool, value: bool, + level = SOL_SOCKET) {.tags: [WriteIOEffect].} = + ## Sets option ``opt`` to a boolean value specified by ``value``. + var valuei = cint(if value: 1 else: 0) + setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei) + +proc isSsl*(socket: AsyncSocket): bool = + ## Determines whether ``socket`` is a SSL socket. + socket.isSsl + +proc getFd*(socket: AsyncSocket): SocketHandle = + ## Returns the socket's file descriptor. + return socket.fd + +proc isClosed*(socket: AsyncSocket): bool = + ## Determines whether the socket has been closed. + return socket.closed when isMainModule: type @@ -267,7 +468,7 @@ when isMainModule: when test == HighClient: proc main() {.async.} = var sock = newAsyncSocket() - await sock.connect("irc.freenode.net", TPort(6667)) + await sock.connect("irc.freenode.net", Port(6667)) while true: let line = await sock.recvLine() if line == "": @@ -278,25 +479,25 @@ when isMainModule: asyncCheck main() elif test == LowClient: var sock = newAsyncSocket() - var f = connect(sock, "irc.freenode.net", TPort(6667)) + var f = connect(sock, "irc.freenode.net", Port(6667)) f.callback = - proc (future: PFuture[void]) = + proc (future: Future[void]) = echo("Connected in future!") for i in 0 .. 50: var recvF = recv(sock, 10) recvF.callback = - proc (future: PFuture[string]) = + proc (future: Future[string]) = echo("Read ", future.read.len, ": ", future.read.repr) elif test == LowServer: var sock = newAsyncSocket() - sock.bindAddr(TPort(6667)) + sock.bindAddr(Port(6667)) sock.listen() - proc onAccept(future: PFuture[PAsyncSocket]) = + proc onAccept(future: Future[AsyncSocket]) = let client = future.read echo "Accepted ", client.fd.cint var t = send(client, "test\c\L") t.callback = - proc (future: PFuture[void]) = + proc (future: Future[void]) = echo("Send") client.close() diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim index 7b3b0e6f5..41d19dc0f 100644 --- a/lib/pure/base64.nim +++ b/lib/pure/base64.nim @@ -1,127 +1,128 @@ -# -# -# Nimrod's Runtime Library -# (c) Copyright 2010 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module implements a base64 encoder and decoder. - -const - cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - -template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediate.} = - ## encodes `s` into base64 representation. After `lineLen` characters, a - ## `newline` is added. - var total = ((len(s) + 2) div 3) * 4 - var numLines = (total + lineLen - 1) div lineLen - if numLines > 0: inc(total, (numLines-1) * newLine.len) - - result = newString(total) - var i = 0 - var r = 0 - var currLine = 0 - while i < s.len - 2: - var a = ord(s[i]) - var b = ord(s[i+1]) - var c = ord(s[i+2]) - result[r] = cb64[a shr 2] - result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] - result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)] - result[r+3] = cb64[c and 0x3F] - inc(r, 4) - inc(i, 3) - inc(currLine, 4) - if currLine >= lineLen and i != s.len-2: - for x in items(newLine): - result[r] = x - inc(r) - currLine = 0 - - if i < s.len-1: - var a = ord(s[i]) - var b = ord(s[i+1]) - result[r] = cb64[a shr 2] - result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] - result[r+2] = cb64[((b and 0x0F) shl 2)] - result[r+3] = '=' - if r+4 != result.len: - setLen(result, r+4) - elif i < s.len: - var a = ord(s[i]) - result[r] = cb64[a shr 2] - result[r+1] = cb64[(a and 3) shl 4] - result[r+2] = '=' - result[r+3] = '=' - if r+4 != result.len: - setLen(result, r+4) - else: - #assert(r == result.len) - -proc encode*[T:TInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string = - ## encodes `s` into base64 representation. After `lineLen` characters, a - ## `newline` is added. - encodeInternal(s, lineLen, newLine) - -proc encode*(s: string, lineLen = 75, newLine="\13\10"): string = - ## encodes `s` into base64 representation. After `lineLen` characters, a - ## `newline` is added. - encodeInternal(s, lineLen, newLine) - -proc decodeByte(b: char): int {.inline.} = - case b - of '+': result = ord('>') - of '0'..'9': result = ord(b) + 4 - of 'A'..'Z': result = ord(b) - ord('A') - of 'a'..'z': result = ord(b) - 71 - else: result = 63 - -proc decode*(s: string): string = - ## decodes a string in base64 representation back into its original form. - ## Whitespace is skipped. - const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'} - var total = ((len(s) + 3) div 4) * 3 - # total is an upper bound, as we will skip arbitrary whitespace: - result = newString(total) - - var i = 0 - var r = 0 - while true: - while s[i] in Whitespace: inc(i) - if i < s.len-3: - var a = s[i].decodeByte - var b = s[i+1].decodeByte - var c = s[i+2].decodeByte - var d = s[i+3].decodeByte - - result[r] = chr((a shl 2) and 0xff or ((b shr 4) and 0x03)) - result[r+1] = chr((b shl 4) and 0xff or ((c shr 2) and 0x0F)) - result[r+2] = chr((c shl 6) and 0xff or (d and 0x3F)) - inc(r, 3) - inc(i, 4) - else: break - assert i == s.len - # adjust the length: - if i > 0 and s[i-1] == '=': - dec(r) - if i > 1 and s[i-2] == '=': dec(r) - setLen(result, r) - -when isMainModule: - assert encode("leasure.") == "bGVhc3VyZS4=" - assert encode("easure.") == "ZWFzdXJlLg==" - assert encode("asure.") == "YXN1cmUu" - assert encode("sure.") == "c3VyZS4=" - - const longText = """Man is distinguished, not only by his reason, but by this - singular passion from other animals, which is a lust of the mind, - that by a perseverance of delight in the continued and indefatigable - generation of knowledge, exceeds the short vehemence of any carnal - pleasure.""" - const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.", - "asure.", longText] - for t in items(tests): - assert decode(encode(t)) == t - +# +# +# Nim's Runtime Library +# (c) Copyright 2010 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements a base64 encoder and decoder. + +const + cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + +template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediate.} = + ## encodes `s` into base64 representation. After `lineLen` characters, a + ## `newline` is added. + var total = ((len(s) + 2) div 3) * 4 + var numLines = (total + lineLen - 1) div lineLen + if numLines > 0: inc(total, (numLines-1) * newLine.len) + + result = newString(total) + var i = 0 + var r = 0 + var currLine = 0 + while i < s.len - 2: + var a = ord(s[i]) + var b = ord(s[i+1]) + var c = ord(s[i+2]) + result[r] = cb64[a shr 2] + result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] + result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)] + result[r+3] = cb64[c and 0x3F] + inc(r, 4) + inc(i, 3) + inc(currLine, 4) + if currLine >= lineLen and i != s.len-2: + for x in items(newLine): + result[r] = x + inc(r) + currLine = 0 + + if i < s.len-1: + var a = ord(s[i]) + var b = ord(s[i+1]) + result[r] = cb64[a shr 2] + result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] + result[r+2] = cb64[((b and 0x0F) shl 2)] + result[r+3] = '=' + if r+4 != result.len: + setLen(result, r+4) + elif i < s.len: + var a = ord(s[i]) + result[r] = cb64[a shr 2] + result[r+1] = cb64[(a and 3) shl 4] + result[r+2] = '=' + result[r+3] = '=' + if r+4 != result.len: + setLen(result, r+4) + else: + #assert(r == result.len) + discard + +proc encode*[T:SomeInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string = + ## encodes `s` into base64 representation. After `lineLen` characters, a + ## `newline` is added. + encodeInternal(s, lineLen, newLine) + +proc encode*(s: string, lineLen = 75, newLine="\13\10"): string = + ## encodes `s` into base64 representation. After `lineLen` characters, a + ## `newline` is added. + encodeInternal(s, lineLen, newLine) + +proc decodeByte(b: char): int {.inline.} = + case b + of '+': result = ord('>') + of '0'..'9': result = ord(b) + 4 + of 'A'..'Z': result = ord(b) - ord('A') + of 'a'..'z': result = ord(b) - 71 + else: result = 63 + +proc decode*(s: string): string = + ## decodes a string in base64 representation back into its original form. + ## Whitespace is skipped. + const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'} + var total = ((len(s) + 3) div 4) * 3 + # total is an upper bound, as we will skip arbitrary whitespace: + result = newString(total) + + var i = 0 + var r = 0 + while true: + while s[i] in Whitespace: inc(i) + if i < s.len-3: + var a = s[i].decodeByte + var b = s[i+1].decodeByte + var c = s[i+2].decodeByte + var d = s[i+3].decodeByte + + result[r] = chr((a shl 2) and 0xff or ((b shr 4) and 0x03)) + result[r+1] = chr((b shl 4) and 0xff or ((c shr 2) and 0x0F)) + result[r+2] = chr((c shl 6) and 0xff or (d and 0x3F)) + inc(r, 3) + inc(i, 4) + else: break + assert i == s.len + # adjust the length: + if i > 0 and s[i-1] == '=': + dec(r) + if i > 1 and s[i-2] == '=': dec(r) + setLen(result, r) + +when isMainModule: + assert encode("leasure.") == "bGVhc3VyZS4=" + assert encode("easure.") == "ZWFzdXJlLg==" + assert encode("asure.") == "YXN1cmUu" + assert encode("sure.") == "c3VyZS4=" + + const longText = """Man is distinguished, not only by his reason, but by this + singular passion from other animals, which is a lust of the mind, + that by a perseverance of delight in the continued and indefatigable + generation of knowledge, exceeds the short vehemence of any carnal + pleasure.""" + const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.", + "asure.", longText] + for t in items(tests): + assert decode(encode(t)) == t + diff --git a/lib/pure/basic2d.nim b/lib/pure/basic2d.nim index f8391a368..f2fc1566b 100644 --- a/lib/pure/basic2d.nim +++ b/lib/pure/basic2d.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Robert Persson # # See the file "copying.txt", included in this @@ -92,7 +92,7 @@ proc point2d*(x,y:float):TPoint2d {.noInit,inline.} let IDMATRIX*:TMatrix2d=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*:TPoint2d=point2d(0.0,0.0) ## Quick acces to point (0,0) XAXIS*:TVector2d=vector2d(1.0,0.0) ## Quick acces to an 2d x-axis unit vector @@ -212,7 +212,7 @@ proc mirror*(v:TVector2d):TMatrix2d {.noInit.} = xy2=v.x*v.y*2.0*nd sqd=nd*(sqx-sqy) - if nd==inf or nd==neginf: + if nd==Inf or nd==NegInf: return IDMATRIX #mirroring around a zero vector is arbitrary=>just use identity result.setElements( @@ -231,7 +231,7 @@ proc mirror*(org:TPoint2d,v:TVector2d):TMatrix2d {.noInit.} = xy2=v.x*v.y*2.0*nd sqd=nd*(sqx-sqy) - if nd==inf or nd==neginf: + if nd==Inf or nd==NegInf: return IDMATRIX #mirroring around a zero vector is arbitrary=>just use identity result.setElements( @@ -285,7 +285,7 @@ proc inverse*(m:TMatrix2d):TMatrix2d {.noInit.} = ## will be raised. let d=m.determinant if d==0.0: - raise newException(EDivByZero,"Cannot invert a zero determinant matrix") + raise newException(DivByZeroError,"Cannot invert a zero determinant matrix") result.setElements( m.by/d,-m.ay/d, @@ -360,7 +360,7 @@ proc `len=`*(v:var TVector2d,newlen:float) {.noInit.} = v.y=0.0 return - if fac==inf or fac==neginf: + if fac==Inf or fac==NegInf: #to short for float accuracy #do as good as possible: v.x=newlen @@ -431,7 +431,7 @@ proc normalize*(v:var TVector2d) {.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(EDivByZero,"Cannot normalize zero length vector") + raise newException(DivByZeroError,"Cannot normalize zero length vector") proc transformNorm*(v:var TVector2d,t:TMatrix2d)= ## Applies a normal direction transformation `t` onto `v` in place. @@ -447,7 +447,7 @@ proc transformNorm*(v:var TVector2d,t:TMatrix2d)= # | | 0 0 1 | | let d=t.determinant if(d==0.0): - raise newException(EDivByZero,"Matrix is not invertible") + raise newException(DivByZeroError,"Matrix is not invertible") let newx = (t.by*v.x-t.ay*v.y)/d v.y = (t.ax*v.y-t.bx*v.x)/d v.x = newx @@ -461,7 +461,7 @@ proc transformInv*(v:var TVector2d,t:TMatrix2d)= let d=t.determinant if(d==0.0): - raise newException(EDivByZero,"Matrix is not invertible") + raise newException(DivByZeroError,"Matrix is not invertible") let newx=(t.by*v.x-t.bx*v.y)/d v.y = (t.ax*v.y-t.ay*v.x)/d @@ -531,7 +531,7 @@ proc mirror*(v:var TVector2d,mirrvec:TVector2d)= xy2=mirrvec.x*mirrvec.y*2.0*nd sqd=nd*(sqx-sqy) - if nd==inf or nd==neginf: + if nd==Inf or nd==NegInf: return #mirroring around a zero vector is arbitrary=>keep as is is fastest let newx=xy2*v.y+sqd*v.x @@ -703,7 +703,7 @@ proc transformInv*(p:var TPoint2d,t:TMatrix2d){.inline.}= # | TX TY 1 | let d=t.determinant if d==0.0: - raise newException(EDivByZero,"Cannot invert a zero determinant matrix") + raise newException(DivByZeroError,"Cannot invert a zero determinant matrix") let newx= (t.bx*t.ty-t.by*t.tx+p.x*t.by-p.y*t.bx)/d p.y = -(t.ax*t.ty-t.ay*t.tx+p.x*t.ay-p.y*t.ax)/d @@ -820,11 +820,11 @@ proc closestPoint*(p:TPoint2d,pts:varargs[TPoint2d]):TPoint2d= var bestidx=0 - bestdist=p.sqrdist(pts[0]) + bestdist=p.sqrDist(pts[0]) curdist:float for idx in 1..high(pts): - curdist=p.sqrdist(pts[idx]) + curdist=p.sqrDist(pts[idx]) if curdist= reProbeLimit or key.isTomb): + if (not(expVal != 0 and match)) and (probes >= reProbeLimit or key.isTomb): if key.isTomb: echo("Key is Tombstone") #if probes >= reProbeLimit: echo("Too much probing " & $probes) #echo("try to resize") @@ -361,7 +393,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int, return oldVal nextTable = atomic_load_n(table.next.addr, ATOMIC_SEQ_CST) if nextTable == nil and - ((oldVal == NULL and + ((oldVal == 0 and (probes >= reProbeLimit or table.used / table.len > 0.8)) or (isPrime(oldVal))): if table.used / table.len > 0.8: echo("resize because usage ratio = " & @@ -380,12 +412,12 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int, if atomic_compare_exchange_n(table[idx].value.addr, oldVal.addr, val, false, ATOMIC_RELEASE, ATOMIC_RELAXED): #echo("val set at table " & $cast[int](table)) - if expVal != NULL: - if (oldVal == NULL or isTomb(oldVal)) and not isTomb(val): + if expVal != 0: + if (oldVal == 0 or isTomb(oldVal)) and not isTomb(val): discard atomic_add_fetch(table.active.addr, 1, ATOMIC_RELAXED) - elif not (oldVal == NULL or isTomb(oldVal)) and isTomb(val): + elif not (oldVal == 0 or isTomb(oldVal)) and isTomb(val): discard atomic_add_fetch(table.active.addr, -1, ATOMIC_RELAXED) - if oldVal == NULL and expVal != NULL: + if oldVal == 0 and expVal != 0: return setTomb(oldVal) else: return oldVal if isPrime(oldVal): @@ -415,7 +447,7 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int = if not isPrime(val): if isTomb(val): #echo("val was tomb but not prime") - return NULL + return 0 else: #echo("-GotIt- idx = ", idx, " key = ", key, " val ", val ) return val @@ -427,7 +459,7 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int = if probes >= reProbeLimit*4 or key.isTomb: if newTable == nil: #echo("too many probes and no new table ", key, " ", idx ) - return NULL + return 0 else: newTable = helpCopy(table) return getVal(newTable, key) @@ -437,10 +469,10 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int = #------------------------------------------------------------------------------ #proc set*(table: var PConcTable[TRaw,TRaw], key: TRaw, val: TRaw) = -# discard setVal(table, pack(key), pack(key), NULL, false) +# discard setVal(table, pack(key), pack(key), 0, false) #proc set*[V](table: var PConcTable[TRaw,V], key: TRaw, val: ptr V) = -# discard setVal(table, pack(key), cast[int](val), NULL, false) +# 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): @@ -451,10 +483,10 @@ proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) = var newVal = cast[int](copyShared(val)) else: var newVal = pack(val) - var oldPtr = pop(setVal(table, newKey, newVal, NULL, false)) + var oldPtr = pop(setVal(table, newKey, newVal, 0, false)) #echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr)) when not (V is TRaw): - if newVal != oldPtr and oldPtr != NULL: + if newVal != oldPtr and oldPtr != 0: deallocShared(cast[ptr V](oldPtr)) @@ -573,10 +605,3 @@ when isMainModule: # echo(i, " = ", hashInt(i) and 8191) deleteConcTable(table) - - - - - - - diff --git a/lib/pure/collections/baseutils.nim b/lib/pure/collections/baseutils.nim deleted file mode 100644 index 565a89ccb..000000000 --- a/lib/pure/collections/baseutils.nim +++ /dev/null @@ -1,41 +0,0 @@ - - - -#------------------------------------------------------------------------------ -## Useful Constants -const NULL* = 0 - - -#------------------------------------------------------------------------------ -## Memory Utility Functions - -proc newHeap*[T](): ptr T = - result = cast[ptr T](alloc0(sizeof(T))) - -proc copyNew*[T](x: var T): ptr T = - var - size = sizeof(T) - mem = alloc(size) - copyMem(mem, x.addr, size) - return cast[ptr T](mem) - -proc copyTo*[T](val: var T, dest: int) = - copyMem(pointer(dest), val.addr, sizeof(T)) - -proc allocType*[T](): pointer = alloc(sizeof(T)) - -proc newShared*[T](): ptr T = - result = cast[ptr T](allocShared0(sizeof(T))) - -proc copyShared*[T](x: var T): ptr T = - var - size = sizeof(T) - mem = allocShared(size) - copyMem(mem, x.addr, size) - return cast[ptr T](mem) - -#------------------------------------------------------------------------------ -## Pointer arithmetic - -proc `+`*(p: pointer, i: int): pointer {.inline.} = - cast[pointer](cast[int](p) + i) \ No newline at end of file diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index 1fde1f419..8f506409b 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -12,30 +12,32 @@ ## by Adam Langley. type - TNode[T] = object {.pure, final, acyclic.} + NodeObj[T] = object {.pure, final, acyclic.} byte: int ## byte index of the difference otherbits: char case isLeaf: bool - of false: child: array[0..1, ref TNode[T]] + of false: child: array[0..1, ref NodeObj[T]] of true: key: string when T isnot void: val: T - PNode[T] = ref TNode[T] - TCritBitTree*[T] = object {. + Node[T] = ref NodeObj[T] + CritBitTree*[T] = object {. pure, final.} ## The crit bit tree can either be used ## as a mapping from strings to ## some type ``T`` or as a set of ## strings if ``T`` is void. - root: PNode[T] + root: Node[T] count: int - -proc len*[T](c: TCritBitTree[T]): int = + +{.deprecated: [TCritBitTree: CritBitTree].} + +proc len*[T](c: CritBitTree[T]): int = ## returns the number of elements in `c` in O(1). result = c.count -proc rawGet[T](c: TCritBitTree[T], key: string): PNode[T] = +proc rawGet[T](c: CritBitTree[T], key: string): Node[T] = var it = c.root while it != nil: if not it.isLeaf: @@ -45,15 +47,15 @@ proc rawGet[T](c: TCritBitTree[T], key: string): PNode[T] = else: return if it.key == key: it else: nil -proc contains*[T](c: TCritBitTree[T], key: string): bool {.inline.} = +proc contains*[T](c: CritBitTree[T], key: string): bool {.inline.} = ## returns true iff `c` contains the given `key`. result = rawGet(c, key) != nil -proc hasKey*[T](c: TCritBitTree[T], key: string): bool {.inline.} = +proc hasKey*[T](c: CritBitTree[T], key: string): bool {.inline.} = ## alias for `contains`. result = rawGet(c, key) != nil -proc rawInsert[T](c: var TCritBitTree[T], key: string): PNode[T] = +proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] = if c.root == nil: new c.root c.root.isleaf = true @@ -84,7 +86,7 @@ proc rawInsert[T](c: var TCritBitTree[T], key: string): PNode[T] = let ch = it.key[newByte] let dir = (1 + (ord(ch) or newOtherBits)) shr 8 - var inner: PNode[T] + var inner: Node[T] new inner new result result.isLeaf = true @@ -106,7 +108,7 @@ proc rawInsert[T](c: var TCritBitTree[T], key: string): PNode[T] = wherep[] = inner inc c.count -proc containsOrIncl*[T](c: var TCritBitTree[T], key: string, val: T): bool = +proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: T): bool = ## returns true iff `c` contains the given `key`. If the key does not exist ## ``c[key] = val`` is performed. let oldCount = c.count @@ -115,23 +117,23 @@ proc containsOrIncl*[T](c: var TCritBitTree[T], key: string, val: T): bool = when T isnot void: if not result: n.val = val -proc containsOrIncl*(c: var TCritBitTree[void], key: string): bool = +proc containsOrIncl*(c: var CritBitTree[void], key: string): bool = ## returns true iff `c` contains the given `key`. If the key does not exist ## it is inserted into `c`. let oldCount = c.count var n = rawInsert(c, key) result = c.count == oldCount -proc incl*(c: var TCritBitTree[void], key: string) = +proc incl*(c: var CritBitTree[void], key: string) = ## includes `key` in `c`. discard rawInsert(c, key) -proc `[]=`*[T](c: var TCritBitTree[T], key: string, val: T) = +proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) = ## puts a (key, value)-pair into `t`. var n = rawInsert(c, key) n.val = val -proc `[]`*[T](c: TCritBitTree[T], key: string): T {.inline.} = +proc `[]`*[T](c: CritBitTree[T], key: string): T {.inline.} = ## retrieves the value at ``c[key]``. If `key` is not in `t`, ## default empty value for the type `B` is returned ## and no exception is raised. One can check with ``hasKey`` whether the key @@ -139,22 +141,22 @@ proc `[]`*[T](c: TCritBitTree[T], key: string): T {.inline.} = let n = rawGet(c, key) if n != nil: result = n.val -proc mget*[T](c: var TCritBitTree[T], key: string): var T {.inline.} = +proc mget*[T](c: var CritBitTree[T], key: string): var T {.inline.} = ## retrieves the value at ``c[key]``. The value can be modified. - ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. + ## If `key` is not in `t`, the ``KeyError`` exception is raised. let n = rawGet(c, key) if n != nil: result = n.val - else: raise newException(EInvalidKey, "key not found: " & $key) + else: raise newException(KeyError, "key not found: " & $key) -proc excl*[T](c: var TCritBitTree[T], key: string) = +proc excl*[T](c: var CritBitTree[T], key: string) = ## removes `key` (and its associated value) from the set `c`. ## If the `key` does not exist, nothing happens. var p = c.root var wherep = addr(c.root) - var whereq: ptr PNode = nil + var whereq: ptr Node[T] = nil if p == nil: return var dir = 0 - var q: PNode + var q: Node[T] while not p.isLeaf: whereq = wherep q = p @@ -170,7 +172,7 @@ proc excl*[T](c: var TCritBitTree[T], key: string) = whereq[] = q.child[1 - dir] dec c.count -iterator leaves[T](n: PNode[T]): PNode[T] = +iterator leaves[T](n: Node[T]): Node[T] = if n != nil: # XXX actually we could compute the necessary stack size in advance: # it's rougly log2(c.count). @@ -183,33 +185,33 @@ iterator leaves[T](n: PNode[T]): PNode[T] = assert(it != nil) yield it -iterator keys*[T](c: TCritBitTree[T]): string = +iterator keys*[T](c: CritBitTree[T]): string = ## yields all keys in lexicographical order. for x in leaves(c.root): yield x.key -iterator values*[T](c: TCritBitTree[T]): T = +iterator values*[T](c: CritBitTree[T]): T = ## yields all values of `c` in the lexicographical order of the ## corresponding keys. for x in leaves(c.root): yield x.val -iterator mvalues*[T](c: var TCritBitTree[T]): var T = +iterator mvalues*[T](c: var CritBitTree[T]): var T = ## yields all values of `c` in the lexicographical order of the ## corresponding keys. The values can be modified. for x in leaves(c.root): yield x.val -iterator items*[T](c: TCritBitTree[T]): string = +iterator items*[T](c: CritBitTree[T]): string = ## yields all keys in lexicographical order. for x in leaves(c.root): yield x.key -iterator pairs*[T](c: TCritBitTree[T]): tuple[key: string, val: T] = +iterator pairs*[T](c: CritBitTree[T]): tuple[key: string, val: T] = ## yields all (key, value)-pairs of `c`. for x in leaves(c.root): yield (x.key, x.val) -iterator mpairs*[T](c: var TCritBitTree[T]): tuple[key: string, val: var T] = +iterator mpairs*[T](c: var CritBitTree[T]): tuple[key: string, val: var T] = ## yields all (key, value)-pairs of `c`. The yielded values can be modified. for x in leaves(c.root): yield (x.key, x.val) -proc allprefixedAux[T](c: TCritBitTree[T], key: string): PNode[T] = +proc allprefixedAux[T](c: CritBitTree[T], key: string): Node[T] = var p = c.root var top = p if p != nil: @@ -223,42 +225,42 @@ proc allprefixedAux[T](c: TCritBitTree[T], key: string): PNode[T] = if p.key[i] != key[i]: return result = top -iterator itemsWithPrefix*[T](c: TCritBitTree[T], prefix: string): string = +iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string): string = ## yields all keys starting with `prefix`. let top = allprefixedAux(c, prefix) for x in leaves(top): yield x.key -iterator keysWithPrefix*[T](c: TCritBitTree[T], prefix: string): string = +iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string): string = ## yields all keys starting with `prefix`. let top = allprefixedAux(c, prefix) for x in leaves(top): yield x.key -iterator valuesWithPrefix*[T](c: TCritBitTree[T], prefix: string): T = +iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string): T = ## yields all values of `c` starting with `prefix` of the ## corresponding keys. let top = allprefixedAux(c, prefix) for x in leaves(top): yield x.val -iterator mvaluesWithPrefix*[T](c: var TCritBitTree[T], prefix: string): var T = +iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string): var T = ## yields all values of `c` starting with `prefix` of the ## corresponding keys. The values can be modified. let top = allprefixedAux(c, prefix) for x in leaves(top): yield x.val -iterator pairsWithPrefix*[T](c: TCritBitTree[T], +iterator pairsWithPrefix*[T](c: CritBitTree[T], prefix: string): tuple[key: string, val: T] = ## yields all (key, value)-pairs of `c` starting with `prefix`. let top = allprefixedAux(c, prefix) for x in leaves(top): yield (x.key, x.val) -iterator mpairsWithPrefix*[T](c: var TCritBitTree[T], +iterator mpairsWithPrefix*[T](c: var CritBitTree[T], prefix: string): tuple[key: string, val: var T] = ## yields all (key, value)-pairs of `c` starting with `prefix`. ## The yielded values can be modified. let top = allprefixedAux(c, prefix) for x in leaves(top): yield (x.key, x.val) -proc `$`*[T](c: TCritBitTree[T]): string = +proc `$`*[T](c: CritBitTree[T]): string = ## turns `c` into a string representation. Example outputs: ## ``{keyA: value, keyB: value}``, ``{:}`` ## If `T` is void the outputs look like: @@ -285,7 +287,7 @@ proc `$`*[T](c: TCritBitTree[T]): string = result.add("}") when isMainModule: - var r: TCritBitTree[void] + var r: CritBitTree[void] r.incl "abc" r.incl "xyz" r.incl "def" diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim index f1e67fc0e..5d9c3f445 100644 --- a/lib/pure/collections/intsets.nim +++ b/lib/pure/collections/intsets.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -9,7 +9,7 @@ ## The ``intsets`` module implements an efficient int set implemented as a ## sparse bit set. -## **Note**: Since Nimrod currently does not allow the assignment operator to +## **Note**: Since Nim currently does not allow the assignment operator to ## be overloaded, ``=`` for int sets performs some rather meaningless shallow ## copy; use ``assign`` to get a deep copy. @@ -17,7 +17,7 @@ import os, hashes, math type - TBitScalar = int + BitScalar = int const InitIntSetSize = 8 # must be a power of two! @@ -25,8 +25,8 @@ const BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and # divisible by 64 TrunkMask = BitsPerTrunk - 1 - IntsPerTrunk = BitsPerTrunk div (sizeof(TBitScalar) * 8) - IntShift = 5 + ord(sizeof(TBitScalar) == 8) # 5 or 6, depending on int width + IntsPerTrunk = BitsPerTrunk div (sizeof(BitScalar) * 8) + IntShift = 5 + ord(sizeof(BitScalar) == 8) # 5 or 6, depending on int width IntMask = 1 shl IntShift - 1 type @@ -34,15 +34,16 @@ type TTrunk {.final.} = object next: PTrunk # all nodes are connected with this pointer key: int # start address at bit 0 - bits: array[0..IntsPerTrunk - 1, TBitScalar] # a bit vector + bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector TTrunkSeq = seq[PTrunk] - TIntSet* {.final.} = object ## an efficient set of 'int' implemented as a - ## sparse bit set + IntSet* = object ## an efficient set of 'int' implemented as a sparse bit set counter, max: int head: PTrunk data: TTrunkSeq +{.deprecated: [TIntSet: IntSet].} + proc mustRehash(length, counter: int): bool {.inline.} = assert(length > counter) result = (length * 2 < counter * 3) or (length - counter < 4) @@ -50,7 +51,7 @@ proc mustRehash(length, counter: int): bool {.inline.} = proc nextTry(h, maxHash: THash): THash {.inline.} = result = ((5 * h) + 1) and maxHash -proc intSetGet(t: TIntSet, key: int): PTrunk = +proc intSetGet(t: IntSet, key: int): PTrunk = var h = key and t.max while t.data[h] != nil: if t.data[h].key == key: @@ -58,7 +59,7 @@ proc intSetGet(t: TIntSet, key: int): PTrunk = h = nextTry(h, t.max) result = nil -proc intSetRawInsert(t: TIntSet, data: var TTrunkSeq, desc: PTrunk) = +proc intSetRawInsert(t: IntSet, data: var TTrunkSeq, desc: PTrunk) = var h = desc.key and t.max while data[h] != nil: assert(data[h] != desc) @@ -66,7 +67,7 @@ proc intSetRawInsert(t: TIntSet, data: var TTrunkSeq, desc: PTrunk) = assert(data[h] == nil) data[h] = desc -proc intSetEnlarge(t: var TIntSet) = +proc intSetEnlarge(t: var IntSet) = var n: TTrunkSeq var oldMax = t.max t.max = ((t.max + 1) * 2) - 1 @@ -75,7 +76,7 @@ proc intSetEnlarge(t: var TIntSet) = if t.data[i] != nil: intSetRawInsert(t, n, t.data[i]) swap(t.data, n) -proc intSetPut(t: var TIntSet, key: int): PTrunk = +proc intSetPut(t: var IntSet, key: int): PTrunk = var h = key and t.max while t.data[h] != nil: if t.data[h].key == key: @@ -92,7 +93,7 @@ proc intSetPut(t: var TIntSet, key: int): PTrunk = t.head = result t.data[h] = result -proc contains*(s: TIntSet, key: int): bool = +proc contains*(s: IntSet, key: int): bool = ## returns true iff `key` is in `s`. var t = intSetGet(s, `shr`(key, TrunkShift)) if t != nil: @@ -101,14 +102,14 @@ proc contains*(s: TIntSet, key: int): bool = else: result = false -proc incl*(s: var TIntSet, key: int) = +proc incl*(s: var IntSet, key: int) = ## includes an element `key` in `s`. var t = intSetPut(s, `shr`(key, TrunkShift)) var u = key and TrunkMask t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] or `shl`(1, u and IntMask) -proc excl*(s: var TIntSet, key: int) = +proc excl*(s: var IntSet, key: int) = ## excludes `key` from the set `s`. var t = intSetGet(s, `shr`(key, TrunkShift)) if t != nil: @@ -116,7 +117,7 @@ proc excl*(s: var TIntSet, key: int) = t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] and not `shl`(1, u and IntMask) -proc containsOrIncl*(s: var TIntSet, key: int): bool = +proc containsOrIncl*(s: var IntSet, key: int): bool = ## returns true if `s` contains `key`, otherwise `key` is included in `s` ## and false is returned. var t = intSetGet(s, `shr`(key, TrunkShift)) @@ -130,14 +131,14 @@ proc containsOrIncl*(s: var TIntSet, key: int): bool = incl(s, key) result = false -proc initIntSet*: TIntSet = +proc initIntSet*: IntSet = ## creates a new int set that is empty. newSeq(result.data, InitIntSetSize) result.max = InitIntSetSize-1 result.counter = 0 result.head = nil -proc assign*(dest: var TIntSet, src: TIntSet) = +proc assign*(dest: var IntSet, src: IntSet) = ## copies `src` to `dest`. `dest` does not need to be initialized by ## `initIntSet`. dest.counter = src.counter @@ -161,7 +162,7 @@ proc assign*(dest: var TIntSet, src: TIntSet) = it = it.next -iterator items*(s: TIntSet): int {.inline.} = +iterator items*(s: IntSet): int {.inline.} = ## iterates over any included element of `s`. var r = s.head while r != nil: @@ -186,11 +187,11 @@ template dollarImpl(): stmt = result.add($key) result.add("}") -proc `$`*(s: TIntSet): string = +proc `$`*(s: IntSet): string = ## The `$` operator for int sets. dollarImpl() -proc empty*(s: TIntSet): bool {.inline.} = +proc empty*(s: IntSet): bool {.inline.} = ## returns true if `s` is empty. This is safe to call even before ## the set has been initialized with `initIntSet`. result = s.counter == 0 diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index b8f8d20b5..929de5973 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -15,52 +15,59 @@ when not defined(nimhygiene): {.pragma: dirty.} type - TDoublyLinkedNode* {.pure, - final.}[T] = object ## a node a doubly linked list consists of - next*, prev*: ref TDoublyLinkedNode[T] + DoublyLinkedNodeObj*[T] = object ## a node a doubly linked list consists of + next*, prev*: ref DoublyLinkedNodeObj[T] value*: T - PDoublyLinkedNode*[T] = ref TDoublyLinkedNode[T] + DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T] - TSinglyLinkedNode* {.pure, - final.}[T] = object ## a node a singly linked list consists of - next*: ref TSinglyLinkedNode[T] + SinglyLinkedNodeObj*[T] = object ## a node a singly linked list consists of + next*: ref SinglyLinkedNodeObj[T] value*: T - PSinglyLinkedNode*[T] = ref TSinglyLinkedNode[T] + SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T] - TSinglyLinkedList* {.pure, final.}[T] = object ## a singly linked list - head*, tail*: PSinglyLinkedNode[T] + SinglyLinkedList*[T] = object ## a singly linked list + head*, tail*: SinglyLinkedNode[T] - TDoublyLinkedList* {.pure, final.}[T] = object ## a doubly linked list - head*, tail*: PDoublyLinkedNode[T] + DoublyLinkedList*[T] = object ## a doubly linked list + head*, tail*: DoublyLinkedNode[T] - TSinglyLinkedRing* {.pure, final.}[T] = object ## a singly linked ring - head*: PSinglyLinkedNode[T] + SinglyLinkedRing*[T] = object ## a singly linked ring + head*: SinglyLinkedNode[T] - TDoublyLinkedRing* {.pure, final.}[T] = object ## a doubly linked ring - head*: PDoublyLinkedNode[T] + DoublyLinkedRing*[T] = object ## a doubly linked ring + head*: DoublyLinkedNode[T] -proc initSinglyLinkedList*[T](): TSinglyLinkedList[T] = +{.deprecated: [TDoublyLinkedNode: DoublyLinkedNodeObj, + PDoublyLinkedNode: DoublyLinkedNode, + TSinglyLinkedNode: SinglyLinkedNodeObj, + PSinglyLinkedNode: SinglyLinkedNode, + TDoublyLinkedList: DoublyLinkedList, + TSinglyLinkedRing: SinglyLinkedRing, + TDoublyLinkedRing: DoublyLinkedRing, + TSinglyLinkedList: SinglyLinkedList].} + +proc initSinglyLinkedList*[T](): SinglyLinkedList[T] = ## creates a new singly linked list that is empty. discard -proc initDoublyLinkedList*[T](): TDoublyLinkedList[T] = +proc initDoublyLinkedList*[T](): DoublyLinkedList[T] = ## creates a new doubly linked list that is empty. discard -proc initSinglyLinkedRing*[T](): TSinglyLinkedRing[T] = +proc initSinglyLinkedRing*[T](): SinglyLinkedRing[T] = ## creates a new singly linked ring that is empty. discard -proc initDoublyLinkedRing*[T](): TDoublyLinkedRing[T] = +proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] = ## creates a new doubly linked ring that is empty. discard -proc newDoublyLinkedNode*[T](value: T): PDoublyLinkedNode[T] = +proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] = ## creates a new doubly linked node with the given `value`. new(result) result.value = value -proc newSinglyLinkedNode*[T](value: T): PSinglyLinkedNode[T] = +proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] = ## creates a new singly linked node with the given `value`. new(result) result.value = value @@ -99,38 +106,38 @@ template findImpl() {.dirty.} = for x in nodes(L): if x.value == value: return x -iterator items*[T](L: TDoublyLinkedList[T]): T = +iterator items*[T](L: DoublyLinkedList[T]): T = ## yields every value of `L`. itemsListImpl() -iterator items*[T](L: TSinglyLinkedList[T]): T = +iterator items*[T](L: SinglyLinkedList[T]): T = ## yields every value of `L`. itemsListImpl() -iterator items*[T](L: TSinglyLinkedRing[T]): T = +iterator items*[T](L: SinglyLinkedRing[T]): T = ## yields every value of `L`. itemsRingImpl() -iterator items*[T](L: TDoublyLinkedRing[T]): T = +iterator items*[T](L: DoublyLinkedRing[T]): T = ## yields every value of `L`. itemsRingImpl() -iterator nodes*[T](L: TSinglyLinkedList[T]): PSinglyLinkedNode[T] = +iterator nodes*[T](L: SinglyLinkedList[T]): SinglyLinkedNode[T] = ## iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. nodesListImpl() -iterator nodes*[T](L: TDoublyLinkedList[T]): PDoublyLinkedNode[T] = +iterator nodes*[T](L: DoublyLinkedList[T]): DoublyLinkedNode[T] = ## iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. nodesListImpl() -iterator nodes*[T](L: TSinglyLinkedRing[T]): PSinglyLinkedNode[T] = +iterator nodes*[T](L: SinglyLinkedRing[T]): SinglyLinkedNode[T] = ## iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. nodesRingImpl() -iterator nodes*[T](L: TDoublyLinkedRing[T]): PDoublyLinkedNode[T] = +iterator nodes*[T](L: DoublyLinkedRing[T]): DoublyLinkedNode[T] = ## iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. nodesRingImpl() @@ -142,73 +149,73 @@ template dollarImpl() {.dirty.} = result.add($x.value) result.add("]") -proc `$`*[T](L: TSinglyLinkedList[T]): string = +proc `$`*[T](L: SinglyLinkedList[T]): string = ## turns a list into its string representation. dollarImpl() -proc `$`*[T](L: TDoublyLinkedList[T]): string = +proc `$`*[T](L: DoublyLinkedList[T]): string = ## turns a list into its string representation. dollarImpl() -proc `$`*[T](L: TSinglyLinkedRing[T]): string = +proc `$`*[T](L: SinglyLinkedRing[T]): string = ## turns a list into its string representation. dollarImpl() -proc `$`*[T](L: TDoublyLinkedRing[T]): string = +proc `$`*[T](L: DoublyLinkedRing[T]): string = ## turns a list into its string representation. dollarImpl() -proc find*[T](L: TSinglyLinkedList[T], value: T): PSinglyLinkedNode[T] = +proc find*[T](L: SinglyLinkedList[T], value: T): SinglyLinkedNode[T] = ## searches in the list for a value. Returns nil if the value does not ## exist. findImpl() -proc find*[T](L: TDoublyLinkedList[T], value: T): PDoublyLinkedNode[T] = +proc find*[T](L: DoublyLinkedList[T], value: T): DoublyLinkedNode[T] = ## searches in the list for a value. Returns nil if the value does not ## exist. findImpl() -proc find*[T](L: TSinglyLinkedRing[T], value: T): PSinglyLinkedNode[T] = +proc find*[T](L: SinglyLinkedRing[T], value: T): SinglyLinkedNode[T] = ## searches in the list for a value. Returns nil if the value does not ## exist. findImpl() -proc find*[T](L: TDoublyLinkedRing[T], value: T): PDoublyLinkedNode[T] = +proc find*[T](L: DoublyLinkedRing[T], value: T): DoublyLinkedNode[T] = ## searches in the list for a value. Returns nil if the value does not ## exist. findImpl() -proc contains*[T](L: TSinglyLinkedList[T], value: T): bool {.inline.} = +proc contains*[T](L: SinglyLinkedList[T], value: T): bool {.inline.} = ## searches in the list for a value. Returns false if the value does not ## exist, true otherwise. result = find(L, value) != nil -proc contains*[T](L: TDoublyLinkedList[T], value: T): bool {.inline.} = +proc contains*[T](L: DoublyLinkedList[T], value: T): bool {.inline.} = ## searches in the list for a value. Returns false if the value does not ## exist, true otherwise. result = find(L, value) != nil -proc contains*[T](L: TSinglyLinkedRing[T], value: T): bool {.inline.} = +proc contains*[T](L: SinglyLinkedRing[T], value: T): bool {.inline.} = ## searches in the list for a value. Returns false if the value does not ## exist, true otherwise. result = find(L, value) != nil -proc contains*[T](L: TDoublyLinkedRing[T], value: T): bool {.inline.} = +proc contains*[T](L: DoublyLinkedRing[T], value: T): bool {.inline.} = ## searches in the list for a value. Returns false if the value does not ## exist, true otherwise. result = find(L, value) != nil -proc prepend*[T](L: var TSinglyLinkedList[T], - n: PSinglyLinkedNode[T]) {.inline.} = +proc prepend*[T](L: var SinglyLinkedList[T], + n: SinglyLinkedNode[T]) {.inline.} = ## prepends a node to `L`. Efficiency: O(1). n.next = L.head L.head = n -proc prepend*[T](L: var TSinglyLinkedList[T], value: T) {.inline.} = +proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} = ## prepends a node to `L`. Efficiency: O(1). prepend(L, newSinglyLinkedNode(value)) -proc append*[T](L: var TDoublyLinkedList[T], n: PDoublyLinkedNode[T]) = +proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = ## appends a node `n` to `L`. Efficiency: O(1). n.next = nil n.prev = L.tail @@ -218,11 +225,11 @@ proc append*[T](L: var TDoublyLinkedList[T], n: PDoublyLinkedNode[T]) = L.tail = n if L.head == nil: L.head = n -proc append*[T](L: var TDoublyLinkedList[T], value: T) = +proc append*[T](L: var DoublyLinkedList[T], value: T) = ## appends a value to `L`. Efficiency: O(1). append(L, newDoublyLinkedNode(value)) -proc prepend*[T](L: var TDoublyLinkedList[T], n: PDoublyLinkedNode[T]) = +proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = ## prepends a node `n` to `L`. Efficiency: O(1). n.prev = nil n.next = L.head @@ -232,11 +239,11 @@ proc prepend*[T](L: var TDoublyLinkedList[T], n: PDoublyLinkedNode[T]) = L.head = n if L.tail == nil: L.tail = n -proc prepend*[T](L: var TDoublyLinkedList[T], value: T) = +proc prepend*[T](L: var DoublyLinkedList[T], value: T) = ## prepends a value to `L`. Efficiency: O(1). prepend(L, newDoublyLinkedNode(value)) -proc remove*[T](L: var TDoublyLinkedList[T], n: PDoublyLinkedNode[T]) = +proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = ## removes `n` from `L`. Efficiency: O(1). if n == L.tail: L.tail = n.prev if n == L.head: L.head = n.next @@ -244,7 +251,7 @@ proc remove*[T](L: var TDoublyLinkedList[T], n: PDoublyLinkedNode[T]) = if n.prev != nil: n.prev.next = n.next -proc prepend*[T](L: var TSinglyLinkedRing[T], n: PSinglyLinkedNode[T]) = +proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = ## prepends a node `n` to `L`. Efficiency: O(1). if L.head != nil: n.next = L.head @@ -253,11 +260,11 @@ proc prepend*[T](L: var TSinglyLinkedRing[T], n: PSinglyLinkedNode[T]) = n.next = n L.head = n -proc prepend*[T](L: var TSinglyLinkedRing[T], value: T) = +proc prepend*[T](L: var SinglyLinkedRing[T], value: T) = ## prepends a value to `L`. Efficiency: O(1). prepend(L, newSinglyLinkedNode(value)) -proc append*[T](L: var TDoublyLinkedRing[T], n: PDoublyLinkedNode[T]) = +proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = ## appends a node `n` to `L`. Efficiency: O(1). if L.head != nil: n.next = L.head @@ -269,11 +276,11 @@ proc append*[T](L: var TDoublyLinkedRing[T], n: PDoublyLinkedNode[T]) = n.next = n L.head = n -proc append*[T](L: var TDoublyLinkedRing[T], value: T) = +proc append*[T](L: var DoublyLinkedRing[T], value: T) = ## appends a value to `L`. Efficiency: O(1). append(L, newDoublyLinkedNode(value)) -proc prepend*[T](L: var TDoublyLinkedRing[T], n: PDoublyLinkedNode[T]) = +proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = ## prepends a node `n` to `L`. Efficiency: O(1). if L.head != nil: n.next = L.head @@ -285,11 +292,11 @@ proc prepend*[T](L: var TDoublyLinkedRing[T], n: PDoublyLinkedNode[T]) = n.next = n L.head = n -proc prepend*[T](L: var TDoublyLinkedRing[T], value: T) = +proc prepend*[T](L: var DoublyLinkedRing[T], value: T) = ## prepends a value to `L`. Efficiency: O(1). prepend(L, newDoublyLinkedNode(value)) -proc remove*[T](L: var TDoublyLinkedRing[T], n: PDoublyLinkedNode[T]) = +proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = ## removes `n` from `L`. Efficiency: O(1). n.next.prev = n.prev n.prev.next = n.next @@ -300,5 +307,3 @@ proc remove*[T](L: var TDoublyLinkedRing[T], n: PDoublyLinkedNode[T]) = L.head = nil else: L.head = L.head.prev - - diff --git a/lib/pure/collections/queues.nim b/lib/pure/collections/queues.nim index 5481272f0..defce657f 100644 --- a/lib/pure/collections/queues.nim +++ b/lib/pure/collections/queues.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -8,25 +8,29 @@ # ## Implementation of a queue. The underlying implementation uses a ``seq``. +## Note: For inter thread communication use +## a `TChannel `_ instead. import math type - TQueue* {.pure, final.}[T] = object ## a queue + Queue*[T] = object ## a queue data: seq[T] rd, wr, count, mask: int - -proc initQueue*[T](initialSize=4): TQueue[T] = + +{.deprecated: [TQueue: Queue].} + +proc initQueue*[T](initialSize=4): Queue[T] = ## creates a new queue. `initialSize` needs to be a power of 2. - assert IsPowerOfTwo(initialSize) + assert isPowerOfTwo(initialSize) result.mask = initialSize-1 newSeq(result.data, initialSize) -proc len*[T](q: TQueue[T]): int = +proc len*[T](q: Queue[T]): int = ## returns the number of elements of `q`. result = q.count -iterator items*[T](q: TQueue[T]): T = +iterator items*[T](q: Queue[T]): T = ## yields every element of `q`. var i = q.rd var c = q.count @@ -35,7 +39,7 @@ iterator items*[T](q: TQueue[T]): T = yield q.data[i] i = (i + 1) and q.mask -proc add*[T](q: var TQueue[T], item: T) = +proc add*[T](q: var Queue[T], item: T) = ## adds an `item` to the end of the queue `q`. var cap = q.mask+1 if q.count >= cap: @@ -53,18 +57,18 @@ proc add*[T](q: var TQueue[T], item: T) = q.data[q.wr] = item q.wr = (q.wr + 1) and q.mask -proc enqueue*[T](q: var TQueue[T], item: T) = +proc enqueue*[T](q: var Queue[T], item: T) = ## alias for the ``add`` operation. add(q, item) -proc dequeue*[T](q: var TQueue[T]): T = +proc dequeue*[T](q: var Queue[T]): T = ## removes and returns the first element of the queue `q`. assert q.count > 0 dec q.count result = q.data[q.rd] q.rd = (q.rd + 1) and q.mask -proc `$`*[T](q: TQueue[T]): string = +proc `$`*[T](q: Queue[T]): string = ## turns a queue into its string representation. result = "[" for x in items(q): diff --git a/lib/pure/collections/rtarrays.nim b/lib/pure/collections/rtarrays.nim new file mode 100644 index 000000000..e5d0b0d8a --- /dev/null +++ b/lib/pure/collections/rtarrays.nim @@ -0,0 +1,36 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2014 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + + +## Module that implements a fixed length array whose size +## is determined at runtime. Note: This is not ready for other people to use! + +const + ArrayPartSize = 10 + +type + RtArray*[T] = object ## + L: Natural + spart: seq[T] + apart: array [ArrayPartSize, T] + UncheckedArray* {.unchecked.}[T] = array[0..100_000_000, T] + +template usesSeqPart(x): expr = x.L > ArrayPartSize + +proc initRtArray*[T](len: Natural): RtArray[T] = + result.L = len + if usesSeqPart(result): + newSeq(result.spart, len) + +proc getRawData*[T](x: var RtArray[T]): ptr UncheckedArray[T] = + if usesSeqPart(x): cast[ptr UncheckedArray[T]](addr(x.spart[0])) + else: cast[ptr UncheckedArray[T]](addr(x.apart[0])) + +#proc len*[T](x: RtArray[T]): int = x.L + diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 2629e9f40..befc9bacb 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2011 Alex Mitchell # # See the file "copying.txt", included in this @@ -31,7 +31,7 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] = ## ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: ## let ## s1 = @[1, 2, 3] ## s2 = @[4, 5] @@ -50,7 +50,7 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] = proc deduplicate*[T](seq1: seq[T]): seq[T] = ## Returns a new sequence without duplicates. ## - ## .. code-block:: nimrod + ## .. code-block:: ## let ## dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4] ## dup2 = @["a", "a", "c", "d", "d"] @@ -61,6 +61,8 @@ proc deduplicate*[T](seq1: seq[T]): seq[T] = result = @[] for itm in items(seq1): if not result.contains(itm): result.add(itm) + +{.deprecated: [distnct: deduplicate].} proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] = ## Returns a new sequence with a combination of the two input sequences. @@ -69,7 +71,7 @@ proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] = ## fields `a` and `b`. If one sequence is shorter, the remaining items in the ## longer sequence are discarded. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: ## let ## short = @[1, 2, 3] ## long = @[6, 5, 4, 3, 2, 1] @@ -104,7 +106,7 @@ proc distribute*[T](s: seq[T], num: int, spread = true): seq[seq[T]] = ## ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: ## let numbers = @[1, 2, 3, 4, 5, 6, 7] ## assert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]] ## assert numbers.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]] @@ -155,7 +157,7 @@ iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T = ## ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: ## let numbers = @[1, 4, 5, 8, 9, 7, 4] ## for n in filter(numbers, proc (x: int): bool = x mod 2 == 0): ## echo($n) @@ -169,7 +171,7 @@ proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] = ## ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: ## let ## colors = @["red", "yellow", "black"] ## f1 = filter(colors, proc(x: string): bool = x.len < 6) @@ -184,7 +186,7 @@ proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.}) = ## ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: ## var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1] ## keepIf(floats, proc(x: float): bool = x > 10) ## assert floats == @[13.0, 12.5, 10.1] @@ -202,7 +204,7 @@ proc delete*[T](s: var seq[T], first=0, last=0) = ## ## Example: ## - ##.. code-block:: nimrod + ##.. code-block:: ## let outcome = @[1,1,1,1,1,1,1,1] ## var dest = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1] ## dest.delete(3, 8) @@ -223,7 +225,7 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos=0) = ## ## Example: ## - ##.. code-block:: nimrod + ##.. code-block:: ## var dest = @[1,1,1,1,1,1,1,1] ## let ## src = @[2,2,2,2,2,2] @@ -254,7 +256,7 @@ template filterIt*(seq1, pred: expr): expr {.immediate.} = ## the ``it`` variable for testing, like: ``filterIt("abcxyz", it == 'x')``. ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: ## let ## temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44] ## acceptable = filterIt(temperatures, it < 50 and it > -10) @@ -273,7 +275,7 @@ template keepItIf*(varSeq, pred: expr) = ## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``. ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: ## var candidates = @["foo", "bar", "baz", "foobar"] ## keepItIf(candidates, it.len == 3 and it[0] == 'b') ## assert candidates == @["bar", "baz"] @@ -292,7 +294,7 @@ template toSeq*(iter: expr): expr {.immediate.} = ## ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: ## let ## numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] ## odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: @@ -300,6 +302,10 @@ template toSeq*(iter: expr): expr {.immediate.} = ## result = true) ## assert odd_numbers == @[1, 3, 5, 7, 9] ## + ## **Note**: Since this is an immediate macro, you cannot always invoke this + ## as ``x.toSeq``, depending on the ``x``. + ## See `this `_ + ## for an explanation. var result {.gensym.}: seq[type(iter)] = @[] for x in iter: add(result, x) result @@ -318,18 +324,18 @@ template foldl*(sequence, operation: expr): expr = ## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) - ## 3). Example: ## - ## .. code-block:: nimrod + ## .. code-block:: ## let ## numbers = @[5, 9, 11] ## addition = foldl(numbers, a + b) ## substraction = foldl(numbers, a - b) ## multiplication = foldl(numbers, a * b) - ## words = @["nim", "rod", "is", "cool"] + ## words = @["nim", "is", "cool"] ## concatenation = foldl(words, a & b) ## assert addition == 25, "Addition is (((5)+9)+11)" ## assert substraction == -15, "Substraction is (((5)-9)-11)" ## assert multiplication == 495, "Multiplication is (((5)*9)*11)" - ## assert concatenation == "nimrodiscool" + ## assert concatenation == "nimiscool" assert sequence.len > 0, "Can't fold empty sequences" var result {.gensym.}: type(sequence[0]) result = sequence[0] @@ -354,18 +360,18 @@ template foldr*(sequence, operation: expr): expr = ## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 - ## (3))). Example: ## - ## .. code-block:: nimrod + ## .. code-block:: ## let ## numbers = @[5, 9, 11] ## addition = foldr(numbers, a + b) ## substraction = foldr(numbers, a - b) ## multiplication = foldr(numbers, a * b) - ## words = @["nim", "rod", "is", "cool"] + ## words = @["nim", "is", "cool"] ## concatenation = foldr(words, a & b) ## assert addition == 25, "Addition is (5+(9+(11)))" ## assert substraction == 7, "Substraction is (5-(9-(11)))" ## assert multiplication == 495, "Multiplication is (5*(9*(11)))" - ## assert concatenation == "nimrodiscool" + ## assert concatenation == "nimiscool" assert sequence.len > 0, "Can't fold empty sequences" var result {.gensym.}: type(sequence[0]) result = sequence[sequence.len - 1] @@ -384,7 +390,7 @@ template mapIt*(seq1, typ, pred: expr): expr = ## since the new returned sequence can have a different type than the ## original. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: ## let ## nums = @[1, 2, 3, 4] ## strings = nums.mapIt(string, $(4 * it)) @@ -401,7 +407,7 @@ template mapIt*(varSeq, pred: expr) = ## expression. The expression has to return the same type as the sequence you ## are mutating. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: ## var nums = @[1, 2, 3, 4] ## nums.mapIt(it * 3) ## assert nums[0] + nums[3] == 15 @@ -412,7 +418,7 @@ template mapIt*(varSeq, pred: expr) = template newSeqWith*(len: int, init: expr): expr = ## creates a new sequence, calling `init` to initialize each value. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: ## var seq2D = newSeqWith(20, newSeq[bool](10)) ## seq2D[0][0] = true ## seq2D[1][0] = true @@ -503,12 +509,12 @@ when isMainModule: addition = foldl(numbers, a + b) substraction = foldl(numbers, a - b) multiplication = foldl(numbers, a * b) - words = @["nim", "rod", "is", "cool"] + words = @["nim", "is", "cool"] concatenation = foldl(words, a & b) assert addition == 25, "Addition is (((5)+9)+11)" assert substraction == -15, "Substraction is (((5)-9)-11)" assert multiplication == 495, "Multiplication is (((5)*9)*11)" - assert concatenation == "nimrodiscool" + assert concatenation == "nimiscool" block: # foldr tests let @@ -516,12 +522,12 @@ when isMainModule: addition = foldr(numbers, a + b) substraction = foldr(numbers, a - b) multiplication = foldr(numbers, a * b) - words = @["nim", "rod", "is", "cool"] + words = @["nim", "is", "cool"] concatenation = foldr(words, a & b) assert addition == 25, "Addition is (5+(9+(11)))" assert substraction == 7, "Substraction is (5-(9-(11)))" assert multiplication == 495, "Multiplication is (5*(9*(11)))" - assert concatenation == "nimrodiscool" + assert concatenation == "nimiscool" block: # delete tests let outcome = @[1,1,1,1,1,1,1,1] diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index ce901963e..92ef3152d 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -24,18 +24,20 @@ when not defined(nimhygiene): {.pragma: dirty.} type - TSlotEnum = enum seEmpty, seFilled, seDeleted - TKeyValuePair[A] = tuple[slot: TSlotEnum, key: A] - TKeyValuePairSeq[A] = seq[TKeyValuePair[A]] - TSet* {.final, myShallow.}[A] = object ## \ + SlotEnum = enum seEmpty, seFilled, seDeleted + KeyValuePair[A] = tuple[slot: SlotEnum, key: A] + KeyValuePairSeq[A] = seq[KeyValuePair[A]] + HashSet* {.myShallow.}[A] = object ## \ ## A generic hash set. ## - ## Use `init() <#init,TSet[A],int>`_ or `initSet[type]() <#initSet>`_ + ## Use `init() <#init,HashSet[A],int>`_ or `initSet[type]() <#initSet>`_ ## before calling other procs on it. - data: TKeyValuePairSeq[A] + data: KeyValuePairSeq[A] counter: int -proc isValid*[A](s: TSet[A]): bool = +{.deprecated: [TSet: HashSet].} + +proc isValid*[A](s: HashSet[A]): bool = ## Returns `true` if the set has been initialized with `initSet <#initSet>`_. ## ## Most operations over an uninitialized set will crash at runtime and @@ -43,13 +45,13 @@ proc isValid*[A](s: TSet[A]): bool = ## your own procs to verify that sets passed to your procs are correctly ## initialized. Example: ## - ## .. code-block :: nimrod + ## .. code-block :: ## proc savePreferences(options: TSet[string]) = ## assert options.isValid, "Pass an initialized set!" ## # Do stuff here, may crash in release builds! result = not s.data.isNil -proc len*[A](s: TSet[A]): int = +proc len*[A](s: HashSet[A]): int = ## Returns the number of keys in `s`. ## ## Due to an implementation detail you can call this proc on variables which @@ -63,14 +65,14 @@ proc len*[A](s: TSet[A]): int = ## assert values.len == 0 result = s.counter -proc card*[A](s: TSet[A]): int = +proc card*[A](s: HashSet[A]): int = ## Alias for `len() <#len,TSet[A]>`_. ## ## Card stands for the `cardinality ## `_ of a set. result = s.counter -iterator items*[A](s: TSet[A]): A = +iterator items*[A](s: HashSet[A]): A = ## Iterates over keys in the set `s`. ## ## If you need a sequence with the keys you can use `sequtils.toSeq() @@ -118,10 +120,10 @@ template rawInsertImpl() {.dirty.} = data[h].key = key data[h].slot = seFilled -proc rawGet[A](s: TSet[A], key: A): int = +proc rawGet[A](s: HashSet[A], key: A): int = rawGetImpl() -proc mget*[A](s: var TSet[A], key: A): var A = +proc mget*[A](s: var HashSet[A], key: A): var A = ## returns the element that is actually stored in 's' which has the same ## value as 'key' or raises the ``EInvalidKey`` exception. This is useful ## when one overloaded 'hash' and '==' but still needs reference semantics @@ -129,9 +131,9 @@ proc mget*[A](s: var TSet[A], key: A): var A = assert s.isValid, "The set needs to be initialized." var index = rawGet(s, key) if index >= 0: result = s.data[index].key - else: raise newException(EInvalidKey, "key not found: " & $key) + else: raise newException(KeyError, "key not found: " & $key) -proc contains*[A](s: TSet[A], key: A): bool = +proc contains*[A](s: HashSet[A], key: A): bool = ## Returns true iff `key` is in `s`. ## ## Example: @@ -147,11 +149,11 @@ proc contains*[A](s: TSet[A], key: A): bool = var index = rawGet(s, key) result = index >= 0 -proc rawInsert[A](s: var TSet[A], data: var TKeyValuePairSeq[A], key: A) = +proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A) = rawInsertImpl() -proc enlarge[A](s: var TSet[A]) = - var n: TKeyValuePairSeq[A] +proc enlarge[A](s: var HashSet[A]) = + var n: KeyValuePairSeq[A] newSeq(n, len(s.data) * growthFactor) for i in countup(0, high(s.data)): if s.data[i].slot == seFilled: rawInsert(s, n, s.data[i].key) @@ -173,7 +175,7 @@ template containsOrInclImpl() {.dirty.} = rawInsert(s, s.data, key) inc(s.counter) -proc incl*[A](s: var TSet[A], key: A) = +proc incl*[A](s: var HashSet[A], key: A) = ## Includes an element `key` in `s`. ## ## This doesn't do anything if `key` is already in `s`. Example: @@ -186,7 +188,7 @@ proc incl*[A](s: var TSet[A], key: A) = assert s.isValid, "The set needs to be initialized." inclImpl() -proc incl*[A](s: var TSet[A], other: TSet[A]) = +proc incl*[A](s: var HashSet[A], other: HashSet[A]) = ## Includes all elements from `other` into `s`. ## ## Example: @@ -201,7 +203,7 @@ proc incl*[A](s: var TSet[A], other: TSet[A]) = assert other.isValid, "The set `other` needs to be initialized." for item in other: incl(s, item) -proc excl*[A](s: var TSet[A], key: A) = +proc excl*[A](s: var HashSet[A], key: A) = ## Excludes `key` from the set `s`. ## ## This doesn't do anything if `key` is not found in `s`. Example: @@ -217,7 +219,7 @@ proc excl*[A](s: var TSet[A], key: A) = s.data[index].slot = seDeleted dec(s.counter) -proc excl*[A](s: var TSet[A], other: TSet[A]) = +proc excl*[A](s: var HashSet[A], other: HashSet[A]) = ## Excludes everything in `other` from `s`. ## ## Example: @@ -233,7 +235,7 @@ proc excl*[A](s: var TSet[A], other: TSet[A]) = assert other.isValid, "The set `other` needs to be initialized." for item in other: excl(s, item) -proc containsOrIncl*[A](s: var TSet[A], key: A): bool = +proc containsOrIncl*[A](s: var HashSet[A], key: A): bool = ## Includes `key` in the set `s` and tells if `key` was added to `s`. ## ## The difference with regards to the `incl() <#incl,TSet[A],A>`_ proc is @@ -248,7 +250,7 @@ proc containsOrIncl*[A](s: var TSet[A], key: A): bool = assert s.isValid, "The set needs to be initialized." containsOrInclImpl() -proc init*[A](s: var TSet[A], initialSize=64) = +proc init*[A](s: var HashSet[A], initialSize=64) = ## Initializes a hash set. ## ## The `initialSize` parameter needs to be a power of too. You can use @@ -271,7 +273,7 @@ proc init*[A](s: var TSet[A], initialSize=64) = s.counter = 0 newSeq(s.data, initialSize) -proc initSet*[A](initialSize=64): TSet[A] = +proc initSet*[A](initialSize=64): HashSet[A] = ## Wrapper around `init() <#init,TSet[A],int>`_ for initialization of hash ## sets. ## @@ -283,7 +285,7 @@ proc initSet*[A](initialSize=64): TSet[A] = ## a.incl(2) result.init(initialSize) -proc toSet*[A](keys: openArray[A]): TSet[A] = +proc toSet*[A](keys: openArray[A]): HashSet[A] = ## Creates a new hash set that contains the given `keys`. ## ## Example: @@ -302,7 +304,7 @@ template dollarImpl(): stmt {.dirty.} = result.add($key) result.add("}") -proc `$`*[A](s: TSet[A]): string = +proc `$`*[A](s: HashSet[A]): string = ## Converts the set `s` to a string, mostly for logging purposes. ## ## Don't use this proc for serialization, the representation may change at @@ -318,7 +320,7 @@ proc `$`*[A](s: TSet[A]): string = assert s.isValid, "The set needs to be initialized." dollarImpl() -proc union*[A](s1, s2: TSet[A]): TSet[A] = +proc union*[A](s1, s2: HashSet[A]): HashSet[A] = ## Returns the union of the sets `s1` and `s2`. ## ## The union of two sets is represented mathematically as *A ∪ B* and is the @@ -335,7 +337,7 @@ proc union*[A](s1, s2: TSet[A]): TSet[A] = result = s1 incl(result, s2) -proc intersection*[A](s1, s2: TSet[A]): TSet[A] = +proc intersection*[A](s1, s2: HashSet[A]): HashSet[A] = ## Returns the intersection of the sets `s1` and `s2`. ## ## The intersection of two sets is represented mathematically as *A ∩ B* and @@ -354,7 +356,7 @@ proc intersection*[A](s1, s2: TSet[A]): TSet[A] = for item in s1: if item in s2: incl(result, item) -proc difference*[A](s1, s2: TSet[A]): TSet[A] = +proc difference*[A](s1, s2: HashSet[A]): HashSet[A] = ## Returns the difference of the sets `s1` and `s2`. ## ## The difference of two sets is represented mathematically as *A \ B* and is @@ -374,7 +376,7 @@ proc difference*[A](s1, s2: TSet[A]): TSet[A] = if not contains(s2, item): incl(result, item) -proc symmetricDifference*[A](s1, s2: TSet[A]): TSet[A] = +proc symmetricDifference*[A](s1, s2: HashSet[A]): HashSet[A] = ## Returns the symmetric difference of the sets `s1` and `s2`. ## ## The symmetric difference of two sets is represented mathematically as *A △ @@ -393,23 +395,23 @@ proc symmetricDifference*[A](s1, s2: TSet[A]): TSet[A] = for item in s2: if containsOrIncl(result, item): excl(result, item) -proc `+`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = +proc `+`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} = ## Alias for `union(s1, s2) <#union>`_. result = union(s1, s2) -proc `*`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = +proc `*`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} = ## Alias for `intersection(s1, s2) <#intersection>`_. result = intersection(s1, s2) -proc `-`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = +proc `-`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} = ## Alias for `difference(s1, s2) <#difference>`_. result = difference(s1, s2) -proc `-+-`*[A](s1, s2: TSet[A]): TSet[A] {.inline.} = +proc `-+-`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} = ## Alias for `symmetricDifference(s1, s2) <#symmetricDifference>`_. result = symmetricDifference(s1, s2) -proc disjoint*[A](s1, s2: TSet[A]): bool = +proc disjoint*[A](s1, s2: HashSet[A]): bool = ## Returns true iff the sets `s1` and `s2` have no items in common. ## ## Example: @@ -426,7 +428,7 @@ proc disjoint*[A](s1, s2: TSet[A]): bool = if item in s2: return false return true -proc `<`*[A](s, t: TSet[A]): bool = +proc `<`*[A](s, t: HashSet[A]): bool = ## Returns true if `s` is a strict or proper subset of `t`. ## ## A strict or proper subset `s` has all of its members in `t` but `t` has @@ -441,7 +443,7 @@ proc `<`*[A](s, t: TSet[A]): bool = ## assert((a < a) == false) s.counter != t.counter and s <= t -proc `<=`*[A](s, t: TSet[A]): bool = +proc `<=`*[A](s, t: HashSet[A]): bool = ## Returns true if `s` is subset of `t`. ## ## A subset `s` has all of its members in `t` and `t` doesn't necessarily @@ -462,7 +464,7 @@ proc `<=`*[A](s, t: TSet[A]): bool = result = false return -proc `==`*[A](s, t: TSet[A]): bool = +proc `==`*[A](s, t: HashSet[A]): bool = ## Returns true if both `s` and `t` have the same members and set size. ## ## Example: @@ -475,7 +477,7 @@ proc `==`*[A](s, t: TSet[A]): bool = ## assert a == b s.counter == t.counter and s <= t -proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[B] = +proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] = ## Returns a new set after applying `op` on each of the elements of `data`. ## ## You can use this proc to transform the elements from a set. Example: @@ -490,19 +492,20 @@ proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[B] = # ------------------------------ ordered set ------------------------------ type - TOrderedKeyValuePair[A] = tuple[ - slot: TSlotEnum, next: int, key: A] - TOrderedKeyValuePairSeq[A] = seq[TOrderedKeyValuePair[A]] - TOrderedSet* {. - final, myShallow.}[A] = object ## \ + OrderedKeyValuePair[A] = tuple[ + slot: SlotEnum, next: int, key: A] + OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]] + OrderedSet* {.myShallow.}[A] = object ## \ ## A generic hash set that remembers insertion order. ## - ## Use `init() <#init,TOrderedSet[A],int>`_ or `initOrderedSet[type]() + ## Use `init() <#init,OrderedSet[A],int>`_ or `initOrderedSet[type]() ## <#initOrderedSet>`_ before calling other procs on it. - data: TOrderedKeyValuePairSeq[A] + data: OrderedKeyValuePairSeq[A] counter, first, last: int -proc isValid*[A](s: TOrderedSet[A]): bool = +{.deprecated: [TOrderedSet: OrderedSet].} + +proc isValid*[A](s: OrderedSet[A]): bool = ## Returns `true` if the ordered set has been initialized with `initSet ## <#initOrderedSet>`_. ## @@ -511,13 +514,13 @@ proc isValid*[A](s: TOrderedSet[A]): bool = ## in your own procs to verify that ordered sets passed to your procs are ## correctly initialized. Example: ## - ## .. code-block :: nimrod + ## .. code-block:: ## proc saveTarotCards(cards: TOrderedSet[int]) = ## assert cards.isValid, "Pass an initialized set!" ## # Do stuff here, may crash in release builds! result = not s.data.isNil -proc len*[A](s: TOrderedSet[A]): int {.inline.} = +proc len*[A](s: OrderedSet[A]): int {.inline.} = ## Returns the number of keys in `s`. ## ## Due to an implementation detail you can call this proc on variables which @@ -531,7 +534,7 @@ proc len*[A](s: TOrderedSet[A]): int {.inline.} = ## assert values.len == 0 result = s.counter -proc card*[A](s: TOrderedSet[A]): int {.inline.} = +proc card*[A](s: OrderedSet[A]): int {.inline.} = ## Alias for `len() <#len,TOrderedSet[A]>`_. ## ## Card stands for the `cardinality @@ -545,7 +548,7 @@ template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} = if s.data[h].slot == seFilled: yieldStmt h = nxt -iterator items*[A](s: TOrderedSet[A]): A = +iterator items*[A](s: OrderedSet[A]): A = ## Iterates over keys in the ordered set `s` in insertion order. ## ## If you need a sequence with the keys you can use `sequtils.toSeq() @@ -567,10 +570,10 @@ iterator items*[A](s: TOrderedSet[A]): A = forAllOrderedPairs: yield s.data[h].key -proc rawGet[A](s: TOrderedSet[A], key: A): int = +proc rawGet[A](s: OrderedSet[A], key: A): int = rawGetImpl() -proc contains*[A](s: TOrderedSet[A], key: A): bool = +proc contains*[A](s: OrderedSet[A], key: A): bool = ## Returns true iff `key` is in `s`. ## ## Example: @@ -584,16 +587,16 @@ proc contains*[A](s: TOrderedSet[A], key: A): bool = var index = rawGet(s, key) result = index >= 0 -proc rawInsert[A](s: var TOrderedSet[A], - data: var TOrderedKeyValuePairSeq[A], key: A) = +proc rawInsert[A](s: var OrderedSet[A], + data: var OrderedKeyValuePairSeq[A], key: A) = rawInsertImpl() data[h].next = -1 if s.first < 0: s.first = h if s.last >= 0: data[s.last].next = h s.last = h -proc enlarge[A](s: var TOrderedSet[A]) = - var n: TOrderedKeyValuePairSeq[A] +proc enlarge[A](s: var OrderedSet[A]) = + var n: OrderedKeyValuePairSeq[A] newSeq(n, len(s.data) * growthFactor) var h = s.first s.first = -1 @@ -605,7 +608,7 @@ proc enlarge[A](s: var TOrderedSet[A]) = h = nxt swap(s.data, n) -proc incl*[A](s: var TOrderedSet[A], key: A) = +proc incl*[A](s: var OrderedSet[A], key: A) = ## Includes an element `key` in `s`. ## ## This doesn't do anything if `key` is already in `s`. Example: @@ -618,7 +621,7 @@ proc incl*[A](s: var TOrderedSet[A], key: A) = assert s.isValid, "The set needs to be initialized." inclImpl() -proc incl*[A](s: var TSet[A], other: TOrderedSet[A]) = +proc incl*[A](s: var HashSet[A], other: OrderedSet[A]) = ## Includes all elements from `other` into `s`. ## ## Example: @@ -633,7 +636,7 @@ proc incl*[A](s: var TSet[A], other: TOrderedSet[A]) = assert other.isValid, "The set `other` needs to be initialized." for item in other: incl(s, item) -proc containsOrIncl*[A](s: var TOrderedSet[A], key: A): bool = +proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool = ## Includes `key` in the set `s` and tells if `key` was added to `s`. ## ## The difference with regards to the `incl() <#incl,TOrderedSet[A],A>`_ proc @@ -648,7 +651,7 @@ proc containsOrIncl*[A](s: var TOrderedSet[A], key: A): bool = assert s.isValid, "The set needs to be initialized." containsOrInclImpl() -proc init*[A](s: var TOrderedSet[A], initialSize=64) = +proc init*[A](s: var OrderedSet[A], initialSize=64) = ## Initializes an ordered hash set. ## ## The `initialSize` parameter needs to be a power of too. You can use @@ -673,7 +676,7 @@ proc init*[A](s: var TOrderedSet[A], initialSize=64) = s.last = -1 newSeq(s.data, initialSize) -proc initOrderedSet*[A](initialSize=64): TOrderedSet[A] = +proc initOrderedSet*[A](initialSize=64): OrderedSet[A] = ## Wrapper around `init() <#init,TOrderedSet[A],int>`_ for initialization of ## ordered hash sets. ## @@ -685,7 +688,7 @@ proc initOrderedSet*[A](initialSize=64): TOrderedSet[A] = ## a.incl(2) result.init(initialSize) -proc toOrderedSet*[A](keys: openArray[A]): TOrderedSet[A] = +proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] = ## Creates a new ordered hash set that contains the given `keys`. ## ## Example: @@ -697,7 +700,7 @@ proc toOrderedSet*[A](keys: openArray[A]): TOrderedSet[A] = result = initOrderedSet[A](nextPowerOfTwo(keys.len+10)) for key in items(keys): result.incl(key) -proc `$`*[A](s: TOrderedSet[A]): string = +proc `$`*[A](s: OrderedSet[A]): string = ## Converts the ordered hash set `s` to a string, mostly for logging purposes. ## ## Don't use this proc for serialization, the representation may change at @@ -713,7 +716,7 @@ proc `$`*[A](s: TOrderedSet[A]): string = assert s.isValid, "The set needs to be initialized." dollarImpl() -proc `==`*[A](s, t: TOrderedSet[A]): bool = +proc `==`*[A](s, t: OrderedSet[A]): bool = ## Equality for ordered sets. if s.counter != t.counter: return false var h = s.first @@ -734,14 +737,14 @@ proc `==`*[A](s, t: TOrderedSet[A]): bool = proc testModule() = ## Internal micro test to validate docstrings and such. block isValidTest: - var options: TSet[string] - proc savePreferences(options: TSet[string]) = + var options: HashSet[string] + proc savePreferences(options: HashSet[string]) = assert options.isValid, "Pass an initialized set!" options = initSet[string]() options.savePreferences block lenTest: - var values: TSet[int] + var values: HashSet[int] assert(not values.isValid) assert values.len == 0 assert values.card == 0 @@ -835,14 +838,14 @@ proc testModule() = assert b == toSet(["1", "2", "3"]) block isValidTest: - var cards: TOrderedSet[string] - proc saveTarotCards(cards: TOrderedSet[string]) = + var cards: OrderedSet[string] + proc saveTarotCards(cards: OrderedSet[string]) = assert cards.isValid, "Pass an initialized set!" cards = initOrderedSet[string]() cards.saveTarotCards block lenTest: - var values: TOrderedSet[int] + var values: OrderedSet[int] assert(not values.isValid) assert values.len == 0 assert values.card == 0 @@ -879,7 +882,7 @@ proc testModule() = assert(a == b) # https://github.com/Araq/Nimrod/issues/1413 block initBlocks: - var a: TOrderedSet[int] + var a: OrderedSet[int] a.init(4) a.incl(2) a.init @@ -888,7 +891,7 @@ proc testModule() = a.incl(2) assert a.len == 1 - var b: TSet[int] + var b: HashSet[int] b.init(4) b.incl(2) b.init diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index dcf2ab481..28b7fde92 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -1,14 +1,20 @@ # # -# Nimrod's Runtime Library -# (c) Copyright 2013 Andreas Rumpf +# Nim's Runtime Library +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## The ``tables`` module implements an efficient hash table that is -## a mapping from keys to values. +## The ``tables`` module implements variants of an efficient hash table that is +## a mapping from keys to values. ``Table`` is the usual hash table, +## ``OrderedTable`` is like ``Table`` but remembers insertion order +## and ``CountTable`` is a mapping from a key to its number of occurances. +## For consistency with every other data type in Nim these have **value** +## semantics, this means that ``=`` performs a copy of the hash table. +## For **reference** semantics use the ``Ref`` variant: ``TableRef``, +## ``OrderedTableRef``, ``CountTableRef``. ## ## If you are using simple standard types like ``int`` or ``string`` for the ## keys of the table you won't have any problems, but as soon as you try to use @@ -24,11 +30,17 @@ ## ## 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 `_ -## value, and the compiler is listing all the hash functions it knows. After -## you add such a proc for your custom type everything will work. See this -## example: +## 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. ## -## .. code-block:: nimrod +## After you add ``hash`` and ``==`` for your custom type everything will work. +## Currently however ``hash`` for objects is not defined, whereas +## ``system.==`` for objects does exist and performs a "deep" comparison (every +## field is compared) which is usually what you want. So in the following +## example implementing only ``hash`` suffices: +## +## .. code-block:: ## type ## Person = object ## firstName, lastName: string @@ -51,9 +63,6 @@ ## p2.firstName = "소진" ## p2.lastName = "박" ## salaries[p2] = 45_000 -## -## **Note:** The data types declared here have *value semantics*: This means -## that ``=`` performs a copy of the hash table. import hashes, math @@ -61,43 +70,45 @@ import {.pragma: myShallow.} type - TSlotEnum = enum seEmpty, seFilled, seDeleted - TKeyValuePair[A, B] = tuple[slot: TSlotEnum, key: A, val: B] - TKeyValuePairSeq[A, B] = seq[TKeyValuePair[A, B]] - TTable* {.final, myShallow.}[A, B] = object ## generic hash table - data: TKeyValuePairSeq[A, B] + SlotEnum = enum seEmpty, seFilled, seDeleted + KeyValuePair[A, B] = tuple[slot: SlotEnum, key: A, val: B] + KeyValuePairSeq[A, B] = seq[KeyValuePair[A, B]] + Table* {.myShallow.}[A, B] = object ## generic hash table + data: KeyValuePairSeq[A, B] counter: int - PTable*[A,B] = ref TTable[A, B] + TableRef*[A,B] = ref Table[A, B] + +{.deprecated: [TTable: Table, PTable: TableRef].} when not defined(nimhygiene): {.pragma: dirty.} -proc len*[A, B](t: TTable[A, B]): int = +proc len*[A, B](t: Table[A, B]): int = ## returns the number of keys in `t`. result = t.counter -iterator pairs*[A, B](t: TTable[A, B]): tuple[key: A, val: B] = +iterator pairs*[A, B](t: Table[A, B]): tuple[key: A, val: B] = ## iterates over any (key, value) pair in the table `t`. for h in 0..high(t.data): if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val) -iterator mpairs*[A, B](t: var TTable[A, B]): tuple[key: A, val: var B] = +iterator mpairs*[A, B](t: var Table[A, B]): tuple[key: A, val: var B] = ## iterates over any (key, value) pair in the table `t`. The values ## can be modified. for h in 0..high(t.data): if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val) -iterator keys*[A, B](t: TTable[A, B]): A = +iterator keys*[A, B](t: Table[A, B]): A = ## iterates over any key in the table `t`. for h in 0..high(t.data): if t.data[h].slot == seFilled: yield t.data[h].key -iterator values*[A, B](t: TTable[A, B]): B = +iterator values*[A, B](t: Table[A, B]): B = ## iterates over any value in the table `t`. for h in 0..high(t.data): if t.data[h].slot == seFilled: yield t.data[h].val -iterator mvalues*[A, B](t: var TTable[A, B]): var B = +iterator mvalues*[A, B](t: var Table[A, B]): var B = ## iterates over any value in the table `t`. The values can be modified. for h in 0..high(t.data): if t.data[h].slot == seFilled: yield t.data[h].val @@ -128,10 +139,10 @@ template rawInsertImpl() {.dirty.} = data[h].val = val data[h].slot = seFilled -proc rawGet[A, B](t: TTable[A, B], key: A): int = +proc rawGet[A, B](t: Table[A, B], key: A): int = rawGetImpl() -proc `[]`*[A, B](t: TTable[A, B], key: A): B = +proc `[]`*[A, B](t: Table[A, B], key: A): B = ## retrieves the value at ``t[key]``. If `key` is not in `t`, ## default empty value for the type `B` is returned ## and no exception is raised. One can check with ``hasKey`` whether the key @@ -139,14 +150,14 @@ proc `[]`*[A, B](t: TTable[A, B], key: A): B = var index = rawGet(t, key) if index >= 0: result = t.data[index].val -proc mget*[A, B](t: var TTable[A, B], key: A): var B = +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 ``EInvalidKey`` exception is raised. var index = rawGet(t, key) if index >= 0: result = t.data[index].val - else: raise newException(EInvalidKey, "key not found: " & $key) + else: raise newException(KeyError, "key not found: " & $key) -iterator allValues*[A, B](t: TTable[A, B]; key: A): 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) while t.data[h].slot != seEmpty: @@ -154,16 +165,16 @@ iterator allValues*[A, B](t: TTable[A, B]; key: A): B = yield t.data[h].val h = nextTry(h, high(t.data)) -proc hasKey*[A, B](t: TTable[A, B], key: A): bool = +proc hasKey*[A, B](t: Table[A, B], key: A): bool = ## returns true iff `key` is in the table `t`. result = rawGet(t, key) >= 0 -proc rawInsert[A, B](t: var TTable[A, B], data: var TKeyValuePairSeq[A, B], +proc rawInsert[A, B](t: var Table[A, B], data: var KeyValuePairSeq[A, B], key: A, val: B) = rawInsertImpl() -proc enlarge[A, B](t: var TTable[A, B]) = - var n: TKeyValuePairSeq[A, B] +proc enlarge[A, B](t: var Table[A, B]) = + var n: KeyValuePairSeq[A, B] newSeq(n, len(t.data) * growthFactor) for i in countup(0, high(t.data)): if t.data[i].slot == seFilled: rawInsert(t, n, t.data[i].key, t.data[i].val) @@ -194,22 +205,22 @@ when false: inc(t.counter) result = false -proc `[]=`*[A, B](t: var TTable[A, B], key: A, val: B) = +proc `[]=`*[A, B](t: var Table[A, B], key: A, val: B) = ## puts a (key, value)-pair into `t`. putImpl() -proc add*[A, B](t: var TTable[A, B], key: A, val: B) = +proc add*[A, B](t: var Table[A, B], key: A, val: B) = ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. addImpl() -proc del*[A, B](t: var TTable[A, B], key: A) = +proc del*[A, B](t: var Table[A, B], key: A) = ## deletes `key` from hash table `t`. let index = rawGet(t, key) if index >= 0: t.data[index].slot = seDeleted dec(t.counter) -proc initTable*[A, B](initialSize=64): TTable[A, B] = +proc initTable*[A, B](initialSize=64): Table[A, B] = ## creates a new hash table that is empty. ## ## `initialSize` needs to be a power of two. If you need to accept runtime @@ -220,7 +231,7 @@ proc initTable*[A, B](initialSize=64): TTable[A, B] = newSeq(result.data, initialSize) proc toTable*[A, B](pairs: openArray[tuple[key: A, - val: B]]): TTable[A, B] = + val: B]]): Table[A, B] = ## creates a new hash table that contains the given `pairs`. result = initTable[A, B](nextPowerOfTwo(pairs.len+10)) for key, val in items(pairs): result[key] = val @@ -237,7 +248,7 @@ template dollarImpl(): stmt {.dirty.} = result.add($val) result.add("}") -proc `$`*[A, B](t: TTable[A, B]): string = +proc `$`*[A, B](t: Table[A, B]): string = ## The `$` operator for hash tables. dollarImpl() @@ -251,93 +262,93 @@ template equalsImpl() = if t[key] != val: return false return true -proc `==`*[A, B](s, t: TTable[A, B]): bool = +proc `==`*[A, B](s, t: Table[A, B]): bool = equalsImpl() -proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): TTable[C, B] = +proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): Table[C, B] = ## Index the collection with the proc provided. # TODO: As soon as supported, change collection: A to collection: A[B] result = initTable[C, B]() for item in collection: result[index(item)] = item -proc len*[A, B](t: PTable[A, B]): int = +proc len*[A, B](t: TableRef[A, B]): int = ## returns the number of keys in `t`. result = t.counter -iterator pairs*[A, B](t: PTable[A, B]): tuple[key: A, val: B] = +iterator pairs*[A, B](t: TableRef[A, B]): tuple[key: A, val: B] = ## iterates over any (key, value) pair in the table `t`. for h in 0..high(t.data): if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val) -iterator mpairs*[A, B](t: PTable[A, B]): tuple[key: A, val: var B] = +iterator mpairs*[A, B](t: TableRef[A, B]): tuple[key: A, val: var B] = ## iterates over any (key, value) pair in the table `t`. The values ## can be modified. for h in 0..high(t.data): if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val) -iterator keys*[A, B](t: PTable[A, B]): A = +iterator keys*[A, B](t: TableRef[A, B]): A = ## iterates over any key in the table `t`. for h in 0..high(t.data): if t.data[h].slot == seFilled: yield t.data[h].key -iterator values*[A, B](t: PTable[A, B]): B = +iterator values*[A, B](t: TableRef[A, B]): B = ## iterates over any value in the table `t`. for h in 0..high(t.data): if t.data[h].slot == seFilled: yield t.data[h].val -iterator mvalues*[A, B](t: PTable[A, B]): var B = +iterator mvalues*[A, B](t: TableRef[A, B]): var B = ## iterates over any value in the table `t`. The values can be modified. for h in 0..high(t.data): if t.data[h].slot == seFilled: yield t.data[h].val -proc `[]`*[A, B](t: PTable[A, B], key: A): B = +proc `[]`*[A, B](t: TableRef[A, B], key: A): B = ## retrieves the value at ``t[key]``. If `key` is not in `t`, ## default empty value for the type `B` is returned ## and no exception is raised. One can check with ``hasKey`` whether the key ## exists. result = t[][key] -proc mget*[A, B](t: PTable[A, B], key: A): var B = +proc mget*[A, B](t: TableRef[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. t[].mget(key) -proc hasKey*[A, B](t: PTable[A, B], key: A): bool = +proc hasKey*[A, B](t: TableRef[A, B], key: A): bool = ## returns true iff `key` is in the table `t`. result = t[].hasKey(key) -proc `[]=`*[A, B](t: PTable[A, B], key: A, val: B) = +proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) = ## puts a (key, value)-pair into `t`. t[][key] = val -proc add*[A, B](t: PTable[A, B], key: A, val: B) = +proc add*[A, B](t: TableRef[A, B], key: A, val: B) = ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. t[].add(key, val) -proc del*[A, B](t: PTable[A, B], key: A) = +proc del*[A, B](t: TableRef[A, B], key: A) = ## deletes `key` from hash table `t`. t[].del(key) -proc newTable*[A, B](initialSize=64): PTable[A, B] = +proc newTable*[A, B](initialSize=64): TableRef[A, B] = new(result) result[] = initTable[A, B](initialSize) -proc newTable*[A, B](pairs: openArray[tuple[key: A, val: B]]): PTable[A, B] = +proc newTable*[A, B](pairs: openArray[tuple[key: A, val: B]]): TableRef[A, B] = ## creates a new hash table that contains the given `pairs`. new(result) result[] = toTable[A, B](pairs) -proc `$`*[A, B](t: PTable[A, B]): string = +proc `$`*[A, B](t: TableRef[A, B]): string = ## The `$` operator for hash tables. dollarImpl() -proc `==`*[A, B](s, t: PTable[A, B]): bool = +proc `==`*[A, B](s, t: TableRef[A, B]): bool = if isNil(s): result = isNil(t) elif isNil(t): result = false else: result = equalsImpl() -proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): PTable[C, B] = +proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] = ## Index the collection with the proc provided. # TODO: As soon as supported, change collection: A to collection: A[B] result = newTable[C, B]() @@ -347,16 +358,18 @@ proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): PTable[C, B] = # ------------------------------ ordered table ------------------------------ type - TOrderedKeyValuePair[A, B] = tuple[ - slot: TSlotEnum, next: int, key: A, val: B] - TOrderedKeyValuePairSeq[A, B] = seq[TOrderedKeyValuePair[A, B]] - TOrderedTable* {. - final, myShallow.}[A, B] = object ## table that remembers insertion order - data: TOrderedKeyValuePairSeq[A, B] + OrderedKeyValuePair[A, B] = tuple[ + slot: SlotEnum, next: int, key: A, val: B] + OrderedKeyValuePairSeq[A, B] = seq[OrderedKeyValuePair[A, B]] + OrderedTable* {. + myShallow.}[A, B] = object ## table that remembers insertion order + data: OrderedKeyValuePairSeq[A, B] counter, first, last: int - POrderedTable*[A, B] = ref TOrderedTable[A, B] + OrderedTableRef*[A, B] = ref OrderedTable[A, B] -proc len*[A, B](t: TOrderedTable[A, B]): int {.inline.} = +{.deprecated: [TOrderedTable: OrderedTable, POrderedTable: OrderedTableRef].} + +proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} = ## returns the number of keys in `t`. result = t.counter @@ -367,38 +380,38 @@ template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} = if t.data[h].slot == seFilled: yieldStmt h = nxt -iterator pairs*[A, B](t: TOrderedTable[A, B]): tuple[key: A, val: B] = +iterator pairs*[A, B](t: OrderedTable[A, B]): tuple[key: A, val: B] = ## iterates over any (key, value) pair in the table `t` in insertion ## order. forAllOrderedPairs: yield (t.data[h].key, t.data[h].val) -iterator mpairs*[A, B](t: var TOrderedTable[A, B]): tuple[key: A, val: var B] = +iterator mpairs*[A, B](t: var OrderedTable[A, B]): tuple[key: A, val: var B] = ## iterates over any (key, value) pair in the table `t` in insertion ## order. The values can be modified. forAllOrderedPairs: yield (t.data[h].key, t.data[h].val) -iterator keys*[A, B](t: TOrderedTable[A, B]): A = +iterator keys*[A, B](t: OrderedTable[A, B]): A = ## iterates over any key in the table `t` in insertion order. forAllOrderedPairs: yield t.data[h].key -iterator values*[A, B](t: TOrderedTable[A, B]): B = +iterator values*[A, B](t: OrderedTable[A, B]): B = ## iterates over any value in the table `t` in insertion order. forAllOrderedPairs: yield t.data[h].val -iterator mvalues*[A, B](t: var TOrderedTable[A, B]): var B = +iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B = ## iterates over any value in the table `t` in insertion order. The values ## can be modified. forAllOrderedPairs: yield t.data[h].val -proc rawGet[A, B](t: TOrderedTable[A, B], key: A): int = +proc rawGet[A, B](t: OrderedTable[A, B], key: A): int = rawGetImpl() -proc `[]`*[A, B](t: TOrderedTable[A, B], key: A): B = +proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B = ## retrieves the value at ``t[key]``. If `key` is not in `t`, ## default empty value for the type `B` is returned ## and no exception is raised. One can check with ``hasKey`` whether the key @@ -406,19 +419,19 @@ proc `[]`*[A, B](t: TOrderedTable[A, B], key: A): B = var index = rawGet(t, key) if index >= 0: result = t.data[index].val -proc mget*[A, B](t: var TOrderedTable[A, B], key: A): var B = +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 index = rawGet(t, key) if index >= 0: result = t.data[index].val - else: raise newException(EInvalidKey, "key not found: " & $key) + else: raise newException(KeyError, "key not found: " & $key) -proc hasKey*[A, B](t: TOrderedTable[A, B], key: A): bool = +proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool = ## returns true iff `key` is in the table `t`. result = rawGet(t, key) >= 0 -proc rawInsert[A, B](t: var TOrderedTable[A, B], - data: var TOrderedKeyValuePairSeq[A, B], +proc rawInsert[A, B](t: var OrderedTable[A, B], + data: var OrderedKeyValuePairSeq[A, B], key: A, val: B) = rawInsertImpl() data[h].next = -1 @@ -426,8 +439,8 @@ proc rawInsert[A, B](t: var TOrderedTable[A, B], if t.last >= 0: data[t.last].next = h t.last = h -proc enlarge[A, B](t: var TOrderedTable[A, B]) = - var n: TOrderedKeyValuePairSeq[A, B] +proc enlarge[A, B](t: var OrderedTable[A, B]) = + var n: OrderedKeyValuePairSeq[A, B] newSeq(n, len(t.data) * growthFactor) var h = t.first t.first = -1 @@ -439,15 +452,15 @@ proc enlarge[A, B](t: var TOrderedTable[A, B]) = h = nxt swap(t.data, n) -proc `[]=`*[A, B](t: var TOrderedTable[A, B], key: A, val: B) = +proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: B) = ## puts a (key, value)-pair into `t`. putImpl() -proc add*[A, B](t: var TOrderedTable[A, B], key: A, val: B) = +proc add*[A, B](t: var OrderedTable[A, B], key: A, val: B) = ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. addImpl() -proc initOrderedTable*[A, B](initialSize=64): TOrderedTable[A, B] = +proc initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] = ## creates a new ordered hash table that is empty. ## ## `initialSize` needs to be a power of two. If you need to accept runtime @@ -460,16 +473,16 @@ proc initOrderedTable*[A, B](initialSize=64): TOrderedTable[A, B] = newSeq(result.data, initialSize) proc toOrderedTable*[A, B](pairs: openArray[tuple[key: A, - val: B]]): TOrderedTable[A, B] = + val: B]]): OrderedTable[A, B] = ## creates a new ordered hash table that contains the given `pairs`. result = initOrderedTable[A, B](nextPowerOfTwo(pairs.len+10)) for key, val in items(pairs): result[key] = val -proc `$`*[A, B](t: TOrderedTable[A, B]): string = +proc `$`*[A, B](t: OrderedTable[A, B]): string = ## The `$` operator for ordered hash tables. dollarImpl() -proc sort*[A, B](t: var TOrderedTable[A, B], +proc sort*[A, B](t: var OrderedTable[A, B], cmp: proc (x,y: tuple[key: A, val: B]): int) = ## sorts `t` according to `cmp`. This modifies the internal list ## that kept the insertion order, so insertion order is lost after this @@ -515,7 +528,7 @@ proc sort*[A, B](t: var TOrderedTable[A, B], t.first = list t.last = tail -proc len*[A, B](t: POrderedTable[A, B]): int {.inline.} = +proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} = ## returns the number of keys in `t`. result = t.counter @@ -526,59 +539,59 @@ template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} = if t.data[h].slot == seFilled: yieldStmt h = nxt -iterator pairs*[A, B](t: POrderedTable[A, B]): tuple[key: A, val: B] = +iterator pairs*[A, B](t: OrderedTableRef[A, B]): tuple[key: A, val: B] = ## iterates over any (key, value) pair in the table `t` in insertion ## order. forAllOrderedPairs: yield (t.data[h].key, t.data[h].val) -iterator mpairs*[A, B](t: POrderedTable[A, B]): tuple[key: A, val: var B] = +iterator mpairs*[A, B](t: OrderedTableRef[A, B]): tuple[key: A, val: var B] = ## iterates over any (key, value) pair in the table `t` in insertion ## order. The values can be modified. forAllOrderedPairs: yield (t.data[h].key, t.data[h].val) -iterator keys*[A, B](t: POrderedTable[A, B]): A = +iterator keys*[A, B](t: OrderedTableRef[A, B]): A = ## iterates over any key in the table `t` in insertion order. forAllOrderedPairs: yield t.data[h].key -iterator values*[A, B](t: POrderedTable[A, B]): B = +iterator values*[A, B](t: OrderedTableRef[A, B]): B = ## iterates over any value in the table `t` in insertion order. forAllOrderedPairs: yield t.data[h].val -iterator mvalues*[A, B](t: POrderedTable[A, B]): var B = +iterator mvalues*[A, B](t: OrderedTableRef[A, B]): var B = ## iterates over any value in the table `t` in insertion order. The values ## can be modified. forAllOrderedPairs: yield t.data[h].val -proc `[]`*[A, B](t: POrderedTable[A, B], key: A): B = +proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): B = ## retrieves the value at ``t[key]``. If `key` is not in `t`, ## default empty value for the type `B` is returned ## and no exception is raised. One can check with ``hasKey`` whether the key ## exists. result = t[][key] -proc mget*[A, B](t: POrderedTable[A, B], key: A): var B = +proc mget*[A, B](t: OrderedTableRef[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. result = t[].mget(key) -proc hasKey*[A, B](t: POrderedTable[A, B], key: A): bool = +proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool = ## returns true iff `key` is in the table `t`. result = t[].hasKey(key) -proc `[]=`*[A, B](t: POrderedTable[A, B], key: A, val: B) = +proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: B) = ## puts a (key, value)-pair into `t`. t[][key] = val -proc add*[A, B](t: POrderedTable[A, B], key: A, val: B) = +proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: B) = ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. t[].add(key, val) -proc newOrderedTable*[A, B](initialSize=64): POrderedTable[A, B] = +proc newOrderedTable*[A, B](initialSize=64): OrderedTableRef[A, B] = ## creates a new ordered hash table that is empty. ## ## `initialSize` needs to be a power of two. If you need to accept runtime @@ -588,16 +601,16 @@ proc newOrderedTable*[A, B](initialSize=64): POrderedTable[A, B] = result[] = initOrderedTable[A, B]() proc newOrderedTable*[A, B](pairs: openArray[tuple[key: A, - val: B]]): POrderedTable[A, B] = + val: B]]): OrderedTableRef[A, B] = ## creates a new ordered hash table that contains the given `pairs`. result = newOrderedTable[A, B](nextPowerOfTwo(pairs.len+10)) for key, val in items(pairs): result[key] = val -proc `$`*[A, B](t: POrderedTable[A, B]): string = +proc `$`*[A, B](t: OrderedTableRef[A, B]): string = ## The `$` operator for ordered hash tables. dollarImpl() -proc sort*[A, B](t: POrderedTable[A, B], +proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x,y: tuple[key: A, val: B]): int) = ## sorts `t` according to `cmp`. This modifies the internal list ## that kept the insertion order, so insertion order is lost after this @@ -608,87 +621,89 @@ proc sort*[A, B](t: POrderedTable[A, B], # ------------------------------ count tables ------------------------------- type - TCountTable* {.final, myShallow.}[ + CountTable* {.myShallow.}[ A] = object ## table that counts the number of each key data: seq[tuple[key: A, val: int]] counter: int - PCountTable*[A] = ref TCountTable[A] + CountTableRef*[A] = ref CountTable[A] -proc len*[A](t: TCountTable[A]): int = +{.deprecated: [TCountTable: CountTable, PCountTable: CountTableRef].} + +proc len*[A](t: CountTable[A]): int = ## returns the number of keys in `t`. result = t.counter -iterator pairs*[A](t: TCountTable[A]): tuple[key: A, val: int] = +iterator pairs*[A](t: CountTable[A]): tuple[key: A, val: int] = ## iterates over any (key, value) pair in the table `t`. for h in 0..high(t.data): if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val) -iterator mpairs*[A](t: var TCountTable[A]): tuple[key: A, val: var int] = +iterator mpairs*[A](t: var CountTable[A]): tuple[key: A, val: var int] = ## iterates over any (key, value) pair in the table `t`. The values can ## be modified. for h in 0..high(t.data): if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val) -iterator keys*[A](t: TCountTable[A]): A = +iterator keys*[A](t: CountTable[A]): A = ## iterates over any key in the table `t`. for h in 0..high(t.data): if t.data[h].val != 0: yield t.data[h].key -iterator values*[A](t: TCountTable[A]): int = +iterator values*[A](t: CountTable[A]): int = ## iterates over any value in the table `t`. for h in 0..high(t.data): if t.data[h].val != 0: yield t.data[h].val -iterator mvalues*[A](t: TCountTable[A]): var int = +iterator mvalues*[A](t: CountTable[A]): var int = ## iterates over any value in the table `t`. The values can be modified. for h in 0..high(t.data): if t.data[h].val != 0: yield t.data[h].val -proc rawGet[A](t: TCountTable[A], key: A): int = +proc rawGet[A](t: CountTable[A], key: A): int = var h: THash = 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)) result = -1 -proc `[]`*[A](t: TCountTable[A], key: A): int = +proc `[]`*[A](t: CountTable[A], key: A): int = ## retrieves the value at ``t[key]``. If `key` is not in `t`, ## 0 is returned. One can check with ``hasKey`` whether the key ## exists. var index = rawGet(t, key) if index >= 0: result = t.data[index].val -proc mget*[A](t: var TCountTable[A], key: A): var int = +proc mget*[A](t: var CountTable[A], key: A): var int = ## retrieves the value at ``t[key]``. The value can be modified. ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. var index = rawGet(t, key) if index >= 0: result = t.data[index].val - else: raise newException(EInvalidKey, "key not found: " & $key) + else: raise newException(KeyError, "key not found: " & $key) -proc hasKey*[A](t: TCountTable[A], key: A): bool = +proc hasKey*[A](t: CountTable[A], key: A): bool = ## returns true iff `key` is in the table `t`. result = rawGet(t, key) >= 0 -proc rawInsert[A](t: TCountTable[A], data: var seq[tuple[key: A, val: int]], +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) while data[h].val != 0: h = nextTry(h, high(data)) data[h].key = key data[h].val = val -proc enlarge[A](t: var TCountTable[A]) = +proc enlarge[A](t: var CountTable[A]) = var n: seq[tuple[key: A, val: int]] newSeq(n, len(t.data) * growthFactor) for i in countup(0, high(t.data)): if t.data[i].val != 0: rawInsert(t, n, t.data[i].key, t.data[i].val) swap(t.data, n) -proc `[]=`*[A](t: var TCountTable[A], key: A, val: int) = +proc `[]=`*[A](t: var CountTable[A], key: A, val: int) = ## puts a (key, value)-pair into `t`. `val` has to be positive. assert val > 0 putImpl() -proc initCountTable*[A](initialSize=64): TCountTable[A] = +proc initCountTable*[A](initialSize=64): CountTable[A] = ## creates a new count table that is empty. ## ## `initialSize` needs to be a power of two. If you need to accept runtime @@ -698,16 +713,16 @@ proc initCountTable*[A](initialSize=64): TCountTable[A] = result.counter = 0 newSeq(result.data, initialSize) -proc toCountTable*[A](keys: openArray[A]): TCountTable[A] = +proc toCountTable*[A](keys: openArray[A]): CountTable[A] = ## creates a new count table with every key in `keys` having a count of 1. result = initCountTable[A](nextPowerOfTwo(keys.len+10)) for key in items(keys): result[key] = 1 -proc `$`*[A](t: TCountTable[A]): string = +proc `$`*[A](t: CountTable[A]): string = ## The `$` operator for count tables. dollarImpl() -proc inc*[A](t: var TCountTable[A], key: A, val = 1) = +proc inc*[A](t: var CountTable[A], key: A, val = 1) = ## increments `t[key]` by `val`. var index = rawGet(t, key) if index >= 0: @@ -717,7 +732,7 @@ proc inc*[A](t: var TCountTable[A], key: A, val = 1) = rawInsert(t, t.data, key, val) inc(t.counter) -proc smallest*[A](t: TCountTable[A]): tuple[key: A, val: int] = +proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] = ## returns the largest (key,val)-pair. Efficiency: O(n) assert t.len > 0 var minIdx = 0 @@ -726,7 +741,7 @@ proc smallest*[A](t: TCountTable[A]): tuple[key: A, val: int] = result.key = t.data[minIdx].key result.val = t.data[minIdx].val -proc largest*[A](t: TCountTable[A]): tuple[key: A, val: int] = +proc largest*[A](t: CountTable[A]): tuple[key: A, val: int] = ## returns the (key,val)-pair with the largest `val`. Efficiency: O(n) assert t.len > 0 var maxIdx = 0 @@ -735,7 +750,7 @@ proc largest*[A](t: TCountTable[A]): tuple[key: A, val: int] = result.key = t.data[maxIdx].key result.val = t.data[maxIdx].val -proc sort*[A](t: var TCountTable[A]) = +proc sort*[A](t: var CountTable[A]) = ## sorts the count table so that the entry with the highest counter comes ## first. This is destructive! You must not modify `t` afterwards! ## You can use the iterators `pairs`, `keys`, and `values` to iterate over @@ -756,57 +771,57 @@ proc sort*[A](t: var TCountTable[A]) = if j < h: break if h == 1: break -proc len*[A](t: PCountTable[A]): int = +proc len*[A](t: CountTableRef[A]): int = ## returns the number of keys in `t`. result = t.counter -iterator pairs*[A](t: PCountTable[A]): tuple[key: A, val: int] = +iterator pairs*[A](t: CountTableRef[A]): tuple[key: A, val: int] = ## iterates over any (key, value) pair in the table `t`. for h in 0..high(t.data): if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val) -iterator mpairs*[A](t: PCountTable[A]): tuple[key: A, val: var int] = +iterator mpairs*[A](t: CountTableRef[A]): tuple[key: A, val: var int] = ## iterates over any (key, value) pair in the table `t`. The values can ## be modified. for h in 0..high(t.data): if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val) -iterator keys*[A](t: PCountTable[A]): A = +iterator keys*[A](t: CountTableRef[A]): A = ## iterates over any key in the table `t`. for h in 0..high(t.data): if t.data[h].val != 0: yield t.data[h].key -iterator values*[A](t: PCountTable[A]): int = +iterator values*[A](t: CountTableRef[A]): int = ## iterates over any value in the table `t`. for h in 0..high(t.data): if t.data[h].val != 0: yield t.data[h].val -iterator mvalues*[A](t: PCountTable[A]): var int = +iterator mvalues*[A](t: CountTableRef[A]): var int = ## iterates over any value in the table `t`. The values can be modified. for h in 0..high(t.data): if t.data[h].val != 0: yield t.data[h].val -proc `[]`*[A](t: PCountTable[A], key: A): int = +proc `[]`*[A](t: CountTableRef[A], key: A): int = ## retrieves the value at ``t[key]``. If `key` is not in `t`, ## 0 is returned. One can check with ``hasKey`` whether the key ## exists. result = t[][key] -proc mget*[A](t: PCountTable[A], key: A): var int = +proc mget*[A](t: CountTableRef[A], key: A): var int = ## retrieves the value at ``t[key]``. The value can be modified. ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised. result = t[].mget(key) -proc hasKey*[A](t: PCountTable[A], key: A): bool = +proc hasKey*[A](t: CountTableRef[A], key: A): bool = ## returns true iff `key` is in the table `t`. result = t[].hasKey(key) -proc `[]=`*[A](t: PCountTable[A], key: A, val: int) = +proc `[]=`*[A](t: CountTableRef[A], key: A, val: int) = ## puts a (key, value)-pair into `t`. `val` has to be positive. assert val > 0 t[][key] = val -proc newCountTable*[A](initialSize=64): PCountTable[A] = +proc newCountTable*[A](initialSize=64): CountTableRef[A] = ## creates a new count table that is empty. ## ## `initialSize` needs to be a power of two. If you need to accept runtime @@ -815,28 +830,28 @@ proc newCountTable*[A](initialSize=64): PCountTable[A] = new(result) result[] = initCountTable[A](initialSize) -proc newCountTable*[A](keys: openArray[A]): PCountTable[A] = +proc newCountTable*[A](keys: openArray[A]): CountTableRef[A] = ## creates a new count table with every key in `keys` having a count of 1. result = newCountTable[A](nextPowerOfTwo(keys.len+10)) for key in items(keys): result[key] = 1 -proc `$`*[A](t: PCountTable[A]): string = +proc `$`*[A](t: CountTableRef[A]): string = ## The `$` operator for count tables. dollarImpl() -proc inc*[A](t: PCountTable[A], key: A, val = 1) = +proc inc*[A](t: CountTableRef[A], key: A, val = 1) = ## increments `t[key]` by `val`. t[].inc(key, val) -proc smallest*[A](t: PCountTable[A]): tuple[key: A, val: int] = +proc smallest*[A](t: CountTableRef[A]): tuple[key: A, val: int] = ## returns the largest (key,val)-pair. Efficiency: O(n) t[].smallest -proc largest*[A](t: PCountTable[A]): tuple[key: A, val: int] = +proc largest*[A](t: CountTableRef[A]): tuple[key: A, val: int] = ## returns the (key,val)-pair with the largest `val`. Efficiency: O(n) t[].largest -proc sort*[A](t: PCountTable[A]) = +proc sort*[A](t: CountTableRef[A]) = ## sorts the count table so that the entry with the highest counter comes ## first. This is destructive! You must not modify `t` afterwards! ## You can use the iterators `pairs`, `keys`, and `values` to iterate over diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim index 9f824e5de..7942255cb 100644 --- a/lib/pure/colors.nim +++ b/lib/pure/colors.nim @@ -1,5 +1,5 @@ # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2010 Andreas Rumpf # # See the file "copying.txt", included in this @@ -12,18 +12,20 @@ import strutils type - TColor* = distinct int ## a color stored as RGB + Color* = distinct int ## a color stored as RGB -proc `==` *(a, b: TColor): bool {.borrow.} +{.deprecated: [TColor: Color].} + +proc `==` *(a, b: Color): bool {.borrow.} ## compares two colors. -template extract(a: TColor, r, g, b: expr) {.immediate.}= +template extract(a: Color, r, g, b: expr) {.immediate.}= var r = a.int shr 16 and 0xff var g = a.int shr 8 and 0xff var b = a.int and 0xff template rawRGB(r, g, b: int): expr = - TColor(r shl 16 or g shl 8 or b) + Color(r shl 16 or g shl 8 or b) template colorOp(op: expr) {.immediate.} = extract(a, ar, ag, ab) @@ -38,23 +40,23 @@ proc satMinus(a, b: int): int {.inline.} = result = a -% b if result < 0: result = 0 -proc `+`*(a, b: TColor): TColor = +proc `+`*(a, b: Color): Color = ## adds two colors: This uses saturated artithmetic, so that each color ## component cannot overflow (255 is used as a maximum). colorOp(satPlus) -proc `-`*(a, b: TColor): TColor = +proc `-`*(a, b: Color): Color = ## substracts two colors: This uses saturated artithmetic, so that each color ## component cannot overflow (255 is used as a maximum). colorOp(satMinus) -proc extractRGB*(a: TColor): tuple[r, g, b: range[0..255]] = +proc extractRGB*(a: Color): tuple[r, g, b: range[0..255]] = ## extracts the red/green/blue components of the color `a`. result.r = a.int shr 16 and 0xff result.g = a.int shr 8 and 0xff result.b = a.int and 0xff -proc intensity*(a: TColor, f: float): TColor = +proc intensity*(a: Color, f: float): Color = ## returns `a` with intensity `f`. `f` should be a float from 0.0 (completely ## dark) to 1.0 (full color intensity). var r = toInt(toFloat(a.int shr 16 and 0xff) * f) @@ -65,7 +67,7 @@ proc intensity*(a: TColor, f: float): TColor = if b >% 255: b = 255 result = rawRGB(r, g, b) -template mix*(a, b: TColor, fn: expr): expr = +template mix*(a, b: Color, fn: expr): expr = ## uses `fn` to mix the colors `a` and `b`. `fn` is invoked for each component ## R, G, and B. This is a template because `fn` should be inlined and the ## compiler cannot inline proc pointers yet. If `fn`'s result is not in the @@ -84,146 +86,146 @@ template mix*(a, b: TColor, fn: expr): expr = const - colAliceBlue* = TColor(0xF0F8FF) - colAntiqueWhite* = TColor(0xFAEBD7) - colAqua* = TColor(0x00FFFF) - colAquamarine* = TColor(0x7FFFD4) - colAzure* = TColor(0xF0FFFF) - colBeige* = TColor(0xF5F5DC) - colBisque* = TColor(0xFFE4C4) - colBlack* = TColor(0x000000) - colBlanchedAlmond* = TColor(0xFFEBCD) - colBlue* = TColor(0x0000FF) - colBlueViolet* = TColor(0x8A2BE2) - colBrown* = TColor(0xA52A2A) - colBurlyWood* = TColor(0xDEB887) - colCadetBlue* = TColor(0x5F9EA0) - colChartreuse* = TColor(0x7FFF00) - colChocolate* = TColor(0xD2691E) - colCoral* = TColor(0xFF7F50) - colCornflowerBlue* = TColor(0x6495ED) - colCornsilk* = TColor(0xFFF8DC) - colCrimson* = TColor(0xDC143C) - colCyan* = TColor(0x00FFFF) - colDarkBlue* = TColor(0x00008B) - colDarkCyan* = TColor(0x008B8B) - colDarkGoldenRod* = TColor(0xB8860B) - colDarkGray* = TColor(0xA9A9A9) - colDarkGreen* = TColor(0x006400) - colDarkKhaki* = TColor(0xBDB76B) - colDarkMagenta* = TColor(0x8B008B) - colDarkOliveGreen* = TColor(0x556B2F) - colDarkorange* = TColor(0xFF8C00) - colDarkOrchid* = TColor(0x9932CC) - colDarkRed* = TColor(0x8B0000) - colDarkSalmon* = TColor(0xE9967A) - colDarkSeaGreen* = TColor(0x8FBC8F) - colDarkSlateBlue* = TColor(0x483D8B) - colDarkSlateGray* = TColor(0x2F4F4F) - colDarkTurquoise* = TColor(0x00CED1) - colDarkViolet* = TColor(0x9400D3) - colDeepPink* = TColor(0xFF1493) - colDeepSkyBlue* = TColor(0x00BFFF) - colDimGray* = TColor(0x696969) - colDodgerBlue* = TColor(0x1E90FF) - colFireBrick* = TColor(0xB22222) - colFloralWhite* = TColor(0xFFFAF0) - colForestGreen* = TColor(0x228B22) - colFuchsia* = TColor(0xFF00FF) - colGainsboro* = TColor(0xDCDCDC) - colGhostWhite* = TColor(0xF8F8FF) - colGold* = TColor(0xFFD700) - colGoldenRod* = TColor(0xDAA520) - colGray* = TColor(0x808080) - colGreen* = TColor(0x008000) - colGreenYellow* = TColor(0xADFF2F) - colHoneyDew* = TColor(0xF0FFF0) - colHotPink* = TColor(0xFF69B4) - colIndianRed* = TColor(0xCD5C5C) - colIndigo* = TColor(0x4B0082) - colIvory* = TColor(0xFFFFF0) - colKhaki* = TColor(0xF0E68C) - colLavender* = TColor(0xE6E6FA) - colLavenderBlush* = TColor(0xFFF0F5) - colLawnGreen* = TColor(0x7CFC00) - colLemonChiffon* = TColor(0xFFFACD) - colLightBlue* = TColor(0xADD8E6) - colLightCoral* = TColor(0xF08080) - colLightCyan* = TColor(0xE0FFFF) - colLightGoldenRodYellow* = TColor(0xFAFAD2) - colLightGrey* = TColor(0xD3D3D3) - colLightGreen* = TColor(0x90EE90) - colLightPink* = TColor(0xFFB6C1) - colLightSalmon* = TColor(0xFFA07A) - colLightSeaGreen* = TColor(0x20B2AA) - colLightSkyBlue* = TColor(0x87CEFA) - colLightSlateGray* = TColor(0x778899) - colLightSteelBlue* = TColor(0xB0C4DE) - colLightYellow* = TColor(0xFFFFE0) - colLime* = TColor(0x00FF00) - colLimeGreen* = TColor(0x32CD32) - colLinen* = TColor(0xFAF0E6) - colMagenta* = TColor(0xFF00FF) - colMaroon* = TColor(0x800000) - colMediumAquaMarine* = TColor(0x66CDAA) - colMediumBlue* = TColor(0x0000CD) - colMediumOrchid* = TColor(0xBA55D3) - colMediumPurple* = TColor(0x9370D8) - colMediumSeaGreen* = TColor(0x3CB371) - colMediumSlateBlue* = TColor(0x7B68EE) - colMediumSpringGreen* = TColor(0x00FA9A) - colMediumTurquoise* = TColor(0x48D1CC) - colMediumVioletRed* = TColor(0xC71585) - colMidnightBlue* = TColor(0x191970) - colMintCream* = TColor(0xF5FFFA) - colMistyRose* = TColor(0xFFE4E1) - colMoccasin* = TColor(0xFFE4B5) - colNavajoWhite* = TColor(0xFFDEAD) - colNavy* = TColor(0x000080) - colOldLace* = TColor(0xFDF5E6) - colOlive* = TColor(0x808000) - colOliveDrab* = TColor(0x6B8E23) - colOrange* = TColor(0xFFA500) - colOrangeRed* = TColor(0xFF4500) - colOrchid* = TColor(0xDA70D6) - colPaleGoldenRod* = TColor(0xEEE8AA) - colPaleGreen* = TColor(0x98FB98) - colPaleTurquoise* = TColor(0xAFEEEE) - colPaleVioletRed* = TColor(0xD87093) - colPapayaWhip* = TColor(0xFFEFD5) - colPeachPuff* = TColor(0xFFDAB9) - colPeru* = TColor(0xCD853F) - colPink* = TColor(0xFFC0CB) - colPlum* = TColor(0xDDA0DD) - colPowderBlue* = TColor(0xB0E0E6) - colPurple* = TColor(0x800080) - colRed* = TColor(0xFF0000) - colRosyBrown* = TColor(0xBC8F8F) - colRoyalBlue* = TColor(0x4169E1) - colSaddleBrown* = TColor(0x8B4513) - colSalmon* = TColor(0xFA8072) - colSandyBrown* = TColor(0xF4A460) - colSeaGreen* = TColor(0x2E8B57) - colSeaShell* = TColor(0xFFF5EE) - colSienna* = TColor(0xA0522D) - colSilver* = TColor(0xC0C0C0) - colSkyBlue* = TColor(0x87CEEB) - colSlateBlue* = TColor(0x6A5ACD) - colSlateGray* = TColor(0x708090) - colSnow* = TColor(0xFFFAFA) - colSpringGreen* = TColor(0x00FF7F) - colSteelBlue* = TColor(0x4682B4) - colTan* = TColor(0xD2B48C) - colTeal* = TColor(0x008080) - colThistle* = TColor(0xD8BFD8) - colTomato* = TColor(0xFF6347) - colTurquoise* = TColor(0x40E0D0) - colViolet* = TColor(0xEE82EE) - colWheat* = TColor(0xF5DEB3) - colWhite* = TColor(0xFFFFFF) - colWhiteSmoke* = TColor(0xF5F5F5) - colYellow* = TColor(0xFFFF00) - colYellowGreen* = TColor(0x9ACD32) + colAliceBlue* = Color(0xF0F8FF) + colAntiqueWhite* = Color(0xFAEBD7) + colAqua* = Color(0x00FFFF) + colAquamarine* = Color(0x7FFFD4) + colAzure* = Color(0xF0FFFF) + colBeige* = Color(0xF5F5DC) + colBisque* = Color(0xFFE4C4) + colBlack* = Color(0x000000) + colBlanchedAlmond* = Color(0xFFEBCD) + colBlue* = Color(0x0000FF) + colBlueViolet* = Color(0x8A2BE2) + colBrown* = Color(0xA52A2A) + colBurlyWood* = Color(0xDEB887) + colCadetBlue* = Color(0x5F9EA0) + colChartreuse* = Color(0x7FFF00) + colChocolate* = Color(0xD2691E) + colCoral* = Color(0xFF7F50) + colCornflowerBlue* = Color(0x6495ED) + colCornsilk* = Color(0xFFF8DC) + colCrimson* = Color(0xDC143C) + colCyan* = Color(0x00FFFF) + colDarkBlue* = Color(0x00008B) + colDarkCyan* = Color(0x008B8B) + colDarkGoldenRod* = Color(0xB8860B) + colDarkGray* = Color(0xA9A9A9) + colDarkGreen* = Color(0x006400) + colDarkKhaki* = Color(0xBDB76B) + colDarkMagenta* = Color(0x8B008B) + colDarkOliveGreen* = Color(0x556B2F) + colDarkorange* = Color(0xFF8C00) + colDarkOrchid* = Color(0x9932CC) + colDarkRed* = Color(0x8B0000) + colDarkSalmon* = Color(0xE9967A) + colDarkSeaGreen* = Color(0x8FBC8F) + colDarkSlateBlue* = Color(0x483D8B) + colDarkSlateGray* = Color(0x2F4F4F) + colDarkTurquoise* = Color(0x00CED1) + colDarkViolet* = Color(0x9400D3) + colDeepPink* = Color(0xFF1493) + colDeepSkyBlue* = Color(0x00BFFF) + colDimGray* = Color(0x696969) + colDodgerBlue* = Color(0x1E90FF) + colFireBrick* = Color(0xB22222) + colFloralWhite* = Color(0xFFFAF0) + colForestGreen* = Color(0x228B22) + colFuchsia* = Color(0xFF00FF) + colGainsboro* = Color(0xDCDCDC) + colGhostWhite* = Color(0xF8F8FF) + colGold* = Color(0xFFD700) + colGoldenRod* = Color(0xDAA520) + colGray* = Color(0x808080) + colGreen* = Color(0x008000) + colGreenYellow* = Color(0xADFF2F) + colHoneyDew* = Color(0xF0FFF0) + colHotPink* = Color(0xFF69B4) + colIndianRed* = Color(0xCD5C5C) + colIndigo* = Color(0x4B0082) + colIvory* = Color(0xFFFFF0) + colKhaki* = Color(0xF0E68C) + colLavender* = Color(0xE6E6FA) + colLavenderBlush* = Color(0xFFF0F5) + colLawnGreen* = Color(0x7CFC00) + colLemonChiffon* = Color(0xFFFACD) + colLightBlue* = Color(0xADD8E6) + colLightCoral* = Color(0xF08080) + colLightCyan* = Color(0xE0FFFF) + colLightGoldenRodYellow* = Color(0xFAFAD2) + colLightGrey* = Color(0xD3D3D3) + colLightGreen* = Color(0x90EE90) + colLightPink* = Color(0xFFB6C1) + colLightSalmon* = Color(0xFFA07A) + colLightSeaGreen* = Color(0x20B2AA) + colLightSkyBlue* = Color(0x87CEFA) + colLightSlateGray* = Color(0x778899) + colLightSteelBlue* = Color(0xB0C4DE) + colLightYellow* = Color(0xFFFFE0) + colLime* = Color(0x00FF00) + colLimeGreen* = Color(0x32CD32) + colLinen* = Color(0xFAF0E6) + colMagenta* = Color(0xFF00FF) + colMaroon* = Color(0x800000) + colMediumAquaMarine* = Color(0x66CDAA) + colMediumBlue* = Color(0x0000CD) + colMediumOrchid* = Color(0xBA55D3) + colMediumPurple* = Color(0x9370D8) + colMediumSeaGreen* = Color(0x3CB371) + colMediumSlateBlue* = Color(0x7B68EE) + colMediumSpringGreen* = Color(0x00FA9A) + colMediumTurquoise* = Color(0x48D1CC) + colMediumVioletRed* = Color(0xC71585) + colMidnightBlue* = Color(0x191970) + colMintCream* = Color(0xF5FFFA) + colMistyRose* = Color(0xFFE4E1) + colMoccasin* = Color(0xFFE4B5) + colNavajoWhite* = Color(0xFFDEAD) + colNavy* = Color(0x000080) + colOldLace* = Color(0xFDF5E6) + colOlive* = Color(0x808000) + colOliveDrab* = Color(0x6B8E23) + colOrange* = Color(0xFFA500) + colOrangeRed* = Color(0xFF4500) + colOrchid* = Color(0xDA70D6) + colPaleGoldenRod* = Color(0xEEE8AA) + colPaleGreen* = Color(0x98FB98) + colPaleTurquoise* = Color(0xAFEEEE) + colPaleVioletRed* = Color(0xD87093) + colPapayaWhip* = Color(0xFFEFD5) + colPeachPuff* = Color(0xFFDAB9) + colPeru* = Color(0xCD853F) + colPink* = Color(0xFFC0CB) + colPlum* = Color(0xDDA0DD) + colPowderBlue* = Color(0xB0E0E6) + colPurple* = Color(0x800080) + colRed* = Color(0xFF0000) + colRosyBrown* = Color(0xBC8F8F) + colRoyalBlue* = Color(0x4169E1) + colSaddleBrown* = Color(0x8B4513) + colSalmon* = Color(0xFA8072) + colSandyBrown* = Color(0xF4A460) + colSeaGreen* = Color(0x2E8B57) + colSeaShell* = Color(0xFFF5EE) + colSienna* = Color(0xA0522D) + colSilver* = Color(0xC0C0C0) + colSkyBlue* = Color(0x87CEEB) + colSlateBlue* = Color(0x6A5ACD) + colSlateGray* = Color(0x708090) + colSnow* = Color(0xFFFAFA) + colSpringGreen* = Color(0x00FF7F) + colSteelBlue* = Color(0x4682B4) + colTan* = Color(0xD2B48C) + colTeal* = Color(0x008080) + colThistle* = Color(0xD8BFD8) + colTomato* = Color(0xFF6347) + colTurquoise* = Color(0x40E0D0) + colViolet* = Color(0xEE82EE) + colWheat* = Color(0xF5DEB3) + colWhite* = Color(0xFFFFFF) + colWhiteSmoke* = Color(0xF5F5F5) + colYellow* = Color(0xFFFF00) + colYellowGreen* = Color(0x9ACD32) colorNames = [ ("aliceblue", colAliceBlue), @@ -367,11 +369,11 @@ const ("yellow", colYellow), ("yellowgreen", colYellowGreen)] -proc `$`*(c: TColor): string = +proc `$`*(c: Color): string = ## converts a color into its textual representation. Example: ``#00FF00``. result = '#' & toHex(int(c), 6) -proc binaryStrSearch(x: openarray[tuple[name: string, col: TColor]], +proc binaryStrSearch(x: openArray[tuple[name: string, col: Color]], y: string): int = var a = 0 var b = len(x) - 1 @@ -383,14 +385,14 @@ proc binaryStrSearch(x: openarray[tuple[name: string, col: TColor]], else: return mid result = - 1 -proc parseColor*(name: string): TColor = +proc parseColor*(name: string): Color = ## parses `name` to a color value. If no valid color could be ## parsed ``EInvalidValue`` is raised. if name[0] == '#': - result = TColor(parseHexInt(name)) + result = Color(parseHexInt(name)) else: var idx = binaryStrSearch(colorNames, name) - if idx < 0: raise newException(EInvalidValue, "unkown color: " & name) + if idx < 0: raise newException(ValueError, "unkown color: " & name) result = colorNames[idx][1] proc isColor*(name: string): bool = @@ -403,7 +405,7 @@ proc isColor*(name: string): bool = else: result = binaryStrSearch(colorNames, name) >= 0 -proc rgb*(r, g, b: range[0..255]): TColor = +proc rgb*(r, g, b: range[0..255]): Color = ## constructs a color from RGB values. result = rawRGB(r, g, b) diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim index 1392b73aa..0c5704f69 100644 --- a/lib/pure/complex.nim +++ b/lib/pure/complex.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2010 Andreas Rumpf # # See the file "copying.txt", included in this @@ -19,59 +19,58 @@ import math const - EPS = 5.0e-6 ## Epsilon used for float comparisons (should be smaller - ## if float is really float64, but w/ the current version - ## it seems to be float32?) - + EPS = 1.0e-7 ## Epsilon used for float comparisons. type - TComplex* = tuple[re, im: float] + Complex* = tuple[re, im: float] ## a complex number, consisting of a real and an imaginary part -proc `==` *(x, y: TComplex): bool = +{.deprecated: [TComplex: Complex].} + +proc `==` *(x, y: Complex): bool = ## Compare two complex numbers `x` and `y` for equality. result = x.re == y.re and x.im == y.im -proc `=~` *(x, y: TComplex): bool = +proc `=~` *(x, y: Complex): bool = ## Compare two complex numbers `x` and `y` approximately. result = abs(x.re-y.re)".} var f = open("/proc/loadavg") diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index f46822d94..0bd97043a 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -1,13 +1,13 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## Implements Nimrod's 'spawn'. +## Implements Nim's 'spawn'. when not compileOption("threads"): {.error: "Threadpool requires --threads:on option.".} @@ -17,27 +17,27 @@ import cpuinfo, cpuload, locks {.push stackTrace:off.} type - CondVar = object + Semaphore = object c: TCond L: TLock counter: int -proc createCondVar(): CondVar = +proc createSemaphore(): Semaphore = initCond(result.c) initLock(result.L) -proc destroyCondVar(cv: var CondVar) {.inline.} = +proc destroySemaphore(cv: var Semaphore) {.inline.} = deinitCond(cv.c) deinitLock(cv.L) -proc await(cv: var CondVar) = +proc await(cv: var Semaphore) = acquire(cv.L) while cv.counter <= 0: wait(cv.c, cv.L) dec cv.counter release(cv.L) -proc signal(cv: var CondVar) = +proc signal(cv: var Semaphore) = acquire(cv.L) inc cv.counter release(cv.L) @@ -48,7 +48,7 @@ const CacheLineSize = 32 # true for most archs type Barrier {.compilerProc.} = object entered: int - cv: CondVar # condvar takes 3 words at least + cv: Semaphore # Semaphore takes 3 words at least when sizeof(int) < 8: cacheAlign: array[CacheLineSize-4*sizeof(int), byte] left: int @@ -65,7 +65,9 @@ proc barrierEnter(b: ptr Barrier) {.compilerProc, inline.} = proc barrierLeave(b: ptr Barrier) {.compilerProc, inline.} = atomicInc b.left when not defined(x86): fence() - if b.interest and b.left == b.entered: signal(b.cv) + # We may not have seen the final value of b.entered yet, + # so we need to check for >= instead of ==. + if b.interest and b.left >= b.entered: signal(b.cv) proc openBarrier(b: ptr Barrier) {.compilerProc, inline.} = b.entered = 0 @@ -75,12 +77,12 @@ proc openBarrier(b: ptr Barrier) {.compilerProc, inline.} = proc closeBarrier(b: ptr Barrier) {.compilerProc.} = fence() if b.left != b.entered: - b.cv = createCondVar() + b.cv = createSemaphore() fence() b.interest = true fence() while b.left != b.entered: await(b.cv) - destroyCondVar(b.cv) + destroySemaphore(b.cv) {.pop.} @@ -90,17 +92,18 @@ type foreign* = object ## a region that indicates the pointer comes from a ## foreign thread heap. AwaitInfo = object - cv: CondVar + cv: Semaphore idx: int FlowVarBase* = ref FlowVarBaseObj ## untyped base class for 'FlowVar[T]' - FlowVarBaseObj = object of TObject - ready, usesCondVar, awaited: bool - cv: CondVar #\ + FlowVarBaseObj = object of RootObj + ready, usesSemaphore, awaited: bool + cv: Semaphore #\ # for 'awaitAny' support ai: ptr AwaitInfo idx: int - data: pointer # we incRef and unref it to keep it alive + data: pointer # we incRef and unref it to keep it alive; note this MUST NOT + # be RootRef here otherwise the wrong GC keeps track of it! owner: pointer # ptr Worker FlowVarObj[T] = object of FlowVarBaseObj @@ -111,13 +114,13 @@ type ToFreeQueue = object len: int lock: TLock - empty: TCond - data: array[512, pointer] + empty: Semaphore + data: array[128, pointer] WorkerProc = proc (thread, args: pointer) {.nimcall, gcsafe.} Worker = object - taskArrived: CondVar - taskStarted: CondVar #\ + taskArrived: Semaphore + taskStarted: Semaphore #\ # task data: f: WorkerProc data: pointer @@ -129,10 +132,40 @@ type proc await*(fv: FlowVarBase) = ## waits until the value for the flowVar arrives. Usually it is not necessary ## to call this explicitly. - if fv.usesCondVar and not fv.awaited: + if fv.usesSemaphore and not fv.awaited: fv.awaited = true await(fv.cv) - destroyCondVar(fv.cv) + destroySemaphore(fv.cv) + +proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool = + if cas(addr w.ready, true, false): + w.data = data + w.f = fn + signal(w.taskArrived) + await(w.taskStarted) + result = true + +proc cleanFlowVars(w: ptr Worker) = + let q = addr(w.q) + acquire(q.lock) + for i in 0 .. ".}: int RTLD_GLOBAL {.importc: "RTLD_GLOBAL", header: "".}: int - proc dlclose(lib: TLibHandle) {.importc, header: "".} - proc dlopen(path: CString, mode: int): TLibHandle {. + proc dlclose(lib: LibHandle) {.importc, header: "".} + proc dlopen(path: cstring, mode: int): LibHandle {. importc, header: "".} - proc dlsym(lib: TLibHandle, name: cstring): pointer {. + proc dlsym(lib: LibHandle, name: cstring): pointer {. importc, header: "".} - proc loadLib(path: string, global_symbols=false): TLibHandle = + proc loadLib(path: string, global_symbols=false): LibHandle = var flags = RTLD_NOW if global_symbols: flags = flags or RTLD_GLOBAL return dlopen(path, flags) - proc loadLib(): TLibHandle = return dlopen(nil, RTLD_NOW) - proc unloadLib(lib: TLibHandle) = dlclose(lib) - proc symAddr(lib: TLibHandle, name: cstring): pointer = + proc loadLib(): LibHandle = return dlopen(nil, RTLD_NOW) + proc unloadLib(lib: LibHandle) = dlclose(lib) + proc symAddr(lib: LibHandle, name: cstring): pointer = return dlsym(lib, name) elif defined(windows) or defined(dos): @@ -85,13 +87,13 @@ elif defined(windows) or defined(dos): proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {. importc: "GetProcAddress", header: "", stdcall.} - proc loadLib(path: string, global_symbols=false): TLibHandle = - result = cast[TLibHandle](winLoadLibrary(path)) - proc loadLib(): TLibHandle = - result = cast[TLibHandle](winLoadLibrary(nil)) - proc unloadLib(lib: TLibHandle) = FreeLibrary(cast[THINSTANCE](lib)) + proc loadLib(path: string, global_symbols=false): LibHandle = + result = cast[LibHandle](winLoadLibrary(path)) + proc loadLib(): LibHandle = + result = cast[LibHandle](winLoadLibrary(nil)) + proc unloadLib(lib: LibHandle) = FreeLibrary(cast[THINSTANCE](lib)) - proc symAddr(lib: TLibHandle, name: cstring): pointer = + proc symAddr(lib: LibHandle, name: cstring): pointer = result = getProcAddress(cast[THINSTANCE](lib), name) else: diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index 633ea6020..958a4133b 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -14,18 +14,20 @@ import os, parseutils, strutils when not defined(windows): type - TConverter = object - PConverter* = ptr TConverter ## can convert between two character sets + ConverterObj = object + EncodingConverter* = ptr ConverterObj ## can convert between two character sets else: type - TCodePage = distinct int32 - PConverter* = object - dest, src: TCodePage + CodePage = distinct int32 + EncodingConverter* = object + dest, src: CodePage type - EInvalidEncoding* = object of EInvalidValue ## exception that is raised - ## for encoding errors + EncodingError* = object of ValueError ## exception that is raised + ## for encoding errors + +{.deprecated: [EInvalidEncoding: EncodingError, PConverter: EncodingConverter].} when defined(windows): proc eqEncodingNames(a, b: string): bool = @@ -34,7 +36,7 @@ when defined(windows): while i < a.len and j < b.len: if a[i] in {'-', '_'}: inc i if b[j] in {'-', '_'}: inc j - if a[i].tolower != b[j].tolower: return false + if a[i].toLower != b[j].toLower: return false inc i inc j result = i == a.len and j == b.len @@ -214,26 +216,26 @@ when defined(windows): defaultChar: array[0..1, char] leadByte: array[0..12-1, char] - proc getCPInfo(codePage: TCodePage, lpCPInfo: var TCpInfo): int32 {. + proc getCPInfo(codePage: CodePage, lpCPInfo: var TCpInfo): int32 {. stdcall, importc: "GetCPInfo", dynlib: "kernel32".} - proc nameToCodePage(name: string): TCodePage = + proc nameToCodePage(name: string): CodePage = var nameAsInt: int if parseInt(name, nameAsInt) == 0: nameAsInt = -1 for no, na in items(winEncodings): - if no == nameAsInt or eqEncodingNames(na, name): return TCodePage(no) - result = TCodePage(-1) + if no == nameAsInt or eqEncodingNames(na, name): return CodePage(no) + result = CodePage(-1) - proc codePageToName(c: TCodePage): string = + proc codePageToName(c: CodePage): string = for no, na in items(winEncodings): if no == int(c): return if na.len != 0: na else: $no result = "" - proc getACP(): TCodePage {.stdcall, importc: "GetACP", dynlib: "kernel32".} + proc getACP(): CodePage {.stdcall, importc: "GetACP", dynlib: "kernel32".} proc multiByteToWideChar( - codePage: TCodePage, + codePage: CodePage, dwFlags: int32, lpMultiByteStr: cstring, cbMultiByte: cint, @@ -242,7 +244,7 @@ when defined(windows): stdcall, importc: "MultiByteToWideChar", dynlib: "kernel32".} proc wideCharToMultiByte( - codePage: TCodePage, + codePage: CodePage, dwFlags: int32, lpWideCharStr: cstring, cchWideChar: cint, @@ -260,7 +262,7 @@ else: else: const iconvDll = "(libc.so.6|libiconv.so)" - when defined(macosx) and defined(powerpc32): + when defined(macosx) and defined(powerpc): const prefix = "lib" else: const prefix = "" @@ -279,50 +281,50 @@ else: var errno {.importc, header: "".}: cint - proc iconvOpen(tocode, fromcode: cstring): PConverter {. + proc iconvOpen(tocode, fromcode: cstring): EncodingConverter {. importc: prefix & "iconv_open", cdecl, dynlib: iconvDll.} - proc iconvClose(c: PConverter) {. + proc iconvClose(c: EncodingConverter) {. importc: prefix & "iconv_close", cdecl, dynlib: iconvDll.} - proc iconv(c: PConverter, inbuf: var cstring, inbytesLeft: var int, + proc iconv(c: EncodingConverter, inbuf: var cstring, inbytesLeft: var int, outbuf: var cstring, outbytesLeft: var int): int {. importc: prefix & "iconv", cdecl, dynlib: iconvDll.} - proc iconv(c: PConverter, inbuf: pointer, inbytesLeft: pointer, + proc iconv(c: EncodingConverter, inbuf: pointer, inbytesLeft: pointer, outbuf: var cstring, outbytesLeft: var int): int {. importc: prefix & "iconv", cdecl, dynlib: iconvDll.} proc getCurrentEncoding*(): string = ## retrieves the current encoding. On Unix, always "UTF-8" is returned. when defined(windows): - result = codePageToName(GetACP()) + result = codePageToName(getACP()) else: result = "UTF-8" -proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): PConverter = +proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): EncodingConverter = ## opens a converter that can convert from `srcEncoding` to `destEncoding`. ## Raises `EIO` if it cannot fullfill the request. when not defined(windows): result = iconvOpen(destEncoding, srcEncoding) if result == nil: - raise newException(EInvalidEncoding, + raise newException(EncodingError, "cannot create encoding converter from " & srcEncoding & " to " & destEncoding) else: result.dest = nameToCodePage(destEncoding) result.src = nameToCodePage(srcEncoding) if int(result.dest) == -1: - raise newException(EInvalidEncoding, + raise newException(EncodingError, "cannot find encoding " & destEncoding) if int(result.src) == -1: - raise newException(EInvalidEncoding, + raise newException(EncodingError, "cannot find encoding " & srcEncoding) -proc close*(c: PConverter) = +proc close*(c: EncodingConverter) = ## frees the resources the converter `c` holds. when not defined(windows): iconvClose(c) when defined(windows): - proc convert*(c: PConverter, s: string): string = + proc convert*(c: EncodingConverter, s: string): string = ## converts `s` to `destEncoding` that was given to the converter `c`. It ## assumed that `s` is in `srcEncoding`. @@ -352,7 +354,7 @@ when defined(windows): cbMultiByte = cint(s.len), lpWideCharStr = cstring(result), cchWideChar = cint(cap)) - if m == 0: osError(osLastError()) + if m == 0: raiseOSError(osLastError()) setLen(result, m*2) elif m <= cap: setLen(result, m*2) @@ -389,7 +391,7 @@ when defined(windows): cchWideChar = cint(result.len div 2), lpMultiByteStr = cstring(res), cbMultiByte = cap.cint) - if m == 0: osError(osLastError()) + if m == 0: raiseOSError(osLastError()) setLen(res, m) result = res elif m <= cap: @@ -399,7 +401,7 @@ when defined(windows): assert(false) # cannot happen else: - proc convert*(c: PConverter, s: string): string = + proc convert*(c: EncodingConverter, s: string): string = result = newString(s.len) var inLen = len(s) var outLen = len(result) @@ -412,7 +414,7 @@ else: var lerr = errno if lerr == EILSEQ or lerr == EINVAL: # unknown char, skip - Dst[0] = Src[0] + dst[0] = src[0] src = cast[cstring](cast[int](src) + 1) dst = cast[cstring](cast[int](dst) + 1) dec(inLen) @@ -424,19 +426,19 @@ else: dst = cast[cstring](cast[int](cstring(result)) + offset) outLen = len(result) - offset else: - osError(lerr.TOSErrorCode) + raiseOSError(lerr.OSErrorCode) # iconv has a buffer that needs flushing, specially if the last char is # not '\0' - discard iconv(c, nil, nil, dst, outlen) + discard iconv(c, nil, nil, dst, outLen) if iconvres == cint(-1) and errno == E2BIG: var offset = cast[int](dst) - cast[int](cstring(result)) setLen(result, len(result)+inLen*2+5) # 5 is minimally one utf-8 char dst = cast[cstring](cast[int](cstring(result)) + offset) outLen = len(result) - offset - discard iconv(c, nil, nil, dst, outlen) + discard iconv(c, nil, nil, dst, outLen) # trim output buffer - setLen(result, len(result) - outlen) + setLen(result, len(result) - outLen) proc convert*(s: string, destEncoding = "UTF-8", srcEncoding = "CP1252"): string = diff --git a/lib/pure/endians.nim b/lib/pure/endians.nim index 73017464d..6e33d4624 100644 --- a/lib/pure/endians.nim +++ b/lib/pure/endians.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/lib/pure/events.nim b/lib/pure/events.nim index 5830d9109..77faa6a66 100644 --- a/lib/pure/events.nim +++ b/lib/pure/events.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2011 Alex Mitchell # # See the file "copying.txt", included in this @@ -14,10 +14,10 @@ ## it was inspired by Python's PyEE module. There are two ways you can use ## events: one is a python-inspired way; the other is more of a C-style way. ## -## .. code-block:: Nimrod +## .. code-block:: Nim ## var ee = initEventEmitter() -## var genericargs: TEventArgs -## proc handleevent(e: TEventArgs) = +## var genericargs: EventArgs +## proc handleevent(e: EventArgs) = ## echo("Handled!") ## ## # Python way @@ -27,74 +27,77 @@ ## # C/Java way ## # Declare a type ## type -## TSomeObject = object of TObject -## SomeEvent: TEventHandler -## var myobj: TSomeObject +## SomeObject = object of RootObj +## SomeEvent: EventHandler +## var myobj: SomeObject ## myobj.SomeEvent = initEventHandler("SomeEvent") ## myobj.SomeEvent.addHandler(handleevent) ## ee.emit(myobj.SomeEvent, genericargs) type - TEventArgs* = object of TObject ## Base object for event arguments that are passed to callback functions. - TEventHandler* = tuple[name: string, handlers: seq[proc(e:TEventArgs) {.closure.}]] ## An eventhandler for an event. + EventArgs* = object of RootObj ## Base object for event arguments that are passed to callback functions. + EventHandler* = tuple[name: string, handlers: seq[proc(e: EventArgs) {.closure.}]] ## An eventhandler for an event. type - TEventEmitter* = object {.pure, final.} ## An object that fires events and holds event handlers for an object. - s: seq[TEventHandler] - EInvalidEvent* = object of EInvalidValue + EventEmitter* = object ## An object that fires events and holds event handlers for an object. + s: seq[EventHandler] + EventError* = object of ValueError + +{.deprecated: [TEventArgs: EventArgs, TEventHandler: EventHandler, + TEventEmitter: EventEmitter, EInvalidEvent: EventError].} -proc initEventHandler*(name: string): TEventHandler = +proc initEventHandler*(name: string): EventHandler = ## Initializes an EventHandler with the specified name and returns it. result.handlers = @[] result.name = name -proc addHandler*(handler: var TEventHandler, func: proc(e: TEventArgs) {.closure.}) = +proc addHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}) = ## Adds the callback to the specified event handler. - handler.handlers.add(func) + handler.handlers.add(fn) -proc removeHandler*(handler: var TEventHandler, func: proc(e: TEventArgs) {.closure.}) = +proc removeHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}) = ## Removes the callback from the specified event handler. for i in countup(0, len(handler.handlers) -1): - if func == handler.handlers[i]: + if fn == handler.handlers[i]: handler.handlers.del(i) break -proc containsHandler*(handler: var TEventHandler, func: proc(e: TEventArgs) {.closure.}): bool = +proc containsHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}): bool = ## Checks if a callback is registered to this event handler. - return handler.handlers.contains(func) + return handler.handlers.contains(fn) -proc clearHandlers*(handler: var TEventHandler) = +proc clearHandlers*(handler: var EventHandler) = ## Clears all of the callbacks from the event handler. setLen(handler.handlers, 0) -proc getEventHandler(emitter: var TEventEmitter, event: string): int = +proc getEventHandler(emitter: var EventEmitter, event: string): int = for k in 0..high(emitter.s): if emitter.s[k].name == event: return k return -1 -proc on*(emitter: var TEventEmitter, event: string, func: proc(e: TEventArgs) {.closure.}) = +proc on*(emitter: var EventEmitter, event: string, fn: proc(e: EventArgs) {.closure.}) = ## Assigns a event handler with the specified callback. If the event ## doesn't exist, it will be created. var i = getEventHandler(emitter, event) if i < 0: var eh = initEventHandler(event) - addHandler(eh, func) + addHandler(eh, fn) emitter.s.add(eh) else: - addHandler(emitter.s[i], func) + addHandler(emitter.s[i], fn) -proc emit*(emitter: var TEventEmitter, eventhandler: var TEventHandler, - args: TEventArgs) = +proc emit*(emitter: var EventEmitter, eventhandler: var EventHandler, + args: EventArgs) = ## Fires an event handler with specified event arguments. - for func in items(eventhandler.handlers): func(args) + for fn in items(eventhandler.handlers): fn(args) -proc emit*(emitter: var TEventEmitter, event: string, args: TEventArgs) = +proc emit*(emitter: var EventEmitter, event: string, args: EventArgs) = ## Fires an event handler with specified event arguments. var i = getEventHandler(emitter, event) if i >= 0: emit(emitter, emitter.s[i], args) -proc initEventEmitter*(): TEventEmitter = +proc initEventEmitter*(): EventEmitter = ## Creates and returns a new EventEmitter. result.s = @[] diff --git a/lib/impure/fenv.nim b/lib/pure/fenv.nim similarity index 87% rename from lib/impure/fenv.nim rename to lib/pure/fenv.nim index 9d7c8809b..415ef455e 100644 --- a/lib/impure/fenv.nim +++ b/lib/pure/fenv.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -8,7 +8,7 @@ # ## Floating-point environment. Handling of floating-point rounding and -## exceptions (overflow, zero-devide, etc.). +## exceptions (overflow, division by zero, etc.). {.deadCodeElim:on.} @@ -42,16 +42,6 @@ var ## case the default environment will be used type - TFloatClass* = enum ## describes the class a floating point value belongs to. - ## This is the type that is returned by `classify`. - fcNormal, ## value is an ordinary nonzero floating point value - fcSubnormal, ## value is a subnormal (a very small) floating point value - fcZero, ## value is zero - fcNegZero, ## value is the negative zero - fcNan, ## value is Not-A-Number (NAN) - fcInf, ## value is positive infinity - fcNegInf ## value is negative infinity - Tfenv* {.importc: "fenv_t", header: "", final, pure.} = object ## Represents the entire floating-point environment. The ## floating-point environment refers collectively to any diff --git a/lib/pure/fsmonitor.nim b/lib/pure/fsmonitor.nim index b35466771..bf4aef61c 100644 --- a/lib/pure/fsmonitor.nim +++ b/lib/pure/fsmonitor.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Dominik Picheta # # See the file "copying.txt", included in this @@ -25,13 +25,13 @@ else: import inotify, os, asyncio, tables type - PFSMonitor* = ref TFSMonitor - TFSMonitor = object of TObject + FSMonitor* = ref FSMonitorObj + FSMonitorObj = object of RootObj fd: cint - handleEvent: proc (m: PFSMonitor, ev: TMonitorEvent) {.closure.} + handleEvent: proc (m: FSMonitor, ev: MonitorEvent) {.closure.} targets: TTable[cint, string] - TMonitorEventType* = enum ## Monitor event type + MonitorEventType* = enum ## Monitor event type MonitorAccess, ## File was accessed. MonitorAttrib, ## Metadata changed. MonitorCloseWrite, ## Writtable file was closed. @@ -45,8 +45,8 @@ type MonitorOpen, ## File was opened. MonitorAll ## Filter for all event types. - TMonitorEvent* = object - case kind*: TMonitorEventType ## Type of the event. + MonitorEvent* = object + case kind*: MonitorEventType ## Type of the event. of MonitorMoveSelf, MonitorMoved: oldPath*: string ## Old absolute location newPath*: string ## New absolute location @@ -58,6 +58,9 @@ type ## watched. wd*: cint ## Watch descriptor. +{.deprecated: [PFSMonitor: FSMonitor, TFSMonitor: FSMonitorObj, + TMonitorEventType: MonitorEventType, TMonitorEvent: MonitorEvent].} + const MaxEvents = 100 @@ -67,7 +70,7 @@ proc newMonitor*(): PFSMonitor = result.targets = initTable[cint, string]() result.fd = inotifyInit() if result.fd < 0: - OSError(OSLastError()) + raiseOSError(osLastError()) proc add*(monitor: PFSMonitor, target: string, filters = {MonitorAll}): cint {.discardable.} = @@ -93,7 +96,7 @@ proc add*(monitor: PFSMonitor, target: string, result = inotifyAddWatch(monitor.fd, target, INFilter.uint32) if result < 0: - OSError(OSLastError()) + raiseOSError(osLastError()) monitor.targets.add(result, target) proc del*(monitor: PFSMonitor, wd: cint) = @@ -101,7 +104,7 @@ proc del*(monitor: PFSMonitor, wd: cint) = ## ## If ``wd`` is not a part of ``monitor`` an EOS error is raised. if inotifyRmWatch(monitor.fd, wd) < 0: - OSError(OSLastError()) + raiseOSError(osLastError()) proc getEvent(m: PFSMonitor, fd: cint): seq[TMonitorEvent] = result = @[] @@ -110,8 +113,7 @@ proc getEvent(m: PFSMonitor, fd: cint): seq[TMonitorEvent] = let le = read(fd, addr(buffer[0]), size) - var movedFrom: TTable[cint, tuple[wd: cint, old: string]] = - initTable[cint, tuple[wd: cint, old: string]]() + var movedFrom = initTable[cint, tuple[wd: cint, old: string]]() var i = 0 while i < le: @@ -197,18 +199,19 @@ proc register*(d: PDispatcher, monitor: PFSMonitor, d.register(deleg) when isMainModule: - var disp = newDispatcher() - var monitor = newMonitor() - echo monitor.add("/home/dom/inotifytests/") - disp.register(monitor, - proc (m: PFSMonitor, ev: TMonitorEvent) = - echo("Got event: ", ev.kind) - if ev.kind == MonitorMoved: - echo("From ", ev.oldPath, " to ", ev.newPath) - echo("Name is ", ev.name) - else: - echo("Name ", ev.name, " fullname ", ev.fullName)) - - while true: - if not disp.poll(): break - + proc main = + var disp = newDispatcher() + var monitor = newMonitor() + echo monitor.add("/home/dom/inotifytests/") + disp.register(monitor, + proc (m: PFSMonitor, ev: TMonitorEvent) = + echo("Got event: ", ev.kind) + if ev.kind == MonitorMoved: + echo("From ", ev.oldPath, " to ", ev.newPath) + echo("Name is ", ev.name) + else: + echo("Name ", ev.name, " fullname ", ev.fullName)) + + while true: + if not disp.poll(): break + main() diff --git a/lib/pure/ftpclient.nim b/lib/pure/ftpclient.nim index e8d3f762e..975eae7e2 100644 --- a/lib/pure/ftpclient.nim +++ b/lib/pure/ftpclient.nim @@ -1,7 +1,7 @@ # # -# Nimrod's Runtime Library -# (c) Copyright 2012 Dominik Picheta +# Nim's Runtime Library +# (c) Copyright 2014 Dominik Picheta # See the file "copying.txt", included in this # distribution, for details about the copyright. # @@ -18,8 +18,8 @@ from asyncdispatch import PFuture ## by `RFC 959 `_. ## ## This module provides both a synchronous and asynchronous implementation. -## The asynchronous implementation requires you to use the ``AsyncFTPClient`` -## function. You are then required to register the ``PAsyncFTPClient`` with a +## The asynchronous implementation requires you to use the ``asyncFTPClient`` +## function. You are then required to register the ``AsyncFTPClient`` with a ## asyncio dispatcher using the ``register`` function. Take a look at the ## asyncio module documentation for more information. ## @@ -28,65 +28,64 @@ from asyncdispatch import PFuture ## ## Here is some example usage of this module: ## -## .. code-block:: Nimrod -## var ftp = FTPClient("example.org", user = "user", pass = "pass") +## .. code-block:: Nim +## var ftp = ftpClient("example.org", user = "user", pass = "pass") ## ftp.connect() ## ftp.retrFile("file.ext", "file.ext") ## ## **Warning:** The API of this module is unstable, and therefore is subject ## to change. - type - PFtpBase*[SockType] = ref TFtpBase[SockType] - TFtpBase*[SockType] = object + FtpBase*[SockType] = ref FtpBaseObj[SockType] + FtpBaseObj*[SockType] = object csock*: SockType dsock*: SockType - when SockType is asyncio.PAsyncSocket: - handleEvent*: proc (ftp: PAsyncFTPClient, ev: TFTPEvent){.closure,gcsafe.} - disp: PDispatcher - asyncDSockID: PDelegate + when SockType is asyncio.AsyncSocket: + handleEvent*: proc (ftp: AsyncFTPClient, ev: FTPEvent){.closure,gcsafe.} + disp: Dispatcher + asyncDSockID: Delegate user*, pass*: string address*: string - when SockType is asyncnet.PAsyncSocket: - port*: rawsockets.TPort + when SockType is asyncnet.AsyncSocket: + port*: rawsockets.Port else: - port*: TPort + port*: Port jobInProgress*: bool - job*: PFTPJob[SockType] + job*: FTPJob[SockType] dsockConnected*: bool FTPJobType* = enum JRetrText, JRetr, JStore - PFtpJob[T] = ref TFtpJob[T] - TFTPJob[T] = object - prc: proc (ftp: PFTPBase[T], async: bool): bool {.nimcall, gcsafe.} + FtpJob[T] = ref FtpJobObj[T] + FTPJobObj[T] = object + prc: proc (ftp: FTPBase[T], async: bool): bool {.nimcall, gcsafe.} case typ*: FTPJobType of JRetrText: lines: string of JRetr, JStore: - file: TFile + file: File filename: string - total: biggestInt # In bytes. - progress: biggestInt # In bytes. - oneSecond: biggestInt # Bytes transferred in one second. + total: BiggestInt # In bytes. + progress: BiggestInt # In bytes. + oneSecond: BiggestInt # Bytes transferred in one second. lastProgressReport: float # Time toStore: string # Data left to upload (Only used with async) else: nil - TFtpClient* = TFtpBase[TSocket] - PFtpClient* = ref TFTPClient + FtpClientObj* = FtpBaseObj[Socket] + FtpClient* = ref FtpClientObj - PAsyncFTPClient* = ref TAsyncFTPClient ## Async alternative to TFTPClient. - TAsyncFTPClient* = TFtpBase[asyncio.PAsyncSocket] + AsyncFtpClient* = ref AsyncFtpClientObj ## Async alternative to TFTPClient. + AsyncFtpClientObj* = FtpBaseObj[asyncio.AsyncSocket] FTPEventType* = enum EvTransferProgress, EvLines, EvRetr, EvStore - TFTPEvent* = object ## Event + FTPEvent* = object ## Event filename*: string case typ*: FTPEventType of EvLines: @@ -94,17 +93,23 @@ type of EvRetr, EvStore: ## Retr/Store operation finished. nil of EvTransferProgress: - bytesTotal*: biggestInt ## Bytes total. - bytesFinished*: biggestInt ## Bytes transferred. - speed*: biggestInt ## Speed in bytes/s + bytesTotal*: BiggestInt ## Bytes total. + bytesFinished*: BiggestInt ## Bytes transferred. + speed*: BiggestInt ## Speed in bytes/s currentJob*: FTPJobType ## The current job being performed. - EInvalidReply* = object of ESynch - EFTP* = object of ESynch + ReplyError* = object of IOError + FTPError* = object of IOError + +{.deprecated: [ + TFTPClient: FTPClientObj, TFTPJob: FTPJob, PAsyncFTPClient: AsyncFTPClient, + TAsyncFTPClient: AsyncFTPClientObj, TFTPEvent: FTPEvent, + EInvalidReply: ReplyError, EFTP: FTPError +].} proc ftpClient*(address: string, port = TPort(21), - user, pass = ""): PFTPClient = - ## Create a ``PFTPClient`` object. + user, pass = ""): FtpClient = + ## Create a ``FtpClient`` object. new(result) result.user = user result.pass = pass @@ -113,13 +118,7 @@ proc ftpClient*(address: string, port = TPort(21), result.dsockConnected = false result.csock = socket() - if result.csock == InvalidSocket: osError(osLastError()) - -proc getDSock[T](ftp: PFTPBase[T]): TSocket = - return ftp.dsock - -proc getCSock[T](ftp: PFTPBase[T]): TSocket = - return ftp.csock + if result.csock == invalidSocket: raiseOSError(osLastError()) template blockingOperation(sock: TSocket, body: stmt) {.immediate.} = body @@ -129,16 +128,19 @@ template blockingOperation(sock: asyncio.PAsyncSocket, body: stmt) {.immediate.} body sock.setBlocking(false) -proc expectReply[T](ftp: PFtpBase[T]): TaintedString = +proc expectReply[T](ftp: FtpBase[T]): TaintedString = result = TaintedString"" - blockingOperation(ftp.getCSock()): - ftp.getCSock().readLine(result) + blockingOperation(ftp.csock): + when T is Socket: + ftp.csock.readLine(result) + else: + discard ftp.csock.readLine(result) -proc send*[T](ftp: PFtpBase[T], m: string): TaintedString = +proc send*[T](ftp: FtpBase[T], m: string): TaintedString = ## Send a message to the server, and wait for a primary reply. ## ``\c\L`` is added for you. - blockingOperation(ftp.getCSock()): - ftp.getCSock().send(m & "\c\L") + blockingOperation(ftp.csock): + ftp.csock.send(m & "\c\L") return ftp.expectReply() proc assertReply(received: TaintedString, expected: string) = @@ -154,8 +156,8 @@ proc assertReply(received: TaintedString, expected: varargs[string]) = "Expected reply '$1' got: $2" % [expected.join("' or '"), received.string]) -proc createJob[T](ftp: PFtpBase[T], - prc: proc (ftp: PFtpBase[T], async: bool): bool {. +proc createJob[T](ftp: FtpBase[T], + prc: proc (ftp: FtpBase[T], async: bool): bool {. nimcall,gcsafe.}, cmd: FTPJobType) = if ftp.jobInProgress: @@ -170,7 +172,7 @@ proc createJob[T](ftp: PFtpBase[T], of JRetr, JStore: ftp.job.toStore = "" -proc deleteJob[T](ftp: PFtpBase[T]) = +proc deleteJob[T](ftp: FtpBase[T]) = assert ftp.jobInProgress ftp.jobInProgress = false case ftp.job.typ @@ -215,13 +217,13 @@ proc handleRead(s: PAsyncSocket, ftp: PAsyncFTPClient) = # 226 from csock. assert(not ftp.job.prc(ftp, true)) -proc pasv[T](ftp: PFtpBase[T]) = +proc pasv[T](ftp: FtpBase[T]) = ## Negotiate a data connection. when T is TSocket: ftp.dsock = socket() - if ftp.dsock == InvalidSocket: osError(osLastError()) + if ftp.dsock == invalidSocket: raiseOSError(osLastError()) elif T is PAsyncSocket: - ftp.dsock = AsyncSocket() + ftp.dsock = asyncSocket() ftp.dsock.handleRead = proc (s: PAsyncSocket) = handleRead(s, ftp) @@ -244,14 +246,14 @@ proc pasv[T](ftp: PFtpBase[T]) = var properPort = port[0].parseInt()*256+port[1].parseInt() ftp.dsock.connect(ip.join("."), TPort(properPort.toU16)) when T is PAsyncSocket: - ftp.dsockConnected = False + ftp.dsockConnected = false else: - ftp.dsockConnected = True + ftp.dsockConnected = true proc normalizePathSep(path: string): string = return replace(path, '\\', '/') -proc connect*[T](ftp: PFtpBase[T]) = +proc connect*[T](ftp: FtpBase[T]) = ## Connect to the FTP server specified by ``ftp``. when T is PAsyncSocket: blockingOperation(ftp.csock): @@ -270,21 +272,21 @@ proc connect*[T](ftp: PFtpBase[T]) = if ftp.pass != "": assertReply ftp.send("PASS " & ftp.pass), "230" -proc pwd*(ftp: PFTPClient): string = +proc pwd*[T](ftp: FtpBase[T]): string = ## Returns the current working directory. var wd = ftp.send("PWD") assertReply wd, "257" return wd.string.captureBetween('"') # " -proc cd*(ftp: PFTPClient, dir: string) = +proc cd*[T](ftp: FtpBase[T], dir: string) = ## Changes the current directory on the remote FTP server to ``dir``. assertReply ftp.send("CWD " & dir.normalizePathSep), "250" -proc cdup*(ftp: PFTPClient) = +proc cdup*[T](ftp: FtpBase[T]) = ## Changes the current directory to the parent of the current directory. assertReply ftp.send("CDUP"), "200" -proc getLines[T](ftp: PFtpBase[T], async: bool = false): bool = +proc getLines[T](ftp: FtpBase[T], async: bool = false): bool = ## Downloads text data in ASCII mode ## Returns true if the download is complete. ## It doesn't if `async` is true, because it doesn't check for 226 then. @@ -307,14 +309,14 @@ proc getLines[T](ftp: PFtpBase[T], async: bool = false): bool = {.fatal: "Incorrect socket instantiation".} if not async: - var readSocks: seq[TSocket] = @[ftp.getCSock()] + var readSocks: seq[TSocket] = @[ftp.csock] # This is only needed here. Asyncio gets this socket... - blockingOperation(ftp.getCSock()): - if readSocks.select(1) != 0 and ftp.getCSock() in readSocks: + blockingOperation(ftp.csock): + if readSocks.select(1) != 0 and ftp.csock in readSocks: assertReply ftp.expectReply(), "226" return true -proc listDirs*[T](ftp: PFtpBase[T], dir: string = "", +proc listDirs*[T](ftp: FtpBase[T], dir: string = "", async = false): seq[string] = ## Returns a list of filenames in the given directory. If ``dir`` is "", ## the current directory is used. If ``async`` is true, this @@ -332,7 +334,7 @@ proc listDirs*[T](ftp: PFtpBase[T], dir: string = "", ftp.deleteJob() else: return @[] -proc fileExists*(ftp: PFTPClient, file: string): bool {.deprecated.} = +proc fileExists*(ftp: FtpClient, file: string): bool {.deprecated.} = ## **Deprecated since version 0.9.0:** Please use ``existsFile``. ## ## Determines whether ``file`` exists. @@ -343,7 +345,7 @@ proc fileExists*(ftp: PFTPClient, file: string): bool {.deprecated.} = for f in items(files): if f.normalizePathSep == file.normalizePathSep: return true -proc existsFile*(ftp: PFTPClient, file: string): bool = +proc existsFile*(ftp: FtpClient, file: string): bool = ## Determines whether ``file`` exists. ## ## Warning: This function may block. Especially on directories with many @@ -352,7 +354,7 @@ proc existsFile*(ftp: PFTPClient, file: string): bool = for f in items(files): if f.normalizePathSep == file.normalizePathSep: return true -proc createDir*(ftp: PFTPClient, dir: string, recursive: bool = false) = +proc createDir*[T](ftp: FtpBase[T], dir: string, recursive: bool = false) = ## Creates a directory ``dir``. If ``recursive`` is true, the topmost ## subdirectory of ``dir`` will be created first, following the secondmost... ## etc. this allows you to give a full path as the ``dir`` without worrying @@ -362,14 +364,14 @@ proc createDir*(ftp: PFTPClient, dir: string, recursive: bool = false) = else: var reply = TaintedString"" var previousDirs = "" - for p in split(dir, {os.dirSep, os.altSep}): + for p in split(dir, {os.DirSep, os.AltSep}): if p != "": previousDirs.add(p) reply = ftp.send("MKD " & previousDirs) previousDirs.add('/') assertReply reply, "257" -proc chmod*(ftp: PFTPClient, path: string, +proc chmod*[T](ftp: FtpBase[T], path: string, permissions: set[TFilePermission]) = ## Changes permission of ``path`` to ``permissions``. var userOctal = 0 @@ -391,7 +393,7 @@ proc chmod*(ftp: PFTPClient, path: string, assertReply ftp.send("SITE CHMOD " & perm & " " & path.normalizePathSep), "200" -proc list*[T](ftp: PFtpBase[T], dir: string = "", async = false): string = +proc list*[T](ftp: FtpBase[T], dir: string = "", async = false): string = ## Lists all files in ``dir``. If ``dir`` is ``""``, uses the current ## working directory. If ``async`` is true, this function will return ## immediately and it will be your job to call asyncio's @@ -408,7 +410,7 @@ proc list*[T](ftp: PFtpBase[T], dir: string = "", async = false): string = else: return "" -proc retrText*[T](ftp: PFtpBase[T], file: string, async = false): string = +proc retrText*[T](ftp: FtpBase[T], file: string, async = false): string = ## Retrieves ``file``. File must be ASCII text. ## If ``async`` is true, this function will return immediately and ## it will be your job to call asyncio's ``poll`` to progress this operation. @@ -423,7 +425,7 @@ proc retrText*[T](ftp: PFtpBase[T], file: string, async = false): string = else: return "" -proc getFile[T](ftp: PFtpBase[T], async = false): bool = +proc getFile[T](ftp: FtpBase[T], async = false): bool = if ftp.dsockConnected: var r = "".TaintedString var bytesRead = 0 @@ -435,7 +437,7 @@ proc getFile[T](ftp: PFtpBase[T], async = false): bool = bytesRead = ftp.dsock.recvAsync(r, BufferSize) returned = bytesRead != -1 else: - bytesRead = getDSock(ftp).recv(r, BufferSize) + bytesRead = ftp.dsock.recv(r, BufferSize) returned = true let r2 = r.string if r2 != "": @@ -443,16 +445,17 @@ proc getFile[T](ftp: PFtpBase[T], async = false): bool = ftp.job.oneSecond.inc(r2.len) ftp.job.file.write(r2) elif returned and r2 == "": - ftp.dsockConnected = False - - if not async: - var readSocks: seq[TSocket] = @[ftp.getCSock()] - blockingOperation(ftp.getCSock()): - if readSocks.select(1) != 0 and ftp.getCSock() in readSocks: - assertReply ftp.expectReply(), "226" - return true + ftp.dsockConnected = false -proc retrFile*[T](ftp: PFtpBase[T], file, dest: string, async = false) = + when T is TSocket: + if not async: + var readSocks: seq[TSocket] = @[ftp.csock] + blockingOperation(ftp.csock): + if readSocks.select(1) != 0 and ftp.csock in readSocks: + assertReply ftp.expectReply(), "226" + return true + +proc retrFile*[T](ftp: FtpBase[T], file, dest: string, async = false) = ## Downloads ``file`` and saves it to ``dest``. Usage of this function ## asynchronously is recommended to view the progress of the download. ## The ``EvRetr`` event is passed to the specified ``handleEvent`` function @@ -465,7 +468,7 @@ proc retrFile*[T](ftp: PFtpBase[T], file, dest: string, async = false) = assertReply reply, ["125", "150"] if {'(', ')'} notin reply.string: raise newException(EInvalidReply, "Reply has no file size.") - var fileSize: biggestInt + var fileSize: BiggestInt if reply.string.captureBetween('(', ')').parseBiggestInt(fileSize) == 0: raise newException(EInvalidReply, "Reply has no file size.") @@ -477,7 +480,7 @@ proc retrFile*[T](ftp: PFtpBase[T], file, dest: string, async = false) = while not ftp.job.prc(ftp, false): discard ftp.deleteJob() -proc doUpload[T](ftp: PFtpBase[T], async = false): bool = +proc doUpload[T](ftp: FtpBase[T], async = false): bool = if ftp.dsockConnected: if ftp.job.toStore.len() > 0: assert(async) @@ -503,7 +506,7 @@ proc doUpload[T](ftp: PFtpBase[T], async = false): bool = return false if not async: - getDSock(ftp).send(s) + ftp.dsock.send(s) else: let bytesSent = ftp.dsock.sendAsync(s) if bytesSent == 0: @@ -515,7 +518,7 @@ proc doUpload[T](ftp: PFtpBase[T], async = false): bool = ftp.job.progress.inc(len) ftp.job.oneSecond.inc(len) -proc store*[T](ftp: PFtpBase[T], file, dest: string, async = false) = +proc store*[T](ftp: FtpBase[T], file, dest: string, async = false) = ## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this ## function asynchronously is recommended to view the progress of ## the download. @@ -535,7 +538,7 @@ proc store*[T](ftp: PFtpBase[T], file, dest: string, async = false) = while not ftp.job.prc(ftp, false): discard ftp.deleteJob() -proc close*[T](ftp: PFTPBase[T]) = +proc close*[T](ftp: FtpBase[T]) = ## Terminates the connection to the server. assertReply ftp.send("QUIT"), "221" if ftp.jobInProgress: ftp.deleteJob() @@ -571,7 +574,7 @@ proc asyncFTPClient*(address: string, port = TPort(21), ## Create a ``PAsyncFTPClient`` object. ## ## Use this if you want to use asyncio's dispatcher. - var dres: PAsyncFTPClient + var dres: AsyncFtpClient new(dres) dres.user = user dres.pass = pass @@ -579,9 +582,9 @@ proc asyncFTPClient*(address: string, port = TPort(21), dres.port = port dres.dsockConnected = false dres.handleEvent = handleEvent - dres.csock = AsyncSocket() + dres.csock = asyncSocket() dres.csock.handleRead = - proc (s: PAsyncSocket) = + proc (s: AsyncSocket) = csockHandleRead(s, dres) result = dres @@ -594,7 +597,7 @@ when isMainModule: proc main = var d = newDispatcher() let hev = - proc (ftp: PAsyncFTPClient, event: TFTPEvent) = + proc (ftp: AsyncFTPClient, event: FTPEvent) = case event.typ of EvStore: echo("Upload finished!") diff --git a/lib/pure/future.nim b/lib/pure/future.nim index 480ee2b0d..a0c1b5c2d 100644 --- a/lib/pure/future.nim +++ b/lib/pure/future.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Dominik Picheta # # See the file "copying.txt", included in this @@ -53,7 +53,7 @@ proc createProcType(p, b: PNimrodNode): PNimrodNode {.compileTime.} = macro `=>`*(p, b: expr): expr {.immediate.} = ## Syntax sugar for anonymous procedures. ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## proc passTwoAndTwo(f: (int, int) -> int): int = ## f(2, 2) @@ -77,7 +77,17 @@ macro `=>`*(p, b: expr): expr {.immediate.} = identDefs.add(c) identDefs.add(newEmptyNode()) identDefs.add(newEmptyNode()) + of nnkInfix: + if c[0].kind == nnkIdent and c[0].ident == !"->": + var procTy = createProcType(c[1], c[2]) + params[0] = procTy[0][0] + for i in 1 .. ) got (" & $c[0].ident & ").") + break else: + echo treeRepr c error("Incorrect procedure parameter list.") params.add(identDefs) of nnkIdent: @@ -104,7 +114,7 @@ macro `=>`*(p, b: expr): expr {.immediate.} = macro `->`*(p, b: expr): expr {.immediate.} = ## Syntax sugar for procedure types. ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## proc pass2(f: (float, float) -> float): float = ## f(2, 2) diff --git a/lib/pure/gentabs.nim b/lib/pure/gentabs.nim index 617473c14..a6128efc9 100644 --- a/lib/pure/gentabs.nim +++ b/lib/pure/gentabs.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -9,9 +9,11 @@ ## The ``gentabs`` module implements an efficient hash table that is a ## key-value mapping. The keys are required to be strings, but the values -## may be any Nimrod or user defined type. This module supports matching +## may be any Nim or user defined type. This module supports matching ## of keys in case-sensitive, case-insensitive and style-insensitive modes. +{.deprecated.} + import os, hashes, strutils @@ -23,7 +25,7 @@ type TGenKeyValuePair[T] = tuple[key: string, val: T] TGenKeyValuePairSeq[T] = seq[TGenKeyValuePair[T]] - TGenTable*[T] = object of TObject + TGenTable*[T] = object of RootObj counter: int data: TGenKeyValuePairSeq[T] mode: TGenTableMode @@ -72,16 +74,16 @@ proc newGenTable*[T](mode: TGenTableMode): PGenTable[T] = proc nextTry(h, maxHash: THash): THash {.inline.} = result = ((5 * h) + 1) and maxHash -proc RawGet[T](tbl: PGenTable[T], key: string): int = +proc rawGet[T](tbl: PGenTable[T], key: string): int = var h: THash 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): + if myCmp(tbl, tbl.data[h].key, key): return h 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 TGenKeyValuePairSeq[T], key: string, val: T) = var h: THash h = myhash(tbl, key) and high(data) @@ -90,12 +92,12 @@ proc RawInsert[T](tbl: PGenTable[T], data: var TGenKeyValuePairSeq[T], data[h].key = key data[h].val = val -proc Enlarge[T](tbl: PGenTable[T]) = +proc enlarge[T](tbl: PGenTable[T]) = var n: TGenKeyValuePairSeq[T] newSeq(n, len(tbl.data) * growthFactor) for i in countup(0, high(tbl.data)): if not isNil(tbl.data[i].key): - RawInsert[T](tbl, n, tbl.data[i].key, tbl.data[i].val) + rawInsert[T](tbl, n, tbl.data[i].key, tbl.data[i].val) swap(tbl.data, n) proc hasKey*[T](tbl: PGenTable[T], key: string): bool = @@ -106,17 +108,17 @@ proc `[]`*[T](tbl: PGenTable[T], key: string): T = ## retrieves the value at ``tbl[key]``. If `key` is not in `tbl`, ## default(T) is returned and no exception is raised. One can check ## with ``hasKey`` whether the key exists. - var index = RawGet(tbl, key) + var index = rawGet(tbl, key) if index >= 0: result = tbl.data[index].val proc `[]=`*[T](tbl: PGenTable[T], key: string, val: T) = ## puts a (key, value)-pair into `tbl`. - var index = RawGet(tbl, key) + var index = rawGet(tbl, key) if index >= 0: tbl.data[index].val = val else: - if mustRehash(len(tbl.data), tbl.counter): Enlarge(tbl) - RawInsert(tbl, tbl.data, key, val) + if mustRehash(len(tbl.data), tbl.counter): enlarge(tbl) + rawInsert(tbl, tbl.data, key, val) inc(tbl.counter) diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 740355e55..30daaf2dc 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -8,13 +8,13 @@ # ## This module implements efficient computations of hash values for diverse -## Nimrod types. All the procs are based on these two building blocks: the `!& +## Nim types. All the procs are based on these two building blocks: the `!& ## proc <#!&>`_ used to start or mix a hash value, and the `!$ proc <#!$>`_ ## used to *finish* the hash value. If you want to implement hash procs for ## your custom types you will end up writing the following kind of skeleton of ## code: ## -## .. code-block:: nimrod +## .. code-block:: Nim ## proc hash(x: Something): THash = ## ## Computes a THash from `x`. ## var h: THash = 0 @@ -29,7 +29,7 @@ ## like for example objects made up of ``strings``, you can simply hash ## together the hash value of the individual fields: ## -## .. code-block:: nimrod +## .. code-block:: Nim ## proc hash(x: Something): THash = ## ## Computes a THash from `x`. ## var h: THash = 0 diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim index b9d6aec7b..4852ed50d 100644 --- a/lib/pure/htmlgen.nim +++ b/lib/pure/htmlgen.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -18,13 +18,13 @@ ## ## Example: ## -## .. code-block:: nimrod -## var nim = "Nimrod" -## echo h1(a(href="http://nimrod-lang.org", nim)) +## .. code-block:: Nim +## var nim = "Nim" +## echo h1(a(href="http://nim-lang.org", nim)) ## ## Writes the string:: ## -##

      Nimrod

      +##

      Nim

      ## import @@ -53,8 +53,7 @@ proc delete[T](s: var seq[T], attr: T): bool = setLen(s, L-1) result = true -proc xmlCheckedTag*(e: PNimrodNode, tag: string, - optAttr = "", reqAttr = "", +proc xmlCheckedTag*(e: PNimrodNode, tag: string, optAttr = "", reqAttr = "", isLeaf = false): PNimrodNode {.compileTime.} = ## use this procedure to define a new XML tag @@ -265,7 +264,7 @@ macro html*(e: expr): expr {.immediate.} = let e = callsite() result = xmlCheckedTag(e, "html", "xmlns", "") -macro hr*(e: expr): expr {.immediate.} = +macro hr*(): expr {.immediate.} = ## generates the HTML ``hr`` element. let e = callsite() result = xmlCheckedTag(e, "hr", commonAttr, "", true) @@ -484,7 +483,7 @@ macro `var`*(e: expr): expr {.immediate.} = result = xmlCheckedTag(e, "var", commonAttr) when isMainModule: - var nim = "Nimrod" - echo h1(a(href="http://nimrod-code.org", nim)) + var nim = "Nim" + echo h1(a(href="http://nim-lang.org", nim)) echo form(action="test", `accept-charset` = "Content-Type") diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim index c38eb7063..e2cbb4949 100644 --- a/lib/pure/htmlparser.nim +++ b/lib/pure/htmlparser.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -13,7 +13,7 @@ ## It can be used to parse a wild HTML document and output it as valid XHTML ## document (well, if you are lucky): ## -## .. code-block:: nimrod +## .. code-block:: Nim ## ## echo loadHtml("mydirty.html") ## @@ -29,7 +29,7 @@ ## and write back the modified version. In this case we look for hyperlinks ## ending with the extension ``.rst`` and convert them to ``.html``. ## -## .. code-block:: nimrod +## .. code-block:: Nim ## ## import htmlparser ## import xmltree # To use '$' for PXmlNode @@ -422,7 +422,7 @@ proc toHtmlTag(s: string): THtmlTag = of "wbr": tagWbr else: tagUnknown -proc htmlTag*(n: PXmlNode): THtmlTag = +proc htmlTag*(n: XmlNode): THtmlTag = ## gets `n`'s tag as a ``THtmlTag``. if n.clientData == 0: n.clientData = toHtmlTag(n.tag).ord @@ -438,24 +438,24 @@ proc entityToUtf8*(entity: string): string = ## converts an HTML entity name like ``Ü`` to its UTF-8 equivalent. ## "" is returned if the entity name is unknown. The HTML parser ## already converts entities to UTF-8. - for name, val in items(entities): - if name == entity: return toUTF8(TRune(val)) + for name, val in items(Entities): + if name == entity: return toUTF8(Rune(val)) result = "" -proc addNode(father, son: PXmlNode) = +proc addNode(father, son: XmlNode) = if son != nil: add(father, son) -proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode +proc parse(x: var XmlParser, errors: var seq[string]): XmlNode -proc expected(x: var TXmlParser, n: PXmlNode): string = +proc expected(x: var XmlParser, n: XmlNode): string = result = errorMsg(x, " expected") template elemName(x: expr): expr = rawData(x) -proc untilElementEnd(x: var TXmlParser, result: PXmlNode, +proc untilElementEnd(x: var XmlParser, result: XmlNode, errors: var seq[string]) = # we parsed e.g. ``
      `` and don't really expect a ``
      ``: - if result.htmlTag in singleTags: + if result.htmlTag in SingleTags: if x.kind != xmlElementEnd or cmpIgnoreCase(x.elemName, result.tag) != 0: return while true: @@ -496,7 +496,7 @@ proc untilElementEnd(x: var TXmlParser, result: PXmlNode, else: result.addNode(parse(x, errors)) -proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode = +proc parse(x: var XmlParser, errors: var seq[string]): XmlNode = case x.kind of xmlComment: result = newComment(x.rawData) @@ -549,11 +549,11 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode = next(x) of xmlEof: discard -proc parseHtml*(s: PStream, filename: string, - errors: var seq[string]): PXmlNode = +proc parseHtml*(s: Stream, filename: string, + errors: var seq[string]): XmlNode = ## parses the XML from stream `s` and returns a ``PXmlNode``. Every ## occured parsing error is added to the `errors` sequence. - var x: TXmlParser + var x: XmlParser open(x, s, filename, {reportComments, reportWhitespace}) next(x) # skip the DOCTYPE: @@ -573,21 +573,21 @@ proc parseHtml*(s: PStream, filename: string, if result.len == 1: result = result[0] -proc parseHtml*(s: PStream): PXmlNode = +proc parseHtml*(s: Stream): XmlNode = ## parses the XTML from stream `s` and returns a ``PXmlNode``. All parsing ## errors are ignored. var errors: seq[string] = @[] result = parseHtml(s, "unknown_html_doc", errors) -proc loadHtml*(path: string, errors: var seq[string]): PXmlNode = +proc loadHtml*(path: string, errors: var seq[string]): XmlNode = ## Loads and parses HTML from file specified by ``path``, and returns ## a ``PXmlNode``. Every occured parsing error is added to ## the `errors` sequence. var s = newFileStream(path, fmRead) - if s == nil: raise newException(EIO, "Unable to read file: " & path) + if s == nil: raise newException(IOError, "Unable to read file: " & path) result = parseHtml(s, path, errors) -proc loadHtml*(path: string): PXmlNode = +proc loadHtml*(path: string): XmlNode = ## Loads and parses HTML from file specified by ``path``, and returns ## a ``PXmlNode``. All parsing errors are ignored. var errors: seq[string] = @[] @@ -600,7 +600,7 @@ when isMainModule: var x = loadHtml(paramStr(1), errors) for e in items(errors): echo e - var f: TFile + var f: File if open(f, "test.txt", fmWrite): f.write($x) f.close() diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 4db6ac6ed..3afb625ee 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2010 Dominik Picheta, Andreas Rumpf # # See the file "copying.txt", included in this @@ -17,27 +17,27 @@ ## ## Retrieving a website ## ==================== -## +## ## This example uses HTTP GET to retrieve ## ``http://google.com`` -## -## .. code-block:: nimrod +## +## .. code-block:: Nim ## echo(getContent("http://google.com")) -## +## ## Using HTTP POST ## =============== -## -## This example demonstrates the usage of the W3 HTML Validator, it +## +## This example demonstrates the usage of the W3 HTML Validator, it ## uses ``multipart/form-data`` as the ``Content-Type`` to send the HTML to -## the server. -## -## .. code-block:: nimrod +## the server. +## +## .. code-block:: Nim ## var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L" ## var body: string = "--xyz\c\L" ## # soap 1.2 output ## body.add("Content-Disposition: form-data; name=\"output\"\c\L") ## body.add("\c\Lsoap12\c\L") -## +## ## # html ## body.add("--xyz\c\L") ## body.add("Content-Disposition: form-data; name=\"uploaded_file\";" & @@ -45,16 +45,29 @@ ## body.add("Content-Type: text/html\c\L") ## body.add("\c\L

      test

      \c\L") ## body.add("--xyz--") -## +## ## echo(postContent("http://validator.w3.org/check", headers, body)) ## +## Asynchronous HTTP requests +## ========================== +## +## You simply have to create a new instance of the ``AsyncHttpClient`` object. +## You may then use ``await`` on the functions defined for that object. +## Keep in mind that the following code needs to be inside an asynchronous +## procedure. +## +## .. code-block::nim +## +## var client = newAsyncHttpClient() +## var resp = await client.request("http://google.com") +## ## SSL/TLS support ## =============== ## This requires the OpenSSL library, fortunately it's widely used and installed ## on many operating systems. httpclient will use SSL automatically if you give ## any of the functions a url with the ``https`` schema, for example: ## ``https://github.com/``, you also have to compile with ``ssl`` defined like so: -## ``nimrod c -d:ssl ...``. +## ``nim c -d:ssl ...``. ## ## Timeouts ## ======== @@ -75,44 +88,48 @@ ## constructor should be used for this purpose. However, ## currently only basic authentication is supported. -import sockets, strutils, parseurl, parseutils, strtabs, base64, os +import net, strutils, uri, parseutils, strtabs, base64, os import asyncnet, asyncdispatch import rawsockets type - TResponse* = tuple[ - version: string, - status: string, - headers: PStringTable, + Response* = tuple[ + version: string, + status: string, + headers: StringTableRef, body: string] - PProxy* = ref object - url*: TUrl + Proxy* = ref object + url*: Uri auth*: string - EInvalidProtocol* = object of ESynch ## exception that is raised when server + ProtocolError* = object of IOError ## exception that is raised when server ## does not conform to the implemented ## protocol - EHttpRequestErr* = object of ESynch ## Thrown in the ``getContent`` proc - ## and ``postContent`` proc, - ## when the server returns an error + HttpRequestError* = object of IOError ## Thrown in the ``getContent`` proc + ## and ``postContent`` proc, + ## when the server returns an error -const defUserAgent* = "Nimrod httpclient/0.1" +{.deprecated: [TResponse: Response, PProxy: Proxy, + EInvalidProtocol: ProtocolError, EHttpRequestErr: HttpRequestError +].} + +const defUserAgent* = "Nim httpclient/0.1" proc httpError(msg: string) = - var e: ref EInvalidProtocol - new(e) - e.msg = msg - raise e - -proc fileError(msg: string) = - var e: ref EIO + var e: ref ProtocolError new(e) e.msg = msg raise e -proc parseChunks(s: TSocket, timeout: int): string = +proc fileError(msg: string) = + var e: ref IOError + new(e) + e.msg = msg + raise e + +proc parseChunks(s: Socket, timeout: int): string = result = "" var ri = 0 while true: @@ -139,7 +156,9 @@ proc parseChunks(s: TSocket, timeout: int): string = else: httpError("Invalid chunk size: " & chunkSizeStr) inc(i) - if chunkSize <= 0: break + if chunkSize <= 0: + s.skip(2, timeout) # Skip \c\L + break result.setLen(ri+chunkSize) var bytesRead = 0 while bytesRead != chunkSize: @@ -149,8 +168,8 @@ proc parseChunks(s: TSocket, timeout: int): string = s.skip(2, timeout) # Skip \c\L # Trailer headers will only be sent if the request specifies that we want # them: http://tools.ietf.org/html/rfc2616#section-3.6.1 - -proc parseBody(s: TSocket, headers: PStringTable, timeout: int): string = + +proc parseBody(s: Socket, headers: StringTableRef, timeout: int): string = result = "" if headers["Transfer-Encoding"] == "chunked": result = parseChunks(s, timeout) @@ -160,37 +179,38 @@ proc parseBody(s: TSocket, headers: PStringTable, timeout: int): string = var contentLengthHeader = headers["Content-Length"] if contentLengthHeader != "": var length = contentLengthHeader.parseint() - result = newString(length) - var received = 0 - while true: - if received >= length: break - let r = s.recv(addr(result[received]), length-received, timeout) - if r == 0: break - received += r - if received != length: - httpError("Got invalid content length. Expected: " & $length & - " got: " & $received) + if length > 0: + result = newString(length) + var received = 0 + while true: + if received >= length: break + let r = s.recv(addr(result[received]), length-received, timeout) + if r == 0: break + received += r + if received != length: + httpError("Got invalid content length. Expected: " & $length & + " got: " & $received) else: # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.4 TODO - + # -REGION- Connection: Close # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.5 if headers["Connection"] == "close": var buf = "" - while True: + while true: buf = newString(4000) let r = s.recv(addr(buf[0]), 4000, timeout) if r == 0: break buf.setLen(r) result.add(buf) -proc parseResponse(s: TSocket, getBody: bool, timeout: int): TResponse = +proc parseResponse(s: Socket, getBody: bool, timeout: int): Response = var parsedStatus = false var linei = 0 var fullyRead = false var line = "" result.headers = newStringTable(modeCaseInsensitive) - while True: + while true: line = "" linei = 0 s.readLine(line, timeout) @@ -222,7 +242,7 @@ proc parseResponse(s: TSocket, getBody: bool, timeout: int): TResponse = inc(linei, le) if line[linei] != ':': httpError("invalid headers") inc(linei) # Skip : - + result.headers[name] = line[linei.. -1].strip() if not fullyRead: httpError("Connection was closed before full request has been made") @@ -232,44 +252,46 @@ proc parseResponse(s: TSocket, getBody: bool, timeout: int): TResponse = result.body = "" type - THttpMethod* = enum ## the requested HttpMethod + HttpMethod* = enum ## the requested HttpMethod httpHEAD, ## Asks for the response identical to the one that would ## correspond to a GET request, but without the response ## body. httpGET, ## Retrieves the specified resource. - httpPOST, ## Submits data to be processed to the identified - ## resource. The data is included in the body of the + httpPOST, ## Submits data to be processed to the identified + ## resource. The data is included in the body of the ## request. httpPUT, ## Uploads a representation of the specified resource. httpDELETE, ## Deletes the specified resource. - httpTRACE, ## Echoes back the received request, so that a client + httpTRACE, ## Echoes back the received request, so that a client ## can see what intermediate servers are adding or ## changing in the request. - httpOPTIONS, ## Returns the HTTP methods that the server supports + httpOPTIONS, ## Returns the HTTP methods that the server supports ## for specified address. - httpCONNECT ## Converts the request connection to a transparent + httpCONNECT ## Converts the request connection to a transparent ## TCP/IP tunnel, usually used for proxies. +{.deprecated: [THttpMethod: HttpMethod].} + when not defined(ssl): - type PSSLContext = ref object - let defaultSSLContext: PSSLContext = nil + type SSLContext = ref object + let defaultSSLContext: SSLContext = nil else: let defaultSSLContext = newContext(verifyMode = CVerifyNone) -proc newProxy*(url: string, auth = ""): PProxy = +proc newProxy*(url: string, auth = ""): Proxy = ## Constructs a new ``TProxy`` object. - result = PProxy(url: parseUrl(url), auth: auth) + result = Proxy(url: parseUri(url), auth: auth) -proc request*(url: string, httpMethod = httpGET, extraHeaders = "", +proc request*(url: string, httpMethod = httpGET, extraHeaders = "", body = "", - sslContext: PSSLContext = defaultSSLContext, + sslContext: SSLContext = defaultSSLContext, timeout = -1, userAgent = defUserAgent, - proxy: PProxy = nil): TResponse = + proxy: Proxy = nil): Response = ## | Requests ``url`` with the specified ``httpMethod``. ## | Extra headers can be specified and must be seperated by ``\c\L`` ## | An optional timeout can be specified in miliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. - var r = if proxy == nil: parseUrl(url) else: proxy.url + var r = if proxy == nil: parseUri(url) else: proxy.url var headers = substr($httpMethod, len("http")) if proxy == nil: headers.add(" /" & r.path & r.query) @@ -277,7 +299,7 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "", headers.add(" " & url) headers.add(" HTTP/1.1\c\L") - + add(headers, "Host: " & r.hostname & "\c\L") if userAgent != "": add(headers, "User-Agent: " & userAgent & "\c\L") @@ -286,20 +308,20 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "", add(headers, "Proxy-Authorization: basic " & auth & "\c\L") add(headers, extraHeaders) add(headers, "\c\L") - - var s = socket() - if s == InvalidSocket: osError(osLastError()) - var port = sockets.TPort(80) + + var s = newSocket() + if s == nil: raiseOSError(osLastError()) + var port = net.Port(80) if r.scheme == "https": when defined(ssl): sslContext.wrapSocket(s) - port = sockets.TPort(443) + port = net.Port(443) else: - raise newException(EHttpRequestErr, + raise newException(HttpRequestError, "SSL support is not available. Cannot connect over SSL.") if r.port != "": - port = sockets.TPort(r.port.parseInt) - + port = net.Port(r.port.parseInt) + if timeout == -1: s.connect(r.hostname, port) else: @@ -307,30 +329,30 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "", s.send(headers) if body != "": s.send(body) - + result = parseResponse(s, httpMethod != httpHEAD, timeout) s.close() - + proc redirection(status: string): bool = const redirectionNRs = ["301", "302", "303", "307"] for i in items(redirectionNRs): if status.startsWith(i): - return True + return true -proc getNewLocation(lastUrl: string, headers: PStringTable): string = +proc getNewLocation(lastUrl: string, headers: StringTableRef): string = result = headers["Location"] if result == "": httpError("location header expected") # Relative URLs. (Not part of the spec, but soon will be.) - let r = parseURL(result) + let r = parseUri(result) if r.hostname == "" and r.path != "": - let origParsed = parseURL(lastUrl) + let origParsed = parseUri(lastUrl) result = origParsed.hostname & "/" & r.path - + proc get*(url: string, extraHeaders = "", maxRedirects = 5, - sslContext: PSSLContext = defaultSSLContext, + sslContext: SSLContext = defaultSSLContext, timeout = -1, userAgent = defUserAgent, - proxy: PProxy = nil): TResponse = - ## | GETs the ``url`` and returns a ``TResponse`` object + proxy: Proxy = nil): Response = + ## | GETs the ``url`` and returns a ``Response`` object ## | This proc also handles redirection ## | Extra headers can be specified and must be separated by ``\c\L``. ## | An optional timeout can be specified in miliseconds, if reading from the @@ -344,11 +366,11 @@ proc get*(url: string, extraHeaders = "", maxRedirects = 5, result = request(redirectTo, httpGET, extraHeaders, "", sslContext, timeout, userAgent, proxy) lastUrl = redirectTo - + proc getContent*(url: string, extraHeaders = "", maxRedirects = 5, - sslContext: PSSLContext = defaultSSLContext, + sslContext: SSLContext = defaultSSLContext, timeout = -1, userAgent = defUserAgent, - proxy: PProxy = nil): string = + proxy: Proxy = nil): string = ## | GETs the body and returns it as a string. ## | Raises exceptions for the status codes ``4xx`` and ``5xx`` ## | Extra headers can be specified and must be separated by ``\c\L``. @@ -357,16 +379,16 @@ proc getContent*(url: string, extraHeaders = "", maxRedirects = 5, var r = get(url, extraHeaders, maxRedirects, sslContext, timeout, userAgent, proxy) if r.status[0] in {'4','5'}: - raise newException(EHTTPRequestErr, r.status) + raise newException(HttpRequestError, r.status) else: return r.body - + proc post*(url: string, extraHeaders = "", body = "", maxRedirects = 5, - sslContext: PSSLContext = defaultSSLContext, + sslContext: SSLContext = defaultSSLContext, timeout = -1, userAgent = defUserAgent, - proxy: PProxy = nil): TResponse = - ## | POSTs ``body`` to the ``url`` and returns a ``TResponse`` object. + proxy: Proxy = nil): Response = + ## | POSTs ``body`` to the ``url`` and returns a ``Response`` object. ## | This proc adds the necessary Content-Length header. ## | This proc also handles redirection. ## | Extra headers can be specified and must be separated by ``\c\L``. @@ -383,12 +405,12 @@ proc post*(url: string, extraHeaders = "", body = "", result = request(redirectTo, meth, xh, body, sslContext, timeout, userAgent, proxy) lastUrl = redirectTo - + proc postContent*(url: string, extraHeaders = "", body = "", maxRedirects = 5, - sslContext: PSSLContext = defaultSSLContext, + sslContext: SSLContext = defaultSSLContext, timeout = -1, userAgent = defUserAgent, - proxy: PProxy = nil): string = + proxy: Proxy = nil): string = ## | POSTs ``body`` to ``url`` and returns the response's body as a string ## | Raises exceptions for the status codes ``4xx`` and ``5xx`` ## | Extra headers can be specified and must be separated by ``\c\L``. @@ -397,18 +419,18 @@ proc postContent*(url: string, extraHeaders = "", body = "", var r = post(url, extraHeaders, body, maxRedirects, sslContext, timeout, userAgent, proxy) if r.status[0] in {'4','5'}: - raise newException(EHTTPRequestErr, r.status) + raise newException(HttpRequestError, r.status) else: return r.body - + proc downloadFile*(url: string, outputFilename: string, - sslContext: PSSLContext = defaultSSLContext, + sslContext: SSLContext = defaultSSLContext, timeout = -1, userAgent = defUserAgent, - proxy: PProxy = nil) = + proxy: Proxy = nil) = ## | Downloads ``url`` and saves it to ``outputFilename`` ## | An optional timeout can be specified in miliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. - var f: TFile + var f: File if open(f, outputFilename, fmWrite): f.write(getContent(url, sslContext = sslContext, timeout = timeout, userAgent = userAgent, proxy = proxy)) @@ -416,8 +438,8 @@ proc downloadFile*(url: string, outputFilename: string, else: fileError("Unable to open file") -proc generateHeaders(r: TURL, httpMethod: THttpMethod, - headers: PStringTable): string = +proc generateHeaders(r: Uri, httpMethod: HttpMethod, + headers: StringTableRef): string = result = substr($httpMethod, len("http")) # TODO: Proxies result.add(" /" & r.path & r.query) @@ -431,35 +453,43 @@ proc generateHeaders(r: TURL, httpMethod: THttpMethod, add(result, "\c\L") type - PAsyncHttpClient* = ref object - socket: PAsyncSocket + AsyncHttpClient* = ref object + socket: AsyncSocket connected: bool - currentURL: TURL ## Where we are currently connected. - headers: PStringTable + currentURL: Uri ## Where we are currently connected. + headers*: StringTableRef maxRedirects: int userAgent: string + when defined(ssl): + sslContext: net.SslContext + +{.deprecated: [PAsyncHttpClient: AsyncHttpClient].} proc newAsyncHttpClient*(userAgent = defUserAgent, - maxRedirects = 5): PAsyncHttpClient = - ## Creates a new PAsyncHttpClient instance. + maxRedirects = 5, sslContext = defaultSslContext): AsyncHttpClient = + ## Creates a new AsyncHttpClient instance. ## ## ``userAgent`` specifies the user agent that will be used when making ## requests. ## ## ``maxRedirects`` specifies the maximum amount of redirects to follow, ## default is 5. + ## + ## ``sslContext`` specifies the SSL context to use for HTTPS requests. new result result.headers = newStringTable(modeCaseInsensitive) result.userAgent = defUserAgent result.maxRedirects = maxRedirects + when defined(ssl): + result.sslContext = net.SslContext(sslContext) -proc close*(client: PAsyncHttpClient) = +proc close*(client: AsyncHttpClient) = ## Closes any connections held by the HTTP client. if client.connected: client.socket.close() client.connected = false -proc recvFull(socket: PAsyncSocket, size: int): PFuture[string] {.async.} = +proc recvFull(socket: AsyncSocket, size: int): Future[string] {.async.} = ## Ensures that all the data requested is read and returned. result = "" while true: @@ -468,9 +498,8 @@ proc recvFull(socket: PAsyncSocket, size: int): PFuture[string] {.async.} = if data == "": break # We've been disconnected. result.add data -proc parseChunks(client: PAsyncHttpClient): PFuture[string] {.async.} = +proc parseChunks(client: AsyncHttpClient): Future[string] {.async.} = result = "" - var ri = 0 while true: var chunkSize = 0 var chunkSizeStr = await client.socket.recvLine() @@ -494,14 +523,16 @@ proc parseChunks(client: PAsyncHttpClient): PFuture[string] {.async.} = else: httpError("Invalid chunk size: " & chunkSizeStr) inc(i) - if chunkSize <= 0: break + if chunkSize <= 0: + discard await recvFull(client.socket, 2) # Skip \c\L + break result.add await recvFull(client.socket, chunkSize) discard await recvFull(client.socket, 2) # Skip \c\L # Trailer headers will only be sent if the request specifies that we want # them: http://tools.ietf.org/html/rfc2616#section-3.6.1 - -proc parseBody(client: PAsyncHttpClient, - headers: PStringTable): PFuture[string] {.async.} = + +proc parseBody(client: AsyncHttpClient, + headers: StringTableRef): Future[string] {.async.} = result = "" if headers["Transfer-Encoding"] == "chunked": result = await parseChunks(client) @@ -511,32 +542,33 @@ proc parseBody(client: PAsyncHttpClient, var contentLengthHeader = headers["Content-Length"] if contentLengthHeader != "": var length = contentLengthHeader.parseint() - result = await client.socket.recvFull(length) - if result == "": - httpError("Got disconnected while trying to read body.") - if result.len != length: - httpError("Received length doesn't match expected length. Wanted " & - $length & " got " & $result.len) + if length > 0: + result = await client.socket.recvFull(length) + if result == "": + httpError("Got disconnected while trying to read body.") + if result.len != length: + httpError("Received length doesn't match expected length. Wanted " & + $length & " got " & $result.len) else: # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.4 TODO - + # -REGION- Connection: Close # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.5 if headers["Connection"] == "close": var buf = "" - while True: + while true: buf = await client.socket.recvFull(4000) if buf == "": break result.add(buf) -proc parseResponse(client: PAsyncHttpClient, - getBody: bool): PFuture[TResponse] {.async.} = +proc parseResponse(client: AsyncHttpClient, + getBody: bool): Future[Response] {.async.} = var parsedStatus = false var linei = 0 var fullyRead = false var line = "" result.headers = newStringTable(modeCaseInsensitive) - while True: + while true: linei = 0 line = await client.socket.recvLine() if line == "": break # We've been disconnected. @@ -568,7 +600,7 @@ proc parseResponse(client: PAsyncHttpClient, inc(linei, le) if line[linei] != ':': httpError("invalid headers") inc(linei) # Skip : - + result.headers[name] = line[linei.. -1].strip() if not fullyRead: httpError("Connection was closed before full request has been made") @@ -577,25 +609,34 @@ proc parseResponse(client: PAsyncHttpClient, else: result.body = "" -proc newConnection(client: PAsyncHttpClient, url: TURL) {.async.} = +proc newConnection(client: AsyncHttpClient, url: Uri) {.async.} = if client.currentURL.hostname != url.hostname or client.currentURL.scheme != url.scheme: if client.connected: client.close() client.socket = newAsyncSocket() - if url.scheme == "https": - assert false, "TODO SSL" - # TODO: I should be able to write 'net.TPort' here... + # TODO: I should be able to write 'net.Port' here... let port = - if url.port == "": rawsockets.TPort(80) - else: rawsockets.TPort(url.port.parseInt) - + if url.port == "": + if url.scheme.toLower() == "https": + rawsockets.Port(443) + else: + rawsockets.Port(80) + else: rawsockets.Port(url.port.parseInt) + + if url.scheme.toLower() == "https": + when defined(ssl): + client.sslContext.wrapSocket(client.socket) + else: + raise newException(HttpRequestError, + "SSL support is not available. Cannot connect over SSL.") + await client.socket.connect(url.hostname, port) client.currentURL = url client.connected = true -proc request*(client: PAsyncHttpClient, url: string, httpMethod = httpGET, - body = ""): PFuture[TResponse] {.async.} = +proc request*(client: AsyncHttpClient, url: string, httpMethod = httpGET, + body = ""): Future[Response] {.async.} = ## Connects to the hostname specified by the URL and performs a request ## using the method specified. ## @@ -604,21 +645,21 @@ proc request*(client: PAsyncHttpClient, url: string, httpMethod = httpGET, ## connection can be closed by using the ``close`` procedure. ## ## The returned future will complete once the request is completed. - let r = parseUrl(url) + let r = parseUri(url) await newConnection(client, r) if not client.headers.hasKey("user-agent") and client.userAgent != "": client.headers["User-Agent"] = client.userAgent - + var headers = generateHeaders(r, httpMethod, client.headers) - + await client.socket.send(headers) if body != "": await client.socket.send(body) - + result = await parseResponse(client, httpMethod != httpHEAD) -proc get*(client: PAsyncHttpClient, url: string): PFuture[TResponse] {.async.} = +proc get*(client: AsyncHttpClient, url: string): Future[Response] {.async.} = ## Connects to the hostname specified by the URL and performs a GET request. ## ## This procedure will follow redirects up to a maximum number of redirects @@ -637,7 +678,7 @@ when isMainModule: proc main() {.async.} = var client = newAsyncHttpClient() var resp = await client.request("http://picheta.me") - + echo("Got response: ", resp.status) echo("Body:\n") echo(resp.body) @@ -648,29 +689,29 @@ when isMainModule: resp = await client.request("http://picheta.me/aboutme.html") echo("Got response: ", resp.status) - resp = await client.request("http://nimrod-lang.org/") + resp = await client.request("http://nim-lang.org/") echo("Got response: ", resp.status) - resp = await client.request("http://nimrod-lang.org/download.html") + resp = await client.request("http://nim-lang.org/download.html") echo("Got response: ", resp.status) waitFor main() else: - #downloadFile("http://force7.de/nimrod/index.html", "nimrodindex.html") + #downloadFile("http://force7.de/nim/index.html", "nimindex.html") #downloadFile("http://www.httpwatch.com/", "ChunkTest.html") #downloadFile("http://validator.w3.org/check?uri=http%3A%2F%2Fgoogle.com", # "validator.html") #var r = get("http://validator.w3.org/check?uri=http%3A%2F%2Fgoogle.com& # charset=%28detect+automatically%29&doctype=Inline&group=0") - + var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L" var body: string = "--xyz\c\L" # soap 1.2 output body.add("Content-Disposition: form-data; name=\"output\"\c\L") body.add("\c\Lsoap12\c\L") - + # html body.add("--xyz\c\L") body.add("Content-Disposition: form-data; name=\"uploaded_file\";" & diff --git a/lib/pure/httpserver.nim b/lib/pure/httpserver.nim index 885742b64..38a068ea1 100644 --- a/lib/pure/httpserver.nim +++ b/lib/pure/httpserver.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf, Dominik Picheta # # See the file "copying.txt", included in this @@ -11,19 +11,17 @@ ## ## Example: ## -## .. code-block:: nimrod +## .. code-block:: nim ## import strutils, sockets, httpserver ## ## var counter = 0 -## proc handleRequest(client: TSocket, path, query: string): bool {.procvar.} = +## proc handleRequest(client: Socket, path, query: string): bool {.procvar.} = ## inc(counter) ## client.send("Hello for the $#th time." % $counter & wwwNL) ## return false # do not stop processing ## -## run(handleRequest, TPort(80)) +## run(handleRequest, Port(80)) ## -## **Warning:** The API of this module is unstable, and therefore is subject -## to change. import parseutils, strutils, os, osproc, strtabs, streams, sockets, asyncio @@ -33,14 +31,14 @@ const # --------------- output messages -------------------------------------------- -proc sendTextContentType(client: TSocket) = +proc sendTextContentType(client: Socket) = send(client, "Content-type: text/html" & wwwNL) send(client, wwwNL) -proc sendStatus(client: TSocket, status: string) = +proc sendStatus(client: Socket, status: string) = send(client, "HTTP/1.1 " & status & wwwNL) -proc badRequest(client: TSocket) = +proc badRequest(client: Socket) = # Inform the client that a request it has made has a problem. send(client, "HTTP/1.1 400 Bad Request" & wwwNL) sendTextContentType(client) @@ -48,18 +46,18 @@ proc badRequest(client: TSocket) = "such as a POST without a Content-Length.

      " & wwwNL) when false: - proc cannotExec(client: TSocket) = + proc cannotExec(client: Socket) = send(client, "HTTP/1.1 500 Internal Server Error" & wwwNL) sendTextContentType(client) send(client, "

      Error prohibited CGI execution." & wwwNL) -proc headers(client: TSocket, filename: string) = +proc headers(client: Socket, filename: string) = # XXX could use filename to determine file type send(client, "HTTP/1.1 200 OK" & wwwNL) send(client, ServerSig) sendTextContentType(client) -proc notFound(client: TSocket) = +proc notFound(client: Socket) = send(client, "HTTP/1.1 404 NOT FOUND" & wwwNL) send(client, ServerSig) sendTextContentType(client) @@ -69,7 +67,7 @@ proc notFound(client: TSocket) = send(client, "is unavailable or nonexistent.

      " & wwwNL) send(client, "" & wwwNL) -proc unimplemented(client: TSocket) = +proc unimplemented(client: Socket) = send(client, "HTTP/1.1 501 Method Not Implemented" & wwwNL) send(client, ServerSig) sendTextContentType(client) @@ -81,23 +79,23 @@ proc unimplemented(client: TSocket) = # ----------------- file serving --------------------------------------------- when false: - proc discardHeaders(client: TSocket) = skip(client) + proc discardHeaders(client: Socket) = skip(client) -proc serveFile*(client: TSocket, filename: string) = +proc serveFile*(client: Socket, filename: string) = ## serves a file to the client. - var f: TFile + var f: File if open(f, filename): headers(client, filename) const bufSize = 8000 # != 8K might be good for memory manager var buf = alloc(bufsize) - while True: + while true: var bytesread = readBuffer(f, buf, bufsize) if bytesread > 0: var byteswritten = send(client, buf, bytesread) if bytesread != bytesWritten: dealloc(buf) close(f) - OSError(OSLastError()) + raiseOSError(osLastError()) if bytesread != bufSize: break dealloc(buf) close(f) @@ -110,7 +108,7 @@ when false: type TRequestMethod = enum reqGet, reqPost - proc executeCgi(client: TSocket, path, query: string, meth: TRequestMethod) = + proc executeCgi(client: Socket, path, query: string, meth: TRequestMethod) = var env = newStringTable(modeCaseInsensitive) var contentLength = -1 case meth @@ -145,7 +143,7 @@ when false: var buf = alloc(contentLength) if recv(client, buf, contentLength) != contentLength: dealloc(buf) - OSError() + raiseOSError() var inp = process.inputStream inp.writeData(buf, contentLength) dealloc(buf) @@ -160,7 +158,7 @@ when false: # --------------- Server Setup ----------------------------------------------- - proc acceptRequest(client: TSocket) = + proc acceptRequest(client: Socket) = var cgi = false var query = "" var buf = TaintedString"" @@ -210,48 +208,48 @@ when false: executeCgi(client, path, query, meth) type - TServer* = object of TObject ## contains the current server state - socket: TSocket - port: TPort - client*: TSocket ## the socket to write the file data to - reqMethod*: string ## Request method. GET or POST. - path*, query*: string ## path and query the client requested - headers*: PStringTable ## headers with which the client made the request - body*: string ## only set with POST requests - ip*: string ## ip address of the requesting client + TServer* = object of RootObj ## contains the current server state + socket: Socket + port: Port + client*: Socket ## the socket to write the file data to + reqMethod*: string ## Request method. GET or POST. + path*, query*: string ## path and query the client requested + headers*: StringTableRef ## headers with which the client made the request + body*: string ## only set with POST requests + ip*: string ## ip address of the requesting client PAsyncHTTPServer* = ref TAsyncHTTPServer TAsyncHTTPServer = object of TServer - asyncSocket: PAsyncSocket + asyncSocket: AsyncSocket -proc open*(s: var TServer, port = TPort(80), reuseAddr = false) = +proc open*(s: var TServer, 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) - if s.socket == InvalidSocket: OSError(OSLastError()) + if s.socket == invalidSocket: raiseOSError(osLastError()) if reuseAddr: - s.socket.setSockOpt(OptReuseAddr, True) + s.socket.setSockOpt(OptReuseAddr, true) bindAddr(s.socket, port) listen(s.socket) - if port == TPort(0): + if port == Port(0): s.port = getSockName(s.socket) else: s.port = port - s.client = InvalidSocket + s.client = invalidSocket s.reqMethod = "" s.body = "" s.path = "" s.query = "" s.headers = {:}.newStringTable() -proc port*(s: var TServer): TPort = +proc port*(s: var TServer): Port = ## get the port number the server has acquired. result = s.port proc next*(s: var TServer) = ## proceed to the first/next request. - var client: TSocket + var client: Socket new(client) var ip: string acceptAddr(s.socket, client, ip) @@ -346,7 +344,7 @@ proc next*(s: var TServer) = # XXX we ignore "HTTP/1.1" etc. for now here var query = 0 var last = i - while last < data.len and data[last] notin whitespace: + while last < data.len and data[last] notin Whitespace: if data[last] == '?' and query == 0: query = last inc(last) if query > 0: @@ -360,9 +358,9 @@ proc close*(s: TServer) = ## closes the server (and the socket the server uses). close(s.socket) -proc run*(handleRequest: proc (client: TSocket, +proc run*(handleRequest: proc (client: Socket, path, query: string): bool {.closure.}, - port = TPort(80)) = + port = Port(80)) = ## encapsulates the server object and main loop var s: TServer open(s, port) @@ -377,7 +375,7 @@ proc run*(handleRequest: proc (client: TSocket, proc nextAsync(s: PAsyncHTTPServer) = ## proceed to the first/next request. - var client: TSocket + var client: Socket new(client) var ip: string acceptAddr(getSocket(s.asyncSocket), client, ip) @@ -466,7 +464,7 @@ proc nextAsync(s: PAsyncHTTPServer) = # XXX we ignore "HTTP/1.1" etc. for now here var query = 0 var last = i - while last < data.len and data[last] notin whitespace: + while last < data.len and data[last] notin Whitespace: if data[last] == '?' and query == 0: query = last inc(last) if query > 0: @@ -476,31 +474,31 @@ proc nextAsync(s: PAsyncHTTPServer) = s.query = "" s.path = data.substr(i, last-1) -proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: TSocket, +proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: Socket, path, query: string): bool {.closure, gcsafe.}, - port = TPort(80), address = "", + port = Port(80), address = "", reuseAddr = false): PAsyncHTTPServer = ## Creates an Asynchronous HTTP server at ``port``. var capturedRet: PAsyncHTTPServer new(capturedRet) - capturedRet.asyncSocket = AsyncSocket() + capturedRet.asyncSocket = asyncSocket() capturedRet.asyncSocket.handleAccept = - proc (s: PAsyncSocket) = + proc (s: AsyncSocket) = nextAsync(capturedRet) let quit = handleRequest(capturedRet, capturedRet.client, capturedRet.path, capturedRet.query) if quit: capturedRet.asyncSocket.close() if reuseAddr: - capturedRet.asyncSocket.setSockOpt(OptReuseAddr, True) + capturedRet.asyncSocket.setSockOpt(OptReuseAddr, true) capturedRet.asyncSocket.bindAddr(port, address) capturedRet.asyncSocket.listen() - if port == TPort(0): + if port == Port(0): capturedRet.port = getSockName(capturedRet.asyncSocket) else: capturedRet.port = port - capturedRet.client = InvalidSocket + capturedRet.client = invalidSocket capturedRet.reqMethod = "" capturedRet.body = "" capturedRet.path = "" @@ -508,8 +506,8 @@ proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: TSo capturedRet.headers = {:}.newStringTable() result = capturedRet -proc register*(d: PDispatcher, s: PAsyncHTTPServer) = - ## Registers a ``PAsyncHTTPServer`` with a ``PDispatcher``. +proc register*(d: Dispatcher, s: PAsyncHTTPServer) = + ## Registers a ``PAsyncHTTPServer`` with a ``Dispatcher``. d.register(s.asyncSocket) proc close*(h: PAsyncHTTPServer) = @@ -520,7 +518,7 @@ when isMainModule: var counter = 0 var s: TServer - open(s, TPort(0)) + open(s, Port(0)) echo("httpserver running on port ", s.port) while true: next(s) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 5d51c2d87..385787d6c 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1,77 +1,53 @@ # # -# Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf, Dominik Picheta +# Nim's Runtime Library +# (c) Copyright 2014 Andreas Rumpf, Dominik Picheta # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## This module implements a simple high performance `JSON`:idx: parser. `JSON -## (JavaScript Object Notation) `_ is a lightweight -## data-interchange format that is easy for humans to read and write (unlike -## XML). It is easy for machines to parse and generate. JSON is based on a -## subset of the JavaScript Programming Language, `Standard ECMA-262 3rd -## Edition - December 1999 -## `_. +## This module implements a simple high performance `JSON`:idx: +## parser. JSON (JavaScript Object Notation) is a lightweight +## data-interchange format that is easy for humans to read and write +## (unlike XML). It is easy for machines to parse and generate. +## JSON is based on a subset of the JavaScript Programming Language, +## Standard ECMA-262 3rd Edition - December 1999. ## -## Parsing small values quickly can be done with the convenience `parseJson() -## <#parseJson,string>`_ proc which returns the whole JSON tree. If you are -## parsing very big JSON inputs or want to skip most of the items in them you -## can initialize your own `TJsonParser <#TJsonParser>`_ with the `open() -## <#open>`_ proc and call `next() <#next>`_ in a loop to process the -## individual parsing events. +## Usage example: ## -## If you need to create JSON objects from your Nimrod types you can call procs -## like `newJObject() <#newJObject>`_ (or their equivalent `%() -## <#%,openArray[tuple[string,PJsonNode]]>`_ generic constructor). For -## consistency you can provide your own ``%`` operators for custom object -## types: +## .. code-block:: nim +## let +## small_json = """{"test": 1.3, "key2": true}""" +## jobj = parseJson(small_json) +## assert (jobj.kind == JObject) +## echo($jobj["test"].fnum) +## echo($jobj["key2"].bval) ## -## .. code-block:: nimrod -## type -## Person = object ## Generic person record. -## age: int ## The age of the person. -## name: string ## The name of the person. +## Results in: ## -## proc `%`(p: Person): PJsonNode = -## ## Converts a Person into a PJsonNode. -## result = %[("age", %p.age), ("name", %p.name)] +## .. code-block:: nim ## -## proc test() = -## # Tests making some jsons. -## var p: Person -## p.age = 24 -## p.name = "Minah" -## echo(%p) # { "age": 24, "name": "Minah"} -## -## p.age = 33 -## p.name = "Sojin" -## echo(%p) # { "age": 33, "name": "Sojin"} -## -## If you don't need special logic in your Nimrod objects' serialization code -## you can also use the `marshal module `_ which converts objects -## directly to JSON. +## 1.3000000000000000e+00 +## true import hashes, strutils, lexbase, streams, unicode type - TJsonEventKind* = enum ## Events that may occur when parsing. \ - ## - ## You compare these values agains the result of the `kind() proc <#kind>`_. - jsonError, ## An error ocurred during parsing. - jsonEof, ## End of file reached. - jsonString, ## A string literal. - jsonInt, ## An integer literal. - jsonFloat, ## A float literal. - jsonTrue, ## The value ``true``. - jsonFalse, ## The value ``false``. - jsonNull, ## The value ``null``. - jsonObjectStart, ## Start of an object: the ``{`` token. - jsonObjectEnd, ## End of an object: the ``}`` token. - jsonArrayStart, ## Start of an array: the ``[`` token. - jsonArrayEnd ## Start of an array: the ``]`` token. + JsonEventKind* = enum ## enumeration of all events that may occur when parsing + jsonError, ## an error ocurred during parsing + jsonEof, ## end of file reached + jsonString, ## a string literal + jsonInt, ## an integer literal + jsonFloat, ## a float literal + jsonTrue, ## the value ``true`` + jsonFalse, ## the value ``false`` + jsonNull, ## the value ``null`` + jsonObjectStart, ## start of an object: the ``{`` token + jsonObjectEnd, ## end of an object: the ``}`` token + jsonArrayStart, ## start of an array: the ``[`` token + jsonArrayEnd ## start of an array: the ``]`` token TTokKind = enum # must be synchronized with TJsonEventKind! tkError, @@ -89,7 +65,7 @@ type tkColon, tkComma - TJsonError = enum ## enumeration that lists all errors that can occur + JsonError* = enum ## enumeration that lists all errors that can occur errNone, ## no error errInvalidToken, ## invalid token errStringExpected, ## string expected @@ -102,22 +78,23 @@ type errEofExpected, ## EOF expected errExprExpected ## expr expected - TParserState = enum + ParserState = enum stateEof, stateStart, stateObject, stateArray, stateExpectArrayComma, stateExpectObjectComma, stateExpectColon, stateExpectValue - TJsonParser* = object of TBaseLexer ## The JSON parser object. \ - ## - ## Create a variable of this type and use `open() <#open>`_ on it. + JsonParser* = object of BaseLexer ## the parser object. a: string tok: TTokKind - kind: TJsonEventKind - err: TJsonError - state: seq[TParserState] + kind: JsonEventKind + err: JsonError + state: seq[ParserState] filename: string + +{.deprecated: [TJsonEventKind: JsonEventKind, TJsonError: JsonError, + TJsonParser: JsonParser].} const - errorMessages: array [TJsonError, string] = [ + errorMessages: array [JsonError, string] = [ "no error", "invalid token", "string expected", @@ -142,130 +119,60 @@ const "{", "}", "[", "]", ":", "," ] -proc open*(my: var TJsonParser, input: PStream, filename: string) = - ## Initializes the JSON parser with an `input stream `_. - ## - ## The `filename` parameter is not strictly required and is used only for - ## nice error messages. You can pass ``nil`` as long as you never use procs - ## like `errorMsg() <#errorMsg>`_ or `errorMsgExpected() - ## <#errorMsgExpected>`_ but passing a dummy filename like ```` - ## is safer and more user friendly. Example: - ## - ## .. code-block:: nimrod - ## import json, streams - ## - ## var - ## s = newStringStream("some valid json") - ## p: TJsonParser - ## p.open(s, "") - ## - ## Once opened, you can process JSON parsing events with the `next() - ## <#next>`_ proc. +proc open*(my: var JsonParser, input: Stream, filename: string) = + ## initializes the parser with an input stream. `Filename` is only used + ## for nice error messages. + lexbase.open(my, input) my.filename = filename my.state = @[stateStart] my.kind = jsonError my.a = "" -proc close*(my: var TJsonParser) {.inline.} = - ## Closes the parser `my` and its associated input stream. - ## - ## Example: - ## - ## .. code-block:: nimrod - ## var - ## s = newStringStream("some valid json") - ## p: TJsonParser - ## p.open(s, "") - ## finally: p.close - ## # write here parsing of input +proc close*(my: var JsonParser) {.inline.} = + ## closes the parser `my` and its associated input stream. lexbase.close(my) -proc str*(my: TJsonParser): string {.inline.} = - ## Returns the character data for the `events <#TJsonEventKind>`_ - ## ``jsonInt``, ``jsonFloat`` and ``jsonString``. - ## - ## This proc will `assert `_ in debug builds when used - ## with other event types. See `next() <#next>`_ for an usage example. +proc str*(my: JsonParser): string {.inline.} = + ## returns the character data for the events: ``jsonInt``, ``jsonFloat``, + ## ``jsonString`` assert(my.kind in {jsonInt, jsonFloat, jsonString}) return my.a -proc getInt*(my: TJsonParser): BiggestInt {.inline.} = - ## Returns the number for the `jsonInt <#TJsonEventKind>`_ event. - ## - ## This proc will `assert `_ in debug builds when used - ## with other event types. See `next() <#next>`_ for an usage example. +proc getInt*(my: JsonParser): BiggestInt {.inline.} = + ## returns the number for the event: ``jsonInt`` assert(my.kind == jsonInt) return parseBiggestInt(my.a) -proc getFloat*(my: TJsonParser): float {.inline.} = - ## Returns the number for the `jsonFloat <#TJsonEventKind>`_ event. - ## - ## This proc will `assert `_ in debug builds when used - ## with other event types. See `next() <#next>`_ for an usage example. +proc getFloat*(my: JsonParser): float {.inline.} = + ## returns the number for the event: ``jsonFloat`` assert(my.kind == jsonFloat) return parseFloat(my.a) -proc kind*(my: TJsonParser): TJsonEventKind {.inline.} = - ## Returns the current event type for the `JSON parser <#TJsonParser>`_. - ## - ## Call this proc just after `next() <#next>`_ to act on the new event. +proc kind*(my: JsonParser): JsonEventKind {.inline.} = + ## returns the current event type for the JSON parser return my.kind -proc getColumn*(my: TJsonParser): int {.inline.} = - ## Get the current column the parser has arrived at. - ## - ## While this is mostly used by procs like `errorMsg() <#errorMsg>`_ you can - ## use it as well to show user warnings if you are validating JSON values - ## during parsing. See `next() <#next>`_ for the full example: - ## - ## .. code-block:: nimrod - ## case parser.kind - ## ... - ## of jsonString: - ## let inputValue = parser.str - ## if previousValues.contains(inputValue): - ## echo "$1($2, $3) Warning: repeated value '$4'" % [ - ## parser.getFilename, $parser.getLine, $parser.getColumn, - ## inputValue] - ## ... +proc getColumn*(my: JsonParser): int {.inline.} = + ## get the current column the parser has arrived at. result = getColNumber(my, my.bufpos) -proc getLine*(my: TJsonParser): int {.inline.} = - ## Get the current line the parser has arrived at. - ## - ## While this is mostly used by procs like `errorMsg() <#errorMsg>`_ you can - ## use it as well to indicate user warnings if you are validating JSON values - ## during parsing. See `next() <#next>`_ and `getColumn() <#getColumn>`_ for - ## examples. +proc getLine*(my: JsonParser): int {.inline.} = + ## get the current line the parser has arrived at. result = my.lineNumber -proc getFilename*(my: TJsonParser): string {.inline.} = - ## Get the filename of the file that the parser is processing. - ## - ## This is the value you pass to the `open() <#open>`_ proc. While this is - ## mostly used by procs like `errorMsg() <#errorMsg>`_ you can use it as well - ## to indicate user warnings if you are validating JSON values during - ## parsing. See `next() <#next>`_ and `getColumn() <#getColumn>`_ for - ## examples. +proc getFilename*(my: JsonParser): string {.inline.} = + ## get the filename of the file that the parser processes. result = my.filename -proc errorMsg*(my: TJsonParser): string = - ## Returns a helpful error message for the `jsonError <#TJsonEventKind>`_ - ## event. - ## - ## This proc will `assert `_ in debug builds when used - ## with other event types. See `next() <#next>`_ for an usage example. +proc errorMsg*(my: JsonParser): string = + ## returns a helpful error message for the event ``jsonError`` assert(my.kind == jsonError) result = "$1($2, $3) Error: $4" % [ my.filename, $getLine(my), $getColumn(my), errorMessages[my.err]] -proc errorMsgExpected*(my: TJsonParser, e: string): string = - ## Returns an error message "`e` expected". - ## - ## The message is in the same format as the other error messages which - ## include the parser filename, line and column values. This is used by - ## `raiseParseErr() <#raiseParseErr>`_ to raise an `EJsonParsingError - ## <#EJsonParsingError>`_. +proc errorMsgExpected*(my: JsonParser, e: string): string = + ## returns an error message "`e` expected" in the same format as the + ## other error messages result = "$1($2, $3) Error: $4" % [ my.filename, $getLine(my), $getColumn(my), e & " expected"] @@ -277,7 +184,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 TJsonParser): TTokKind = +proc parseString(my: var JsonParser): TTokKind = result = tkString var pos = my.bufpos + 1 var buf = my.buf @@ -317,7 +224,7 @@ proc parseString(my: var TJsonParser): TTokKind = 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(TRune(r))) + add(my.a, toUTF8(Rune(r))) else: # don't bother with the error add(my.a, buf[pos]) @@ -335,7 +242,7 @@ proc parseString(my: var TJsonParser): TTokKind = inc(pos) my.bufpos = pos # store back -proc skip(my: var TJsonParser) = +proc skip(my: var JsonParser) = var pos = my.bufpos var buf = my.buf while true: @@ -393,7 +300,7 @@ proc skip(my: var TJsonParser) = break my.bufpos = pos -proc parseNumber(my: var TJsonParser) = +proc parseNumber(my: var JsonParser) = var pos = my.bufpos var buf = my.buf if buf[pos] == '-': @@ -424,7 +331,7 @@ proc parseNumber(my: var TJsonParser) = inc(pos) my.bufpos = pos -proc parseName(my: var TJsonParser) = +proc parseName(my: var JsonParser) = var pos = my.bufpos var buf = my.buf if buf[pos] in IdentStartChars: @@ -433,7 +340,7 @@ proc parseName(my: var TJsonParser) = inc(pos) my.bufpos = pos -proc getTok(my: var TJsonParser): TTokKind = +proc getTok(my: var JsonParser): TTokKind = setLen(my.a, 0) skip(my) # skip whitespace, comments case my.buf[my.bufpos] @@ -477,33 +384,8 @@ proc getTok(my: var TJsonParser): TTokKind = result = tkError my.tok = result -proc next*(my: var TJsonParser) = - ## Retrieves the first/next event for the `JSON parser <#TJsonParser>`_. - ## - ## You are meant to call this method inside an infinite loop. After each - ## call, check the result of the `kind() <#kind>`_ proc to know what has to - ## be done next (eg. break out due to end of file). Here is a basic example - ## which simply echoes all found elements by the parser: - ## - ## .. code-block:: nimrod - ## parser.open(stream, "") - ## while true: - ## parser.next - ## case parser.kind - ## of jsonError: - ## echo parser.errorMsg - ## break - ## of jsonEof: break - ## of jsonString: echo parser.str - ## of jsonInt: echo parser.getInt - ## of jsonFloat: echo parser.getFloat - ## of jsonTrue: echo "true" - ## of jsonFalse: echo "false" - ## of jsonNull: echo "null" - ## of jsonObjectStart: echo "{" - ## of jsonObjectEnd: echo "}" - ## of jsonArrayStart: echo "[" - ## of jsonArrayEnd: echo "]" +proc next*(my: var JsonParser) = + ## retrieves the first/next event. This controls the parser. var tk = getTok(my) var i = my.state.len-1 # the following code is a state machine. If we had proper coroutines, @@ -520,7 +402,7 @@ proc next*(my: var TJsonParser) = case tk of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull: my.state[i] = stateEof # expect EOF next! - my.kind = TJsonEventKind(ord(tk)) + my.kind = JsonEventKind(ord(tk)) of tkBracketLe: my.state.add(stateArray) # we expect any my.kind = jsonArrayStart @@ -536,7 +418,7 @@ proc next*(my: var TJsonParser) = case tk of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull: my.state.add(stateExpectColon) - my.kind = TJsonEventKind(ord(tk)) + my.kind = JsonEventKind(ord(tk)) of tkBracketLe: my.state.add(stateExpectColon) my.state.add(stateArray) @@ -555,7 +437,7 @@ proc next*(my: var TJsonParser) = case tk of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull: my.state.add(stateExpectArrayComma) # expect value next! - my.kind = TJsonEventKind(ord(tk)) + my.kind = JsonEventKind(ord(tk)) of tkBracketLe: my.state.add(stateExpectArrayComma) my.state.add(stateArray) @@ -606,7 +488,7 @@ proc next*(my: var TJsonParser) = case tk of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull: my.state[i] = stateExpectObjectComma - my.kind = TJsonEventKind(ord(tk)) + my.kind = JsonEventKind(ord(tk)) of tkBracketLe: my.state[i] = stateExpectObjectComma my.state.add(stateArray) @@ -623,16 +505,7 @@ proc next*(my: var TJsonParser) = # ------------- higher level interface --------------------------------------- type - TJsonNodeKind* = enum ## Possible `JSON node <#TJsonNodeKind>`_ types. \ - ## - ## To build nodes use the helper procs - ## `newJNull() <#newJNull>`_, - ## `newJBool() <#newJBool>`_, - ## `newJInt() <#newJInt>`_, - ## `newJFloat() <#newJFloat>`_, - ## `newJString() <#newJString>`_, - ## `newJObject() <#newJObject>`_ and - ## `newJArray() <#newJArray>`_. + JsonNodeKind* = enum ## possible JSON node types JNull, JBool, JInt, @@ -641,10 +514,9 @@ type JObject, JArray - PJsonNode* = ref TJsonNode ## Reference to a `JSON node <#TJsonNode>`_. - TJsonNode* {.final, pure, acyclic.} = object ## `Object variant \ - ## `_ wrapping all possible JSON types. - case kind*: TJsonNodeKind + JsonNode* = ref JsonNodeObj ## JSON node + JsonNodeObj* {.acyclic.} = object + case kind*: JsonNodeKind of JString: str*: string of JInt: @@ -656,250 +528,105 @@ type of JNull: nil of JObject: - fields*: seq[tuple[key: string, val: PJsonNode]] + fields*: seq[tuple[key: string, val: JsonNode]] of JArray: - elems*: seq[PJsonNode] + elems*: seq[JsonNode] - EJsonParsingError* = object of EInvalidValue ## Raised during JSON parsing. \ - ## - ## Example: - ## - ## .. code-block:: nimrod - ## let smallJson = """{"test: 1.3, "key2": true}""" - ## try: - ## discard parseJson(smallJson) - ## # --> Bad JSON! input(1, 18) Error: : expected - ## except EJsonParsingError: - ## echo "Bad JSON! " & getCurrentExceptionMsg() + JsonParsingError* = object of ValueError ## is raised for a JSON error -proc raiseParseErr*(p: TJsonParser, msg: string) {.noinline, noreturn.} = - ## Raises an `EJsonParsingError <#EJsonParsingError>`_ exception. - ## - ## The message for the exception will be built passing the `msg` parameter to - ## the `errorMsgExpected() <#errorMsgExpected>`_ proc. - raise newException(EJsonParsingError, errorMsgExpected(p, msg)) +{.deprecated: [EJsonParsingError: JsonParsingError, TJsonNode: JsonNodeObj, + PJsonNode: JsonNode, TJsonNodeKind: JsonNodeKind].} -proc newJString*(s: string): PJsonNode = - ## Creates a new `JString PJsonNode <#TJsonNodeKind>`_. - ## - ## Example: - ## - ## .. code-block:: nimrod - ## var node = newJString("A string") - ## echo node - ## # --> "A string" - ## - ## Or you can use the shorter `%() proc <#%,string>`_. +proc raiseParseErr*(p: JsonParser, msg: string) {.noinline, noreturn.} = + ## raises an `EJsonParsingError` exception. + raise newException(JsonParsingError, errorMsgExpected(p, msg)) + +proc newJString*(s: string): JsonNode = + ## Creates a new `JString JsonNode`. new(result) result.kind = JString result.str = s -proc newJStringMove(s: string): PJsonNode = +proc newJStringMove(s: string): JsonNode = new(result) result.kind = JString shallowCopy(result.str, s) -proc newJInt*(n: BiggestInt): PJsonNode = - ## Creates a new `JInt PJsonNode <#TJsonNodeKind>`_. - ## - ## Example: - ## - ## .. code-block:: nimrod - ## var node = newJInt(900_100_200_300) - ## echo node - ## # --> 900100200300 - ## - ## Or you can use the shorter `%() proc <#%,BiggestInt>`_. +proc newJInt*(n: BiggestInt): JsonNode = + ## Creates a new `JInt JsonNode`. new(result) result.kind = JInt result.num = n -proc newJFloat*(n: float): PJsonNode = - ## Creates a new `JFloat PJsonNode <#TJsonNodeKind>`_. - ## - ## Example: - ## - ## .. code-block:: nimrod - ## var node = newJFloat(3.14) - ## echo node - ## # --> 3.14 - ## - ## Or you can use the shorter `%() proc <#%,float>`_. +proc newJFloat*(n: float): JsonNode = + ## Creates a new `JFloat JsonNode`. new(result) result.kind = JFloat result.fnum = n -proc newJBool*(b: bool): PJsonNode = - ## Creates a new `JBool PJsonNode <#TJsonNodeKind>`_. - ## - ## Example: - ## - ## .. code-block:: nimrod - ## var node = newJBool(true) - ## echo node - ## # --> true - ## - ## Or you can use the shorter `%() proc <#%,bool>`_. +proc newJBool*(b: bool): JsonNode = + ## Creates a new `JBool JsonNode`. new(result) result.kind = JBool result.bval = b -proc newJNull*(): PJsonNode = - ## Creates a new `JNull PJsonNode <#TJsonNodeKind>`_. - ## - ## Example: - ## - ## .. code-block:: nimrod - ## var node = newJNull() - ## echo node - ## # --> null +proc newJNull*(): JsonNode = + ## Creates a new `JNull JsonNode`. new(result) -proc newJObject*(): PJsonNode = - ## Creates a new `JObject PJsonNode <#TJsonNodeKind>`_. - ## - ## The `PJsonNode <#PJsonNode>`_ will be initialized with an empty ``fields`` - ## sequence to which you can add new elements. Example: - ## - ## .. code-block:: nimrod - ## var node = newJObject() - ## node.add("age", newJInt(24)) - ## node.add("name", newJString("Minah")) - ## echo node - ## # --> { "age": 24, "name": "Minah"} - ## - ## Or you can use the shorter `%() proc - ## <#%,openArray[tuple[string,PJsonNode]]>`_. +proc newJObject*(): JsonNode = + ## Creates a new `JObject JsonNode` new(result) result.kind = JObject result.fields = @[] -proc newJArray*(): PJsonNode = - ## Creates a new `JArray PJsonNode <#TJsonNodeKind>`_. - ## - ## The `PJsonNode <#PJsonNode>`_ will be initialized with an empty ``elems`` - ## sequence to which you can add new elements. Example: - ## - ## .. code-block:: nimrod - ## var node = newJArray() - ## node.add(newJString("Mixing types")) - ## node.add(newJInt(42)) - ## node.add(newJString("is madness")) - ## node.add(newJFloat(3.14)) - ## echo node - ## # --> [ "Mixing types", 42, "is madness", 3.14] - ## - ## Or you can use the shorter `%() proc <#%,openArray[PJsonNode]>`_. +proc newJArray*(): JsonNode = + ## Creates a new `JArray JsonNode` new(result) result.kind = JArray result.elems = @[] -proc `%`*(s: string): PJsonNode = - ## Creates a new `JString PJsonNode <#TJsonNodeKind>`_. - ## - ## Example: - ## - ## .. code-block:: nimrod - ## var node = %"A string" - ## echo node - ## # --> "A string" - ## - ## This generic constructor is equivalent to the `newJString() - ## <#newJString>`_ proc. +proc `%`*(s: string): JsonNode = + ## Generic constructor for JSON data. Creates a new `JString JsonNode`. new(result) result.kind = JString result.str = s -proc `%`*(n: BiggestInt): PJsonNode = - ## Creates a new `JInt PJsonNode <#TJsonNodeKind>`_. - ## - ## Example: - ## - ## .. code-block:: nimrod - ## var node = %900_100_200_300 - ## echo node - ## # --> 900100200300 - ## - ## This generic constructor is equivalent to the `newJInt() <#newJInt>`_ - ## proc. +proc `%`*(n: BiggestInt): JsonNode = + ## Generic constructor for JSON data. Creates a new `JInt JsonNode`. new(result) result.kind = JInt result.num = n -proc `%`*(n: float): PJsonNode = - ## Creates a new `JFloat PJsonNode <#TJsonNodeKind>`_. - ## - ## Example: - ## - ## .. code-block:: nimrod - ## var node = %3.14 - ## echo node - ## # --> 3.14 - ## - ## This generic constructor is equivalent to the `newJFloat() <#newJFloat>`_ - ## proc. +proc `%`*(n: float): JsonNode = + ## Generic constructor for JSON data. Creates a new `JFloat JsonNode`. new(result) result.kind = JFloat result.fnum = n -proc `%`*(b: bool): PJsonNode = - ## Creates a new `JBool PJsonNode <#TJsonNodeKind>`_. - ## - ## Example: - ## - ## .. code-block:: nimrod - ## var node = %true - ## echo node - ## # --> true - ## - ## This generic constructor is equivalent to the `newJBool() <#newJBool>`_ - ## proc. +proc `%`*(b: bool): JsonNode = + ## Generic constructor for JSON data. Creates a new `JBool JsonNode`. new(result) result.kind = JBool result.bval = b -proc `%`*(keyVals: openArray[tuple[key: string, val: PJsonNode]]): PJsonNode = - ## Creates a new `JObject PJsonNode <#TJsonNodeKind>`_. - ## - ## Unlike the `newJObject() <#newJObject>`_ proc, which returns an object - ## that has to be further manipulated, you can use this generic constructor - ## to create JSON objects with all their fields in one go. Example: - ## - ## .. code-block:: nimrod - ## let node = %[("age", %24), ("name", %"Minah")] - ## echo node - ## # --> { "age": 24, "name": "Minah"} +proc `%`*(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode = + ## Generic constructor for JSON data. Creates a new `JObject JsonNode` new(result) result.kind = JObject newSeq(result.fields, keyVals.len) for i, p in pairs(keyVals): result.fields[i] = p -proc `%`*(elements: openArray[PJsonNode]): PJsonNode = - ## Creates a new `JArray PJsonNode <#TJsonNodeKind>`_. - ## - ## Unlike the `newJArray() <#newJArray>`_ proc, which returns an object - ## that has to be further manipulated, you can use this generic constructor - ## to create JSON arrays with all their values in one go. Example: - ## - ## .. code-block:: nimrod - ## let node = %[%"Mixing types", %42, - ## %"is madness", %3.14,] - ## echo node - ## # --> [ "Mixing types", 42, "is madness", 3.14] +proc `%`*(elements: openArray[JsonNode]): JsonNode = + ## Generic constructor for JSON data. Creates a new `JArray JsonNode` new(result) result.kind = JArray newSeq(result.elems, elements.len) for i, p in pairs(elements): result.elems[i] = p -proc `==`* (a,b: PJsonNode): bool = - ## Check two `PJsonNode <#PJsonNode>`_ nodes for equality. - ## - ## Example: - ## - ## .. code-block:: nimrod - ## assert(%1 == %1) - ## assert(%1 != %2) +proc `==`* (a,b: JsonNode): bool = + ## Check two nodes for equality if a.isNil: if b.isNil: return true return false @@ -922,22 +649,8 @@ proc `==`* (a,b: PJsonNode): bool = of JObject: a.fields == b.fields -proc hash* (n:PJsonNode): THash = - ## Computes the hash for a JSON node. - ## - ## The `THash `_ allows JSON nodes to be used as keys for - ## `sets `_ or `tables `_. Example: - ## - ## .. code-block:: nimrod - ## import json, sets - ## - ## var - ## uniqueValues = initSet[PJsonNode]() - ## values = %[%1, %2, %1, %2, %3] - ## for value in values.elems: - ## discard uniqueValues.containsOrIncl(value) - ## echo uniqueValues - ## # --> {1, 2, 3} +proc hash* (n:JsonNode): THash = + ## Compute the hash for a JSON node case n.kind of JArray: result = hash(n.elems) @@ -954,41 +667,18 @@ proc hash* (n:PJsonNode): THash = of JNull: result = hash(0) -proc len*(n: PJsonNode): int = - ## Returns the number of children items for this `PJsonNode <#PJsonNode>`_. - ## - ## If `n` is a `JArray <#TJsonNodeKind>`_, it will return the number of - ## elements. If `n` is a `JObject <#TJsonNodeKind>`_, it will return the - ## number of key-value pairs. For all other types this proc returns zero. - ## Example: - ## - ## .. code-block:: nimrod - ## let - ## n1 = %[("age", %33), ("name", %"Sojin")] - ## n2 = %[%1, %2, %3, %4, %5, %6, %7] - ## n3 = %"Some odd string we have here" - ## echo n1.len # --> 2 - ## echo n2.len # --> 7 - ## echo n3.len # --> 0 - ## +proc len*(n: JsonNode): int = + ## If `n` is a `JArray`, 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 JArray: result = n.elems.len of JObject: result = n.fields.len else: discard -proc `[]`*(node: PJsonNode, name: string): PJsonNode = - ## Gets a named field from a `JObject <#TJsonNodeKind>`_ `PJsonNode - ## <#PJsonNode>`_. - ## - ## Returns the value for `name` or nil if `node` doesn't contain such a - ## field. This proc will `assert `_ in debug builds if - ## `name` is ``nil`` or `node` is not a ``JObject``. On release builds it - ## will likely crash. Example: - ## - ## .. code-block:: nimrod - ## let node = %[("age", %40), ("name", %"Britney")] - ## echo node["name"] - ## # --> "Britney" +proc `[]`*(node: JsonNode, name: string): JsonNode = + ## Gets a field from a `JObject`, which must not be nil. + ## If the value at `name` does not exist, returns nil assert(not isNil(node)) assert(node.kind == JObject) for key, item in items(node.fields): @@ -996,93 +686,36 @@ proc `[]`*(node: PJsonNode, name: string): PJsonNode = return item return nil -proc `[]`*(node: PJsonNode, index: int): PJsonNode = - ## Gets the `index` item from a `JArray <#TJsonNodeKind>`_ `PJsonNode - ## <#PJsonNode>`_. - ## - ## Returns the specified item. Result is undefined if `index` is out of - ## bounds. This proc will `assert `_ in debug builds if - ## `node` is ``nil`` or not a ``JArray``. Example: - ## - ## .. code-block:: nimrod - ## let node = %[%"Mixing types", %42, - ## %"is madness", %3.14,] - ## echo node[2] - ## # --> "is madness" +proc `[]`*(node: JsonNode, index: int): JsonNode = + ## Gets the node at `index` in an Array. Result is undefined if `index` + ## is out of bounds assert(not isNil(node)) assert(node.kind == JArray) return node.elems[index] -proc hasKey*(node: PJsonNode, key: string): bool = - ## Returns `true` if `key` exists in a `JObject <#TJsonNodeKind>`_ `PJsonNode - ## <#PJsonNode>`_. - ## - ## This proc will `assert `_ in debug builds if `node` is - ## not a ``JObject``. On release builds it will likely crash. Example: - ## - ## .. code-block:: nimrod - ## let node = %[("age", %40), ("name", %"Britney")] - ## echo node.hasKey("email") - ## # --> false +proc hasKey*(node: JsonNode, key: string): bool = + ## Checks if `key` exists in `node`. assert(node.kind == JObject) for k, item in items(node.fields): if k == key: return true -proc existsKey*(node: PJsonNode, key: string): bool {.deprecated.} = node.hasKey(key) - ## Deprecated for `hasKey() <#hasKey>`_. +proc existsKey*(node: JsonNode, key: string): bool {.deprecated.} = node.hasKey(key) + ## Deprecated for `hasKey` -proc add*(father, child: PJsonNode) = - ## Adds `child` to a `JArray <#TJsonNodeKind>`_ `PJsonNode <#PJsonNode>`_ - ## `father` node. - ## - ## This proc will `assert `_ in debug builds if `node` is - ## not a ``JArray``. On release builds it will likely crash. Example: - ## - ## .. code-block:: nimrod - ## var node = %[%"Mixing types", %42] - ## node.add(%"is madness") - ## echo node - ## # --> false +proc add*(father, child: JsonNode) = + ## Adds `child` to a JArray node `father`. assert father.kind == JArray father.elems.add(child) -proc add*(obj: PJsonNode, key: string, val: PJsonNode) = - ## Adds ``(key, val)`` pair to a `JObject <#TJsonNodeKind>`_ `PJsonNode - ## <#PJsonNode>`_ `obj` node. - ## - ## For speed reasons no check for duplicate keys is performed! But ``[]=`` - ## performs the check. - ## - ## This proc will `assert `_ in debug builds if `node` is - ## not a ``JObject``. On release builds it will likely crash. Example: - ## - ## .. code-block:: nimrod - ## var node = newJObject() - ## node.add("age", newJInt(12)) - ## # This is wrong! But we need speed… - ## node.add("age", newJInt(24)) - ## echo node - ## # --> { "age": 12, "age": 24} +proc add*(obj: JsonNode, key: string, val: JsonNode) = + ## Adds ``(key, val)`` pair to the JObject node `obj`. For speed + ## reasons no check for duplicate keys is performed! + ## But ``[]=`` performs the check. assert obj.kind == JObject obj.fields.add((key, val)) -proc `[]=`*(obj: PJsonNode, key: string, val: PJsonNode) = - ## Sets a field from a `JObject <#TJsonNodeKind>`_ `PJsonNode - ## <#PJsonNode>`_ `obj` node. - ## - ## Unlike the `add() <#add,PJsonNode,string,PJsonNode>`_ proc this will - ## perform a check for duplicate keys and replace existing values. - ## - ## This proc will `assert `_ in debug builds if `node` is - ## not a ``JObject``. On release builds it will likely crash. Example: - ## - ## .. code-block:: nimrod - ## var node = newJObject() - ## node["age"] = %12 - ## # The new value replaces the previous one. - ## node["age"] = %24 - ## echo node - ## # --> { "age": 24} +proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) = + ## Sets a field from a `JObject`. Performs a check for duplicate keys. assert(obj.kind == JObject) for i in 0..obj.fields.len-1: if obj.fields[i].key == key: @@ -1090,14 +723,14 @@ proc `[]=`*(obj: PJsonNode, key: string, val: PJsonNode) = return obj.fields.add((key, val)) -proc `{}`*(node: PJsonNode, key: string): PJsonNode = +proc `{}`*(node: JsonNode, key: string): JsonNode = ## Transverses the node and gets the given value. If any of the ## names does not exist, returns nil result = node if isNil(node): return nil result = result[key] -proc `{}=`*(node: PJsonNode, names: varargs[string], value: PJsonNode) = +proc `{}=`*(node: JsonNode, names: varargs[string], value: JsonNode) = ## Transverses the node and tries to set the value at the given location ## to `value` If any of the names are missing, they are added var node = node @@ -1107,31 +740,17 @@ proc `{}=`*(node: PJsonNode, names: varargs[string], value: PJsonNode) = node = node[names[i]] node[names[names.len-1]] = value -proc delete*(obj: PJsonNode, key: string) = +proc delete*(obj: JsonNode, key: string) = ## Deletes ``obj[key]`` preserving the order of the other (key, value)-pairs. - ## - ## If `key` doesn't exist in `obj` ``EInvalidIndex`` will be raised. This - ## proc will `assert `_ in debug builds if `node` is not - ## a ``JObject``. On release builds it will likely crash. Example: - ## - ## .. code-block:: nimrod - ## var node = %[("age", %37), ("name", %"Chris"), ("male", %false)] - ## echo node - ## # --> { "age": 37, "name": "Chris", "male": false} - ## node.delete("age") - ## echo node - ## # --> { "name": "Chris", "male": false} assert(obj.kind == JObject) for i in 0..obj.fields.len-1: if obj.fields[i].key == key: obj.fields.delete(i) return - raise newException(EInvalidIndex, "key not in object") + raise newException(IndexError, "key not in object") -proc copy*(p: PJsonNode): PJsonNode = - ## Performs a deep copy of `p`. - ## - ## Modifications to the copy won't affect the original. +proc copy*(p: JsonNode): JsonNode = + ## Performs a deep copy of `a`. case p.kind of JString: result = newJString(p.str) @@ -1166,12 +785,6 @@ proc nl(s: var string, ml: bool) = proc escapeJson*(s: string): string = ## Converts a string `s` to its JSON representation. - ## - ## Example: - ## - ## .. code-block:: nimrod - ## echo """name: "Torbjørn"""".escapeJson - ## # --> "name: \"Torbj\u00F8rn\"" result = newStringOfCap(s.len + s.len shr 3) result.add("\"") for x in runes(s): @@ -1187,7 +800,7 @@ proc escapeJson*(s: string): string = result.add(toHex(r, 4)) result.add("\"") -proc toPretty(result: var string, node: PJsonNode, indent = 2, ml = true, +proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true, lstArr = false, currIndent = 0) = case node.kind of JObject: @@ -1242,68 +855,34 @@ proc toPretty(result: var string, node: PJsonNode, indent = 2, ml = true, if lstArr: result.indent(currIndent) result.add("null") -proc pretty*(node: PJsonNode, indent = 2): string = - ## Converts `node` to a pretty JSON representation. - ## - ## The representation will have indentation use multiple lines. Example: - ## - ## .. code-block:: nimrod - ## let node = %[("age", %33), ("name", %"Sojin")] - ## echo node - ## # --> { "age": 33, "name": "Sojin"} - ## echo node.pretty - ## # --> { - ## # "age": 33, - ## # "name": "Sojin" - ## # } +proc pretty*(node: JsonNode, indent = 2): string = + ## Converts `node` to its JSON Representation, with indentation and + ## on multiple lines. result = "" toPretty(result, node, indent) -proc `$`*(node: PJsonNode): string = - ## Converts `node` to its JSON representation on one line. +proc `$`*(node: JsonNode): string = + ## Converts `node` to its JSON Representation on one line. result = "" toPretty(result, node, 1, false) -iterator items*(node: PJsonNode): PJsonNode = - ## Iterator for the items of `node`. - ## - ## This proc will `assert `_ in debug builds if `node` is - ## not a `JArray <#TJsonNodeKind>`_. On release builds it will likely crash. - ## Example: - ## - ## .. code-block:: nimrod - ## let numbers = %[%1, %2, %3] - ## for n in numbers.items: - ## echo "Number ", n - ## ## --> Number 1 - ## ## Number 2 - ## ## Number 3 +iterator items*(node: JsonNode): JsonNode = + ## Iterator for the items of `node`. `node` has to be a JArray. assert node.kind == JArray for i in items(node.elems): yield i -iterator pairs*(node: PJsonNode): tuple[key: string, val: PJsonNode] = - ## Iterator for the child elements of `node`. - ## - ## This proc will `assert `_ in debug builds if `node` is - ## not a `JObject <#TJsonNodeKind>`_. On release builds it will likely crash. - ## Example: - ## - ## .. code-block:: nimrod - ## var node = %[("age", %37), ("name", %"Chris")] - ## for key, value in node.pairs: - ## echo "Key: ", key, ", value: ", value - ## # --> Key: age, value: 37 - ## # Key: name, value: "Chris" +iterator pairs*(node: JsonNode): tuple[key: string, val: JsonNode] = + ## Iterator for the child elements of `node`. `node` has to be a JObject. assert node.kind == JObject for key, val in items(node.fields): yield (key, val) -proc eat(p: var TJsonParser, tok: TTokKind) = +proc eat(p: var JsonParser, tok: TTokKind) = if p.tok == tok: discard getTok(p) else: raiseParseErr(p, tokToStr[tok]) -proc parseJson(p: var TJsonParser): PJsonNode = +proc parseJson(p: var JsonParser): JsonNode = ## Parses JSON from a JSON Parser `p`. case p.tok of tkString: @@ -1352,46 +931,24 @@ proc parseJson(p: var TJsonParser): PJsonNode = raiseParseErr(p, "{") when not defined(js): - proc parseJson*(s: PStream, filename: string): PJsonNode = - ## Generic convenience proc to parse stream `s` into a `PJsonNode`. - ## - ## This wraps around `open() <#open>`_ and `next() <#next>`_ to return the - ## full JSON DOM. Errors will be raised as exceptions, this requires the - ## `filename` parameter to not be ``nil`` to avoid crashes. - assert(not isNil(filename)) - var p: TJsonParser + proc parseJson*(s: Stream, filename: string): JsonNode = + ## Parses from a stream `s` into a `JsonNode`. `filename` is only needed + ## for nice error messages. + var p: JsonParser p.open(s, filename) discard getTok(p) # read first token result = p.parseJson() p.close() - proc parseJson*(buffer: string): PJsonNode = + proc parseJson*(buffer: string): JsonNode = ## Parses JSON from `buffer`. - ## - ## Specialized version around `parseJson(PStream, string) - ## <#parseJson,PStream,string>`_. Example: - ## - ## .. code-block:: nimrod - ## let - ## smallJson = """{"test": 1.3, "key2": true}""" - ## jobj = parseJson(smallJson) - ## assert jobj.kind == JObject - ## - ## assert jobj["test"].kind == JFloat - ## echo jobj["test"].fnum # --> 1.3 - ## - ## assert jobj["key2"].kind == JBool - ## echo jobj["key2"].bval # --> true result = parseJson(newStringStream(buffer), "input") - proc parseFile*(filename: string): PJsonNode = - ## Parses `file` into a `PJsonNode`. - ## - ## Specialized version around `parseJson(PStream, string) - ## <#parseJson,PStream,string>`_. + proc parseFile*(filename: string): JsonNode = + ## Parses `file` into a `JsonNode`. var stream = newFileStream(filename, fmRead) if stream == nil: - raise newException(EIO, "cannot read from file: " & filename) + raise newException(IOError, "cannot read from file: " & filename) result = parseJson(stream, filename) else: from math import `mod` @@ -1399,7 +956,7 @@ else: TJSObject = object proc parseNativeJson(x: cstring): TJSObject {.importc: "JSON.parse".} - proc getVarType(x): TJsonNodeKind = + proc getVarType(x): JsonNodeKind = result = JNull proc getProtoName(y): cstring {.importc: "Object.prototype.toString.call".} @@ -1434,7 +991,7 @@ else: return `x`[`y`]; """ - proc convertObject(x: TJSObject): PJsonNode = + proc convertObject(x: TJSObject): JsonNode = case getVarType(x) of JArray: result = newJArray() @@ -1461,15 +1018,15 @@ else: of JNull: result = newJNull() - proc parseJson*(buffer: string): PJsonNode = + proc parseJson*(buffer: string): JsonNode = return parseNativeJson(buffer).convertObject() when false: import os - var s = newFileStream(ParamStr(1), fmRead) - if s == nil: quit("cannot open the file" & ParamStr(1)) - var x: TJsonParser - open(x, s, ParamStr(1)) + var s = newFileStream(paramStr(1), fmRead) + if s == nil: quit("cannot open the file" & paramStr(1)) + var x: JsonParser + open(x, s, paramStr(1)) while true: next(x) case x.kind @@ -1478,13 +1035,13 @@ when false: break of jsonEof: break of jsonString, jsonInt, jsonFloat: echo(x.str) - of jsonTrue: Echo("!TRUE") - of jsonFalse: Echo("!FALSE") - of jsonNull: Echo("!NULL") - of jsonObjectStart: Echo("{") - of jsonObjectEnd: Echo("}") - of jsonArrayStart: Echo("[") - of jsonArrayEnd: Echo("]") + of jsonTrue: echo("!TRUE") + of jsonFalse: echo("!FALSE") + of jsonNull: echo("!NULL") + of jsonObjectStart: echo("{") + of jsonObjectEnd: echo("}") + of jsonArrayStart: echo("[") + of jsonArrayEnd: echo("]") close(x) @@ -1505,8 +1062,8 @@ when isMainModule: echo(pretty(parsed2)) try: echo(parsed["key2"][12123]) - raise newException(EInvalidValue, "That line was expected to fail") - except EInvalidIndex: echo() + raise newException(ValueError, "That line was expected to fail") + except IndexError: echo() let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd" }""" # nil passthrough @@ -1518,12 +1075,12 @@ when isMainModule: try: let a = testJson["a"][9] assert(false, "EInvalidIndex not thrown") - except EInvalidIndex: + except IndexError: discard try: let a = testJson["a"][-1] assert(false, "EInvalidIndex not thrown") - except EInvalidIndex: + except IndexError: discard try: assert(testJson["a"][0].num == 1, "Index doesn't correspond to its value") diff --git a/lib/pure/lexbase.nim b/lib/pure/lexbase.nim index eee95e2e6..a3a3d7b5c 100644 --- a/lib/pure/lexbase.nim +++ b/lib/pure/lexbase.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Compiler +# The Nim Compiler # (c) Copyright 2009 Andreas Rumpf # # See the file "copying.txt", included in this @@ -25,34 +25,36 @@ const # type - TBaseLexer* = object of TObject ## the base lexer. Inherit your lexer from - ## this object. + BaseLexer* = object of RootObj ## the base lexer. Inherit your lexer from + ## this object. bufpos*: int ## the current position within the buffer buf*: cstring ## the buffer itself bufLen*: int ## length of buffer in characters - input: PStream ## the input stream + input: Stream ## the input stream lineNumber*: int ## the current line number sentinel: int lineStart: int # index of last line start in buffer fileOpened: bool -proc open*(L: var TBaseLexer, input: PStream, bufLen: int = 8192) +{.deprecated: [TBaseLexer: BaseLexer].} + +proc open*(L: var BaseLexer, input: Stream, bufLen: int = 8192) ## inits the TBaseLexer with a stream to read from -proc close*(L: var TBaseLexer) +proc close*(L: var BaseLexer) ## closes the base lexer. This closes `L`'s associated stream too. -proc getCurrentLine*(L: TBaseLexer, marker: bool = true): string +proc getCurrentLine*(L: BaseLexer, marker: bool = true): string ## retrieves the current line. -proc getColNumber*(L: TBaseLexer, pos: int): int +proc getColNumber*(L: BaseLexer, pos: int): int ## retrieves the current column. -proc handleCR*(L: var TBaseLexer, pos: int): int +proc handleCR*(L: var BaseLexer, pos: int): int ## Call this if you scanned over '\c' in the buffer; it returns the the ## position to continue the scanning from. `pos` must be the position ## of the '\c'. -proc handleLF*(L: var TBaseLexer, pos: int): int +proc handleLF*(L: var BaseLexer, pos: int): int ## Call this if you scanned over '\L' in the buffer; it returns the the ## position to continue the scanning from. `pos` must be the position ## of the '\L'. @@ -62,11 +64,11 @@ proc handleLF*(L: var TBaseLexer, pos: int): int const chrSize = sizeof(char) -proc close(L: var TBaseLexer) = +proc close(L: var BaseLexer) = dealloc(L.buf) close(L.input) -proc fillBuffer(L: var TBaseLexer) = +proc fillBuffer(L: var BaseLexer) = var charsRead, toCopy, s: int # all are in characters, # not bytes (in case this @@ -111,7 +113,7 @@ proc fillBuffer(L: var TBaseLexer) = break s = L.bufLen - 1 -proc fillBaseLexer(L: var TBaseLexer, pos: int): int = +proc fillBaseLexer(L: var BaseLexer, pos: int): int = assert(pos <= L.sentinel) if pos < L.sentinel: result = pos + 1 # nothing to do @@ -121,24 +123,24 @@ proc fillBaseLexer(L: var TBaseLexer, pos: int): int = result = 0 L.lineStart = result -proc handleCR(L: var TBaseLexer, pos: int): int = +proc handleCR(L: var BaseLexer, pos: int): int = assert(L.buf[pos] == '\c') inc(L.lineNumber) result = fillBaseLexer(L, pos) if L.buf[result] == '\L': result = fillBaseLexer(L, result) -proc handleLF(L: var TBaseLexer, pos: int): int = +proc handleLF(L: var BaseLexer, pos: int): int = assert(L.buf[pos] == '\L') inc(L.lineNumber) result = fillBaseLexer(L, pos) #L.lastNL := result-1; // BUGFIX: was: result; -proc skipUtf8Bom(L: var TBaseLexer) = +proc skipUtf8Bom(L: var BaseLexer) = if (L.buf[0] == '\xEF') and (L.buf[1] == '\xBB') and (L.buf[2] == '\xBF'): inc(L.bufpos, 3) inc(L.lineStart, 3) -proc open(L: var TBaseLexer, input: PStream, bufLen: int = 8192) = +proc open(L: var BaseLexer, input: Stream, bufLen: int = 8192) = assert(bufLen > 0) assert(input != nil) L.input = input @@ -151,10 +153,10 @@ proc open(L: var TBaseLexer, input: PStream, bufLen: int = 8192) = fillBuffer(L) skipUtf8Bom(L) -proc getColNumber(L: TBaseLexer, pos: int): int = +proc getColNumber(L: BaseLexer, pos: int): int = result = abs(pos - L.lineStart) -proc getCurrentLine(L: TBaseLexer, marker: bool = true): string = +proc getCurrentLine(L: BaseLexer, marker: bool = true): string = var i: int result = "" i = L.lineStart diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim index 284384b37..9247f69c5 100644 --- a/lib/pure/logging.nim +++ b/lib/pure/logging.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Andreas Rumpf, Dominik Picheta # # See the file "copying.txt", included in this @@ -26,7 +26,7 @@ ## The following example demonstrates logging to three different handlers ## simultaneously: ## -## .. code-block:: nimrod +## .. code-block:: nim ## ## var L = newConsoleLogger() ## var fL = newFileLogger("test.log", fmtStr = verboseFmtStr) @@ -42,7 +42,7 @@ import strutils, os, times type - TLevel* = enum ## logging level + Level* = enum ## logging level lvlAll, ## all levels active lvlDebug, ## debug level (and any above) active lvlInfo, ## info level (and any above) active @@ -52,7 +52,7 @@ type lvlNone ## no levels active const - LevelNames*: array [TLevel, string] = [ + LevelNames*: array [Level, string] = [ "DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "NONE" ] @@ -60,26 +60,29 @@ const verboseFmtStr* = "$date $time " type - PLogger* = ref object of PObject ## abstract logger; the base type of all loggers - levelThreshold*: TLevel ## only messages of level >= levelThreshold - ## should be processed + Logger* = ref object of RootObj ## abstract logger; the base type of all loggers + levelThreshold*: Level ## only messages of level >= levelThreshold + ## should be processed fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc. - PConsoleLogger* = ref object of PLogger ## logger that writes the messages to the - ## console + ConsoleLogger* = ref object of Logger ## logger that writes the messages to the + ## console - PFileLogger* = ref object of PLogger ## logger that writes the messages to a file - f: TFile + FileLogger* = ref object of Logger ## logger that writes the messages to a file + f: File - PRollingFileLogger* = ref object of PFileLogger ## logger that writes the - ## messages to a file and - ## performs log rotation + RollingFileLogger* = ref object of FileLogger ## logger that writes the + ## messages to a file and + ## performs log rotation maxLines: int # maximum number of lines curLine : int baseName: string # initial filename - baseMode: TFileMode # initial file mode + baseMode: FileMode # initial file mode logFiles: int # how many log files already created, e.g. basename.1, basename.2... +{.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger, + PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].} + proc substituteLog(frmt: string): string = ## converts $date to the current date ## converts $time to the current time @@ -104,21 +107,24 @@ proc substituteLog(frmt: string): string = of "app": result.add(app) of "appdir": result.add(app.splitFile.dir) of "appname": result.add(app.splitFile.name) + else: discard -method log*(logger: PLogger, level: TLevel, - frmt: string, args: varargs[string, `$`]) {.raises: [EBase], tags: [FTime, FWriteIO, FReadIO].} = +method log*(logger: Logger, level: Level, + frmt: string, args: varargs[string, `$`]) {. + raises: [Exception], + tags: [TimeEffect, WriteIOEffect, ReadIOEffect].} = ## Override this method in custom loggers. Default implementation does ## nothing. discard -method log*(logger: PConsoleLogger, level: TLevel, +method log*(logger: ConsoleLogger, level: Level, frmt: string, args: varargs[string, `$`]) = ## Logs to the console using ``logger`` only. if level >= logger.levelThreshold: writeln(stdout, LevelNames[level], " ", substituteLog(logger.fmtStr), frmt % args) -method log*(logger: PFileLogger, level: TLevel, +method log*(logger: FileLogger, level: Level, frmt: string, args: varargs[string, `$`]) = ## Logs to a file using ``logger`` only. if level >= logger.levelThreshold: @@ -130,16 +136,16 @@ proc defaultFilename*(): string = var (path, name, ext) = splitFile(getAppFilename()) result = changeFileExt(path / name, "log") -proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): PConsoleLogger = +proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): ConsoleLogger = ## Creates a new console logger. This logger logs to the console. new result result.fmtStr = fmtStr result.levelThreshold = levelThreshold proc newFileLogger*(filename = defaultFilename(), - mode: TFileMode = fmAppend, + mode: FileMode = fmAppend, levelThreshold = lvlAll, - fmtStr = defaultFmtStr): PFileLogger = + fmtStr = defaultFmtStr): FileLogger = ## Creates a new file logger. This logger logs to a file. new(result) result.levelThreshold = levelThreshold @@ -148,7 +154,7 @@ proc newFileLogger*(filename = defaultFilename(), # ------ -proc countLogLines(logger: PRollingFileLogger): int = +proc countLogLines(logger: RollingFileLogger): int = result = 0 for line in logger.f.lines(): result.inc() @@ -166,13 +172,13 @@ proc countFiles(filename: string): int = let num = parseInt(numS) if num > result: result = num - except EInvalidValue: discard + except ValueError: discard proc newRollingFileLogger*(filename = defaultFilename(), - mode: TFileMode = fmReadWrite, + mode: FileMode = fmReadWrite, levelThreshold = lvlAll, fmtStr = defaultFmtStr, - maxLines = 1000): PRollingFileLogger = + maxLines = 1000): 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. new(result) @@ -190,14 +196,14 @@ proc newRollingFileLogger*(filename = defaultFilename(), # We need to get a line count because we will be appending to the file. result.curLine = countLogLines(result) -proc rotate(logger: PRollingFileLogger) = +proc rotate(logger: RollingFileLogger) = let (dir, name, ext) = splitFile(logger.baseName) for i in countdown(logger.logFiles, 0): let srcSuff = if i != 0: ExtSep & $i else: "" moveFile(dir / (name & ext & srcSuff), dir / (name & ext & ExtSep & $(i+1))) -method log*(logger: PRollingFileLogger, level: TLevel, +method log*(logger: RollingFileLogger, level: Level, frmt: string, args: varargs[string, `$`]) = ## Logs to a file using rolling ``logger`` only. if level >= logger.levelThreshold: @@ -215,20 +221,20 @@ method log*(logger: PRollingFileLogger, level: TLevel, var level* = lvlAll ## global log filter - handlers*: seq[PLogger] = @[] ## handlers with their own log levels + handlers*: seq[Logger] = @[] ## handlers with their own log levels -proc logLoop(level: TLevel, frmt: string, args: varargs[string, `$`]) = +proc logLoop(level: Level, frmt: string, args: varargs[string, `$`]) = for logger in items(handlers): if level >= logger.levelThreshold: log(logger, level, frmt, args) -template log*(level: TLevel, frmt: string, args: varargs[string, `$`]) = +template log*(level: Level, frmt: string, args: varargs[string, `$`]) = ## Logs a message to all registered handlers at the given level. bind logLoop bind `%` - bind logging.Level + bind logging.level - if level >= logging.Level: + if level >= logging.level: logLoop(level, frmt, args) template debug*(frmt: string, args: varargs[string, `$`]) = diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim index 789f6ad76..02abda722 100644 --- a/lib/pure/marshal.nim +++ b/lib/pure/marshal.nim @@ -1,319 +1,319 @@ -# -# -# Nimrod's Runtime Library -# (c) Copyright 2013 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module contains procs for serialization and deseralization of -## arbitrary Nimrod data structures. The serialization format uses JSON. -## -## **Restriction**: For objects their type is **not** serialized. This means -## essentially that it does not work if the object has some other runtime -## type than its compiletime type: -## -## .. code-block:: nimrod -## -## type -## TA = object -## TB = object of TA -## f: int -## -## var -## a: ref TA -## b: ref TB -## -## new(b) -## a = b -## echo($$a[]) # produces "{}", not "{f: 0}" - -import streams, typeinfo, json, intsets, tables - -proc ptrToInt(x: pointer): int {.inline.} = - result = cast[int](x) # don't skip alignment - -proc storeAny(s: PStream, a: TAny, stored: var TIntSet) = - case a.kind - of akNone: assert false - of akBool: s.write($getBool(a)) - of akChar: s.write(escapeJson($getChar(a))) - of akArray, akSequence: - if a.kind == akSequence and isNil(a): s.write("null") - else: - s.write("[") - for i in 0 .. a.len-1: - if i > 0: s.write(", ") - storeAny(s, a[i], stored) - s.write("]") - of akObject, akTuple: - s.write("{") - var i = 0 - for key, val in fields(a): - if i > 0: s.write(", ") - s.write(escapeJson(key)) - s.write(": ") - storeAny(s, val, stored) - inc(i) - s.write("}") - of akSet: - s.write("[") - var i = 0 - for e in elements(a): - if i > 0: s.write(", ") - s.write($e) - inc(i) - s.write("]") - of akRange: storeAny(s, skipRange(a), stored) - of akEnum: s.write(getEnumField(a).escapeJson) - of akPtr, akRef: - var x = a.getPointer - if isNil(x): s.write("null") - elif stored.containsOrIncl(x.ptrToInt): - # already stored, so we simply write out the pointer as an int: - s.write($x.ptrToInt) - else: - # else as a [value, key] pair: - # (reversed order for convenient x[0] access!) - s.write("[") - s.write($x.ptrToInt) - s.write(", ") - storeAny(s, a[], stored) - s.write("]") - of akProc, akPointer, akCString: s.write($a.getPointer.ptrToInt) - of akString: - var x = getString(a) - if IsNil(x): s.write("null") - else: s.write(escapeJson(x)) - of akInt..akInt64, akUInt..akUInt64: s.write($getBiggestInt(a)) - of akFloat..akFloat128: s.write($getBiggestFloat(a)) - -proc loadAny(p: var TJsonParser, a: TAny, t: var TTable[biggestInt, pointer]) = - case a.kind - of akNone: assert false - of akBool: - case p.kind - of jsonFalse: setBiggestInt(a, 0) - of jsonTrue: setBiggestInt(a, 1) - else: raiseParseErr(p, "'true' or 'false' expected for a bool") - next(p) - of akChar: - if p.kind == jsonString: - var x = p.str - if x.len == 1: - setBiggestInt(a, ord(x[0])) - next(p) - return - raiseParseErr(p, "string of length 1 expected for a char") - of akEnum: - if p.kind == jsonString: - setBiggestInt(a, getEnumOrdinal(a, p.str)) - next(p) - return - raiseParseErr(p, "string expected for an enum") - of akArray: - if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for an array") - next(p) - var i = 0 - while p.kind != jsonArrayEnd and p.kind != jsonEof: - loadAny(p, a[i], t) - inc(i) - if p.kind == jsonArrayEnd: next(p) - else: raiseParseErr(p, "']' end of array expected") - of akSequence: - case p.kind - of jsonNull: - setPointer(a, nil) - next(p) - of jsonArrayStart: - next(p) - invokeNewSeq(a, 0) - var i = 0 - while p.kind != jsonArrayEnd and p.kind != jsonEof: - extendSeq(a) - loadAny(p, a[i], t) - inc(i) - if p.kind == jsonArrayEnd: next(p) - else: raiseParseErr(p, "") - else: - raiseParseErr(p, "'[' expected for a seq") - of akObject, akTuple: - if a.kind == akObject: setObjectRuntimeType(a) - if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object") - next(p) - while p.kind != jsonObjectEnd and p.kind != jsonEof: - if p.kind != jsonString: - raiseParseErr(p, "string expected for a field name") - var fieldName = p.str - next(p) - loadAny(p, a[fieldName], t) - if p.kind == jsonObjectEnd: next(p) - else: raiseParseErr(p, "'}' end of object expected") - of akSet: - if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for a set") - next(p) - while p.kind != jsonArrayEnd and p.kind != jsonEof: - if p.kind != jsonInt: raiseParseErr(p, "int expected for a set") - inclSetElement(a, p.getInt.int) - next(p) - if p.kind == jsonArrayEnd: next(p) - else: raiseParseErr(p, "']' end of array expected") - of akPtr, akRef: - case p.kind - of jsonNull: - setPointer(a, nil) - next(p) - of jsonInt: - setPointer(a, t[p.getInt]) - next(p) - of jsonArrayStart: - next(p) - if a.kind == akRef: invokeNew(a) - else: setPointer(a, alloc0(a.baseTypeSize)) - if p.kind == jsonInt: - t[p.getInt] = getPointer(a) - next(p) - else: raiseParseErr(p, "index for ref type expected") - loadAny(p, a[], t) - if p.kind == jsonArrayEnd: next(p) - else: raiseParseErr(p, "']' end of ref-address pair expected") - else: raiseParseErr(p, "int for pointer type expected") - of akProc, akPointer, akCString: - case p.kind - of jsonNull: - setPointer(a, nil) - next(p) - of jsonInt: - setPointer(a, cast[pointer](p.getInt.int)) - next(p) - else: raiseParseErr(p, "int for pointer type expected") - of akString: - case p.kind - of jsonNull: - setPointer(a, nil) - next(p) - of jsonString: - setString(a, p.str) - next(p) - else: raiseParseErr(p, "string expected") - of akInt..akInt64, akUInt..akUInt64: - if p.kind == jsonInt: - setBiggestInt(a, getInt(p)) - next(p) - return - raiseParseErr(p, "int expected") - of akFloat..akFloat128: - if p.kind == jsonFloat: - setBiggestFloat(a, getFloat(p)) - next(p) - return - raiseParseErr(p, "float expected") - of akRange: loadAny(p, a.skipRange, t) - -proc loadAny(s: PStream, a: TAny, t: var TTable[biggestInt, pointer]) = - var p: TJsonParser - open(p, s, "unknown file") - next(p) - loadAny(p, a, t) - close(p) - -proc load*[T](s: PStream, data: var T) = - ## loads `data` from the stream `s`. Raises `EIO` in case of an error. - var tab = initTable[biggestInt, pointer]() - loadAny(s, toAny(data), tab) - -proc store*[T](s: PStream, data: T) = - ## stores `data` into the stream `s`. Raises `EIO` in case of an error. - var stored = initIntSet() - var d: T - shallowCopy(d, data) - storeAny(s, toAny(d), stored) - -proc `$$`*[T](x: T): string = - ## returns a string representation of `x`. - var stored = initIntSet() - var d: T - shallowCopy(d, x) - var s = newStringStream() - storeAny(s, toAny(d), stored) - result = s.data - -proc to*[T](data: string): T = - ## reads data and transforms it to a ``T``. - var tab = initTable[biggestInt, pointer]() - loadAny(newStringStream(data), toAny(result), tab) - -when isMainModule: - template testit(x: expr) = echo($$to[type(x)]($$x)) - - var x: array[0..4, array[0..4, string]] = [ - ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], - ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], - ["test", "1", "2", "3", "4"]] - testit(x) - var test2: tuple[name: string, s: uint] = ("tuple test", 56u) - testit(test2) - - type - TE = enum - blah, blah2 - - TestObj = object - test, asd: int - case test2: TE - of blah: - help: string - else: - nil - - PNode = ref TNode - TNode = object - next, prev: PNode - data: string - - proc buildList(): PNode = - new(result) - new(result.next) - new(result.prev) - result.data = "middle" - result.next.data = "next" - result.prev.data = "prev" - result.next.next = result.prev - result.next.prev = result - result.prev.next = result - result.prev.prev = result.next - - var test3: TestObj - test3.test = 42 - test3.test2 = blah - testit(test3) - - var test4: ref tuple[a, b: string] - new(test4) - test4.a = "ref string test: A" - test4.b = "ref string test: B" - testit(test4) - - var test5 = @[(0,1),(2,3),(4,5)] - testit(test5) - - var test6: set[char] = {'A'..'Z', '_'} - testit(test6) - - var test7 = buildList() - echo($$test7) - testit(test7) - - type - TA {.inheritable.} = object - TB = object of TA - f: int - - var - a: ref TA - b: ref TB - new(b) - a = b - echo($$a[]) # produces "{}", not "{f: 0}" - +# +# +# Nim's Runtime Library +# (c) Copyright 2014 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module contains procs for `serialization`:idx: and `deseralization`:idx: +## of arbitrary Nim data structures. The serialization format uses `JSON`:idx:. +## +## **Restriction**: For objects their type is **not** serialized. This means +## essentially that it does not work if the object has some other runtime +## type than its compiletime type: +## +## .. code-block:: nim +## +## type +## TA = object +## TB = object of TA +## f: int +## +## var +## a: ref TA +## b: ref TB +## +## new(b) +## a = b +## echo($$a[]) # produces "{}", not "{f: 0}" + +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) = + case a.kind + of akNone: assert false + of akBool: s.write($getBool(a)) + of akChar: s.write(escapeJson($getChar(a))) + of akArray, akSequence: + if a.kind == akSequence and isNil(a): s.write("null") + else: + s.write("[") + for i in 0 .. a.len-1: + if i > 0: s.write(", ") + storeAny(s, a[i], stored) + s.write("]") + of akObject, akTuple: + s.write("{") + var i = 0 + for key, val in fields(a): + if i > 0: s.write(", ") + s.write(escapeJson(key)) + s.write(": ") + storeAny(s, val, stored) + inc(i) + s.write("}") + of akSet: + s.write("[") + var i = 0 + for e in elements(a): + if i > 0: s.write(", ") + s.write($e) + inc(i) + s.write("]") + of akRange: storeAny(s, skipRange(a), stored) + of akEnum: s.write(getEnumField(a).escapeJson) + of akPtr, akRef: + var x = a.getPointer + if isNil(x): s.write("null") + elif stored.containsOrIncl(x.ptrToInt): + # already stored, so we simply write out the pointer as an int: + s.write($x.ptrToInt) + else: + # else as a [value, key] pair: + # (reversed order for convenient x[0] access!) + s.write("[") + s.write($x.ptrToInt) + s.write(", ") + storeAny(s, a[], stored) + s.write("]") + of akProc, akPointer, akCString: s.write($a.getPointer.ptrToInt) + of akString: + var x = getString(a) + if isNil(x): s.write("null") + else: s.write(escapeJson(x)) + 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]) = + case a.kind + of akNone: assert false + of akBool: + case p.kind + of jsonFalse: setBiggestInt(a, 0) + of jsonTrue: setBiggestInt(a, 1) + else: raiseParseErr(p, "'true' or 'false' expected for a bool") + next(p) + of akChar: + if p.kind == jsonString: + var x = p.str + if x.len == 1: + setBiggestInt(a, ord(x[0])) + next(p) + return + raiseParseErr(p, "string of length 1 expected for a char") + of akEnum: + if p.kind == jsonString: + setBiggestInt(a, getEnumOrdinal(a, p.str)) + next(p) + return + raiseParseErr(p, "string expected for an enum") + of akArray: + if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for an array") + next(p) + var i = 0 + while p.kind != jsonArrayEnd and p.kind != jsonEof: + loadAny(p, a[i], t) + inc(i) + if p.kind == jsonArrayEnd: next(p) + else: raiseParseErr(p, "']' end of array expected") + of akSequence: + case p.kind + of jsonNull: + setPointer(a, nil) + next(p) + of jsonArrayStart: + next(p) + invokeNewSeq(a, 0) + var i = 0 + while p.kind != jsonArrayEnd and p.kind != jsonEof: + extendSeq(a) + loadAny(p, a[i], t) + inc(i) + if p.kind == jsonArrayEnd: next(p) + else: raiseParseErr(p, "") + else: + raiseParseErr(p, "'[' expected for a seq") + of akObject, akTuple: + if a.kind == akObject: setObjectRuntimeType(a) + if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object") + next(p) + while p.kind != jsonObjectEnd and p.kind != jsonEof: + if p.kind != jsonString: + raiseParseErr(p, "string expected for a field name") + var fieldName = p.str + next(p) + loadAny(p, a[fieldName], t) + if p.kind == jsonObjectEnd: next(p) + else: raiseParseErr(p, "'}' end of object expected") + of akSet: + if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for a set") + next(p) + while p.kind != jsonArrayEnd and p.kind != jsonEof: + if p.kind != jsonInt: raiseParseErr(p, "int expected for a set") + inclSetElement(a, p.getInt.int) + next(p) + if p.kind == jsonArrayEnd: next(p) + else: raiseParseErr(p, "']' end of array expected") + of akPtr, akRef: + case p.kind + of jsonNull: + setPointer(a, nil) + next(p) + of jsonInt: + setPointer(a, t[p.getInt]) + next(p) + of jsonArrayStart: + next(p) + if a.kind == akRef: invokeNew(a) + else: setPointer(a, alloc0(a.baseTypeSize)) + if p.kind == jsonInt: + t[p.getInt] = getPointer(a) + next(p) + else: raiseParseErr(p, "index for ref type expected") + loadAny(p, a[], t) + if p.kind == jsonArrayEnd: next(p) + else: raiseParseErr(p, "']' end of ref-address pair expected") + else: raiseParseErr(p, "int for pointer type expected") + of akProc, akPointer, akCString: + case p.kind + of jsonNull: + setPointer(a, nil) + next(p) + of jsonInt: + setPointer(a, cast[pointer](p.getInt.int)) + next(p) + else: raiseParseErr(p, "int for pointer type expected") + of akString: + case p.kind + of jsonNull: + setPointer(a, nil) + next(p) + of jsonString: + setString(a, p.str) + next(p) + else: raiseParseErr(p, "string expected") + of akInt..akInt64, akUInt..akUInt64: + if p.kind == jsonInt: + setBiggestInt(a, getInt(p)) + next(p) + return + raiseParseErr(p, "int expected") + of akFloat..akFloat128: + if p.kind == jsonFloat: + setBiggestFloat(a, getFloat(p)) + next(p) + return + raiseParseErr(p, "float expected") + of akRange: loadAny(p, a.skipRange, t) + +proc loadAny(s: Stream, a: TAny, t: var Table[BiggestInt, pointer]) = + var p: JsonParser + open(p, s, "unknown file") + next(p) + loadAny(p, a, t) + close(p) + +proc load*[T](s: Stream, data: var T) = + ## loads `data` from the stream `s`. Raises `EIO` in case of an error. + var tab = initTable[BiggestInt, pointer]() + loadAny(s, toAny(data), tab) + +proc store*[T](s: Stream, data: T) = + ## stores `data` into the stream `s`. Raises `EIO` in case of an error. + var stored = initIntSet() + var d: T + shallowCopy(d, data) + storeAny(s, toAny(d), stored) + +proc `$$`*[T](x: T): string = + ## returns a string representation of `x`. + var stored = initIntSet() + var d: T + shallowCopy(d, x) + var s = newStringStream() + storeAny(s, toAny(d), stored) + result = s.data + +proc to*[T](data: string): T = + ## reads data and transforms it to a ``T``. + var tab = initTable[BiggestInt, pointer]() + loadAny(newStringStream(data), toAny(result), tab) + +when isMainModule: + template testit(x: expr) = echo($$to[type(x)]($$x)) + + var x: array[0..4, array[0..4, string]] = [ + ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], + ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], + ["test", "1", "2", "3", "4"]] + testit(x) + var test2: tuple[name: string, s: uint] = ("tuple test", 56u) + testit(test2) + + type + TE = enum + blah, blah2 + + TestObj = object + test, asd: int + case test2: TE + of blah: + help: string + else: + nil + + PNode = ref TNode + TNode = object + next, prev: PNode + data: string + + proc buildList(): PNode = + new(result) + new(result.next) + new(result.prev) + result.data = "middle" + result.next.data = "next" + result.prev.data = "prev" + result.next.next = result.prev + result.next.prev = result + result.prev.next = result + result.prev.prev = result.next + + var test3: TestObj + test3.test = 42 + test3.test2 = blah + testit(test3) + + var test4: ref tuple[a, b: string] + new(test4) + test4.a = "ref string test: A" + test4.b = "ref string test: B" + testit(test4) + + var test5 = @[(0,1),(2,3),(4,5)] + testit(test5) + + var test6: set[char] = {'A'..'Z', '_'} + testit(test6) + + var test7 = buildList() + echo($$test7) + testit(test7) + + type + TA {.inheritable.} = object + TB = object of TA + f: int + + var + a: ref TA + b: ref TB + new(b) + a = b + echo($$a[]) # produces "{}", not "{f: 0}" + diff --git a/lib/pure/matchers.nim b/lib/pure/matchers.nim index 2db7fa660..46fc9985c 100644 --- a/lib/pure/matchers.nim +++ b/lib/pure/matchers.nim @@ -1,7 +1,7 @@ # # -# Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf +# Nim's Runtime Library +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -31,8 +31,8 @@ proc validEmailAddress*(s: string): bool {.noSideEffect, inc(i) if s[i] != '@': return false var j = len(s)-1 - if s[j] notin letters: return false - while j >= i and s[j] in letters: dec(j) + if s[j] notin Letters: return false + while j >= i and s[j] in Letters: dec(j) inc(i) # skip '@' while s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i) if s[i] != '\0': return false @@ -44,7 +44,7 @@ proc validEmailAddress*(s: string): bool {.noSideEffect, "aero", "jobs", "museum": return true return false -proc parseInt*(s: string, value: var int, validRange: TSlice[int]) {. +proc parseInt*(s: string, value: var int, validRange: Slice[int]) {. noSideEffect, rtl, extern: "nmatchParseInt".} = ## parses `s` into an integer in the range `validRange`. If successful, ## `value` is modified to contain the result. Otherwise no exception is @@ -53,7 +53,7 @@ proc parseInt*(s: string, value: var int, validRange: TSlice[int]) {. var x = value try: discard parseutils.parseInt(s, x, 0) - except EOverflow: + except OverflowError: discard if x in validRange: value = x diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 0f6a07f13..987c35d1c 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -1,7 +1,7 @@ # # -# Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf +# Nim's Runtime Library +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -9,12 +9,11 @@ ## Constructive mathematics is naturally typed. -- Simon Thompson ## -## Basic math routines for Nimrod. +## Basic math routines for Nim. ## This module is available for the `JavaScript target ## `_. include "system/inclrtl" -import "impure/fenv" {.push debugger:off .} # the user does not want to trace a part # of the standard library! @@ -30,20 +29,31 @@ const E* = 2.71828182845904523536028747 ## Euler's number MaxFloat64Precision* = 16 ## maximum number of meaningful digits - ## after the decimal point for Nimrod's + ## after the decimal point for Nim's ## ``float64`` type. MaxFloat32Precision* = 8 ## maximum number of meaningful digits - ## after the decimal point for Nimrod's + ## after the decimal point for Nim's ## ``float32`` type. MaxFloatPrecision* = MaxFloat64Precision ## maximum number of ## meaningful digits ## after the decimal point - ## for Nimrod's ``float`` type. + ## for Nim's ``float`` type. -proc classify*(x: float): TFloatClass = +type + FloatClass* = enum ## describes the class a floating point value belongs to. + ## This is the type that is returned by `classify`. + fcNormal, ## value is an ordinary nonzero floating point value + fcSubnormal, ## value is a subnormal (a very small) floating point value + fcZero, ## value is zero + fcNegZero, ## value is the negative zero + fcNan, ## value is Not-A-Number (NAN) + fcInf, ## value is positive infinity + fcNegInf ## value is negative infinity + +proc classify*(x: float): FloatClass = ## classifies a floating point value. Returns `x`'s class as specified by - ## `TFloatClass`. - + ## `FloatClass`. + # JavaScript and most C compilers have no classify: if x == 0.0: if 1.0/x == Inf: @@ -186,7 +196,6 @@ when not defined(JS): ## computes x to power raised of y. # C procs: - proc gettime(dummy: ptr cint): cint {.importc: "time", header: "".} proc srand(seed: cint) {.importc: "srand", header: "".} proc rand(): cint {.importc: "rand", header: "".} @@ -267,21 +276,23 @@ else: proc `mod`*(x, y: float): float = result = if y == 0.0: x else: x - y * (x/y).floor -proc random*[T](x: TSlice[T]): T = +proc random*[T](x: Slice[T]): T = ## For a slice `a .. b` returns a value in the range `a .. b-1`. result = random(x.b - x.a) + x.a -proc random[T](a: openarray[T]): T = +proc random[T](a: openArray[T]): T = ## returns a random element from the openarray `a`. result = a[random(a.low..a.len)] type - TRunningStat* {.pure,final.} = object ## an accumulator for statistical data - n*: int ## number of pushed data - sum*, min*, max*, mean*: float ## self-explaining + RunningStat* = object ## an accumulator for statistical data + n*: int ## number of pushed data + sum*, min*, max*, mean*: float ## self-explaining oldM, oldS, newS: float -proc push*(s: var TRunningStat, x: float) = +{.deprecated: [TFloatClass: FloatClass, TRunningStat: RunningStat].} + +proc push*(s: var RunningStat, x: float) = ## pushes a value `x` for processing inc(s.n) # See Knuth TAOCP vol 2, 3rd edition, page 232 @@ -302,16 +313,16 @@ proc push*(s: var TRunningStat, x: float) = s.oldS = s.newS s.sum = s.sum + x -proc push*(s: var TRunningStat, x: int) = +proc push*(s: var RunningStat, x: int) = ## pushes a value `x` for processing. `x` is simply converted to ``float`` ## and the other push operation is called. push(s, toFloat(x)) -proc variance*(s: TRunningStat): float = +proc variance*(s: RunningStat): float = ## computes the current variance of `s` if s.n > 1: result = s.newS / (toFloat(s.n - 1)) -proc standardDeviation*(s: TRunningStat): float = +proc standardDeviation*(s: RunningStat): float = ## computes the current standard deviation of `s` result = sqrt(variance(s)) @@ -319,6 +330,8 @@ proc standardDeviation*(s: TRunningStat): float = {.pop.} when isMainModule and not defined(JS): + proc gettime(dummy: ptr cint): cint {.importc: "time", header: "".} + # Verifies random seed initialization. let seed = gettime(nil) randomize(seed) diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim index 0328932fd..3b5453957 100644 --- a/lib/pure/md5.nim +++ b/lib/pure/md5.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2010 Andreas Rumpf # # See the file "copying.txt", included in this @@ -84,14 +84,14 @@ proc decode(dest: var openArray[int8], src: openArray[int32]) = dest[i+3] = toU8(src[j] shr 24'i32 and 0xff'i32) inc(i, 4) -proc transform(Buffer: pointer, State: var MD5State) = +proc transform(buffer: pointer, state: var MD5State) = var myBlock: MD5Block - encode(myBlock, cast[cstring](Buffer)) - var a = State[0] - var b = State[1] - var c = State[2] - var d = State[3] + encode(myBlock, cast[cstring](buffer)) + var a = state[0] + var b = state[1] + var c = state[2] + var d = state[3] FF(a, b, c, d, myBlock[0], 7'i8, 0xD76AA478'i32) FF(d, a, b, c, myBlock[1], 12'i8, 0xE8C7B756'i32) FF(c, d, a, b, myBlock[2], 17'i8, 0x242070DB'i32) @@ -156,52 +156,52 @@ proc transform(Buffer: pointer, State: var MD5State) = II(d, a, b, c, myBlock[11], 10'i8, 0xBD3AF235'i32) II(c, d, a, b, myBlock[2], 15'i8, 0x2AD7D2BB'i32) II(b, c, d, a, myBlock[9], 21'i8, 0xEB86D391'i32) - State[0] = State[0] +% a - State[1] = State[1] +% b - State[2] = State[2] +% c - State[3] = State[3] +% d + state[0] = state[0] +% a + state[1] = state[1] +% b + state[2] = state[2] +% c + state[3] = state[3] +% d proc md5Init*(c: var MD5Context) = ## initializes a MD5Context - c.State[0] = 0x67452301'i32 - c.State[1] = 0xEFCDAB89'i32 - c.State[2] = 0x98BADCFE'i32 - c.State[3] = 0x10325476'i32 - c.Count[0] = 0'i32 - c.Count[1] = 0'i32 - zeroMem(addr(c.Buffer), sizeof(MD5Buffer)) + c.state[0] = 0x67452301'i32 + c.state[1] = 0xEFCDAB89'i32 + c.state[2] = 0x98BADCFE'i32 + c.state[3] = 0x10325476'i32 + c.count[0] = 0'i32 + c.count[1] = 0'i32 + zeroMem(addr(c.buffer), sizeof(MD5buffer)) proc md5Update*(c: var MD5Context, input: cstring, len: int) = ## updates the MD5Context with the `input` data of length `len` var input = input var Index = (c.count[0] shr 3) and 0x3F - c.Count[0] = c.count[0] +% toU32(len shl 3) - if c.Count[0] < (len shl 3): c.Count[1] = c.count[1] +% 1'i32 - c.Count[1] = c.count[1] +% toU32(len shr 29) + c.count[0] = c.count[0] +% toU32(len shl 3) + if c.count[0] < (len shl 3): c.count[1] = c.count[1] +% 1'i32 + c.count[1] = c.count[1] +% toU32(len shr 29) var PartLen = 64 - Index if len >= PartLen: - copyMem(addr(c.Buffer[Index]), input, PartLen) - transform(addr(c.Buffer), c.State) + copyMem(addr(c.buffer[Index]), input, PartLen) + transform(addr(c.buffer), c.state) var i = PartLen while i + 63 < len: - transform(addr(input[i]), c.State) + transform(addr(input[i]), c.state) inc(i, 64) - copyMem(addr(c.Buffer[0]), addr(input[i]), len-i) + copyMem(addr(c.buffer[0]), addr(input[i]), len-i) else: - copyMem(addr(c.Buffer[Index]), addr(input[0]), len) + copyMem(addr(c.buffer[Index]), addr(input[0]), len) proc md5Final*(c: var MD5Context, digest: var MD5Digest) = ## finishes the MD5Context and stores the result in `digest` var Bits: MD5CBits PadLen: int - decode(Bits, c.Count) - var Index = (c.Count[0] shr 3) and 0x3F + decode(Bits, c.count) + var Index = (c.count[0] shr 3) and 0x3F if Index < 56: PadLen = 56 - Index else: PadLen = 120 - Index md5Update(c, padding, PadLen) md5Update(c, cast[cstring](addr(Bits)), 8) - decode(digest, c.State) + decode(digest, c.state) zeroMem(addr(c), sizeof(MD5Context)) proc toMD5*(s: string): MD5Digest = @@ -216,8 +216,8 @@ proc `$`*(D: MD5Digest): string = const digits = "0123456789abcdef" result = "" for i in 0..15: - add(result, Digits[(D[i] shr 4) and 0xF]) - add(result, Digits[D[i] and 0xF]) + add(result, digits[(D[i] shr 4) and 0xF]) + add(result, digits[D[i] and 0xF]) proc getMD5*(s: string): string = ## computes an MD5 value of `s` and returns its string representation diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index ffeb0beff..24dbfb6d3 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -1,7 +1,7 @@ # # -# Nimrod's Runtime Library -# (c) Copyright 2014 Nimrod Contributors +# Nim's Runtime Library +# (c) Copyright 2014 Nim Contributors # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -22,7 +22,7 @@ else: import os type - TMemFile* = object {.pure.} ## represents a memory mapped file + MemFile* = object ## represents a memory mapped file mem*: pointer ## a pointer to the memory mapped file. The pointer ## can be used directly to change the contents of the ## file, if it was opened with write access. @@ -34,8 +34,9 @@ type else: handle: cint +{.deprecated: [TMemFile: MemFile].} -proc mapMem*(m: var TMemFile, mode: TFileMode = fmRead, +proc mapMem*(m: var MemFile, mode: FileMode = fmRead, mappedSize = -1, offset = 0): pointer = var readonly = mode == fmRead when defined(windows): @@ -47,7 +48,7 @@ proc mapMem*(m: var TMemFile, mode: TFileMode = fmRead, if mappedSize == -1: 0 else: mappedSize, nil) if result == nil: - osError(osLastError()) + raiseOSError(osLastError()) else: assert mappedSize > 0 result = mmap( @@ -57,30 +58,30 @@ proc mapMem*(m: var TMemFile, mode: TFileMode = fmRead, if readonly: (MAP_PRIVATE or MAP_POPULATE) else: (MAP_SHARED or MAP_POPULATE), m.handle, offset) if result == cast[pointer](MAP_FAILED): - osError(osLastError()) + raiseOSError(osLastError()) -proc unmapMem*(f: var TMemFile, p: pointer, size: int) = +proc unmapMem*(f: var MemFile, p: pointer, size: int) = ## unmaps the memory region ``(p, socket.bufLen-1: @@ -680,9 +720,9 @@ proc peekChar(socket: PSocket, c: var char): int {.tags: [FReadIO].} = return result = recv(socket.fd, addr(c), 1, MSG_PEEK) -proc readLine*(socket: PSocket, line: var TaintedString, timeout = -1, - flags = {TSocketFlags.SafeDisconn}) {. - tags: [FReadIO, FTime].} = +proc readLine*(socket: Socket, line: var TaintedString, timeout = -1, + flags = {SocketFlag.SafeDisconn}) {. + tags: [ReadIOEffect, TimeEffect].} = ## Reads a line of data from ``socket``. ## ## If a full line is read ``\r\L`` is not @@ -729,9 +769,9 @@ proc readLine*(socket: PSocket, line: var TaintedString, timeout = -1, return add(line.string, c) -proc recvFrom*(socket: PSocket, data: var string, length: int, - address: var string, port: var TPort, flags = 0'i32): int {. - tags: [FReadIO].} = +proc recvFrom*(socket: Socket, data: var string, length: int, + address: var string, port: var Port, flags = 0'i32): int {. + tags: [ReadIOEffect].} = ## Receives data from ``socket``. This function should normally be used with ## connection-less sockets (UDP sockets). ## @@ -745,19 +785,19 @@ proc recvFrom*(socket: PSocket, data: var string, length: int, # TODO: Buffered sockets data.setLen(length) - var sockAddress: Tsockaddr_in - var addrLen = sizeof(sockAddress).TSocklen + var sockAddress: Sockaddr_in + var addrLen = sizeof(sockAddress).SockLen result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint, - cast[ptr TSockAddr](addr(sockAddress)), addr(addrLen)) + cast[ptr SockAddr](addr(sockAddress)), addr(addrLen)) if result != -1: data.setLen(result) address = $inet_ntoa(sockAddress.sin_addr) - port = ntohs(sockAddress.sin_port).TPort + port = ntohs(sockAddress.sin_port).Port else: - osError(osLastError()) + raiseOSError(osLastError()) -proc skip*(socket: PSocket, size: int, timeout = -1) = +proc skip*(socket: Socket, size: int, timeout = -1) = ## Skips ``size`` amount of bytes. ## ## An optional timeout can be specified in miliseconds, if skipping the @@ -772,8 +812,8 @@ proc skip*(socket: PSocket, size: int, timeout = -1) = bytesSkipped += recv(socket, dummy, avail) dealloc(dummy) -proc send*(socket: PSocket, data: pointer, size: int): int {. - tags: [FWriteIO].} = +proc send*(socket: Socket, data: pointer, size: int): int {. + tags: [WriteIOEffect].} = ## Sends data to a socket. ## ## **Note**: This is a low-level version of ``send``. You likely should use @@ -789,8 +829,8 @@ proc send*(socket: PSocket, data: pointer, size: int): int {. const MSG_NOSIGNAL = 0 result = send(socket.fd, data, size, int32(MSG_NOSIGNAL)) -proc send*(socket: PSocket, data: string, - flags = {TSocketFlags.SafeDisconn}) {.tags: [FWriteIO].} = +proc send*(socket: Socket, data: string, + flags = {SocketFlag.SafeDisconn}) {.tags: [WriteIOEffect].} = ## sends data to a socket. let sent = send(socket, cstring(data), data.len) if sent < 0: @@ -799,16 +839,16 @@ proc send*(socket: PSocket, data: string, socketError(socket, lastError = lastError) if sent != data.len: - raise newException(EOS, "Could not send all data.") + raise newException(OSError, "Could not send all data.") -proc trySend*(socket: PSocket, data: string): bool {.tags: [FWriteIO].} = +proc trySend*(socket: Socket, data: string): bool {.tags: [WriteIOEffect].} = ## Safe alternative to ``send``. Does not raise an EOS when an error occurs, ## and instead returns ``false`` on failure. result = send(socket, cstring(data), data.len) == data.len -proc sendTo*(socket: PSocket, address: string, port: TPort, data: pointer, - size: int, af: TDomain = AF_INET, flags = 0'i32): int {. - tags: [FWriteIO].} = +proc sendTo*(socket: Socket, address: string, port: Port, data: pointer, + size: int, af: Domain = AF_INET, flags = 0'i32): int {. + tags: [WriteIOEffect].} = ## This proc sends ``data`` to the specified ``address``, ## which may be an IP address or a hostname, if a hostname is specified ## this function will try each IP of that hostname. @@ -825,7 +865,7 @@ proc sendTo*(socket: PSocket, address: string, port: TPort, data: pointer, var it = aiList while it != nil: result = sendto(socket.fd, data, size.cint, flags.cint, it.ai_addr, - it.ai_addrlen.TSocklen) + it.ai_addrlen.SockLen) if result != -1'i32: success = true break @@ -833,8 +873,8 @@ proc sendTo*(socket: PSocket, address: string, port: TPort, data: pointer, dealloc(aiList) -proc sendTo*(socket: PSocket, address: string, port: TPort, - data: string): int {.tags: [FWriteIO].} = +proc sendTo*(socket: Socket, address: string, port: Port, + data: string): int {.tags: [WriteIOEffect].} = ## This proc sends ``data`` to the specified ``address``, ## which may be an IP address or a hostname, if a hostname is specified ## this function will try each IP of that hostname. @@ -842,8 +882,8 @@ proc sendTo*(socket: PSocket, address: string, port: TPort, ## This is the high-level version of the above ``sendTo`` function. result = socket.sendTo(address, port, cstring(data), data.len) -proc connectAsync(socket: PSocket, name: string, port = TPort(0), - af: TDomain = AF_INET) {.tags: [FReadIO].} = +proc connectAsync(socket: Socket, name: string, port = Port(0), + af: Domain = AF_INET) {.tags: [ReadIOEffect].} = ## A variant of ``connect`` for non-blocking sockets. ## ## This procedure will immediatelly return, it will not block until a connection @@ -855,10 +895,10 @@ proc connectAsync(socket: PSocket, name: string, port = TPort(0), var aiList = getAddrInfo(name, port, af) # try all possibilities: var success = false - var lastError: TOSErrorCode + var lastError: OSErrorCode var it = aiList while it != nil: - var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen) + var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.SockLen) if ret == 0'i32: success = true break @@ -877,10 +917,10 @@ proc connectAsync(socket: PSocket, name: string, port = TPort(0), it = it.ai_next dealloc(aiList) - if not success: osError(lastError) + if not success: raiseOSError(lastError) -proc connect*(socket: PSocket, address: string, port = TPort(0), timeout: int, - af: TDomain = AF_INET) {.tags: [FReadIO, FWriteIO].} = +proc connect*(socket: Socket, address: string, port = Port(0), timeout: int, + af: Domain = AF_INET) {.tags: [ReadIOEffect, WriteIOEffect].} = ## Connects to server as specified by ``address`` on port specified by ``port``. ## ## The ``timeout`` paremeter specifies the time in miliseconds to allow for @@ -890,7 +930,7 @@ proc connect*(socket: PSocket, address: string, port = TPort(0), timeout: int, socket.connectAsync(address, port, af) var s = @[socket.fd] if selectWrite(s, timeout) != 1: - raise newException(ETimeout, "Call to 'connect' timed out.") + raise newException(TimeoutError, "Call to 'connect' timed out.") else: when defined(ssl): if socket.isSSL: @@ -898,10 +938,10 @@ proc connect*(socket: PSocket, address: string, port = TPort(0), timeout: int, doAssert socket.handshake() socket.fd.setBlocking(true) -proc isSSL*(socket: PSocket): bool = return socket.isSSL +proc isSsl*(socket: Socket): bool = return socket.isSSL ## Determines whether ``socket`` is a SSL socket. -proc getFD*(socket: PSocket): TSocketHandle = return socket.fd +proc getFd*(socket: Socket): SocketHandle = return socket.fd ## Returns the socket's file descriptor type @@ -1036,23 +1076,23 @@ proc parseIPv4Address(address_str: string): TIpAddress = currentByte = currentByte * 10 + cast[uint16](ord(address_str[i]) - ord('0')) if currentByte > 255'u16: - raise newException(EInvalidValue, + raise newException(ValueError, "Invalid IP Address. Value is out of range") seperatorValid = true elif address_str[i] == '.': # IPv4 address separator if not seperatorValid or byteCount >= 3: - raise newException(EInvalidValue, + raise newException(ValueError, "Invalid IP Address. The address consists of too many groups") result.address_v4[byteCount] = cast[uint8](currentByte) currentByte = 0 byteCount.inc seperatorValid = false else: - raise newException(EInvalidValue, + raise newException(ValueError, "Invalid IP Address. Address contains an invalid character") if byteCount != 3 or not seperatorValid: - raise newException(EInvalidValue, "Invalid IP Address") + raise newException(ValueError, "Invalid IP Address") result.address_v4[byteCount] = cast[uint8](currentByte) proc parseIPv6Address(address_str: string): TIpAddress = @@ -1060,7 +1100,7 @@ proc parseIPv6Address(address_str: string): TIpAddress = ## Raises EInvalidValue on errors result.family = IpAddressFamily.IPv6 if address_str.len < 2: - raise newException(EInvalidValue, "Invalid IP Address") + raise newException(ValueError, "Invalid IP Address") var groupCount = 0 @@ -1075,17 +1115,17 @@ proc parseIPv6Address(address_str: string): TIpAddress = for i,c in address_str: if c == ':': if not seperatorValid: - raise newException(EInvalidValue, + raise newException(ValueError, "Invalid IP Address. Address contains an invalid seperator") if lastWasColon: if dualColonGroup != -1: - raise newException(EInvalidValue, + raise newException(ValueError, "Invalid IP Address. Address contains more than one \"::\" seperator") dualColonGroup = groupCount seperatorValid = false elif i != 0 and i != high(address_str): if groupCount >= 8: - raise newException(EInvalidValue, + raise newException(ValueError, "Invalid IP Address. The address consists of too many groups") result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) @@ -1094,17 +1134,17 @@ proc parseIPv6Address(address_str: string): TIpAddress = if dualColonGroup != -1: seperatorValid = false elif i == 0: # only valid if address starts with :: if address_str[1] != ':': - raise newException(EInvalidValue, + raise newException(ValueError, "Invalid IP Address. Address may not start with \":\"") else: # i == high(address_str) - only valid if address ends with :: if address_str[high(address_str)-1] != ':': - raise newException(EInvalidValue, + raise newException(ValueError, "Invalid IP Address. Address may not end with \":\"") lastWasColon = true currentGroupStart = i + 1 elif c == '.': # Switch to parse IPv4 mode if i < 3 or not seperatorValid or groupCount >= 7: - raise newException(EInvalidValue, "Invalid IP Address") + raise newException(ValueError, "Invalid IP Address") v4StartPos = currentGroupStart currentShort = 0 seperatorValid = false @@ -1117,19 +1157,19 @@ proc parseIPv6Address(address_str: string): TIpAddress = else: # Upper case hex currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10 if currentShort > 65535'u32: - raise newException(EInvalidValue, + raise newException(ValueError, "Invalid IP Address. Value is out of range") lastWasColon = false seperatorValid = true else: - raise newException(EInvalidValue, + raise newException(ValueError, "Invalid IP Address. Address contains an invalid character") if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff if seperatorValid: # Copy remaining data if groupCount >= 8: - raise newException(EInvalidValue, + raise newException(ValueError, "Invalid IP Address. The address consists of too many groups") result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) @@ -1139,32 +1179,32 @@ proc parseIPv6Address(address_str: string): TIpAddress = if c in strutils.Digits: # Character is a number currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0')) if currentShort > 255'u32: - raise newException(EInvalidValue, + raise newException(ValueError, "Invalid IP Address. Value is out of range") seperatorValid = true elif c == '.': # IPv4 address separator if not seperatorValid or byteCount >= 3: - raise newException(EInvalidValue, "Invalid IP Address") + raise newException(ValueError, "Invalid IP Address") result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort) currentShort = 0 byteCount.inc() seperatorValid = false else: # Invalid character - raise newException(EInvalidValue, + raise newException(ValueError, "Invalid IP Address. Address contains an invalid character") if byteCount != 3 or not seperatorValid: - raise newException(EInvalidValue, "Invalid IP Address") + raise newException(ValueError, "Invalid IP Address") result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort) groupCount += 2 # Shift and fill zeros in case of :: if groupCount > 8: - raise newException(EInvalidValue, + raise newException(ValueError, "Invalid IP Address. The address consists of too many groups") elif groupCount < 8: # must fill if dualColonGroup == -1: - raise newException(EInvalidValue, + raise newException(ValueError, "Invalid IP Address. The address consists of too few groups") var toFill = 8 - groupCount # The number of groups to fill var toShift = groupCount - dualColonGroup # Nr of known groups after :: @@ -1173,14 +1213,14 @@ proc parseIPv6Address(address_str: string): TIpAddress = for i in 0..2*toFill-1: # fill with 0s result.address_v6[dualColonGroup*2+i] = 0 elif dualColonGroup != -1: - raise newException(EInvalidValue, + raise newException(ValueError, "Invalid IP Address. The address consists of too many groups") proc parseIpAddress*(address_str: string): TIpAddress = ## Parses an IP address ## Raises EInvalidValue on error if address_str == nil: - raise newException(EInvalidValue, "IP Address string is nil") + raise newException(ValueError, "IP Address string is nil") if address_str.contains(':'): return parseIPv6Address(address_str) else: diff --git a/lib/pure/nimprof.nim b/lib/pure/nimprof.nim index 6f94d0656..3598cdd3a 100644 --- a/lib/pure/nimprof.nim +++ b/lib/pure/nimprof.nim @@ -1,13 +1,13 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## Profiling support for Nimrod. This is an embedded profiler that requires +## Profiling support for Nim. This is an embedded profiler that requires ## ``--profiler:on``. You only need to import this module to get a profiling ## report at program exit. @@ -64,7 +64,7 @@ when withThreads: var profilingLock: TLock - InitLock profilingLock + initLock profilingLock proc hookAux(st: TStackTrace, costs: int) = # this is quite performance sensitive! @@ -132,9 +132,9 @@ else: proc hook(st: TStackTrace) {.nimcall.} = if interval == 0: hookAux(st, 1) - elif getticks() - t0 > interval: + elif getTicks() - t0 > interval: hookAux(st, 1) - t0 = getticks() + t0 = getTicks() proc getTotal(x: ptr TProfileEntry): int = result = if isNil(x): 0 else: x.total @@ -150,7 +150,7 @@ proc writeProfile() {.noconv.} = system.profilerHook = nil const filename = "profile_results.txt" echo "writing " & filename & "..." - var f: TFile + var f: File if open(f, filename, fmWrite): sort(profileData, cmpEntries) writeln(f, "total executions of each stack trace:") diff --git a/lib/pure/numeric.nim b/lib/pure/numeric.nim index 8ef5fabda..9b298c0a0 100644 --- a/lib/pure/numeric.nim +++ b/lib/pure/numeric.nim @@ -1,16 +1,17 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Robert Persson # # See the file "copying.txt", included in this # distribution, for details about the copyright. # +type OneVarFunction* = proc (x: float): float -type TOneVarFunction* =proc (x:float):float +{.deprecated: [TOneVarFunction: OneVarFunction].} -proc brent*(xmin,xmax:float ,function:TOneVarFunction, tol:float,maxiter=1000): +proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000): tuple[rootx, rooty: float, success: bool]= ## Searches `function` for a root between `xmin` and `xmax` ## using brents method. If the function value at `xmin`and `xmax` has the diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim index 2843e6c65..0dc8e3c15 100644 --- a/lib/pure/oids.nim +++ b/lib/pure/oids.nim @@ -1,13 +1,13 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## Nimrod OID support. An OID is a global ID that consists of a timestamp, +## Nim OID support. An OID is a global ID that consists of a timestamp, ## a unique counter and a random value. This combination should suffice to ## produce a globally distributed unique ID. This implementation was extracted ## from the Mongodb interface and it thus binary compatible with a Mongo OID. @@ -18,11 +18,13 @@ import times, endians type - Toid* {.pure, final.} = object ## an OID + Oid* = object ## an OID time: int32 ## fuzz: int32 ## count: int32 ## +{.deprecated: [Toid: Oid].} + proc hexbyte*(hex: char): int = case hex of '0'..'9': result = (ord(hex) - ord('0')) @@ -30,7 +32,7 @@ proc hexbyte*(hex: char): int = of 'A'..'F': result = (ord(hex) - ord('A') + 10) else: discard -proc parseOid*(str: cstring): TOid = +proc parseOid*(str: cstring): Oid = ## parses an OID. var bytes = cast[cstring](addr(result.time)) var i = 0 @@ -38,7 +40,7 @@ proc parseOid*(str: cstring): TOid = bytes[i] = chr((hexbyte(str[2 * i]) shl 4) or hexbyte(str[2 * i + 1])) inc(i) -proc oidToString*(oid: TOid, str: cstring) = +proc oidToString*(oid: Oid, str: cstring) = const hex = "0123456789abcdef" # work around a compiler bug: var str = str @@ -52,15 +54,15 @@ proc oidToString*(oid: TOid, str: cstring) = inc(i) str[24] = '\0' -proc `$`*(oid: TOid): string = - result = newString(25) +proc `$`*(oid: Oid): string = + result = newString(24) oidToString(oid, result) var incr: int fuzz: int32 -proc genOid*(): TOid = +proc genOid*(): Oid = ## generates a new OID. proc rand(): cint {.importc: "rand", header: "", nodecl.} proc gettime(dummy: ptr cint): cint {.importc: "time", header: "".} @@ -79,13 +81,13 @@ proc genOid*(): TOid = result.fuzz = fuzz bigEndian32(addr result.count, addr(i)) -proc generatedTime*(oid: TOid): TTime = +proc generatedTime*(oid: Oid): Time = ## returns the generated timestamp of the OID. var tmp: int32 var dummy = oid.time bigEndian32(addr(tmp), addr(dummy)) - result = TTime(tmp) + result = Time(tmp) when isMainModule: - let xo = genOID() + let xo = genOid() echo xo.generatedTime diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 71089494f..ad82dc682 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -29,18 +29,23 @@ else: include "system/ansi_c" type - FReadEnv* = object of FReadIO ## effect that denotes a read - ## from an environment variable - FWriteEnv* = object of FWriteIO ## effect that denotes a write - ## to an environment variable + ReadEnvEffect* = object of ReadIOEffect ## effect that denotes a read + ## from an environment variable + WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write + ## to an environment variable - FReadDir* = object of FReadIO ## effect that denotes a write operation to - ## the directory structure - FWriteDir* = object of FWriteIO ## effect that denotes a write operation to - ## the directory structure + ReadDirEffect* = object of ReadIOEffect ## effect that denotes a write + ## operation to the directory structure + WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write operation to + ## the directory structure - TOSErrorCode* = distinct int32 ## Specifies an OS Error Code. + OSErrorCode* = distinct int32 ## Specifies an OS Error Code. +{.deprecated: [FReadEnv: ReadEnvEffect, FWriteEnv: WriteEnvEffect, + FReadDir: ReadDirEffect, + FWriteDir: WriteDirEffect, + TOSErrorCode: OSErrorCode +].} const doslike = defined(windows) or defined(OS2) or defined(DOS) # DOS-like filesystem @@ -75,7 +80,7 @@ when defined(Nimdoc): # only for proper documentation: ## search patch components (as in PATH), such as ':' for POSIX or ';' for ## Windows. - FileSystemCaseSensitive* = True + FileSystemCaseSensitive* = true ## True if the file system is case sensitive, false otherwise. Used by ## `cmpPaths` to compare filenames properly. @@ -182,7 +187,7 @@ proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} = ## On Windows ``GetLastError`` is checked before ``errno``. ## Returns "" if no error occured. ## - ## **Deprecated since version 0.9.4**: use the other ``OSErrorMsg`` proc. + ## **Deprecated since version 0.9.4**: use the other ``osErrorMsg`` proc. result = "" when defined(Windows): @@ -204,28 +209,32 @@ proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} = result = $os.strerror(errno) {.push warning[deprecated]: off.} -proc osError*(msg: string = "") {.noinline, rtl, extern: "nos$1", deprecated.} = - ## raises an EOS exception with the given message ``msg``. +proc raiseOSError*(msg: string = "") {.noinline, rtl, extern: "nos$1", + deprecated.} = + ## raises an OSError exception with the given message ``msg``. ## If ``msg == ""``, the operating system's error flag ## (``errno``) is converted to a readable error message. On Windows ## ``GetLastError`` is checked before ``errno``. ## If no error flag is set, the message ``unknown OS error`` is used. ## - ## **Deprecated since version 0.9.4**: use the other ``OSError`` proc. + ## **Deprecated since version 0.9.4**: use the other ``raiseOSError`` proc. if len(msg) == 0: var m = osErrorMsg() - raise newException(EOS, if m.len > 0: m else: "unknown OS error") + raise newException(OSError, if m.len > 0: m else: "unknown OS error") else: - raise newException(EOS, msg) + raise newException(OSError, msg) {.pop.} -proc `==`*(err1, err2: TOSErrorCode): bool {.borrow.} -proc `$`*(err: TOSErrorCode): string {.borrow.} +when not defined(nimfix): + {.deprecated: [osError: raiseOSError].} -proc osErrorMsg*(errorCode: TOSErrorCode): string = +proc `==`*(err1, err2: OSErrorCode): bool {.borrow.} +proc `$`*(err: OSErrorCode): string {.borrow.} + +proc osErrorMsg*(errorCode: OSErrorCode): string = ## Converts an OS error code into a human readable string. ## - ## The error code can be retrieved using the ``OSLastError`` proc. + ## The error code can be retrieved using the ``osLastError`` proc. ## ## If conversion fails, or ``errorCode`` is ``0`` then ``""`` will be ## returned. @@ -235,7 +244,7 @@ proc osErrorMsg*(errorCode: TOSErrorCode): string = ## message. result = "" when defined(Windows): - if errorCode != TOSErrorCode(0'i32): + if errorCode != OSErrorCode(0'i32): when useWinUnicode: var msgbuf: WideCString if formatMessageW(0x00000100 or 0x00001000 or 0x00000200, @@ -249,18 +258,18 @@ proc osErrorMsg*(errorCode: TOSErrorCode): string = result = $msgbuf if msgbuf != nil: localFree(msgbuf) else: - if errorCode != TOSErrorCode(0'i32): + if errorCode != OSErrorCode(0'i32): result = $os.strerror(errorCode.int32) -proc osError*(errorCode: TOSErrorCode) = - ## Raises an ``EOS`` exception. The ``errorCode`` will determine the - ## message, ``OSErrorMsg`` will be used to get this message. +proc raiseOSError*(errorCode: OSErrorCode) = + ## Raises an ``OSError`` exception. The ``errorCode`` will determine the + ## message, ``osErrorMsg`` will be used to get this message. ## - ## The error code can be retrieved using the ``OSLastError`` proc. + ## The error code can be retrieved using the ``osLastError`` proc. ## ## If the error code is ``0`` or an error message could not be retrieved, ## the message ``unknown OS error`` will be used. - var e: ref EOS; new(e) + var e: ref OSError; new(e) e.errorCode = errorCode.int32 e.msg = osErrorMsg(errorCode) if e.msg == "": @@ -268,7 +277,7 @@ proc osError*(errorCode: TOSErrorCode) = raise e {.push stackTrace:off.} -proc osLastError*(): TOSErrorCode = +proc osLastError*(): OSErrorCode = ## Retrieves the last operating system error code. ## ## This procedure is useful in the event when an OS call fails. In that case @@ -283,9 +292,9 @@ proc osLastError*(): TOSErrorCode = ## immediately after an OS call fails. On POSIX systems this is not a problem. when defined(windows): - result = TOSErrorCode(getLastError()) + result = OSErrorCode(getLastError()) else: - result = TOSErrorCode(errno) + result = OSErrorCode(errno) {.pop.} proc unixToNativePath*(path: string, drive=""): string {. @@ -371,7 +380,7 @@ when defined(windows): f.cFileName[1].int == dot and f.cFileName[2].int == 0) proc existsFile*(filename: string): bool {.rtl, extern: "nos$1", - tags: [FReadDir].} = + tags: [ReadDirEffect].} = ## Returns true if the file exists, false otherwise. when defined(windows): when useWinUnicode: @@ -384,7 +393,7 @@ proc existsFile*(filename: string): bool {.rtl, extern: "nos$1", var res: TStat return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode) -proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [FReadDir].} = +proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect].} = ## Returns true iff the directory `dir` exists. If `dir` is a file, false ## is returned. when defined(windows): @@ -399,7 +408,7 @@ proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [FReadDir].} = return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode) proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", - tags: [FReadDir].} = + tags: [ReadDirEffect].} = ## Returns true iff the symlink `link` exists. Will return true ## regardless of whether the link points to a directory or file. when defined(windows): @@ -421,51 +430,55 @@ proc dirExists*(dir: string): bool {.inline.} = ## Synonym for existsDir existsDir(dir) -proc getLastModificationTime*(file: string): TTime {.rtl, extern: "nos$1".} = +proc getLastModificationTime*(file: string): Time {.rtl, extern: "nos$1".} = ## Returns the `file`'s last modification time. when defined(posix): var res: TStat - if stat(file, res) < 0'i32: osError(osLastError()) + if stat(file, res) < 0'i32: raiseOSError(osLastError()) return res.st_mtime else: var f: TWIN32_FIND_DATA var h = findFirstFile(file, f) - if h == -1'i32: osError(osLastError()) + if h == -1'i32: raiseOSError(osLastError()) result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime)) findClose(h) -proc getLastAccessTime*(file: string): TTime {.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 - if stat(file, res) < 0'i32: osError(osLastError()) + if stat(file, res) < 0'i32: raiseOSError(osLastError()) return res.st_atime else: var f: TWIN32_FIND_DATA var h = findFirstFile(file, f) - if h == -1'i32: osError(osLastError()) + if h == -1'i32: raiseOSError(osLastError()) result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime)) findClose(h) -proc getCreationTime*(file: string): TTime {.rtl, extern: "nos$1".} = +proc getCreationTime*(file: string): Time {.rtl, extern: "nos$1".} = ## Returns the `file`'s creation time. ## 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 - if stat(file, res) < 0'i32: osError(osLastError()) + if stat(file, res) < 0'i32: raiseOSError(osLastError()) return res.st_ctime else: var f: TWIN32_FIND_DATA var h = findFirstFile(file, f) - if h == -1'i32: osError(osLastError()) + if h == -1'i32: raiseOSError(osLastError()) result = winTimeToUnixTime(rdFileTime(f.ftCreationTime)) findClose(h) proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} = ## Returns true if the file `a` is newer than file `b`, i.e. if `a`'s ## modification time is later than `b`'s. - result = getLastModificationTime(a) - getLastModificationTime(b) > 0 + when defined(posix): + result = getLastModificationTime(a) - getLastModificationTime(b) >= 0 + # Posix's resolution sucks so, we use '>=' for posix. + else: + result = getLastModificationTime(a) - getLastModificationTime(b) > 0 proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} = ## Returns the `current working directory`:idx:. @@ -474,31 +487,31 @@ proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} = when useWinUnicode: var res = newWideCString("", bufsize) var L = getCurrentDirectoryW(bufsize, res) - if L == 0'i32: osError(osLastError()) + if L == 0'i32: raiseOSError(osLastError()) result = res$L else: result = newString(bufsize) var L = getCurrentDirectoryA(bufsize, result) - if L == 0'i32: osError(osLastError()) + if L == 0'i32: raiseOSError(osLastError()) setLen(result, L) else: result = newString(bufsize) if getcwd(result, bufsize) != nil: setLen(result, c_strlen(result)) else: - osError(osLastError()) + raiseOSError(osLastError()) proc setCurrentDir*(newDir: string) {.inline, tags: [].} = - ## Sets the `current working directory`:idx:; `EOS` is raised if + ## Sets the `current working directory`:idx:; `OSError` is raised if ## `newDir` cannot been set. when defined(Windows): when useWinUnicode: if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32: - osError(osLastError()) + raiseOSError(osLastError()) else: - if setCurrentDirectoryA(newDir) == 0'i32: osError(osLastError()) + if setCurrentDirectoryA(newDir) == 0'i32: raiseOSError(osLastError()) else: - if chdir(newDir) != 0'i32: osError(osLastError()) + if chdir(newDir) != 0'i32: raiseOSError(osLastError()) proc joinPath*(head, tail: string): string {. noSideEffect, rtl, extern: "nos$1".} = @@ -506,12 +519,12 @@ proc joinPath*(head, tail: string): string {. ## ## For example on Unix: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## joinPath("usr", "lib") ## ## results in: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## "usr/lib" ## ## If head is the empty string, tail is returned. If tail is the empty @@ -520,7 +533,7 @@ proc joinPath*(head, tail: string): string {. ## path separators not located on boundaries won't be modified. More ## examples on Unix: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## assert joinPath("usr", "") == "usr/" ## assert joinPath("", "lib") == "lib" ## assert joinPath("", "/lib") == "/lib" @@ -552,7 +565,7 @@ proc `/` * (head, tail: string): string {.noSideEffect.} = ## ## Here are some examples for Unix: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## assert "usr" / "" == "usr/" ## assert "" / "lib" == "lib" ## assert "" / "/lib" == "/lib" @@ -562,11 +575,11 @@ proc `/` * (head, tail: string): string {.noSideEffect.} = proc splitPath*(path: string): tuple[head, tail: string] {. noSideEffect, rtl, extern: "nos$1".} = ## Splits a directory into (head, tail), so that - ## ``joinPath(head, tail) == path``. + ## ``head / tail == path`` (except for edge cases like "/usr"). ## ## Examples: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## splitPath("usr/local/bin") -> ("usr/local", "bin") ## splitPath("usr/local/bin/") -> ("usr/local/bin", "") ## splitPath("bin") -> ("", "bin") @@ -666,10 +679,10 @@ proc splitFile*(path: string): tuple[dir, name, ext: string] {. ## ## Example: ## - ## .. code-block:: nimrod - ## var (dir, name, ext) = splitFile("usr/local/nimrodc.html") + ## .. code-block:: nim + ## var (dir, name, ext) = splitFile("usr/local/nimc.html") ## assert dir == "usr/local" - ## assert name == "nimrodc" + ## assert name == "nimc" ## assert ext == ".html" ## ## If `path` has no extension, `ext` is the empty string. @@ -701,8 +714,8 @@ proc extractFilename*(path: string): string {. result = splitPath(path).tail proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", - tags: [FReadDir].} = - ## Returns the full path of `filename`, raises EOS in case of an error. + tags: [ReadDirEffect].} = + ## Returns the full path of `filename`, raises OSError in case of an error. when defined(windows): const bufsize = 3072'i32 when useWinUnicode: @@ -710,19 +723,19 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", var res = newWideCString("", bufsize div 2) var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused) if L <= 0'i32 or L >= bufsize: - osError(osLastError()) + raiseOSError(osLastError()) result = res$L else: var unused: cstring result = newString(bufsize) var L = getFullPathNameA(filename, bufsize, result, unused) - if L <= 0'i32 or L >= bufsize: osError(osLastError()) + if L <= 0'i32 or L >= bufsize: raiseOSError(osLastError()) setLen(result, L) else: # careful, realpath needs to take an allocated buffer according to Posix: result = newString(pathMax) var r = realpath(filename, result) - if r.isNil: osError(osLastError()) + if r.isNil: raiseOSError(osLastError()) setLen(result, c_strlen(result)) proc changeFileExt*(filename, ext: string): string {. @@ -800,7 +813,7 @@ when defined(Windows): ) proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1", - tags: [FReadDir].} = + tags: [ReadDirEffect].} = ## Returns True if both pathname arguments refer to the same physical ## file or directory. Raises an exception if any of the files does not ## exist or information about it can not be obtained. @@ -812,7 +825,7 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1", var f1 = openHandle(path1) var f2 = openHandle(path2) - var lastErr: TOSErrorCode + var lastErr: OSErrorCode if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE: var fi1, fi2: TBY_HANDLE_FILE_INFORMATION @@ -831,22 +844,22 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1", discard closeHandle(f1) discard closeHandle(f2) - if not success: osError(lastErr) + if not success: raiseOSError(lastErr) else: var a, b: TStat if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32: - osError(osLastError()) + raiseOSError(osLastError()) else: result = a.st_dev == b.st_dev and a.st_ino == b.st_ino proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1", - tags: [FReadIO].} = + tags: [ReadIOEffect].} = ## Returns True if both pathname arguments refer to files with identical ## binary content. const bufSize = 8192 # 8K buffer var - a, b: TFile + a, b: File if not open(a, path1): return false if not open(b, path2): close(a) @@ -871,7 +884,7 @@ proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1", close(b) type - TFilePermission* = enum ## file access permission; modelled after UNIX + FilePermission* = enum ## file access permission; modelled after UNIX fpUserExec, ## execute access for the file owner fpUserWrite, ## write access for the file owner fpUserRead, ## read access for the file owner @@ -882,14 +895,16 @@ type fpOthersWrite, ## write access for others fpOthersRead ## read access for others -proc getFilePermissions*(filename: string): set[TFilePermission] {. - rtl, extern: "nos$1", tags: [FReadDir].} = +{.deprecated: [TFilePermission: FilePermission].} + +proc getFilePermissions*(filename: string): set[FilePermission] {. + rtl, extern: "nos$1", tags: [ReadDirEffect].} = ## retrieves file permissions for `filename`. `OSError` is raised in case of ## an error. On Windows, only the ``readonly`` flag is checked, every other ## permission is available in any case. when defined(posix): var a: TStat - if stat(filename, a) < 0'i32: osError(osLastError()) + if stat(filename, a) < 0'i32: raiseOSError(osLastError()) result = {} if (a.st_mode and S_IRUSR) != 0'i32: result.incl(fpUserRead) if (a.st_mode and S_IWUSR) != 0'i32: result.incl(fpUserWrite) @@ -907,15 +922,15 @@ proc getFilePermissions*(filename: string): set[TFilePermission] {. wrapUnary(res, getFileAttributesW, filename) else: var res = getFileAttributesA(filename) - if res == -1'i32: osError(osLastError()) + if res == -1'i32: raiseOSError(osLastError()) if (res and FILE_ATTRIBUTE_READONLY) != 0'i32: result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead, fpOthersExec, fpOthersRead} else: result = {fpUserExec..fpOthersRead} -proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {. - rtl, extern: "nos$1", tags: [FWriteDir].} = +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 ## an error. On Windows, only the ``readonly`` flag is changed, depending on ## ``fpUserWrite``. @@ -933,13 +948,13 @@ proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {. if fpOthersWrite in permissions: p = p or S_IWOTH if fpOthersExec in permissions: p = p or S_IXOTH - if chmod(filename, p) != 0: osError(osLastError()) + if chmod(filename, p) != 0: raiseOSError(osLastError()) else: when useWinUnicode: wrapUnary(res, getFileAttributesW, filename) else: var res = getFileAttributesA(filename) - if res == -1'i32: osError(osLastError()) + if res == -1'i32: raiseOSError(osLastError()) if fpUserWrite in permissions: res = res and not FILE_ATTRIBUTE_READONLY else: @@ -948,13 +963,13 @@ proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {. wrapBinary(res2, setFileAttributesW, filename, res) else: var res2 = setFileAttributesA(filename, res) - if res2 == - 1'i32: osError(osLastError()) + if res2 == - 1'i32: raiseOSError(osLastError()) proc copyFile*(source, dest: string) {.rtl, extern: "nos$1", - tags: [FReadIO, FWriteIO].} = + tags: [ReadIOEffect, WriteIOEffect].} = ## Copies a file from `source` to `dest`. ## - ## If this fails, `EOS` is raised. On the Windows platform this proc will + ## If this fails, `OSError` is raised. On the Windows platform this proc will ## copy the source file's attributes into dest. On other platforms you need ## to use `getFilePermissions() <#getFilePermissions>`_ and ## `setFilePermissions() <#setFilePermissions>`_ to copy them by hand (or use @@ -966,17 +981,17 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1", when useWinUnicode: let s = newWideCString(source) let d = newWideCString(dest) - if copyFileW(s, d, 0'i32) == 0'i32: osError(osLastError()) + if copyFileW(s, d, 0'i32) == 0'i32: raiseOSError(osLastError()) else: - if copyFileA(source, dest, 0'i32) == 0'i32: osError(osLastError()) + if copyFileA(source, dest, 0'i32) == 0'i32: raiseOSError(osLastError()) else: # generic version of copyFile which works for any platform: const bufSize = 8000 # better for memory manager - var d, s: TFile - if not open(s, source): osError(osLastError()) + var d, s: File + if not open(s, source): raiseOSError(osLastError()) if not open(d, dest, fmWrite): close(s) - osError(osLastError()) + raiseOSError(osLastError()) var buf = alloc(bufSize) while true: var bytesread = readBuffer(s, buf, bufSize) @@ -986,17 +1001,17 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1", dealloc(buf) close(s) close(d) - osError(osLastError()) + raiseOSError(osLastError()) if bytesread != bufSize: break dealloc(buf) close(s) close(d) proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", - tags: [FReadIO, FWriteIO].} = - ## Moves a file from `source` to `dest`. If this fails, `EOS` is raised. + tags: [ReadIOEffect, WriteIOEffect].} = + ## Moves a file from `source` to `dest`. If this fails, `OSError` is raised. if c_rename(source, dest) != 0'i32: - raise newException(EOS, $strerror(errno)) + raise newException(OSError, $strerror(errno)) when not declared(ENOENT) and not defined(Windows): when NoFakeVars: @@ -1014,8 +1029,8 @@ when defined(Windows): template setFileAttributes(file, attrs: expr): expr {.immediate.} = setFileAttributesA(file, attrs) -proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} = - ## Removes the `file`. If this fails, `EOS` is raised. This does not fail +proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} = + ## Removes the `file`. If this fails, `OSError` is raised. This does not fail ## if the file never existed in the first place. ## On Windows, ignores the read-only attribute. when defined(Windows): @@ -1026,15 +1041,15 @@ proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} = if deleteFile(f) == 0: if getLastError() == ERROR_ACCESS_DENIED: if setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) == 0: - osError(osLastError()) + raiseOSError(osLastError()) if deleteFile(f) == 0: - osError(osLastError()) + raiseOSError(osLastError()) else: if c_remove(file) != 0'i32 and errno != ENOENT: - raise newException(EOS, $strerror(errno)) + raise newException(OSError, $strerror(errno)) proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", - tags: [FExecIO].} = + tags: [ExecIOEffect].} = ## Executes a `shell command`:idx:. ## ## Command has the form 'program args' where args are the command @@ -1076,7 +1091,7 @@ when defined(windows): while true: var eend = strEnd(e) add(environment, $e) - e = cast[WideCString](cast[TAddress](eend)+2) + e = cast[WideCString](cast[ByteAddress](eend)+2) if eend[1].int == 0: break discard freeEnvironmentStringsW(env) else: @@ -1087,7 +1102,7 @@ when defined(windows): while true: var eend = strEnd(e) add(environment, $e) - e = cast[cstring](cast[TAddress](eend)+1) + e = cast[cstring](cast[ByteAddress](eend)+1) if eend[1] == '\0': break discard freeEnvironmentStringsA(env) envComputed = true @@ -1130,7 +1145,7 @@ proc findEnvVar(key: string): int = if startsWith(environment[i], temp): return i return -1 -proc getEnv*(key: string): TaintedString {.tags: [FReadEnv].} = +proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} = ## Returns the value of the `environment variable`:idx: named `key`. ## ## If the variable does not exist, "" is returned. To distinguish @@ -1144,13 +1159,13 @@ proc getEnv*(key: string): TaintedString {.tags: [FReadEnv].} = if env == nil: return TaintedString("") result = TaintedString($env) -proc existsEnv*(key: string): bool {.tags: [FReadEnv].} = +proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = ## Checks whether the environment variable named `key` exists. ## Returns true if it exists, false otherwise. if c_getenv(key) != nil: return true else: return findEnvVar(key) >= 0 -proc putEnv*(key, val: string) {.tags: [FWriteEnv].} = +proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = ## Sets the value of the `environment variable`:idx: named `key` to `val`. ## If an error occurs, `EInvalidEnvVar` is raised. @@ -1166,16 +1181,16 @@ proc putEnv*(key, val: string) {.tags: [FWriteEnv].} = indx = high(environment) when defined(unix): if c_putenv(environment[indx]) != 0'i32: - osError(osLastError()) + raiseOSError(osLastError()) else: when useWinUnicode: var k = newWideCString(key) var v = newWideCString(val) - if setEnvironmentVariableW(k, v) == 0'i32: osError(osLastError()) + if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError()) else: - if setEnvironmentVariableA(key, val) == 0'i32: osError(osLastError()) + if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError()) -iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [FReadEnv].} = +iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} = ## Iterate over all `environments variables`:idx:. In the first component ## of the tuple is the name of the current variable stored, in the second ## its value. @@ -1185,7 +1200,7 @@ iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [FReadEnv].} = yield (TaintedString(substr(environment[i], 0, p-1)), TaintedString(substr(environment[i], p+1))) -iterator walkFiles*(pattern: string): string {.tags: [FReadDir].} = +iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect].} = ## Iterate over all the files that match the `pattern`. On POSIX this uses ## the `glob`:idx: call. ## @@ -1218,14 +1233,16 @@ iterator walkFiles*(pattern: string): string {.tags: [FReadDir].} = globfree(addr(f)) type - TPathComponent* = enum ## Enumeration specifying a path component. + PathComponent* = enum ## Enumeration specifying a path component. pcFile, ## path refers to a file pcLinkToFile, ## path refers to a symbolic link to a file pcDir, ## path refers to a directory pcLinkToDir ## path refers to a symbolic link to a directory -iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {. - tags: [FReadDir].} = +{.deprecated: [TPathComponent: PathComponent].} + +iterator walkDir*(dir: string): tuple[kind: PathComponent, path: string] {. + tags: [ReadDirEffect].} = ## walks over the directory `dir` and yields for each directory or file in ## `dir`. The component type and full path for each item is returned. ## Walking is not recursive. @@ -1237,7 +1254,7 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {. ## ## and this code: ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## for kind, path in walkDir("dirA"): ## echo(path) ## @@ -1278,7 +1295,7 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {. discard closedir(d) iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {. - tags: [FReadDir].} = + tags: [ReadDirEffect].} = ## walks over the directory `dir` and yields for each file in `dir`. The ## full path for each file is returned. ## **Warning**: @@ -1313,16 +1330,16 @@ proc rawRemoveDir(dir: string) = let lastError = osLastError() if res == 0'i32 and lastError.int32 != 3'i32 and lastError.int32 != 18'i32 and lastError.int32 != 2'i32: - osError(lastError) + raiseOSError(lastError) else: - if rmdir(dir) != 0'i32 and errno != ENOENT: osError(osLastError()) + if rmdir(dir) != 0'i32 and errno != ENOENT: raiseOSError(osLastError()) proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [ - FWriteDir, FReadDir].} = + WriteDirEffect, ReadDirEffect].} = ## Removes the directory `dir` including all subdirectories and files ## in `dir` (recursively). ## - ## If this fails, `EOS` is raised. This does not fail if the directory never + ## If this fails, `OSError` is raised. This does not fail if the directory never ## existed in the first place. for kind, path in walkDir(dir): case kind @@ -1333,27 +1350,27 @@ proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [ proc rawCreateDir(dir: string) = when defined(solaris): if mkdir(dir, 0o777) != 0'i32 and errno != EEXIST and errno != ENOSYS: - osError(osLastError()) + raiseOSError(osLastError()) elif defined(unix): if mkdir(dir, 0o777) != 0'i32 and errno != EEXIST: - osError(osLastError()) + raiseOSError(osLastError()) else: when useWinUnicode: wrapUnary(res, createDirectoryW, dir) else: var res = createDirectoryA(dir) if res == 0'i32 and getLastError() != 183'i32: - osError(osLastError()) + raiseOSError(osLastError()) -proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} = +proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} = ## Creates the `directory`:idx: `dir`. ## ## The directory may contain several subdirectories that do not exist yet. - ## The full path is created. If this fails, `EOS` is raised. It does **not** + ## The full path is created. If this fails, `OSError` is raised. It does **not** ## fail if the path already exists because for most usages this does not ## indicate an error. var omitNext = false - when defined(doslike): + when doslike: omitNext = isAbsolute(dir) for i in 1.. dir.len-1: if dir[i] in {DirSep, AltSep}: @@ -1364,10 +1381,10 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} = rawCreateDir(dir) proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", - tags: [FWriteIO, FReadIO].} = + tags: [WriteIOEffect, ReadIOEffect].} = ## Copies a directory from `source` to `dest`. ## - ## If this fails, `EOS` is raised. On the Windows platform this proc will + ## If this fails, `OSError` is raised. On the Windows platform this proc will ## copy the attributes from `source` into `dest`. On other platforms created ## files and directories will inherit the default permissions of a newly ## created file/directory for the user. To preserve attributes recursively on @@ -1395,13 +1412,13 @@ proc createSymlink*(src, dest: string) = var wSrc = newWideCString(src) var wDst = newWideCString(dest) if createSymbolicLinkW(wDst, wSrc, flag) == 0 or getLastError() != 0: - osError(osLastError()) + raiseOSError(osLastError()) else: if createSymbolicLinkA(dest, src, flag) == 0 or getLastError() != 0: - osError(osLastError()) + raiseOSError(osLastError()) else: if symlink(src, dest) != 0: - osError(osLastError()) + raiseOSError(osLastError()) proc createHardlink*(src, dest: string) = ## Create a hard link at `dest` which points to the item specified @@ -1414,13 +1431,13 @@ proc createHardlink*(src, dest: string) = var wSrc = newWideCString(src) var wDst = newWideCString(dest) if createHardLinkW(wDst, wSrc, nil) == 0: - osError(osLastError()) + raiseOSError(osLastError()) else: if createHardLinkA(dest, src, nil) == 0: - osError(osLastError()) + raiseOSError(osLastError()) else: if link(src, dest) != 0: - osError(osLastError()) + raiseOSError(osLastError()) proc parseCmdLine*(c: string): seq[string] {. noSideEffect, rtl, extern: "nos$1".} = @@ -1537,10 +1554,10 @@ proc copyFileWithPermissions*(source, dest: string, proc copyDirWithPermissions*(source, dest: string, ignorePermissionErrors = true) {.rtl, extern: "nos$1", - tags: [FWriteIO, FReadIO].} = + tags: [WriteIOEffect, ReadIOEffect].} = ## Copies a directory from `source` to `dest` preserving file permissions. ## - ## If this fails, `EOS` is raised. This is a wrapper proc around `copyDir() + ## If this fails, `OSError` is raised. This is a wrapper proc around `copyDir() ## <#copyDir>`_ and `copyFileWithPermissions() <#copyFileWithPermissions>`_ ## on non Windows platforms. On Windows this proc is just a wrapper for ## `copyDir() <#copyDir>`_ since that proc already copies attributes. @@ -1567,24 +1584,24 @@ proc copyDirWithPermissions*(source, dest: string, else: discard proc inclFilePermissions*(filename: string, - permissions: set[TFilePermission]) {. - rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} = + permissions: set[FilePermission]) {. + rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect].} = ## a convenience procedure for: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## setFilePermissions(filename, getFilePermissions(filename)+permissions) setFilePermissions(filename, getFilePermissions(filename)+permissions) proc exclFilePermissions*(filename: string, - permissions: set[TFilePermission]) {. - rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} = + permissions: set[FilePermission]) {. + rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect].} = ## a convenience procedure for: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## setFilePermissions(filename, getFilePermissions(filename)-permissions) setFilePermissions(filename, getFilePermissions(filename)-permissions) -proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} = +proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} = ## Returns the home directory of the current user. ## ## This proc is wrapped by the expandTilde proc for the convenience of @@ -1592,12 +1609,12 @@ proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} = when defined(windows): return string(getEnv("USERPROFILE")) & "\\" else: return string(getEnv("HOME")) & "/" -proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} = +proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} = ## Returns the config directory of the current user for applications. when defined(windows): return string(getEnv("APPDATA")) & "\\" else: return string(getEnv("HOME")) & "/.config/" -proc getTempDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} = +proc getTempDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect].} = ## Returns the temporary directory of the current user for applications to ## save temporary files in. when defined(windows): return string(getEnv("TEMP")) & "\\" @@ -1605,7 +1622,7 @@ proc getTempDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} = when defined(nimdoc): # Common forward declaration docstring block for parameter retrieval procs. - proc paramCount*(): int {.tags: [FReadIO].} = + proc paramCount*(): int {.tags: [ReadIOEffect].} = ## Returns the number of `command line arguments`:idx: given to the ## application. ## @@ -1619,13 +1636,13 @@ when defined(nimdoc): ## can test for its availability with `declared() `_. ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## when declared(paramCount): ## # Use paramCount() here ## else: ## # Do something else! - proc paramStr*(i: int): TaintedString {.tags: [FReadIO].} = + proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} = ## Returns the `i`-th `command line argument`:idx: given to the application. ## ## `i` should be in the range `1..paramCount()`, the `EInvalidIndex` @@ -1642,14 +1659,14 @@ when defined(nimdoc): ## can test for its availability with `declared() `_. ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## when declared(paramStr): ## # Use paramStr() here ## else: ## # Do something else! elif defined(windows): - # Since we support GUI applications with Nimrod, we sometimes generate + # Since we support GUI applications with Nim, we sometimes generate # a WinMain entry proc. But a WinMain proc has no access to the parsed # command line arguments. The way to get them differs. Thus we parse them # ourselves. This has the additional benefit that the program's behaviour @@ -1657,13 +1674,13 @@ elif defined(windows): var ownArgv {.threadvar.}: seq[string] - proc paramCount*(): int {.rtl, extern: "nos$1", tags: [FReadIO].} = + proc paramCount*(): int {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = # Docstring in nimdoc block. if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLine()) result = ownArgv.len-1 proc paramStr*(i: int): TaintedString {.rtl, extern: "nos$1", - tags: [FReadIO].} = + tags: [ReadIOEffect].} = # Docstring in nimdoc block. if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLine()) return TaintedString(ownArgv[i]) @@ -1674,12 +1691,12 @@ elif not defined(createNimRtl): cmdCount {.importc: "cmdCount".}: cint cmdLine {.importc: "cmdLine".}: cstringArray - proc paramStr*(i: int): TaintedString {.tags: [FReadIO].} = + proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} = # Docstring in nimdoc block. if i < cmdCount and i >= 0: return TaintedString($cmdLine[i]) - raise newException(EInvalidIndex, "invalid index") + raise newException(IndexError, "invalid index") - proc paramCount*(): int {.tags: [FReadIO].} = + proc paramCount*(): int {.tags: [ReadIOEffect].} = # Docstring in nimdoc block. result = cmdCount-1 @@ -1695,7 +1712,7 @@ when declared(paramCount) or defined(nimdoc): ## can test for its availability with `declared() `_. ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## when declared(commandLineParams): ## # Use commandLineParams() here ## else: @@ -1739,7 +1756,7 @@ when defined(macosx): proc getExecPath2(c: cstring, size: var cuint32): bool {. importc: "_NSGetExecutablePath", header: "".} -proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [FReadIO].} = +proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = ## Returns the filename of the application's executable. ## ## This procedure will resolve symlinks. @@ -1795,38 +1812,38 @@ proc getApplicationDir*(): string {.rtl, extern: "nos$1", deprecated.} = ## instead. result = splitFile(getAppFilename()).dir -proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [FReadIO].} = +proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = ## Returns the directory of the application's executable. ## **Note**: This does not work reliably on BSD. result = splitFile(getAppFilename()).dir -proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [FTime].} = +proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect].} = ## sleeps `milsecs` milliseconds. when defined(windows): winlean.sleep(int32(milsecs)) else: var a, b: Ttimespec - a.tv_sec = TTime(milsecs div 1000) + a.tv_sec = Time(milsecs div 1000) a.tv_nsec = (milsecs mod 1000) * 1000 * 1000 discard posix.nanosleep(a, b) proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1", - tags: [FReadIO].} = - ## returns the file size of `file`. Can raise ``EOS``. + tags: [ReadIOEffect].} = + ## returns the file size of `file`. Can raise ``OSError``. when defined(windows): var a: TWIN32_FIND_DATA var resA = findFirstFile(file, a) - if resA == -1: osError(osLastError()) + if resA == -1: raiseOSError(osLastError()) result = rdFileSize(a) findClose(resA) else: - var f: TFile + var f: File if open(f, file): result = getFileSize(f) close(f) - else: osError(osLastError()) + else: raiseOSError(osLastError()) -proc findExe*(exe: string): string {.tags: [FReadDir, FReadEnv].} = +proc findExe*(exe: string): string {.tags: [ReadDirEffect, ReadEnvEffect].} = ## Searches for `exe` in the current working directory and then ## in directories listed in the ``PATH`` environment variable. ## Returns "" if the `exe` cannot be found. On DOS-like platforms, `exe` @@ -1850,11 +1867,10 @@ proc expandTilde*(path: string): string = ## The behaviour of this proc is the same on the Windows platform despite not ## having this convention. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## let configFile = expandTilde("~" / "appname.cfg") ## echo configFile ## # --> C:\Users\amber\appname.cfg - if len(path) > 1 and path[0] == '~' and (path[1] == '/' or path[1] == '\\'): result = getHomeDir() / path[2..len(path)-1] else: @@ -1867,22 +1883,22 @@ when defined(Windows): else: type DeviceId* = TDev - FileId* = TIno + FileId* = Tino type FileInfo* = object ## Contains information associated with a file object. id*: tuple[device: DeviceId, file: FileId] # Device and file id. - kind*: TPathComponent # Kind of file object - directory, symlink, etc. + kind*: PathComponent # Kind of file object - directory, symlink, etc. size*: BiggestInt # Size of file. - permissions*: set[TFilePermission] # File permissions + permissions*: set[FilePermission] # File permissions linkCount*: BiggestInt # Number of hard links the file object has. - lastAccessTime*: TTime # Time file was last accessed. - lastWriteTime*: TTime # Time file was last modified/written to. - creationTime*: TTime # Time file was created. Not supported on all systems! + lastAccessTime*: Time # Time file was last accessed. + lastWriteTime*: Time # Time file was last modified/written to. + creationTime*: Time # Time file was created. Not supported on all systems! template rawToFormalFileInfo(rawInfo, formalInfo): expr = - ## Transforms the native file info structure into the one nimrod uses. + ## 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 when defined(Windows): @@ -1939,7 +1955,7 @@ template rawToFormalFileInfo(rawInfo, formalInfo): expr = if S_ISDIR(rawInfo.st_mode): formalInfo.kind = pcDir if S_ISLNK(rawInfo.st_mode): formalInfo.kind.inc() -proc getFileInfo*(handle: TFileHandle): FileInfo = +proc getFileInfo*(handle: FileHandle): FileInfo = ## Retrieves file information for the file object represented by the given ## handle. ## @@ -1952,16 +1968,16 @@ proc getFileInfo*(handle: TFileHandle): FileInfo = # To transform the C file descripter to a native file handle. var realHandle = get_osfhandle(handle) if getFileInformationByHandle(realHandle, addr rawInfo) == 0: - osError(osLastError()) + raiseOSError(osLastError()) rawToFormalFileInfo(rawInfo, result) else: var rawInfo: TStat if fstat(handle, rawInfo) < 0'i32: - osError(osLastError()) + raiseOSError(osLastError()) rawToFormalFileInfo(rawInfo, result) -proc getFileInfo*(file: TFile): FileInfo = - result = getFileInfo(file.fileHandle()) +proc getFileInfo*(file: File): FileInfo = + result = getFileInfo(file.getFileHandle()) proc getFileInfo*(path: string, followSymlink = true): FileInfo = ## Retrieves file information for the file object pointed to by `path`. @@ -1982,19 +1998,19 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo = handle = openHandle(path, followSymlink) rawInfo: TBY_HANDLE_FILE_INFORMATION if handle == INVALID_HANDLE_VALUE: - osError(osLastError()) + raiseOSError(osLastError()) if getFileInformationByHandle(handle, addr rawInfo) == 0: - osError(osLastError()) + raiseOSError(osLastError()) rawToFormalFileInfo(rawInfo, result) discard closeHandle(handle) else: var rawInfo: TStat if followSymlink: if lstat(path, rawInfo) < 0'i32: - osError(osLastError()) + raiseOSError(osLastError()) else: if stat(path, rawInfo) < 0'i32: - osError(osLastError()) + raiseOSError(osLastError()) rawToFormalFileInfo(rawInfo, result) proc isHidden*(path: string): bool = @@ -2006,7 +2022,10 @@ proc isHidden*(path: string): bool = ## On Unix-like systems, a file is hidden if it starts with a '.' (period) ## and is not *just* '.' or '..' ' ." when defined(Windows): - wrapUnary(attributes, getFileAttributesW, path) + when useWinUnicode: + wrapUnary(attributes, getFileAttributesW, path) + else: + var attributes = getFileAttributesA(path) if attributes != -1'i32: result = (attributes and FILE_ATTRIBUTE_HIDDEN) != 0'i32 else: diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 4384cd7d2..bfdb0efed 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -24,20 +24,20 @@ when defined(linux): import linux type - TProcess = object of TObject + ProcessObj = object of RootObj when defined(windows): fProcessHandle: THandle - inHandle, outHandle, errHandle: TFileHandle + inHandle, outHandle, errHandle: FileHandle id: THandle else: - inHandle, outHandle, errHandle: TFileHandle - inStream, outStream, errStream: PStream + inHandle, outHandle, errHandle: FileHandle + inStream, outStream, errStream: Stream id: TPid exitCode: cint - PProcess* = ref TProcess ## represents an operating system process + Process* = ref ProcessObj ## represents an operating system process - TProcessOption* = enum ## options that can be passed `startProcess` + ProcessOption* = enum ## options that can be passed `startProcess` poEchoCmd, ## echo the command before execution poUsePath, ## Asks system to search for executable using PATH environment ## variable. @@ -47,6 +47,9 @@ type poStdErrToStdOut, ## merge stdout and stderr to the stdout stream poParentStreams ## use the parent's streams +{.deprecated: [TProcess: ProcessObj, PProcess: Process, + TProcessOption: ProcessOption].} + const poUseShell* {.deprecated.} = poUsePath ## Deprecated alias for poUsePath. @@ -103,19 +106,19 @@ proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = {.error:"quoteShell is not supported on your system".} proc execProcess*(command: string, - args: openarray[string] = [], - env: PStringTable = nil, - options: set[TProcessOption] = {poStdErrToStdOut, + args: openArray[string] = [], + env: StringTableRef = nil, + options: set[ProcessOption] = {poStdErrToStdOut, poUsePath, poEvalCommand}): TaintedString {. rtl, extern: "nosp$1", - tags: [FExecIO, FReadIO].} + tags: [ExecIOEffect, ReadIOEffect].} ## A convenience procedure that executes ``command`` with ``startProcess`` ## and returns its output as a string. ## WARNING: this function uses poEvalCommand by default for backward compatibility. ## Make sure to pass options explicitly. -proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [FExecIO].} +proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [ExecIOEffect].} ## Executes ``command`` and returns its error code. Standard input, output, ## error streams are inherited from the calling process. This operation ## is also often called `system`:idx:. @@ -123,9 +126,9 @@ proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [FExecIO].} proc startProcess*(command: string, workingDir: string = "", args: openArray[string] = [], - env: PStringTable = nil, - options: set[TProcessOption] = {poStdErrToStdOut}): - PProcess {.rtl, extern: "nosp$1", tags: [FExecIO, FReadEnv].} + env: StringTableRef = nil, + options: set[ProcessOption] = {poStdErrToStdOut}): + Process {.rtl, extern: "nosp$1", tags: [ExecIOEffect, ReadEnvEffect].} ## Starts a process. `Command` is the executable file, `workingDir` is the ## process's working directory. If ``workingDir == ""`` the current directory ## is used. `args` are the command line arguments that are passed to the @@ -148,63 +151,66 @@ proc startProcess*(command: string, ## Return value: The newly created process object. Nil is never returned, ## but ``EOS`` is raised in case of an error. -proc startCmd*(command: string, options: set[TProcessOption] = { - poStdErrToStdOut, poUsePath}): PProcess {. - tags: [FExecIO, FReadEnv], deprecated.} = +proc startCmd*(command: string, options: set[ProcessOption] = { + poStdErrToStdOut, poUsePath}): Process {. + tags: [ExecIOEffect, ReadEnvEffect], deprecated.} = ## Deprecated - use `startProcess` directly. result = startProcess(command=command, options=options + {poEvalCommand}) -proc close*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].} +proc close*(p: Process) {.rtl, extern: "nosp$1", tags: [].} ## When the process has finished executing, cleanup related handles -proc suspend*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].} +proc suspend*(p: Process) {.rtl, extern: "nosp$1", tags: [].} ## Suspends the process `p`. -proc resume*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].} +proc resume*(p: Process) {.rtl, extern: "nosp$1", tags: [].} ## Resumes the process `p`. -proc terminate*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].} - ## Stop the process `p`. On Posix OSs the procedure sends SIGTERM to the process. On Windows the Win32 API function TerminateProcess() is called to stop the process. +proc terminate*(p: Process) {.rtl, extern: "nosp$1", tags: [].} + ## Stop the process `p`. On Posix OSes the procedure sends ``SIGTERM`` + ## to the process. On Windows the Win32 API function ``TerminateProcess()`` + ## is called to stop the process. -proc kill*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].} - ## Kill the process `p`. On Posix OSs the procedure sends SIGKILL to the process. On Windows kill() is an alias for terminate(). +proc kill*(p: Process) {.rtl, extern: "nosp$1", tags: [].} + ## Kill the process `p`. On Posix OSes the procedure sends ``SIGKILL`` to + ## the process. On Windows ``kill()`` is simply an alias for ``terminate()``. -proc running*(p: PProcess): bool {.rtl, extern: "nosp$1", tags: [].} +proc running*(p: Process): bool {.rtl, extern: "nosp$1", tags: [].} ## Returns true iff the process `p` is still running. Returns immediately. -proc processID*(p: PProcess): int {.rtl, extern: "nosp$1".} = +proc processID*(p: Process): int {.rtl, extern: "nosp$1".} = ## returns `p`'s process ID. return p.id -proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl, +proc waitForExit*(p: Process, timeout: int = -1): int {.rtl, extern: "nosp$1", tags: [].} ## waits for the process to finish and returns `p`'s error code. ## ## **Warning**: Be careful when using waitForExit for processes created without ## poParentStreams because they may fill output buffers, causing deadlock. -proc peekExitCode*(p: PProcess): int {.tags: [].} +proc peekExitCode*(p: Process): int {.tags: [].} ## return -1 if the process is still running. Otherwise the process' exit code -proc inputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].} +proc inputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} ## returns ``p``'s input stream for writing to. ## ## **Warning**: The returned `PStream` should not be closed manually as it ## is closed when closing the PProcess ``p``. -proc outputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].} +proc outputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} ## returns ``p``'s output stream for reading from. ## ## **Warning**: The returned `PStream` should not be closed manually as it ## is closed when closing the PProcess ``p``. -proc errorStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].} +proc errorStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} ## returns ``p``'s error stream for reading from. ## ## **Warning**: The returned `PStream` should not be closed manually as it ## is closed when closing the PProcess ``p``. -proc inputHandle*(p: PProcess): TFileHandle {.rtl, extern: "nosp$1", +proc inputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", tags: [].} = ## returns ``p``'s input file handle for writing to. ## @@ -212,7 +218,7 @@ proc inputHandle*(p: PProcess): TFileHandle {.rtl, extern: "nosp$1", ## it is closed when closing the PProcess ``p``. result = p.inHandle -proc outputHandle*(p: PProcess): TFileHandle {.rtl, extern: "nosp$1", +proc outputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", tags: [].} = ## returns ``p``'s output file handle for reading from. ## @@ -220,7 +226,7 @@ proc outputHandle*(p: PProcess): TFileHandle {.rtl, extern: "nosp$1", ## it is closed when closing the PProcess ``p``. result = p.outHandle -proc errorHandle*(p: PProcess): TFileHandle {.rtl, extern: "nosp$1", +proc errorHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", tags: [].} = ## returns ``p``'s error file handle for reading from. ## @@ -235,21 +241,25 @@ proc countProcessors*(): int {.rtl, extern: "nosp$1".} = proc execProcesses*(cmds: openArray[string], options = {poStdErrToStdOut, poParentStreams}, - n = countProcessors()): int {.rtl, extern: "nosp$1", - tags: [FExecIO, FTime, FReadEnv]} = + n = countProcessors(), + beforeRunEvent: proc(idx: int) = nil): int + {.rtl, extern: "nosp$1", + tags: [ExecIOEffect, TimeEffect, ReadEnvEffect, RootEffect]} = ## executes the commands `cmds` in parallel. Creates `n` processes ## that execute in parallel. The highest return value of all processes - ## is returned. + ## is returned. Runs `beforeRunEvent` before running each command. when defined(posix): # poParentStreams causes problems on Posix, so we simply disable it: var options = options - {poParentStreams} assert n > 0 if n > 1: - var q: seq[PProcess] + var q: seq[Process] newSeq(q, n) var m = min(n, cmds.len) for i in 0..m-1: + if beforeRunEvent != nil: + beforeRunEvent(i) q[i] = startCmd(cmds[i], options=options) when defined(noBusyWaiting): var r = 0 @@ -263,6 +273,8 @@ proc execProcesses*(cmds: openArray[string], echo(err) result = max(waitForExit(q[r]), result) if q[r] != nil: close(q[r]) + if beforeRunEvent != nil: + beforeRunEvent(i) q[r] = startCmd(cmds[i], options=options) r = (r + 1) mod n else: @@ -274,6 +286,8 @@ proc execProcesses*(cmds: openArray[string], #echo(outputStream(q[r]).readLine()) result = max(waitForExit(q[r]), result) if q[r] != nil: close(q[r]) + if beforeRunEvent != nil: + beforeRunEvent(i) q[r] = startCmd(cmds[i], options=options) inc(i) if i > high(cmds): break @@ -282,12 +296,14 @@ proc execProcesses*(cmds: openArray[string], if q[j] != nil: close(q[j]) else: for i in 0..high(cmds): + if beforeRunEvent != nil: + beforeRunEvent(i) var p = startCmd(cmds[i], options=options) result = max(waitForExit(p), result) close(p) -proc select*(readfds: var seq[PProcess], timeout = 500): int - ## `select` with a sensible Nimrod interface. `timeout` is in miliseconds. +proc select*(readfds: var seq[Process], timeout = 500): int + ## `select` with a sensible Nim interface. `timeout` is in miliseconds. ## Specify -1 for no timeout. Returns the number of processes that are ## ready to read from. The processes that are ready to be read from are ## removed from `readfds`. @@ -297,9 +313,9 @@ proc select*(readfds: var seq[PProcess], timeout = 500): int when not defined(useNimRtl): proc execProcess(command: string, - args: openarray[string] = [], - env: PStringTable = nil, - options: set[TProcessOption] = {poStdErrToStdOut, + args: openArray[string] = [], + env: StringTableRef = nil, + options: set[ProcessOption] = {poStdErrToStdOut, poUsePath, poEvalCommand}): TaintedString = var p = startProcess(command, args=args, env=env, options=options) @@ -319,30 +335,31 @@ when defined(Windows) and not defined(useNimRtl): # We need to implement a handle stream for Windows: type PFileHandleStream = ref TFileHandleStream - TFileHandleStream = object of TStream + TFileHandleStream = object of StreamObj handle: THandle atTheEnd: bool - proc hsClose(s: PStream) = discard # nothing to do here - proc hsAtEnd(s: PStream): bool = return PFileHandleStream(s).atTheEnd + proc hsClose(s: Stream) = discard # nothing to do here + proc hsAtEnd(s: Stream): bool = return PFileHandleStream(s).atTheEnd - proc hsReadData(s: PStream, buffer: pointer, bufLen: int): int = + proc hsReadData(s: Stream, buffer: pointer, bufLen: int): int = var s = PFileHandleStream(s) if s.atTheEnd: return 0 var br: int32 - var a = winlean.readFile(s.handle, buffer, bufLen.cint, br, nil) + var a = winlean.readFile(s.handle, buffer, bufLen.cint, addr br, nil) # TRUE and zero bytes returned (EOF). # TRUE and n (>0) bytes returned (good data). # FALSE and bytes returned undefined (system error). - if a == 0 and br != 0: osError(osLastError()) + if a == 0 and br != 0: raiseOSError(osLastError()) s.atTheEnd = br < bufLen result = br - proc hsWriteData(s: PStream, buffer: pointer, bufLen: int) = + proc hsWriteData(s: Stream, buffer: pointer, bufLen: int) = var s = PFileHandleStream(s) var bytesWritten: int32 - var a = winlean.writeFile(s.handle, buffer, bufLen.cint, bytesWritten, nil) - if a == 0: osError(osLastError()) + var a = winlean.writeFile(s.handle, buffer, bufLen.cint, + addr bytesWritten, nil) + if a == 0: raiseOSError(osLastError()) proc newFileHandleStream(handle: THandle): PFileHandleStream = new(result) @@ -360,7 +377,7 @@ when defined(Windows) and not defined(useNimRtl): result = cast[cstring](alloc0(res.len+1)) copyMem(result, cstring(res), res.len) - proc buildEnv(env: PStringTable): cstring = + proc buildEnv(env: StringTableRef): cstring = var L = 0 for key, val in pairs(env): inc(L, key.len + val.len + 2) result = cast[cstring](alloc0(L+2)) @@ -383,7 +400,7 @@ when defined(Windows) and not defined(useNimRtl): piInheritablePipe.lpSecurityDescriptor = nil piInheritablePipe.bInheritHandle = 1 if createPipe(rdHandle, wrHandle, piInheritablePipe, 1024) == 0'i32: - osError(osLastError()) + raiseOSError(osLastError()) proc fileClose(h: THandle) {.inline.} = if h > 4: discard closeHandle(h) @@ -391,8 +408,8 @@ when defined(Windows) and not defined(useNimRtl): proc startProcess(command: string, workingDir: string = "", args: openArray[string] = [], - env: PStringTable = nil, - options: set[TProcessOption] = {poStdErrToStdOut}): PProcess = + env: StringTableRef = nil, + options: set[ProcessOption] = {poStdErrToStdOut}): Process = var si: TSTARTUPINFO procInfo: TPROCESS_INFORMATION @@ -409,16 +426,16 @@ when defined(Windows) and not defined(useNimRtl): he = ho else: createPipeHandles(he, si.hStdError) - result.inHandle = TFileHandle(hi) - result.outHandle = TFileHandle(ho) - result.errHandle = TFileHandle(he) + result.inHandle = FileHandle(hi) + result.outHandle = FileHandle(ho) + result.errHandle = FileHandle(he) else: si.hStdError = getStdHandle(STD_ERROR_HANDLE) si.hStdInput = getStdHandle(STD_INPUT_HANDLE) si.hStdOutput = getStdHandle(STD_OUTPUT_HANDLE) - result.inHandle = TFileHandle(si.hStdInput) - result.outHandle = TFileHandle(si.hStdOutput) - result.errHandle = TFileHandle(si.hStdError) + result.inHandle = FileHandle(si.hStdInput) + result.outHandle = FileHandle(si.hStdOutput) + result.errHandle = FileHandle(si.hStdError) var cmdl: cstring if poEvalCommand in options: @@ -450,13 +467,13 @@ when defined(Windows) and not defined(useNimRtl): fileClose(si.hStdError) if e != nil: dealloc(e) - if success == 0: osError(lastError) + if success == 0: raiseOSError(lastError) # Close the handle now so anyone waiting is woken: discard closeHandle(procInfo.hThread) result.fProcessHandle = procInfo.hProcess result.id = procInfo.dwProcessId - proc close(p: PProcess) = + proc close(p: Process) = when false: # somehow this does not work on Windows: discard closeHandle(p.inHandle) @@ -464,24 +481,24 @@ when defined(Windows) and not defined(useNimRtl): discard closeHandle(p.errHandle) discard closeHandle(p.FProcessHandle) - proc suspend(p: PProcess) = + proc suspend(p: Process) = discard suspendThread(p.fProcessHandle) - proc resume(p: PProcess) = + proc resume(p: Process) = discard resumeThread(p.fProcessHandle) - proc running(p: PProcess): bool = + proc running(p: Process): bool = var x = waitForSingleObject(p.fProcessHandle, 50) return x == WAIT_TIMEOUT - proc terminate(p: PProcess) = + proc terminate(p: Process) = if running(p): discard terminateProcess(p.fProcessHandle, 0) - proc kill(p: PProcess) = + proc kill(p: Process) = terminate(p) - - proc waitForExit(p: PProcess, timeout: int = -1): int = + + proc waitForExit(p: Process, timeout: int = -1): int = discard waitForSingleObject(p.fProcessHandle, timeout.int32) var res: int32 @@ -489,7 +506,7 @@ when defined(Windows) and not defined(useNimRtl): result = res discard closeHandle(p.fProcessHandle) - proc peekExitCode(p: PProcess): int = + proc peekExitCode(p: Process): int = var b = waitForSingleObject(p.fProcessHandle, 50) == WAIT_TIMEOUT if b: result = -1 else: @@ -497,13 +514,13 @@ when defined(Windows) and not defined(useNimRtl): discard getExitCodeProcess(p.fProcessHandle, res) return res - proc inputStream(p: PProcess): PStream = + proc inputStream(p: Process): Stream = result = newFileHandleStream(p.inHandle) - proc outputStream(p: PProcess): PStream = + proc outputStream(p: Process): Stream = result = newFileHandleStream(p.outHandle) - proc errorStream(p: PProcess): PStream = + proc errorStream(p: Process): Stream = result = newFileHandleStream(p.errHandle) proc execCmd(command: string): int = @@ -524,7 +541,7 @@ when defined(Windows) and not defined(useNimRtl): var res = winlean.createProcessA(nil, command, nil, nil, 0, NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo) if res == 0: - osError(osLastError()) + raiseOSError(osLastError()) else: process = procInfo.hProcess discard closeHandle(procInfo.hThread) @@ -535,7 +552,7 @@ when defined(Windows) and not defined(useNimRtl): result = -1 discard closeHandle(process) - proc select(readfds: var seq[PProcess], timeout = 500): int = + proc select(readfds: var seq[Process], timeout = 500): int = assert readfds.len <= MAXIMUM_WAIT_OBJECTS var rfds: TWOHandleArray for i in 0..readfds.len()-1: @@ -547,7 +564,7 @@ when defined(Windows) and not defined(useNimRtl): of WAIT_TIMEOUT: return 0 of WAIT_FAILED: - osError(osLastError()) + raiseOSError(osLastError()) else: var i = ret - WAIT_OBJECT_0 readfds.del(i) @@ -558,7 +575,7 @@ elif not defined(useNimRtl): readIdx = 0 writeIdx = 1 - proc envToCStringArray(t: PStringTable): cstringArray = + proc envToCStringArray(t: StringTableRef): cstringArray = result = cast[cstringArray](alloc0((t.len + 1) * sizeof(cstring))) var i = 0 for key, val in pairs(t): @@ -590,19 +607,19 @@ elif not defined(useNimRtl): when not defined(useFork): proc startProcessAuxSpawn(data: TStartProcessData): TPid {. - tags: [FExecIO, FReadEnv], gcsafe.} + tags: [ExecIOEffect, ReadEnvEffect], gcsafe.} proc startProcessAuxFork(data: TStartProcessData): TPid {. - tags: [FExecIO, FReadEnv], gcsafe.} + tags: [ExecIOEffect, ReadEnvEffect], gcsafe.} {.push stacktrace: off, profiler: off.} proc startProcessAfterFork(data: ptr TStartProcessData) {. - tags: [FExecIO, FReadEnv], cdecl, gcsafe.} + tags: [ExecIOEffect, ReadEnvEffect], cdecl, gcsafe.} {.pop.} proc startProcess(command: string, workingDir: string = "", args: openArray[string] = [], - env: PStringTable = nil, - options: set[TProcessOption] = {poStdErrToStdOut}): PProcess = + env: StringTableRef = nil, + options: set[ProcessOption] = {poStdErrToStdOut}): Process = var pStdin, pStdout, pStderr: array [0..1, cint] new(result) @@ -610,7 +627,7 @@ elif not defined(useNimRtl): if poParentStreams notin options: if pipe(pStdin) != 0'i32 or pipe(pStdout) != 0'i32 or pipe(pStderr) != 0'i32: - osError(osLastError()) + raiseOSError(osLastError()) var sysCommand: string var sysArgsRaw: seq[string] @@ -686,7 +703,7 @@ elif not defined(useNimRtl): var fops: Tposix_spawn_file_actions template chck(e: expr) = - if e != 0'i32: osError(osLastError()) + if e != 0'i32: raiseOSError(osLastError()) chck posix_spawn_file_actions_init(fops) chck posix_spawnattr_init(attr) @@ -729,7 +746,7 @@ elif not defined(useNimRtl): proc startProcessAuxFork(data: TStartProcessData): TPid = if pipe(data.pErrorPipe) != 0: - osError(osLastError()) + raiseOSError(osLastError()) finally: discard close(data.pErrorPipe[readIdx]) @@ -754,12 +771,12 @@ elif not defined(useNimRtl): exitnow(1) discard close(data.pErrorPipe[writeIdx]) - if pid < 0: osError(osLastError()) + if pid < 0: raiseOSError(osLastError()) var error: cint let sizeRead = read(data.pErrorPipe[readIdx], addr error, sizeof(error)) if sizeRead == sizeof(error): - osError($strerror(error)) + raiseOSError($strerror(error)) return pid @@ -774,7 +791,7 @@ elif not defined(useNimRtl): proc startProcessAfterFork(data: ptr TStartProcessData) = # Warning: no GC here! - # Or anything that touches global structures - all called nimrod procs + # Or anything that touches global structures - all called nim procs # must be marked with stackTrace:off. Inspect C code after making changes. if not data.optionPoParentStreams: discard close(data.pStdin[writeIdx]) @@ -805,14 +822,18 @@ elif not defined(useNimRtl): environ = data.sysEnv discard execvp(data.sysCommand, data.sysArgs) else: - discard execvpe(data.sysCommand, data.sysArgs, data.sysEnv) + when defined(uClibc): + # uClibc environment (OpenWrt included) doesn't have the full execvpe + discard execve(data.sysCommand, data.sysArgs, data.sysEnv) + else: + discard execvpe(data.sysCommand, data.sysArgs, data.sysEnv) else: discard execve(data.sysCommand, data.sysArgs, data.sysEnv) startProcessFail(data) {.pop} - proc close(p: PProcess) = + proc close(p: Process) = if p.inStream != nil: close(p.inStream) if p.outStream != nil: close(p.outStream) if p.errStream != nil: close(p.errStream) @@ -820,26 +841,26 @@ elif not defined(useNimRtl): discard close(p.outHandle) discard close(p.errHandle) - proc suspend(p: PProcess) = - if kill(p.id, SIGSTOP) != 0'i32: osError(osLastError()) + proc suspend(p: Process) = + if kill(p.id, SIGSTOP) != 0'i32: raiseOsError(osLastError()) - proc resume(p: PProcess) = - if kill(p.id, SIGCONT) != 0'i32: osError(osLastError()) + proc resume(p: Process) = + if kill(p.id, SIGCONT) != 0'i32: raiseOsError(osLastError()) - proc running(p: PProcess): bool = + proc running(p: Process): bool = var ret = waitpid(p.id, p.exitCode, WNOHANG) if ret == 0: return true # Can't establish status. Assume running. result = ret == int(p.id) - proc terminate(p: PProcess) = + proc terminate(p: Process) = if kill(p.id, SIGTERM) != 0'i32: - osError(osLastError()) + raiseOsError(osLastError()) - proc kill(p: PProcess) = + proc kill(p: Process) = if kill(p.id, SIGKILL) != 0'i32: - osError(osLastError()) + raiseOsError(osLastError()) - proc waitForExit(p: PProcess, timeout: int = -1): int = + proc waitForExit(p: Process, timeout: int = -1): int = #if waitPid(p.id, p.exitCode, 0) == int(p.id): # ``waitPid`` fails if the process is not running anymore. But then # ``running`` probably set ``p.exitCode`` for us. Since ``p.exitCode`` is @@ -847,10 +868,10 @@ elif not defined(useNimRtl): if p.exitCode != -3: return p.exitCode if waitpid(p.id, p.exitCode, 0) < 0: p.exitCode = -3 - osError(osLastError()) + raiseOSError(osLastError()) result = int(p.exitCode) shr 8 - proc peekExitCode(p: PProcess): int = + proc peekExitCode(p: Process): int = if p.exitCode != -3: return p.exitCode var ret = waitpid(p.id, p.exitCode, WNOHANG) var b = ret == int(p.id) @@ -858,28 +879,29 @@ elif not defined(useNimRtl): if p.exitCode == -3: result = -1 else: result = p.exitCode.int shr 8 - proc createStream(stream: var PStream, handle: var TFileHandle, - fileMode: TFileMode) = - var f: TFile - if not open(f, handle, fileMode): osError(osLastError()) + proc createStream(stream: var Stream, handle: var FileHandle, + fileMode: FileMode) = + var f: File + if not open(f, handle, fileMode): raiseOSError(osLastError()) stream = newFileStream(f) - proc inputStream(p: PProcess): PStream = + proc inputStream(p: Process): Stream = if p.inStream == nil: createStream(p.inStream, p.inHandle, fmWrite) return p.inStream - proc outputStream(p: PProcess): PStream = + proc outputStream(p: Process): Stream = if p.outStream == nil: createStream(p.outStream, p.outHandle, fmRead) return p.outStream - proc errorStream(p: PProcess): PStream = + proc errorStream(p: Process): Stream = if p.errStream == nil: createStream(p.errStream, p.errHandle, fmRead) return p.errStream - proc csystem(cmd: cstring): cint {.nodecl, importc: "system", header: "".} + proc csystem(cmd: cstring): cint {.nodecl, importc: "system", + header: "".} proc execCmd(command: string): int = when defined(linux): @@ -887,13 +909,13 @@ elif not defined(useNimRtl): else: result = csystem(command) - proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) = + proc createFdSet(fd: var TFdSet, s: seq[Process], m: var int) = FD_ZERO(fd) for i in items(s): m = max(m, int(i.outHandle)) FD_SET(cint(i.outHandle), fd) - proc pruneProcessSet(s: var seq[PProcess], fd: var TFdSet) = + proc pruneProcessSet(s: var seq[Process], fd: var TFdSet) = var i = 0 var L = s.len while i < L: @@ -904,8 +926,8 @@ elif not defined(useNimRtl): inc(i) setLen(s, L) - proc select(readfds: var seq[PProcess], timeout = 500): int = - var tv: TTimeVal + proc select(readfds: var seq[Process], timeout = 500): int = + var tv: Timeval tv.tv_sec = 0 tv.tv_usec = timeout * 1000 @@ -921,10 +943,10 @@ elif not defined(useNimRtl): pruneProcessSet(readfds, (rd)) -proc execCmdEx*(command: string, options: set[TProcessOption] = { +proc execCmdEx*(command: string, options: set[ProcessOption] = { poStdErrToStdOut, poUsePath}): tuple[ output: TaintedString, - exitCode: int] {.tags: [FExecIO, FReadIO], gcsafe.} = + exitCode: int] {.tags: [ExecIOEffect, ReadIOEffect], gcsafe.} = ## a convenience proc that runs the `command`, grabs all its output and ## exit code and returns both. var p = startCmd(command, options) diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index 1d61a967b..bb9d2aed2 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2010 Andreas Rumpf # # See the file "copying.txt", included in this @@ -11,7 +11,7 @@ ## parser. The configuration file's syntax is similar to the Windows ``.ini`` ## format, but much more powerful, as it is not a line based parser. String ## literals, raw string literals and triple quoted string literals are supported -## as in the Nimrod programming language. +## as in the Nim programming language. ## This is an example of how a configuration file may look like: ## @@ -20,25 +20,25 @@ ## The file ``examples/parsecfgex.nim`` demonstrates how to use the ## configuration file parser: ## -## .. code-block:: nimrod +## .. code-block:: nim ## :file: examples/parsecfgex.nim -import +import hashes, strutils, lexbase, streams include "system/inclrtl" -type - TCfgEventKind* = enum ## enumeration of all events that may occur when parsing +type + CfgEventKind* = enum ## enumeration of all events that may occur when parsing cfgEof, ## end of file reached cfgSectionStart, ## a ``[section]`` has been parsed cfgKeyValuePair, ## a ``key=value`` pair has been detected cfgOption, ## a ``--key=value`` command line option cfgError ## an error ocurred during parsing - TCfgEvent* = object of TObject ## describes a parsing event - case kind*: TCfgEventKind ## the kind of the event + CfgEvent* = object of RootObj ## describes a parsing event + case kind*: CfgEventKind ## the kind of the event of cfgEof: nil of cfgSectionStart: section*: string ## `section` contains the name of the @@ -53,28 +53,29 @@ type msg*: string ## contains the error message. No exceptions ## are thrown if a parse error occurs. - TTokKind = enum + TokKind = enum tkInvalid, tkEof, tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash - TToken {.final.} = object # a token - kind: TTokKind # the type of the token + Token = object # a token + kind: TokKind # the type of the token literal: string # the parsed (string) literal - TCfgParser* = object of TBaseLexer ## the parser object. - tok: TToken + CfgParser* = object of BaseLexer ## the parser object. + tok: Token filename: string +{.deprecated: [TCfgEventKind: CfgEventKind, TCfgEvent: CfgEvent, + TTokKind: TokKind, TToken: Token, TCfgParser: CfgParser].} + # implementation const - SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.', - '/', '\\'} + SymChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.', '/', '\\'} -proc rawGetTok(c: var TCfgParser, tok: var TToken) {.gcsafe.} +proc rawGetTok(c: var CfgParser, tok: var Token) {.gcsafe.} -proc open*(c: var TCfgParser, input: PStream, filename: string, - lineOffset = 0) {. - rtl, extern: "npc$1".} = +proc open*(c: var CfgParser, input: Stream, filename: string, + lineOffset = 0) {.rtl, extern: "npc$1".} = ## initializes the parser with an input stream. `Filename` is only used ## for nice error messages. `lineOffset` can be used to influence the line ## number information in the generated error messages. @@ -85,23 +86,23 @@ proc open*(c: var TCfgParser, input: PStream, filename: string, inc(c.lineNumber, lineOffset) rawGetTok(c, c.tok) -proc close*(c: var TCfgParser) {.rtl, extern: "npc$1".} = +proc close*(c: var CfgParser) {.rtl, extern: "npc$1".} = ## closes the parser `c` and its associated input stream. lexbase.close(c) -proc getColumn*(c: TCfgParser): int {.rtl, extern: "npc$1".} = +proc getColumn*(c: CfgParser): int {.rtl, extern: "npc$1".} = ## get the current column the parser has arrived at. result = getColNumber(c, c.bufpos) -proc getLine*(c: TCfgParser): int {.rtl, extern: "npc$1".} = +proc getLine*(c: CfgParser): int {.rtl, extern: "npc$1".} = ## get the current line the parser has arrived at. result = c.lineNumber -proc getFilename*(c: TCfgParser): string {.rtl, extern: "npc$1".} = +proc getFilename*(c: CfgParser): string {.rtl, extern: "npc$1".} = ## get the filename of the file that the parser processes. result = c.filename -proc handleHexChar(c: var TCfgParser, xi: var int) = +proc handleHexChar(c: var CfgParser, xi: var int) = case c.buf[c.bufpos] of '0'..'9': xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0')) @@ -115,12 +116,12 @@ proc handleHexChar(c: var TCfgParser, xi: var int) = else: discard -proc handleDecChars(c: var TCfgParser, xi: var int) = +proc handleDecChars(c: var CfgParser, xi: var int) = while c.buf[c.bufpos] in {'0'..'9'}: xi = (xi * 10) + (ord(c.buf[c.bufpos]) - ord('0')) inc(c.bufpos) -proc getEscapedChar(c: var TCfgParser, tok: var TToken) = +proc getEscapedChar(c: var CfgParser, tok: var Token) = inc(c.bufpos) # skip '\' case c.buf[c.bufpos] of 'n', 'N': @@ -169,13 +170,13 @@ proc getEscapedChar(c: var TCfgParser, tok: var TToken) = else: tok.kind = tkInvalid else: tok.kind = tkInvalid -proc handleCRLF(c: var TCfgParser, pos: int): int = +proc handleCRLF(c: var CfgParser, pos: int): int = case c.buf[pos] of '\c': result = lexbase.handleCR(c, pos) of '\L': result = lexbase.handleLF(c, pos) else: result = pos -proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) = +proc getString(c: var CfgParser, tok: var Token, rawMode: bool) = var pos = c.bufpos + 1 # skip " var buf = c.buf # put `buf` in a register tok.kind = tkSymbol @@ -221,7 +222,7 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) = inc(pos) c.bufpos = pos -proc getSymbol(c: var TCfgParser, tok: var TToken) = +proc getSymbol(c: var CfgParser, tok: var Token) = var pos = c.bufpos var buf = c.buf while true: @@ -231,7 +232,7 @@ proc getSymbol(c: var TCfgParser, tok: var TToken) = c.bufpos = pos tok.kind = tkSymbol -proc skip(c: var TCfgParser) = +proc skip(c: var CfgParser) = var pos = c.bufpos var buf = c.buf while true: @@ -247,7 +248,7 @@ proc skip(c: var TCfgParser) = break # EndOfFile also leaves the loop c.bufpos = pos -proc rawGetTok(c: var TCfgParser, tok: var TToken) = +proc rawGetTok(c: var CfgParser, tok: var Token) = tok.kind = tkInvalid setLen(tok.literal, 0) skip(c) @@ -286,19 +287,19 @@ proc rawGetTok(c: var TCfgParser, tok: var TToken) = tok.literal = "[EOF]" else: getSymbol(c, tok) -proc errorStr*(c: TCfgParser, msg: string): string {.rtl, extern: "npc$1".} = +proc errorStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} = ## returns a properly formated error message containing current line and ## column information. result = `%`("$1($2, $3) Error: $4", [c.filename, $getLine(c), $getColumn(c), msg]) -proc warningStr*(c: TCfgParser, msg: string): string {.rtl, extern: "npc$1".} = +proc warningStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} = ## returns a properly formated warning message containing current line and ## column information. result = `%`("$1($2, $3) Warning: $4", [c.filename, $getLine(c), $getColumn(c), msg]) -proc ignoreMsg*(c: TCfgParser, e: TCfgEvent): string {.rtl, extern: "npc$1".} = +proc ignoreMsg*(c: CfgParser, e: CfgEvent): string {.rtl, extern: "npc$1".} = ## returns a properly formated warning message containing that ## an entry is ignored. case e.kind @@ -309,7 +310,7 @@ proc ignoreMsg*(c: TCfgParser, e: TCfgEvent): string {.rtl, extern: "npc$1".} = of cfgError: result = e.msg of cfgEof: result = "" -proc getKeyValPair(c: var TCfgParser, kind: TCfgEventKind): TCfgEvent = +proc getKeyValPair(c: var CfgParser, kind: CfgEventKind): CfgEvent = if c.tok.kind == tkSymbol: result.kind = kind result.key = c.tok.literal @@ -329,7 +330,7 @@ proc getKeyValPair(c: var TCfgParser, kind: TCfgEventKind): TCfgEvent = result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal) rawGetTok(c, c.tok) -proc next*(c: var TCfgParser): TCfgEvent {.rtl, extern: "npc$1".} = +proc next*(c: var CfgParser): CfgEvent {.rtl, extern: "npc$1".} = ## retrieves the first/next event. This controls the parser. case c.tok.kind of tkEof: diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim index 4b25babec..f4943ed89 100644 --- a/lib/pure/parsecsv.nim +++ b/lib/pure/parsecsv.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2009 Andreas Rumpf # # See the file "copying.txt", included in this @@ -13,16 +13,16 @@ ## Example: How to use the parser ## ============================== ## -## .. code-block:: nimrod +## .. code-block:: nim ## import os, parsecsv, streams -## var s = newFileStream(ParamStr(1), fmRead) -## if s == nil: quit("cannot open the file" & ParamStr(1)) -## var x: TCsvParser -## open(x, s, ParamStr(1)) +## var s = newFileStream(paramStr(1), fmRead) +## if s == nil: quit("cannot open the file" & paramStr(1)) +## var x: CsvParser +## open(x, s, paramStr(1)) ## while readRow(x): -## Echo "new row: " +## echo "new row: " ## for val in items(x.row): -## Echo "##", val, "##" +## echo "##", val, "##" ## close(x) ## @@ -30,28 +30,30 @@ import lexbase, streams type - TCsvRow* = seq[string] ## a row in a CSV file - TCsvParser* = object of TBaseLexer ## the parser object. - row*: TCsvRow ## the current row + CsvRow* = seq[string] ## a row in a CSV file + CsvParser* = object of BaseLexer ## the parser object. + row*: CsvRow ## the current row filename: string sep, quote, esc: char skipWhite: bool currRow: int - EInvalidCsv* = object of EIO ## exception that is raised if - ## a parsing error occurs + CsvError* = object of IOError ## exception that is raised if + ## a parsing error occurs + +{.deprecated: [TCsvRow: CsvRow, TCsvParser: CsvParser, EInvalidCsv: CsvError].} proc raiseEInvalidCsv(filename: string, line, col: int, msg: string) {.noreturn.} = - var e: ref EInvalidCsv + var e: ref CsvError new(e) e.msg = filename & "(" & $line & ", " & $col & ") Error: " & msg raise e -proc error(my: TCsvParser, pos: int, msg: string) = - raiseEInvalidCsv(my.filename, my.LineNumber, getColNumber(my, pos), msg) +proc error(my: CsvParser, pos: int, msg: string) = + raiseEInvalidCsv(my.filename, my.lineNumber, getColNumber(my, pos), msg) -proc open*(my: var TCsvParser, input: PStream, filename: string, +proc open*(my: var CsvParser, input: Stream, filename: string, separator = ',', quote = '"', escape = '\0', skipInitialSpace = false) = ## initializes the parser with an input stream. `Filename` is only used @@ -75,7 +77,7 @@ proc open*(my: var TCsvParser, input: PStream, filename: string, my.row = @[] my.currRow = 0 -proc parseField(my: var TCsvParser, a: var string) = +proc parseField(my: var CsvParser, a: var string) = var pos = my.bufpos var buf = my.buf if my.skipWhite: @@ -83,7 +85,7 @@ proc parseField(my: var TCsvParser, a: var string) = setLen(a, 0) # reuse memory if buf[pos] == my.quote and my.quote != '\0': inc(pos) - while true: + while true: var c = buf[pos] if c == '\0': my.bufpos = pos # can continue after exception? @@ -121,11 +123,11 @@ proc parseField(my: var TCsvParser, a: var string) = inc(pos) my.bufpos = pos -proc processedRows*(my: var TCsvParser): int = +proc processedRows*(my: var CsvParser): int = ## returns number of the processed rows return my.currRow -proc readRow*(my: var TCsvParser, columns = 0): bool = +proc readRow*(my: var CsvParser, columns = 0): bool = ## reads the next row; if `columns` > 0, it expects the row to have ## exactly this many columns. Returns false if the end of the file ## has been encountered else true. @@ -153,26 +155,26 @@ proc readRow*(my: var TCsvParser, columns = 0): bool = else: error(my, my.bufpos, my.sep & " expected") break - setlen(my.row, col) + setLen(my.row, col) result = col > 0 if result and col != columns and columns > 0: error(my, oldpos+1, $columns & " columns expected, but found " & $col & " columns") inc(my.currRow) -proc close*(my: var TCsvParser) {.inline.} = +proc close*(my: var CsvParser) {.inline.} = ## closes the parser `my` and its associated input stream. lexbase.close(my) when isMainModule: import os - var s = newFileStream(ParamStr(1), fmRead) - if s == nil: quit("cannot open the file" & ParamStr(1)) - var x: TCsvParser - open(x, s, ParamStr(1)) + var s = newFileStream(paramStr(1), fmRead) + if s == nil: quit("cannot open the file" & paramStr(1)) + var x: CsvParser + open(x, s, paramStr(1)) while readRow(x): - Echo "new row: " + echo "new row: " for val in items(x.row): - Echo "##", val, "##" + echo "##", val, "##" close(x) diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index f43853fe6..8af6920c1 100644 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -1,13 +1,13 @@ # # -# Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf +# Nim's Runtime Library +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## This module provides the standard Nimrod command line parser. +## This module provides the standard Nim command line parser. ## It supports one convenience iterator over all command line options and some ## lower-level features. ## @@ -22,26 +22,28 @@ import os, strutils type - TCmdLineKind* = enum ## the detected command line token + CmdLineKind* = enum ## the detected command line token cmdEnd, ## end of command line reached cmdArgument, ## argument detected - cmdLongoption, ## a long option ``--option`` detected + cmdLongOption, ## a long option ``--option`` detected cmdShortOption ## a short option ``-c`` detected - TOptParser* = - object of TObject ## this object implements the command line parser + OptParser* = + object of RootObj ## this object implements the command line parser cmd: string pos: int inShortState: bool - kind*: TCmdLineKind ## the dected command line token + kind*: CmdLineKind ## the dected command line token key*, val*: TaintedString ## key and value pair; ``key`` is the option ## or the argument, ``value`` is not "" if ## the option was given a value +{.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].} + when declared(os.paramCount): # we cannot provide this for NimRtl creation on Posix, because we can't # access the command line arguments then! - proc initOptParser*(cmdline = ""): TOptParser = + proc initOptParser*(cmdline = ""): OptParser = ## inits the option parser. If ``cmdline == ""``, the real command line ## (as provided by the ``OS`` module) is taken. result.pos = 0 @@ -57,7 +59,7 @@ when declared(os.paramCount): result.val = TaintedString"" proc parseWord(s: string, i: int, w: var string, - delim: TCharSet = {'\x09', ' ', '\0'}): int = + delim: set[char] = {'\x09', ' ', '\0'}): int = result = i if s[result] == '\"': inc(result) @@ -70,7 +72,7 @@ proc parseWord(s: string, i: int, w: var string, add(w, s[result]) inc(result) -proc handleShortOption(p: var TOptParser) = +proc handleShortOption(p: var OptParser) = var i = p.pos p.kind = cmdShortOption add(p.key.string, p.cmd[i]) @@ -87,8 +89,7 @@ proc handleShortOption(p: var TOptParser) = if p.cmd[i] == '\0': p.inShortState = false p.pos = i -proc next*(p: var TOptParser) {. - rtl, extern: "npo$1".} = +proc next*(p: var OptParser) {.rtl, extern: "npo$1".} = ## parses the first or next option; ``p.kind`` describes what token has been ## parsed. ``p.key`` and ``p.val`` are set accordingly. var i = p.pos @@ -122,18 +123,16 @@ proc next*(p: var TOptParser) {. p.kind = cmdArgument p.pos = parseWord(p.cmd, i, p.key.string) -proc cmdLineRest*(p: TOptParser): TaintedString {. - rtl, extern: "npo$1".} = +proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1".} = ## retrieves the rest of the command line that has not been parsed yet. result = strip(substr(p.cmd, p.pos, len(p.cmd) - 1)).TaintedString when declared(initOptParser): - - iterator getopt*(): tuple[kind: TCmdLineKind, key, val: TaintedString] = + 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: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## var ## filename = "" ## for kind, key, val in getopt(): diff --git a/lib/pure/parseopt2.nim b/lib/pure/parseopt2.nim index 7638171d1..5b1f50958 100644 --- a/lib/pure/parseopt2.nim +++ b/lib/pure/parseopt2.nim @@ -1,13 +1,13 @@ # # -# Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf +# Nim's Runtime Library +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## This module provides the standard Nimrod command line parser. +## This module provides the standard Nim command line parser. ## It supports one convenience iterator over all command line options and some ## lower-level features. ## @@ -25,22 +25,24 @@ import os, strutils type - TCmdLineKind* = enum ## the detected command line token + CmdLineKind* = enum ## the detected command line token cmdEnd, ## end of command line reached cmdArgument, ## argument detected cmdLongOption, ## a long option ``--option`` detected cmdShortOption ## a short option ``-c`` detected - TOptParser* = - object of TObject ## this object implements the command line parser + OptParser* = + object of RootObj ## this object implements the command line parser cmd: seq[string] pos: int remainingShortOptions: string - kind*: TCmdLineKind ## the dected command line token + kind*: CmdLineKind ## the dected command line token key*, val*: TaintedString ## key and value pair; ``key`` is the option ## or the argument, ``value`` is not "" if ## the option was given a value -proc initOptParser*(cmdline: seq[string]): TOptParser {.rtl.} = +{.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].} + +proc initOptParser*(cmdline: seq[string]): OptParser {.rtl.} = ## Initalizes option parses with cmdline. cmdline should not contain ## argument 0 - program name. ## If cmdline == nil default to current command line arguments. @@ -54,7 +56,7 @@ proc initOptParser*(cmdline: seq[string]): TOptParser {.rtl.} = result.cmd = @cmdline -proc initOptParser*(cmdline: string): TOptParser {.rtl, deprecated.} = +proc initOptParser*(cmdline: string): OptParser {.rtl, deprecated.} = ## Initalizes option parses with cmdline. Splits cmdline in on spaces ## and calls initOptParser(openarray[string]) ## Do not use. @@ -64,13 +66,13 @@ proc initOptParser*(cmdline: string): TOptParser {.rtl, deprecated.} = return initOptParser(cmdline.split) when not defined(createNimRtl): - proc initOptParser*(): TOptParser = + proc initOptParser*(): OptParser = ## Initializes option parser from current command line arguments. return initOptParser(commandLineParams()) -proc next*(p: var TOptParser) {.rtl, extern: "npo$1".} +proc next*(p: var OptParser) {.rtl, extern: "npo$1".} -proc nextOption(p: var TOptParser, token: string, allowEmpty: bool) = +proc nextOption(p: var OptParser, token: string, allowEmpty: bool) = for splitchar in [':', '=']: if splitchar in token: let pos = token.find(splitchar) @@ -85,7 +87,7 @@ proc nextOption(p: var TOptParser, token: string, allowEmpty: bool) = p.remainingShortOptions = token[0..token.len-1] p.next() -proc next(p: var TOptParser) = +proc next(p: var OptParser) = if p.remainingShortOptions.len != 0: p.kind = cmdShortOption p.key = TaintedString(p.remainingShortOptions[0..0]) @@ -100,10 +102,10 @@ proc next(p: var TOptParser) = let token = p.cmd[p.pos] p.pos += 1 - if token.startswith("--"): + if token.startsWith("--"): p.kind = cmdLongOption nextOption(p, token[2..token.len-1], allowEmpty=true) - elif token.startswith("-"): + elif token.startsWith("-"): p.kind = cmdShortOption nextOption(p, token[1..token.len-1], allowEmpty=true) else: @@ -111,20 +113,22 @@ proc next(p: var TOptParser) = p.key = token p.val = "" -proc cmdLineRest*(p: TOptParser): TaintedString {.rtl, extern: "npo$1", deprecated.} = +proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1", deprecated.} = ## Returns part of command line string that has not been parsed yet. ## Do not use - does not correctly handle whitespace. return p.cmd[p.pos..p.cmd.len-1].join(" ") type - TGetoptResult* = tuple[kind: TCmdLineKind, key, val: TaintedString] + GetoptResult* = tuple[kind: CmdLineKind, key, val: TaintedString] + +{.deprecated: [TGetoptResult: GetoptResult].} when declared(paramCount): - iterator getopt*(): TGetoptResult = + iterator getopt*(): GetoptResult = ## 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:: nimrod + ## .. code-block:: nim ## var ## filename = "" ## for kind, key, val in getopt(): diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim index bd8836f7c..bb4ede779 100644 --- a/lib/pure/parsesql.nim +++ b/lib/pure/parsesql.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2009 Andreas Rumpf # # See the file "copying.txt", included in this @@ -10,13 +10,13 @@ ## The ``parsesql`` module implements a high performance SQL file ## parser. It parses PostgreSQL syntax and the SQL ANSI standard. -import +import hashes, strutils, lexbase, streams # ------------------- scanner ------------------------------------------------- type - TTokKind = enum ## enumeration of all SQL tokens + TokKind = enum ## enumeration of all SQL tokens tkInvalid, ## invalid token tkEof, ## end of file reached tkIdentifier, ## abc @@ -38,36 +38,38 @@ type tkBracketRi, ## ']' tkDot ## '.' - TToken {.final.} = object # a token - kind: TTokKind # the type of the token + Token = object # a token + kind: TokKind # the type of the token literal: string # the parsed (string) literal - TSqlLexer* = object of TBaseLexer ## the parser object. + SqlLexer* = object of BaseLexer ## the parser object. filename: string +{.deprecated: [TToken: Token, TSqlLexer: SqlLexer].} + const - tokKindToStr: array[TTokKind, string] = [ + tokKindToStr: array[TokKind, string] = [ "invalid", "[EOF]", "identifier", "quoted identifier", "string constant", "escape string constant", "dollar quoted constant", "bit string constant", "hex string constant", "integer constant", "numeric constant", "operator", ";", ":", ",", "(", ")", "[", "]", "." ] -proc open(L: var TSqlLexer, input: PStream, filename: string) = +proc open(L: var SqlLexer, input: Stream, filename: string) = lexbase.open(L, input) L.filename = filename -proc close(L: var TSqlLexer) = +proc close(L: var SqlLexer) = lexbase.close(L) -proc getColumn(L: TSqlLexer): int = +proc getColumn(L: SqlLexer): int = ## get the current column the parser has arrived at. - result = getColNumber(L, L.bufPos) + result = getColNumber(L, L.bufpos) -proc getLine(L: TSqlLexer): int = - result = L.linenumber +proc getLine(L: SqlLexer): int = + result = L.lineNumber -proc handleHexChar(c: var TSqlLexer, xi: var int) = +proc handleHexChar(c: var SqlLexer, xi: var int) = case c.buf[c.bufpos] of '0'..'9': xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0')) @@ -81,76 +83,76 @@ proc handleHexChar(c: var TSqlLexer, xi: var int) = else: discard -proc handleOctChar(c: var TSqlLexer, xi: var int) = +proc handleOctChar(c: var SqlLexer, xi: var int) = if c.buf[c.bufpos] in {'0'..'7'}: xi = (xi shl 3) or (ord(c.buf[c.bufpos]) - ord('0')) inc(c.bufpos) -proc getEscapedChar(c: var TSqlLexer, tok: var TToken) = +proc getEscapedChar(c: var SqlLexer, tok: var Token) = inc(c.bufpos) case c.buf[c.bufpos] of 'n', 'N': add(tok.literal, '\L') - Inc(c.bufpos) + inc(c.bufpos) of 'r', 'R', 'c', 'C': add(tok.literal, '\c') - Inc(c.bufpos) + inc(c.bufpos) of 'l', 'L': add(tok.literal, '\L') - Inc(c.bufpos) + inc(c.bufpos) of 'f', 'F': add(tok.literal, '\f') inc(c.bufpos) of 'e', 'E': add(tok.literal, '\e') - Inc(c.bufpos) + inc(c.bufpos) of 'a', 'A': add(tok.literal, '\a') - Inc(c.bufpos) + inc(c.bufpos) of 'b', 'B': add(tok.literal, '\b') - Inc(c.bufpos) + inc(c.bufpos) of 'v', 'V': add(tok.literal, '\v') - Inc(c.bufpos) + inc(c.bufpos) of 't', 'T': add(tok.literal, '\t') - Inc(c.bufpos) + inc(c.bufpos) of '\'', '\"': add(tok.literal, c.buf[c.bufpos]) - Inc(c.bufpos) + inc(c.bufpos) of '\\': add(tok.literal, '\\') - Inc(c.bufpos) + inc(c.bufpos) of 'x', 'X': inc(c.bufpos) var xi = 0 handleHexChar(c, xi) handleHexChar(c, xi) - add(tok.literal, Chr(xi)) + add(tok.literal, chr(xi)) of '0'..'7': var xi = 0 handleOctChar(c, xi) handleOctChar(c, xi) handleOctChar(c, xi) - if (xi <= 255): add(tok.literal, Chr(xi)) + if (xi <= 255): add(tok.literal, chr(xi)) else: tok.kind = tkInvalid else: tok.kind = tkInvalid -proc HandleCRLF(c: var TSqlLexer, pos: int): int = +proc handleCRLF(c: var SqlLexer, pos: int): int = case c.buf[pos] - of '\c': result = lexbase.HandleCR(c, pos) - of '\L': result = lexbase.HandleLF(c, pos) + of '\c': result = lexbase.handleCR(c, pos) + of '\L': result = lexbase.handleLF(c, pos) else: result = pos -proc skip(c: var TSqlLexer) = +proc skip(c: var SqlLexer) = var pos = c.bufpos var buf = c.buf var nested = 0 while true: case buf[pos] of ' ', '\t': - Inc(pos) + inc(pos) of '-': if buf[pos+1] == '-': while not (buf[pos] in {'\c', '\L', lexbase.EndOfFile}): inc(pos) @@ -163,7 +165,7 @@ proc skip(c: var TSqlLexer) = case buf[pos] of '\0': break of '\c', '\L': - pos = HandleCRLF(c, pos) + pos = handleCRLF(c, pos) buf = c.buf of '*': if buf[pos+1] == '/': @@ -181,14 +183,14 @@ proc skip(c: var TSqlLexer) = else: inc(pos) else: break of '\c', '\L': - pos = HandleCRLF(c, pos) + pos = handleCRLF(c, pos) buf = c.buf else: break # EndOfFile also leaves the loop c.bufpos = pos -proc getString(c: var TSqlLexer, tok: var TToken, kind: TTokKind) = - var pos = c.bufPos + 1 +proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) = + var pos = c.bufpos + 1 var buf = c.buf tok.kind = kind block parseLoop: @@ -206,16 +208,16 @@ proc getString(c: var TSqlLexer, tok: var TToken, kind: TTokKind) = tok.kind = tkInvalid break parseLoop elif (ch == '\\') and kind == tkEscapeConstant: - c.bufPos = pos + c.bufpos = pos getEscapedChar(c, tok) - pos = c.bufPos + pos = c.bufpos else: add(tok.literal, ch) - Inc(pos) + inc(pos) c.bufpos = pos - var line = c.linenumber + var line = c.lineNumber skip(c) - if c.linenumber > line: + if c.lineNumber > line: # a new line whitespace has been parsed, so we check if the string # continues after the whitespace: buf = c.buf # may have been reallocated @@ -225,8 +227,8 @@ proc getString(c: var TSqlLexer, tok: var TToken, kind: TTokKind) = else: break parseLoop c.bufpos = pos -proc getDollarString(c: var TSqlLexer, tok: var TToken) = - var pos = c.bufPos + 1 +proc getDollarString(c: var SqlLexer, tok: var Token) = + var pos = c.bufpos + 1 var buf = c.buf tok.kind = tkDollarQuotedConstant var tag = "$" @@ -240,7 +242,7 @@ proc getDollarString(c: var TSqlLexer, tok: var TToken) = while true: case buf[pos] of '\c', '\L': - pos = HandleCRLF(c, pos) + pos = handleCRLF(c, pos) buf = c.buf add(tok.literal, "\L") of '\0': @@ -261,19 +263,19 @@ proc getDollarString(c: var TSqlLexer, tok: var TToken) = inc(pos) c.bufpos = pos -proc getSymbol(c: var TSqlLexer, tok: var TToken) = +proc getSymbol(c: var SqlLexer, tok: var Token) = var pos = c.bufpos var buf = c.buf while true: add(tok.literal, buf[pos]) - Inc(pos) + inc(pos) if buf[pos] notin {'a'..'z','A'..'Z','0'..'9','_','$', '\128'..'\255'}: break c.bufpos = pos tok.kind = tkIdentifier -proc getQuotedIdentifier(c: var TSqlLexer, tok: var TToken) = - var pos = c.bufPos + 1 +proc getQuotedIdentifier(c: var SqlLexer, tok: var Token) = + var pos = c.bufpos + 1 var buf = c.buf tok.kind = tkQuotedIdentifier while true: @@ -290,11 +292,11 @@ proc getQuotedIdentifier(c: var TSqlLexer, tok: var TToken) = break else: add(tok.literal, ch) - Inc(pos) + inc(pos) c.bufpos = pos -proc getBitHexString(c: var TSqlLexer, tok: var TToken, validChars: TCharSet) = - var pos = c.bufPos + 1 +proc getBitHexString(c: var SqlLexer, tok: var Token, validChars: set[char]) = + var pos = c.bufpos + 1 var buf = c.buf block parseLoop: while true: @@ -302,7 +304,7 @@ proc getBitHexString(c: var TSqlLexer, tok: var TToken, validChars: TCharSet) = var ch = buf[pos] if ch in validChars: add(tok.literal, ch) - Inc(pos) + inc(pos) elif ch == '\'': inc(pos) break @@ -310,9 +312,9 @@ proc getBitHexString(c: var TSqlLexer, tok: var TToken, validChars: TCharSet) = tok.kind = tkInvalid break parseLoop c.bufpos = pos - var line = c.linenumber + var line = c.lineNumber skip(c) - if c.linenumber > line: + if c.lineNumber > line: # a new line whitespace has been parsed, so we check if the string # continues after the whitespace: buf = c.buf # may have been reallocated @@ -322,9 +324,9 @@ proc getBitHexString(c: var TSqlLexer, tok: var TToken, validChars: TCharSet) = else: break parseLoop c.bufpos = pos -proc getNumeric(c: var TSqlLexer, tok: var TToken) = +proc getNumeric(c: var SqlLexer, tok: var Token) = tok.kind = tkInteger - var pos = c.bufPos + var pos = c.bufpos var buf = c.buf while buf[pos] in Digits: add(tok.literal, buf[pos]) @@ -353,11 +355,11 @@ proc getNumeric(c: var TSqlLexer, tok: var TToken) = tok.kind = tkInvalid c.bufpos = pos -proc getOperator(c: var TSqlLexer, tok: var TToken) = +proc getOperator(c: var SqlLexer, tok: var Token) = const operators = {'+', '-', '*', '/', '<', '>', '=', '~', '!', '@', '#', '%', '^', '&', '|', '`', '?'} tok.kind = tkOperator - var pos = c.bufPos + var pos = c.bufpos var buf = c.buf var trailingPlusMinus = false while true: @@ -379,14 +381,14 @@ proc getOperator(c: var TSqlLexer, tok: var TToken) = inc(pos) c.bufpos = pos -proc getTok(c: var TSqlLexer, tok: var TToken) = +proc getTok(c: var SqlLexer, tok: var Token) = tok.kind = tkInvalid - setlen(tok.literal, 0) + setLen(tok.literal, 0) skip(c) case c.buf[c.bufpos] of ';': - tok.kind = tkSemiColon - inc(c.bufPos) + tok.kind = tkSemicolon + inc(c.bufpos) add(tok.literal, ';') of ',': tok.kind = tkComma @@ -397,19 +399,19 @@ proc getTok(c: var TSqlLexer, tok: var TToken) = inc(c.bufpos) add(tok.literal, ':') of 'e', 'E': - if c.buf[c.bufPos + 1] == '\'': - Inc(c.bufPos) + if c.buf[c.bufpos + 1] == '\'': + inc(c.bufpos) getString(c, tok, tkEscapeConstant) else: getSymbol(c, tok) of 'b', 'B': - if c.buf[c.bufPos + 1] == '\'': + if c.buf[c.bufpos + 1] == '\'': tok.kind = tkBitStringConstant getBitHexString(c, tok, {'0'..'1'}) else: getSymbol(c, tok) of 'x', 'X': - if c.buf[c.bufPos + 1] == '\'': + if c.buf[c.bufpos + 1] == '\'': tok.kind = tkHexStringConstant getBitHexString(c, tok, {'a'..'f','A'..'F','0'..'9'}) else: @@ -421,18 +423,18 @@ proc getTok(c: var TSqlLexer, tok: var TToken) = add(tok.literal, '[') of ']': tok.kind = tkBracketRi - Inc(c.bufpos) + inc(c.bufpos) add(tok.literal, ']') of '(': tok.kind = tkParLe - Inc(c.bufpos) + inc(c.bufpos) add(tok.literal, '(') of ')': tok.kind = tkParRi - Inc(c.bufpos) + inc(c.bufpos) add(tok.literal, ')') of '.': - if c.buf[c.bufPos + 1] in Digits: + if c.buf[c.bufpos + 1] in Digits: getNumeric(c, tok) else: tok.kind = tkDot @@ -454,7 +456,7 @@ proc getTok(c: var TSqlLexer, tok: var TToken) = add(tok.literal, c.buf[c.bufpos]) inc(c.bufpos) -proc errorStr(L: TSqlLexer, msg: string): string = +proc errorStr(L: SqlLexer, msg: string): string = result = "$1($2, $3) Error: $4" % [L.filename, $getLine(L), $getColumn(L), msg] @@ -465,7 +467,7 @@ proc errorStr(L: TSqlLexer, msg: string): string = # :: left PostgreSQL-style typecast # [ ] left array element selection # - right unary minus -# ^ left exponentiation +# ^ left exponentiation # * / % left multiplication, division, modulo # + - left addition, subtraction # IS IS TRUE, IS FALSE, IS UNKNOWN, IS NULL @@ -483,7 +485,7 @@ proc errorStr(L: TSqlLexer, msg: string): string = # OR left logical disjunction type - TSqlNodeKind* = enum ## kind of SQL abstract syntax tree + SqlNodeKind* = enum ## kind of SQL abstract syntax tree nkNone, nkIdent, nkStringLit, @@ -536,79 +538,82 @@ type nkEnumDef type - EInvalidSql* = object of EInvalidValue ## Invalid SQL encountered - PSqlNode* = ref TSqlNode ## an SQL abstract syntax tree node - TSqlNode* = object ## an SQL abstract syntax tree node - case kind*: TSqlNodeKind ## kind of syntax tree + SqlParseError* = object of ValueError ## Invalid SQL encountered + SqlNode* = ref SqlNodeObj ## an SQL abstract syntax tree node + SqlNodeObj* = object ## an SQL abstract syntax tree node + case kind*: SqlNodeKind ## kind of syntax tree of nkIdent, nkStringLit, nkBitStringLit, nkHexStringLit, nkIntegerLit, nkNumericLit: strVal*: string ## AST leaf: the identifier, numeric literal ## string literal, etc. else: - sons*: seq[PSqlNode] ## the node's children + sons*: seq[SqlNode] ## the node's children - TSqlParser* = object of TSqlLexer ## SQL parser object - tok: TToken + SqlParser* = object of SqlLexer ## SQL parser object + tok: Token -proc newNode(k: TSqlNodeKind): PSqlNode = +{.deprecated: [EInvalidSql: SqlParseError, PSqlNode: SqlNode, + TSqlNode: SqlNodeObj, TSqlParser: SqlParser, TSqlNodeKind: SqlNodeKind].} + +proc newNode(k: SqlNodeKind): SqlNode = new(result) result.kind = k -proc newNode(k: TSqlNodeKind, s: string): PSqlNode = +proc newNode(k: SqlNodeKind, s: string): SqlNode = new(result) result.kind = k result.strVal = s -proc len*(n: PSqlNode): int = +proc len*(n: SqlNode): int = if isNil(n.sons): result = 0 else: result = n.sons.len -proc add*(father, n: PSqlNode) = +proc add*(father, n: SqlNode) = if isNil(father.sons): father.sons = @[] add(father.sons, n) -proc getTok(p: var TSqlParser) = +proc getTok(p: var SqlParser) = getTok(p, p.tok) -proc sqlError(p: TSqlParser, msg: string) = - var e: ref EInvalidSql +proc sqlError(p: SqlParser, msg: string) = + var e: ref SqlParseError new(e) e.msg = errorStr(p, msg) raise e -proc isKeyw(p: TSqlParser, keyw: string): bool = +proc isKeyw(p: SqlParser, keyw: string): bool = result = p.tok.kind == tkIdentifier and cmpIgnoreCase(p.tok.literal, keyw) == 0 -proc isOpr(p: TSqlParser, opr: string): bool = +proc isOpr(p: SqlParser, opr: string): bool = result = p.tok.kind == tkOperator and cmpIgnoreCase(p.tok.literal, opr) == 0 -proc optKeyw(p: var TSqlParser, keyw: string) = +proc optKeyw(p: var SqlParser, keyw: string) = if p.tok.kind == tkIdentifier and cmpIgnoreCase(p.tok.literal, keyw) == 0: getTok(p) -proc expectIdent(p: TSqlParser) = +proc expectIdent(p: SqlParser) = if p.tok.kind != tkIdentifier and p.tok.kind != tkQuotedIdentifier: sqlError(p, "identifier expected") -proc expect(p: TSqlParser, kind: TTokKind) = +proc expect(p: SqlParser, kind: TokKind) = if p.tok.kind != kind: sqlError(p, tokKindToStr[kind] & " expected") -proc eat(p: var TSqlParser, kind: TTokKind) = +proc eat(p: var SqlParser, kind: TokKind) = if p.tok.kind == kind: getTok(p) else: sqlError(p, tokKindToStr[kind] & " expected") -proc eat(p: var TSqlParser, keyw: string) = +proc eat(p: var SqlParser, keyw: string) = if isKeyw(p, keyw): getTok(p) else: sqlError(p, keyw.toUpper() & " expected") -proc parseDataType(p: var TSqlParser): PSqlNode = +proc parseDataType(p: var SqlParser): SqlNode = if isKeyw(p, "enum"): result = newNode(nkEnumDef) getTok(p) @@ -636,7 +641,7 @@ proc parseDataType(p: var TSqlParser): PSqlNode = getTok(p) eat(p, tkParRi) -proc getPrecedence(p: TSqlParser): int = +proc getPrecedence(p: SqlParser): int = if isOpr(p, "*") or isOpr(p, "/") or isOpr(p, "%"): result = 6 elif isOpr(p, "+") or isOpr(p, "-"): @@ -655,9 +660,9 @@ proc getPrecedence(p: TSqlParser): int = else: result = - 1 -proc parseExpr(p: var TSqlParser): PSqlNode +proc parseExpr(p: var SqlParser): SqlNode -proc identOrLiteral(p: var TSqlParser): PSqlNode = +proc identOrLiteral(p: var SqlParser): SqlNode = case p.tok.kind of tkIdentifier, tkQuotedIdentifier: result = newNode(nkIdent, p.tok.literal) @@ -685,7 +690,7 @@ proc identOrLiteral(p: var TSqlParser): PSqlNode = sqlError(p, "expression expected") getTok(p) # we must consume a token here to prevend endless loops! -proc primary(p: var TSqlParser): PSqlNode = +proc primary(p: var SqlParser): SqlNode = if p.tok.kind == tkOperator or isKeyw(p, "not"): result = newNode(nkPrefix) result.add(newNode(nkIdent, p.tok.literal)) @@ -723,9 +728,9 @@ proc primary(p: var TSqlParser): PSqlNode = getTok(p) else: break -proc lowestExprAux(p: var TSqlParser, v: var PSqlNode, limit: int): int = +proc lowestExprAux(p: var SqlParser, v: var SqlNode, limit: int): int = var - v2, node, opNode: PSqlNode + v2, node, opNode: SqlNode v = primary(p) # expand while operators have priorities higher than 'limit' var opPred = getPrecedence(p) result = opPred @@ -740,14 +745,14 @@ proc lowestExprAux(p: var TSqlParser, v: var PSqlNode, limit: int): int = v = node opPred = getPrecedence(p) -proc parseExpr(p: var TSqlParser): PSqlNode = +proc parseExpr(p: var SqlParser): SqlNode = discard lowestExprAux(p, result, - 1) -proc parseTableName(p: var TSqlParser): PSqlNode = +proc parseTableName(p: var SqlParser): SqlNode = expectIdent(p) result = primary(p) -proc parseColumnReference(p: var TSqlParser): PSqlNode = +proc parseColumnReference(p: var SqlParser): SqlNode = result = parseTableName(p) if p.tok.kind == tkParLe: getTok(p) @@ -760,12 +765,12 @@ proc parseColumnReference(p: var TSqlParser): PSqlNode = result.add(parseTableName(p)) eat(p, tkParRi) -proc parseCheck(p: var TSqlParser): PSqlNode = +proc parseCheck(p: var SqlParser): SqlNode = getTok(p) result = newNode(nkCheck) result.add(parseExpr(p)) -proc parseConstraint(p: var TSqlParser): PSqlNode = +proc parseConstraint(p: var SqlParser): SqlNode = getTok(p) result = newNode(nkConstraint) expectIdent(p) @@ -774,7 +779,7 @@ proc parseConstraint(p: var TSqlParser): PSqlNode = eat(p, "check") result.add(parseExpr(p)) -proc parseColumnConstraints(p: var TSqlParser, result: PSqlNode) = +proc parseColumnConstraints(p: var SqlParser, result: SqlNode) = while true: if isKeyw(p, "default"): getTok(p) @@ -806,7 +811,7 @@ proc parseColumnConstraints(p: var TSqlParser, result: PSqlNode) = else: break -proc parseColumnDef(p: var TSqlParser): PSqlNode = +proc parseColumnDef(p: var SqlParser): SqlNode = expectIdent(p) result = newNode(nkColumnDef) result.add(newNode(nkIdent, p.tok.literal)) @@ -814,7 +819,7 @@ proc parseColumnDef(p: var TSqlParser): PSqlNode = result.add(parseDataType(p)) parseColumnConstraints(p, result) -proc parseIfNotExists(p: var TSqlParser, k: TSqlNodeKind): PSqlNode = +proc parseIfNotExists(p: var SqlParser, k: SqlNodeKind): SqlNode = getTok(p) if isKeyw(p, "if"): getTok(p) @@ -824,7 +829,7 @@ proc parseIfNotExists(p: var TSqlParser, k: TSqlNodeKind): PSqlNode = else: result = newNode(k) -proc parseParIdentList(p: var TSqlParser, father: PSqlNode) = +proc parseParIdentList(p: var SqlParser, father: SqlNode) = eat(p, tkParLe) while true: expectIdent(p) @@ -834,7 +839,7 @@ proc parseParIdentList(p: var TSqlParser, father: PSqlNode) = getTok(p) eat(p, tkParRi) -proc parseTableConstraint(p: var TSqlParser): PSqlNode = +proc parseTableConstraint(p: var SqlParser): SqlNode = if isKeyw(p, "primary"): getTok(p) eat(p, "key") @@ -861,7 +866,7 @@ proc parseTableConstraint(p: var TSqlParser): PSqlNode = else: sqlError(p, "column definition expected") -proc parseTableDef(p: var TSqlParser): PSqlNode = +proc parseTableDef(p: var SqlParser): SqlNode = result = parseIfNotExists(p, nkCreateTable) expectIdent(p) result.add(newNode(nkIdent, p.tok.literal)) @@ -876,7 +881,7 @@ proc parseTableDef(p: var TSqlParser): PSqlNode = if p.tok.kind != tkComma: break eat(p, tkParRi) -proc parseTypeDef(p: var TSqlParser): PSqlNode = +proc parseTypeDef(p: var SqlParser): SqlNode = result = parseIfNotExists(p, nkCreateType) expectIdent(p) result.add(newNode(nkIdent, p.tok.literal)) @@ -884,12 +889,12 @@ proc parseTypeDef(p: var TSqlParser): PSqlNode = eat(p, "as") result.add(parseDataType(p)) -proc parseWhere(p: var TSqlParser): PSqlNode = +proc parseWhere(p: var SqlParser): SqlNode = getTok(p) result = newNode(nkWhere) result.add(parseExpr(p)) -proc parseIndexDef(p: var TSqlParser): PSqlNode = +proc parseIndexDef(p: var SqlParser): SqlNode = result = parseIfNotExists(p, nkCreateIndex) if isKeyw(p, "primary"): getTok(p) @@ -914,7 +919,7 @@ proc parseIndexDef(p: var TSqlParser): PSqlNode = getTok(p) eat(p, tkParRi) -proc parseInsert(p: var TSqlParser): PSqlNode = +proc parseInsert(p: var SqlParser): SqlNode = getTok(p) eat(p, "into") expectIdent(p) @@ -941,7 +946,7 @@ proc parseInsert(p: var TSqlParser): PSqlNode = result.add(n) eat(p, tkParRi) -proc parseUpdate(p: var TSqlParser): PSqlNode = +proc parseUpdate(p: var SqlParser): SqlNode = getTok(p) result = newNode(nkUpdate) result.add(primary(p)) @@ -962,7 +967,7 @@ proc parseUpdate(p: var TSqlParser): PSqlNode = else: result.add(nil) -proc parseDelete(p: var TSqlParser): PSqlNode = +proc parseDelete(p: var SqlParser): SqlNode = getTok(p) result = newNode(nkDelete) eat(p, "from") @@ -972,7 +977,7 @@ proc parseDelete(p: var TSqlParser): PSqlNode = else: result.add(nil) -proc parseSelect(p: var TSqlParser): PSqlNode = +proc parseSelect(p: var SqlParser): SqlNode = getTok(p) if isKeyw(p, "distinct"): getTok(p) @@ -1041,7 +1046,7 @@ proc parseSelect(p: var TSqlParser): PSqlNode = getTok(p) result.add(n) -proc parseStmt(p: var TSqlParser): PSqlNode = +proc parseStmt(p: var SqlParser): SqlNode = if isKeyw(p, "create"): getTok(p) optKeyw(p, "cached") @@ -1071,43 +1076,43 @@ proc parseStmt(p: var TSqlParser): PSqlNode = else: sqlError(p, "CREATE expected") -proc open(p: var TSqlParser, input: PStream, filename: string) = +proc open(p: var SqlParser, input: Stream, filename: string) = ## opens the parser `p` and assigns the input stream `input` to it. ## `filename` is only used for error messages. - open(TSqlLexer(p), input, filename) + open(SqlLexer(p), input, filename) p.tok.kind = tkInvalid p.tok.literal = "" getTok(p) -proc parse(p: var TSqlParser): PSqlNode = +proc parse(p: var SqlParser): SqlNode = ## parses the content of `p`'s input stream and returns the SQL AST. ## Syntax errors raise an `EInvalidSql` exception. result = newNode(nkStmtList) while p.tok.kind != tkEof: var s = parseStmt(p) - eat(p, tkSemiColon) + eat(p, tkSemicolon) result.add(s) if result.len == 1: result = result.sons[0] -proc close(p: var TSqlParser) = +proc close(p: var SqlParser) = ## closes the parser `p`. The associated input stream is closed too. - close(TSqlLexer(p)) + close(SqlLexer(p)) -proc parseSQL*(input: PStream, filename: string): PSqlNode = +proc parseSQL*(input: Stream, filename: string): SqlNode = ## parses the SQL from `input` into an AST and returns the AST. ## `filename` is only used for error messages. ## Syntax errors raise an `EInvalidSql` exception. - var p: TSqlParser + var p: SqlParser open(p, input, filename) try: result = parse(p) finally: close(p) -proc ra(n: PSqlNode, s: var string, indent: int) +proc ra(n: SqlNode, s: var string, indent: int) -proc rs(n: PSqlNode, s: var string, indent: int, +proc rs(n: SqlNode, s: var string, indent: int, prefix = "(", suffix = ")", sep = ", ") = if n.len > 0: @@ -1117,7 +1122,7 @@ proc rs(n: PSqlNode, s: var string, indent: int, ra(n.sons[i], s, indent) s.add(suffix) -proc ra(n: PSqlNode, s: var string, indent: int) = +proc ra(n: SqlNode, s: var string, indent: int) = if n == nil: return case n.kind of nkNone: discard @@ -1320,7 +1325,7 @@ proc ra(n: PSqlNode, s: var string, indent: int) = #for x, y, z in db.select(fromm = a, b where = a.name == b.name): # writeln x, y, z -proc renderSQL*(n: PSqlNode): string = +proc renderSQL*(n: SqlNode): string = ## Converts an SQL abstract syntax tree to its string representation. result = "" ra(n, result, 0) diff --git a/lib/pure/parseurl.nim b/lib/pure/parseurl.nim index 67c6de905..f27cd8c12 100644 --- a/lib/pure/parseurl.nim +++ b/lib/pure/parseurl.nim @@ -1,26 +1,30 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Dominik Picheta # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## Parses & constructs URLs. +## **Warnings:** This module is deprecated since version 0.10.2. +## Use the `uri `_ module instead. ## -## **Note**: This module will be deprecated in the future and merged into a -## new ``url`` module. +## Parses & constructs URLs. + +{.deprecated.} import strutils type - TUrl* = tuple[ ## represents a *Uniform Resource Locator* (URL) - ## any optional component is "" if it does not exist + Url* = tuple[ ## represents a *Uniform Resource Locator* (URL) + ## any optional component is "" if it does not exist scheme, username, password, hostname, port, path, query, anchor: string] - -proc parseUrl*(url: string): TUrl {.deprecated.} = + +{.deprecated: [TUrl: Url].} + +proc parseUrl*(url: string): Url {.deprecated.} = var i = 0 var scheme, username, password: string = "" @@ -29,12 +33,12 @@ proc parseUrl*(url: string): TUrl {.deprecated.} = var temp = "" if url[i] != '/': # url isn't a relative path - while True: + while true: # Scheme if url[i] == ':': if url[i+1] == '/' and url[i+2] == '/': scheme = temp - temp.setlen(0) + temp.setLen(0) inc(i, 3) # Skip the // # Authority(username, password) if url[i] == '@': @@ -43,7 +47,7 @@ proc parseUrl*(url: string): TUrl {.deprecated.} = if colon >= 0: password = username.substr(colon+1) username = username.substr(0, colon-1) - temp.setlen(0) + temp.setLen(0) inc(i) #Skip the @ # hostname(subdomain, domain, port) if url[i] == '/' or url[i] == '\0': @@ -53,7 +57,7 @@ proc parseUrl*(url: string): TUrl {.deprecated.} = port = hostname.substr(colon+1) hostname = hostname.substr(0, colon-1) - temp.setlen(0) + temp.setLen(0) break temp.add(url[i]) @@ -61,16 +65,16 @@ proc parseUrl*(url: string): TUrl {.deprecated.} = if url[i] == '/': inc(i) # Skip the '/' # Path - while True: + while true: if url[i] == '?': path = temp - temp.setlen(0) + temp.setLen(0) if url[i] == '#': if temp[0] == '?': query = temp else: path = temp - temp.setlen(0) + temp.setLen(0) if url[i] == '\0': if temp[0] == '?': @@ -86,7 +90,7 @@ proc parseUrl*(url: string): TUrl {.deprecated.} = return (scheme, username, password, hostname, port, path, query, anchor) -proc `$`*(u: TUrl): string {.deprecated.} = +proc `$`*(u: Url): string {.deprecated.} = ## turns the URL `u` into its string representation. result = "" if u.scheme.len > 0: diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index 06ee07aa8..1efb141fc 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -35,7 +35,7 @@ proc parseHex*(s: string, number: var int, start = 0): int {. ## can use this feature to *chain* calls, though the result int will quickly ## overflow. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## var value = 0 ## discard parseHex("0x38", value) ## assert value == 56 @@ -227,100 +227,15 @@ proc parseInt*(s: string, number: var int, start = 0): int {. result = parseBiggestInt(s, res, start) if (sizeof(int) <= 4) and ((res < low(int)) or (res > high(int))): - raise newException(EOverflow, "overflow") + raise newException(OverflowError, "overflow") else: number = int(res) -when defined(nimParseBiggestFloatMagic): - proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {. - magic: "ParseBiggestFloat", importc: "nimParseBiggestFloat", noSideEffect.} - ## parses a float starting at `start` and stores the value into `number`. - ## Result is the number of processed chars or 0 if a parsing error - ## occurred. -else: - proc tenToThePowerOf(b: int): BiggestFloat = - var b = b - var a = 10.0 - result = 1.0 - while true: - if (b and 1) == 1: - result *= a - b = b shr 1 - if b == 0: break - a *= a - - proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {. - rtl, extern: "npuParseBiggestFloat", noSideEffect.} = - ## parses a float starting at `start` and stores the value into `number`. - ## Result is the number of processed chars or 0 if there occured a parsing - ## error. - var - esign = 1.0 - sign = 1.0 - i = start - exponent: int - flags: int - number = 0.0 - if s[i] == '+': inc(i) - elif s[i] == '-': - sign = -1.0 - inc(i) - if s[i] == 'N' or s[i] == 'n': - if s[i+1] == 'A' or s[i+1] == 'a': - if s[i+2] == 'N' or s[i+2] == 'n': - if s[i+3] notin IdentChars: - number = NaN - return i+3 - start - return 0 - if s[i] == 'I' or s[i] == 'i': - if s[i+1] == 'N' or s[i+1] == 'n': - if s[i+2] == 'F' or s[i+2] == 'f': - if s[i+3] notin IdentChars: - number = Inf*sign - return i+3 - start - return 0 - while s[i] in {'0'..'9'}: - # Read integer part - flags = flags or 1 - number = number * 10.0 + toFloat(ord(s[i]) - ord('0')) - inc(i) - while s[i] == '_': inc(i) - # Decimal? - if s[i] == '.': - var hd = 1.0 - inc(i) - while s[i] in {'0'..'9'}: - # Read fractional part - flags = flags or 2 - number = number * 10.0 + toFloat(ord(s[i]) - ord('0')) - hd = hd * 10.0 - inc(i) - while s[i] == '_': inc(i) - number = number / hd # this complicated way preserves precision - # Again, read integer and fractional part - if flags == 0: return 0 - # Exponent? - if s[i] in {'e', 'E'}: - inc(i) - if s[i] == '+': - inc(i) - elif s[i] == '-': - esign = -1.0 - inc(i) - if s[i] notin {'0'..'9'}: - return 0 - while s[i] in {'0'..'9'}: - exponent = exponent * 10 + ord(s[i]) - ord('0') - inc(i) - while s[i] == '_': inc(i) - # Calculate Exponent - let hd = tenToThePowerOf(exponent) - if esign > 0.0: number = number * hd - else: number = number / hd - # evaluate sign - number = number * sign - result = i - start - +proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {. + magic: "ParseBiggestFloat", importc: "nimParseBiggestFloat", noSideEffect.} + ## parses a float starting at `start` and stores the value into `number`. + ## Result is the number of processed chars or 0 if a parsing error + ## occurred. proc parseFloat*(s: string, number: var float, start = 0): int {. rtl, extern: "npuParseFloat", noSideEffect.} = @@ -332,7 +247,7 @@ proc parseFloat*(s: string, number: var float, start = 0): int {. number = bf type - TInterpolatedKind* = enum ## describes for `interpolatedFragments` + InterpolatedKind* = enum ## describes for `interpolatedFragments` ## which part of the interpolated string is ## yielded; for example in "str$$$var${expr}" ikStr, ## ``str`` part of the interpolated string @@ -340,19 +255,21 @@ type ikVar, ## ``var`` part of the interpolated string ikExpr ## ``expr`` part of the interpolated string -iterator interpolatedFragments*(s: string): tuple[kind: TInterpolatedKind, +{.deprecated: [TInterpolatedKind: InterpolatedKind].} + +iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind, value: string] = ## Tokenizes the string `s` into substrings for interpolation purposes. ## ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## for k, v in interpolatedFragments(" $this is ${an example} $$"): ## echo "(", k, ", \"", v, "\")" ## ## Results in: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## (ikString, " ") ## (ikExpr, "this") ## (ikString, " is ") @@ -360,7 +277,7 @@ iterator interpolatedFragments*(s: string): tuple[kind: TInterpolatedKind, ## (ikString, " ") ## (ikDollar, "$") var i = 0 - var kind: TInterpolatedKind + var kind: InterpolatedKind while true: var j = i if s[j] == '$': @@ -376,7 +293,7 @@ iterator interpolatedFragments*(s: string): tuple[kind: TInterpolatedKind, break dec nesting of '\0': - raise newException(EInvalidValue, + raise newException(ValueError, "Expected closing '}': " & s[i..s.len]) else: discard inc j @@ -392,7 +309,7 @@ iterator interpolatedFragments*(s: string): tuple[kind: TInterpolatedKind, inc i # skip $ kind = ikDollar else: - raise newException(EInvalidValue, + raise newException(ValueError, "Unable to parse a varible name at " & s[i..s.len]) else: while j < s.len and s[j] != '$': inc j diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim index 667b8aed6..39dead3c0 100644 --- a/lib/pure/parsexml.nim +++ b/lib/pure/parsexml.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2010 Andreas Rumpf # # See the file "copying.txt", included in this @@ -33,7 +33,7 @@ ## XML parser to accomplish a simple task: To determine the title of an HTML ## document. ## -## .. code-block:: nimrod +## .. code-block:: nim ## :file: examples/htmltitle.nim ## ## @@ -44,7 +44,7 @@ ## XML parser to accomplish another simple task: To determine all the links ## an HTML document contains. ## -## .. code-block:: nimrod +## .. code-block:: nim ## :file: examples/htmlrefs.nim ## @@ -56,7 +56,7 @@ import # xmlElementCloseEnd, ## ``/>`` type - TXmlEventKind* = enum ## enumation of all events that may occur when parsing + XmlEventKind* = enum ## enumation of all events that may occur when parsing xmlError, ## an error ocurred during parsing xmlEof, ## end of file reached xmlCharData, ## character data @@ -72,7 +72,7 @@ type xmlEntity, ## &entity; xmlSpecial ## ```` - TXmlError* = enum ## enumeration that lists all errors that can occur + XmlErrorKind* = enum ## enumeration that lists all errors that can occur errNone, ## no error errEndOfCDataExpected, ## ``]]>`` expected errNameExpected, ## name expected @@ -83,23 +83,26 @@ type errQuoteExpected, ## ``"`` or ``'`` expected errEndOfCommentExpected ## ``-->`` expected - TParserState = enum + ParserState = enum stateStart, stateNormal, stateAttr, stateEmptyElementTag, stateError - TXmlParseOption* = enum ## options for the XML parser + XmlParseOption* = enum ## options for the XML parser reportWhitespace, ## report whitespace reportComments ## report comments - TXmlParser* = object of TBaseLexer ## the parser object. + XmlParser* = object of BaseLexer ## the parser object. a, b, c: string - kind: TXmlEventKind - err: TXmlError - state: TParserState + kind: XmlEventKind + err: XmlErrorKind + state: ParserState filename: string - options: set[TXmlParseOption] - + options: set[XmlParseOption] + +{.deprecated: [TXmlParser: XmlParser, TXmlParseOptions: XmlParseOption, + TXmlError: XmlErrorKind, TXmlEventKind: XmlEventKind].} + const - errorMessages: array [TXmlError, string] = [ + errorMessages: array[XmlErrorKind, string] = [ "no error", "']]>' expected", "name expected", @@ -111,8 +114,8 @@ const "'-->' expected" ] -proc open*(my: var TXmlParser, input: PStream, filename: string, - options: set[TXmlParseOption] = {}) = +proc open*(my: var XmlParser, input: Stream, filename: string, + options: set[XmlParseOption] = {}) = ## initializes the parser with an input stream. `Filename` is only used ## for nice error messages. The parser's behaviour can be controlled by ## the `options` parameter: If `options` contains ``reportWhitespace`` @@ -127,97 +130,97 @@ proc open*(my: var TXmlParser, input: PStream, filename: string, my.b = "" my.options = options -proc close*(my: var TXmlParser) {.inline.} = +proc close*(my: var XmlParser) {.inline.} = ## closes the parser `my` and its associated input stream. lexbase.close(my) -proc kind*(my: TXmlParser): TXmlEventKind {.inline.} = +proc kind*(my: XmlParser): XmlEventKind {.inline.} = ## returns the current event type for the XML parser return my.kind -proc charData*(my: TXmlParser): string {.inline.} = +proc charData*(my: XmlParser): string {.inline.} = ## returns the character data for the events: ``xmlCharData``, ## ``xmlWhitespace``, ``xmlComment``, ``xmlCData``, ``xmlSpecial`` assert(my.kind in {xmlCharData, xmlWhitespace, xmlComment, xmlCData, xmlSpecial}) return my.a -proc elementName*(my: TXmlParser): string {.inline.} = +proc elementName*(my: XmlParser): string {.inline.} = ## returns the element name for the events: ``xmlElementStart``, ## ``xmlElementEnd``, ``xmlElementOpen`` assert(my.kind in {xmlElementStart, xmlElementEnd, xmlElementOpen}) return my.a -proc entityName*(my: TXmlParser): string {.inline.} = +proc entityName*(my: XmlParser): string {.inline.} = ## returns the entity name for the event: ``xmlEntity`` assert(my.kind == xmlEntity) return my.a -proc attrKey*(my: TXmlParser): string {.inline.} = +proc attrKey*(my: XmlParser): string {.inline.} = ## returns the attribute key for the event ``xmlAttribute`` assert(my.kind == xmlAttribute) return my.a -proc attrValue*(my: TXmlParser): string {.inline.} = +proc attrValue*(my: XmlParser): string {.inline.} = ## returns the attribute value for the event ``xmlAttribute`` assert(my.kind == xmlAttribute) return my.b -proc PIName*(my: TXmlParser): string {.inline.} = +proc piName*(my: XmlParser): string {.inline.} = ## returns the processing instruction name for the event ``xmlPI`` assert(my.kind == xmlPI) return my.a -proc PIRest*(my: TXmlParser): string {.inline.} = +proc piRest*(my: XmlParser): string {.inline.} = ## returns the rest of the processing instruction for the event ``xmlPI`` assert(my.kind == xmlPI) return my.b -proc rawData*(my: TXmlParser): string {.inline.} = +proc rawData*(my: XmlParser): string {.inline.} = ## returns the underlying 'data' string by reference. ## This is only used for speed hacks. shallowCopy(result, my.a) -proc rawData2*(my: TXmlParser): string {.inline.} = +proc rawData2*(my: XmlParser): string {.inline.} = ## returns the underlying second 'data' string by reference. ## This is only used for speed hacks. shallowCopy(result, my.b) -proc getColumn*(my: TXmlParser): int {.inline.} = +proc getColumn*(my: XmlParser): int {.inline.} = ## get the current column the parser has arrived at. - result = getColNumber(my, my.bufPos) + result = getColNumber(my, my.bufpos) -proc getLine*(my: TXmlParser): int {.inline.} = +proc getLine*(my: XmlParser): int {.inline.} = ## get the current line the parser has arrived at. - result = my.linenumber + result = my.lineNumber -proc getFilename*(my: TXmlParser): string {.inline.} = +proc getFilename*(my: XmlParser): string {.inline.} = ## get the filename of the file that the parser processes. result = my.filename -proc errorMsg*(my: TXmlParser): string = +proc errorMsg*(my: XmlParser): string = ## returns a helpful error message for the event ``xmlError`` assert(my.kind == xmlError) result = "$1($2, $3) Error: $4" % [ my.filename, $getLine(my), $getColumn(my), errorMessages[my.err]] -proc errorMsgExpected*(my: TXmlParser, tag: string): string = +proc errorMsgExpected*(my: XmlParser, tag: string): string = ## returns an error message " expected" in the same format as the ## other error messages result = "$1($2, $3) Error: $4" % [ my.filename, $getLine(my), $getColumn(my), "<$1> expected" % tag] -proc errorMsg*(my: TXmlParser, msg: string): string = +proc errorMsg*(my: XmlParser, msg: string): string = ## returns an error message with text `msg` in the same format as the ## other error messages result = "$1($2, $3) Error: $4" % [ my.filename, $getLine(my), $getColumn(my), msg] -proc markError(my: var TXmlParser, kind: TXmlError) {.inline.} = +proc markError(my: var XmlParser, kind: XmlErrorKind) {.inline.} = my.err = kind my.state = stateError -proc parseCDATA(my: var TXMLParser) = +proc parseCDATA(my: var XmlParser) = var pos = my.bufpos + len("') of '\c': - pos = lexbase.HandleCR(my, pos) + pos = lexbase.handleCR(my, pos) buf = my.buf add(my.a, '\L') of '\L': - pos = lexbase.HandleLF(my, pos) + pos = lexbase.handleLF(my, pos) buf = my.buf add(my.a, '\L') else: @@ -430,7 +433,7 @@ proc parseSpecial(my: var TXmlParser) = my.bufpos = pos my.kind = xmlSpecial -proc parseTag(my: var TXmlParser) = +proc parseTag(my: var XmlParser) = inc(my.bufpos) parseName(my, my.a) # if we have no name, do not interpret the '<': @@ -438,7 +441,7 @@ proc parseTag(my: var TXmlParser) = my.kind = xmlCharData add(my.a, '<') return - parseWhitespace(my, skip=True) + parseWhitespace(my, skip=true) if my.buf[my.bufpos] in NameStartChar: # an attribute follows: my.kind = xmlElementOpen @@ -455,17 +458,17 @@ proc parseTag(my: var TXmlParser) = else: markError(my, errGtExpected) -proc parseEndTag(my: var TXmlParser) = +proc parseEndTag(my: var XmlParser) = inc(my.bufpos, 2) parseName(my, my.a) - parseWhitespace(my, skip=True) + parseWhitespace(my, skip=true) if my.buf[my.bufpos] == '>': inc(my.bufpos) else: markError(my, errGtExpected) my.kind = xmlElementEnd -proc parseAttribute(my: var TXmlParser) = +proc parseAttribute(my: var XmlParser) = my.kind = xmlAttribute setLen(my.a, 0) setLen(my.b, 0) @@ -474,12 +477,12 @@ proc parseAttribute(my: var TXmlParser) = if my.a.len == 0: markError(my, errGtExpected) return - parseWhitespace(my, skip=True) + parseWhitespace(my, skip=true) if my.buf[my.bufpos] != '=': markError(my, errEqExpected) return inc(my.bufpos) - parseWhitespace(my, skip=True) + parseWhitespace(my, skip=true) var pos = my.bufpos var buf = my.buf @@ -504,11 +507,11 @@ proc parseAttribute(my: var TXmlParser) = pendingSpace = true inc(pos) of '\c': - pos = lexbase.HandleCR(my, pos) + pos = lexbase.handleCR(my, pos) buf = my.buf pendingSpace = true of '\L': - pos = lexbase.HandleLF(my, pos) + pos = lexbase.handleLF(my, pos) buf = my.buf pendingSpace = true else: @@ -524,9 +527,9 @@ proc parseAttribute(my: var TXmlParser) = else: markError(my, errQuoteExpected) my.bufpos = pos - parseWhitespace(my, skip=True) + parseWhitespace(my, skip=true) -proc parseCharData(my: var TXmlParser) = +proc parseCharData(my: var XmlParser) = var pos = my.bufpos var buf = my.buf while true: @@ -534,11 +537,11 @@ proc parseCharData(my: var TXmlParser) = of '\0', '<', '&': break of '\c': # the specification says that CR-LF, CR are to be transformed to LF - pos = lexbase.HandleCR(my, pos) + pos = lexbase.handleCR(my, pos) buf = my.buf add(my.a, '\L') of '\L': - pos = lexbase.HandleLF(my, pos) + pos = lexbase.handleLF(my, pos) buf = my.buf add(my.a, '\L') else: @@ -547,7 +550,7 @@ proc parseCharData(my: var TXmlParser) = my.bufpos = pos my.kind = xmlCharData -proc rawGetTok(my: var TXmlParser) = +proc rawGetTok(my: var XmlParser) = my.kind = xmlError setLen(my.a, 0) var pos = my.bufpos @@ -571,7 +574,7 @@ proc rawGetTok(my: var TXmlParser) = else: parseTag(my) of ' ', '\t', '\c', '\l': - parseWhiteSpace(my) + parseWhitespace(my) my.kind = xmlWhitespace of '\0': my.kind = xmlEof @@ -581,7 +584,7 @@ proc rawGetTok(my: var TXmlParser) = parseCharData(my) assert my.kind != xmlError -proc getTok(my: var TXmlParser) = +proc getTok(my: var XmlParser) = while true: rawGetTok(my) case my.kind @@ -591,7 +594,7 @@ proc getTok(my: var TXmlParser) = if my.options.contains(reportWhitespace): break else: break -proc next*(my: var TXmlParser) = +proc next*(my: var XmlParser) = ## retrieves the first/next event. This controls the parser. case my.state of stateNormal: @@ -626,19 +629,19 @@ proc next*(my: var TXmlParser) = when isMainModule: import os - var s = newFileStream(ParamStr(1), fmRead) - if s == nil: quit("cannot open the file" & ParamStr(1)) - var x: TXmlParser - open(x, s, ParamStr(1)) + var s = newFileStream(paramStr(1), fmRead) + if s == nil: quit("cannot open the file" & paramStr(1)) + var x: XmlParser + open(x, s, paramStr(1)) while true: next(x) case x.kind - of xmlError: Echo(x.errorMsg()) + of xmlError: echo(x.errorMsg()) of xmlEof: break of xmlCharData: echo(x.charData) of xmlWhitespace: echo("|$1|" % x.charData) of xmlComment: echo("" % x.charData) - of xmlPI: echo("" % [x.PIName, x.PIRest]) + of xmlPI: echo("" % [x.piName, x.piRest]) of xmlElementStart: echo("<$1>" % x.elementName) of xmlElementEnd: echo("" % x.elementName) diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index efe169c1d..7cef0a00d 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -28,11 +28,11 @@ when useUnicode: const InlineThreshold = 5 ## number of leaves; -1 to disable inlining - MaxSubpatterns* = 10 ## defines the maximum number of subpatterns that + MaxSubpatterns* = 20 ## defines the maximum number of subpatterns that ## can be captured. More subpatterns cannot be captured! type - TPegKind = enum + PegKind = enum pkEmpty, pkAny, ## any character (.) pkAnyRune, ## any Unicode character (_) @@ -67,28 +67,30 @@ type pkRule, ## a <- b pkList, ## a, b pkStartAnchor ## ^ --> Internal DSL: startAnchor() - TNonTerminalFlag = enum + NonTerminalFlag = enum ntDeclared, ntUsed - TNonTerminal {.final.} = object ## represents a non terminal symbol + NonTerminalObj = object ## represents a non terminal symbol name: string ## the name of the symbol line: int ## line the symbol has been declared/used in col: int ## column the symbol has been declared/used in - flags: set[TNonTerminalFlag] ## the nonterminal's flags + flags: set[NonTerminalFlag] ## the nonterminal's flags rule: TNode ## the rule that the symbol refers to - TNode {.final, shallow.} = object - case kind: TPegKind + TNode {.shallow.} = object + case kind: PegKind of pkEmpty..pkWhitespace: nil of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string of pkChar, pkGreedyRepChar: ch: char of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char] - of pkNonTerminal: nt: PNonTerminal + of pkNonTerminal: nt: NonTerminal of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns] else: sons: seq[TNode] - PNonTerminal* = ref TNonTerminal + NonTerminal* = ref NonTerminalObj - TPeg* = TNode ## type that represents a PEG + Peg* = TNode ## type that represents a PEG -proc term*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1Str".} = +{.deprecated: [TPeg: Peg].} + +proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} = ## constructs a PEG from a terminal string if t.len != 1: result.kind = pkTerminal @@ -97,35 +99,35 @@ proc term*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1Str".} = result.kind = pkChar result.ch = t[0] -proc termIgnoreCase*(t: string): TPeg {. +proc termIgnoreCase*(t: string): Peg {. nosideEffect, rtl, extern: "npegs$1".} = ## constructs a PEG from a terminal string; ignore case for matching result.kind = pkTerminalIgnoreCase result.term = t -proc termIgnoreStyle*(t: string): TPeg {. +proc termIgnoreStyle*(t: string): Peg {. nosideEffect, rtl, extern: "npegs$1".} = ## constructs a PEG from a terminal string; ignore style for matching result.kind = pkTerminalIgnoreStyle result.term = t -proc term*(t: char): TPeg {.nosideEffect, rtl, extern: "npegs$1Char".} = +proc term*(t: char): Peg {.nosideEffect, rtl, extern: "npegs$1Char".} = ## constructs a PEG from a terminal char assert t != '\0' result.kind = pkChar result.ch = t -proc charSet*(s: set[char]): TPeg {.nosideEffect, rtl, extern: "npegs$1".} = +proc charSet*(s: set[char]): Peg {.nosideEffect, rtl, extern: "npegs$1".} = ## constructs a PEG from a character set `s` assert '\0' notin s result.kind = pkCharChoice new(result.charChoice) result.charChoice[] = s -proc len(a: TPeg): int {.inline.} = return a.sons.len -proc add(d: var TPeg, s: TPeg) {.inline.} = add(d.sons, s) +proc len(a: Peg): int {.inline.} = return a.sons.len +proc add(d: var Peg, s: Peg) {.inline.} = add(d.sons, s) -proc addChoice(dest: var TPeg, elem: TPeg) = +proc addChoice(dest: var Peg, elem: Peg) = var L = dest.len-1 if L >= 0 and dest.sons[L].kind == pkCharChoice: # caution! Do not introduce false aliasing here! @@ -137,7 +139,7 @@ proc addChoice(dest: var TPeg, elem: TPeg) = else: add(dest, elem) else: add(dest, elem) -template multipleOp(k: TPegKind, localOpt: expr) = +template multipleOp(k: PegKind, localOpt: expr) = result.kind = k result.sons = @[] for x in items(a): @@ -149,12 +151,12 @@ template multipleOp(k: TPegKind, localOpt: expr) = if result.len == 1: result = result.sons[0] -proc `/`*(a: varargs[TPeg]): TPeg {. +proc `/`*(a: varargs[Peg]): Peg {. nosideEffect, rtl, extern: "npegsOrderedChoice".} = ## constructs an ordered choice with the PEGs in `a` multipleOp(pkOrderedChoice, addChoice) -proc addSequence(dest: var TPeg, elem: TPeg) = +proc addSequence(dest: var Peg, elem: Peg) = var L = dest.len-1 if L >= 0 and dest.sons[L].kind == pkTerminal: # caution! Do not introduce false aliasing here! @@ -166,12 +168,12 @@ proc addSequence(dest: var TPeg, elem: TPeg) = else: add(dest, elem) else: add(dest, elem) -proc sequence*(a: varargs[TPeg]): TPeg {. +proc sequence*(a: varargs[Peg]): Peg {. nosideEffect, rtl, extern: "npegs$1".} = ## constructs a sequence with all the PEGs from `a` multipleOp(pkSequence, addSequence) -proc `?`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsOptional".} = +proc `?`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsOptional".} = ## constructs an optional for the PEG `a` if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar, pkGreedyRepSet}: @@ -182,7 +184,7 @@ proc `?`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsOptional".} = result.kind = pkOption result.sons = @[a] -proc `*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyRep".} = +proc `*`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsGreedyRep".} = ## constructs a "greedy repetition" for the PEG `a` case a.kind of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption: @@ -200,111 +202,99 @@ proc `*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyRep".} = result.kind = pkGreedyRep result.sons = @[a] -proc `!*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsSearch".} = +proc `!*`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsSearch".} = ## constructs a "search" for the PEG `a` result.kind = pkSearch result.sons = @[a] -proc `!*\`*(a: TPeg): TPeg {.noSideEffect, rtl, +proc `!*\`*(a: Peg): Peg {.noSideEffect, rtl, extern: "npgegsCapturedSearch".} = ## constructs a "captured search" for the PEG `a` result.kind = pkCapturedSearch result.sons = @[a] - -when false: - proc contains(a: TPeg, k: TPegKind): bool = - if a.kind == k: return true - case a.kind - of pkEmpty, pkAny, pkAnyRune, pkGreedyAny, pkNewLine, pkTerminal, - pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar, - pkCharChoice, pkGreedyRepSet: nil - of pkNonTerminal: return true - else: - for i in 0..a.sons.len-1: - if contains(a.sons[i], k): return true -proc `+`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyPosRep".} = +proc `+`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsGreedyPosRep".} = ## constructs a "greedy positive repetition" with the PEG `a` return sequence(a, *a) -proc `&`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsAndPredicate".} = +proc `&`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsAndPredicate".} = ## constructs an "and predicate" with the PEG `a` result.kind = pkAndPredicate result.sons = @[a] -proc `!`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsNotPredicate".} = +proc `!`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsNotPredicate".} = ## constructs a "not predicate" with the PEG `a` result.kind = pkNotPredicate result.sons = @[a] -proc any*: TPeg {.inline.} = +proc any*: Peg {.inline.} = ## constructs the PEG `any character`:idx: (``.``) result.kind = pkAny -proc anyRune*: TPeg {.inline.} = +proc anyRune*: Peg {.inline.} = ## constructs the PEG `any rune`:idx: (``_``) result.kind = pkAnyRune -proc newLine*: TPeg {.inline.} = +proc newLine*: Peg {.inline.} = ## constructs the PEG `newline`:idx: (``\n``) - result.kind = pkNewline + result.kind = pkNewLine -proc unicodeLetter*: TPeg {.inline.} = +proc unicodeLetter*: Peg {.inline.} = ## constructs the PEG ``\letter`` which matches any Unicode letter. result.kind = pkLetter -proc unicodeLower*: TPeg {.inline.} = +proc unicodeLower*: Peg {.inline.} = ## constructs the PEG ``\lower`` which matches any Unicode lowercase letter. result.kind = pkLower -proc unicodeUpper*: TPeg {.inline.} = +proc unicodeUpper*: Peg {.inline.} = ## constructs the PEG ``\upper`` which matches any Unicode uppercase letter. result.kind = pkUpper -proc unicodeTitle*: TPeg {.inline.} = +proc unicodeTitle*: Peg {.inline.} = ## constructs the PEG ``\title`` which matches any Unicode title letter. result.kind = pkTitle -proc unicodeWhitespace*: TPeg {.inline.} = +proc unicodeWhitespace*: Peg {.inline.} = ## constructs the PEG ``\white`` which matches any Unicode ## whitespace character. result.kind = pkWhitespace -proc startAnchor*: TPeg {.inline.} = +proc startAnchor*: Peg {.inline.} = ## constructs the PEG ``^`` which matches the start of the input. result.kind = pkStartAnchor -proc endAnchor*: TPeg {.inline.} = +proc endAnchor*: Peg {.inline.} = ## constructs the PEG ``$`` which matches the end of the input. result = !any() -proc capture*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsCapture".} = +proc capture*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsCapture".} = ## constructs a capture with the PEG `a` result.kind = pkCapture result.sons = @[a] -proc backref*(index: range[1..MaxSubPatterns]): TPeg {. +proc backref*(index: range[1..MaxSubpatterns]): Peg {. nosideEffect, rtl, extern: "npegs$1".} = ## constructs a back reference of the given `index`. `index` starts counting ## from 1. result.kind = pkBackRef result.index = index-1 -proc backrefIgnoreCase*(index: range[1..MaxSubPatterns]): TPeg {. +proc backrefIgnoreCase*(index: range[1..MaxSubpatterns]): Peg {. nosideEffect, rtl, extern: "npegs$1".} = ## constructs a back reference of the given `index`. `index` starts counting ## from 1. Ignores case for matching. result.kind = pkBackRefIgnoreCase result.index = index-1 -proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg {. +proc backrefIgnoreStyle*(index: range[1..MaxSubpatterns]): Peg {. nosideEffect, rtl, extern: "npegs$1".}= ## constructs a back reference of the given `index`. `index` starts counting ## from 1. Ignores style for matching. result.kind = pkBackRefIgnoreStyle result.index = index-1 -proc spaceCost(n: TPeg): int = +proc spaceCost(n: Peg): int = case n.kind of pkEmpty: discard of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, @@ -319,7 +309,7 @@ proc spaceCost(n: TPeg): int = inc(result, spaceCost(n.sons[i])) if result >= InlineThreshold: break -proc nonterminal*(n: PNonTerminal): TPeg {. +proc nonterminal*(n: NonTerminal): Peg {. nosideEffect, rtl, extern: "npegs$1".} = ## constructs a PEG that consists of the nonterminal symbol assert n != nil @@ -330,7 +320,7 @@ proc nonterminal*(n: PNonTerminal): TPeg {. result.kind = pkNonTerminal result.nt = n -proc newNonTerminal*(name: string, line, column: int): PNonTerminal {. +proc newNonTerminal*(name: string, line, column: int): NonTerminal {. nosideEffect, rtl, extern: "npegs$1".} = ## constructs a nonterminal symbol new(result) @@ -415,7 +405,7 @@ proc charSetEsc(cc: set[char]): string = else: result = '[' & charSetEscAux(cc) & ']' -proc toStrAux(r: TPeg, res: var string) = +proc toStrAux(r: Peg, res: var string) = case r.kind of pkEmpty: add(res, "()") of pkAny: add(res, '.') @@ -426,7 +416,7 @@ proc toStrAux(r: TPeg, res: var string) = of pkTitle: add(res, "\\title") of pkWhitespace: add(res, "\\white") - of pkNewline: add(res, "\\n") + of pkNewLine: add(res, "\\n") of pkTerminal: add(res, singleQuoteEsc(r.term)) of pkTerminalIgnoreCase: add(res, 'i') @@ -501,7 +491,7 @@ proc toStrAux(r: TPeg, res: var string) = of pkStartAnchor: add(res, '^') -proc `$` *(r: TPeg): string {.nosideEffect, rtl, extern: "npegsToString".} = +proc `$` *(r: Peg): string {.nosideEffect, rtl, extern: "npegsToString".} = ## converts a PEG to its string representation result = "" toStrAux(r, result) @@ -509,13 +499,15 @@ proc `$` *(r: TPeg): string {.nosideEffect, rtl, extern: "npegsToString".} = # --------------------- core engine ------------------------------------------- type - TCaptures* {.final.} = object ## contains the captured substrings. - matches: array[0..maxSubpatterns-1, tuple[first, last: int]] + Captures* = object ## contains the captured substrings. + matches: array[0..MaxSubpatterns-1, tuple[first, last: int]] ml: int origStart: int -proc bounds*(c: TCaptures, - i: range[0..maxSubpatterns-1]): tuple[first, last: int] = +{.deprecated: [TCaptures: Captures].} + +proc bounds*(c: Captures, + i: range[0..MaxSubpatterns-1]): tuple[first, last: int] = ## returns the bounds ``[first..last]`` of the `i`'th capture. result = c.matches[i] @@ -533,7 +525,7 @@ when not useUnicode: proc isTitle(a: char): bool {.inline.} = return false proc isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'} -proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {. +proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. nosideEffect, rtl, extern: "npegs$1".} = ## low-level matching proc that implements the PEG interpreter. Use this ## for maximum efficiency (every other PEG operation ends up calling this @@ -551,7 +543,7 @@ proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {. result = -1 of pkLetter: if s[start] != '\0': - var a: TRune + var a: Rune result = start fastRuneAt(s, result, a) if isAlpha(a): dec(result, start) @@ -560,7 +552,7 @@ proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {. result = -1 of pkLower: if s[start] != '\0': - var a: TRune + var a: Rune result = start fastRuneAt(s, result, a) if isLower(a): dec(result, start) @@ -569,7 +561,7 @@ proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {. result = -1 of pkUpper: if s[start] != '\0': - var a: TRune + var a: Rune result = start fastRuneAt(s, result, a) if isUpper(a): dec(result, start) @@ -578,7 +570,7 @@ proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {. result = -1 of pkTitle: if s[start] != '\0': - var a: TRune + var a: Rune result = start fastRuneAt(s, result, a) if isTitle(a): dec(result, start) @@ -587,7 +579,7 @@ proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {. result = -1 of pkWhitespace: if s[start] != '\0': - var a: TRune + var a: Rune result = start fastRuneAt(s, result, a) if isWhiteSpace(a): dec(result, start) @@ -611,7 +603,7 @@ proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {. of pkTerminalIgnoreCase: var i = 0 - a, b: TRune + a, b: Rune result = start while i < len(p.term): fastRuneAt(p.term, i, a) @@ -623,15 +615,15 @@ proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {. of pkTerminalIgnoreStyle: var i = 0 - a, b: TRune + a, b: Rune result = start while i < len(p.term): while true: fastRuneAt(p.term, i, a) - if a != TRune('_'): break + if a != Rune('_'): break while true: fastRuneAt(s, result, b) - if b != TRune('_'): break + if b != Rune('_'): break if toLower(a) != toLower(b): result = -1 break @@ -682,7 +674,7 @@ proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {. while start+result < s.len: var x = rawMatch(s, p.sons[0], start+result, c) if x >= 0: - if idx < maxSubpatterns: + if idx < MaxSubpatterns: c.matches[idx] = (start, start+result-1) #else: silently ignore the capture inc(result, x) @@ -726,7 +718,7 @@ proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {. inc(c.ml) result = rawMatch(s, p.sons[0], start, c) if result >= 0: - if idx < maxSubpatterns: + if idx < MaxSubpatterns: c.matches[idx] = (start, start+result-1) #else: silently ignore the capture else: @@ -734,7 +726,7 @@ proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {. of pkBackRef..pkBackRefIgnoreStyle: if p.index >= c.ml: return -1 var (a, b) = c.matches[p.index] - var n: TPeg + var n: Peg n.kind = succ(pkTerminal, ord(p.kind)-ord(pkBackRef)) n.term = s.substr(a, b) result = rawMatch(s, n, start, c) @@ -745,53 +737,58 @@ proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {. template fillMatches(s, caps, c: expr) = for k in 0..c.ml-1: - caps[k] = substr(s, c.matches[k][0], c.matches[k][1]) + let startIdx = c.matches[k][0] + let endIdx = c.matches[k][1] + if startIdx != -1: + caps[k] = substr(s, startIdx, endIdx) + else: + caps[k] = nil -proc match*(s: string, pattern: TPeg, matches: var openArray[string], +proc match*(s: string, pattern: Peg, matches: var openArray[string], start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} = ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and ## the captured substrings in the array ``matches``. If it does not ## match, nothing is written into ``matches`` and ``false`` is ## returned. - var c: TCaptures + var c: Captures c.origStart = start result = rawMatch(s, pattern, start, c) == len(s) - start if result: fillMatches(s, matches, c) -proc match*(s: string, pattern: TPeg, +proc match*(s: string, pattern: Peg, start = 0): bool {.nosideEffect, rtl, extern: "npegs$1".} = ## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``. - var c: TCaptures + var c: Captures c.origStart = start result = rawMatch(s, pattern, start, c) == len(s)-start -proc matchLen*(s: string, pattern: TPeg, matches: var openArray[string], +proc matchLen*(s: string, pattern: Peg, matches: var openArray[string], start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} = ## the same as ``match``, but it returns the length of the match, ## if there is no match, -1 is returned. Note that a match length ## of zero can happen. It's possible that a suffix of `s` remains ## that does not belong to the match. - var c: TCaptures + var c: Captures c.origStart = start result = rawMatch(s, pattern, start, c) if result >= 0: fillMatches(s, matches, c) -proc matchLen*(s: string, pattern: TPeg, +proc matchLen*(s: string, pattern: Peg, start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} = ## the same as ``match``, but it returns the length of the match, ## if there is no match, -1 is returned. Note that a match length ## of zero can happen. It's possible that a suffix of `s` remains ## that does not belong to the match. - var c: TCaptures + var c: Captures c.origStart = start result = rawMatch(s, pattern, start, c) -proc find*(s: string, pattern: TPeg, matches: var openArray[string], +proc find*(s: string, pattern: Peg, matches: var openArray[string], start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} = ## returns the starting position of ``pattern`` in ``s`` and the captured ## substrings in the array ``matches``. If it does not match, nothing ## is written into ``matches`` and -1 is returned. - var c: TCaptures + var c: Captures c.origStart = start for i in start .. s.len-1: c.ml = 0 @@ -801,14 +798,14 @@ proc find*(s: string, pattern: TPeg, matches: var openArray[string], return -1 # could also use the pattern here: (!P .)* P -proc findBounds*(s: string, pattern: TPeg, matches: var openArray[string], +proc findBounds*(s: string, pattern: Peg, matches: var openArray[string], start = 0): tuple[first, last: int] {. nosideEffect, rtl, extern: "npegs$1Capture".} = ## returns the starting position and end position of ``pattern`` in ``s`` ## and the captured ## substrings in the array ``matches``. If it does not match, nothing ## is written into ``matches`` and (-1,0) is returned. - var c: TCaptures + var c: Captures c.origStart = start for i in start .. s.len-1: c.ml = 0 @@ -818,19 +815,19 @@ proc findBounds*(s: string, pattern: TPeg, matches: var openArray[string], return (i, i+L-1) return (-1, 0) -proc find*(s: string, pattern: TPeg, +proc find*(s: string, pattern: Peg, start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} = ## returns the starting position of ``pattern`` in ``s``. If it does not ## match, -1 is returned. - var c: TCaptures + var c: Captures c.origStart = start for i in start .. s.len-1: if rawMatch(s, pattern, i, c) >= 0: return i return -1 -iterator findAll*(s: string, pattern: TPeg, start = 0): string = +iterator findAll*(s: string, pattern: Peg, start = 0): string = ## yields all matching *substrings* of `s` that match `pattern`. - var c: TCaptures + var c: Captures c.origStart = start var i = start while i < s.len: @@ -842,7 +839,7 @@ iterator findAll*(s: string, pattern: TPeg, start = 0): string = yield substr(s, i, i+L-1) inc(i, L) -proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {. +proc findAll*(s: string, pattern: Peg, start = 0): seq[string] {. nosideEffect, rtl, extern: "npegs$1".} = ## returns all matching *substrings* of `s` that match `pattern`. ## If it does not match, @[] is returned. @@ -851,11 +848,11 @@ proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {. when not defined(nimhygiene): {.pragma: inject.} -template `=~`*(s: string, pattern: TPeg): bool = +template `=~`*(s: string, pattern: Peg): bool = ## This calls ``match`` with an implicit declared ``matches`` array that ## can be used in the scope of the ``=~`` call: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}": ## # matches a key=value pair: @@ -869,53 +866,53 @@ template `=~`*(s: string, pattern: TPeg): bool = ## else: ## echo("syntax error") ## - bind maxSubpatterns + bind MaxSubpatterns when not declaredInScope(matches): var matches {.inject.}: array[0..MaxSubpatterns-1, string] match(s, pattern, matches) # ------------------------- more string handling ------------------------------ -proc contains*(s: string, pattern: TPeg, start = 0): bool {. +proc contains*(s: string, pattern: Peg, start = 0): bool {. nosideEffect, rtl, extern: "npegs$1".} = ## same as ``find(s, pattern, start) >= 0`` return find(s, pattern, start) >= 0 -proc contains*(s: string, pattern: TPeg, matches: var openArray[string], +proc contains*(s: string, pattern: Peg, matches: var openArray[string], start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} = ## same as ``find(s, pattern, matches, start) >= 0`` return find(s, pattern, matches, start) >= 0 -proc startsWith*(s: string, prefix: TPeg, start = 0): bool {. +proc startsWith*(s: string, prefix: Peg, start = 0): bool {. nosideEffect, rtl, extern: "npegs$1".} = ## returns true if `s` starts with the pattern `prefix` result = matchLen(s, prefix, start) >= 0 -proc endsWith*(s: string, suffix: TPeg, start = 0): bool {. +proc endsWith*(s: string, suffix: Peg, start = 0): bool {. nosideEffect, rtl, extern: "npegs$1".} = ## returns true if `s` ends with the pattern `prefix` - var c: TCaptures + var c: Captures c.origStart = start for i in start .. s.len-1: if rawMatch(s, suffix, i, c) == s.len - i: return true -proc replacef*(s: string, sub: TPeg, by: string): string {. +proc replacef*(s: string, sub: Peg, by: string): string {. nosideEffect, rtl, extern: "npegs$1".} = ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by` ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") ## ## Results in: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## "var1<-keykey; val2<-key2key2" result = "" var i = 0 - var caps: array[0..maxSubpatterns-1, string] - var c: TCaptures + var caps: array[0..MaxSubpatterns-1, string] + var c: Captures while i < s.len: c.ml = 0 var x = rawMatch(s, sub, i, c) @@ -928,13 +925,13 @@ proc replacef*(s: string, sub: TPeg, by: string): string {. inc(i, x) add(result, substr(s, i)) -proc replace*(s: string, sub: TPeg, by = ""): string {. +proc replace*(s: string, sub: Peg, by = ""): string {. nosideEffect, rtl, extern: "npegs$1".} = ## Replaces `sub` in `s` by the string `by`. Captures cannot be accessed ## in `by`. result = "" var i = 0 - var c: TCaptures + var c: Captures while i < s.len: var x = rawMatch(s, sub, i, c) if x <= 0: @@ -946,14 +943,14 @@ proc replace*(s: string, sub: TPeg, by = ""): string {. add(result, substr(s, i)) proc parallelReplace*(s: string, subs: varargs[ - tuple[pattern: TPeg, repl: string]]): string {. + tuple[pattern: Peg, repl: string]]): string {. nosideEffect, rtl, extern: "npegs$1".} = ## Returns a modified copy of `s` with the substitutions in `subs` ## applied in parallel. result = "" var i = 0 - var c: TCaptures - var caps: array[0..maxSubpatterns-1, string] + var c: Captures + var caps: array[0..MaxSubpatterns-1, string] while i < s.len: block searchSubs: for j in 0..high(subs): @@ -970,7 +967,7 @@ proc parallelReplace*(s: string, subs: varargs[ add(result, substr(s, i)) proc transformFile*(infile, outfile: string, - subs: varargs[tuple[pattern: TPeg, repl: string]]) {. + subs: varargs[tuple[pattern: Peg, repl: string]]) {. rtl, extern: "npegs$1".} = ## reads in the file `infile`, performs a parallel replacement (calls ## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an @@ -978,25 +975,25 @@ proc transformFile*(infile, outfile: string, var x = readFile(infile).string writeFile(outfile, x.parallelReplace(subs)) -iterator split*(s: string, sep: TPeg): string = +iterator split*(s: string, sep: Peg): string = ## Splits the string `s` into substrings. ## ## Substrings are separated by the PEG `sep`. ## Examples: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## for word in split("00232this02939is39an22example111", peg"\d+"): ## writeln(stdout, word) ## ## Results in: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## "this" ## "is" ## "an" ## "example" ## - var c: TCaptures + var c: Captures var first = 0 last = 0 @@ -1013,7 +1010,7 @@ iterator split*(s: string, sep: TPeg): string = if first < last: yield substr(s, first, last-1) -proc split*(s: string, sep: TPeg): seq[string] {. +proc split*(s: string, sep: Peg): seq[string] {. nosideEffect, rtl, extern: "npegs$1".} = ## Splits the string `s` into substrings. accumulateResult(split(s, sep)) @@ -1060,10 +1057,10 @@ type charset: set[char] ## if kind == tkCharSet index: int ## if kind == tkBackref - TPegLexer {.inheritable.} = object ## the lexer object. + PegLexer {.inheritable.} = object ## the lexer object. bufpos: int ## the current position within the buffer buf: cstring ## the buffer itself - LineNumber: int ## the current line number + lineNumber: int ## the current line number lineStart: int ## index of last line start in buffer colOffset: int ## column to add filename: string @@ -1076,20 +1073,20 @@ const "@", "built-in", "escaped", "$", "$", "^" ] -proc handleCR(L: var TPegLexer, pos: int): int = +proc handleCR(L: var PegLexer, pos: int): int = assert(L.buf[pos] == '\c') - inc(L.linenumber) + inc(L.lineNumber) result = pos+1 if L.buf[result] == '\L': inc(result) L.lineStart = result -proc handleLF(L: var TPegLexer, pos: int): int = +proc handleLF(L: var PegLexer, pos: int): int = assert(L.buf[pos] == '\L') - inc(L.linenumber) + inc(L.lineNumber) result = pos+1 L.lineStart = result -proc init(L: var TPegLexer, input, filename: string, line = 1, col = 0) = +proc init(L: var PegLexer, input, filename: string, line = 1, col = 0) = L.buf = input L.bufpos = 0 L.lineNumber = line @@ -1097,18 +1094,18 @@ proc init(L: var TPegLexer, input, filename: string, line = 1, col = 0) = L.lineStart = 0 L.filename = filename -proc getColumn(L: TPegLexer): int {.inline.} = +proc getColumn(L: PegLexer): int {.inline.} = result = abs(L.bufpos - L.lineStart) + L.colOffset -proc getLine(L: TPegLexer): int {.inline.} = - result = L.linenumber +proc getLine(L: PegLexer): int {.inline.} = + result = L.lineNumber -proc errorStr(L: TPegLexer, msg: string, line = -1, col = -1): string = +proc errorStr(L: PegLexer, msg: string, line = -1, col = -1): string = var line = if line < 0: getLine(L) else: line var col = if col < 0: getColumn(L) else: col result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg] -proc handleHexChar(c: var TPegLexer, xi: var int) = +proc handleHexChar(c: var PegLexer, xi: var int) = case c.buf[c.bufpos] of '0'..'9': xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0')) @@ -1121,7 +1118,7 @@ proc handleHexChar(c: var TPegLexer, xi: var int) = inc(c.bufpos) else: discard -proc getEscapedChar(c: var TPegLexer, tok: var TToken) = +proc getEscapedChar(c: var PegLexer, tok: var TToken) = inc(c.bufpos) case c.buf[c.bufpos] of 'r', 'R', 'c', 'C': @@ -1167,13 +1164,13 @@ proc getEscapedChar(c: var TPegLexer, tok: var TToken) = else: tok.kind = tkInvalid of '\0'..'\31': tok.kind = tkInvalid - elif c.buf[c.bufpos] in strutils.letters: + elif c.buf[c.bufpos] in strutils.Letters: tok.kind = tkInvalid else: add(tok.literal, c.buf[c.bufpos]) inc(c.bufpos) -proc skip(c: var TPegLexer) = +proc skip(c: var PegLexer) = var pos = c.bufpos var buf = c.buf while true: @@ -1192,9 +1189,9 @@ proc skip(c: var TPegLexer) = break # EndOfFile also leaves the loop c.bufpos = pos -proc getString(c: var TPegLexer, tok: var TToken) = +proc getString(c: var PegLexer, tok: var TToken) = tok.kind = tkStringLit - var pos = c.bufPos + 1 + var pos = c.bufpos + 1 var buf = c.buf var quote = buf[pos-1] while true: @@ -1214,8 +1211,8 @@ proc getString(c: var TPegLexer, tok: var TToken) = inc(pos) c.bufpos = pos -proc getDollar(c: var TPegLexer, tok: var TToken) = - var pos = c.bufPos + 1 +proc getDollar(c: var PegLexer, tok: var TToken) = + var pos = c.bufpos + 1 var buf = c.buf if buf[pos] in {'0'..'9'}: tok.kind = tkBackref @@ -1227,10 +1224,10 @@ proc getDollar(c: var TPegLexer, tok: var TToken) = tok.kind = tkDollar c.bufpos = pos -proc getCharSet(c: var TPegLexer, tok: var TToken) = +proc getCharSet(c: var PegLexer, tok: var TToken) = tok.kind = tkCharSet tok.charset = {} - var pos = c.bufPos + 1 + var pos = c.bufpos + 1 var buf = c.buf var caret = false if buf[pos] == '^': @@ -1278,7 +1275,7 @@ proc getCharSet(c: var TPegLexer, tok: var TToken) = c.bufpos = pos if caret: tok.charset = {'\1'..'\xFF'} - tok.charset -proc getSymbol(c: var TPegLexer, tok: var TToken) = +proc getSymbol(c: var PegLexer, tok: var TToken) = var pos = c.bufpos var buf = c.buf while true: @@ -1288,7 +1285,7 @@ proc getSymbol(c: var TPegLexer, tok: var TToken) = c.bufpos = pos tok.kind = tkIdentifier -proc getBuiltin(c: var TPegLexer, tok: var TToken) = +proc getBuiltin(c: var PegLexer, tok: var TToken) = if c.buf[c.bufpos+1] in strutils.Letters: inc(c.bufpos) getSymbol(c, tok) @@ -1297,7 +1294,7 @@ proc getBuiltin(c: var TPegLexer, tok: var TToken) = tok.kind = tkEscaped getEscapedChar(c, tok) # may set tok.kind to tkInvalid -proc getTok(c: var TPegLexer, tok: var TToken) = +proc getTok(c: var PegLexer, tok: var TToken) = tok.kind = tkInvalid tok.modifier = modNone setLen(tok.literal, 0) @@ -1403,7 +1400,7 @@ proc getTok(c: var TPegLexer, tok: var TToken) = add(tok.literal, c.buf[c.bufpos]) inc(c.bufpos) -proc arrowIsNextTok(c: TPegLexer): bool = +proc arrowIsNextTok(c: PegLexer): bool = # the only look ahead we need var pos = c.bufpos while c.buf[pos] in {'\t', ' '}: inc(pos) @@ -1412,33 +1409,33 @@ proc arrowIsNextTok(c: TPegLexer): bool = # ----------------------------- parser ---------------------------------------- type - EInvalidPeg* = object of EInvalidValue ## raised if an invalid + EInvalidPeg* = object of ValueError ## raised if an invalid ## PEG has been detected - TPegParser = object of TPegLexer ## the PEG parser object + PegParser = object of PegLexer ## the PEG parser object tok: TToken - nonterms: seq[PNonTerminal] + nonterms: seq[NonTerminal] modifier: TModifier captures: int identIsVerbatim: bool - skip: TPeg + skip: Peg -proc pegError(p: TPegParser, msg: string, line = -1, col = -1) = +proc pegError(p: PegParser, msg: string, line = -1, col = -1) = var e: ref EInvalidPeg new(e) e.msg = errorStr(p, msg, line, col) raise e -proc getTok(p: var TPegParser) = +proc getTok(p: var PegParser) = getTok(p, p.tok) if p.tok.kind == tkInvalid: pegError(p, "invalid token") -proc eat(p: var TPegParser, kind: TTokKind) = +proc eat(p: var PegParser, kind: TTokKind) = if p.tok.kind == kind: getTok(p) else: pegError(p, tokKindToStr[kind] & " expected") -proc parseExpr(p: var TPegParser): TPeg +proc parseExpr(p: var PegParser): Peg {.gcsafe.} -proc getNonTerminal(p: var TPegParser, name: string): PNonTerminal = +proc getNonTerminal(p: var PegParser, name: string): NonTerminal = for i in 0..high(p.nonterms): result = p.nonterms[i] if cmpIgnoreStyle(result.name, name) == 0: return @@ -1446,19 +1443,19 @@ proc getNonTerminal(p: var TPegParser, name: string): PNonTerminal = result = newNonTerminal(name, getLine(p), getColumn(p)) add(p.nonterms, result) -proc modifiedTerm(s: string, m: TModifier): TPeg = +proc modifiedTerm(s: string, m: TModifier): 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): TPeg = +proc modifiedBackref(s: int, m: TModifier): Peg = case m of modNone, modVerbatim: result = backref(s) of modIgnoreCase: result = backrefIgnoreCase(s) of modIgnoreStyle: result = backrefIgnoreStyle(s) -proc builtin(p: var TPegParser): TPeg = +proc builtin(p: var PegParser): Peg = # do not use "y", "skip" or "i" as these would be ambiguous case p.tok.literal of "n": result = newLine() @@ -1478,11 +1475,11 @@ proc builtin(p: var TPegParser): TPeg = of "white": result = unicodeWhitespace() else: pegError(p, "unknown built-in: " & p.tok.literal) -proc token(terminal: TPeg, p: TPegParser): TPeg = +proc token(terminal: Peg, p: PegParser): Peg = if p.skip.kind == pkEmpty: result = terminal else: result = sequence(p.skip, terminal) -proc primary(p: var TPegParser): TPeg = +proc primary(p: var PegParser): Peg = case p.tok.kind of tkAmp: getTok(p) @@ -1571,11 +1568,11 @@ proc primary(p: var TPegParser): TPeg = getTok(p) else: break -proc seqExpr(p: var TPegParser): TPeg = +proc seqExpr(p: var PegParser): Peg = result = primary(p) while true: case p.tok.kind - of tkAmp, tkNot, tkAt, tkStringLit, tkCharset, tkParLe, tkCurlyLe, + of tkAmp, tkNot, tkAt, tkStringLit, tkCharSet, tkParLe, tkCurlyLe, tkAny, tkAnyRune, tkBuiltin, tkEscaped, tkDollar, tkBackref, tkHat, tkCurlyAt: result = sequence(result, primary(p)) @@ -1585,13 +1582,13 @@ proc seqExpr(p: var TPegParser): TPeg = else: break else: break -proc parseExpr(p: var TPegParser): TPeg = +proc parseExpr(p: var PegParser): Peg = result = seqExpr(p) while p.tok.kind == tkBar: getTok(p) result = result / seqExpr(p) -proc parseRule(p: var TPegParser): PNonTerminal = +proc parseRule(p: var PegParser): NonTerminal = if p.tok.kind == tkIdentifier and arrowIsNextTok(p): result = getNonTerminal(p, p.tok.literal) if ntDeclared in result.flags: @@ -1605,7 +1602,7 @@ proc parseRule(p: var TPegParser): PNonTerminal = else: pegError(p, "rule expected, but found: " & p.tok.literal) -proc rawParse(p: var TPegParser): TPeg = +proc rawParse(p: var PegParser): Peg = ## parses a rule or a PEG expression while p.tok.kind == tkBuiltin: case p.tok.literal @@ -1635,12 +1632,12 @@ proc rawParse(p: var TPegParser): TPeg = elif ntUsed notin nt.flags and i > 0: pegError(p, "unused rule: " & nt.name, nt.line, nt.col) -proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): TPeg = - ## constructs a TPeg object from `pattern`. `filename`, `line`, `col` are +proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg = + ## constructs a Peg object from `pattern`. `filename`, `line`, `col` are ## used for error messages, but they only provide start offsets. `parsePeg` ## keeps track of line and column numbers within `pattern`. - var p: TPegParser - init(TPegLexer(p), pattern, filename, line, col) + var p: PegParser + init(PegLexer(p), pattern, filename, line, col) p.tok.kind = tkInvalid p.tok.modifier = modNone p.tok.literal = "" @@ -1650,8 +1647,8 @@ proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): TPeg = getTok(p) result = rawParse(p) -proc peg*(pattern: string): TPeg = - ## constructs a TPeg object from the `pattern`. The short name has been +proc peg*(pattern: string): Peg = + ## constructs a Peg object from the `pattern`. The short name has been ## chosen to encourage its use as a raw string modifier:: ## ## peg"{\ident} \s* '=' \s* {.*}" @@ -1704,7 +1701,7 @@ when isMainModule: expr.rule = sequence(capture(ident), *sequence( nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr))) - var c: TCaptures + var c: Captures var s = "a+b + c +d+e+f" assert rawMatch(s, expr.rule, 0, c) == len(s) var a = "" @@ -1720,7 +1717,7 @@ when isMainModule: assert match("_______ana", peg"A <- 'ana' / . A") assert match("abcs%%%", peg"A <- ..A / .A / '%'") - var matches: array[0..maxSubpatterns-1, string] + var matches: array[0..MaxSubpatterns-1, string] if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}": assert matches[0] == "abc" else: @@ -1775,3 +1772,14 @@ when isMainModule: assert match("prefix/start", peg"^start$", 7) + if "foo" =~ peg"{'a'}?.*": + assert matches[0] == nil + else: assert false + + if "foo" =~ peg"{''}.*": + assert matches[0] == "" + else: assert false + + if "foo" =~ peg"{'foo'}": + assert matches[0] == "foo" + else: assert false diff --git a/lib/pure/pegs.nimfix b/lib/pure/pegs.nimfix new file mode 100644 index 000000000..8dd172a48 --- /dev/null +++ b/lib/pure/pegs.nimfix @@ -0,0 +1,1770 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Simple PEG (Parsing expression grammar) matching. Uses no memorization, but +## uses superoperators and symbol inlining to improve performance. Note: +## Matching performance is hopefully competitive with optimized regular +## expression engines. +## +## .. include:: ../doc/pegdocs.txt +## + +include "system/inclrtl" + +const + useUnicode = true ## change this to deactivate proper UTF-8 support + +import + strutils + +when useUnicode: + import unicode + +const + InlineThreshold = 5 ## number of leaves; -1 to disable inlining + MaxSubpatterns* = 10 ## defines the maximum number of subpatterns that + ## can be captured. More subpatterns cannot be captured! + +type + PegKind = enum + pkEmpty, + pkAny, ## any character (.) + pkAnyRune, ## any Unicode character (_) + pkNewLine, ## CR-LF, LF, CR + pkLetter, ## Unicode letter + pkLower, ## Unicode lower case letter + pkUpper, ## Unicode upper case letter + pkTitle, ## Unicode title character + pkWhitespace, ## Unicode whitespace character + pkTerminal, + pkTerminalIgnoreCase, + pkTerminalIgnoreStyle, + pkChar, ## single character to match + pkCharChoice, + pkNonTerminal, + pkSequence, ## a b c ... --> Internal DSL: peg(a, b, c) + pkOrderedChoice, ## a / b / ... --> Internal DSL: a / b or /[a, b, c] + pkGreedyRep, ## a* --> Internal DSL: *a + ## a+ --> (a a*) + pkGreedyRepChar, ## x* where x is a single character (superop) + pkGreedyRepSet, ## [set]* (superop) + pkGreedyAny, ## .* or _* (superop) + pkOption, ## a? --> Internal DSL: ?a + pkAndPredicate, ## &a --> Internal DSL: &a + pkNotPredicate, ## !a --> Internal DSL: !a + pkCapture, ## {a} --> Internal DSL: capture(a) + pkBackRef, ## $i --> Internal DSL: backref(i) + pkBackRefIgnoreCase, + pkBackRefIgnoreStyle, + pkSearch, ## @a --> Internal DSL: !*a + pkCapturedSearch, ## {@} a --> Internal DSL: !*\a + pkRule, ## a <- b + pkList, ## a, b + pkStartAnchor ## ^ --> Internal DSL: startAnchor() + NonTerminalFlag = enum + ntDeclared, ntUsed + NonTerminalObj = object ## represents a non terminal symbol + name: string ## the name of the symbol + 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 + case kind: PegKind + of pkEmpty..pkWhitespace: nil + of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string + of pkChar, pkGreedyRepChar: ch: char + of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char] + of pkNonTerminal: nt: PNonTerminal + of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns] + else: sons: seq[TNode] + PNonTerminal* = ref NonTerminalObj + TPeg* = TNode + +block: + type + Peg = TNode + NonTerminal = PNonTerminal + {.deprecated: [TPeg: Peg, PNonTerminal: NonTerminal].} + +proc term*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1Str".} = + ## constructs a PEG from a terminal string + if t.len != 1: + result.kind = pkTerminal + result.term = t + else: + result.kind = pkChar + result.ch = t[0] + +proc termIgnoreCase*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1".} = + ## constructs a PEG from a terminal string; ignore case for matching + result.kind = pkTerminalIgnoreCase + result.term = t + +proc termIgnoreStyle*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1".} = + ## constructs a PEG from a terminal string; ignore style for matching + result.kind = pkTerminalIgnoreStyle + result.term = t + +proc term*(t: char): TPeg {.nosideEffect, rtl, extern: "npegs$1Char".} = + ## constructs a PEG from a terminal char + assert t != '\0' + result.kind = pkChar + result.ch = t + +proc charSet*(s: set[char]): TPeg {.nosideEffect, rtl, extern: "npegs$1".} = + ## constructs a PEG from a character set `s` + assert '\0' notin s + result.kind = pkCharChoice + new(result.charChoice) + result.charChoice[] = s + +proc len(a: TPeg): int {.inline.} = return a.sons.len +proc add(d: var TPeg, s: TPeg) {.inline.} = add(d.sons, s) + +proc addChoice(dest: var TPeg, elem: TPeg) = + var L = dest.len-1 + if L >= 0 and dest.sons[L].kind == pkCharChoice: + # caution! Do not introduce false aliasing here! + case elem.kind + of pkCharChoice: + dest.sons[L] = charSet(dest.sons[L].charChoice[] + elem.charChoice[]) + of pkChar: + dest.sons[L] = charSet(dest.sons[L].charChoice[] + {elem.ch}) + else: add(dest, elem) + else: add(dest, elem) + +template multipleOp(k: PegKind, localOpt: expr) = + result.kind = k + result.sons = @[] + for x in items(a): + if x.kind == k: + for y in items(x.sons): + localOpt(result, y) + else: + localOpt(result, x) + if result.len == 1: + result = result.sons[0] + +proc `/`*(a: varargs[TPeg]): TPeg {. + nosideEffect, rtl, extern: "npegsOrderedChoice".} = + ## constructs an ordered choice with the PEGs in `a` + multipleOp(pkOrderedChoice, addChoice) + +proc addSequence(dest: var TPeg, elem: TPeg) = + var L = dest.len-1 + if L >= 0 and dest.sons[L].kind == pkTerminal: + # caution! Do not introduce false aliasing here! + case elem.kind + of pkTerminal: + dest.sons[L] = term(dest.sons[L].term & elem.term) + of pkChar: + dest.sons[L] = term(dest.sons[L].term & elem.ch) + else: add(dest, elem) + else: add(dest, elem) + +proc sequence*(a: varargs[TPeg]): TPeg {. + nosideEffect, rtl, extern: "npegs$1".} = + ## constructs a sequence with all the PEGs from `a` + multipleOp(pkSequence, addSequence) + +proc `?`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsOptional".} = + ## constructs an optional for the PEG `a` + if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar, + pkGreedyRepSet}: + # a* ? --> a* + # a? ? --> a? + result = a + else: + result.kind = pkOption + result.sons = @[a] + +proc `*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyRep".} = + ## constructs a "greedy repetition" for the PEG `a` + case a.kind + of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption: + assert false + # produces endless loop! + of pkChar: + result.kind = pkGreedyRepChar + result.ch = a.ch + of pkCharChoice: + result.kind = pkGreedyRepSet + result.charChoice = a.charChoice # copying a reference suffices! + of pkAny, pkAnyRune: + result.kind = pkGreedyAny + else: + result.kind = pkGreedyRep + result.sons = @[a] + +proc `!*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsSearch".} = + ## constructs a "search" for the PEG `a` + result.kind = pkSearch + result.sons = @[a] + +proc `!*\`*(a: TPeg): TPeg {.noSideEffect, rtl, + extern: "npgegsCapturedSearch".} = + ## constructs a "captured search" for the PEG `a` + result.kind = pkCapturedSearch + result.sons = @[a] + +proc `+`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyPosRep".} = + ## constructs a "greedy positive repetition" with the PEG `a` + return sequence(a, *a) + +proc `&`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsAndPredicate".} = + ## constructs an "and predicate" with the PEG `a` + result.kind = pkAndPredicate + result.sons = @[a] + +proc `!`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsNotPredicate".} = + ## constructs a "not predicate" with the PEG `a` + result.kind = pkNotPredicate + result.sons = @[a] + +proc any*: TPeg {.inline.} = + ## constructs the PEG `any character`:idx: (``.``) + result.kind = pkAny + +proc anyRune*: TPeg {.inline.} = + ## constructs the PEG `any rune`:idx: (``_``) + result.kind = pkAnyRune + +proc newLine*: TPeg {.inline.} = + ## constructs the PEG `newline`:idx: (``\n``) + result.kind = pkNewLine + +proc unicodeLetter*: TPeg {.inline.} = + ## constructs the PEG ``\letter`` which matches any Unicode letter. + result.kind = pkLetter + +proc unicodeLower*: TPeg {.inline.} = + ## constructs the PEG ``\lower`` which matches any Unicode lowercase letter. + result.kind = pkLower + +proc unicodeUpper*: TPeg {.inline.} = + ## constructs the PEG ``\upper`` which matches any Unicode uppercase letter. + result.kind = pkUpper + +proc unicodeTitle*: TPeg {.inline.} = + ## constructs the PEG ``\title`` which matches any Unicode title letter. + result.kind = pkTitle + +proc unicodeWhitespace*: TPeg {.inline.} = + ## constructs the PEG ``\white`` which matches any Unicode + ## whitespace character. + result.kind = pkWhitespace + +proc startAnchor*: TPeg {.inline.} = + ## constructs the PEG ``^`` which matches the start of the input. + result.kind = pkStartAnchor + +proc endAnchor*: TPeg {.inline.} = + ## constructs the PEG ``$`` which matches the end of the input. + result = !any() + +proc capture*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsCapture".} = + ## constructs a capture with the PEG `a` + result.kind = pkCapture + result.sons = @[a] + +proc backref*(index: range[1..MaxSubpatterns]): TPeg {. + nosideEffect, rtl, extern: "npegs$1".} = + ## constructs a back reference of the given `index`. `index` starts counting + ## from 1. + result.kind = pkBackRef + result.index = index-1 + +proc backrefIgnoreCase*(index: range[1..MaxSubpatterns]): TPeg {. + nosideEffect, rtl, extern: "npegs$1".} = + ## constructs a back reference of the given `index`. `index` starts counting + ## from 1. Ignores case for matching. + result.kind = pkBackRefIgnoreCase + result.index = index-1 + +proc backrefIgnoreStyle*(index: range[1..MaxSubpatterns]): TPeg {. + nosideEffect, rtl, extern: "npegs$1".}= + ## constructs a back reference of the given `index`. `index` starts counting + ## from 1. Ignores style for matching. + result.kind = pkBackRefIgnoreStyle + result.index = index-1 + +proc spaceCost(n: TPeg): int = + case n.kind + of pkEmpty: discard + of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, + pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, + pkAny..pkWhitespace, pkGreedyAny: + result = 1 + of pkNonTerminal: + # we cannot inline a rule with a non-terminal + result = InlineThreshold+1 + else: + for i in 0..n.len-1: + inc(result, spaceCost(n.sons[i])) + if result >= InlineThreshold: break + +proc nonterminal*(n: PNonTerminal): TPeg {. + nosideEffect, rtl, extern: "npegs$1".} = + ## constructs a PEG that consists of the nonterminal symbol + assert n != nil + if ntDeclared in n.flags and spaceCost(n.rule) < InlineThreshold: + when false: echo "inlining symbol: ", n.name + result = n.rule # inlining of rule enables better optimizations + else: + result.kind = pkNonTerminal + result.nt = n + +proc newNonTerminal*(name: string, line, column: int): PNonTerminal {. + nosideEffect, rtl, extern: "npegs$1".} = + ## constructs a nonterminal symbol + new(result) + result.name = name + result.line = line + result.col = column + +template letters*: expr = + ## expands to ``charset({'A'..'Z', 'a'..'z'})`` + charSet({'A'..'Z', 'a'..'z'}) + +template digits*: expr = + ## expands to ``charset({'0'..'9'})`` + charSet({'0'..'9'}) + +template whitespace*: expr = + ## expands to ``charset({' ', '\9'..'\13'})`` + charSet({' ', '\9'..'\13'}) + +template identChars*: expr = + ## expands to ``charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})`` + charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'}) + +template identStartChars*: expr = + ## expands to ``charset({'A'..'Z', 'a'..'z', '_'})`` + charSet({'a'..'z', 'A'..'Z', '_'}) + +template ident*: expr = + ## same as ``[a-zA-Z_][a-zA-z_0-9]*``; standard identifier + sequence(charSet({'a'..'z', 'A'..'Z', '_'}), + *charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'})) + +template natural*: expr = + ## same as ``\d+`` + +digits + +# ------------------------- debugging ----------------------------------------- + +proc esc(c: char, reserved = {'\0'..'\255'}): string = + case c + of '\b': result = "\\b" + of '\t': result = "\\t" + of '\c': result = "\\c" + of '\L': result = "\\l" + of '\v': result = "\\v" + of '\f': result = "\\f" + of '\e': result = "\\e" + of '\a': result = "\\a" + of '\\': result = "\\\\" + of 'a'..'z', 'A'..'Z', '0'..'9', '_': result = $c + elif c < ' ' or c >= '\128': result = '\\' & $ord(c) + elif c in reserved: result = '\\' & c + else: result = $c + +proc singleQuoteEsc(c: char): string = return "'" & esc(c, {'\''}) & "'" + +proc singleQuoteEsc(str: string): string = + result = "'" + for c in items(str): add result, esc(c, {'\''}) + add result, '\'' + +proc charSetEscAux(cc: set[char]): string = + const reserved = {'^', '-', ']'} + result = "" + var c1 = 0 + while c1 <= 0xff: + if chr(c1) in cc: + var c2 = c1 + while c2 < 0xff and chr(succ(c2)) in cc: inc(c2) + if c1 == c2: + add result, esc(chr(c1), reserved) + elif c2 == succ(c1): + add result, esc(chr(c1), reserved) & esc(chr(c2), reserved) + else: + add result, esc(chr(c1), reserved) & '-' & esc(chr(c2), reserved) + c1 = c2 + inc(c1) + +proc charSetEsc(cc: set[char]): string = + if card(cc) >= 128+64: + result = "[^" & charSetEscAux({'\1'..'\xFF'} - cc) & ']' + else: + result = '[' & charSetEscAux(cc) & ']' + +proc toStrAux(r: TPeg, res: var string) = + case r.kind + of pkEmpty: add(res, "()") + of pkAny: add(res, '.') + of pkAnyRune: add(res, '_') + of pkLetter: add(res, "\\letter") + of pkLower: add(res, "\\lower") + of pkUpper: add(res, "\\upper") + of pkTitle: add(res, "\\title") + of pkWhitespace: add(res, "\\white") + + of pkNewLine: add(res, "\\n") + of pkTerminal: add(res, singleQuoteEsc(r.term)) + of pkTerminalIgnoreCase: + add(res, 'i') + add(res, singleQuoteEsc(r.term)) + of pkTerminalIgnoreStyle: + add(res, 'y') + add(res, singleQuoteEsc(r.term)) + of pkChar: add(res, singleQuoteEsc(r.ch)) + of pkCharChoice: add(res, charSetEsc(r.charChoice[])) + of pkNonTerminal: add(res, r.nt.name) + of pkSequence: + add(res, '(') + toStrAux(r.sons[0], res) + for i in 1 .. high(r.sons): + add(res, ' ') + toStrAux(r.sons[i], res) + add(res, ')') + of pkOrderedChoice: + add(res, '(') + toStrAux(r.sons[0], res) + for i in 1 .. high(r.sons): + add(res, " / ") + toStrAux(r.sons[i], res) + add(res, ')') + of pkGreedyRep: + toStrAux(r.sons[0], res) + add(res, '*') + of pkGreedyRepChar: + add(res, singleQuoteEsc(r.ch)) + add(res, '*') + of pkGreedyRepSet: + add(res, charSetEsc(r.charChoice[])) + add(res, '*') + of pkGreedyAny: + add(res, ".*") + of pkOption: + toStrAux(r.sons[0], res) + add(res, '?') + of pkAndPredicate: + add(res, '&') + toStrAux(r.sons[0], res) + of pkNotPredicate: + add(res, '!') + toStrAux(r.sons[0], res) + of pkSearch: + add(res, '@') + toStrAux(r.sons[0], res) + of pkCapturedSearch: + add(res, "{@}") + toStrAux(r.sons[0], res) + of pkCapture: + add(res, '{') + toStrAux(r.sons[0], res) + add(res, '}') + of pkBackRef: + add(res, '$') + add(res, $r.index) + of pkBackRefIgnoreCase: + add(res, "i$") + add(res, $r.index) + of pkBackRefIgnoreStyle: + add(res, "y$") + add(res, $r.index) + of pkRule: + toStrAux(r.sons[0], res) + add(res, " <- ") + toStrAux(r.sons[1], res) + of pkList: + for i in 0 .. high(r.sons): + toStrAux(r.sons[i], res) + add(res, "\n") + of pkStartAnchor: + add(res, '^') + +proc `$` *(r: TPeg): string {.nosideEffect, rtl, extern: "npegsToString".} = + ## converts a PEG to its string representation + result = "" + toStrAux(r, result) + +# --------------------- core engine ------------------------------------------- + +type + Captures* = object ## contains the captured substrings. + matches: array[0..MaxSubpatterns-1, tuple[first, last: int]] + ml: int + origStart: int + +{.deprecated: [TCaptures: Captures].} + +proc bounds*(c: Captures, + i: range[0..MaxSubpatterns-1]): tuple[first, last: int] = + ## returns the bounds ``[first..last]`` of the `i`'th capture. + result = c.matches[i] + +when not useUnicode: + type + TRune = char + template fastRuneAt(s, i, ch: expr) = + ch = s[i] + inc(i) + template runeLenAt(s, i: expr): expr = 1 + + proc isAlpha(a: char): bool {.inline.} = return a in {'a'..'z','A'..'Z'} + proc isUpper(a: char): bool {.inline.} = return a in {'A'..'Z'} + proc isLower(a: char): bool {.inline.} = return a in {'a'..'z'} + proc isTitle(a: char): bool {.inline.} = return false + proc isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'} + +proc rawMatch*(s: string, p: TPeg, start: int, c: var Captures): int {. + nosideEffect, rtl, extern: "npegs$1".} = + ## low-level matching proc that implements the PEG interpreter. Use this + ## for maximum efficiency (every other PEG operation ends up calling this + ## proc). + ## Returns -1 if it does not match, else the length of the match + case p.kind + of pkEmpty: result = 0 # match of length 0 + of pkAny: + if s[start] != '\0': result = 1 + else: result = -1 + of pkAnyRune: + if s[start] != '\0': + result = runeLenAt(s, start) + else: + result = -1 + of pkLetter: + if s[start] != '\0': + var a: Rune + result = start + fastRuneAt(s, result, a) + if isAlpha(a): dec(result, start) + else: result = -1 + else: + result = -1 + of pkLower: + if s[start] != '\0': + var a: Rune + result = start + fastRuneAt(s, result, a) + if isLower(a): dec(result, start) + else: result = -1 + else: + result = -1 + of pkUpper: + if s[start] != '\0': + var a: Rune + result = start + fastRuneAt(s, result, a) + if isUpper(a): dec(result, start) + else: result = -1 + else: + result = -1 + of pkTitle: + if s[start] != '\0': + var a: Rune + result = start + fastRuneAt(s, result, a) + if isTitle(a): dec(result, start) + else: result = -1 + else: + result = -1 + of pkWhitespace: + if s[start] != '\0': + var a: Rune + result = start + fastRuneAt(s, result, a) + if isWhiteSpace(a): dec(result, start) + else: result = -1 + else: + result = -1 + of pkGreedyAny: + result = len(s) - start + of pkNewLine: + if s[start] == '\L': result = 1 + elif s[start] == '\C': + if s[start+1] == '\L': result = 2 + else: result = 1 + else: result = -1 + of pkTerminal: + result = len(p.term) + for i in 0..result-1: + if p.term[i] != s[start+i]: + result = -1 + break + of pkTerminalIgnoreCase: + var + i = 0 + a, b: Rune + result = start + while i < len(p.term): + fastRuneAt(p.term, i, a) + fastRuneAt(s, result, b) + if toLower(a) != toLower(b): + result = -1 + break + dec(result, start) + of pkTerminalIgnoreStyle: + var + i = 0 + a, b: Rune + result = start + while i < len(p.term): + while true: + fastRuneAt(p.term, i, a) + if a != Rune('_'): break + while true: + fastRuneAt(s, result, b) + if b != Rune('_'): break + if toLower(a) != toLower(b): + result = -1 + break + dec(result, start) + of pkChar: + if p.ch == s[start]: result = 1 + else: result = -1 + of pkCharChoice: + if contains(p.charChoice[], s[start]): result = 1 + else: result = -1 + of pkNonTerminal: + var oldMl = c.ml + when false: echo "enter: ", p.nt.name + result = rawMatch(s, p.nt.rule, start, c) + when false: echo "leave: ", p.nt.name + if result < 0: c.ml = oldMl + of pkSequence: + var oldMl = c.ml + result = 0 + for i in 0..high(p.sons): + var x = rawMatch(s, p.sons[i], start+result, c) + if x < 0: + c.ml = oldMl + result = -1 + break + else: inc(result, x) + of pkOrderedChoice: + var oldMl = c.ml + for i in 0..high(p.sons): + result = rawMatch(s, p.sons[i], start, c) + if result >= 0: break + c.ml = oldMl + of pkSearch: + var oldMl = c.ml + result = 0 + while start+result < s.len: + var x = rawMatch(s, p.sons[0], start+result, c) + if x >= 0: + inc(result, x) + return + inc(result) + result = -1 + c.ml = oldMl + of pkCapturedSearch: + var idx = c.ml # reserve a slot for the subpattern + inc(c.ml) + result = 0 + while start+result < s.len: + var x = rawMatch(s, p.sons[0], start+result, c) + if x >= 0: + if idx < MaxSubpatterns: + c.matches[idx] = (start, start+result-1) + #else: silently ignore the capture + inc(result, x) + return + inc(result) + result = -1 + c.ml = idx + of pkGreedyRep: + result = 0 + while true: + var x = rawMatch(s, p.sons[0], start+result, c) + # if x == 0, we have an endless loop; so the correct behaviour would be + # not to break. But endless loops can be easily introduced: + # ``(comment / \w*)*`` is such an example. Breaking for x == 0 does the + # expected thing in this case. + if x <= 0: break + inc(result, x) + of pkGreedyRepChar: + result = 0 + var ch = p.ch + while ch == s[start+result]: inc(result) + of pkGreedyRepSet: + result = 0 + while contains(p.charChoice[], s[start+result]): inc(result) + of pkOption: + result = max(0, rawMatch(s, p.sons[0], start, c)) + of pkAndPredicate: + var oldMl = c.ml + result = rawMatch(s, p.sons[0], start, c) + if result >= 0: result = 0 # do not consume anything + else: c.ml = oldMl + of pkNotPredicate: + var oldMl = c.ml + result = rawMatch(s, p.sons[0], start, c) + if result < 0: result = 0 + else: + c.ml = oldMl + result = -1 + of pkCapture: + var idx = c.ml # reserve a slot for the subpattern + inc(c.ml) + result = rawMatch(s, p.sons[0], start, c) + if result >= 0: + if idx < MaxSubpatterns: + c.matches[idx] = (start, start+result-1) + #else: silently ignore the capture + else: + c.ml = idx + of pkBackRef..pkBackRefIgnoreStyle: + if p.index >= c.ml: return -1 + var (a, b) = c.matches[p.index] + var n: TPeg + n.kind = succ(pkTerminal, ord(p.kind)-ord(pkBackRef)) + n.term = s.substr(a, b) + result = rawMatch(s, n, start, c) + of pkStartAnchor: + if c.origStart == start: result = 0 + else: result = -1 + of pkRule, pkList: assert false + +template fillMatches(s, caps, c: expr) = + for k in 0..c.ml-1: + caps[k] = substr(s, c.matches[k][0], c.matches[k][1]) + +proc match*(s: string, pattern: TPeg, matches: var openArray[string], + start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} = + ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and + ## the captured substrings in the array ``matches``. If it does not + ## match, nothing is written into ``matches`` and ``false`` is + ## returned. + var c: Captures + c.origStart = start + result = rawMatch(s, pattern, start, c) == len(s) - start + if result: fillMatches(s, matches, c) + +proc match*(s: string, pattern: TPeg, + start = 0): bool {.nosideEffect, rtl, extern: "npegs$1".} = + ## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``. + var c: Captures + c.origStart = start + result = rawMatch(s, pattern, start, c) == len(s)-start + +proc matchLen*(s: string, pattern: TPeg, matches: var openArray[string], + start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} = + ## the same as ``match``, but it returns the length of the match, + ## if there is no match, -1 is returned. Note that a match length + ## of zero can happen. It's possible that a suffix of `s` remains + ## that does not belong to the match. + var c: Captures + c.origStart = start + result = rawMatch(s, pattern, start, c) + if result >= 0: fillMatches(s, matches, c) + +proc matchLen*(s: string, pattern: TPeg, + start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} = + ## the same as ``match``, but it returns the length of the match, + ## if there is no match, -1 is returned. Note that a match length + ## of zero can happen. It's possible that a suffix of `s` remains + ## that does not belong to the match. + var c: Captures + c.origStart = start + result = rawMatch(s, pattern, start, c) + +proc find*(s: string, pattern: TPeg, matches: var openArray[string], + start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} = + ## returns the starting position of ``pattern`` in ``s`` and the captured + ## substrings in the array ``matches``. If it does not match, nothing + ## is written into ``matches`` and -1 is returned. + var c: Captures + c.origStart = start + for i in start .. s.len-1: + c.ml = 0 + if rawMatch(s, pattern, i, c) >= 0: + fillMatches(s, matches, c) + return i + return -1 + # could also use the pattern here: (!P .)* P + +proc findBounds*(s: string, pattern: TPeg, matches: var openArray[string], + start = 0): tuple[first, last: int] {. + nosideEffect, rtl, extern: "npegs$1Capture".} = + ## returns the starting position and end position of ``pattern`` in ``s`` + ## and the captured + ## substrings in the array ``matches``. If it does not match, nothing + ## is written into ``matches`` and (-1,0) is returned. + var c: Captures + c.origStart = start + for i in start .. s.len-1: + c.ml = 0 + var L = rawMatch(s, pattern, i, c) + if L >= 0: + fillMatches(s, matches, c) + return (i, i+L-1) + return (-1, 0) + +proc find*(s: string, pattern: TPeg, + start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} = + ## returns the starting position of ``pattern`` in ``s``. If it does not + ## match, -1 is returned. + var c: Captures + c.origStart = start + for i in start .. s.len-1: + if rawMatch(s, pattern, i, c) >= 0: return i + return -1 + +iterator findAll*(s: string, pattern: TPeg, start = 0): string = + ## yields all matching *substrings* of `s` that match `pattern`. + var c: Captures + c.origStart = start + var i = start + while i < s.len: + c.ml = 0 + var L = rawMatch(s, pattern, i, c) + if L < 0: + inc(i, 1) + else: + yield substr(s, i, i+L-1) + inc(i, L) + +proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {. + nosideEffect, rtl, extern: "npegs$1".} = + ## returns all matching *substrings* of `s` that match `pattern`. + ## If it does not match, @[] is returned. + accumulateResult(findAll(s, pattern, start)) + +when not defined(nimhygiene): + {.pragma: inject.} + +template `=~`*(s: string, pattern: TPeg): bool = + ## This calls ``match`` with an implicit declared ``matches`` array that + ## can be used in the scope of the ``=~`` call: + ## + ## .. code-block:: nim + ## + ## if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}": + ## # matches a key=value pair: + ## echo("Key: ", matches[0]) + ## echo("Value: ", matches[1]) + ## elif line =~ peg"\s*{'#'.*}": + ## # matches a comment + ## # note that the implicit ``matches`` array is different from the + ## # ``matches`` array of the first branch + ## echo("comment: ", matches[0]) + ## else: + ## echo("syntax error") + ## + bind MaxSubpatterns + when not declaredInScope(matches): + var matches {.inject.}: array[0..MaxSubpatterns-1, string] + match(s, pattern, matches) + +# ------------------------- more string handling ------------------------------ + +proc contains*(s: string, pattern: TPeg, start = 0): bool {. + nosideEffect, rtl, extern: "npegs$1".} = + ## same as ``find(s, pattern, start) >= 0`` + return find(s, pattern, start) >= 0 + +proc contains*(s: string, pattern: TPeg, matches: var openArray[string], + start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} = + ## same as ``find(s, pattern, matches, start) >= 0`` + return find(s, pattern, matches, start) >= 0 + +proc startsWith*(s: string, prefix: TPeg, start = 0): bool {. + nosideEffect, rtl, extern: "npegs$1".} = + ## returns true if `s` starts with the pattern `prefix` + result = matchLen(s, prefix, start) >= 0 + +proc endsWith*(s: string, suffix: TPeg, start = 0): bool {. + nosideEffect, rtl, extern: "npegs$1".} = + ## returns true if `s` ends with the pattern `prefix` + var c: Captures + c.origStart = start + for i in start .. s.len-1: + if rawMatch(s, suffix, i, c) == s.len - i: return true + +proc replacef*(s: string, sub: TPeg, by: string): string {. + nosideEffect, rtl, extern: "npegs$1".} = + ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by` + ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: + ## + ## .. code-block:: nim + ## "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") + ## + ## Results in: + ## + ## .. code-block:: nim + ## + ## "var1<-keykey; val2<-key2key2" + result = "" + var i = 0 + var caps: array[0..MaxSubpatterns-1, string] + var c: Captures + while i < s.len: + c.ml = 0 + var x = rawMatch(s, sub, i, c) + if x <= 0: + add(result, s[i]) + inc(i) + else: + fillMatches(s, caps, c) + addf(result, by, caps) + inc(i, x) + add(result, substr(s, i)) + +proc replace*(s: string, sub: TPeg, by = ""): string {. + nosideEffect, rtl, extern: "npegs$1".} = + ## Replaces `sub` in `s` by the string `by`. Captures cannot be accessed + ## in `by`. + result = "" + var i = 0 + var c: Captures + while i < s.len: + var x = rawMatch(s, sub, i, c) + if x <= 0: + add(result, s[i]) + inc(i) + else: + add(result, by) + inc(i, x) + add(result, substr(s, i)) + +proc parallelReplace*(s: string, subs: varargs[ + tuple[pattern: TPeg, repl: string]]): string {. + nosideEffect, rtl, extern: "npegs$1".} = + ## Returns a modified copy of `s` with the substitutions in `subs` + ## applied in parallel. + result = "" + var i = 0 + var c: Captures + var caps: array[0..MaxSubpatterns-1, string] + while i < s.len: + block searchSubs: + for j in 0..high(subs): + c.ml = 0 + var x = rawMatch(s, subs[j][0], i, c) + if x > 0: + fillMatches(s, caps, c) + addf(result, subs[j][1], caps) + inc(i, x) + break searchSubs + add(result, s[i]) + inc(i) + # copy the rest: + add(result, substr(s, i)) + +proc transformFile*(infile, outfile: string, + subs: varargs[tuple[pattern: TPeg, repl: string]]) {. + rtl, extern: "npegs$1".} = + ## reads in the file `infile`, performs a parallel replacement (calls + ## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an + ## error occurs. This is supposed to be used for quick scripting. + var x = readFile(infile).string + writeFile(outfile, x.parallelReplace(subs)) + +iterator split*(s: string, sep: TPeg): string = + ## Splits the string `s` into substrings. + ## + ## Substrings are separated by the PEG `sep`. + ## Examples: + ## + ## .. code-block:: nim + ## for word in split("00232this02939is39an22example111", peg"\d+"): + ## writeln(stdout, word) + ## + ## Results in: + ## + ## .. code-block:: nim + ## "this" + ## "is" + ## "an" + ## "example" + ## + var c: Captures + var + first = 0 + last = 0 + while last < len(s): + c.ml = 0 + var x = rawMatch(s, sep, last, c) + if x > 0: inc(last, x) + first = last + while last < len(s): + inc(last) + c.ml = 0 + x = rawMatch(s, sep, last, c) + if x > 0: break + if first < last: + yield substr(s, first, last-1) + +proc split*(s: string, sep: TPeg): seq[string] {. + nosideEffect, rtl, extern: "npegs$1".} = + ## Splits the string `s` into substrings. + accumulateResult(split(s, sep)) + +# ------------------- scanner ------------------------------------------------- + +type + TModifier = enum + modNone, + modVerbatim, + modIgnoreCase, + modIgnoreStyle + TTokKind = enum ## enumeration of all tokens + tkInvalid, ## invalid token + tkEof, ## end of file reached + tkAny, ## . + tkAnyRune, ## _ + tkIdentifier, ## abc + tkStringLit, ## "abc" or 'abc' + tkCharSet, ## [^A-Z] + tkParLe, ## '(' + tkParRi, ## ')' + tkCurlyLe, ## '{' + tkCurlyRi, ## '}' + tkCurlyAt, ## '{@}' + tkArrow, ## '<-' + tkBar, ## '/' + tkStar, ## '*' + tkPlus, ## '+' + tkAmp, ## '&' + tkNot, ## '!' + tkOption, ## '?' + tkAt, ## '@' + tkBuiltin, ## \identifier + tkEscaped, ## \\ + tkBackref, ## '$' + tkDollar, ## '$' + tkHat ## '^' + + TToken {.final.} = object ## a token + kind: TTokKind ## the type of the token + modifier: TModifier + literal: string ## the parsed (string) literal + charset: set[char] ## if kind == tkCharSet + index: int ## if kind == tkBackref + + PegLexer {.inheritable.} = object ## the lexer object. + bufpos: int ## the current position within the buffer + buf: cstring ## the buffer itself + lineNumber: int ## the current line number + lineStart: int ## index of last line start in buffer + colOffset: int ## column to add + filename: string + +const + tokKindToStr: array[TTokKind, string] = [ + "invalid", "[EOF]", ".", "_", "identifier", "string literal", + "character set", "(", ")", "{", "}", "{@}", + "<-", "/", "*", "+", "&", "!", "?", + "@", "built-in", "escaped", "$", "$", "^" + ] + +proc handleCR(L: var PegLexer, pos: int): int = + assert(L.buf[pos] == '\c') + inc(L.lineNumber) + result = pos+1 + if L.buf[result] == '\L': inc(result) + L.lineStart = result + +proc handleLF(L: var PegLexer, pos: int): int = + assert(L.buf[pos] == '\L') + inc(L.lineNumber) + result = pos+1 + L.lineStart = result + +proc init(L: var PegLexer, input, filename: string, line = 1, col = 0) = + L.buf = input + L.bufpos = 0 + L.lineNumber = line + L.colOffset = col + L.lineStart = 0 + L.filename = filename + +proc getColumn(L: PegLexer): int {.inline.} = + result = abs(L.bufpos - L.lineStart) + L.colOffset + +proc getLine(L: PegLexer): int {.inline.} = + result = L.lineNumber + +proc errorStr(L: PegLexer, msg: string, line = -1, col = -1): string = + var line = if line < 0: getLine(L) else: line + var col = if col < 0: getColumn(L) else: col + result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg] + +proc handleHexChar(c: var PegLexer, xi: var int) = + case c.buf[c.bufpos] + of '0'..'9': + xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0')) + inc(c.bufpos) + of 'a'..'f': + xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10) + inc(c.bufpos) + of 'A'..'F': + xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) + inc(c.bufpos) + else: discard + +proc getEscapedChar(c: var PegLexer, tok: var TToken) = + inc(c.bufpos) + case c.buf[c.bufpos] + of 'r', 'R', 'c', 'C': + add(tok.literal, '\c') + inc(c.bufpos) + of 'l', 'L': + add(tok.literal, '\L') + inc(c.bufpos) + of 'f', 'F': + add(tok.literal, '\f') + inc(c.bufpos) + of 'e', 'E': + add(tok.literal, '\e') + inc(c.bufpos) + of 'a', 'A': + add(tok.literal, '\a') + inc(c.bufpos) + of 'b', 'B': + add(tok.literal, '\b') + inc(c.bufpos) + of 'v', 'V': + add(tok.literal, '\v') + inc(c.bufpos) + of 't', 'T': + add(tok.literal, '\t') + inc(c.bufpos) + of 'x', 'X': + inc(c.bufpos) + var xi = 0 + handleHexChar(c, xi) + handleHexChar(c, xi) + if xi == 0: tok.kind = tkInvalid + else: add(tok.literal, chr(xi)) + of '0'..'9': + var val = ord(c.buf[c.bufpos]) - ord('0') + inc(c.bufpos) + var i = 1 + while (i <= 3) and (c.buf[c.bufpos] in {'0'..'9'}): + val = val * 10 + ord(c.buf[c.bufpos]) - ord('0') + inc(c.bufpos) + inc(i) + if val > 0 and val <= 255: add(tok.literal, chr(val)) + else: tok.kind = tkInvalid + of '\0'..'\31': + tok.kind = tkInvalid + elif c.buf[c.bufpos] in strutils.Letters: + tok.kind = tkInvalid + else: + add(tok.literal, c.buf[c.bufpos]) + inc(c.bufpos) + +proc skip(c: var PegLexer) = + var pos = c.bufpos + var buf = c.buf + while true: + case buf[pos] + of ' ', '\t': + inc(pos) + of '#': + while not (buf[pos] in {'\c', '\L', '\0'}): inc(pos) + of '\c': + pos = handleCR(c, pos) + buf = c.buf + of '\L': + pos = handleLF(c, pos) + buf = c.buf + else: + break # EndOfFile also leaves the loop + c.bufpos = pos + +proc getString(c: var PegLexer, tok: var TToken) = + tok.kind = tkStringLit + var pos = c.bufpos + 1 + var buf = c.buf + var quote = buf[pos-1] + while true: + case buf[pos] + of '\\': + c.bufpos = pos + getEscapedChar(c, tok) + pos = c.bufpos + of '\c', '\L', '\0': + tok.kind = tkInvalid + break + elif buf[pos] == quote: + inc(pos) + break + else: + add(tok.literal, buf[pos]) + inc(pos) + c.bufpos = pos + +proc getDollar(c: var PegLexer, tok: var TToken) = + var pos = c.bufpos + 1 + var buf = c.buf + if buf[pos] in {'0'..'9'}: + tok.kind = tkBackref + tok.index = 0 + while buf[pos] in {'0'..'9'}: + tok.index = tok.index * 10 + ord(buf[pos]) - ord('0') + inc(pos) + else: + tok.kind = tkDollar + c.bufpos = pos + +proc getCharSet(c: var PegLexer, tok: var TToken) = + tok.kind = tkCharSet + tok.charset = {} + var pos = c.bufpos + 1 + var buf = c.buf + var caret = false + if buf[pos] == '^': + inc(pos) + caret = true + while true: + var ch: char + case buf[pos] + of ']': + inc(pos) + break + of '\\': + c.bufpos = pos + getEscapedChar(c, tok) + pos = c.bufpos + ch = tok.literal[tok.literal.len-1] + of '\C', '\L', '\0': + tok.kind = tkInvalid + break + else: + ch = buf[pos] + inc(pos) + incl(tok.charset, ch) + if buf[pos] == '-': + if buf[pos+1] == ']': + incl(tok.charset, '-') + inc(pos) + else: + inc(pos) + var ch2: char + case buf[pos] + of '\\': + c.bufpos = pos + getEscapedChar(c, tok) + pos = c.bufpos + ch2 = tok.literal[tok.literal.len-1] + of '\C', '\L', '\0': + tok.kind = tkInvalid + break + else: + ch2 = buf[pos] + inc(pos) + for i in ord(ch)+1 .. ord(ch2): + incl(tok.charset, chr(i)) + c.bufpos = pos + if caret: tok.charset = {'\1'..'\xFF'} - tok.charset + +proc getSymbol(c: var PegLexer, tok: var TToken) = + var pos = c.bufpos + var buf = c.buf + while true: + add(tok.literal, buf[pos]) + inc(pos) + if buf[pos] notin strutils.IdentChars: break + c.bufpos = pos + tok.kind = tkIdentifier + +proc getBuiltin(c: var PegLexer, tok: var TToken) = + if c.buf[c.bufpos+1] in strutils.Letters: + inc(c.bufpos) + getSymbol(c, tok) + tok.kind = tkBuiltin + else: + tok.kind = tkEscaped + getEscapedChar(c, tok) # may set tok.kind to tkInvalid + +proc getTok(c: var PegLexer, tok: var TToken) = + tok.kind = tkInvalid + tok.modifier = modNone + setLen(tok.literal, 0) + skip(c) + case c.buf[c.bufpos] + of '{': + inc(c.bufpos) + if c.buf[c.bufpos] == '@' and c.buf[c.bufpos+1] == '}': + tok.kind = tkCurlyAt + inc(c.bufpos, 2) + add(tok.literal, "{@}") + else: + tok.kind = tkCurlyLe + add(tok.literal, '{') + of '}': + tok.kind = tkCurlyRi + inc(c.bufpos) + add(tok.literal, '}') + of '[': + getCharSet(c, tok) + of '(': + tok.kind = tkParLe + inc(c.bufpos) + add(tok.literal, '(') + of ')': + tok.kind = tkParRi + inc(c.bufpos) + add(tok.literal, ')') + of '.': + tok.kind = tkAny + inc(c.bufpos) + add(tok.literal, '.') + of '_': + tok.kind = tkAnyRune + inc(c.bufpos) + add(tok.literal, '_') + of '\\': + getBuiltin(c, tok) + of '\'', '"': getString(c, tok) + of '$': getDollar(c, tok) + of '\0': + tok.kind = tkEof + tok.literal = "[EOF]" + of 'a'..'z', 'A'..'Z', '\128'..'\255': + getSymbol(c, tok) + if c.buf[c.bufpos] in {'\'', '"'} or + c.buf[c.bufpos] == '$' and c.buf[c.bufpos+1] in {'0'..'9'}: + case tok.literal + of "i": tok.modifier = modIgnoreCase + of "y": tok.modifier = modIgnoreStyle + of "v": tok.modifier = modVerbatim + else: discard + setLen(tok.literal, 0) + if c.buf[c.bufpos] == '$': + getDollar(c, tok) + else: + getString(c, tok) + if tok.modifier == modNone: tok.kind = tkInvalid + of '+': + tok.kind = tkPlus + inc(c.bufpos) + add(tok.literal, '+') + of '*': + tok.kind = tkStar + inc(c.bufpos) + add(tok.literal, '+') + of '<': + if c.buf[c.bufpos+1] == '-': + inc(c.bufpos, 2) + tok.kind = tkArrow + add(tok.literal, "<-") + else: + add(tok.literal, '<') + of '/': + tok.kind = tkBar + inc(c.bufpos) + add(tok.literal, '/') + of '?': + tok.kind = tkOption + inc(c.bufpos) + add(tok.literal, '?') + of '!': + tok.kind = tkNot + inc(c.bufpos) + add(tok.literal, '!') + of '&': + tok.kind = tkAmp + inc(c.bufpos) + add(tok.literal, '!') + of '@': + tok.kind = tkAt + inc(c.bufpos) + add(tok.literal, '@') + if c.buf[c.bufpos] == '@': + tok.kind = tkCurlyAt + inc(c.bufpos) + add(tok.literal, '@') + of '^': + tok.kind = tkHat + inc(c.bufpos) + add(tok.literal, '^') + else: + add(tok.literal, c.buf[c.bufpos]) + inc(c.bufpos) + +proc arrowIsNextTok(c: PegLexer): bool = + # the only look ahead we need + var pos = c.bufpos + while c.buf[pos] in {'\t', ' '}: inc(pos) + result = c.buf[pos] == '<' and c.buf[pos+1] == '-' + +# ----------------------------- parser ---------------------------------------- + +type + EInvalidPeg* = object of ValueError ## raised if an invalid + ## PEG has been detected + PegParser = object of PegLexer ## the PEG parser object + tok: TToken + nonterms: seq[PNonTerminal] + modifier: TModifier + captures: int + identIsVerbatim: bool + skip: TPeg + +proc pegError(p: PegParser, msg: string, line = -1, col = -1) = + var e: ref EInvalidPeg + new(e) + e.msg = errorStr(p, msg, line, col) + raise e + +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) = + if p.tok.kind == kind: getTok(p) + else: pegError(p, tokKindToStr[kind] & " expected") + +proc parseExpr(p: var PegParser): TPeg + +proc getNonTerminal(p: var PegParser, name: string): PNonTerminal = + for i in 0..high(p.nonterms): + result = p.nonterms[i] + if cmpIgnoreStyle(result.name, name) == 0: return + # forward reference: + result = newNonTerminal(name, getLine(p), getColumn(p)) + add(p.nonterms, result) + +proc modifiedTerm(s: string, m: TModifier): TPeg = + case m + of modNone, modVerbatim: result = term(s) + of modIgnoreCase: result = termIgnoreCase(s) + of modIgnoreStyle: result = termIgnoreStyle(s) + +proc modifiedBackref(s: int, m: TModifier): TPeg = + case m + of modNone, modVerbatim: result = backref(s) + of modIgnoreCase: result = backrefIgnoreCase(s) + of modIgnoreStyle: result = backrefIgnoreStyle(s) + +proc builtin(p: var PegParser): TPeg = + # do not use "y", "skip" or "i" as these would be ambiguous + case p.tok.literal + of "n": result = newLine() + of "d": result = charSet({'0'..'9'}) + of "D": result = charSet({'\1'..'\xff'} - {'0'..'9'}) + of "s": result = charSet({' ', '\9'..'\13'}) + of "S": result = charSet({'\1'..'\xff'} - {' ', '\9'..'\13'}) + of "w": result = charSet({'a'..'z', 'A'..'Z', '_', '0'..'9'}) + of "W": result = charSet({'\1'..'\xff'} - {'a'..'z','A'..'Z','_','0'..'9'}) + of "a": result = charSet({'a'..'z', 'A'..'Z'}) + of "A": result = charSet({'\1'..'\xff'} - {'a'..'z', 'A'..'Z'}) + of "ident": result = pegs.ident + of "letter": result = unicodeLetter() + of "upper": result = unicodeUpper() + of "lower": result = unicodeLower() + of "title": result = unicodeTitle() + of "white": result = unicodeWhitespace() + else: pegError(p, "unknown built-in: " & p.tok.literal) + +proc token(terminal: TPeg, p: PegParser): TPeg = + if p.skip.kind == pkEmpty: result = terminal + else: result = sequence(p.skip, terminal) + +proc primary(p: var PegParser): TPeg = + case p.tok.kind + of tkAmp: + getTok(p) + return &primary(p) + of tkNot: + getTok(p) + return !primary(p) + of tkAt: + getTok(p) + return !*primary(p) + of tkCurlyAt: + getTok(p) + return !*\primary(p).token(p) + else: discard + case p.tok.kind + of tkIdentifier: + if p.identIsVerbatim: + var m = p.tok.modifier + if m == modNone: m = p.modifier + result = modifiedTerm(p.tok.literal, m).token(p) + getTok(p) + elif not arrowIsNextTok(p): + var nt = getNonTerminal(p, p.tok.literal) + incl(nt.flags, ntUsed) + result = nonterminal(nt).token(p) + getTok(p) + else: + pegError(p, "expression expected, but found: " & p.tok.literal) + of tkStringLit: + var m = p.tok.modifier + if m == modNone: m = p.modifier + result = modifiedTerm(p.tok.literal, m).token(p) + getTok(p) + of tkCharSet: + if '\0' in p.tok.charset: + pegError(p, "binary zero ('\\0') not allowed in character class") + result = charSet(p.tok.charset).token(p) + getTok(p) + of tkParLe: + getTok(p) + result = parseExpr(p) + eat(p, tkParRi) + of tkCurlyLe: + getTok(p) + result = capture(parseExpr(p)).token(p) + eat(p, tkCurlyRi) + inc(p.captures) + of tkAny: + result = any().token(p) + getTok(p) + of tkAnyRune: + result = anyRune().token(p) + getTok(p) + of tkBuiltin: + result = builtin(p).token(p) + getTok(p) + of tkEscaped: + result = term(p.tok.literal[0]).token(p) + getTok(p) + of tkDollar: + result = endAnchor() + getTok(p) + of tkHat: + result = startAnchor() + getTok(p) + of tkBackref: + var m = p.tok.modifier + if m == modNone: m = p.modifier + result = modifiedBackref(p.tok.index, m).token(p) + if p.tok.index < 0 or p.tok.index > p.captures: + pegError(p, "invalid back reference index: " & $p.tok.index) + getTok(p) + else: + pegError(p, "expression expected, but found: " & p.tok.literal) + getTok(p) # we must consume a token here to prevent endless loops! + while true: + case p.tok.kind + of tkOption: + result = ?result + getTok(p) + of tkStar: + result = *result + getTok(p) + of tkPlus: + result = +result + getTok(p) + else: break + +proc seqExpr(p: var PegParser): TPeg = + result = primary(p) + while true: + case p.tok.kind + of tkAmp, tkNot, tkAt, tkStringLit, tkCharSet, tkParLe, tkCurlyLe, + tkAny, tkAnyRune, tkBuiltin, tkEscaped, tkDollar, tkBackref, + tkHat, tkCurlyAt: + result = sequence(result, primary(p)) + of tkIdentifier: + if not arrowIsNextTok(p): + result = sequence(result, primary(p)) + else: break + else: break + +proc parseExpr(p: var PegParser): TPeg = + result = seqExpr(p) + while p.tok.kind == tkBar: + getTok(p) + result = result / seqExpr(p) + +proc parseRule(p: var PegParser): PNonTerminal = + if p.tok.kind == tkIdentifier and arrowIsNextTok(p): + result = getNonTerminal(p, p.tok.literal) + if ntDeclared in result.flags: + pegError(p, "attempt to redefine: " & result.name) + result.line = getLine(p) + result.col = getColumn(p) + getTok(p) + eat(p, tkArrow) + result.rule = parseExpr(p) + incl(result.flags, ntDeclared) # NOW inlining may be attempted + else: + pegError(p, "rule expected, but found: " & p.tok.literal) + +proc rawParse(p: var PegParser): TPeg = + ## parses a rule or a PEG expression + while p.tok.kind == tkBuiltin: + case p.tok.literal + of "i": + p.modifier = modIgnoreCase + getTok(p) + of "y": + p.modifier = modIgnoreStyle + getTok(p) + of "skip": + getTok(p) + p.skip = ?primary(p) + else: break + if p.tok.kind == tkIdentifier and arrowIsNextTok(p): + result = parseRule(p).rule + while p.tok.kind != tkEof: + discard parseRule(p) + else: + p.identIsVerbatim = true + result = parseExpr(p) + if p.tok.kind != tkEof: + pegError(p, "EOF expected, but found: " & p.tok.literal) + for i in 0..high(p.nonterms): + var nt = p.nonterms[i] + if ntDeclared notin nt.flags: + pegError(p, "undeclared identifier: " & nt.name, nt.line, nt.col) + elif ntUsed notin nt.flags and i > 0: + pegError(p, "unused rule: " & nt.name, nt.line, nt.col) + +proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): TPeg = + ## constructs a Peg object from `pattern`. `filename`, `line`, `col` are + ## used for error messages, but they only provide start offsets. `parsePeg` + ## keeps track of line and column numbers within `pattern`. + var p: PegParser + init(PegLexer(p), pattern, filename, line, col) + p.tok.kind = tkInvalid + p.tok.modifier = modNone + p.tok.literal = "" + p.tok.charset = {} + p.nonterms = @[] + p.identIsVerbatim = false + getTok(p) + result = rawParse(p) + +proc peg*(pattern: string): TPeg = + ## constructs a Peg object from the `pattern`. The short name has been + ## chosen to encourage its use as a raw string modifier:: + ## + ## peg"{\ident} \s* '=' \s* {.*}" + result = parsePeg(pattern, "pattern") + +proc escapePeg*(s: string): string = + ## escapes `s` so that it is matched verbatim when used as a peg. + result = "" + var inQuote = false + for c in items(s): + case c + of '\0'..'\31', '\'', '"', '\\': + if inQuote: + result.add('\'') + inQuote = false + result.add("\\x") + result.add(toHex(ord(c), 2)) + else: + if not inQuote: + result.add('\'') + inQuote = true + result.add(c) + if inQuote: result.add('\'') + +when isMainModule: + assert escapePeg("abc''def'") == r"'abc'\x27\x27'def'\x27" + assert match("(a b c)", peg"'(' @ ')'") + assert match("W_HI_Le", peg"\y 'while'") + assert(not match("W_HI_L", peg"\y 'while'")) + assert(not match("W_HI_Le", peg"\y v'while'")) + assert match("W_HI_Le", peg"y'while'") + + assert($ +digits == $peg"\d+") + assert "0158787".match(peg"\d+") + assert "ABC 0232".match(peg"\w+\s+\d+") + assert "ABC".match(peg"\d+ / \w+") + + for word in split("00232this02939is39an22example111", peg"\d+"): + writeln(stdout, word) + + assert matchLen("key", ident) == 3 + + var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident) + assert matchLen("key1= cal9", pattern) == 11 + + var ws = newNonTerminal("ws", 1, 1) + ws.rule = *whitespace + + var expr = newNonTerminal("expr", 1, 1) + expr.rule = sequence(capture(ident), *sequence( + nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr))) + + var c: Captures + var s = "a+b + c +d+e+f" + assert rawMatch(s, expr.rule, 0, c) == len(s) + var a = "" + for i in 0..c.ml-1: + a.add(substr(s, c.matches[i][0], c.matches[i][1])) + assert a == "abcdef" + #echo expr.rule + + #const filename = "lib/devel/peg/grammar.txt" + #var grammar = parsePeg(newFileStream(filename, fmRead), filename) + #echo "a <- [abc]*?".match(grammar) + assert find("_____abc_______", term("abc"), 2) == 5 + assert match("_______ana", peg"A <- 'ana' / . A") + assert match("abcs%%%", peg"A <- ..A / .A / '%'") + + var matches: array[0..MaxSubpatterns-1, string] + if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}": + assert matches[0] == "abc" + else: + assert false + + var g2 = peg"""S <- A B / C D + A <- 'a'+ + B <- 'b'+ + C <- 'c'+ + D <- 'd'+ + """ + assert($g2 == "((A B) / (C D))") + assert match("cccccdddddd", g2) + assert("var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") == + "var1<-keykey; var2<-key2key2") + assert("var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") == + "$1<-$2$2; $1<-$2$2") + assert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}") + + if "aaaaaa" =~ peg"'aa' !. / ({'a'})+": + assert matches[0] == "a" + else: + assert false + + if match("abcdefg", peg"c {d} ef {g}", matches, 2): + assert matches[0] == "d" + assert matches[1] == "g" + else: + assert false + + for x in findAll("abcdef", peg".", 3): + echo x + + for x in findAll("abcdef", peg"^{.}", 3): + assert x == "d" + + if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')": + assert matches[0] == "f" + assert matches[1] == "a, b" + else: + assert false + + assert match("eine übersicht und außerdem", peg"(\letter \white*)+") + # ß is not a lower cased letter?! + assert match("eine übersicht und auerdem", peg"(\lower \white*)+") + assert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+") + assert(not match("456678", peg"(\letter)+")) + + assert("var1 = key; var2 = key2".replacef( + peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") == + "var1<-keykey;var2<-key2key2") + + assert match("prefix/start", peg"^start$", 7) + diff --git a/lib/pure/poly.nim b/lib/pure/poly.nim index 45e528604..286e5a8fd 100644 --- a/lib/pure/poly.nim +++ b/lib/pure/poly.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Robert Persson # # See the file "copying.txt", included in this @@ -12,17 +12,18 @@ import strutils import numeric type - TPoly* = object - cofs:seq[float] + Poly* = object + cofs:seq[float] - -proc degree*(p:TPoly):int= +{.deprecated: [TPoly: Poly].} + +proc degree*(p:Poly):int= ## Returns the degree of the polynomial, ## that is the number of coefficients-1 return p.cofs.len-1 -proc eval*(p:TPoly,x:float):float= +proc eval*(p:Poly,x:float):float= ## Evaluates a polynomial function value for `x` ## quickly using Horners method var n=p.degree @@ -32,7 +33,7 @@ proc eval*(p:TPoly,x:float):float= result = result*x+p.cofs[n] dec n -proc `[]` *(p:TPoly;idx:int):float= +proc `[]` *(p:Poly;idx:int):float= ## Gets a coefficient of the polynomial. ## p[2] will returns the quadric term, p[3] the cubic etc. ## Out of bounds index will return 0.0. @@ -40,7 +41,7 @@ proc `[]` *(p:TPoly;idx:int):float= return 0.0 return p.cofs[idx] -proc `[]=` *(p:var TPoly;idx:int,v:float)= +proc `[]=` *(p:var Poly;idx:int,v:float)= ## Sets an coefficient of the polynomial by index. ## p[2] set the quadric term, p[3] the cubic etc. ## If index is out of range for the coefficients, @@ -56,14 +57,14 @@ proc `[]=` *(p:var TPoly;idx:int,v:float)= p.cofs[idx]=v -iterator items*(p:TPoly):float= +iterator items*(p:Poly):float= ## Iterates through the corfficients of the polynomial. var i=p.degree while i>=0: yield p[i] dec i -proc clean*(p:var TPoly;zerotol=0.0)= +proc clean*(p:var Poly;zerotol=0.0)= ## Removes leading zero coefficients of the polynomial. ## An optional tolerance can be given for what's considered zero. var n=p.degree @@ -76,7 +77,7 @@ proc clean*(p:var TPoly;zerotol=0.0)= if relen: p.cofs.setLen(n+1) -proc `$` *(p:TPoly):string = +proc `$` *(p:Poly):string = ## Gets a somewhat reasonable string representation of the polynomial ## The format should be compatible with most online function plotters, ## for example directly in google search @@ -104,13 +105,13 @@ proc `$` *(p:TPoly):string = result="0" -proc derivative*(p:TPoly):TPoly= +proc derivative*(p: Poly): Poly= ## Returns a new polynomial, which is the derivative of `p` newSeq[float](result.cofs,p.degree) for idx in 0..high(result.cofs): result.cofs[idx]=p.cofs[idx+1]*float(idx+1) -proc diff*(p:TPoly,x:float):float= +proc diff*(p:Poly,x:float):float= ## Evaluates the differentiation of a polynomial with ## respect to `x` quickly using a modifed Horners method var n=p.degree @@ -120,7 +121,7 @@ proc diff*(p:TPoly,x:float):float= result = result*x+p[n]*float(n) dec n -proc integral*(p:TPoly):TPoly= +proc integral*(p:Poly):Poly= ## Returns a new polynomial which is the indefinite ## integral of `p`. The constant term is set to 0.0 newSeq(result.cofs,p.cofs.len+1) @@ -129,7 +130,7 @@ proc integral*(p:TPoly):TPoly= result.cofs[i]=p.cofs[i-1]/float(i) -proc integrate*(p:TPoly;xmin,xmax:float):float= +proc integrate*(p:Poly;xmin,xmax:float):float= ## Computes the definite integral of `p` between `xmin` and `xmax` ## quickly using a modified version of Horners method var @@ -147,7 +148,7 @@ proc integrate*(p:TPoly;xmin,xmax:float):float= result=s2*xmax-s1*xmin -proc initPoly*(cofs:varargs[float]):TPoly= +proc initPoly*(cofs:varargs[float]):Poly= ## Initializes a polynomial with given coefficients. ## The most significant coefficient is first, so to create x^2-2x+3: ## intiPoly(1.0,-2.0,3.0) @@ -163,7 +164,7 @@ proc initPoly*(cofs:varargs[float]):TPoly= result.clean #remove leading zero terms -proc divMod*(p,d:TPoly;q,r:var TPoly)= +proc divMod*(p,d:Poly;q,r:var Poly)= ## Divides `p` with `d`, and stores the quotinent in `q` and ## the remainder in `d` var @@ -191,7 +192,7 @@ proc divMod*(p,d:TPoly;q,r:var TPoly)= r.clean # drop zero coefficients in remainder -proc `+` *(p1:TPoly,p2:TPoly):TPoly= +proc `+` *(p1:Poly,p2:Poly):Poly= ## Adds two polynomials var n=max(p1.cofs.len,p2.cofs.len) newSeq(result.cofs,n) @@ -201,7 +202,7 @@ proc `+` *(p1:TPoly,p2:TPoly):TPoly= result.clean # drop zero coefficients in remainder -proc `*` *(p1:TPoly,p2:TPoly):TPoly= +proc `*` *(p1:Poly,p2:Poly):Poly= ## Multiplies the polynomial `p1` with `p2` var d1=p1.degree @@ -218,24 +219,24 @@ proc `*` *(p1:TPoly,p2:TPoly):TPoly= result.clean -proc `*` *(p:TPoly,f:float):TPoly= +proc `*` *(p:Poly,f:float):Poly= ## Multiplies the polynomial `p` with a real number newSeq(result.cofs,p.cofs.len) for i in 0..high(p.cofs): result[i]=p.cofs[i]*f result.clean -proc `*` *(f:float,p:TPoly):TPoly= +proc `*` *(f:float,p:Poly):Poly= ## Multiplies a real number with a polynomial return p*f -proc `-`*(p:TPoly):TPoly= +proc `-`*(p:Poly):Poly= ## Negates a polynomial result=p for i in countup(0, no roots return @[linrt] elif p.degree==2: diff --git a/lib/pure/rawsockets.nim b/lib/pure/rawsockets.nim index fea09dfa2..62a011999 100644 --- a/lib/pure/rawsockets.nim +++ b/lib/pure/rawsockets.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Dominik Picheta # # See the file "copying.txt", included in this @@ -14,7 +14,7 @@ import unsigned, os -when hostos == "solaris": +when hostOS == "solaris": {.passl: "-lsocket -lnsl".} const useWinVersion = defined(Windows) or defined(nimdoc) @@ -28,7 +28,7 @@ else: export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL, EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET -export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen, +export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen, inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto export @@ -40,22 +40,22 @@ export MSG_PEEK type - TPort* = distinct uint16 ## port type + Port* = distinct uint16 ## port type - TDomain* = enum ## domain, which specifies the protocol family of the + Domain* = enum ## domain, which specifies the protocol family of the ## created socket. Other domains than those that are listed ## here are unsupported. AF_UNIX, ## for local socket (using a file). Unsupported on Windows. AF_INET = 2, ## for network protocol IPv4 or AF_INET6 = 23 ## for network protocol IPv6. - TType* = enum ## second argument to `socket` proc + SockType* = enum ## second argument to `socket` proc SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets SOCK_DGRAM = 2, ## datagram service or Datagram Sockets SOCK_RAW = 3, ## raw protocols atop the network layer. SOCK_SEQPACKET = 5 ## reliable sequenced packet service - TProtocol* = enum ## third argument to `socket` proc + Protocol* = enum ## third argument to `socket` proc IPPROTO_TCP = 6, ## Transmission control protocol. IPPROTO_UDP = 17, ## User datagram protocol. IPPROTO_IP, ## Internet protocol. Unsupported on Windows. @@ -63,19 +63,22 @@ type IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows. IPPROTO_ICMP ## Control message protocol. Unsupported on Windows. - TServent* {.pure, final.} = object ## information about a service + Servent* = object ## information about a service name*: string aliases*: seq[string] - port*: TPort + port*: Port proto*: string - Thostent* {.pure, final.} = object ## information about a given host + Hostent* = object ## information about a given host name*: string aliases*: seq[string] - addrtype*: TDomain + addrtype*: Domain length*: int addrList*: seq[string] +{.deprecated: [TPort: Port, TDomain: Domain, TType: SockType, + TProtocol: Protocol, TServent: Servent, THostent: Hostent].} + when useWinVersion: let osInvalidSocket* = winlean.INVALID_SOCKET @@ -86,37 +89,37 @@ when useWinVersion: FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or (102 shl 8) or 126 - proc ioctlsocket*(s: TSocketHandle, cmd: clong, + proc ioctlsocket*(s: SocketHandle, cmd: clong, argptr: ptr clong): cint {. stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".} else: let osInvalidSocket* = posix.INVALID_SOCKET -proc `==`*(a, b: TPort): bool {.borrow.} +proc `==`*(a, b: Port): bool {.borrow.} ## ``==`` for ports. -proc `$`*(p: TPort): string {.borrow.} +proc `$`*(p: Port): string {.borrow.} ## returns the port number as a string -proc toInt*(domain: TDomain): cint +proc toInt*(domain: Domain): cint ## Converts the TDomain enum to a platform-dependent ``cint``. -proc toInt*(typ: TType): cint +proc toInt*(typ: SockType): cint ## Converts the TType enum to a platform-dependent ``cint``. -proc toInt*(p: TProtocol): cint +proc toInt*(p: Protocol): cint ## Converts the TProtocol enum to a platform-dependent ``cint``. when not useWinVersion: - proc toInt(domain: TDomain): cint = + proc toInt(domain: Domain): cint = case domain of AF_UNIX: result = posix.AF_UNIX of AF_INET: result = posix.AF_INET of AF_INET6: result = posix.AF_INET6 else: discard - proc toInt(typ: TType): cint = + proc toInt(typ: SockType): cint = case typ of SOCK_STREAM: result = posix.SOCK_STREAM of SOCK_DGRAM: result = posix.SOCK_DGRAM @@ -124,7 +127,7 @@ when not useWinVersion: of SOCK_RAW: result = posix.SOCK_RAW else: discard - proc toInt(p: TProtocol): cint = + proc toInt(p: Protocol): cint = case p of IPPROTO_TCP: result = posix.IPPROTO_TCP of IPPROTO_UDP: result = posix.IPPROTO_UDP @@ -135,34 +138,41 @@ when not useWinVersion: else: discard else: - proc toInt(domain: TDomain): cint = + proc toInt(domain: Domain): cint = result = toU16(ord(domain)) - proc toInt(typ: TType): cint = + proc toInt(typ: SockType): cint = result = cint(ord(typ)) - proc toInt(p: TProtocol): cint = + proc toInt(p: Protocol): cint = result = cint(ord(p)) -proc newRawSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, - protocol: TProtocol = IPPROTO_TCP): TSocketHandle = +proc newRawSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM, + protocol: Protocol = IPPROTO_TCP): SocketHandle = ## Creates a new socket; returns `InvalidSocket` if an error occurs. socket(toInt(domain), toInt(typ), toInt(protocol)) -proc close*(socket: TSocketHandle) = +proc newRawSocket*(domain: cint, typ: cint, protocol: cint): SocketHandle = + ## Creates a new socket; returns `InvalidSocket` if an error occurs. + ## + ## Use this overload if one of the enums specified above does + ## not contain what you need. + socket(domain, typ, protocol) + +proc close*(socket: SocketHandle) = ## closes a socket. when useWinVersion: - discard winlean.closeSocket(socket) + discard winlean.closesocket(socket) else: discard posix.close(socket) # TODO: These values should not be discarded. An EOS should be raised. # http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times -proc bindAddr*(socket: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint = +proc bindAddr*(socket: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint = result = bindSocket(socket, name, namelen) -proc listen*(socket: TSocketHandle, backlog = SOMAXCONN): cint {.tags: [FReadIO].} = +proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [ReadIOEffect].} = ## Marks ``socket`` as accepting connections. ## ``Backlog`` specifies the maximum length of the ## queue of pending connections. @@ -171,24 +181,24 @@ proc listen*(socket: TSocketHandle, backlog = SOMAXCONN): cint {.tags: [FReadIO] else: result = posix.listen(socket, cint(backlog)) -proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TType = SOCK_STREAM, - prot: TProtocol = IPPROTO_TCP): ptr TAddrInfo = +proc getAddrInfo*(address: string, port: Port, af: Domain = AF_INET, typ: SockType = SOCK_STREAM, + prot: Protocol = IPPROTO_TCP): ptr AddrInfo = ## ## ## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``! - var hints: TAddrInfo + var hints: AddrInfo result = nil hints.ai_family = toInt(af) hints.ai_socktype = toInt(typ) hints.ai_protocol = toInt(prot) - var gaiResult = getAddrInfo(address, $port, addr(hints), result) + var gaiResult = getaddrinfo(address, $port, addr(hints), result) if gaiResult != 0'i32: when useWinVersion: - osError(osLastError()) + raiseOSError(osLastError()) else: - raise newException(EOS, $gai_strerror(gaiResult)) + raise newException(OSError, $gai_strerror(gaiResult)) -proc dealloc*(ai: ptr TAddrInfo) = +proc dealloc*(ai: ptr AddrInfo) = freeaddrinfo(ai) proc ntohl*(x: int32): int32 = @@ -220,7 +230,7 @@ proc htons*(x: int16): int16 = ## order, this is a no-op; otherwise, it performs a 2-byte swap operation. result = rawsockets.ntohs(x) -proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} = +proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} = ## Searches the database from the beginning and finds the first entry for ## which the service name specified by ``name`` matches the s_name member ## and the protocol name specified by ``proto`` matches the s_proto member. @@ -230,13 +240,13 @@ proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} = var s = winlean.getservbyname(name, proto) else: var s = posix.getservbyname(name, proto) - if s == nil: raise newException(EOS, "Service not found.") + if s == nil: raise newException(OSError, "Service not found.") result.name = $s.s_name result.aliases = cstringArrayToSeq(s.s_aliases) - result.port = TPort(s.s_port) + result.port = Port(s.s_port) result.proto = $s.s_proto -proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} = +proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} = ## Searches the database from the beginning and finds the first entry for ## which the port specified by ``port`` matches the s_port member and the ## protocol name specified by ``proto`` matches the s_proto member. @@ -246,125 +256,125 @@ proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} = var s = winlean.getservbyport(ze(int16(port)).cint, proto) else: var s = posix.getservbyport(ze(int16(port)).cint, proto) - if s == nil: raise newException(EOS, "Service not found.") + if s == nil: raise newException(OSError, "Service not found.") result.name = $s.s_name result.aliases = cstringArrayToSeq(s.s_aliases) - result.port = TPort(s.s_port) + result.port = Port(s.s_port) result.proto = $s.s_proto -proc getHostByAddr*(ip: string): Thostent {.tags: [FReadIO].} = +proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} = ## This function will lookup the hostname of an IP Address. - var myaddr: TInAddr + var myaddr: InAddr myaddr.s_addr = inet_addr(ip) when useWinVersion: var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint, cint(rawsockets.AF_INET)) - if s == nil: osError(osLastError()) + if s == nil: raiseOSError(osLastError()) else: - var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSocklen, + var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen, cint(posix.AF_INET)) if s == nil: - raise newException(EOS, $hstrerror(h_errno)) + raise newException(OSError, $hstrerror(h_errno)) result.name = $s.h_name result.aliases = cstringArrayToSeq(s.h_aliases) - when useWinVersion: - result.addrtype = TDomain(s.h_addrtype) + when useWinVersion: + result.addrtype = Domain(s.h_addrtype) else: if s.h_addrtype == posix.AF_INET: result.addrtype = AF_INET elif s.h_addrtype == posix.AF_INET6: result.addrtype = AF_INET6 else: - raise newException(EOS, "unknown h_addrtype") + raise newException(OSError, "unknown h_addrtype") result.addrList = cstringArrayToSeq(s.h_addr_list) result.length = int(s.h_length) -proc getHostByName*(name: string): Thostent {.tags: [FReadIO].} = +proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} = ## This function will lookup the IP address of a hostname. when useWinVersion: var s = winlean.gethostbyname(name) else: var s = posix.gethostbyname(name) - if s == nil: osError(osLastError()) + if s == nil: raiseOSError(osLastError()) result.name = $s.h_name result.aliases = cstringArrayToSeq(s.h_aliases) - when useWinVersion: - result.addrtype = TDomain(s.h_addrtype) + when useWinVersion: + result.addrtype = Domain(s.h_addrtype) else: if s.h_addrtype == posix.AF_INET: result.addrtype = AF_INET elif s.h_addrtype == posix.AF_INET6: result.addrtype = AF_INET6 else: - raise newException(EOS, "unknown h_addrtype") + raise newException(OSError, "unknown h_addrtype") result.addrList = cstringArrayToSeq(s.h_addr_list) result.length = int(s.h_length) -proc getSockName*(socket: TSocketHandle): TPort = +proc getSockName*(socket: SocketHandle): Port = ## returns the socket's associated port number. - var name: Tsockaddr_in + var name: Sockaddr_in when useWinVersion: name.sin_family = int16(ord(AF_INET)) else: name.sin_family = posix.AF_INET #name.sin_port = htons(cint16(port)) #name.sin_addr.s_addr = htonl(INADDR_ANY) - var namelen = sizeof(name).TSocklen - if getsockname(socket, cast[ptr TSockAddr](addr(name)), + var namelen = sizeof(name).SockLen + if getsockname(socket, cast[ptr SockAddr](addr(name)), addr(namelen)) == -1'i32: - osError(osLastError()) - result = TPort(rawsockets.ntohs(name.sin_port)) + raiseOSError(osLastError()) + result = Port(rawsockets.ntohs(name.sin_port)) -proc getSockOptInt*(socket: TSocketHandle, level, optname: int): int {. - tags: [FReadIO].} = +proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {. + tags: [ReadIOEffect].} = ## getsockopt for integer options. var res: cint - var size = sizeof(res).TSocklen + var size = sizeof(res).SockLen if getsockopt(socket, cint(level), cint(optname), addr(res), addr(size)) < 0'i32: - osError(osLastError()) + raiseOSError(osLastError()) result = int(res) -proc setSockOptInt*(socket: TSocketHandle, level, optname, optval: int) {. - tags: [FWriteIO].} = +proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {. + tags: [WriteIOEffect].} = ## setsockopt for integer options. var value = cint(optval) if setsockopt(socket, cint(level), cint(optname), addr(value), - sizeof(value).TSocklen) < 0'i32: - osError(osLastError()) + sizeof(value).SockLen) < 0'i32: + raiseOSError(osLastError()) -proc setBlocking*(s: TSocketHandle, blocking: bool) = +proc setBlocking*(s: SocketHandle, blocking: bool) = ## Sets blocking mode on socket. ## ## Raises EOS on error. when useWinVersion: var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking if ioctlsocket(s, FIONBIO, addr(mode)) == -1: - osError(osLastError()) + raiseOSError(osLastError()) else: # BSD sockets var x: int = fcntl(s, F_GETFL, 0) if x == -1: - osError(osLastError()) + raiseOSError(osLastError()) else: var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK if fcntl(s, F_SETFL, mode) == -1: - osError(osLastError()) + raiseOSError(osLastError()) -proc timeValFromMilliseconds(timeout = 500): Ttimeval = +proc timeValFromMilliseconds(timeout = 500): Timeval = if timeout != -1: var seconds = timeout div 1000 result.tv_sec = seconds.int32 result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 -proc createFdSet(fd: var TFdSet, s: seq[TSocketHandle], m: var int) = +proc createFdSet(fd: var TFdSet, s: seq[SocketHandle], m: var int) = FD_ZERO(fd) for i in items(s): m = max(m, int(i)) FD_SET(i, fd) -proc pruneSocketSet(s: var seq[TSocketHandle], fd: var TFdSet) = +proc pruneSocketSet(s: var seq[SocketHandle], fd: var TFdSet) = var i = 0 var L = s.len while i < L: @@ -375,7 +385,7 @@ proc pruneSocketSet(s: var seq[TSocketHandle], fd: var TFdSet) = inc(i) setLen(s, L) -proc select*(readfds: var seq[TSocketHandle], timeout = 500): int = +proc select*(readfds: var seq[SocketHandle], timeout = 500): int = ## Traditional select function. This function will return the number of ## sockets that are ready to be read from, written to, or which have errors. ## If there are none; 0 is returned. @@ -383,7 +393,7 @@ proc select*(readfds: var seq[TSocketHandle], timeout = 500): int = ## ## A socket is removed from the specific ``seq`` when it has data waiting to ## be read/written to or has errors (``exceptfds``). - var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout) + var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) var rd: TFdSet var m = 0 @@ -396,8 +406,8 @@ proc select*(readfds: var seq[TSocketHandle], timeout = 500): int = pruneSocketSet(readfds, (rd)) -proc selectWrite*(writefds: var seq[TSocketHandle], - timeout = 500): int {.tags: [FReadIO].} = +proc selectWrite*(writefds: var seq[SocketHandle], + timeout = 500): int {.tags: [ReadIOEffect].} = ## When a socket in ``writefds`` is ready to be written to then a non-zero ## value will be returned specifying the count of the sockets which can be ## written to. The sockets which can be written to will also be removed @@ -405,7 +415,7 @@ proc selectWrite*(writefds: var seq[TSocketHandle], ## ## ``timeout`` is specified in miliseconds and ``-1`` can be specified for ## an unlimited time. - var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout) + var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) var wr: TFdSet var m = 0 @@ -419,5 +429,5 @@ proc selectWrite*(writefds: var seq[TSocketHandle], pruneSocketSet(writefds, (wr)) when defined(Windows): - var wsa: TWSADATA - if WSAStartup(0x0101'i16, addr wsa) != 0: osError(osLastError()) + var wsa: WSAData + if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError()) diff --git a/lib/pure/rawsockets.pretty.nim b/lib/pure/rawsockets.pretty.nim new file mode 100644 index 000000000..46bfba9e7 --- /dev/null +++ b/lib/pure/rawsockets.pretty.nim @@ -0,0 +1,426 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2014 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements a low-level cross-platform sockets interface. Look +## at the ``net`` module for the higher-level version. + +# TODO: Clean up the exports a bit and everything else in general. + +import unsigned, os + +when hostOS == "solaris": + {.passl: "-lsocket -lnsl".} + +const useWinVersion = defined(Windows) or defined(nimdoc) + +when useWinVersion: + import winlean + export WSAEWOULDBLOCK, WSAECONNRESET, WSAECONNABORTED, WSAENETRESET, + WSAEDISCON, ERROR_NETNAME_DELETED +else: + import posix + export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL, + EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET + +export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen, + inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto + +export + SO_ERROR, + SOL_SOCKET, + SOMAXCONN, + SO_ACCEPTCONN, SO_BROADCAST, SO_DEBUG, SO_DONTROUTE, + SO_KEEPALIVE, SO_OOBINLINE, SO_REUSEADDR, + MSG_PEEK + +type + Port* = distinct uint16 ## port type + + Domain* = enum ## domain, which specifies the protocol family of the + ## created socket. Other domains than those that are listed + ## here are unsupported. + AF_UNIX, ## for local socket (using a file). Unsupported on Windows. + AF_INET = 2, ## for network protocol IPv4 or + AF_INET6 = 23 ## for network protocol IPv6. + + SockType* = enum ## second argument to `socket` proc + SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets + SOCK_DGRAM = 2, ## datagram service or Datagram Sockets + SOCK_RAW = 3, ## raw protocols atop the network layer. + SOCK_SEQPACKET = 5 ## reliable sequenced packet service + + Protocol* = enum ## third argument to `socket` proc + IPPROTO_TCP = 6, ## Transmission control protocol. + IPPROTO_UDP = 17, ## User datagram protocol. + IPPROTO_IP, ## Internet protocol. Unsupported on Windows. + IPPROTO_IPV6, ## Internet Protocol Version 6. Unsupported on Windows. + IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows. + IPPROTO_ICMP ## Control message protocol. Unsupported on Windows. + + Servent* = object ## information about a service + name*: string + aliases*: seq[string] + port*: Port + proto*: string + + Hostent* = object ## information about a given host + name*: string + aliases*: seq[string] + addrtype*: Domain + length*: int + addrList*: seq[string] + +{.deprecated: [TPort: Port, TDomain: Domain, TType: SockType, + TProtocol: Protocol, TServent: Servent, THostent: Hostent].} + +when useWinVersion: + let + osInvalidSocket* = winlean.INVALID_SOCKET + + const + IOCPARM_MASK* = 127 + IOC_IN* = int(-2147483648) + FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or + (102 shl 8) or 126 + + proc ioctlsocket*(s: SocketHandle, cmd: clong, + argptr: ptr clong): cint {. + stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".} +else: + let + osInvalidSocket* = posix.INVALID_SOCKET + +proc `==`*(a, b: Port): bool {.borrow.} + ## ``==`` for ports. + +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``. + +proc toInt*(typ: SockType): cint + ## Converts the TType enum to a platform-dependent ``cint``. + +proc toInt*(p: Protocol): cint + ## Converts the TProtocol enum to a platform-dependent ``cint``. + +when not useWinVersion: + proc toInt(domain: Domain): cint = + case domain + of AF_UNIX: result = posix.AF_UNIX + of AF_INET: result = posix.AF_INET + of AF_INET6: result = posix.AF_INET6 + else: discard + + proc toInt(typ: SockType): cint = + case typ + of SOCK_STREAM: result = posix.SOCK_STREAM + of SOCK_DGRAM: result = posix.SOCK_DGRAM + of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET + of SOCK_RAW: result = posix.SOCK_RAW + else: discard + + proc toInt(p: Protocol): cint = + case p + of IPPROTO_TCP: result = posix.IPPROTO_TCP + of IPPROTO_UDP: result = posix.IPPROTO_UDP + of IPPROTO_IP: result = posix.IPPROTO_IP + of IPPROTO_IPV6: result = posix.IPPROTO_IPV6 + of IPPROTO_RAW: result = posix.IPPROTO_RAW + of IPPROTO_ICMP: result = posix.IPPROTO_ICMP + else: discard + +else: + proc toInt(domain: Domain): cint = + result = toU16(ord(domain)) + + proc toInt(typ: SockType): cint = + result = cint(ord(typ)) + + proc toInt(p: Protocol): cint = + result = cint(ord(p)) + + +proc newRawSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM, + protocol: Protocol = IPPROTO_TCP): SocketHandle = + ## Creates a new socket; returns `InvalidSocket` if an error occurs. + socket(toInt(domain), toInt(typ), toInt(protocol)) + +proc close*(socket: SocketHandle) = + ## closes a socket. + when useWinVersion: + discard winlean.closesocket(socket) + else: + discard posix.close(socket) + # TODO: These values should not be discarded. An EOS should be raised. + # http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times + +proc bindAddr*(socket: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint = + result = bindSocket(socket, name, namelen) + +proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [ReadIOEffect].} = + ## Marks ``socket`` as accepting connections. + ## ``Backlog`` specifies the maximum length of the + ## queue of pending connections. + when useWinVersion: + result = winlean.listen(socket, cint(backlog)) + else: + result = posix.listen(socket, cint(backlog)) + +proc getAddrInfo*(address: string, port: Port, af: Domain = AF_INET, typ: SockType = SOCK_STREAM, + prot: Protocol = IPPROTO_TCP): ptr AddrInfo = + ## + ## + ## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``! + var hints: AddrInfo + result = nil + hints.ai_family = toInt(af) + hints.ai_socktype = toInt(typ) + hints.ai_protocol = toInt(prot) + var gaiResult = getaddrinfo(address, $port, addr(hints), result) + if gaiResult != 0'i32: + when useWinVersion: + raiseOSError(osLastError()) + else: + raise newException(OSError, $gai_strerror(gaiResult)) + +proc dealloc*(ai: ptr AddrInfo) = + freeaddrinfo(ai) + +proc ntohl*(x: int32): int32 = + ## Converts 32-bit integers from network to host byte order. + ## On machines where the host byte order is the same as network byte order, + ## this is a no-op; otherwise, it performs a 4-byte swap operation. + when cpuEndian == bigEndian: result = x + else: result = (x shr 24'i32) or + (x shr 8'i32 and 0xff00'i32) or + (x shl 8'i32 and 0xff0000'i32) or + (x shl 24'i32) + +proc ntohs*(x: int16): int16 = + ## Converts 16-bit integers from network to host byte order. On machines + ## where the host byte order is the same as network byte order, this is + ## a no-op; otherwise, it performs a 2-byte swap operation. + when cpuEndian == bigEndian: result = x + else: result = (x shr 8'i16) or (x shl 8'i16) + +proc htonl*(x: int32): int32 = + ## Converts 32-bit integers from host to network byte order. On machines + ## where the host byte order is the same as network byte order, this is + ## a no-op; otherwise, it performs a 4-byte swap operation. + result = rawsockets.ntohl(x) + +proc htons*(x: int16): int16 = + ## Converts 16-bit positive integers from host to network byte order. + ## On machines where the host byte order is the same as network byte + ## order, this is a no-op; otherwise, it performs a 2-byte swap operation. + result = rawsockets.ntohs(x) + +proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} = + ## Searches the database from the beginning and finds the first entry for + ## which the service name specified by ``name`` matches the s_name member + ## and the protocol name specified by ``proto`` matches the s_proto member. + ## + ## On posix this will search through the ``/etc/services`` file. + when useWinVersion: + var s = winlean.getservbyname(name, proto) + else: + var s = posix.getservbyname(name, proto) + if s == nil: raise newException(OSError, "Service not found.") + result.name = $s.s_name + result.aliases = cstringArrayToSeq(s.s_aliases) + result.port = Port(s.s_port) + result.proto = $s.s_proto + +proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} = + ## Searches the database from the beginning and finds the first entry for + ## which the port specified by ``port`` matches the s_port member and the + ## protocol name specified by ``proto`` matches the s_proto member. + ## + ## On posix this will search through the ``/etc/services`` file. + when useWinVersion: + var s = winlean.getservbyport(ze(int16(port)).cint, proto) + else: + var s = posix.getservbyport(ze(int16(port)).cint, proto) + if s == nil: raise newException(OSError, "Service not found.") + result.name = $s.s_name + result.aliases = cstringArrayToSeq(s.s_aliases) + result.port = Port(s.s_port) + result.proto = $s.s_proto + +proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} = + ## This function will lookup the hostname of an IP Address. + var myaddr: InAddr + myaddr.s_addr = inet_addr(ip) + + when useWinVersion: + var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint, + cint(rawsockets.AF_INET)) + if s == nil: raiseOSError(osLastError()) + else: + var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen, + cint(posix.AF_INET)) + if s == nil: + raise newException(OSError, $hstrerror(h_errno)) + + result.name = $s.h_name + result.aliases = cstringArrayToSeq(s.h_aliases) + when useWinVersion: + result.addrtype = Domain(s.h_addrtype) + else: + if s.h_addrtype == posix.AF_INET: + result.addrtype = AF_INET + elif s.h_addrtype == posix.AF_INET6: + result.addrtype = AF_INET6 + else: + raise newException(OSError, "unknown h_addrtype") + result.addrList = cstringArrayToSeq(s.h_addr_list) + result.length = int(s.h_length) + +proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} = + ## This function will lookup the IP address of a hostname. + when useWinVersion: + var s = winlean.gethostbyname(name) + else: + var s = posix.gethostbyname(name) + if s == nil: raiseOSError(osLastError()) + result.name = $s.h_name + result.aliases = cstringArrayToSeq(s.h_aliases) + when useWinVersion: + result.addrtype = Domain(s.h_addrtype) + else: + if s.h_addrtype == posix.AF_INET: + result.addrtype = AF_INET + elif s.h_addrtype == posix.AF_INET6: + result.addrtype = AF_INET6 + else: + raise newException(OSError, "unknown h_addrtype") + result.addrList = cstringArrayToSeq(s.h_addr_list) + result.length = int(s.h_length) + +proc getSockName*(socket: SocketHandle): Port = + ## returns the socket's associated port number. + var name: Sockaddr_in + when useWinVersion: + name.sin_family = int16(ord(AF_INET)) + else: + name.sin_family = posix.AF_INET + #name.sin_port = htons(cint16(port)) + #name.sin_addr.s_addr = htonl(INADDR_ANY) + var namelen = sizeof(name).SockLen + if getsockname(socket, cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: + raiseOSError(osLastError()) + result = Port(rawsockets.ntohs(name.sin_port)) + +proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {. + tags: [ReadIOEffect].} = + ## getsockopt for integer options. + var res: cint + var size = sizeof(res).SockLen + if getsockopt(socket, cint(level), cint(optname), + addr(res), addr(size)) < 0'i32: + raiseOSError(osLastError()) + result = int(res) + +proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {. + tags: [WriteIOEffect].} = + ## setsockopt for integer options. + var value = cint(optval) + if setsockopt(socket, cint(level), cint(optname), addr(value), + sizeof(value).SockLen) < 0'i32: + raiseOSError(osLastError()) + +proc setBlocking*(s: SocketHandle, blocking: bool) = + ## Sets blocking mode on socket. + ## + ## Raises EOS on error. + when useWinVersion: + var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking + if ioctlsocket(s, FIONBIO, addr(mode)) == -1: + raiseOSError(osLastError()) + else: # BSD sockets + var x: int = fcntl(s, F_GETFL, 0) + if x == -1: + raiseOSError(osLastError()) + else: + var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK + if fcntl(s, F_SETFL, mode) == -1: + raiseOSError(osLastError()) + +proc timeValFromMilliseconds(timeout = 500): Timeval = + if timeout != -1: + var seconds = timeout div 1000 + result.tv_sec = seconds.int32 + result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 + +proc createFdSet(fd: var FdSet, s: seq[SocketHandle], m: var int) = + FD_ZERO(fd) + for i in items(s): + m = max(m, int(i)) + fdSet(i, fd) + +proc pruneSocketSet(s: var seq[SocketHandle], fd: var FdSet) = + var i = 0 + var L = s.len + while i < L: + if FD_ISSET(s[i], fd) == 0'i32: + s[i] = s[L-1] + dec(L) + else: + inc(i) + setLen(s, L) + +proc select*(readfds: var seq[SocketHandle], timeout = 500): int = + ## Traditional select function. This function will return the number of + ## sockets that are ready to be read from, written to, or which have errors. + ## If there are none; 0 is returned. + ## ``Timeout`` is in miliseconds and -1 can be specified for no timeout. + ## + ## A socket is removed from the specific ``seq`` when it has data waiting to + ## be read/written to or has errors (``exceptfds``). + var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) + + var rd: FdSet + var m = 0 + createFdSet((rd), readfds, m) + + if timeout != -1: + result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv))) + else: + result = int(select(cint(m+1), addr(rd), nil, nil, nil)) + + pruneSocketSet(readfds, (rd)) + +proc selectWrite*(writefds: var seq[SocketHandle], + timeout = 500): int {.tags: [ReadIOEffect].} = + ## When a socket in ``writefds`` is ready to be written to then a non-zero + ## value will be returned specifying the count of the sockets which can be + ## written to. The sockets which can be written to will also be removed + ## from ``writefds``. + ## + ## ``timeout`` is specified in miliseconds and ``-1`` can be specified for + ## an unlimited time. + var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) + + var wr: FdSet + var m = 0 + createFdSet((wr), writefds, m) + + if timeout != -1: + result = int(select(cint(m+1), nil, addr(wr), nil, addr(tv))) + else: + result = int(select(cint(m+1), nil, addr(wr), nil, nil)) + + pruneSocketSet(writefds, (wr)) + +when defined(Windows): + var wsa: WSAData + if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError()) diff --git a/lib/pure/redis.nim b/lib/pure/redis.nim index 959f5c6ef..52d81b3a4 100644 --- a/lib/pure/redis.nim +++ b/lib/pure/redis.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Dominik Picheta # # See the file "copying.txt", included in this @@ -20,135 +20,139 @@ const redisNil* = "\0\0" type - PPipeline = ref object + Pipeline = ref object enabled: bool buffer: string expected: int ## number of replies expected if pipelined type - TSendMode = enum + SendMode = enum normal, pipelined, multiple type - TRedis* {.pure, final.} = object - socket: TSocket + Redis* = object + socket: Socket connected: bool - pipeline: PPipeline + pipeline: Pipeline - TRedisStatus* = string - TRedisInteger* = biggestInt - TRedisString* = string ## Bulk reply - TRedisList* = seq[TRedisString] ## Multi-bulk reply + RedisStatus* = string + RedisInteger* = BiggestInt + RedisString* = string ## Bulk reply + RedisList* = seq[RedisString] ## Multi-bulk reply - EInvalidReply* = object of ESynch ## Invalid reply from redis - ERedis* = object of ESynch ## Error in redis + ReplyError* = object of IOError ## Invalid reply from redis + RedisError* = object of IOError ## Error in redis -proc newPipeline(): PPipeline = +{.deprecated: [TSendMode: SendMode, TRedis: Redis, TRedisStatus: RedisStatus, + TRedisInteger: RedisInteger, TRedisString: RedisString, + TRedisList: RedisList, EInvalidReply: ReplyError, ERedis: RedisError].} + +proc newPipeline(): Pipeline = new(result) result.buffer = "" result.enabled = false result.expected = 0 -proc open*(host = "localhost", port = 6379.TPort): TRedis = +proc open*(host = "localhost", port = 6379.Port): Redis = ## Opens a connection to the redis server. result.socket = socket(buffered = false) - if result.socket == InvalidSocket: - OSError(OSLastError()) + if result.socket == invalidSocket: + raiseOSError(osLastError()) result.socket.connect(host, port) result.pipeline = newPipeline() proc raiseInvalidReply(expected, got: char) = - raise newException(EInvalidReply, + raise newException(ReplyError, "Expected '$1' at the beginning of a status reply got '$2'" % [$expected, $got]) -proc raiseNoOK(status: string, pipelineEnabled:bool) = +proc raiseNoOK(status: string, pipelineEnabled: bool) = if pipelineEnabled and not (status == "QUEUED" or status == "PIPELINED"): - raise newException(EInvalidReply, "Expected \"QUEUED\" or \"PIPELINED\" got \"$1\"" % status) + raise newException(ReplyError, "Expected \"QUEUED\" or \"PIPELINED\" got \"$1\"" % status) elif not pipelineEnabled and status != "OK": - raise newException(EInvalidReply, "Expected \"OK\" got \"$1\"" % status) + raise newException(ReplyError, "Expected \"OK\" got \"$1\"" % status) -template readSocket(r: TRedis, dummyVal:expr): stmt = - var line {.inject.} :TaintedString = "" +template readSocket(r: Redis, dummyVal:expr): stmt = + var line {.inject.}: TaintedString = "" if r.pipeline.enabled: return dummyVal else: readLine(r.socket, line) -proc parseStatus(r: TRedis, line: string = ""): TRedisStatus = +proc parseStatus(r: Redis, line: string = ""): RedisStatus = if r.pipeline.enabled: return "PIPELINED" if line == "": - raise newException(ERedis, "Server closed connection prematurely") + raise newException(RedisError, "Server closed connection prematurely") if line[0] == '-': - raise newException(ERedis, strip(line)) + raise newException(RedisError, strip(line)) if line[0] != '+': raiseInvalidReply('+', line[0]) return line.substr(1) # Strip '+' -proc readStatus(r:TRedis): TRedisStatus = +proc readStatus(r:Redis): RedisStatus = r.readSocket("PIPELINED") return r.parseStatus(line) -proc parseInteger(r: TRedis, line: string = ""): TRedisInteger = +proc parseInteger(r: Redis, line: string = ""): RedisInteger = if r.pipeline.enabled: return -1 #if line == "+QUEUED": # inside of multi # return -1 if line == "": - raise newException(ERedis, "Server closed connection prematurely") + raise newException(RedisError, "Server closed connection prematurely") if line[0] == '-': - raise newException(ERedis, strip(line)) + raise newException(RedisError, strip(line)) if line[0] != ':': raiseInvalidReply(':', line[0]) # Strip ':' if parseBiggestInt(line, result, 1) == 0: - raise newException(EInvalidReply, "Unable to parse integer.") + raise newException(ReplyError, "Unable to parse integer.") -proc readInteger(r: TRedis): TRedisInteger = +proc readInteger(r: Redis): RedisInteger = r.readSocket(-1) return r.parseInteger(line) -proc recv(sock: TSocket, size: int): TaintedString = +proc recv(sock: Socket, size: int): TaintedString = result = newString(size).TaintedString if sock.recv(cstring(result), size) != size: - raise newException(EInvalidReply, "recv failed") + raise newException(ReplyError, "recv failed") -proc parseSingleString(r: TRedis, line:string, allowMBNil = False): TRedisString = +proc parseSingleString(r: Redis, line:string, allowMBNil = false): RedisString = if r.pipeline.enabled: return "" # Error. if line[0] == '-': - raise newException(ERedis, strip(line)) + raise newException(RedisError, strip(line)) # Some commands return a /bulk/ value or a /multi-bulk/ nil. Odd. if allowMBNil: if line == "*-1": - return RedisNil + return redisNil if line[0] != '$': raiseInvalidReply('$', line[0]) var numBytes = parseInt(line.substr(1)) if numBytes == -1: - return RedisNil + return redisNil var s = r.socket.recv(numBytes+2) result = strip(s.string) -proc readSingleString(r: TRedis): TRedisString = +proc readSingleString(r: Redis): RedisString = r.readSocket("") return r.parseSingleString(line) -proc readNext(r: TRedis): TRedisList +proc readNext(r: Redis): RedisList -proc parseArrayLines(r: TRedis, countLine:string): TRedisList = +proc parseArrayLines(r: Redis, countLine:string): RedisList = if countLine.string[0] != '*': raiseInvalidReply('*', countLine.string[0]) @@ -162,24 +166,24 @@ proc parseArrayLines(r: TRedis, countLine:string): TRedisList = for item in parsed: result.add(item) -proc readArrayLines(r: TRedis): TRedisList = +proc readArrayLines(r: Redis): RedisList = r.readSocket(nil) return r.parseArrayLines(line) -proc parseBulkString(r: TRedis, allowMBNil = False, line:string = ""): TRedisString = +proc parseBulkString(r: Redis, allowMBNil = false, line:string = ""): RedisString = if r.pipeline.enabled: return "" return r.parseSingleString(line, allowMBNil) -proc readBulkString(r: TRedis, allowMBNil = false): TRedisString = +proc readBulkString(r: Redis, allowMBNil = false): RedisString = r.readSocket("") return r.parseBulkString(allowMBNil, line) -proc readArray(r: TRedis): TRedisList = +proc readArray(r: Redis): RedisList = r.readSocket(@[]) return r.parseArrayLines(line) -proc readNext(r: TRedis): TRedisList = +proc readNext(r: Redis): RedisList = r.readSocket(@[]) var res = case line[0] @@ -188,12 +192,12 @@ proc readNext(r: TRedis): TRedisList = of '$': @[r.parseBulkString(true,line)] of '*': r.parseArrayLines(line) else: - raise newException(EInvalidReply, "readNext failed on line: " & line) + raise newException(ReplyError, "readNext failed on line: " & line) nil r.pipeline.expected -= 1 return res -proc flushPipeline*(r: TRedis, wasMulti = false): TRedisList = +proc flushPipeline*(r: Redis, wasMulti = false): RedisList = ## Send buffered commands, clear buffer, return results if r.pipeline.buffer.len > 0: r.socket.send(r.pipeline.buffer) @@ -212,7 +216,7 @@ proc flushPipeline*(r: TRedis, wasMulti = false): TRedisList = r.pipeline.expected = 0 -proc startPipelining*(r: TRedis) = +proc startPipelining*(r: Redis) = ## Enable command pipelining (reduces network roundtrips). ## Note that when enabled, you must call flushPipeline to actually send commands, except ## for multi/exec() which enable and flush the pipeline automatically. @@ -221,7 +225,7 @@ proc startPipelining*(r: TRedis) = r.pipeline.expected = 0 r.pipeline.enabled = true -proc sendCommand(r: TRedis, cmd: string, args: varargs[string]) = +proc sendCommand(r: Redis, cmd: string, args: varargs[string]) = var request = "*" & $(1 + args.len()) & "\c\L" request.add("$" & $cmd.len() & "\c\L") request.add(cmd & "\c\L") @@ -235,7 +239,7 @@ proc sendCommand(r: TRedis, cmd: string, args: varargs[string]) = else: r.socket.send(request) -proc sendCommand(r: TRedis, cmd: string, arg1: string, +proc sendCommand(r: Redis, cmd: string, arg1: string, args: varargs[string]) = var request = "*" & $(2 + args.len()) & "\c\L" request.add("$" & $cmd.len() & "\c\L") @@ -254,68 +258,68 @@ proc sendCommand(r: TRedis, cmd: string, arg1: string, # Keys -proc del*(r: TRedis, keys: varargs[string]): TRedisInteger = +proc del*(r: Redis, keys: varargs[string]): RedisInteger = ## Delete a key or multiple keys r.sendCommand("DEL", keys) return r.readInteger() -proc exists*(r: TRedis, key: string): bool = +proc exists*(r: Redis, key: string): bool = ## Determine if a key exists r.sendCommand("EXISTS", key) return r.readInteger() == 1 -proc expire*(r: TRedis, key: string, seconds: int): bool = +proc expire*(r: Redis, key: string, seconds: int): bool = ## Set a key's time to live in seconds. Returns `false` if the key could ## not be found or the timeout could not be set. r.sendCommand("EXPIRE", key, $seconds) return r.readInteger() == 1 -proc expireAt*(r: TRedis, key: string, timestamp: int): bool = +proc expireAt*(r: Redis, key: string, timestamp: int): bool = ## Set the expiration for a key as a UNIX timestamp. Returns `false` ## if the key could not be found or the timeout could not be set. r.sendCommand("EXPIREAT", key, $timestamp) return r.readInteger() == 1 -proc keys*(r: TRedis, pattern: string): TRedisList = +proc keys*(r: Redis, pattern: string): RedisList = ## Find all keys matching the given pattern r.sendCommand("KEYS", pattern) return r.readArray() -proc move*(r: TRedis, key: string, db: int): bool = +proc move*(r: Redis, key: string, db: int): bool = ## Move a key to another database. Returns `true` on a successful move. r.sendCommand("MOVE", key, $db) return r.readInteger() == 1 -proc persist*(r: TRedis, key: string): bool = +proc persist*(r: Redis, key: string): bool = ## Remove the expiration from a key. ## Returns `true` when the timeout was removed. r.sendCommand("PERSIST", key) return r.readInteger() == 1 -proc randomKey*(r: TRedis): TRedisString = +proc randomKey*(r: Redis): RedisString = ## Return a random key from the keyspace r.sendCommand("RANDOMKEY") return r.readBulkString() -proc rename*(r: TRedis, key, newkey: string): TRedisStatus = +proc rename*(r: Redis, key, newkey: string): RedisStatus = ## Rename a key. ## ## **WARNING:** Overwrites `newkey` if it exists! r.sendCommand("RENAME", key, newkey) raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc renameNX*(r: TRedis, key, newkey: string): bool = +proc renameNX*(r: Redis, key, newkey: string): bool = ## Same as ``rename`` but doesn't continue if `newkey` exists. ## Returns `true` if key was renamed. r.sendCommand("RENAMENX", key, newkey) return r.readInteger() == 1 -proc ttl*(r: TRedis, key: string): TRedisInteger = +proc ttl*(r: Redis, key: string): RedisInteger = ## Get the time to live for a key r.sendCommand("TTL", key) return r.readInteger() -proc keyType*(r: TRedis, key: string): TRedisStatus = +proc keyType*(r: Redis, key: string): RedisStatus = ## Determine the type stored at key r.sendCommand("TYPE", key) return r.readStatus() @@ -323,131 +327,131 @@ proc keyType*(r: TRedis, key: string): TRedisStatus = # Strings -proc append*(r: TRedis, key, value: string): TRedisInteger = +proc append*(r: Redis, key, value: string): RedisInteger = ## Append a value to a key r.sendCommand("APPEND", key, value) return r.readInteger() -proc decr*(r: TRedis, key: string): TRedisInteger = +proc decr*(r: Redis, key: string): RedisInteger = ## Decrement the integer value of a key by one r.sendCommand("DECR", key) return r.readInteger() -proc decrBy*(r: TRedis, key: string, decrement: int): TRedisInteger = +proc decrBy*(r: Redis, key: string, decrement: int): RedisInteger = ## Decrement the integer value of a key by the given number r.sendCommand("DECRBY", key, $decrement) return r.readInteger() -proc get*(r: TRedis, key: string): TRedisString = +proc get*(r: Redis, key: string): RedisString = ## Get the value of a key. Returns `redisNil` when `key` doesn't exist. r.sendCommand("GET", key) return r.readBulkString() -proc getBit*(r: TRedis, key: string, offset: int): TRedisInteger = +proc getBit*(r: Redis, key: string, offset: int): RedisInteger = ## Returns the bit value at offset in the string value stored at key r.sendCommand("GETBIT", key, $offset) return r.readInteger() -proc getRange*(r: TRedis, key: string, start, stop: int): TRedisString = +proc getRange*(r: Redis, key: string, start, stop: int): RedisString = ## Get a substring of the string stored at a key r.sendCommand("GETRANGE", key, $start, $stop) return r.readBulkString() -proc getSet*(r: TRedis, key: string, value: string): TRedisString = +proc getSet*(r: Redis, key: string, value: string): RedisString = ## Set the string value of a key and return its old value. Returns `redisNil` ## when key doesn't exist. r.sendCommand("GETSET", key, value) return r.readBulkString() -proc incr*(r: TRedis, key: string): TRedisInteger = +proc incr*(r: Redis, key: string): RedisInteger = ## Increment the integer value of a key by one. r.sendCommand("INCR", key) return r.readInteger() -proc incrBy*(r: TRedis, key: string, increment: int): TRedisInteger = +proc incrBy*(r: Redis, key: string, increment: int): RedisInteger = ## Increment the integer value of a key by the given number r.sendCommand("INCRBY", key, $increment) return r.readInteger() -proc setk*(r: TRedis, key, value: string) = +proc setk*(r: Redis, key, value: string) = ## Set the string value of a key. ## ## NOTE: This function had to be renamed due to a clash with the `set` type. r.sendCommand("SET", key, value) raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc setNX*(r: TRedis, key, value: string): bool = +proc setNX*(r: Redis, key, value: string): bool = ## Set the value of a key, only if the key does not exist. Returns `true` ## if the key was set. r.sendCommand("SETNX", key, value) return r.readInteger() == 1 -proc setBit*(r: TRedis, key: string, offset: int, - value: string): TRedisInteger = +proc setBit*(r: Redis, key: string, offset: int, + value: string): RedisInteger = ## Sets or clears the bit at offset in the string value stored at key r.sendCommand("SETBIT", key, $offset, value) return r.readInteger() -proc setEx*(r: TRedis, key: string, seconds: int, value: string): TRedisStatus = +proc setEx*(r: Redis, key: string, seconds: int, value: string): RedisStatus = ## Set the value and expiration of a key r.sendCommand("SETEX", key, $seconds, value) raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc setRange*(r: TRedis, key: string, offset: int, - value: string): TRedisInteger = +proc setRange*(r: Redis, key: string, offset: int, + value: string): RedisInteger = ## Overwrite part of a string at key starting at the specified offset r.sendCommand("SETRANGE", key, $offset, value) return r.readInteger() -proc strlen*(r: TRedis, key: string): TRedisInteger = +proc strlen*(r: Redis, key: string): RedisInteger = ## Get the length of the value stored in a key. Returns 0 when key doesn't ## exist. r.sendCommand("STRLEN", key) return r.readInteger() # Hashes -proc hDel*(r: TRedis, key, field: string): bool = +proc hDel*(r: Redis, key, field: string): bool = ## Delete a hash field at `key`. Returns `true` if the field was removed. r.sendCommand("HDEL", key, field) return r.readInteger() == 1 -proc hExists*(r: TRedis, key, field: string): bool = +proc hExists*(r: Redis, key, field: string): bool = ## Determine if a hash field exists. r.sendCommand("HEXISTS", key, field) return r.readInteger() == 1 -proc hGet*(r: TRedis, key, field: string): TRedisString = +proc hGet*(r: Redis, key, field: string): RedisString = ## Get the value of a hash field r.sendCommand("HGET", key, field) return r.readBulkString() -proc hGetAll*(r: TRedis, key: string): TRedisList = +proc hGetAll*(r: Redis, key: string): RedisList = ## Get all the fields and values in a hash r.sendCommand("HGETALL", key) return r.readArray() -proc hIncrBy*(r: TRedis, key, field: string, incr: int): TRedisInteger = +proc hIncrBy*(r: Redis, key, field: string, incr: int): RedisInteger = ## Increment the integer value of a hash field by the given number r.sendCommand("HINCRBY", key, field, $incr) return r.readInteger() -proc hKeys*(r: TRedis, key: string): TRedisList = +proc hKeys*(r: Redis, key: string): RedisList = ## Get all the fields in a hash r.sendCommand("HKEYS", key) return r.readArray() -proc hLen*(r: TRedis, key: string): TRedisInteger = +proc hLen*(r: Redis, key: string): RedisInteger = ## Get the number of fields in a hash r.sendCommand("HLEN", key) return r.readInteger() -proc hMGet*(r: TRedis, key: string, fields: varargs[string]): TRedisList = +proc hMGet*(r: Redis, key: string, fields: varargs[string]): RedisList = ## Get the values of all the given hash fields r.sendCommand("HMGET", key, fields) return r.readArray() -proc hMSet*(r: TRedis, key: string, - fieldValues: openarray[tuple[field, value: string]]) = +proc hMSet*(r: Redis, key: string, + fieldValues: openArray[tuple[field, value: string]]) = ## Set multiple hash fields to multiple values var args = @[key] for field, value in items(fieldValues): @@ -456,24 +460,24 @@ proc hMSet*(r: TRedis, key: string, r.sendCommand("HMSET", args) raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc hSet*(r: TRedis, key, field, value: string): TRedisInteger = +proc hSet*(r: Redis, key, field, value: string): RedisInteger = ## Set the string value of a hash field r.sendCommand("HSET", key, field, value) return r.readInteger() -proc hSetNX*(r: TRedis, key, field, value: string): TRedisInteger = +proc hSetNX*(r: Redis, key, field, value: string): RedisInteger = ## Set the value of a hash field, only if the field does **not** exist r.sendCommand("HSETNX", key, field, value) return r.readInteger() -proc hVals*(r: TRedis, key: string): TRedisList = +proc hVals*(r: Redis, key: string): RedisList = ## Get all the values in a hash r.sendCommand("HVALS", key) return r.readArray() # Lists -proc bLPop*(r: TRedis, keys: varargs[string], timeout: int): TRedisList = +proc bLPop*(r: Redis, keys: varargs[string], timeout: int): RedisList = ## Remove and get the *first* element in a list, or block until ## one is available var args: seq[string] = @[] @@ -482,7 +486,7 @@ proc bLPop*(r: TRedis, keys: varargs[string], timeout: int): TRedisList = r.sendCommand("BLPOP", args) return r.readArray() -proc bRPop*(r: TRedis, keys: varargs[string], timeout: int): TRedisList = +proc bRPop*(r: Redis, keys: varargs[string], timeout: int): RedisList = ## Remove and get the *last* element in a list, or block until one ## is available. var args: seq[string] = @[] @@ -491,8 +495,8 @@ proc bRPop*(r: TRedis, keys: varargs[string], timeout: int): TRedisList = r.sendCommand("BRPOP", args) return r.readArray() -proc bRPopLPush*(r: TRedis, source, destination: string, - timeout: int): TRedisString = +proc bRPopLPush*(r: Redis, source, destination: string, + timeout: int): RedisString = ## Pop a value from a list, push it to another list and return it; or ## block until one is available. ## @@ -500,32 +504,32 @@ proc bRPopLPush*(r: TRedis, source, destination: string, r.sendCommand("BRPOPLPUSH", source, destination, $timeout) return r.readBulkString(true) # Multi-Bulk nil allowed. -proc lIndex*(r: TRedis, key: string, index: int): TRedisString = +proc lIndex*(r: Redis, key: string, index: int): RedisString = ## Get an element from a list by its index r.sendCommand("LINDEX", key, $index) return r.readBulkString() -proc lInsert*(r: TRedis, key: string, before: bool, pivot, value: string): - TRedisInteger = +proc lInsert*(r: Redis, key: string, before: bool, pivot, value: string): + RedisInteger = ## Insert an element before or after another element in a list var pos = if before: "BEFORE" else: "AFTER" r.sendCommand("LINSERT", key, pos, pivot, value) return r.readInteger() -proc lLen*(r: TRedis, key: string): TRedisInteger = +proc lLen*(r: Redis, key: string): RedisInteger = ## Get the length of a list r.sendCommand("LLEN", key) return r.readInteger() -proc lPop*(r: TRedis, key: string): TRedisString = +proc lPop*(r: Redis, key: string): RedisString = ## Remove and get the first element in a list r.sendCommand("LPOP", key) return r.readBulkString() -proc lPush*(r: TRedis, key, value: string, create: bool = True): TRedisInteger = +proc lPush*(r: Redis, key, value: string, create: bool = true): RedisInteger = ## Prepend a value to a list. Returns the length of the list after the push. ## The ``create`` param specifies whether a list should be created if it - ## doesn't exist at ``key``. More specifically if ``create`` is True, `LPUSH` + ## doesn't exist at ``key``. More specifically if ``create`` is true, `LPUSH` ## will be used, otherwise `LPUSHX`. if create: r.sendCommand("LPUSH", key, value) @@ -533,42 +537,42 @@ proc lPush*(r: TRedis, key, value: string, create: bool = True): TRedisInteger = r.sendCommand("LPUSHX", key, value) return r.readInteger() -proc lRange*(r: TRedis, key: string, start, stop: int): TRedisList = +proc lRange*(r: Redis, key: string, start, stop: int): RedisList = ## Get a range of elements from a list. Returns `nil` when `key` ## doesn't exist. r.sendCommand("LRANGE", key, $start, $stop) return r.readArray() -proc lRem*(r: TRedis, key: string, value: string, count: int = 0): TRedisInteger = +proc lRem*(r: Redis, key: string, value: string, count: int = 0): RedisInteger = ## Remove elements from a list. Returns the number of elements that have been ## removed. r.sendCommand("LREM", key, $count, value) return r.readInteger() -proc lSet*(r: TRedis, key: string, index: int, value: string) = +proc lSet*(r: Redis, key: string, index: int, value: string) = ## Set the value of an element in a list by its index r.sendCommand("LSET", key, $index, value) raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc lTrim*(r: TRedis, key: string, start, stop: int) = +proc lTrim*(r: Redis, key: string, start, stop: int) = ## Trim a list to the specified range r.sendCommand("LTRIM", key, $start, $stop) raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc rPop*(r: TRedis, key: string): TRedisString = +proc rPop*(r: Redis, key: string): RedisString = ## Remove and get the last element in a list r.sendCommand("RPOP", key) return r.readBulkString() -proc rPopLPush*(r: TRedis, source, destination: string): TRedisString = +proc rPopLPush*(r: Redis, source, destination: string): RedisString = ## Remove the last element in a list, append it to another list and return it r.sendCommand("RPOPLPUSH", source, destination) return r.readBulkString() -proc rPush*(r: TRedis, key, value: string, create: bool = True): TRedisInteger = +proc rPush*(r: Redis, key, value: string, create: bool = true): RedisInteger = ## Append a value to a list. Returns the length of the list after the push. ## The ``create`` param specifies whether a list should be created if it - ## doesn't exist at ``key``. More specifically if ``create`` is True, `RPUSH` + ## doesn't exist at ``key``. More specifically if ``create`` is true, `RPUSH` ## will be used, otherwise `RPUSHX`. if create: r.sendCommand("RPUSH", key, value) @@ -578,106 +582,106 @@ proc rPush*(r: TRedis, key, value: string, create: bool = True): TRedisInteger = # Sets -proc sadd*(r: TRedis, key: string, member: string): TRedisInteger = +proc sadd*(r: Redis, key: string, member: string): RedisInteger = ## Add a member to a set r.sendCommand("SADD", key, member) return r.readInteger() -proc scard*(r: TRedis, key: string): TRedisInteger = +proc scard*(r: Redis, key: string): RedisInteger = ## Get the number of members in a set r.sendCommand("SCARD", key) return r.readInteger() -proc sdiff*(r: TRedis, keys: varargs[string]): TRedisList = +proc sdiff*(r: Redis, keys: varargs[string]): RedisList = ## Subtract multiple sets r.sendCommand("SDIFF", keys) return r.readArray() -proc sdiffstore*(r: TRedis, destination: string, - keys: varargs[string]): TRedisInteger = +proc sdiffstore*(r: Redis, destination: string, + keys: varargs[string]): RedisInteger = ## Subtract multiple sets and store the resulting set in a key r.sendCommand("SDIFFSTORE", destination, keys) return r.readInteger() -proc sinter*(r: TRedis, keys: varargs[string]): TRedisList = +proc sinter*(r: Redis, keys: varargs[string]): RedisList = ## Intersect multiple sets r.sendCommand("SINTER", keys) return r.readArray() -proc sinterstore*(r: TRedis, destination: string, - keys: varargs[string]): TRedisInteger = +proc sinterstore*(r: Redis, destination: string, + keys: varargs[string]): RedisInteger = ## Intersect multiple sets and store the resulting set in a key r.sendCommand("SINTERSTORE", destination, keys) return r.readInteger() -proc sismember*(r: TRedis, key: string, member: string): TRedisInteger = +proc sismember*(r: Redis, key: string, member: string): RedisInteger = ## Determine if a given value is a member of a set r.sendCommand("SISMEMBER", key, member) return r.readInteger() -proc smembers*(r: TRedis, key: string): TRedisList = +proc smembers*(r: Redis, key: string): RedisList = ## Get all the members in a set r.sendCommand("SMEMBERS", key) return r.readArray() -proc smove*(r: TRedis, source: string, destination: string, - member: string): TRedisInteger = +proc smove*(r: Redis, source: string, destination: string, + member: string): RedisInteger = ## Move a member from one set to another r.sendCommand("SMOVE", source, destination, member) return r.readInteger() -proc spop*(r: TRedis, key: string): TRedisString = +proc spop*(r: Redis, key: string): RedisString = ## Remove and return a random member from a set r.sendCommand("SPOP", key) return r.readBulkString() -proc srandmember*(r: TRedis, key: string): TRedisString = +proc srandmember*(r: Redis, key: string): RedisString = ## Get a random member from a set r.sendCommand("SRANDMEMBER", key) return r.readBulkString() -proc srem*(r: TRedis, key: string, member: string): TRedisInteger = +proc srem*(r: Redis, key: string, member: string): RedisInteger = ## Remove a member from a set r.sendCommand("SREM", key, member) return r.readInteger() -proc sunion*(r: TRedis, keys: varargs[string]): TRedisList = +proc sunion*(r: Redis, keys: varargs[string]): RedisList = ## Add multiple sets r.sendCommand("SUNION", keys) return r.readArray() -proc sunionstore*(r: TRedis, destination: string, - key: varargs[string]): TRedisInteger = +proc sunionstore*(r: Redis, destination: string, + key: varargs[string]): RedisInteger = ## Add multiple sets and store the resulting set in a key r.sendCommand("SUNIONSTORE", destination, key) return r.readInteger() # Sorted sets -proc zadd*(r: TRedis, key: string, score: int, member: string): TRedisInteger = +proc zadd*(r: Redis, key: string, score: int, member: string): RedisInteger = ## Add a member to a sorted set, or update its score if it already exists r.sendCommand("ZADD", key, $score, member) return r.readInteger() -proc zcard*(r: TRedis, key: string): TRedisInteger = +proc zcard*(r: Redis, key: string): RedisInteger = ## Get the number of members in a sorted set r.sendCommand("ZCARD", key) return r.readInteger() -proc zcount*(r: TRedis, key: string, min: string, max: string): TRedisInteger = +proc zcount*(r: Redis, key: string, min: string, max: string): RedisInteger = ## Count the members in a sorted set with scores within the given values r.sendCommand("ZCOUNT", key, min, max) return r.readInteger() -proc zincrby*(r: TRedis, key: string, increment: string, - member: string): TRedisString = +proc zincrby*(r: Redis, key: string, increment: string, + member: string): RedisString = ## Increment the score of a member in a sorted set r.sendCommand("ZINCRBY", key, increment, member) return r.readBulkString() -proc zinterstore*(r: TRedis, destination: string, numkeys: string, - keys: openarray[string], weights: openarray[string] = [], - aggregate: string = ""): TRedisInteger = +proc zinterstore*(r: Redis, destination: string, numkeys: string, + keys: openArray[string], weights: openArray[string] = [], + aggregate: string = ""): RedisInteger = ## Intersect multiple sorted sets and store the resulting sorted set in ## a new key var args = @[destination, numkeys] @@ -694,8 +698,8 @@ proc zinterstore*(r: TRedis, destination: string, numkeys: string, return r.readInteger() -proc zrange*(r: TRedis, key: string, start: string, stop: string, - withScores: bool): TRedisList = +proc zrange*(r: Redis, key: string, start: string, stop: string, + withScores: bool): RedisList = ## Return a range of members in a sorted set, by index if not withScores: r.sendCommand("ZRANGE", key, start, stop) @@ -703,9 +707,9 @@ proc zrange*(r: TRedis, key: string, start: string, stop: string, r.sendCommand("ZRANGE", "WITHSCORES", key, start, stop) return r.readArray() -proc zrangebyscore*(r: TRedis, key: string, min: string, max: string, - withScore: bool = false, limit: bool = False, - limitOffset: int = 0, limitCount: int = 0): TRedisList = +proc zrangebyscore*(r: Redis, key: string, min: string, max: string, + withScore: bool = false, limit: bool = false, + limitOffset: int = 0, limitCount: int = 0): RedisList = ## Return a range of members in a sorted set, by score var args = @[key, min, max] @@ -718,30 +722,30 @@ proc zrangebyscore*(r: TRedis, key: string, min: string, max: string, r.sendCommand("ZRANGEBYSCORE", args) return r.readArray() -proc zrank*(r: TRedis, key: string, member: string): TRedisString = +proc zrank*(r: Redis, key: string, member: string): RedisString = ## Determine the index of a member in a sorted set r.sendCommand("ZRANK", key, member) return r.readBulkString() -proc zrem*(r: TRedis, key: string, member: string): TRedisInteger = +proc zrem*(r: Redis, key: string, member: string): RedisInteger = ## Remove a member from a sorted set r.sendCommand("ZREM", key, member) return r.readInteger() -proc zremrangebyrank*(r: TRedis, key: string, start: string, - stop: string): TRedisInteger = +proc zremrangebyrank*(r: Redis, key: string, start: string, + stop: string): RedisInteger = ## Remove all members in a sorted set within the given indexes r.sendCommand("ZREMRANGEBYRANK", key, start, stop) return r.readInteger() -proc zremrangebyscore*(r: TRedis, key: string, min: string, - max: string): TRedisInteger = +proc zremrangebyscore*(r: Redis, key: string, min: string, + max: string): RedisInteger = ## Remove all members in a sorted set within the given scores r.sendCommand("ZREMRANGEBYSCORE", key, min, max) return r.readInteger() -proc zrevrange*(r: TRedis, key: string, start: string, stop: string, - withScore: bool): TRedisList = +proc zrevrange*(r: Redis, key: string, start: string, stop: string, + withScore: bool): RedisList = ## Return a range of members in a sorted set, by index, ## with scores ordered from high to low if withScore: @@ -749,9 +753,9 @@ proc zrevrange*(r: TRedis, key: string, start: string, stop: string, else: r.sendCommand("ZREVRANGE", key, start, stop) return r.readArray() -proc zrevrangebyscore*(r: TRedis, key: string, min: string, max: string, - withScore: bool = false, limit: bool = False, - limitOffset: int = 0, limitCount: int = 0): TRedisList = +proc zrevrangebyscore*(r: Redis, key: string, min: string, max: string, + withScore: bool = false, limit: bool = false, + limitOffset: int = 0, limitCount: int = 0): RedisList = ## Return a range of members in a sorted set, by score, with ## scores ordered from high to low var args = @[key, min, max] @@ -765,20 +769,20 @@ proc zrevrangebyscore*(r: TRedis, key: string, min: string, max: string, r.sendCommand("ZREVRANGEBYSCORE", args) return r.readArray() -proc zrevrank*(r: TRedis, key: string, member: string): TRedisString = +proc zrevrank*(r: Redis, key: string, member: string): RedisString = ## Determine the index of a member in a sorted set, with ## scores ordered from high to low r.sendCommand("ZREVRANK", key, member) return r.readBulkString() -proc zscore*(r: TRedis, key: string, member: string): TRedisString = +proc zscore*(r: Redis, key: string, member: string): RedisString = ## Get the score associated with the given member in a sorted set r.sendCommand("ZSCORE", key, member) return r.readBulkString() -proc zunionstore*(r: TRedis, destination: string, numkeys: string, - keys: openarray[string], weights: openarray[string] = [], - aggregate: string = ""): TRedisInteger = +proc zunionstore*(r: Redis, destination: string, numkeys: string, + keys: openArray[string], weights: openArray[string] = [], + aggregate: string = ""): RedisInteger = ## Add multiple sorted sets and store the resulting sorted set in a new key var args = @[destination, numkeys] for i in items(keys): args.add(i) @@ -828,12 +832,12 @@ proc unsubscribe*(r: TRedis, [channel: openarray[string], : string): ???? = # Transactions -proc discardMulti*(r: TRedis) = +proc discardMulti*(r: Redis) = ## Discard all commands issued after MULTI r.sendCommand("DISCARD") raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc exec*(r: TRedis): TRedisList = +proc exec*(r: Redis): RedisList = ## Execute all commands issued after MULTI r.sendCommand("EXEC") r.pipeline.enabled = false @@ -842,106 +846,106 @@ proc exec*(r: TRedis): TRedisList = return r.flushPipeline(true) -proc multi*(r: TRedis) = +proc multi*(r: Redis) = ## Mark the start of a transaction block r.startPipelining() r.sendCommand("MULTI") raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc unwatch*(r: TRedis) = +proc unwatch*(r: Redis) = ## Forget about all watched keys r.sendCommand("UNWATCH") raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc watch*(r: TRedis, key: varargs[string]) = +proc watch*(r: Redis, key: varargs[string]) = ## Watch the given keys to determine execution of the MULTI/EXEC block r.sendCommand("WATCH", key) raiseNoOK(r.readStatus(), r.pipeline.enabled) # Connection -proc auth*(r: TRedis, password: string) = +proc auth*(r: Redis, password: string) = ## Authenticate to the server r.sendCommand("AUTH", password) raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc echoServ*(r: TRedis, message: string): TRedisString = +proc echoServ*(r: Redis, message: string): RedisString = ## Echo the given string r.sendCommand("ECHO", message) return r.readBulkString() -proc ping*(r: TRedis): TRedisStatus = +proc ping*(r: Redis): RedisStatus = ## Ping the server r.sendCommand("PING") return r.readStatus() -proc quit*(r: TRedis) = +proc quit*(r: Redis) = ## Close the connection r.sendCommand("QUIT") raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc select*(r: TRedis, index: int): TRedisStatus = +proc select*(r: Redis, index: int): RedisStatus = ## Change the selected database for the current connection r.sendCommand("SELECT", $index) return r.readStatus() # Server -proc bgrewriteaof*(r: TRedis) = +proc bgrewriteaof*(r: Redis) = ## Asynchronously rewrite the append-only file r.sendCommand("BGREWRITEAOF") raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc bgsave*(r: TRedis) = +proc bgsave*(r: Redis) = ## Asynchronously save the dataset to disk r.sendCommand("BGSAVE") raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc configGet*(r: TRedis, parameter: string): TRedisList = +proc configGet*(r: Redis, parameter: string): RedisList = ## Get the value of a configuration parameter r.sendCommand("CONFIG", "GET", parameter) return r.readArray() -proc configSet*(r: TRedis, parameter: string, value: string) = +proc configSet*(r: Redis, parameter: string, value: string) = ## Set a configuration parameter to the given value r.sendCommand("CONFIG", "SET", parameter, value) raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc configResetStat*(r: TRedis) = +proc configResetStat*(r: Redis) = ## Reset the stats returned by INFO r.sendCommand("CONFIG", "RESETSTAT") raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc dbsize*(r: TRedis): TRedisInteger = +proc dbsize*(r: Redis): RedisInteger = ## Return the number of keys in the selected database r.sendCommand("DBSIZE") return r.readInteger() -proc debugObject*(r: TRedis, key: string): TRedisStatus = +proc debugObject*(r: Redis, key: string): RedisStatus = ## Get debugging information about a key r.sendCommand("DEBUG", "OBJECT", key) return r.readStatus() -proc debugSegfault*(r: TRedis) = +proc debugSegfault*(r: Redis) = ## Make the server crash r.sendCommand("DEBUG", "SEGFAULT") -proc flushall*(r: TRedis): TRedisStatus = +proc flushall*(r: Redis): RedisStatus = ## Remove all keys from all databases r.sendCommand("FLUSHALL") raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc flushdb*(r: TRedis): TRedisStatus = +proc flushdb*(r: Redis): RedisStatus = ## Remove all keys from the current database r.sendCommand("FLUSHDB") raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc info*(r: TRedis): TRedisString = +proc info*(r: Redis): RedisString = ## Get information and statistics about the server r.sendCommand("INFO") return r.readBulkString() -proc lastsave*(r: TRedis): TRedisInteger = +proc lastsave*(r: Redis): RedisInteger = ## Get the UNIX time stamp of the last successful save to disk r.sendCommand("LASTSAVE") return r.readInteger() @@ -953,24 +957,24 @@ proc monitor*(r: TRedis) = raiseNoOK(r.readStatus(), r.pipeline.enabled) """ -proc save*(r: TRedis) = +proc save*(r: Redis) = ## Synchronously save the dataset to disk r.sendCommand("SAVE") raiseNoOK(r.readStatus(), r.pipeline.enabled) -proc shutdown*(r: TRedis) = +proc shutdown*(r: Redis) = ## Synchronously save the dataset to disk and then shut down the server r.sendCommand("SHUTDOWN") var s = "".TaintedString r.socket.readLine(s) - if s.string.len != 0: raise newException(ERedis, s.string) + if s.string.len != 0: raise newException(RedisError, s.string) -proc slaveof*(r: TRedis, host: string, port: string) = +proc slaveof*(r: Redis, host: string, port: string) = ## Make the server a slave of another instance, or promote it as master r.sendCommand("SLAVEOF", host, port) raiseNoOK(r.readStatus(), r.pipeline.enabled) -iterator hPairs*(r: TRedis, key: string): tuple[key, value: string] = +iterator hPairs*(r: Redis, key: string): tuple[key, value: string] = ## Iterator for keys and values in a hash. var contents = r.hGetAll(key) @@ -982,7 +986,7 @@ iterator hPairs*(r: TRedis, key: string): tuple[key, value: string] = yield (k, i) k = "" -proc someTests(r: TRedis, how: TSendMode):seq[string] = +proc someTests(r: Redis, how: SendMode):seq[string] = var list:seq[string] = @[] if how == pipelined: @@ -1003,10 +1007,10 @@ proc someTests(r: TRedis, how: TSendMode):seq[string] = for r in res: list.add(r) list.add(r.get("invalid_key")) - list.add($(r.lpush("mylist","itema"))) - list.add($(r.lpush("mylist","itemb"))) - r.ltrim("mylist",0,1) - var p = r.lrange("mylist", 0, -1) + list.add($(r.lPush("mylist","itema"))) + list.add($(r.lPush("mylist","itemb"))) + r.lTrim("mylist",0,1) + var p = r.lRange("mylist", 0, -1) for i in items(p): if not isNil(i): diff --git a/lib/pure/romans.nim b/lib/pure/romans.nim index dee3226d8..79fb75526 100644 --- a/lib/pure/romans.nim +++ b/lib/pure/romans.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2011 Philippe Lhoste # # See the file "copying.txt", included in this diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index eb3792bce..995dff2aa 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2010 Andreas Rumpf # # See the file "copying.txt", included in this @@ -10,7 +10,7 @@ ## This module contains support for a `rope`:idx: data type. ## Ropes can represent very long strings efficiently; especially concatenation ## is done in O(1) instead of O(n). They are essentially concatenation -## trees that are only flattened when converting to a native Nimrod +## trees that are only flattened when converting to a native Nim ## string. The empty string is represented by ``nil``. Ropes are immutable and ## subtrees can be shared without copying. ## Leaves can be cached for better memory efficiency at the cost of @@ -30,13 +30,15 @@ var cacheEnabled = false type - PRope* = ref TRope ## empty rope is represented by nil - TRope {.acyclic, final, pure.} = object - left, right: PRope + Rope* = ref RopeObj ## empty rope is represented by nil + RopeObj {.acyclic.} = object + left, right: Rope length: int data: string # != nil if a leaf -proc isConc(r: PRope): bool {.inline.} = return isNil(r.data) +{.deprecated: [PRope: Rope].} + +proc isConc(r: Rope): bool {.inline.} = return isNil(r.data) # Note that the left and right pointers are not needed for leafs. # Leaves have relatively high memory overhead (~30 bytes on a 32 @@ -46,25 +48,25 @@ proc isConc(r: PRope): bool {.inline.} = return isNil(r.data) # performance. But for the caching tree we use the leaf's left and right # pointers. -proc len*(a: PRope): int {.rtl, extern: "nro$1".} = +proc len*(a: Rope): int {.rtl, extern: "nro$1".} = ## the rope's length if a == nil: result = 0 else: result = a.length -proc newRope(): PRope = new(result) -proc newRope(data: string): PRope = +proc newRope(): Rope = new(result) +proc newRope(data: string): Rope = new(result) result.length = len(data) result.data = data var - cache {.threadvar.}: PRope # the root of the cache tree - N {.threadvar.}: PRope # dummy rope needed for splay algorithm + cache {.threadvar.}: Rope # the root of the cache tree + N {.threadvar.}: Rope # dummy rope needed for splay algorithm when countCacheMisses: var misses, hits: int -proc splay(s: string, tree: PRope, cmpres: var int): PRope = +proc splay(s: string, tree: Rope, cmpres: var int): Rope = var c: int var t = tree N.left = nil @@ -102,7 +104,7 @@ proc splay(s: string, tree: PRope, cmpres: var int): PRope = t.right = N.left result = t -proc insertInCache(s: string, tree: PRope): PRope = +proc insertInCache(s: string, tree: Rope): Rope = var t = tree if t == nil: result = newRope(s) @@ -128,7 +130,7 @@ proc insertInCache(s: string, tree: PRope): PRope = result.left = t t.right = nil -proc rope*(s: string): PRope {.rtl, extern: "nro$1Str".} = +proc rope*(s: string): Rope {.rtl, extern: "nro$1Str".} = ## Converts a string to a rope. if s.len == 0: result = nil @@ -138,11 +140,11 @@ proc rope*(s: string): PRope {.rtl, extern: "nro$1Str".} = else: result = newRope(s) -proc rope*(i: BiggestInt): PRope {.rtl, extern: "nro$1BiggestInt".} = +proc rope*(i: BiggestInt): Rope {.rtl, extern: "nro$1BiggestInt".} = ## Converts an int to a rope. result = rope($i) -proc rope*(f: BiggestFloat): PRope {.rtl, extern: "nro$1BiggestFloat".} = +proc rope*(f: BiggestFloat): Rope {.rtl, extern: "nro$1BiggestFloat".} = ## Converts a float to a rope. result = rope($f) @@ -156,7 +158,7 @@ proc disableCache*() {.rtl, extern: "nro$1".} = cache = nil cacheEnabled = false -proc `&`*(a, b: PRope): PRope {.rtl, extern: "nroConcRopeRope".} = +proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} = ## the concatenation operator for ropes. if a == nil: result = b @@ -176,27 +178,27 @@ proc `&`*(a, b: PRope): PRope {.rtl, extern: "nroConcRopeRope".} = result.left = a result.right = b -proc `&`*(a: PRope, b: string): PRope {.rtl, extern: "nroConcRopeStr".} = +proc `&`*(a: Rope, b: string): Rope {.rtl, extern: "nroConcRopeStr".} = ## the concatenation operator for ropes. result = a & rope(b) -proc `&`*(a: string, b: PRope): PRope {.rtl, extern: "nroConcStrRope".} = +proc `&`*(a: string, b: Rope): Rope {.rtl, extern: "nroConcStrRope".} = ## the concatenation operator for ropes. result = rope(a) & b -proc `&`*(a: openarray[PRope]): PRope {.rtl, extern: "nroConcOpenArray".} = +proc `&`*(a: openArray[Rope]): Rope {.rtl, extern: "nroConcOpenArray".} = ## the concatenation operator for an openarray of ropes. for i in countup(0, high(a)): result = result & a[i] -proc add*(a: var PRope, b: PRope) {.rtl, extern: "nro$1Rope".} = +proc add*(a: var Rope, b: Rope) {.rtl, extern: "nro$1Rope".} = ## adds `b` to the rope `a`. a = a & b -proc add*(a: var PRope, b: string) {.rtl, extern: "nro$1Str".} = +proc add*(a: var Rope, b: string) {.rtl, extern: "nro$1Str".} = ## adds `b` to the rope `a`. a = a & b -proc `[]`*(r: PRope, i: int): char {.rtl, extern: "nroCharAt".} = +proc `[]`*(r: Rope, i: int): char {.rtl, extern: "nroCharAt".} = ## returns the character at position `i` in the rope `r`. This is quite ## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned. var x = r @@ -213,7 +215,7 @@ proc `[]`*(r: PRope, i: int): char {.rtl, extern: "nroCharAt".} = x = x.right dec(j, x.len) -iterator leaves*(r: PRope): string = +iterator leaves*(r: Rope): string = ## iterates over any leaf string in the rope `r`. if r != nil: var stack = @[r] @@ -226,16 +228,16 @@ iterator leaves*(r: PRope): string = assert(it.data != nil) yield it.data -iterator items*(r: PRope): char = +iterator items*(r: Rope): char = ## iterates over any character in the rope `r`. for s in leaves(r): for c in items(s): yield c -proc write*(f: TFile, r: PRope) {.rtl, extern: "nro$1".} = +proc write*(f: File, r: Rope) {.rtl, extern: "nro$1".} = ## writes a rope to a file. for s in leaves(r): write(f, s) -proc `$`*(r: PRope): string {.rtl, extern: "nroToString".}= +proc `$`*(r: Rope): string {.rtl, extern: "nroToString".}= ## converts a rope back to a string. result = newString(r.len) setLen(result, 0) @@ -245,11 +247,11 @@ when false: # Format string caching seems reasonable: All leaves can be shared and format # string parsing has to be done only once. A compiled format string is stored # as a rope. A negative length is used for the index into the args array. - proc compiledArg(idx: int): PRope = + proc compiledArg(idx: int): Rope = new(result) result.length = -idx - proc compileFrmt(frmt: string): PRope = + proc compileFrmt(frmt: string): Rope = var i = 0 var length = len(frmt) result = nil @@ -289,7 +291,7 @@ when false: if i - 1 >= start: add(result, substr(frmt, start, i-1)) -proc `%`*(frmt: string, args: openarray[PRope]): PRope {. +proc `%`*(frmt: string, args: openArray[Rope]): Rope {. rtl, extern: "nroFormat".} = ## `%` substitution operator for ropes. Does not support the ``$identifier`` ## nor ``${identifier}`` notations. @@ -322,9 +324,9 @@ proc `%`*(frmt: string, args: openarray[PRope]): PRope {. j = j * 10 + ord(frmt[i]) - ord('0') inc(i) if frmt[i] == '}': inc(i) - else: raise newException(EInvalidValue, "invalid format string") + else: raise newException(ValueError, "invalid format string") add(result, args[j-1]) - else: raise newException(EInvalidValue, "invalid format string") + else: raise newException(ValueError, "invalid format string") var start = i while i < length: if frmt[i] != '$': inc(i) @@ -332,15 +334,15 @@ proc `%`*(frmt: string, args: openarray[PRope]): PRope {. if i - 1 >= start: add(result, substr(frmt, start, i - 1)) -proc addf*(c: var PRope, frmt: string, args: openarray[PRope]) {. +proc addf*(c: var Rope, frmt: string, args: openArray[Rope]) {. rtl, extern: "nro$1".} = ## shortcut for ``add(c, frmt % args)``. add(c, frmt % args) -proc equalsFile*(r: PRope, f: TFile): bool {.rtl, extern: "nro$1File".} = +proc equalsFile*(r: Rope, f: File): bool {.rtl, extern: "nro$1File".} = ## returns true if the contents of the file `f` equal `r`. var bufSize = 1024 # reasonable start value - var buf = alloc(BufSize) + var buf = alloc(bufSize) for s in leaves(r): if s.len > bufSize: bufSize = max(bufSize * 2, s.len) @@ -352,10 +354,10 @@ proc equalsFile*(r: PRope, f: TFile): bool {.rtl, extern: "nro$1File".} = result = readBuffer(f, buf, 1) == 0 # really at the end of file? dealloc(buf) -proc equalsFile*(r: PRope, f: string): bool {.rtl, extern: "nro$1Str".} = +proc equalsFile*(r: Rope, f: string): bool {.rtl, extern: "nro$1Str".} = ## returns true if the contents of the file `f` equal `r`. If `f` does not ## exist, false is returned. - var bin: TFile + var bin: File result = open(bin, f) if result: result = equalsFile(r, bin) diff --git a/lib/pure/scgi.nim b/lib/pure/scgi.nim index 45f837833..f3e2b583c 100644 --- a/lib/pure/scgi.nim +++ b/lib/pure/scgi.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Andreas Rumpf, Dominik Picheta # # See the file "copying.txt", included in this @@ -9,13 +9,13 @@ ## This module implements helper procs for SCGI applications. Example: ## -## .. code-block:: Nimrod +## .. code-block:: Nim ## ## import strtabs, sockets, scgi ## ## var counter = 0 -## proc handleRequest(client: TSocket, input: string, -## headers: PStringTable): bool {.procvar.} = +## proc handleRequest(client: Socket, input: string, +## headers: StringTableRef): bool {.procvar.} = ## inc(counter) ## client.writeStatusOkTextContent() ## client.send("Hello for the $#th time." % $counter & "\c\L") @@ -25,17 +25,21 @@ ## ## **Warning:** The API of this module is unstable, and therefore is subject ## to change. +## +## **Warning:** This module only supports the old asynchronous interface. +## You may wish to use the `asynchttpserver `_ +## instead for web applications. include "system/inclrtl" import sockets, strutils, os, strtabs, asyncio type - EScgi* = object of EIO ## the exception that is raised, if a SCGI error occurs + ScgiError* = object of IOError ## the exception that is raised, if a SCGI error occurs -proc scgiError*(msg: string) {.noreturn.} = - ## raises an EScgi exception with message `msg`. - var e: ref EScgi +proc raiseScgiError*(msg: string) {.noreturn.} = + ## raises an ScgiError exception with message `msg`. + var e: ref ScgiError new(e) e.msg = msg raise e @@ -45,7 +49,7 @@ proc parseWord(inp: string, outp: var string, start: int): int = while inp[result] != '\0': inc(result) outp = substr(inp, start, result-1) -proc parseHeaders(s: string, L: int): PStringTable = +proc parseHeaders(s: string, L: int): StringTableRef = result = newStringTable() var i = 0 while i < L: @@ -54,73 +58,77 @@ proc parseHeaders(s: string, L: int): PStringTable = i = parseWord(s, val, i)+1 result[key] = val if s[i] == ',': inc(i) - else: scgiError("',' after netstring expected") + else: raiseScgiError("',' after netstring expected") -proc recvChar(s: TSocket): char = +proc recvChar(s: Socket): char = var c: char if recv(s, addr(c), sizeof(c)) == sizeof(c): result = c type - TScgiState* = object of TObject ## SCGI state object - server: TSocket + ScgiState* = object of RootObj ## SCGI state object + server: Socket bufLen: int - client*: TSocket ## the client socket to send data to - headers*: PStringTable ## the parsed headers + client*: Socket ## the client socket to send data to + headers*: StringTableRef ## the parsed headers input*: string ## the input buffer # Async - TClientMode = enum + ClientMode = enum ClientReadChar, ClientReadHeaders, ClientReadContent - PAsyncClient = ref object - c: PAsyncSocket - mode: TClientMode + AsyncClient = ref object + c: AsyncSocket + mode: ClientMode dataLen: int - headers: PStringTable ## the parsed headers + headers: StringTableRef ## the parsed headers input: string ## the input buffer - TAsyncScgiState = object - handleRequest: proc (client: PAsyncSocket, - input: string, headers: PStringTable) {.closure,gcsafe.} - asyncServer: PAsyncSocket - disp: PDispatcher - PAsyncScgiState* = ref TAsyncScgiState - -proc recvBuffer(s: var TScgiState, L: int) = + AsyncScgiStateObj = object + handleRequest: proc (client: AsyncSocket, + input: string, + headers: StringTableRef) {.closure, gcsafe.} + asyncServer: AsyncSocket + disp: Dispatcher + AsyncScgiState* = ref AsyncScgiStateObj + +{.deprecated: [EScgi: ScgiError, TScgiState: ScgiState, + PAsyncScgiState: AsyncScgiState, scgiError: raiseScgiError].} + +proc recvBuffer(s: var ScgiState, L: int) = if L > s.bufLen: s.bufLen = L s.input = newString(L) if L > 0 and recv(s.client, cstring(s.input), L) != L: - scgiError("could not read all data") + raiseScgiError("could not read all data") setLen(s.input, L) -proc open*(s: var TScgiState, port = TPort(4000), address = "127.0.0.1", - reuseAddr = False) = +proc open*(s: var ScgiState, port = Port(4000), address = "127.0.0.1", + reuseAddr = false) = ## opens a connection. s.bufLen = 4000 - s.input = newString(s.buflen) # will be reused + s.input = newString(s.bufLen) # will be reused s.server = socket() - if s.server == InvalidSocket: osError(osLastError()) + if s.server == invalidSocket: raiseOSError(osLastError()) new(s.client) # Initialise s.client for `next` - if s.server == InvalidSocket: scgiError("could not open socket") + if s.server == invalidSocket: raiseScgiError("could not open socket") #s.server.connect(connectionName, port) if reuseAddr: - s.server.setSockOpt(OptReuseAddr, True) + s.server.setSockOpt(OptReuseAddr, true) bindAddr(s.server, port, address) listen(s.server) -proc close*(s: var TScgiState) = +proc close*(s: var ScgiState) = ## closes the connection. s.server.close() -proc next*(s: var TScgistate, timeout: int = -1): bool = +proc next*(s: var ScgiState, timeout: int = -1): bool = ## proceed to the first/next request. Waits ``timeout`` miliseconds for a ## request, if ``timeout`` is `-1` then this function will never time out. - ## Returns `True` if a new request has been processed. + ## Returns `true` if a new request has been processed. var rsocks = @[s.server] if select(rsocks, timeout) == 1 and rsocks.len == 1: new(s.client) @@ -131,18 +139,18 @@ proc next*(s: var TScgistate, timeout: int = -1): bool = if d == '\0': s.client.close() return false - if d notin strutils.digits: - if d != ':': scgiError("':' after length expected") + if d notin strutils.Digits: + if d != ':': raiseScgiError("':' after length expected") break L = L * 10 + ord(d) - ord('0') recvBuffer(s, L+1) s.headers = parseHeaders(s.input, L) - if s.headers["SCGI"] != "1": scgiError("SCGI Version 1 expected") + if s.headers["SCGI"] != "1": raiseScgiError("SCGI Version 1 expected") L = parseInt(s.headers["CONTENT_LENGTH"]) recvBuffer(s, L) - return True + return true -proc writeStatusOkTextContent*(c: TSocket, contentType = "text/html") = +proc writeStatusOkTextContent*(c: Socket, contentType = "text/html") = ## sends the following string to the socket `c`:: ## ## Status: 200 OK\r\LContent-Type: text/html\r\L\r\L @@ -151,11 +159,11 @@ proc writeStatusOkTextContent*(c: TSocket, contentType = "text/html") = c.send("Status: 200 OK\r\L" & "Content-Type: $1\r\L\r\L" % contentType) -proc run*(handleRequest: proc (client: TSocket, input: string, - headers: PStringTable): bool {.nimcall,gcsafe.}, - port = TPort(4000)) = +proc run*(handleRequest: proc (client: Socket, input: string, + headers: StringTableRef): bool {.nimcall,gcsafe.}, + port = Port(4000)) = ## encapsulates the SCGI object and main loop. - var s: TScgiState + var s: ScgiState s.open(port) var stop = false while not stop: @@ -166,11 +174,11 @@ proc run*(handleRequest: proc (client: TSocket, input: string, # -- AsyncIO start -proc recvBufferAsync(client: PAsyncClient, L: int): TReadLineResult = +proc recvBufferAsync(client: AsyncClient, L: int): ReadLineResult = result = ReadPartialLine var data = "" if L < 1: - scgiError("Cannot read negative or zero length: " & $L) + raiseScgiError("Cannot read negative or zero length: " & $L) let ret = recvAsync(client.c, data, L) if ret == 0 and data == "": client.c.close() @@ -181,16 +189,16 @@ proc recvBufferAsync(client: PAsyncClient, L: int): TReadLineResult = if ret == L: return ReadFullLine -proc checkCloseSocket(client: PAsyncClient) = +proc checkCloseSocket(client: AsyncClient) = if not client.c.isClosed: if client.c.isSendDataBuffered: - client.c.setHandleWrite do (s: PAsyncSocket): + client.c.setHandleWrite do (s: AsyncSocket): if not s.isClosed and not s.isSendDataBuffered: s.close() s.delHandleWrite() else: client.c.close() -proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) = +proc handleClientRead(client: AsyncClient, s: AsyncScgiState) = case client.mode of ClientReadChar: while true: @@ -202,8 +210,8 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) = return if ret == -1: return # No more data available - if d[0] notin strutils.digits: - if d[0] != ':': scgiError("':' after length expected") + if d[0] notin strutils.Digits: + if d[0] != ':': raiseScgiError("':' after length expected") break client.dataLen = client.dataLen * 10 + ord(d[0]) - ord('0') client.mode = ClientReadHeaders @@ -213,7 +221,7 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) = case ret of ReadFullLine: client.headers = parseHeaders(client.input, client.input.len-1) - if client.headers["SCGI"] != "1": scgiError("SCGI Version 1 expected") + if client.headers["SCGI"] != "1": raiseScgiError("SCGI Version 1 expected") client.input = "" # For next part let contentLen = parseInt(client.headers["CONTENT_LENGTH"]) @@ -236,50 +244,50 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) = s.handleRequest(client.c, client.input, client.headers) checkCloseSocket(client) -proc handleAccept(sock: PAsyncSocket, s: PAsyncScgiState) = - var client: PAsyncSocket +proc handleAccept(sock: AsyncSocket, s: AsyncScgiState) = + var client: AsyncSocket new(client) accept(s.asyncServer, client) - var asyncClient = PAsyncClient(c: client, mode: ClientReadChar, dataLen: 0, + var asyncClient = AsyncClient(c: client, mode: ClientReadChar, dataLen: 0, headers: newStringTable(), input: "") client.handleRead = - proc (sock: PAsyncSocket) = + proc (sock: AsyncSocket) = handleClientRead(asyncClient, s) s.disp.register(client) -proc open*(handleRequest: proc (client: PAsyncSocket, - input: string, headers: PStringTable) {. +proc open*(handleRequest: proc (client: AsyncSocket, + input: string, headers: StringTableRef) {. closure, gcsafe.}, - port = TPort(4000), address = "127.0.0.1", - reuseAddr = false): PAsyncScgiState = - ## Creates an ``PAsyncScgiState`` object which serves as a SCGI server. + port = Port(4000), address = "127.0.0.1", + reuseAddr = false): AsyncScgiState = + ## Creates an ``AsyncScgiState`` object which serves as a SCGI server. ## ## After the execution of ``handleRequest`` the client socket will be closed ## automatically unless it has already been closed. - var cres: PAsyncScgiState + var cres: AsyncScgiState new(cres) - cres.asyncServer = AsyncSocket() - cres.asyncServer.handleAccept = proc (s: PAsyncSocket) = handleAccept(s, cres) + cres.asyncServer = asyncSocket() + cres.asyncServer.handleAccept = proc (s: AsyncSocket) = handleAccept(s, cres) if reuseAddr: - cres.asyncServer.setSockOpt(OptReuseAddr, True) + cres.asyncServer.setSockOpt(OptReuseAddr, true) bindAddr(cres.asyncServer, port, address) listen(cres.asyncServer) cres.handleRequest = handleRequest result = cres -proc register*(d: PDispatcher, s: PAsyncScgiState): PDelegate {.discardable.} = +proc register*(d: Dispatcher, s: AsyncScgiState): Delegate {.discardable.} = ## Registers ``s`` with dispatcher ``d``. result = d.register(s.asyncServer) s.disp = d -proc close*(s: PAsyncScgiState) = - ## Closes the ``PAsyncScgiState``. +proc close*(s: AsyncScgiState) = + ## Closes the ``AsyncScgiState``. s.asyncServer.close() when false: var counter = 0 - proc handleRequest(client: TSocket, input: string, - headers: PStringTable): bool {.procvar.} = + proc handleRequest(client: Socket, input: string, + headers: StringTableRef): bool {.procvar.} = inc(counter) client.writeStatusOkTextContent() client.send("Hello for the $#th time." % $counter & "\c\L") diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index bd53c2dbf..1c988c609 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Dominik Picheta # # See the file "copying.txt", included in this @@ -18,57 +18,57 @@ elif defined(windows): else: import posix -proc hash*(x: TSocketHandle): THash {.borrow.} -proc `$`*(x: TSocketHandle): string {.borrow.} +proc hash*(x: SocketHandle): THash {.borrow.} +proc `$`*(x: SocketHandle): string {.borrow.} type - TEvent* = enum + Event* = enum EvRead, EvWrite - PSelectorKey* = ref object - fd*: TSocketHandle - events*: set[TEvent] ## The events which ``fd`` listens for. - data*: PObject ## User object. + SelectorKey* = ref object + fd*: SocketHandle + events*: set[Event] ## The events which ``fd`` listens for. + data*: RootRef ## User object. - TReadyInfo* = tuple[key: PSelectorKey, events: set[TEvent]] + ReadyInfo* = tuple[key: SelectorKey, events: set[Event]] when defined(nimdoc): type - PSelector* = ref object + Selector* = ref object ## An object which holds file descripters to be checked for read/write ## status. - fds: TTable[TSocketHandle, PSelectorKey] + fds: Table[SocketHandle, SelectorKey] - proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent], - data: PObject): PSelectorKey {.discardable.} = + 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 ## ``events``. - proc update*(s: PSelector, fd: TSocketHandle, - events: set[TEvent]): PSelectorKey {.discardable.} = + proc update*(s: Selector, fd: SocketHandle, + events: set[Event]): SelectorKey {.discardable.} = ## Updates the events which ``fd`` wants notifications for. - proc select*(s: PSelector, timeout: int): seq[TReadyInfo] = + proc select*(s: Selector, timeout: int): seq[ReadyInfo] = ## The ``events`` field of the returned ``key`` contains the original events ## for which the ``fd`` was bound. This is contrary to the ``events`` field ## of the ``TReadyInfo`` tuple which determines which events are ready ## on the ``fd``. - proc contains*(s: PSelector, fd: TSocketHandle): bool = + proc contains*(s: Selector, fd: SocketHandle): bool = ## Determines whether selector contains a file descriptor. - proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey = + proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey = ## Retrieves the selector key for ``fd``. elif defined(linux): type - PSelector* = ref object + Selector* = ref object epollFD: cint events: array[64, epoll_event] - fds: TTable[TSocketHandle, PSelectorKey] + fds: Table[SocketHandle, SelectorKey] - proc createEventStruct(events: set[TEvent], fd: TSocketHandle): epoll_event = + proc createEventStruct(events: set[Event], fd: SocketHandle): epoll_event = if EvRead in events: result.events = EPOLLIN if EvWrite in events: @@ -76,22 +76,22 @@ elif defined(linux): result.events = result.events or EPOLLRDHUP result.data.fd = fd.cint - proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent], - data: PObject): PSelectorKey {.discardable.} = + 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 ## ``events``. var event = createEventStruct(events, fd) if events != {}: if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: - OSError(OSLastError()) + raiseOSError(osLastError()) - var key = PSelectorKey(fd: fd, events: events, data: data) + var key = SelectorKey(fd: fd, events: events, data: data) s.fds[fd] = key result = key - proc update*(s: PSelector, fd: TSocketHandle, - events: set[TEvent]): PSelectorKey {.discardable.} = + proc update*(s: Selector, fd: SocketHandle, + events: set[Event]): SelectorKey {.discardable.} = ## Updates the events which ``fd`` wants notifications for. if s.fds[fd].events != events: if events == {}: @@ -101,7 +101,7 @@ elif defined(linux): # because its got fds which are waiting for no events and # are therefore constantly ready. (leading to 100% CPU usage). if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0: - OSError(OSLastError()) + raiseOSError(osLastError()) s.fds[fd].events = events else: var event = createEventStruct(events, fd) @@ -109,36 +109,36 @@ elif defined(linux): # This fd is idle. It's not a member of this epoll instance and must # be re-registered. if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: - OSError(OSLastError()) + raiseOSError(osLastError()) else: if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0: - OSError(OSLastError()) + raiseOSError(osLastError()) s.fds[fd].events = events result = s.fds[fd] - proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} = + proc unregister*(s: Selector, fd: SocketHandle): SelectorKey {.discardable.} = if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0: - let err = OSLastError() + let err = osLastError() if err.cint notin {ENOENT, EBADF}: # TODO: Why do we sometimes get an EBADF? Is this normal? - OSError(err) + raiseOSError(err) result = s.fds[fd] s.fds.del(fd) - proc close*(s: PSelector) = - if s.epollFD.close() != 0: OSError(OSLastError()) + proc close*(s: Selector) = + if s.epollFD.close() != 0: raiseOSError(osLastError()) dealloc(addr s.events) # TODO: Test this - proc epollHasFd(s: PSelector, fd: TSocketHandle): bool = + proc epollHasFd(s: Selector, fd: SocketHandle): bool = result = true var event = createEventStruct(s.fds[fd].events, fd) if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0: let err = osLastError() if err.cint in {ENOENT, EBADF}: return false - OSError(OSLastError()) + raiseOSError(osLastError()) - proc select*(s: PSelector, timeout: int): seq[TReadyInfo] = + proc select*(s: Selector, timeout: int): seq[ReadyInfo] = ## ## The ``events`` field of the returned ``key`` contains the original events ## for which the ``fd`` was bound. This is contrary to the ``events`` field @@ -146,12 +146,12 @@ elif defined(linux): ## on the ``fd``. result = @[] let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint) - if evNum < 0: OSError(OSLastError()) + if evNum < 0: raiseOSError(osLastError()) if evNum == 0: return @[] for i in 0 .. \c\L") smtp.checkReply("250") @@ -120,13 +136,13 @@ proc sendmail*(smtp: var TSMTP, fromaddr: string, smtp.debugSend(".\c\L") smtp.checkReply("250") -proc close*(smtp: TSMTP) = +proc close*(smtp: Smtp) = ## Disconnects from the SMTP server and closes the socket. smtp.debugSend("QUIT\c\L") smtp.sock.close() proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string], - otherHeaders: openarray[tuple[name, value: string]]): TMessage = + otherHeaders: openarray[tuple[name, value: string]]): Message = ## Creates a new MIME compliant message. result.msgTo = mTo result.msgCc = mCc @@ -137,7 +153,7 @@ proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string], result.msgOtherHeaders[n] = v proc createMessage*(mSubject, mBody: string, mTo, - mCc: seq[string] = @[]): TMessage = + mCc: seq[string] = @[]): Message = ## Alternate version of the above. result.msgTo = mTo result.msgCc = mCc @@ -145,8 +161,8 @@ proc createMessage*(mSubject, mBody: string, mTo, result.msgBody = mBody result.msgOtherHeaders = newStringTable() -proc `$`*(msg: TMessage): string = - ## stringify for ``TMessage``. +proc `$`*(msg: Message): string = + ## stringify for ``Message``. result = "" if msg.msgTo.len() > 0: result = "TO: " & msg.msgTo.join(", ") & "\c\L" @@ -160,6 +176,82 @@ proc `$`*(msg: TMessage): string = result.add("\c\L") result.add(msg.msgBody) +proc newAsyncSmtp*(address: string, port: Port, useSsl = false, + sslContext = defaultSslContext): AsyncSmtp = + ## Creates a new ``AsyncSmtp`` instance. + new result + result.address = address + result.port = port + result.useSsl = useSsl + + result.sock = newAsyncSocket() + if useSsl: + when compiledWithSsl: + sslContext.wrapSocket(result.sock) + else: + raise newException(ESystem, + "SMTP module compiled without SSL support") + +proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] = + var retFuture = newFuture[void]() + var sendFut = smtp.sock.send("QUIT") + sendFut.callback = + proc () = + # TODO: Fix this in async procs. + raise newException(ReplyError, msg) + return retFuture + +proc checkReply(smtp: AsyncSmtp, reply: string) {.async.} = + var line = await smtp.sock.recvLine() + if not line.string.startswith(reply): + await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string) + +proc connect*(smtp: AsyncSmtp) {.async.} = + ## Establishes a connection with a SMTP server. + ## May fail with ReplyError or with a socket error. + await smtp.sock.connect(smtp.address, smtp.port) + + await smtp.checkReply("220") + await smtp.sock.send("HELO " & smtp.address & "\c\L") + await smtp.checkReply("250") + +proc auth*(smtp: AsyncSmtp, username, password: string) {.async.} = + ## Sends an AUTH command to the server to login as the `username` + ## using `password`. + ## May fail with ReplyError. + + await smtp.sock.send("AUTH LOGIN\c\L") + await smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:" + # i.e "334 VXNlcm5hbWU6" + await smtp.sock.send(encode(username) & "\c\L") + await smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?) + + await smtp.sock.send(encode(password) & "\c\L") + await smtp.checkReply("235") # Check whether the authentification was successful. + +proc sendMail*(smtp: AsyncSmtp, fromAddr: string, + toAddrs: seq[string], msg: string) {.async.} = + ## Sends ``msg`` from ``fromAddr`` to the addresses specified in ``toAddrs``. + ## Messages may be formed using ``createMessage`` by converting the + ## Message into a string. + + await smtp.sock.send("MAIL FROM:<" & fromAddr & ">\c\L") + await smtp.checkReply("250") + for address in items(toAddrs): + await smtp.sock.send("RCPT TO:<" & smtp.address & ">\c\L") + await smtp.checkReply("250") + + # Send the message + await smtp.sock.send("DATA " & "\c\L") + await smtp.checkReply("354") + await smtp.sock.send(msg & "\c\L") + await smtp.sock.send(".\c\L") + await smtp.checkReply("250") + +proc close*(smtp: AsyncSmtp) {.async.} = + ## Disconnects from the SMTP server and closes the socket. + await smtp.sock.send("QUIT\c\L") + smtp.sock.close() when isMainModule: #var msg = createMessage("Test subject!", @@ -170,15 +262,16 @@ when isMainModule: #smtp.sendmail("root@localhost", @["dominik@localhost"], $msg) #echo(decode("a17sm3701420wbe.12")) - var msg = createMessage("Hello from Nimrod's SMTP!", - "Hello!!!!.\n Is this awesome or what?", - @["someone@yahoo.com", "someone@gmail.com"]) - echo(msg) + proc main() {.async.} = + var client = newAsyncSmtp("smtp.gmail.com", Port(465), true) + await client.connect() + await client.auth("johndoe", "foo") + var msg = createMessage("Hello from Nim's SMTP!", + "Hello!!!!.\n Is this awesome or what?", + @["blah@gmail.com"]) + echo(msg) + await client.sendMail("blah@gmail.com", @["blah@gmail.com"], $msg) - var smtp = connect("smtp.gmail.com", 465, True, True) - smtp.auth("someone", "password") - smtp.sendmail("someone@gmail.com", - @["someone@yahoo.com", "someone@gmail.com"], $msg) - smtp.close() + await client.close() - + waitFor main() diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim index 7b8b3d557..11eeefcb9 100644 --- a/lib/pure/sockets.nim +++ b/lib/pure/sockets.nim @@ -1,12 +1,16 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Andreas Rumpf, Dominik Picheta # # See the file "copying.txt", included in this # distribution, for details about the copyright. # +## **Warning:** Since version 0.10.2 this module is deprecated. +## Use the `net `_ or the +## `rawsockets `_ module instead. +## ## This module implements portable sockets, it supports a mix of different types ## of sockets. Sockets are buffered by default meaning that data will be ## received in ``BufferSize`` (4000) sized chunks, buffering @@ -14,8 +18,8 @@ ## the ``socket`` function to `false`. Be aware that some functions may not yet ## support buffered sockets (mainly the recvFrom function). ## -## Most procedures raise EOS on error, but some may return ``-1`` or a boolean -## ``false``. +## Most procedures raise OSError on error, but some may return ``-1`` or a +## boolean ``false``. ## ## SSL is supported through the OpenSSL library. This support can be activated ## by compiling with the ``-d:ssl`` switch. When an SSL socket is used it will @@ -24,6 +28,8 @@ ## Asynchronous sockets are supported, however a better alternative is to use ## the `asyncio `_ module. +{.deprecated.} + include "system/inclrtl" {.deadCodeElim: on.} @@ -45,27 +51,32 @@ else: # Note: The enumerations are mapped to Window's constants. -when defined(ssl): - type - ESSL* = object of ESynch +when defined(ssl): - TSSLCVerifyMode* = enum + type + SSLError* = object of Exception + + SSLCVerifyMode* = enum CVerifyNone, CVerifyPeer - TSSLProtVersion* = enum + SSLProtVersion* = enum protSSLv2, protSSLv3, protTLSv1, protSSLv23 - PSSLContext* = distinct PSSLCTX + SSLContext* = distinct SSLCTX - TSSLAcceptResult* = enum + SSLAcceptResult* = enum AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess + {.deprecated: [ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode, + TSSLProtVersion: SSLProtVersion, PSSLContext: SSLContext, + TSSLAcceptResult: SSLAcceptResult].} + const BufferSize*: int = 4000 ## size of a buffered socket's buffer type TSocketImpl = object ## socket type - fd: TSocketHandle + fd: SocketHandle case isBuffered: bool # determines whether this socket is buffered. of true: buffer: array[0..BufferSize, char] @@ -75,32 +86,32 @@ type when defined(ssl): case isSsl: bool of true: - sslHandle: PSSL - sslContext: PSSLContext + sslHandle: SSLPtr + sslContext: SSLContext sslNoHandshake: bool # True if needs handshake. sslHasPeekChar: bool sslPeekChar: char of false: nil nonblocking: bool - TSocket* = ref TSocketImpl + Socket* = ref TSocketImpl - TPort* = distinct uint16 ## port type + Port* = distinct uint16 ## port type - TDomain* = enum ## domain, which specifies the protocol family of the + Domain* = enum ## domain, which specifies the protocol family of the ## created socket. Other domains than those that are listed ## here are unsupported. AF_UNIX, ## for local socket (using a file). Unsupported on Windows. AF_INET = 2, ## for network protocol IPv4 or AF_INET6 = 23 ## for network protocol IPv6. - TType* = enum ## second argument to `socket` proc + SockType* = enum ## second argument to `socket` proc SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets SOCK_DGRAM = 2, ## datagram service or Datagram Sockets SOCK_RAW = 3, ## raw protocols atop the network layer. SOCK_SEQPACKET = 5 ## reliable sequenced packet service - TProtocol* = enum ## third argument to `socket` proc + Protocol* = enum ## third argument to `socket` proc IPPROTO_TCP = 6, ## Transmission control protocol. IPPROTO_UDP = 17, ## User datagram protocol. IPPROTO_IP, ## Internet protocol. Unsupported on Windows. @@ -108,33 +119,40 @@ type IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows. IPPROTO_ICMP ## Control message protocol. Unsupported on Windows. - TServent* {.pure, final.} = object ## information about a service + Servent* = object ## information about a service name*: string aliases*: seq[string] - port*: TPort + port*: Port proto*: string - Thostent* {.pure, final.} = object ## information about a given host + Hostent* = object ## information about a given host name*: string aliases*: seq[string] - addrtype*: TDomain + addrtype*: Domain length*: int addrList*: seq[string] - TSOBool* = enum ## Boolean socket options. + SOBool* = enum ## Boolean socket options. OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive, OptOOBInline, OptReuseAddr - TRecvLineResult* = enum ## result for recvLineAsync + RecvLineResult* = enum ## result for recvLineAsync RecvFullLine, RecvPartialLine, RecvDisconnected, RecvFail - TReadLineResult* = enum ## result for readLineAsync + ReadLineResult* = enum ## result for readLineAsync ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone - ETimeout* = object of ESynch + TimeoutError* = object of Exception -let - invalidSocket*: TSocket = nil ## invalid socket +{.deprecated: [TSocket: Socket, TType: SockType, TPort: Port, TDomain: Domain, + TProtocol: Protocol, TServent: Servent, THostent: Hostent, + TSOBool: SOBool, TRecvLineResult: RecvLineResult, + TReadLineResult: ReadLineResult, ETimeout: TimeoutError].} + +when defined(booting): + let invalidSocket*: Socket = nil ## invalid socket +else: + const invalidSocket*: Socket = nil ## invalid socket when defined(windows): let @@ -143,7 +161,7 @@ else: let osInvalidSocket = posix.INVALID_SOCKET -proc newTSocket(fd: TSocketHandle, isBuff: bool): TSocket = +proc newTSocket(fd: SocketHandle, isBuff: bool): Socket = if fd == osInvalidSocket: return nil new(result) @@ -153,10 +171,10 @@ proc newTSocket(fd: TSocketHandle, isBuff: bool): TSocket = result.currPos = 0 result.nonblocking = false -proc `==`*(a, b: TPort): bool {.borrow.} +proc `==`*(a, b: Port): bool {.borrow.} ## ``==`` for ports. -proc `$`*(p: TPort): string {.borrow.} +proc `$`*(p: Port): string {.borrow.} ## returns the port number as a string proc ntohl*(x: int32): int32 = @@ -189,14 +207,14 @@ proc htons*(x: int16): int16 = result = sockets.ntohs(x) when defined(Posix): - proc toInt(domain: TDomain): cint = + proc toInt(domain: Domain): cint = case domain of AF_UNIX: result = posix.AF_UNIX of AF_INET: result = posix.AF_INET of AF_INET6: result = posix.AF_INET6 else: discard - proc toInt(typ: TType): cint = + proc toInt(typ: SockType): cint = case typ of SOCK_STREAM: result = posix.SOCK_STREAM of SOCK_DGRAM: result = posix.SOCK_DGRAM @@ -204,7 +222,7 @@ when defined(Posix): of SOCK_RAW: result = posix.SOCK_RAW else: discard - proc toInt(p: TProtocol): cint = + proc toInt(p: Protocol): cint = case p of IPPROTO_TCP: result = posix.IPPROTO_TCP of IPPROTO_UDP: result = posix.IPPROTO_UDP @@ -215,17 +233,17 @@ when defined(Posix): else: discard else: - proc toInt(domain: TDomain): cint = + proc toInt(domain: Domain): cint = result = toU16(ord(domain)) - proc toInt(typ: TType): cint = + proc toInt(typ: SockType): cint = result = cint(ord(typ)) - proc toInt(p: TProtocol): cint = + proc toInt(p: Protocol): cint = result = cint(ord(p)) -proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, - protocol: TProtocol = IPPROTO_TCP, buffered = true): TSocket = +proc socket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM, + protocol: Protocol = IPPROTO_TCP, buffered = true): Socket = ## Creates a new socket; returns `InvalidSocket` if an error occurs. # TODO: Perhaps this should just raise EOS when an error occurs. @@ -241,40 +259,40 @@ when defined(ssl): ErrLoadBioStrings() OpenSSL_add_all_algorithms() - proc SSLError(s = "") = + proc raiseSSLError(s = "") = if s != "": - raise newException(ESSL, s) + raise newException(SSLError, s) let err = ErrPeekLastError() if err == 0: - raise newException(ESSL, "No error reported.") + raise newException(SSLError, "No error reported.") if err == -1: - OSError(OSLastError()) + raiseOSError(osLastError()) var errStr = ErrErrorString(err, nil) - raise newException(ESSL, $errStr) + raise newException(SSLError, $errStr) # http://simplestcodings.blogspot.co.uk/2010/08/secure-server-client-using-openssl-in-c.html - proc loadCertificates(ctx: PSSL_CTX, certFile, keyFile: string) = + proc loadCertificates(ctx: SSL_CTX, certFile, keyFile: string) = if certFile != "" and not existsFile(certFile): - raise newException(system.EIO, "Certificate file could not be found: " & certFile) + raise newException(system.IOError, "Certificate file could not be found: " & certFile) if keyFile != "" and not existsFile(keyFile): - raise newException(system.EIO, "Key file could not be found: " & keyFile) + raise newException(system.IOError, "Key file could not be found: " & keyFile) if certFile != "": var ret = SSLCTXUseCertificateChainFile(ctx, certFile) if ret != 1: - SSLError() + raiseSslError() # TODO: Password? www.rtfm.com/openssl-examples/part1.pdf if keyFile != "": if SSL_CTX_use_PrivateKey_file(ctx, keyFile, SSL_FILETYPE_PEM) != 1: - SSLError() + raiseSslError() if SSL_CTX_check_private_key(ctx) != 1: - SSLError("Verification of private key file failed.") + raiseSslError("Verification of private key file failed.") - proc newContext*(protVersion = ProtSSLv23, verifyMode = CVerifyPeer, - certFile = "", keyFile = ""): PSSLContext = + proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer, + certFile = "", keyFile = ""): SSLContext = ## Creates an SSL context. ## ## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 are @@ -290,7 +308,7 @@ when defined(ssl): ## path, a server socket will most likely not work without these. ## Certificates can be generated using the following command: ## ``openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem``. - var newCTX: PSSL_CTX + var newCTX: SSL_CTX case protVersion of protSSLv23: newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support. @@ -298,27 +316,27 @@ when defined(ssl): when not defined(linux) and not defined(OpenBSD): newCTX = SSL_CTX_new(SSLv2_method()) else: - SSLError() + raiseSslError() of protSSLv3: newCTX = SSL_CTX_new(SSLv3_method()) of protTLSv1: newCTX = SSL_CTX_new(TLSv1_method()) if newCTX.SSLCTXSetCipherList("ALL") != 1: - SSLError() + raiseSslError() case verifyMode of CVerifyPeer: newCTX.SSLCTXSetVerify(SSLVerifyPeer, nil) of CVerifyNone: newCTX.SSLCTXSetVerify(SSLVerifyNone, nil) if newCTX == nil: - SSLError() + raiseSslError() discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY) newCTX.loadCertificates(certFile, keyFile) - return PSSLContext(newCTX) + return SSLContext(newCTX) - proc wrapSocket*(ctx: PSSLContext, socket: TSocket) = + proc wrapSocket*(ctx: SSLContext, socket: Socket) = ## Wraps a socket in an SSL context. This function effectively turns ## ``socket`` into an SSL socket. ## @@ -327,16 +345,16 @@ when defined(ssl): socket.isSSL = true socket.sslContext = ctx - socket.sslHandle = SSLNew(PSSLCTX(socket.sslContext)) + socket.sslHandle = SSLNew(SSLCTX(socket.sslContext)) socket.sslNoHandshake = false socket.sslHasPeekChar = false if socket.sslHandle == nil: - SSLError() + raiseSslError() if SSLSetFd(socket.sslHandle, socket.fd) != 1: - SSLError() + raiseSslError() -proc socketError*(socket: TSocket, err: int = -1, async = false) = +proc raiseSocketError*(socket: Socket, err: int = -1, async = false) = ## Raises proper errors based on return values of ``recv`` functions. ## ## If ``async`` is ``True`` no error will be thrown in the case when the @@ -349,20 +367,20 @@ proc socketError*(socket: TSocket, err: int = -1, async = false) = var ret = SSLGetError(socket.sslHandle, err.cint) case ret of SSL_ERROR_ZERO_RETURN: - SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.") + raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.") of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: if async: return - else: SSLError("Not enough data on socket.") + else: raiseSslError("Not enough data on socket.") of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ: if async: return - else: SSLError("Not enough data on socket.") + else: raiseSslError("Not enough data on socket.") of SSL_ERROR_WANT_X509_LOOKUP: - SSLError("Function for x509 lookup has been called.") + raiseSslError("Function for x509 lookup has been called.") of SSL_ERROR_SYSCALL, SSL_ERROR_SSL: - SSLError() - else: SSLError("Unknown Error") + raiseSslError() + else: raiseSslError("Unknown Error") if err == -1 and not (when defined(ssl): socket.isSSL else: false): let lastError = osLastError() @@ -370,21 +388,21 @@ proc socketError*(socket: TSocket, err: int = -1, async = false) = when defined(windows): if lastError.int32 == WSAEWOULDBLOCK: return - else: osError(lastError) + else: raiseOSError(lastError) else: if lastError.int32 == EAGAIN or lastError.int32 == EWOULDBLOCK: return - else: osError(lastError) - else: osError(lastError) + else: raiseOSError(lastError) + else: raiseOSError(lastError) -proc listen*(socket: TSocket, backlog = SOMAXCONN) {.tags: [FReadIO].} = +proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} = ## Marks ``socket`` as accepting connections. ## ``Backlog`` specifies the maximum length of the ## queue of pending connections. - if listen(socket.fd, cint(backlog)) < 0'i32: osError(osLastError()) + if listen(socket.fd, cint(backlog)) < 0'i32: raiseOSError(osLastError()) proc invalidIp4(s: string) {.noreturn, noinline.} = - raise newException(EInvalidValue, "invalid ip4 address: " & s) + raise newException(ValueError, "invalid ip4 address: " & s) proc parseIp4*(s: string): BiggestInt = ## parses an IP version 4 in dotted decimal form like "a.b.c.d". @@ -420,58 +438,58 @@ template gaiNim(a, p, h, list: expr): stmt = var gaiResult = getaddrinfo(a, $p, addr(h), list) if gaiResult != 0'i32: when defined(windows): - osError(osLastError()) + raiseOSError(osLastError()) else: - raise newException(EOS, $gai_strerror(gaiResult)) + raise newException(OSError, $gai_strerror(gaiResult)) -proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {. - tags: [FReadIO].} = +proc bindAddr*(socket: Socket, port = Port(0), address = "") {. + tags: [ReadIOEffect].} = ## binds an address/port number to a socket. ## Use address string in dotted decimal form like "a.b.c.d" ## or leave "" for any address. if address == "": - var name: Tsockaddr_in + var name: Sockaddr_in when defined(Windows): name.sin_family = int16(ord(AF_INET)) else: name.sin_family = posix.AF_INET name.sin_port = sockets.htons(int16(port)) name.sin_addr.s_addr = sockets.htonl(INADDR_ANY) - if bindSocket(socket.fd, cast[ptr TSockAddr](addr(name)), - sizeof(name).TSocklen) < 0'i32: - osError(osLastError()) + if bindSocket(socket.fd, cast[ptr SockAddr](addr(name)), + sizeof(name).SockLen) < 0'i32: + raiseOSError(osLastError()) else: - var hints: Taddrinfo - var aiList: ptr Taddrinfo = nil + var hints: AddrInfo + var aiList: ptr AddrInfo = nil hints.ai_family = toInt(AF_INET) hints.ai_socktype = toInt(SOCK_STREAM) hints.ai_protocol = toInt(IPPROTO_TCP) gaiNim(address, port, hints, aiList) - if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32: - osError(osLastError()) + if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32: + raiseOSError(osLastError()) -proc getSockName*(socket: TSocket): TPort = +proc getSockName*(socket: Socket): Port = ## returns the socket's associated port number. - var name: Tsockaddr_in + var name: Sockaddr_in when defined(Windows): name.sin_family = int16(ord(AF_INET)) else: name.sin_family = posix.AF_INET #name.sin_port = htons(cint16(port)) #name.sin_addr.s_addr = htonl(INADDR_ANY) - var namelen = sizeof(name).TSocklen - if getsockname(socket.fd, cast[ptr TSockAddr](addr(name)), + var namelen = sizeof(name).SockLen + if getsockname(socket.fd, cast[ptr SockAddr](addr(name)), addr(namelen)) == -1'i32: - osError(osLastError()) - result = TPort(sockets.ntohs(name.sin_port)) + raiseOSError(osLastError()) + result = Port(sockets.ntohs(name.sin_port)) template acceptAddrPlain(noClientRet, successRet: expr, sslImplementation: stmt): stmt {.immediate.} = assert(client != nil) - var sockAddress: Tsockaddr_in - var addrLen = sizeof(sockAddress).TSocklen - var sock = accept(server.fd, cast[ptr TSockAddr](addr(sockAddress)), + var sockAddress: Sockaddr_in + var addrLen = sizeof(sockAddress).SockLen + var sock = accept(server.fd, cast[ptr SockAddr](addr(sockAddress)), addr(addrLen)) if sock == osInvalidSocket: @@ -484,7 +502,7 @@ template acceptAddrPlain(noClientRet, successRet: expr, return else: return noClientRet - else: osError(err) + else: raiseOSError(err) else: if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK: client = invalidSocket @@ -493,7 +511,7 @@ template acceptAddrPlain(noClientRet, successRet: expr, return else: return noClientRet - else: osError(err) + else: raiseOSError(err) else: client.fd = sock client.isBuffered = server.isBuffered @@ -505,8 +523,8 @@ template acceptAddrPlain(noClientRet, successRet: expr, else: return successRet -proc acceptAddr*(server: TSocket, client: var TSocket, address: var string) {. - tags: [FReadIO].} = +proc acceptAddr*(server: Socket, client: var Socket, address: var string) {. + tags: [ReadIOEffect].} = ## Blocks until a connection is being made from a client. When a connection ## is made sets ``client`` to the client socket and ``address`` to the address ## of the connecting client. @@ -535,24 +553,24 @@ proc acceptAddr*(server: TSocket, client: var TSocket, address: var string) {. if err != SSL_ERROR_WANT_ACCEPT: case err of SSL_ERROR_ZERO_RETURN: - SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.") + raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.") of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: - SSLError("acceptAddrSSL should be used for non-blocking SSL sockets.") + raiseSslError("acceptAddrSSL should be used for non-blocking SSL sockets.") of SSL_ERROR_WANT_X509_LOOKUP: - SSLError("Function for x509 lookup has been called.") + raiseSslError("Function for x509 lookup has been called.") of SSL_ERROR_SYSCALL, SSL_ERROR_SSL: - SSLError() + raiseSslError() else: - SSLError("Unknown error") + raiseSslError("Unknown error") -proc setBlocking*(s: TSocket, blocking: bool) {.tags: [], gcsafe.} +proc setBlocking*(s: Socket, blocking: bool) {.tags: [], gcsafe.} ## Sets blocking mode on socket when defined(ssl): - proc acceptAddrSSL*(server: TSocket, client: var TSocket, - address: var string): TSSLAcceptResult {. - tags: [FReadIO].} = + proc acceptAddrSSL*(server: Socket, client: var Socket, + 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: ## @@ -581,17 +599,17 @@ when defined(ssl): if err != SSL_ERROR_WANT_ACCEPT: case err of SSL_ERROR_ZERO_RETURN: - SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.") + raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.") of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: client.sslNoHandshake = true return AcceptNoHandshake of SSL_ERROR_WANT_X509_LOOKUP: - SSLError("Function for x509 lookup has been called.") + raiseSslError("Function for x509 lookup has been called.") of SSL_ERROR_SYSCALL, SSL_ERROR_SSL: - SSLError() + raiseSslError() else: - SSLError("Unknown error") + raiseSslError("Unknown error") client.sslNoHandshake = false if client.isSSL and client.sslNoHandshake: @@ -601,7 +619,7 @@ when defined(ssl): acceptAddrPlain(AcceptNoClient, AcceptSuccess): doHandshake() -proc accept*(server: TSocket, client: var TSocket) {.tags: [FReadIO].} = +proc accept*(server: Socket, client: var Socket) {.tags: [ReadIOEffect].} = ## Equivalent to ``acceptAddr`` but doesn't return the address, only the ## socket. ## @@ -611,24 +629,24 @@ proc accept*(server: TSocket, client: var TSocket) {.tags: [FReadIO].} = var addrDummy = "" acceptAddr(server, client, addrDummy) -proc acceptAddr*(server: TSocket): tuple[client: TSocket, address: string] {. - deprecated, tags: [FReadIO].} = +proc acceptAddr*(server: Socket): tuple[client: Socket, address: string] {. + deprecated, tags: [ReadIOEffect].} = ## Slightly different version of ``acceptAddr``. ## ## **Deprecated since version 0.9.0:** Please use the function above. - var client: TSocket + var client: Socket new(client) var address = "" acceptAddr(server, client, address) return (client, address) -proc accept*(server: TSocket): TSocket {.deprecated, tags: [FReadIO].} = +proc accept*(server: Socket): Socket {.deprecated, tags: [ReadIOEffect].} = ## **Deprecated since version 0.9.0:** Please use the function above. new(result) var address = "" acceptAddr(server, result, address) -proc close*(socket: TSocket) = +proc close*(socket: Socket) = ## closes a socket. when defined(windows): discard winlean.closesocket(socket.fd) @@ -640,7 +658,7 @@ proc close*(socket: TSocket) = if socket.isSSL: discard SSLShutdown(socket.sslHandle) -proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} = +proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} = ## Searches the database from the beginning and finds the first entry for ## which the service name specified by ``name`` matches the s_name member ## and the protocol name specified by ``proto`` matches the s_proto member. @@ -650,13 +668,13 @@ proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} = var s = winlean.getservbyname(name, proto) else: var s = posix.getservbyname(name, proto) - if s == nil: raise newException(EOS, "Service not found.") + if s == nil: raise newException(OSError, "Service not found.") result.name = $s.s_name result.aliases = cstringArrayToSeq(s.s_aliases) - result.port = TPort(s.s_port) + result.port = Port(s.s_port) result.proto = $s.s_proto -proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} = +proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} = ## Searches the database from the beginning and finds the first entry for ## which the port specified by ``port`` matches the s_port member and the ## protocol name specified by ``proto`` matches the s_proto member. @@ -666,81 +684,81 @@ proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} = var s = winlean.getservbyport(ze(int16(port)).cint, proto) else: var s = posix.getservbyport(ze(int16(port)).cint, proto) - if s == nil: raise newException(EOS, "Service not found.") + if s == nil: raise newException(OSError, "Service not found.") result.name = $s.s_name result.aliases = cstringArrayToSeq(s.s_aliases) - result.port = TPort(s.s_port) + result.port = Port(s.s_port) result.proto = $s.s_proto -proc getHostByAddr*(ip: string): Thostent {.tags: [FReadIO].} = +proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} = ## This function will lookup the hostname of an IP Address. - var myaddr: TInAddr + var myaddr: InAddr myaddr.s_addr = inet_addr(ip) when defined(windows): var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint, cint(sockets.AF_INET)) - if s == nil: osError(osLastError()) + if s == nil: raiseOSError(osLastError()) else: - var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSocklen, + var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen, cint(posix.AF_INET)) if s == nil: - raise newException(EOS, $hstrerror(h_errno)) + raise newException(OSError, $hstrerror(h_errno)) result.name = $s.h_name result.aliases = cstringArrayToSeq(s.h_aliases) when defined(windows): - result.addrtype = TDomain(s.h_addrtype) + result.addrtype = Domain(s.h_addrtype) else: if s.h_addrtype == posix.AF_INET: result.addrtype = AF_INET elif s.h_addrtype == posix.AF_INET6: result.addrtype = AF_INET6 else: - raise newException(EOS, "unknown h_addrtype") + raise newException(OSError, "unknown h_addrtype") result.addrList = cstringArrayToSeq(s.h_addr_list) result.length = int(s.h_length) -proc getHostByName*(name: string): Thostent {.tags: [FReadIO].} = +proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} = ## This function will lookup the IP address of a hostname. when defined(Windows): var s = winlean.gethostbyname(name) else: var s = posix.gethostbyname(name) - if s == nil: osError(osLastError()) + if s == nil: raiseOSError(osLastError()) result.name = $s.h_name result.aliases = cstringArrayToSeq(s.h_aliases) when defined(windows): - result.addrtype = TDomain(s.h_addrtype) + result.addrtype = Domain(s.h_addrtype) else: if s.h_addrtype == posix.AF_INET: result.addrtype = AF_INET elif s.h_addrtype == posix.AF_INET6: result.addrtype = AF_INET6 else: - raise newException(EOS, "unknown h_addrtype") + raise newException(OSError, "unknown h_addrtype") result.addrList = cstringArrayToSeq(s.h_addr_list) result.length = int(s.h_length) -proc getSockOptInt*(socket: TSocket, level, optname: int): int {. - tags: [FReadIO].} = +proc getSockOptInt*(socket: Socket, level, optname: int): int {. + tags: [ReadIOEffect].} = ## getsockopt for integer options. var res: cint - var size = sizeof(res).TSocklen + var size = sizeof(res).SockLen if getsockopt(socket.fd, cint(level), cint(optname), addr(res), addr(size)) < 0'i32: - osError(osLastError()) + raiseOSError(osLastError()) result = int(res) -proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {. - tags: [FWriteIO].} = +proc setSockOptInt*(socket: Socket, level, optname, optval: int) {. + tags: [WriteIOEffect].} = ## setsockopt for integer options. var value = cint(optval) if setsockopt(socket.fd, cint(level), cint(optname), addr(value), - sizeof(value).TSocklen) < 0'i32: - osError(osLastError()) + sizeof(value).SockLen) < 0'i32: + raiseOSError(osLastError()) -proc toCInt(opt: TSOBool): cint = +proc toCInt(opt: SOBool): cint = case opt of OptAcceptConn: SO_ACCEPTCONN of OptBroadcast: SO_BROADCAST @@ -750,51 +768,51 @@ proc toCInt(opt: TSOBool): cint = of OptOOBInline: SO_OOBINLINE of OptReuseAddr: SO_REUSEADDR -proc getSockOpt*(socket: TSocket, opt: TSOBool, level = SOL_SOCKET): bool {. - tags: [FReadIO].} = +proc getSockOpt*(socket: Socket, opt: SOBool, level = SOL_SOCKET): bool {. + tags: [ReadIOEffect].} = ## Retrieves option ``opt`` as a boolean value. var res: cint - var size = sizeof(res).TSocklen + var size = sizeof(res).SockLen if getsockopt(socket.fd, cint(level), toCInt(opt), addr(res), addr(size)) < 0'i32: - osError(osLastError()) + raiseOSError(osLastError()) result = res != 0 -proc setSockOpt*(socket: TSocket, opt: TSOBool, value: bool, level = SOL_SOCKET) {. - tags: [FWriteIO].} = +proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {. + tags: [WriteIOEffect].} = ## Sets option ``opt`` to a boolean value specified by ``value``. var valuei = cint(if value: 1 else: 0) if setsockopt(socket.fd, cint(level), toCInt(opt), addr(valuei), - sizeof(valuei).TSocklen) < 0'i32: - osError(osLastError()) + sizeof(valuei).SockLen) < 0'i32: + raiseOSError(osLastError()) -proc connect*(socket: TSocket, address: string, port = TPort(0), - af: TDomain = AF_INET) {.tags: [FReadIO].} = +proc connect*(socket: Socket, address: string, port = Port(0), + af: Domain = AF_INET) {.tags: [ReadIOEffect].} = ## Connects socket to ``address``:``port``. ``Address`` can be an IP address or a ## host name. If ``address`` is a host name, this function will try each IP ## of that host name. ``htons`` is already performed on ``port`` so you must ## not do it. ## ## If ``socket`` is an SSL socket a handshake will be automatically performed. - var hints: Taddrinfo - var aiList: ptr Taddrinfo = nil + var hints: AddrInfo + var aiList: ptr AddrInfo = nil hints.ai_family = toInt(af) hints.ai_socktype = toInt(SOCK_STREAM) hints.ai_protocol = toInt(IPPROTO_TCP) gaiNim(address, port, hints, aiList) # try all possibilities: var success = false - var lastError: TOSErrorCode + var lastError: OSErrorCode var it = aiList while it != nil: - if connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen) == 0'i32: + if connect(socket.fd, it.ai_addr, it.ai_addrlen.SockLen) == 0'i32: success = true break else: lastError = osLastError() it = it.ai_next freeaddrinfo(aiList) - if not success: osError(lastError) + if not success: raiseOSError(lastError) when defined(ssl): if socket.isSSL: @@ -803,16 +821,16 @@ proc connect*(socket: TSocket, address: string, port = TPort(0), let err = SSLGetError(socket.sslHandle, ret) case err of SSL_ERROR_ZERO_RETURN: - SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.") + raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.") of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: - SSLError("The operation did not complete. Perhaps you should use connectAsync?") + raiseSslError("The operation did not complete. Perhaps you should use connectAsync?") of SSL_ERROR_WANT_X509_LOOKUP: - SSLError("Function for x509 lookup has been called.") + raiseSslError("Function for x509 lookup has been called.") of SSL_ERROR_SYSCALL, SSL_ERROR_SSL: - SSLError() + raiseSslError() else: - SSLError("Unknown error") + raiseSslError("Unknown error") when false: var s: TSockAddrIn @@ -829,8 +847,8 @@ proc connect*(socket: TSocket, address: string, port = TPort(0), if connect(socket.fd, cast[ptr TSockAddr](addr(s)), sizeof(s).cint) < 0'i32: OSError() -proc connectAsync*(socket: TSocket, name: string, port = TPort(0), - af: TDomain = AF_INET) {.tags: [FReadIO].} = +proc connectAsync*(socket: Socket, name: string, port = Port(0), + af: Domain = AF_INET) {.tags: [ReadIOEffect].} = ## A variant of ``connect`` for non-blocking sockets. ## ## This procedure will immediatelly return, it will not block until a connection @@ -839,18 +857,18 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0), ## ## **Note**: For SSL sockets, the ``handshake`` procedure must be called ## whenever the socket successfully connects to a server. - var hints: Taddrinfo - var aiList: ptr Taddrinfo = nil + var hints: AddrInfo + var aiList: ptr AddrInfo = nil hints.ai_family = toInt(af) hints.ai_socktype = toInt(SOCK_STREAM) hints.ai_protocol = toInt(IPPROTO_TCP) gaiNim(name, port, hints, aiList) # try all possibilities: var success = false - var lastError: TOSErrorCode + var lastError: OSErrorCode var it = aiList while it != nil: - var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen) + var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.SockLen) if ret == 0'i32: success = true break @@ -869,13 +887,13 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0), it = it.ai_next freeaddrinfo(aiList) - if not success: osError(lastError) + if not success: raiseOSError(lastError) when defined(ssl): if socket.isSSL: socket.sslNoHandshake = true when defined(ssl): - proc handshake*(socket: TSocket): bool {.tags: [FReadIO, FWriteIO].} = + proc handshake*(socket: Socket): bool {.tags: [ReadIOEffect, WriteIOEffect].} = ## This proc needs to be called on a socket after it connects. This is ## only applicable when using ``connectAsync``. ## This proc performs the SSL handshake. @@ -891,21 +909,21 @@ when defined(ssl): var errret = SSLGetError(socket.sslHandle, ret) case errret of SSL_ERROR_ZERO_RETURN: - SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.") + raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.") of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE: return false of SSL_ERROR_WANT_X509_LOOKUP: - SSLError("Function for x509 lookup has been called.") + raiseSslError("Function for x509 lookup has been called.") of SSL_ERROR_SYSCALL, SSL_ERROR_SSL: - SSLError() + raiseSslError() else: - SSLError("Unknown Error") + raiseSslError("Unknown Error") socket.sslNoHandshake = false else: - SSLError("Socket is not an SSL socket.") + raiseSslError("Socket is not an SSL socket.") - proc gotHandshake*(socket: TSocket): bool = + proc gotHandshake*(socket: Socket): bool = ## Determines whether a handshake has occurred between a client (``socket``) ## and the server that ``socket`` is connected to. ## @@ -913,21 +931,21 @@ when defined(ssl): if socket.isSSL: return not socket.sslNoHandshake else: - SSLError("Socket is not an SSL socket.") + raiseSslError("Socket is not an SSL socket.") -proc timeValFromMilliseconds(timeout = 500): Ttimeval = +proc timeValFromMilliseconds(timeout = 500): Timeval = if timeout != -1: var seconds = timeout div 1000 result.tv_sec = seconds.int32 result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 -proc createFdSet(fd: var TFdSet, s: seq[TSocket], m: var int) = +proc createFdSet(fd: var TFdSet, s: seq[Socket], m: var int) = FD_ZERO(fd) for i in items(s): m = max(m, int(i.fd)) FD_SET(i.fd, fd) -proc pruneSocketSet(s: var seq[TSocket], fd: var TFdSet) = +proc pruneSocketSet(s: var seq[Socket], fd: var TFdSet) = var i = 0 var L = s.len while i < L: @@ -939,7 +957,7 @@ proc pruneSocketSet(s: var seq[TSocket], fd: var TFdSet) = inc(i) setLen(s, L) -proc hasDataBuffered*(s: TSocket): bool = +proc hasDataBuffered*(s: Socket): bool = ## Determines whether a socket has data buffered. result = false if s.isBuffered: @@ -949,10 +967,10 @@ proc hasDataBuffered*(s: TSocket): bool = if s.isSSL and not result: result = s.sslHasPeekChar -proc checkBuffer(readfds: var seq[TSocket]): int = +proc checkBuffer(readfds: var seq[Socket]): int = ## Checks the buffer of each socket in ``readfds`` to see whether there is data. ## Removes the sockets from ``readfds`` and returns the count of removed sockets. - var res: seq[TSocket] = @[] + var res: seq[Socket] = @[] result = 0 for s in readfds: if hasDataBuffered(s): @@ -961,8 +979,8 @@ proc checkBuffer(readfds: var seq[TSocket]): int = if result > 0: readfds = res -proc select*(readfds, writefds, exceptfds: var seq[TSocket], - timeout = 500): int {.tags: [FReadIO].} = +proc select*(readfds, writefds, exceptfds: var seq[Socket], + timeout = 500): int {.tags: [ReadIOEffect].} = ## Traditional select function. This function will return the number of ## sockets that are ready to be read from, written to, or which have errors. ## If there are none; 0 is returned. @@ -975,7 +993,7 @@ proc select*(readfds, writefds, exceptfds: var seq[TSocket], if buffersFilled > 0: return buffersFilled - var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout) + var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) var rd, wr, ex: TFdSet var m = 0 @@ -992,13 +1010,13 @@ proc select*(readfds, writefds, exceptfds: var seq[TSocket], pruneSocketSet(writefds, (wr)) pruneSocketSet(exceptfds, (ex)) -proc select*(readfds, writefds: var seq[TSocket], - timeout = 500): int {.tags: [FReadIO].} = +proc select*(readfds, writefds: var seq[Socket], + timeout = 500): int {.tags: [ReadIOEffect].} = ## Variant of select with only a read and write list. let buffersFilled = checkBuffer(readfds) if buffersFilled > 0: return buffersFilled - var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout) + var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) var rd, wr: TFdSet var m = 0 @@ -1013,8 +1031,8 @@ proc select*(readfds, writefds: var seq[TSocket], pruneSocketSet(readfds, (rd)) pruneSocketSet(writefds, (wr)) -proc selectWrite*(writefds: var seq[TSocket], - timeout = 500): int {.tags: [FReadIO].} = +proc selectWrite*(writefds: var seq[Socket], + timeout = 500): int {.tags: [ReadIOEffect].} = ## When a socket in ``writefds`` is ready to be written to then a non-zero ## value will be returned specifying the count of the sockets which can be ## written to. The sockets which **cannot** be written to will also be removed @@ -1022,7 +1040,7 @@ proc selectWrite*(writefds: var seq[TSocket], ## ## ``timeout`` is specified in miliseconds and ``-1`` can be specified for ## an unlimited time. - var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout) + var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) var wr: TFdSet var m = 0 @@ -1035,12 +1053,12 @@ proc selectWrite*(writefds: var seq[TSocket], pruneSocketSet(writefds, (wr)) -proc select*(readfds: var seq[TSocket], timeout = 500): int = +proc select*(readfds: var seq[Socket], timeout = 500): int = ## variant of select with a read list only let buffersFilled = checkBuffer(readfds) if buffersFilled > 0: return buffersFilled - var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout) + var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) var rd: TFdSet var m = 0 @@ -1053,7 +1071,7 @@ proc select*(readfds: var seq[TSocket], timeout = 500): int = pruneSocketSet(readfds, (rd)) -proc readIntoBuf(socket: TSocket, flags: int32): int = +proc readIntoBuf(socket: Socket, flags: int32): int = result = 0 when defined(ssl): if socket.isSSL: @@ -1077,7 +1095,7 @@ template retRead(flags, readBytes: int) {.dirty.} = else: return res -proc recv*(socket: TSocket, data: pointer, size: int): int {.tags: [FReadIO].} = +proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ReadIOEffect].} = ## Receives data from a socket. ## ## **Note**: This is a low-level function, you may be interested in the higher @@ -1117,8 +1135,8 @@ proc recv*(socket: TSocket, data: pointer, size: int): int {.tags: [FReadIO].} = else: result = recv(socket.fd, data, size.cint, 0'i32) -proc waitFor(socket: TSocket, waited: var float, timeout, size: int, - funcName: string): int {.tags: [FTime].} = +proc waitFor(socket: Socket, waited: var float, timeout, size: int, + funcName: string): int {.tags: [TimeEffect].} = ## determines the amount of characters that can be read. Result will never ## be larger than ``size``. For unbuffered sockets this will be ``1``. ## For buffered sockets it can be as big as ``BufferSize``. @@ -1133,7 +1151,7 @@ proc waitFor(socket: TSocket, waited: var float, timeout, size: int, result = min(result, size) else: if timeout - int(waited * 1000.0) < 1: - raise newException(ETimeout, "Call to '" & funcName & "' timed out.") + raise newException(TimeoutError, "Call to '" & funcName & "' timed out.") when defined(ssl): if socket.isSSL: @@ -1147,13 +1165,13 @@ proc waitFor(socket: TSocket, waited: var float, timeout, size: int, var s = @[socket] var startTime = epochTime() let selRet = select(s, timeout - int(waited * 1000.0)) - if selRet < 0: osError(osLastError()) + if selRet < 0: raiseOSError(osLastError()) if selRet != 1: - raise newException(ETimeout, "Call to '" & funcName & "' timed out.") + raise newException(TimeoutError, "Call to '" & funcName & "' timed out.") waited += (epochTime() - startTime) -proc recv*(socket: TSocket, data: pointer, size: int, timeout: int): int {. - tags: [FReadIO, FTime].} = +proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {. + tags: [ReadIOEffect, TimeEffect].} = ## overload with a ``timeout`` parameter in miliseconds. var waited = 0.0 # number of seconds already waited @@ -1169,7 +1187,7 @@ proc recv*(socket: TSocket, data: pointer, size: int, timeout: int): int {. result = read -proc recv*(socket: TSocket, data: var string, size: int, timeout = -1): int = +proc recv*(socket: Socket, data: var string, size: int, timeout = -1): int = ## Higher-level version of ``recv``. ## ## When 0 is returned the socket's connection has been closed. @@ -1185,10 +1203,10 @@ proc recv*(socket: TSocket, data: var string, size: int, timeout = -1): int = result = recv(socket, cstring(data), size, timeout) if result < 0: data.setLen(0) - socket.socketError(result) + socket.raiseSocketError(result) data.setLen(result) -proc recvAsync*(socket: TSocket, data: var string, size: int): int = +proc recvAsync*(socket: Socket, data: var string, size: int): int = ## Async version of ``recv``. ## ## When socket is non-blocking and no data is available on the socket, @@ -1199,11 +1217,11 @@ proc recvAsync*(socket: TSocket, data: var string, size: int): int = result = recv(socket, cstring(data), size) if result < 0: data.setLen(0) - socket.socketError(async = true) + socket.raiseSocketError(async = true) result = -1 data.setLen(result) -proc peekChar(socket: TSocket, c: var char): int {.tags: [FReadIO].} = +proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} = if socket.isBuffered: result = 1 if socket.bufLen == 0 or socket.currPos > socket.bufLen-1: @@ -1223,8 +1241,8 @@ proc peekChar(socket: TSocket, c: var char): int {.tags: [FReadIO].} = return result = recv(socket.fd, addr(c), 1, MSG_PEEK) -proc recvLine*(socket: TSocket, line: var TaintedString, timeout = -1): bool {. - tags: [FReadIO, FTime], deprecated.} = +proc recvLine*(socket: Socket, line: var TaintedString, timeout = -1): bool {. + tags: [ReadIOEffect, TimeEffect], deprecated.} = ## Receive a line of data from ``socket``. ## ## If a full line is received ``\r\L`` is not @@ -1270,8 +1288,8 @@ proc recvLine*(socket: TSocket, line: var TaintedString, timeout = -1): bool {. return true add(line.string, c) -proc readLine*(socket: TSocket, line: var TaintedString, timeout = -1) {. - tags: [FReadIO, FTime].} = +proc readLine*(socket: Socket, line: var TaintedString, timeout = -1) {. + tags: [ReadIOEffect, TimeEffect].} = ## Reads a line of data from ``socket``. ## ## If a full line is read ``\r\L`` is not @@ -1296,14 +1314,14 @@ proc readLine*(socket: TSocket, line: var TaintedString, timeout = -1) {. var c: char discard waitFor(socket, waited, timeout, 1, "readLine") var n = recv(socket, addr(c), 1) - if n < 0: socket.socketError() + if n < 0: socket.raiseSocketError() elif n == 0: return if c == '\r': discard waitFor(socket, waited, timeout, 1, "readLine") n = peekChar(socket, c) if n > 0 and c == '\L': discard recv(socket, addr(c), 1) - elif n <= 0: socket.socketError() + elif n <= 0: socket.raiseSocketError() addNLIfEmpty() return elif c == '\L': @@ -1311,8 +1329,8 @@ proc readLine*(socket: TSocket, line: var TaintedString, timeout = -1) {. return add(line.string, c) -proc recvLineAsync*(socket: TSocket, - line: var TaintedString): TRecvLineResult {.tags: [FReadIO], deprecated.} = +proc recvLineAsync*(socket: Socket, + line: var TaintedString): RecvLineResult {.tags: [ReadIOEffect], deprecated.} = ## Similar to ``recvLine`` but designed for non-blocking sockets. ## ## The values of the returned enum should be pretty self explanatory: @@ -1343,8 +1361,8 @@ proc recvLineAsync*(socket: TSocket, elif c == '\L': return RecvFullLine add(line.string, c) -proc readLineAsync*(socket: TSocket, - line: var TaintedString): TReadLineResult {.tags: [FReadIO].} = +proc readLineAsync*(socket: Socket, + line: var TaintedString): ReadLineResult {.tags: [ReadIOEffect].} = ## Similar to ``recvLine`` but designed for non-blocking sockets. ## ## The values of the returned enum should be pretty self explanatory: @@ -1357,7 +1375,7 @@ proc readLineAsync*(socket: TSocket, setLen(line.string, 0) template errorOrNone = - socket.socketError(async = true) + socket.raiseSocketError(async = true) return ReadNone while true: @@ -1378,7 +1396,7 @@ proc readLineAsync*(socket: TSocket, elif c == '\L': return ReadFullLine add(line.string, c) -proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO], deprecated.} = +proc recv*(socket: Socket): TaintedString {.tags: [ReadIOEffect], deprecated.} = ## receives all the available data from the socket. ## Socket errors will result in an ``EOS`` error. ## If socket is not a connectionless socket and socket is not connected @@ -1390,7 +1408,7 @@ proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO], deprecated.} = var pos = 0 while true: var bytesRead = recv(socket, addr(string(result)[pos]), bufSize-1) - if bytesRead == -1: osError(osLastError()) + if bytesRead == -1: raiseOSError(osLastError()) setLen(result.string, pos + bytesRead) if bytesRead != bufSize-1: break # increase capacity: @@ -1402,7 +1420,7 @@ proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO], deprecated.} = while true: var bytesRead = recv(socket, cstring(buf), bufSize-1) # Error - if bytesRead == -1: OSError(OSLastError()) + if bytesRead == -1: OSError(osLastError()) buf[bytesRead] = '\0' # might not be necessary setLen(buf, bytesRead) @@ -1410,8 +1428,8 @@ proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO], deprecated.} = if bytesRead != bufSize-1: break {.push warning[deprecated]: off.} -proc recvTimeout*(socket: TSocket, timeout: int): TaintedString {. - tags: [FReadIO], deprecated.} = +proc recvTimeout*(socket: Socket, timeout: int): TaintedString {. + tags: [ReadIOEffect], deprecated.} = ## overloaded variant to support a ``timeout`` parameter, the ``timeout`` ## parameter specifies the amount of miliseconds to wait for data on the ## socket. @@ -1420,13 +1438,13 @@ proc recvTimeout*(socket: TSocket, timeout: int): TaintedString {. if socket.bufLen == 0: var s = @[socket] if s.select(timeout) != 1: - raise newException(ETimeout, "Call to recv() timed out.") + raise newException(TimeoutError, "Call to recv() timed out.") return socket.recv {.pop.} -proc recvAsync*(socket: TSocket, s: var TaintedString): bool {. - tags: [FReadIO], deprecated.} = +proc recvAsync*(socket: Socket, s: var TaintedString): bool {. + tags: [ReadIOEffect], deprecated.} = ## receives all the data from a non-blocking socket. If socket is non-blocking ## and there are no messages available, `False` will be returned. ## Other socket errors will result in an ``EOS`` error. @@ -1447,27 +1465,27 @@ proc recvAsync*(socket: TSocket, s: var TaintedString): bool {. var ret = SSLGetError(socket.sslHandle, bytesRead.cint) case ret of SSL_ERROR_ZERO_RETURN: - SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.") + raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.") of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: - SSLError("Unexpected error occured.") # This should just not happen. + raiseSslError("Unexpected error occured.") # This should just not happen. of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ: return false of SSL_ERROR_WANT_X509_LOOKUP: - SSLError("Function for x509 lookup has been called.") + raiseSslError("Function for x509 lookup has been called.") of SSL_ERROR_SYSCALL, SSL_ERROR_SSL: - SSLError() - else: SSLError("Unknown Error") + raiseSslError() + else: raiseSslError("Unknown Error") if bytesRead == -1 and not (when defined(ssl): socket.isSSL else: false): let err = osLastError() when defined(windows): if err.int32 == WSAEWOULDBLOCK: return false - else: osError(err) + else: raiseOSError(err) else: if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK: return false - else: osError(err) + else: raiseOSError(err) setLen(s.string, pos + bytesRead) if bytesRead != bufSize-1: break @@ -1476,9 +1494,9 @@ proc recvAsync*(socket: TSocket, s: var TaintedString): bool {. inc(pos, bytesRead) result = true -proc recvFrom*(socket: TSocket, data: var string, length: int, - address: var string, port: var TPort, flags = 0'i32): int {. - tags: [FReadIO].} = +proc recvFrom*(socket: Socket, data: var string, length: int, + address: var string, port: var Port, flags = 0'i32): int {. + tags: [ReadIOEffect].} = ## Receives data from ``socket``. This function should normally be used with ## connection-less sockets (UDP sockets). ## @@ -1492,19 +1510,19 @@ proc recvFrom*(socket: TSocket, data: var string, length: int, # TODO: Buffered sockets data.setLen(length) - var sockAddress: Tsockaddr_in - var addrLen = sizeof(sockAddress).TSocklen + var sockAddress: Sockaddr_in + var addrLen = sizeof(sockAddress).SockLen result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint, - cast[ptr TSockAddr](addr(sockAddress)), addr(addrLen)) + cast[ptr SockAddr](addr(sockAddress)), addr(addrLen)) if result != -1: data.setLen(result) address = $inet_ntoa(sockAddress.sin_addr) - port = ntohs(sockAddress.sin_port).TPort + port = ntohs(sockAddress.sin_port).Port -proc recvFromAsync*(socket: TSocket, data: var string, length: int, - address: var string, port: var TPort, - flags = 0'i32): bool {.tags: [FReadIO].} = +proc recvFromAsync*(socket: Socket, data: var string, length: int, + address: var string, port: var Port, + flags = 0'i32): bool {.tags: [ReadIOEffect].} = ## Variant of ``recvFrom`` for non-blocking sockets. Unlike ``recvFrom``, ## this function will raise an EOS error whenever a socket error occurs. ## @@ -1516,13 +1534,13 @@ proc recvFromAsync*(socket: TSocket, data: var string, length: int, when defined(windows): if err.int32 == WSAEWOULDBLOCK: return false - else: osError(err) + else: raiseOSError(err) else: if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK: return false - else: osError(err) + else: raiseOSError(err) -proc skip*(socket: TSocket) {.tags: [FReadIO], deprecated.} = +proc skip*(socket: Socket) {.tags: [ReadIOEffect], deprecated.} = ## skips all the data that is pending for the socket ## ## **Deprecated since version 0.9.2**: This function is not safe for use. @@ -1531,7 +1549,7 @@ proc skip*(socket: TSocket) {.tags: [FReadIO], deprecated.} = while recv(socket, buf, bufSize) == bufSize: discard dealloc(buf) -proc skip*(socket: TSocket, size: int, timeout = -1) = +proc skip*(socket: Socket, size: int, timeout = -1) = ## Skips ``size`` amount of bytes. ## ## An optional timeout can be specified in miliseconds, if skipping the @@ -1546,8 +1564,8 @@ proc skip*(socket: TSocket, size: int, timeout = -1) = bytesSkipped += recv(socket, dummy, avail) dealloc(dummy) -proc send*(socket: TSocket, data: pointer, size: int): int {. - tags: [FWriteIO].} = +proc send*(socket: Socket, data: pointer, size: int): int {. + tags: [WriteIOEffect].} = ## sends data to a socket. when defined(ssl): if socket.isSSL: @@ -1560,22 +1578,22 @@ proc send*(socket: TSocket, data: pointer, size: int): int {. const MSG_NOSIGNAL = 0 result = send(socket.fd, data, size, int32(MSG_NOSIGNAL)) -proc send*(socket: TSocket, data: string) {.tags: [FWriteIO].} = +proc send*(socket: Socket, data: string) {.tags: [WriteIOEffect].} = ## sends data to a socket. if socket.nonblocking: - raise newException(EInvalidValue, "This function cannot be used on non-blocking sockets.") + raise newException(ValueError, "This function cannot be used on non-blocking sockets.") let sent = send(socket, cstring(data), data.len) if sent < 0: when defined(ssl): if socket.isSSL: - SSLError() + raiseSslError() - osError(osLastError()) + raiseOSError(osLastError()) if sent != data.len: - raise newException(EOS, "Could not send all data.") + raise newException(OSError, "Could not send all data.") -proc sendAsync*(socket: TSocket, data: string): int {.tags: [FWriteIO].} = +proc sendAsync*(socket: Socket, data: string): int {.tags: [WriteIOEffect].} = ## sends data to a non-blocking socket. ## Returns ``0`` if no data could be sent, if data has been sent ## returns the amount of bytes of ``data`` that was successfully sent. This @@ -1590,16 +1608,16 @@ proc sendAsync*(socket: TSocket, data: string): int {.tags: [FWriteIO].} = let ret = SSLGetError(socket.sslHandle, result.cint) case ret of SSL_ERROR_ZERO_RETURN: - SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.") + raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.") of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: - SSLError("Unexpected error occured.") # This should just not happen. + raiseSslError("Unexpected error occured.") # This should just not happen. of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ: return 0 of SSL_ERROR_WANT_X509_LOOKUP: - SSLError("Function for x509 lookup has been called.") + raiseSslError("Function for x509 lookup has been called.") of SSL_ERROR_SYSCALL, SSL_ERROR_SSL: - SSLError() - else: SSLError("Unknown Error") + raiseSslError() + else: raiseSslError("Unknown Error") else: return if result == -1: @@ -1607,28 +1625,28 @@ proc sendAsync*(socket: TSocket, data: string): int {.tags: [FWriteIO].} = when defined(windows): if err.int32 == WSAEINPROGRESS: return 0 - else: osError(err) + else: raiseOSError(err) else: if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK: return 0 - else: osError(err) + else: raiseOSError(err) -proc trySend*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} = +proc trySend*(socket: Socket, data: string): bool {.tags: [WriteIOEffect].} = ## safe alternative to ``send``. Does not raise an EOS when an error occurs, ## and instead returns ``false`` on failure. result = send(socket, cstring(data), data.len) == data.len -proc sendTo*(socket: TSocket, address: string, port: TPort, data: pointer, - size: int, af: TDomain = AF_INET, flags = 0'i32): int {. - tags: [FWriteIO].} = +proc sendTo*(socket: Socket, address: string, port: Port, data: pointer, + size: int, af: Domain = AF_INET, flags = 0'i32): int {. + tags: [WriteIOEffect].} = ## low-level sendTo proc. This proc sends ``data`` to the specified ``address``, ## which may be an IP address or a hostname, if a hostname is specified ## this function will try each IP of that hostname. ## ## **Note:** This proc is not available for SSL sockets. - var hints: Taddrinfo - var aiList: ptr Taddrinfo = nil + var hints: AddrInfo + var aiList: ptr AddrInfo = nil hints.ai_family = toInt(af) hints.ai_socktype = toInt(SOCK_STREAM) hints.ai_protocol = toInt(IPPROTO_TCP) @@ -1639,7 +1657,7 @@ proc sendTo*(socket: TSocket, address: string, port: TPort, data: pointer, var it = aiList while it != nil: result = sendto(socket.fd, data, size.cint, flags.cint, it.ai_addr, - it.ai_addrlen.TSocklen) + it.ai_addrlen.SockLen) if result != -1'i32: success = true break @@ -1647,8 +1665,8 @@ proc sendTo*(socket: TSocket, address: string, port: TPort, data: pointer, freeaddrinfo(aiList) -proc sendTo*(socket: TSocket, address: string, port: TPort, - data: string): int {.tags: [FWriteIO].} = +proc sendTo*(socket: Socket, address: string, port: Port, + data: string): int {.tags: [WriteIOEffect].} = ## Friendlier version of the low-level ``sendTo``. result = socket.sendTo(address, port, cstring(data), data.len) @@ -1659,33 +1677,33 @@ when defined(Windows): FIONBIO = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or (102 shl 8) or 126 - proc ioctlsocket(s: TSocketHandle, cmd: clong, + proc ioctlsocket(s: SocketHandle, cmd: clong, argptr: ptr clong): cint {. stdcall, importc:"ioctlsocket", dynlib: "ws2_32.dll".} -proc setBlocking(s: TSocket, blocking: bool) = +proc setBlocking(s: Socket, blocking: bool) = when defined(Windows): var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking if ioctlsocket(s.fd, FIONBIO, addr(mode)) == -1: - osError(osLastError()) + raiseOSError(osLastError()) else: # BSD sockets var x: int = fcntl(s.fd, F_GETFL, 0) if x == -1: - osError(osLastError()) + raiseOSError(osLastError()) else: var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK if fcntl(s.fd, F_SETFL, mode) == -1: - osError(osLastError()) + raiseOSError(osLastError()) s.nonblocking = not blocking -discard """ proc setReuseAddr*(s: TSocket) = +discard """ proc setReuseAddr*(s: Socket) = var blah: int = 1 var mode = SO_REUSEADDR if setsockopt(s.fd, SOL_SOCKET, mode, addr blah, TSOcklen(sizeof(int))) == -1: - OSError(OSLastError()) """ + raiseOSError(osLastError()) """ -proc connect*(socket: TSocket, address: string, port = TPort(0), timeout: int, - af: TDomain = AF_INET) {.tags: [FReadIO, FWriteIO].} = +proc connect*(socket: Socket, address: string, port = Port(0), timeout: int, + af: Domain = AF_INET) {.tags: [ReadIOEffect, WriteIOEffect].} = ## Connects to server as specified by ``address`` on port specified by ``port``. ## ## The ``timeout`` paremeter specifies the time in miliseconds to allow for @@ -1694,9 +1712,9 @@ proc connect*(socket: TSocket, address: string, port = TPort(0), timeout: int, socket.setBlocking(false) socket.connectAsync(address, port, af) - var s: seq[TSocket] = @[socket] + var s: seq[Socket] = @[socket] if selectWrite(s, timeout) != 1: - raise newException(ETimeout, "Call to 'connect' timed out.") + raise newException(TimeoutError, "Call to 'connect' timed out.") else: when defined(ssl): if socket.isSSL: @@ -1704,17 +1722,17 @@ proc connect*(socket: TSocket, address: string, port = TPort(0), timeout: int, doAssert socket.handshake() socket.setBlocking(originalStatus) -proc isSSL*(socket: TSocket): bool = return socket.isSSL +proc isSSL*(socket: Socket): bool = return socket.isSSL ## Determines whether ``socket`` is a SSL socket. -proc getFD*(socket: TSocket): TSocketHandle = return socket.fd +proc getFD*(socket: Socket): SocketHandle = return socket.fd ## Returns the socket's file descriptor -proc isBlocking*(socket: TSocket): bool = not socket.nonblocking +proc isBlocking*(socket: Socket): bool = not socket.nonblocking ## Determines whether ``socket`` is blocking. when defined(Windows): - var wsa: TWSAData - if wsaStartup(0x0101'i16, addr wsa) != 0: osError(osLastError()) + var wsa: WSAData + if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError()) diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 63622a26c..31aa7497d 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -8,158 +8,160 @@ # ## This module provides a stream interface and two implementations thereof: -## the `PFileStream` and the `PStringStream` which implement the stream -## interface for Nimrod file objects (`TFile`) and strings. Other modules +## the `FileStream` and the `StringStream` which implement the stream +## interface for Nim file objects (`File`) and strings. Other modules ## may provide other implementations for this standard stream interface. include "system/inclrtl" -proc newEIO(msg: string): ref EIO = +proc newEIO(msg: string): ref IOError = new(result) result.msg = msg type - PStream* = ref TStream - TStream* = object of TObject ## Stream interface that supports - ## writing or reading. Note that these fields - ## here shouldn't be used directly. They are - ## accessible so that a stream implementation - ## can override them. - closeImpl*: proc (s: PStream) {.nimcall, tags: [], gcsafe.} - atEndImpl*: proc (s: PStream): bool {.nimcall, tags: [], gcsafe.} - setPositionImpl*: proc (s: PStream, pos: int) {.nimcall, tags: [], gcsafe.} - getPositionImpl*: proc (s: PStream): int {.nimcall, tags: [], gcsafe.} - readDataImpl*: proc (s: PStream, buffer: pointer, - bufLen: int): int {.nimcall, tags: [FReadIO], gcsafe.} - writeDataImpl*: proc (s: PStream, buffer: pointer, bufLen: int) {.nimcall, - tags: [FWriteIO], gcsafe.} - flushImpl*: proc (s: PStream) {.nimcall, tags: [FWriteIO], gcsafe.} + Stream* = ref StreamObj + StreamObj* = object of RootObj ## Stream interface that supports + ## writing or reading. Note that these fields + ## here shouldn't be used directly. They are + ## accessible so that a stream implementation + ## can override them. + closeImpl*: proc (s: Stream) {.nimcall, tags: [], gcsafe.} + atEndImpl*: proc (s: Stream): bool {.nimcall, tags: [], gcsafe.} + setPositionImpl*: proc (s: Stream, pos: int) {.nimcall, tags: [], gcsafe.} + getPositionImpl*: proc (s: Stream): int {.nimcall, tags: [], gcsafe.} + readDataImpl*: proc (s: Stream, buffer: pointer, + bufLen: int): int {.nimcall, tags: [ReadIOEffect], gcsafe.} + writeDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int) {.nimcall, + tags: [WriteIOEffect], gcsafe.} + flushImpl*: proc (s: Stream) {.nimcall, tags: [WriteIOEffect], gcsafe.} -proc flush*(s: PStream) = +{.deprecated: [PStream: Stream, TStream: StreamObj].} + +proc flush*(s: Stream) = ## flushes the buffers that the stream `s` might use. if not isNil(s.flushImpl): s.flushImpl(s) -proc close*(s: PStream) = +proc close*(s: Stream) = ## closes the stream `s`. if not isNil(s.closeImpl): s.closeImpl(s) -proc close*(s, unused: PStream) {.deprecated.} = +proc close*(s, unused: Stream) {.deprecated.} = ## closes the stream `s`. s.closeImpl(s) -proc atEnd*(s: PStream): bool = +proc atEnd*(s: Stream): bool = ## checks if more data can be read from `f`. Returns true if all data has ## been read. result = s.atEndImpl(s) -proc atEnd*(s, unused: PStream): bool {.deprecated.} = +proc atEnd*(s, unused: Stream): bool {.deprecated.} = ## checks if more data can be read from `f`. Returns true if all data has ## been read. result = s.atEndImpl(s) -proc setPosition*(s: PStream, pos: int) = +proc setPosition*(s: Stream, pos: int) = ## sets the position `pos` of the stream `s`. s.setPositionImpl(s, pos) -proc setPosition*(s, unused: PStream, pos: int) {.deprecated.} = +proc setPosition*(s, unused: Stream, pos: int) {.deprecated.} = ## sets the position `pos` of the stream `s`. s.setPositionImpl(s, pos) -proc getPosition*(s: PStream): int = +proc getPosition*(s: Stream): int = ## retrieves the current position in the stream `s`. result = s.getPositionImpl(s) -proc getPosition*(s, unused: PStream): int {.deprecated.} = +proc getPosition*(s, unused: Stream): int {.deprecated.} = ## retrieves the current position in the stream `s`. result = s.getPositionImpl(s) -proc readData*(s: PStream, buffer: pointer, bufLen: int): int = +proc readData*(s: Stream, buffer: pointer, bufLen: int): int = ## low level proc that reads data into an untyped `buffer` of `bufLen` size. result = s.readDataImpl(s, buffer, bufLen) -proc readData*(s, unused: PStream, buffer: pointer, +proc readData*(s, unused: Stream, buffer: pointer, bufLen: int): int {.deprecated.} = ## low level proc that reads data into an untyped `buffer` of `bufLen` size. result = s.readDataImpl(s, buffer, bufLen) -proc writeData*(s: PStream, buffer: pointer, bufLen: int) = +proc writeData*(s: Stream, buffer: pointer, bufLen: int) = ## low level proc that writes an untyped `buffer` of `bufLen` size ## to the stream `s`. s.writeDataImpl(s, buffer, bufLen) -proc writeData*(s, unused: PStream, buffer: pointer, +proc writeData*(s, unused: Stream, buffer: pointer, bufLen: int) {.deprecated.} = ## low level proc that writes an untyped `buffer` of `bufLen` size ## to the stream `s`. s.writeDataImpl(s, buffer, bufLen) -proc write*[T](s: PStream, x: T) = +proc write*[T](s: Stream, x: T) = ## generic write procedure. Writes `x` to the stream `s`. Implementation: ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## ## s.writeData(s, addr(x), sizeof(x)) var y: T shallowCopy(y, x) writeData(s, addr(y), sizeof(y)) -proc write*(s: PStream, x: string) = +proc write*(s: Stream, x: string) = ## writes the string `x` to the the stream `s`. No length field or ## terminating zero is written. writeData(s, cstring(x), x.len) -proc writeln*(s: PStream, args: varargs[string, `$`]) = +proc writeln*(s: Stream, args: varargs[string, `$`]) = ## writes one or more strings to the the stream `s` followed ## by a new line. No length field or terminating zero is written. for str in args: write(s, str) write(s, "\n") -proc read[T](s: PStream, result: var T) = +proc read[T](s: Stream, result: var T) = ## generic read procedure. Reads `result` from the stream `s`. if readData(s, addr(result), sizeof(T)) != sizeof(T): raise newEIO("cannot read from stream") -proc readChar*(s: PStream): char = +proc readChar*(s: Stream): char = ## reads a char from the stream `s`. Raises `EIO` if an error occured. ## Returns '\0' as an EOF marker. if readData(s, addr(result), sizeof(result)) != 1: result = '\0' -proc readBool*(s: PStream): bool = +proc readBool*(s: Stream): bool = ## reads a bool from the stream `s`. Raises `EIO` if an error occured. read(s, result) -proc readInt8*(s: PStream): int8 = +proc readInt8*(s: Stream): int8 = ## reads an int8 from the stream `s`. Raises `EIO` if an error occured. read(s, result) -proc readInt16*(s: PStream): int16 = +proc readInt16*(s: Stream): int16 = ## reads an int16 from the stream `s`. Raises `EIO` if an error occured. read(s, result) -proc readInt32*(s: PStream): int32 = +proc readInt32*(s: Stream): int32 = ## reads an int32 from the stream `s`. Raises `EIO` if an error occured. read(s, result) -proc readInt64*(s: PStream): int64 = +proc readInt64*(s: Stream): int64 = ## reads an int64 from the stream `s`. Raises `EIO` if an error occured. read(s, result) -proc readFloat32*(s: PStream): float32 = +proc readFloat32*(s: Stream): float32 = ## reads a float32 from the stream `s`. Raises `EIO` if an error occured. read(s, result) -proc readFloat64*(s: PStream): float64 = +proc readFloat64*(s: Stream): float64 = ## reads a float64 from the stream `s`. Raises `EIO` if an error occured. read(s, result) -proc readStr*(s: PStream, length: int): TaintedString = +proc readStr*(s: Stream, length: int): TaintedString = ## reads a string of length `length` from the stream `s`. Raises `EIO` if ## an error occured. result = newString(length).TaintedString var L = readData(s, addr(string(result)[0]), length) if L != length: setLen(result.string, L) -proc readLine*(s: PStream, line: var TaintedString): bool = +proc readLine*(s: Stream, line: var TaintedString): bool = ## reads a line of text from the stream `s` into `line`. `line` must not be ## ``nil``! May throw an IO exception. ## A line of text may be delimited by ``CR``, ``LF`` or @@ -179,7 +181,7 @@ proc readLine*(s: PStream, line: var TaintedString): bool = line.string.add(c) result = true -proc readLine*(s: PStream): TaintedString = +proc readLine*(s: Stream): TaintedString = ## Reads a line from a stream `s`. Note: This is not very efficient. Raises ## `EIO` if an error occured. result = TaintedString"" @@ -194,42 +196,44 @@ proc readLine*(s: PStream): TaintedString = result.string.add(c) type - PStringStream* = ref TStringStream ## a stream that encapsulates a string - TStringStream* = object of TStream + StringStream* = ref StringStreamObj ## a stream that encapsulates a string + StringStreamObj* = object of StreamObj data*: string pos: int - -proc ssAtEnd(s: PStream): bool = - var s = PStringStream(s) + +{.deprecated: [PStringStream: StringStream, TStringStream: StringStreamObj].} + +proc ssAtEnd(s: Stream): bool = + var s = StringStream(s) return s.pos >= s.data.len -proc ssSetPosition(s: PStream, pos: int) = - var s = PStringStream(s) +proc ssSetPosition(s: Stream, pos: int) = + var s = StringStream(s) s.pos = clamp(pos, 0, s.data.high) -proc ssGetPosition(s: PStream): int = - var s = PStringStream(s) +proc ssGetPosition(s: Stream): int = + var s = StringStream(s) return s.pos -proc ssReadData(s: PStream, buffer: pointer, bufLen: int): int = - var s = PStringStream(s) +proc ssReadData(s: Stream, buffer: pointer, bufLen: int): int = + var s = StringStream(s) result = min(bufLen, s.data.len - s.pos) if result > 0: copyMem(buffer, addr(s.data[s.pos]), result) inc(s.pos, result) -proc ssWriteData(s: PStream, buffer: pointer, bufLen: int) = - var s = PStringStream(s) +proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) = + var s = StringStream(s) if bufLen > 0: setLen(s.data, s.data.len + bufLen) copyMem(addr(s.data[s.pos]), buffer, bufLen) inc(s.pos, bufLen) -proc ssClose(s: PStream) = - var s = PStringStream(s) +proc ssClose(s: Stream) = + var s = StringStream(s) s.data = nil -proc newStringStream*(s: string = ""): PStringStream = +proc newStringStream*(s: string = ""): StringStream = ## creates a new stream from the string `s`. new(result) result.data = s @@ -244,27 +248,28 @@ proc newStringStream*(s: string = ""): PStringStream = when not defined(js): type - PFileStream* = ref TFileStream ## a stream that encapsulates a `TFile` - TFileStream* = object of TStream - f: TFile + FileStream* = ref FileStreamObj ## a stream that encapsulates a `TFile` + FileStreamObj* = object of Stream + f: File + {.deprecated: [PFileStream: FileStream, TFileStream: FileStreamObj].} - proc fsClose(s: PStream) = - if PFileStream(s).f != nil: - close(PFileStream(s).f) - PFileStream(s).f = nil - proc fsFlush(s: PStream) = flushFile(PFileStream(s).f) - proc fsAtEnd(s: PStream): bool = return endOfFile(PFileStream(s).f) - proc fsSetPosition(s: PStream, pos: int) = setFilePos(PFileStream(s).f, pos) - proc fsGetPosition(s: PStream): int = return int(getFilePos(PFileStream(s).f)) + proc fsClose(s: Stream) = + if FileStream(s).f != nil: + close(FileStream(s).f) + FileStream(s).f = nil + proc fsFlush(s: Stream) = flushFile(FileStream(s).f) + proc fsAtEnd(s: Stream): bool = return endOfFile(FileStream(s).f) + proc fsSetPosition(s: Stream, pos: int) = setFilePos(FileStream(s).f, pos) + proc fsGetPosition(s: Stream): int = return int(getFilePos(FileStream(s).f)) - proc fsReadData(s: PStream, buffer: pointer, bufLen: int): int = - result = readBuffer(PFileStream(s).f, buffer, bufLen) + proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int = + result = readBuffer(FileStream(s).f, buffer, bufLen) - proc fsWriteData(s: PStream, buffer: pointer, bufLen: int) = - if writeBuffer(PFileStream(s).f, buffer, bufLen) != bufLen: + proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) = + if writeBuffer(FileStream(s).f, buffer, bufLen) != bufLen: raise newEIO("cannot write to stream") - proc newFileStream*(f: TFile): PFileStream = + proc newFileStream*(f: File): FileStream = ## creates a new stream from the file `f`. new(result) result.f = f @@ -276,11 +281,11 @@ when not defined(js): result.writeDataImpl = fsWriteData result.flushImpl = fsFlush - proc newFileStream*(filename: string, mode: TFileMode): PFileStream = + proc newFileStream*(filename: string, mode: FileMode): FileStream = ## creates a new stream from the file named `filename` with the mode `mode`. ## If the file cannot be opened, nil is returned. See the `system ## `_ module for a list of available TFileMode enums. - var f: TFile + var f: File if open(f, filename, mode): result = newFileStream(f) @@ -288,45 +293,47 @@ when true: discard else: type - TFileHandle* = cint ## Operating system file handle - PFileHandleStream* = ref TFileHandleStream - TFileHandleStream* = object of TStream - handle*: TFileHandle + FileHandleStream* = ref FileHandleStreamObj + FileHandleStreamObj* = object of Stream + handle*: FileHandle pos: int - proc newEOS(msg: string): ref EOS = + {.deprecated: [PFileHandleStream: FileHandleStream, + TFileHandleStream: FileHandleStreamObj].} + + proc newEOS(msg: string): ref OSError = new(result) result.msg = msg - proc hsGetPosition(s: PFileHandleStream): int = + proc hsGetPosition(s: FileHandleStream): int = return s.pos when defined(windows): # do not import windows as this increases compile times: - nil + discard else: import posix - proc hsSetPosition(s: PFileHandleStream, pos: int) = + proc hsSetPosition(s: FileHandleStream, pos: int) = discard lseek(s.handle, pos, SEEK_SET) - proc hsClose(s: PFileHandleStream) = discard close(s.handle) - proc hsAtEnd(s: PFileHandleStream): bool = + proc hsClose(s: FileHandleStream) = discard close(s.handle) + proc hsAtEnd(s: FileHandleStream): bool = var pos = hsGetPosition(s) var theEnd = lseek(s.handle, 0, SEEK_END) result = pos >= theEnd hsSetPosition(s, pos) # set position back - proc hsReadData(s: PFileHandleStream, buffer: pointer, bufLen: int): int = + proc hsReadData(s: FileHandleStream, buffer: pointer, bufLen: int): int = result = posix.read(s.handle, buffer, bufLen) inc(s.pos, result) - proc hsWriteData(s: PFileHandleStream, buffer: pointer, bufLen: int) = + proc hsWriteData(s: FileHandleStream, buffer: pointer, bufLen: int) = if posix.write(s.handle, buffer, bufLen) != bufLen: raise newEIO("cannot write to stream") inc(s.pos, bufLen) - proc newFileHandleStream*(handle: TFileHandle): PFileHandleStream = + proc newFileHandleStream*(handle: FileHandle): FileHandleStream = new(result) result.handle = handle result.pos = 0 @@ -338,9 +345,9 @@ else: result.writeData = hsWriteData proc newFileHandleStream*(filename: string, - mode: TFileMode): PFileHandleStream = - when defined(windows): - nil + mode: FileMode): FileHandleStream = + when defined(windows): + discard else: var flags: cint case mode diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 7003acfcf..5b7149d8e 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -18,43 +18,46 @@ import include "system/inclrtl" type - TStringTableMode* = enum ## describes the tables operation mode + StringTableMode* = enum ## describes the tables operation mode modeCaseSensitive, ## the table is case sensitive modeCaseInsensitive, ## the table is case insensitive modeStyleInsensitive ## the table is style insensitive - TKeyValuePair = tuple[key, val: string] - TKeyValuePairSeq = seq[TKeyValuePair] - TStringTable* = object of TObject + KeyValuePair = tuple[key, val: string] + KeyValuePairSeq = seq[KeyValuePair] + StringTableObj* = object of RootObj counter: int - data: TKeyValuePairSeq - mode: TStringTableMode + data: KeyValuePairSeq + mode: StringTableMode - PStringTable* = ref TStringTable ## use this type to declare string tables + StringTableRef* = ref StringTableObj ## use this type to declare string tables -proc len*(t: PStringTable): int {.rtl, extern: "nst$1".} = +{.deprecated: [TStringTableMode: StringTableMode, + TStringTable: StringTableObj, PStringTable: StringTableRef].} + +proc len*(t: StringTableRef): int {.rtl, extern: "nst$1".} = ## returns the number of keys in `t`. result = t.counter -iterator pairs*(t: PStringTable): tuple[key, value: string] = +iterator pairs*(t: StringTableRef): tuple[key, value: string] = ## iterates over every (key, value) pair in the table `t`. for h in 0..high(t.data): if not isNil(t.data[h].key): yield (t.data[h].key, t.data[h].val) -iterator keys*(t: PStringTable): string = +iterator keys*(t: StringTableRef): string = ## iterates over every key in the table `t`. for h in 0..high(t.data): if not isNil(t.data[h].key): yield t.data[h].key -iterator values*(t: PStringTable): string = +iterator values*(t: StringTableRef): string = ## iterates over every value in the table `t`. for h in 0..high(t.data): if not isNil(t.data[h].key): yield t.data[h].val type - TFormatFlag* = enum ## flags for the `%` operator + FormatFlag* = enum ## flags for the `%` operator useEnvironment, ## use environment variable if the ``$key`` ## is not found in the table useEmpty, ## use the empty string as a default, thus it @@ -63,19 +66,21 @@ type useKey ## do not replace ``$key`` if it is not found ## in the table (or in the environment) +{.deprecated: [TFormatFlag: FormatFlag].} + # implementation const growthFactor = 2 startSize = 64 -proc myhash(t: PStringTable, key: string): THash = +proc myhash(t: StringTableRef, key: string): THash = case t.mode of modeCaseSensitive: result = hashes.hash(key) of modeCaseInsensitive: result = hashes.hashIgnoreCase(key) of modeStyleInsensitive: result = hashes.hashIgnoreStyle(key) -proc myCmp(t: PStringTable, a, b: string): bool = +proc myCmp(t: StringTableRef, a, b: string): bool = case t.mode of modeCaseSensitive: result = cmp(a, b) == 0 of modeCaseInsensitive: result = cmpIgnoreCase(a, b) == 0 @@ -88,7 +93,7 @@ proc mustRehash(length, counter: int): bool = proc nextTry(h, maxHash: THash): THash {.inline.} = result = ((5 * h) + 1) and maxHash -proc rawGet(t: PStringTable, key: string): int = +proc rawGet(t: StringTableRef, key: string): int = var h: THash = 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): @@ -96,7 +101,7 @@ proc rawGet(t: PStringTable, key: string): int = h = nextTry(h, high(t.data)) result = - 1 -proc `[]`*(t: PStringTable, key: string): string {.rtl, extern: "nstGet".} = +proc `[]`*(t: StringTableRef, key: string): string {.rtl, extern: "nstGet".} = ## retrieves the value at ``t[key]``. If `key` is not in `t`, "" is returned ## and no exception is raised. One can check with ``hasKey`` whether the key ## exists. @@ -104,33 +109,33 @@ proc `[]`*(t: PStringTable, key: string): string {.rtl, extern: "nstGet".} = if index >= 0: result = t.data[index].val else: result = "" -proc mget*(t: PStringTable, key: string): var string {. +proc mget*(t: StringTableRef, key: string): var string {. rtl, extern: "nstTake".} = ## retrieves the location at ``t[key]``. If `key` is not in `t`, the ## ``EInvalidKey`` exception is raised. var index = rawGet(t, key) if index >= 0: result = t.data[index].val - else: raise newException(EInvalidKey, "key does not exist: " & key) + else: raise newException(KeyError, "key does not exist: " & key) -proc hasKey*(t: PStringTable, key: string): bool {.rtl, extern: "nst$1".} = +proc hasKey*(t: StringTableRef, key: string): bool {.rtl, extern: "nst$1".} = ## returns true iff `key` is in the table `t`. result = rawGet(t, key) >= 0 -proc rawInsert(t: PStringTable, data: var TKeyValuePairSeq, key, val: string) = +proc rawInsert(t: StringTableRef, data: var KeyValuePairSeq, key, val: string) = var h: THash = myhash(t, key) and high(data) while not isNil(data[h].key): h = nextTry(h, high(data)) data[h].key = key data[h].val = val -proc enlarge(t: PStringTable) = - var n: TKeyValuePairSeq +proc enlarge(t: StringTableRef) = + var n: KeyValuePairSeq newSeq(n, len(t.data) * growthFactor) for i in countup(0, high(t.data)): if not isNil(t.data[i].key): rawInsert(t, n, t.data[i].key, t.data[i].val) swap(t.data, n) -proc `[]=`*(t: PStringTable, key, val: string) {.rtl, extern: "nstPut".} = +proc `[]=`*(t: StringTableRef, key, val: string) {.rtl, extern: "nstPut".} = ## puts a (key, value)-pair into `t`. var index = rawGet(t, key) if index >= 0: @@ -141,12 +146,12 @@ proc `[]=`*(t: PStringTable, key, val: string) {.rtl, extern: "nstPut".} = inc(t.counter) proc raiseFormatException(s: string) = - var e: ref EInvalidValue + var e: ref ValueError new(e) e.msg = "format string: key not found: " & s raise e -proc getValue(t: PStringTable, flags: set[TFormatFlag], key: string): string = +proc getValue(t: StringTableRef, flags: set[FormatFlag], key: string): string = if hasKey(t, key): return t[key] # hm difficult: assume safety in taint mode here. XXX This is dangerous! if useEnvironment in flags: result = os.getEnv(key).string @@ -155,7 +160,7 @@ proc getValue(t: PStringTable, flags: set[TFormatFlag], key: string): string = if useKey in flags: result = '$' & key elif not (useEmpty in flags): raiseFormatException(key) -proc newStringTable*(mode: TStringTableMode): PStringTable {. +proc newStringTable*(mode: StringTableMode): StringTableRef {. rtl, extern: "nst$1".} = ## creates a new string table that is empty. new(result) @@ -164,7 +169,7 @@ proc newStringTable*(mode: TStringTableMode): PStringTable {. newSeq(result.data, startSize) proc newStringTable*(keyValuePairs: varargs[string], - mode: TStringTableMode): PStringTable {. + mode: StringTableMode): StringTableRef {. rtl, extern: "nst$1WithPairs".} = ## creates a new string table with given key value pairs. ## Example:: @@ -177,7 +182,7 @@ proc newStringTable*(keyValuePairs: varargs[string], inc(i, 2) proc newStringTable*(keyValuePairs: varargs[tuple[key, val: string]], - mode: TStringTableMode = modeCaseSensitive): PStringTable {. + mode: StringTableMode = modeCaseSensitive): StringTableRef {. rtl, extern: "nst$1WithTableConstr".} = ## creates a new string table with given key value pairs. ## Example:: @@ -186,7 +191,7 @@ proc newStringTable*(keyValuePairs: varargs[tuple[key, val: string]], result = newStringTable(mode) for key, val in items(keyValuePairs): result[key] = val -proc `%`*(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string {. +proc `%`*(f: string, t: StringTableRef, flags: set[FormatFlag] = {}): string {. rtl, extern: "nstFormat".} = ## The `%` operator for string tables. const @@ -216,7 +221,7 @@ proc `%`*(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string {. add(result, f[i]) inc(i) -proc `$`*(t: PStringTable): string {.rtl, extern: "nstDollar".} = +proc `$`*(t: StringTableRef): string {.rtl, extern: "nstDollar".} = ## The `$` operator for string tables. if t.len == 0: result = "{:}" diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 1d17de233..4f449cb0e 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -23,7 +23,7 @@ import parseutils include "system/inclrtl" type - TCharSet* = set[char] # for compatibility with Nim + TCharSet* {.deprecated.} = set[char] # for compatibility with Nim const Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'} @@ -54,7 +54,7 @@ const ## make the `find() proc <#find,string,set[char],int>`_ find **invalid** ## characters in strings. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## let invalid = AllChars - Digits ## doAssert "01234".find(invalid) == -1 ## doAssert "01A34".find(invalid) == 2 @@ -117,7 +117,7 @@ proc normalize*(s: string): string {.noSideEffect, procvar, ## Normalizes the string `s`. ## ## That means to convert it to lower case and remove any '_'. This is needed - ## for Nimrod identifiers for example. + ## for Nim identifiers for example. result = newString(s.len) var j = 0 for i in 0..len(s) - 1: @@ -130,7 +130,7 @@ proc normalize*(s: string): string {.noSideEffect, procvar, if j != s.len: setLen(result, j) proc cmpIgnoreCase*(a, b: string): int {.noSideEffect, - rtl, extern: "nsuCmpIgnoreCase", procvar, operator: 4.} = + rtl, extern: "nsuCmpIgnoreCase", procvar.} = ## Compares two strings in a case insensitive manner. Returns: ## ## | 0 iff a == b @@ -148,7 +148,7 @@ proc cmpIgnoreCase*(a, b: string): int {.noSideEffect, # thus we compile without checks here proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect, - rtl, extern: "nsuCmpIgnoreStyle", procvar, operator: 3.} = + rtl, extern: "nsuCmpIgnoreStyle", procvar.} = ## Compares two strings normalized (i.e. case and ## underscores do not matter). Returns: ## @@ -170,7 +170,7 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect, {.pop.} proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect, - rtl, extern: "nsuStrip", operator: 5.} = + rtl, extern: "nsuStrip".} = ## Strips whitespace from `s` and returns the resulting string. ## ## If `leading` is true, leading whitespace is stripped. @@ -205,7 +205,7 @@ iterator split*(s: string, seps: set[char] = Whitespace): string = ## a single split point and leading/trailing separators will be ignored. ## The following example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## for word in split(" this is an example "): ## writeln(stdout, word) ## @@ -219,13 +219,13 @@ iterator split*(s: string, seps: set[char] = Whitespace): string = ## ## And the following code: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## for word in split(";;this;is;an;;example;;;", {';'}): ## writeln(stdout, word) ## ## ...produces the same output as the first example. The code: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## let date = "2012-11-20T22:08:08.398990" ## let separators = {' ', '-', ':', 'T'} ## for number in split(date, separators): @@ -258,7 +258,7 @@ iterator split*(s: string, sep: char): string = ## characters, this proc will not coalesce groups of the ## separator, returning a string for each found character. The code: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## for word in split(";;this;is;an;;example;;;", ';'): ## writeln(stdout, word) ## @@ -308,13 +308,13 @@ iterator splitLines*(s: string): string = ## ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## for line in splitLines("\nthis\nis\nan\n\nexample\n"): ## writeln(stdout, line) ## ## Results in: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## "" ## "this" ## "is" @@ -417,34 +417,34 @@ proc parseInt*(s: string): int {.noSideEffect, procvar, rtl, extern: "nsuParseInt".} = ## Parses a decimal integer value contained in `s`. ## - ## If `s` is not a valid integer, `EInvalidValue` is raised. + ## If `s` is not a valid integer, `ValueError` is raised. var L = parseutils.parseInt(s, result, 0) if L != s.len or L == 0: - raise newException(EInvalidValue, "invalid integer: " & s) + raise newException(ValueError, "invalid integer: " & s) proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, procvar, rtl, extern: "nsuParseBiggestInt".} = ## Parses a decimal integer value contained in `s`. ## - ## If `s` is not a valid integer, `EInvalidValue` is raised. + ## If `s` is not a valid integer, `ValueError` is raised. var L = parseutils.parseBiggestInt(s, result, 0) if L != s.len or L == 0: - raise newException(EInvalidValue, "invalid integer: " & s) + raise newException(ValueError, "invalid integer: " & s) proc parseFloat*(s: string): float {.noSideEffect, procvar, rtl, extern: "nsuParseFloat".} = ## Parses a decimal floating point value contained in `s`. If `s` is not - ## a valid floating point number, `EInvalidValue` is raised. ``NAN``, + ## a valid floating point number, `ValueError` is raised. ``NAN``, ## ``INF``, ``-INF`` are also supported (case insensitive comparison). var L = parseutils.parseFloat(s, result, 0) if L != s.len or L == 0: - raise newException(EInvalidValue, "invalid float: " & s) + raise newException(ValueError, "invalid float: " & s) proc parseHexInt*(s: string): int {.noSideEffect, procvar, rtl, extern: "nsuParseHexInt".} = ## Parses a hexadecimal integer value contained in `s`. ## - ## If `s` is not a valid integer, `EInvalidValue` is raised. `s` can have one + ## If `s` is not a valid integer, `ValueError` is raised. `s` can have one ## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores ## within `s` are ignored. var i = 0 @@ -463,7 +463,7 @@ proc parseHexInt*(s: string): int {.noSideEffect, procvar, result = result shl 4 or (ord(s[i]) - ord('A') + 10) inc(i) of '\0': break - else: raise newException(EInvalidValue, "invalid integer: " & s) + else: raise newException(ValueError, "invalid integer: " & s) proc parseBool*(s: string): bool = ## Parses a value into a `bool`. @@ -471,21 +471,21 @@ proc parseBool*(s: string): bool = ## If ``s`` is one of the following values: ``y, yes, true, 1, on``, then ## returns `true`. If ``s`` is one of the following values: ``n, no, false, ## 0, off``, then returns `false`. If ``s`` is something else a - ## ``EInvalidValue`` exception is raised. + ## ``ValueError`` exception is raised. case normalize(s) of "y", "yes", "true", "1", "on": result = true of "n", "no", "false", "0", "off": result = false - else: raise newException(EInvalidValue, "cannot interpret as a bool: " & s) + else: raise newException(ValueError, "cannot interpret as a bool: " & s) proc parseEnum*[T: enum](s: string): T = ## Parses an enum ``T``. ## - ## Raises ``EInvalidValue`` for an invalid value in `s`. The comparison is + ## Raises ``ValueError`` for an invalid value in `s`. The comparison is ## done in a style insensitive way. for e in low(T)..high(T): if cmpIgnoreStyle(s, $e) == 0: return e - raise newException(EInvalidValue, "invalid enum value: " & s) + raise newException(ValueError, "invalid enum value: " & s) proc parseEnum*[T: enum](s: string, default: T): T = ## Parses an enum ``T``. @@ -502,7 +502,7 @@ proc repeatChar*(count: int, c: char = ' '): string {.noSideEffect, ## Returns a string of length `count` consisting only of ## the character `c`. You can use this proc to left align strings. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## let ## width = 15 ## text1 = "Hello user!" @@ -527,7 +527,7 @@ proc align*(s: string, count: int, padding = ' '): string {. ## returned unchanged. If you need to left align a string use the `repeatChar ## proc <#repeatChar>`_. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## assert align("abc", 4) == " abc" ## assert align("a", 0) == "a" ## assert align("1232", 6) == " 1232" @@ -547,13 +547,13 @@ iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[ ## Substrings are separated by a substring containing only `seps`. ## Examples: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## for word in tokenize(" this is an example "): ## writeln(stdout, word) ## ## Results in: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## (" ", true) ## ("this", false) ## (" ", true) @@ -676,7 +676,7 @@ proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect, ## ## A shorthand for: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## if dest.len > startLen: add(dest, sep) ## ## This is often useful for generating some code where the items need to @@ -684,7 +684,7 @@ proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect, ## `startLen`. The following example creates a string describing ## an array of integers: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## var arr = "[" ## for x in items([2, 3, 5, 7, 11]): ## addSep(arr, startLen=len("[")) @@ -692,7 +692,7 @@ proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect, ## add(arr, "]") if dest.len > startLen: add(dest, sep) -proc allCharsInSet*(s: string, theSet: TCharSet): bool = +proc allCharsInSet*(s: string, theSet: set[char]): bool = ## Returns true iff each character of `s` is in the set `theSet`. for c in items(s): if c notin theSet: return false @@ -739,14 +739,14 @@ proc join*(a: openArray[string]): string {. result = "" type - TSkipTable = array[char, int] + SkipTable = array[char, int] -proc preprocessSub(sub: string, a: var TSkipTable) = +proc preprocessSub(sub: string, a: var SkipTable) = var m = len(sub) for i in 0..0xff: a[chr(i)] = m+1 for i in 0..m-1: a[sub[i]] = m-i -proc findAux(s, sub: string, start: int, a: TSkipTable): int = +proc findAux(s, sub: string, start: int, a: SkipTable): int = # Fast "quick search" algorithm: var m = len(sub) @@ -762,11 +762,11 @@ proc findAux(s, sub: string, start: int, a: TSkipTable): int = return -1 proc find*(s, sub: string, start: int = 0): int {.noSideEffect, - rtl, extern: "nsuFindStr", operator: 6.} = + rtl, extern: "nsuFindStr".} = ## Searches for `sub` in `s` starting at position `start`. ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. - var a {.noinit.}: TSkipTable + var a {.noinit.}: SkipTable preprocessSub(sub, a) result = findAux(s, sub, start, a) @@ -803,6 +803,16 @@ proc rfind*(s, sub: string, start: int = -1): int {.noSideEffect.} = if result != -1: return return -1 +proc rfind*(s: string, sub: char, start: int = -1): int {.noSideEffect, + rtl.} = + ## Searches for `sub` in `s` in reverse starting at position `start`. + ## + ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. + let realStart = if start == -1: s.len-1 else: start + for i in countdown(realStart, 0): + if sub == s[i]: return i + return -1 + proc count*(s: string, sub: string, overlapping: bool = false): int {.noSideEffect, rtl, extern: "nsuCountString".} = ## Count the occurences of a substring `sub` in the string `s`. @@ -857,9 +867,9 @@ proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} = return find(s, chars) >= 0 proc replace*(s, sub: string, by = ""): string {.noSideEffect, - rtl, extern: "nsuReplaceStr", operator: 1.} = + rtl, extern: "nsuReplaceStr".} = ## Replaces `sub` in `s` by the string `by`. - var a {.noinit.}: TSkipTable + var a {.noinit.}: SkipTable result = "" preprocessSub(sub, a) var i = 0 @@ -892,7 +902,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect, ## (comparable to ``\\w`` in regular expressions), otherwise it is not ## replaced. const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'} - var a {.noinit.}: TSkipTable + var a {.noinit.}: SkipTable result = "" preprocessSub(sub, a) var i = 0 @@ -929,7 +939,7 @@ proc parseOctInt*(s: string): int {.noSideEffect, rtl, extern: "nsuParseOctInt".} = ## Parses an octal integer value contained in `s`. ## - ## If `s` is not a valid integer, `EInvalidValue` is raised. `s` can have one + ## If `s` is not a valid integer, `ValueError` is raised. `s` can have one ## of the following optional prefixes: ``0o``, ``0O``. Underscores within ## `s` are ignored. var i = 0 @@ -941,7 +951,7 @@ proc parseOctInt*(s: string): int {.noSideEffect, result = result shl 3 or (ord(s[i]) - ord('0')) inc(i) of '\0': break - else: raise newException(EInvalidValue, "invalid integer: " & s) + else: raise newException(ValueError, "invalid integer: " & s) proc toOct*(x: BiggestInt, len: int): string {.noSideEffect, rtl, extern: "nsuToOct".} = @@ -1029,11 +1039,11 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect, ## operations. ## ## If `s` does not begin with ``prefix`` and end with ``suffix`` a - ## EInvalidValue exception will be raised. + ## ValueError exception will be raised. result = newStringOfCap(s.len) var i = 0 if s[0 .. prefix.len-1] != prefix: - raise newException(EInvalidValue, + raise newException(ValueError, "String does not start with a prefix of: " & prefix) i.inc() while true: @@ -1058,7 +1068,7 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect, result.add(s[i]) i.inc() if s[i .. -1] != suffix: - raise newException(EInvalidValue, + raise newException(ValueError, "String does not end with a suffix of: " & suffix) proc validIdentifier*(s: string): bool {.noSideEffect, @@ -1168,14 +1178,16 @@ proc c_sprintf(buf, frmt: cstring) {.header: "", importc: "sprintf", varargs, noSideEffect.} type - TFloatFormat* = enum ## the different modes of floating point formating + FloatFormatMode* = enum ## the different modes of floating point formating ffDefault, ## use the shorter floating point notation ffDecimal, ## use decimal floating point notation ffScientific ## use scientific notation (using ``e`` character) -proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault, +{.deprecated: [TFloatFormat: FloatFormatMode].} + +proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, precision: range[0..32] = 16): string {. - noSideEffect, operator: 2, rtl, extern: "nsu$1".} = + noSideEffect, rtl, extern: "nsu$1".} = ## Converts a floating point value `f` to a string. ## ## If ``format == ffDecimal`` then precision is the number of digits to @@ -1183,10 +1195,10 @@ proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault, ## If ``format == ffScientific`` then precision is the maximum number ## of significant digits to be printed. ## `precision`'s default value is the maximum number of meaningful digits - ## after the decimal point for Nimrod's ``biggestFloat`` type. + ## after the decimal point for Nim's ``biggestFloat`` type. ## ## If ``precision == 0``, it tries to format it nicely. - const floatFormatToChar: array[TFloatFormat, char] = ['g', 'f', 'e'] + const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e'] var frmtstr {.noinit.}: array[0..5, char] buf {.noinit.}: array[0..2500, char] @@ -1204,9 +1216,9 @@ proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault, c_sprintf(buf, frmtstr, f) result = $buf -proc formatFloat*(f: float, format: TFloatFormat = ffDefault, +proc formatFloat*(f: float, format: FloatFormatMode = ffDefault, precision: range[0..32] = 16): string {. - noSideEffect, operator: 2, rtl, extern: "nsu$1".} = + noSideEffect, rtl, extern: "nsu$1".} = ## Converts a floating point value `f` to a string. ## ## If ``format == ffDecimal`` then precision is the number of digits to @@ -1214,13 +1226,13 @@ proc formatFloat*(f: float, format: TFloatFormat = ffDefault, ## If ``format == ffScientific`` then precision is the maximum number ## of significant digits to be printed. ## `precision`'s default value is the maximum number of meaningful digits - ## after the decimal point for Nimrod's ``float`` type. + ## after the decimal point for Nim's ``float`` type. result = formatBiggestFloat(f, format, precision) proc formatSize*(bytes: BiggestInt, decimalSep = '.'): string = ## Rounds and formats `bytes`. Examples: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## formatSize(1'i64 shl 31 + 300'i64) == "2.204GB" ## formatSize(4096) == "4KB" @@ -1249,7 +1261,7 @@ proc findNormalized(x: string, inArray: openArray[string]): int = return -1 proc invalidFormatString() {.noinline.} = - raise newException(EInvalidValue, "invalid format string") + raise newException(ValueError, "invalid format string") proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {. noSideEffect, rtl, extern: "nsuAddf".} = @@ -1310,12 +1322,12 @@ proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect, ## ## This is best explained by an example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## "$1 eats $2." % ["The cat", "fish"] ## ## Results in: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## "The cat eats fish." ## ## The substitution variables (the thing after the ``$``) are enumerated @@ -1324,7 +1336,7 @@ proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect, ## The notation ``$#`` can be used to refer to the next substitution ## variable: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## "$# eats $#." % ["The cat", "fish"] ## ## Substitution variables can also be words (that is @@ -1332,15 +1344,15 @@ proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect, ## indices are keys and with odd indices are the corresponding values. ## An example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## "$animal eats $food." % ["animal", "The cat", "food", "fish"] ## ## Results in: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## "The cat eats fish." ## - ## The variables are compared with `cmpIgnoreStyle`. `EInvalidValue` is + ## The variables are compared with `cmpIgnoreStyle`. `ValueError` is ## raised if an ill-formed format string has been passed to the `%` operator. result = newStringOfCap(formatstr.len + a.len shl 4) addf(result, formatstr, a) @@ -1380,8 +1392,8 @@ when isMainModule: doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz " doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc" - type TMyEnum = enum enA, enB, enC, enuD, enE - doAssert parseEnum[TMyEnum]("enu_D") == enuD + type MyEnum = enum enA, enB, enC, enuD, enE + doAssert parseEnum[MyEnum]("enu_D") == enuD doAssert parseEnum("invalid enum value", enC) == enC diff --git a/lib/pure/subexes.nim b/lib/pure/subexes.nim index ed87610d6..adcfdd288 100644 --- a/lib/pure/subexes.nim +++ b/lib/pure/subexes.nim @@ -1,13 +1,13 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## Nimrod support for `substitution expressions`:idx: (`subex`:idx:). +## Nim support for `substitution expressions`:idx: (`subex`:idx:). ## ## .. include:: ../doc/subexes.txt ## @@ -28,15 +28,21 @@ proc findNormalized(x: string, inArray: openarray[string]): int = return -1 type - EInvalidSubex* = object of EInvalidValue ## exception that is raised for - ## an invalid subex + SubexError* = object of ValueError ## exception that is raised for + ## an invalid subex + +{.deprecated: [EInvalidSubex: SubexError].} proc raiseInvalidFormat(msg: string) {.noinline.} = - raise newException(EInvalidSubex, "invalid format string: " & msg) + raise newException(SubexError, "invalid format string: " & msg) type TFormatParser = object {.pure, final.} - f: cstring + 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 template call(x: stmt) {.immediate.} = @@ -194,6 +200,9 @@ proc scanDollar(p: var TFormatParser, a: openarray[string], s: var string) = of '$': emitChar p, s, '$' inc i + of '*': + for j in 0..a.high: emitStr p, s, a[j] + inc i of '{': call: let (x, y) = scanSlice(p, a) @@ -291,14 +300,16 @@ proc scanDollar(p: var TFormatParser, a: openarray[string], s: var string) = type - TSubex* = distinct string ## string that contains a substitution expression + Subex* = distinct string ## string that contains a substitution expression -proc subex*(s: string): TSubex = +{.deprecated: [TSubex: Subex].} + +proc subex*(s: string): Subex = ## constructs a *substitution expression* from `s`. Currently this performs ## no syntax checking but this may change in later versions. - result = TSubex(s) + result = Subex(s) -proc addf*(s: var string, formatstr: TSubex, a: varargs[string, `$`]) {. +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 @@ -312,7 +323,7 @@ proc addf*(s: var string, formatstr: TSubex, a: varargs[string, `$`]) {. emitChar(p, s, p.f[i]) inc(i) -proc `%` *(formatstr: TSubex, a: openarray[string]): string {.noSideEffect, +proc `%` *(formatstr: Subex, a: openarray[string]): string {.noSideEffect, rtl, extern: "nfrmtFormatOpenArray".} = ## The `substitution`:idx: operator performs string substitutions in ## `formatstr` and returns a modified `formatstr`. This is often called @@ -321,13 +332,13 @@ proc `%` *(formatstr: TSubex, a: openarray[string]): string {.noSideEffect, result = newStringOfCap(formatstr.string.len + a.len shl 4) addf(result, formatstr, a) -proc `%` *(formatstr: TSubex, a: string): string {.noSideEffect, +proc `%` *(formatstr: Subex, a: string): string {.noSideEffect, rtl, extern: "nfrmtFormatSingleElem".} = ## This is the same as ``formatstr % [a]``. result = newStringOfCap(formatstr.string.len + a.len) addf(result, formatstr, [a]) -proc format*(formatstr: TSubex, a: varargs[string, `$`]): string {.noSideEffect, +proc format*(formatstr: Subex, a: varargs[string, `$`]): string {.noSideEffect, rtl, extern: "nfrmtFormatVarargs".} = ## The `substitution`:idx: operator performs string substitutions in ## `formatstr` and returns a modified `formatstr`. This is often called diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 20f1d0695..1c1d973ee 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -27,12 +27,13 @@ when defined(windows): var hTemp = GetStdHandle(STD_OUTPUT_HANDLE) if DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(), addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0: - osError(osLastError()) + raiseOSError(osLastError()) proc getCursorPos(): tuple [x,y: int] = var c: TCONSOLESCREENBUFFERINFO - if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: osError(osLastError()) - return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y)) + if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: + raiseOSError(osLastError()) + return (int(c.dwCursorPosition.X), int(c.dwCursorPosition.Y)) proc getAttributes(): int16 = var c: TCONSOLESCREENBUFFERINFO @@ -49,9 +50,9 @@ proc setCursorPos*(x, y: int) = ## upper left of the screen. when defined(windows): var c: TCOORD - c.x = int16(x) - c.y = int16(y) - if SetConsoleCursorPosition(conHandle, c) == 0: osError(osLastError()) + c.X = int16(x) + c.Y = int16(y) + if SetConsoleCursorPosition(conHandle, c) == 0: raiseOSError(osLastError()) else: stdout.write("\e[" & $y & ';' & $x & 'f') @@ -61,10 +62,12 @@ proc setCursorXPos*(x: int) = when defined(windows): var scrbuf: TCONSOLESCREENBUFFERINFO var hStdout = conHandle - if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError()) + if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: + raiseOSError(osLastError()) var origin = scrbuf.dwCursorPosition - origin.x = int16(x) - if SetConsoleCursorPosition(conHandle, origin) == 0: osError(osLastError()) + origin.X = int16(x) + if SetConsoleCursorPosition(conHandle, origin) == 0: + raiseOSError(osLastError()) else: stdout.write("\e[" & $x & 'G') @@ -75,10 +78,12 @@ when defined(windows): when defined(windows): var scrbuf: TCONSOLESCREENBUFFERINFO var hStdout = conHandle - if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError()) + if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: + raiseOSError(osLastError()) var origin = scrbuf.dwCursorPosition - origin.y = int16(y) - if SetConsoleCursorPosition(conHandle, origin) == 0: osError(osLastError()) + origin.Y = int16(y) + if SetConsoleCursorPosition(conHandle, origin) == 0: + raiseOSError(osLastError()) else: discard @@ -155,18 +160,20 @@ proc eraseLine* = var scrbuf: TCONSOLESCREENBUFFERINFO var numwrote: DWORD var hStdout = conHandle - if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError()) + if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: + raiseOSError(osLastError()) var origin = scrbuf.dwCursorPosition - origin.x = 0'i16 - if SetConsoleCursorPosition(conHandle, origin) == 0: osError(osLastError()) + origin.X = 0'i16 + if SetConsoleCursorPosition(conHandle, origin) == 0: + raiseOSError(osLastError()) var ht = scrbuf.dwSize.Y - origin.Y var wt = scrbuf.dwSize.X - origin.X if FillConsoleOutputCharacter(hStdout,' ', ht*wt, origin, addr(numwrote)) == 0: - osError(osLastError()) + raiseOSError(osLastError()) if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt, scrbuf.dwCursorPosition, addr(numwrote)) == 0: - osError(osLastError()) + raiseOSError(osLastError()) else: stdout.write("\e[2K") setCursorXPos(0) @@ -178,14 +185,15 @@ proc eraseScreen* = var numwrote: DWORD var origin: TCOORD # is inititalized to 0, 0 var hStdout = conHandle - if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: osError(osLastError()) + if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: + raiseOSError(osLastError()) if FillConsoleOutputCharacter(hStdout, ' ', scrbuf.dwSize.X*scrbuf.dwSize.Y, origin, addr(numwrote)) == 0: - osError(osLastError()) + raiseOSError(osLastError()) if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, scrbuf.dwSize.X * scrbuf.dwSize.Y, origin, addr(numwrote)) == 0: - osError(osLastError()) + raiseOSError(osLastError()) setCursorXPos(0) else: stdout.write("\e[2J") @@ -199,7 +207,7 @@ proc resetAttributes* {.noconv.} = stdout.write("\e[0m") type - TStyle* = enum ## different styles for text output + Style* = enum ## different styles for text output styleBright = 1, ## bright text styleDim, ## dim text styleUnknown, ## unknown @@ -208,13 +216,15 @@ type styleReverse = 7, ## unknown styleHidden ## hidden text +{.deprecated: [TStyle: Style].} + when not defined(windows): var # XXX: These better be thread-local gFG = 0 gBG = 0 -proc setStyle*(style: set[TStyle]) = +proc setStyle*(style: set[Style]) = ## sets the terminal style when defined(windows): var a = 0'i16 @@ -227,7 +237,7 @@ proc setStyle*(style: set[TStyle]) = for s in items(style): stdout.write("\e[" & $ord(s) & 'm') -proc writeStyled*(txt: string, style: set[TStyle] = {styleBright}) = +proc writeStyled*(txt: string, style: set[Style] = {styleBright}) = ## writes the text `txt` in a given `style`. when defined(windows): var old = getAttributes() @@ -244,7 +254,7 @@ proc writeStyled*(txt: string, style: set[TStyle] = {styleBright}) = stdout.write("\e[" & $ord(gBG) & 'm') type - TForegroundColor* = enum ## terminal's foreground colors + ForegroundColor* = enum ## terminal's foreground colors fgBlack = 30, ## black fgRed, ## red fgGreen, ## green @@ -254,7 +264,7 @@ type fgCyan, ## cyan fgWhite ## white - TBackgroundColor* = enum ## terminal's background colors + BackgroundColor* = enum ## terminal's background colors bgBlack = 40, ## black bgRed, ## red bgGreen, ## green @@ -264,13 +274,16 @@ type bgCyan, ## cyan bgWhite ## white -proc setForegroundColor*(fg: TForegroundColor, bright=false) = +{.deprecated: [TForegroundColor: ForegroundColor, + TBackgroundColor: BackgroundColor].} + +proc setForegroundColor*(fg: ForegroundColor, bright=false) = ## sets the terminal's foreground color when defined(windows): var old = getAttributes() and not 0x0007 if bright: old = old or FOREGROUND_INTENSITY - const lookup: array [TForegroundColor, int] = [ + const lookup: array [ForegroundColor, int] = [ 0, (FOREGROUND_RED), (FOREGROUND_GREEN), @@ -285,13 +298,13 @@ proc setForegroundColor*(fg: TForegroundColor, bright=false) = if bright: inc(gFG, 60) stdout.write("\e[" & $gFG & 'm') -proc setBackgroundColor*(bg: TBackgroundColor, bright=false) = +proc setBackgroundColor*(bg: BackgroundColor, bright=false) = ## sets the terminal's background color when defined(windows): var old = getAttributes() and not 0x0070 if bright: old = old or BACKGROUND_INTENSITY - const lookup: array [TBackgroundColor, int] = [ + const lookup: array [BackgroundColor, int] = [ 0, (BACKGROUND_RED), (BACKGROUND_GREEN), @@ -306,22 +319,22 @@ proc setBackgroundColor*(bg: TBackgroundColor, bright=false) = if bright: inc(gBG, 60) stdout.write("\e[" & $gBG & 'm') -proc isatty*(f: TFile): bool = +proc isatty*(f: File): bool = ## returns true if `f` is associated with a terminal device. when defined(posix): - proc isatty(fildes: TFileHandle): cint {. + proc isatty(fildes: FileHandle): cint {. importc: "isatty", header: "".} else: - proc isatty(fildes: TFileHandle): cint {. + proc isatty(fildes: FileHandle): cint {. importc: "_isatty", header: "".} - result = isatty(fileHandle(f)) != 0'i32 + result = isatty(getFileHandle(f)) != 0'i32 -proc styledEchoProcessArg(s: string) = write stdout, s -proc styledEchoProcessArg(style: TStyle) = setStyle({style}) -proc styledEchoProcessArg(style: set[TStyle]) = setStyle style -proc styledEchoProcessArg(color: TForegroundColor) = setForegroundColor color -proc styledEchoProcessArg(color: TBackgroundColor) = setBackgroundColor color +proc styledEchoProcessArg(s: string) = write stdout, s +proc styledEchoProcessArg(style: Style) = setStyle({style}) +proc styledEchoProcessArg(style: set[Style]) = setStyle style +proc styledEchoProcessArg(color: ForegroundColor) = setForegroundColor color +proc styledEchoProcessArg(color: BackgroundColor) = setBackgroundColor color macro styledEcho*(m: varargs[expr]): stmt = ## to be documented. diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 8b33d2c73..1cabd381b 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -21,9 +21,9 @@ import include "system/inclrtl" type - TMonth* = enum ## represents a month + Month* = enum ## represents a month mJan, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec - TWeekDay* = enum ## represents a weekday + WeekDay* = enum ## represents a weekday dMon, dTue, dWed, dThu, dFri, dSat, dSun var @@ -32,12 +32,12 @@ var when defined(posix) and not defined(JS): type - TTimeImpl {.importc: "time_t", header: "".} = int - TTime* = distinct TTimeImpl ## distinct type that represents a time - ## measured as number of seconds since the epoch + TimeImpl {.importc: "time_t", header: "".} = int + Time* = distinct TimeImpl ## distinct type that represents a time + ## measured as number of seconds since the epoch - Ttimeval {.importc: "struct timeval", header: "", - final, pure.} = object ## struct timeval + Timeval {.importc: "struct timeval", + header: "".} = object ## struct timeval tv_sec: int ## Seconds. tv_usec: int ## Microseconds. @@ -45,7 +45,7 @@ when defined(posix) and not defined(JS): # Ok, we could, but I don't want circular dependencies. # And gettimeofday() is not defined in the posix module anyway. Sigh. - proc posix_gettimeofday(tp: var Ttimeval, unused: pointer = nil) {. + proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {. importc: "gettimeofday", header: "".} elif defined(windows): @@ -53,16 +53,16 @@ elif defined(windows): when defined(vcc): # newest version of Visual C++ defines time_t to be of 64 bits - type TTimeImpl {.importc: "time_t", header: "".} = int64 + type TimeImpl {.importc: "time_t", header: "".} = int64 else: - type TTimeImpl {.importc: "time_t", header: "".} = int32 + type TimeImpl {.importc: "time_t", header: "".} = int32 type - TTime* = distinct TTimeImpl + Time* = distinct TimeImpl elif defined(JS): type - TTime* {.final, importc.} = object + Time* {.importc.} = object getDay: proc (): int {.tags: [], raises: [], gcsafe.} getFullYear: proc (): int {.tags: [], raises: [], gcsafe.} getHours: proc (): int {.tags: [], raises: [], gcsafe.} @@ -82,7 +82,7 @@ elif defined(JS): getUTCSeconds: proc (): int {.tags: [], raises: [], gcsafe.} getUTCDay: proc (): int {.tags: [], raises: [], gcsafe.} getYear: proc (): int {.tags: [], raises: [], gcsafe.} - parse: proc (s: cstring): TTime {.tags: [], raises: [], gcsafe.} + parse: proc (s: cstring): Time {.tags: [], raises: [], gcsafe.} setDate: proc (x: int) {.tags: [], raises: [], gcsafe.} setFullYear: proc (x: int) {.tags: [], raises: [], gcsafe.} setHours: proc (x: int) {.tags: [], raises: [], gcsafe.} @@ -103,7 +103,7 @@ elif defined(JS): toLocaleString: proc (): cstring {.tags: [], raises: [], gcsafe.} type - TTimeInfo* = object of TObject ## represents a time in different parts + TimeInfo* = object of RootObj ## represents a time in different parts second*: range[0..61] ## The number of seconds after the minute, ## normally in the range 0 to 59, but can ## be up to 61 to allow for leap seconds. @@ -112,9 +112,9 @@ type hour*: range[0..23] ## The number of hours past midnight, ## in the range 0 to 23. monthday*: range[1..31] ## The day of the month, in the range 1 to 31. - month*: TMonth ## The current month. + month*: Month ## The current month. year*: range[-10_000..10_000] ## The current year. - weekday*: TWeekDay ## The current day of the week. + weekday*: WeekDay ## The current day of the week. yearday*: range[0..365] ## The number of days since January 1, ## in the range 0 to 365. ## Always 0 if the target is JS. @@ -127,7 +127,7 @@ type ## I make some assumptions about the data in here. Either ## everything should be positive or everything negative. Zero is ## fine too. Mixed signs will lead to unexpected results. - TTimeInterval* {.pure.} = object ## a time interval + TimeInterval* = object ## a time interval miliseconds*: int ## The number of miliseconds seconds*: int ## The number of seconds minutes*: int ## The number of minutes @@ -136,75 +136,78 @@ type months*: int ## The number of months years*: int ## The number of years -proc getTime*(): TTime {.tags: [FTime], gcsafe.} +{.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time, + TTimeInterval: TimeInterval, TTimeInfo: TimeInfo].} + +proc getTime*(): Time {.tags: [TimeEffect], gcsafe.} ## gets the current calendar time as a UNIX epoch value (number of seconds ## elapsed since 1970) with integer precission. Use epochTime for higher ## resolution. -proc getLocalTime*(t: TTime): TTimeInfo {.tags: [FTime], raises: [], gcsafe.} +proc getLocalTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], gcsafe.} ## converts the calendar time `t` to broken-time representation, ## expressed relative to the user's specified time zone. -proc getGMTime*(t: TTime): TTimeInfo {.tags: [FTime], raises: [], gcsafe.} +proc getGMTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], gcsafe.} ## converts the calendar time `t` to broken-down time representation, ## expressed in Coordinated Universal Time (UTC). -proc timeInfoToTime*(timeInfo: TTimeInfo): TTime {.tags: [], gcsafe.} +proc timeInfoToTime*(timeInfo: TimeInfo): Time {.tags: [], gcsafe.} ## converts a broken-down time structure to ## calendar time representation. The function ignores the specified ## contents of the structure members `weekday` and `yearday` and recomputes ## them from the other information in the broken-down time structure. -proc fromSeconds*(since1970: float): TTime {.tags: [], raises: [], gcsafe.} +proc fromSeconds*(since1970: float): Time {.tags: [], raises: [], gcsafe.} ## Takes a float which contains the number of seconds since the unix epoch and ## returns a time object. -proc fromSeconds*(since1970: int64): TTime {.tags: [], raises: [], gcsafe.} = +proc fromSeconds*(since1970: int64): Time {.tags: [], raises: [], gcsafe.} = ## Takes an int which contains the number of seconds since the unix epoch and ## returns a time object. fromSeconds(float(since1970)) -proc toSeconds*(time: TTime): float {.tags: [], raises: [], gcsafe.} +proc toSeconds*(time: Time): float {.tags: [], raises: [], gcsafe.} ## Returns the time in seconds since the unix epoch. -proc `$` *(timeInfo: TTimeInfo): string {.tags: [], raises: [], gcsafe.} - ## converts a `TTimeInfo` object to a string representation. -proc `$` *(time: TTime): string {.tags: [], raises: [], gcsafe.} +proc `$` *(timeInfo: TimeInfo): string {.tags: [], raises: [], gcsafe.} + ## converts a `TimeInfo` object to a string representation. +proc `$` *(time: Time): string {.tags: [], raises: [], gcsafe.} ## converts a calendar time to a string representation. -proc `-`*(a, b: TTime): int64 {. +proc `-`*(a, b: Time): int64 {. rtl, extern: "ntDiffTime", tags: [], raises: [].} ## computes the difference of two calendar times. Result is in seconds. -proc `<`*(a, b: TTime): bool {. +proc `<`*(a, b: Time): bool {. rtl, extern: "ntLtTime", tags: [], raises: [].} = ## returns true iff ``a < b``, that is iff a happened before b. result = a - b < 0 -proc `<=` * (a, b: TTime): bool {. +proc `<=` * (a, b: Time): bool {. rtl, extern: "ntLeTime", tags: [], raises: [].}= ## returns true iff ``a <= b``. result = a - b <= 0 -proc `==`*(a, b: TTime): bool {. +proc `==`*(a, b: Time): bool {. rtl, extern: "ntEqTime", tags: [], raises: [].} = ## returns true if ``a == b``, that is if both times represent the same value result = a - b == 0 when not defined(JS): - proc getTzname*(): tuple[nonDST, DST: string] {.tags: [FTime], raises: [], + proc getTzname*(): tuple[nonDST, DST: string] {.tags: [TimeEffect], raises: [], gcsafe.} ## returns the local timezone; ``nonDST`` is the name of the local non-DST ## timezone, ``DST`` is the name of the local DST timezone. -proc getTimezone*(): int {.tags: [FTime], raises: [], gcsafe.} +proc getTimezone*(): int {.tags: [TimeEffect], raises: [], gcsafe.} ## returns the offset of the local (non-DST) timezone in seconds west of UTC. -proc getStartMilsecs*(): int {.deprecated, tags: [FTime], gcsafe.} +proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], gcsafe.} ## get the miliseconds from the start of the program. **Deprecated since ## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead. proc initInterval*(miliseconds, seconds, minutes, hours, days, months, - years: int = 0): TTimeInterval = - ## creates a new ``TTimeInterval``. + years: int = 0): TimeInterval = + ## creates a new ``TimeInterval``. result.miliseconds = miliseconds result.seconds = seconds result.minutes = minutes @@ -225,7 +228,7 @@ proc isLeapYear*(year: int): bool = else: return false -proc getDaysInMonth*(month: TMonth, year: int): int = +proc getDaysInMonth*(month: Month, year: int): int = ## gets the amount of days in a ``month`` of a ``year`` # http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month @@ -234,7 +237,7 @@ proc getDaysInMonth*(month: TMonth, year: int): int = of mApr, mJun, mSep, mNov: result = 30 else: result = 31 -proc toSeconds(a: TTimeInfo, interval: TTimeInterval): float = +proc toSeconds(a: TimeInfo, interval: TimeInterval): float = ## Calculates how many seconds the interval is worth by adding up ## all the fields @@ -257,7 +260,7 @@ proc toSeconds(a: TTimeInfo, interval: TTimeInterval): float = result += float(newinterv.seconds) result += newinterv.miliseconds / 1000 -proc `+`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo = +proc `+`*(a: TimeInfo, interval: TimeInterval): TimeInfo = ## adds ``interval`` time. ## ## **Note:** This has been only briefly tested and it may not be @@ -269,7 +272,7 @@ proc `+`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo = else: result = getLocalTime(fromSeconds(t + secs)) -proc `-`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo = +proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo = ## subtracts ``interval`` time. ## ## **Note:** This has been only briefly tested, it is inaccurate especially @@ -282,12 +285,12 @@ proc `-`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo = result = getLocalTime(fromSeconds(t - secs)) when not defined(JS): - proc epochTime*(): float {.rtl, extern: "nt$1", tags: [FTime].} + proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} ## gets time after the UNIX epoch (1970) in seconds. It is a float ## because sub-second resolution is likely to be supported (depending ## on the hardware/OS). - proc cpuTime*(): float {.rtl, extern: "nt$1", tags: [FTime].} + proc cpuTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} ## gets time spent that the CPU spent to run the current process in ## seconds. This may be more useful for benchmarking than ``epochTime``. ## However, it may measure the real time instead (depending on the OS). @@ -295,7 +298,7 @@ when not defined(JS): ## To generate useful timing values, take the difference between ## the results of two ``cpuTime`` calls: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## var t0 = cpuTime() ## doWork() ## echo "CPU time [s] ", cpuTime() - t0 @@ -314,42 +317,40 @@ when not defined(JS): yearday {.importc: "tm_yday".}, isdst {.importc: "tm_isdst".}: cint - PTimeInfo = ptr StructTM - PTime = ptr TTime + TimeInfoPtr = ptr StructTM + Clock {.importc: "clock_t".} = distinct int - TClock {.importc: "clock_t".} = distinct int - - proc localtime(timer: PTime): PTimeInfo {. + proc localtime(timer: ptr Time): TimeInfoPtr {. importc: "localtime", header: "", tags: [].} - proc gmtime(timer: PTime): PTimeInfo {. + proc gmtime(timer: ptr Time): TimeInfoPtr {. importc: "gmtime", header: "", tags: [].} - proc timec(timer: PTime): TTime {. + proc timec(timer: ptr Time): Time {. importc: "time", header: "", tags: [].} - proc mktime(t: StructTM): TTime {. + proc mktime(t: StructTM): Time {. importc: "mktime", header: "", tags: [].} proc asctime(tblock: StructTM): cstring {. importc: "asctime", header: "", tags: [].} - proc ctime(time: PTime): cstring {. + proc ctime(time: ptr Time): cstring {. importc: "ctime", header: "", tags: [].} # strftime(s: CString, maxsize: int, fmt: CString, t: tm): int {. # importc: "strftime", header: "".} - proc clock(): TClock {.importc: "clock", header: "", tags: [FTime].} - proc difftime(a, b: TTime): float {.importc: "difftime", header: "", + proc getClock(): Clock {.importc: "clock", header: "", tags: [TimeEffect].} + proc difftime(a, b: Time): float {.importc: "difftime", header: "", tags: [].} var clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int # our own procs on top of that: - proc tmToTimeInfo(tm: StructTM, local: bool): TTimeInfo = + proc tmToTimeInfo(tm: StructTM, local: bool): TimeInfo = const - weekDays: array [0..6, TWeekDay] = [ + weekDays: array [0..6, WeekDay] = [ dSun, dMon, dTue, dWed, dThu, dFri, dSat] - TTimeInfo(second: int(tm.second), + TimeInfo(second: int(tm.second), minute: int(tm.minute), hour: int(tm.hour), monthday: int(tm.monthday), - month: TMonth(tm.month), + month: Month(tm.month), year: tm.year + 1900'i32, weekday: weekDays[int(tm.weekday)], yearday: int(tm.yearday), @@ -364,9 +365,9 @@ when not defined(JS): timezone: if local: getTimezone() else: 0 ) - proc timeInfoToTM(t: TTimeInfo): StructTM = + proc timeInfoToTM(t: TimeInfo): StructTM = const - weekDays: array [TWeekDay, int8] = [1'i8,2'i8,3'i8,4'i8,5'i8,6'i8,0'i8] + weekDays: array [WeekDay, int8] = [1'i8,2'i8,3'i8,4'i8,5'i8,6'i8,0'i8] result.second = t.second result.minute = t.minute result.hour = t.hour @@ -378,36 +379,36 @@ when not defined(JS): result.isdst = if t.isDST: 1 else: 0 when not defined(useNimRtl): - proc `-` (a, b: TTime): int64 = + proc `-` (a, b: Time): int64 = return toBiggestInt(difftime(a, b)) proc getStartMilsecs(): int = - #echo "clocks per sec: ", clocksPerSec, "clock: ", int(clock()) - #return clock() div (clocksPerSec div 1000) + #echo "clocks per sec: ", clocksPerSec, "clock: ", int(getClock()) + #return getClock() div (clocksPerSec div 1000) when defined(macosx): - result = toInt(toFloat(int(clock())) / (toFloat(clocksPerSec) / 1000.0)) + result = toInt(toFloat(int(getClock())) / (toFloat(clocksPerSec) / 1000.0)) else: - result = int(clock()) div (clocksPerSec div 1000) + result = int(getClock()) div (clocksPerSec div 1000) when false: - var a: Ttimeval + var a: Timeval posix_gettimeofday(a) result = a.tv_sec * 1000'i64 + a.tv_usec div 1000'i64 #echo "result: ", result - proc getTime(): TTime = return timec(nil) - proc getLocalTime(t: TTime): TTimeInfo = + proc getTime(): Time = return timec(nil) + proc getLocalTime(t: Time): TimeInfo = var a = t result = tmToTimeInfo(localtime(addr(a))[], true) # copying is needed anyway to provide reentrancity; thus # the conversion is not expensive - proc getGMTime(t: TTime): TTimeInfo = + proc getGMTime(t: Time): TimeInfo = var a = t result = tmToTimeInfo(gmtime(addr(a))[], false) # copying is needed anyway to provide reentrancity; thus # the conversion is not expensive - proc timeInfoToTime(timeInfo: TTimeInfo): TTime = + proc timeInfoToTime(timeInfo: TimeInfo): Time = var cTimeInfo = timeInfo # for C++ we have to make a copy, # because the header of mktime is broken in my version of libc return mktime(timeInfoToTM(cTimeInfo)) @@ -419,12 +420,12 @@ when not defined(JS): add(result, p[i]) inc(i) - proc `$`(timeInfo: TTimeInfo): string = + proc `$`(timeInfo: TimeInfo): string = # BUGFIX: asctime returns a newline at the end! var p = asctime(timeInfoToTM(timeInfo)) result = toStringTillNL(p) - proc `$`(time: TTime): string = + proc `$`(time: Time): string = # BUGFIX: ctime returns a newline at the end! var a = time return toStringTillNL(ctime(addr(a))) @@ -433,13 +434,13 @@ when not defined(JS): epochDiff = 116444736000000000'i64 rateDiff = 10000000'i64 # 100 nsecs - proc unixTimeToWinTime*(t: TTime): int64 = - ## converts a UNIX `TTime` (``time_t``) to a Windows file time + proc unixTimeToWinTime*(t: Time): int64 = + ## converts a UNIX `Time` (``time_t``) to a Windows file time result = int64(t) * rateDiff + epochDiff - proc winTimeToUnixTime*(t: int64): TTime = - ## converts a Windows time to a UNIX `TTime` (``time_t``) - result = TTime((t - epochDiff) div rateDiff) + proc winTimeToUnixTime*(t: int64): Time = + ## converts a Windows time to a UNIX `Time` (``time_t``) + result = Time((t - epochDiff) div rateDiff) proc getTzname(): tuple[nonDST, DST: string] = return ($tzname[0], $tzname[1]) @@ -447,18 +448,18 @@ when not defined(JS): proc getTimezone(): int = return timezone - proc fromSeconds(since1970: float): TTime = TTime(since1970) + proc fromSeconds(since1970: float): Time = Time(since1970) - proc toSeconds(time: TTime): float = float(time) + proc toSeconds(time: Time): float = float(time) when not defined(useNimRtl): proc epochTime(): float = when defined(posix): - var a: Ttimeval + var a: Timeval 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.TFILETIME getSystemTimeAsFileTime(f) var i64 = rdFileTime(f) - epochDiff var secs = i64 div rateDiff @@ -468,15 +469,15 @@ when not defined(JS): {.error: "unknown OS".} proc cpuTime(): float = - result = toFloat(int(clock())) / toFloat(clocksPerSec) + result = toFloat(int(getClock())) / toFloat(clocksPerSec) elif defined(JS): - proc newDate(): TTime {.importc: "new Date".} - proc internGetTime(): TTime {.importc: "new Date", tags: [].} + proc newDate(): Time {.importc: "new Date".} + proc internGetTime(): Time {.importc: "new Date", tags: [].} - proc newDate(value: float): TTime {.importc: "new Date".} - proc newDate(value: string): TTime {.importc: "new Date".} - proc getTime(): TTime = + proc newDate(value: float): Time {.importc: "new Date".} + proc newDate(value: string): Time {.importc: "new Date".} + proc getTime(): Time = # Warning: This is something different in JS. return newDate() @@ -484,7 +485,7 @@ elif defined(JS): weekDays: array [0..6, TWeekDay] = [ dSun, dMon, dTue, dWed, dThu, dFri, dSat] - proc getLocalTime(t: TTime): TTimeInfo = + proc getLocalTime(t: Time): TimeInfo = result.second = t.getSeconds() result.minute = t.getMinutes() result.hour = t.getHours() @@ -494,7 +495,7 @@ elif defined(JS): result.weekday = weekDays[t.getDay()] result.yearday = 0 - proc getGMTime(t: TTime): TTimeInfo = + proc getGMTime(t: Time): TimeInfo = result.second = t.getUTCSeconds() result.minute = t.getUTCMinutes() result.hour = t.getUTCHours() @@ -504,7 +505,7 @@ elif defined(JS): result.weekday = weekDays[t.getUTCDay()] result.yearday = 0 - proc timeInfoToTime*(timeInfo: TTimeInfo): TTime = + proc timeInfoToTime*(timeInfo: TimeInfo): Time = result = internGetTime() result.setSeconds(timeInfo.second) result.setMinutes(timeInfo.minute) @@ -513,10 +514,10 @@ elif defined(JS): result.setFullYear(timeInfo.year) result.setDate(timeInfo.monthday) - proc `$`(timeInfo: TTimeInfo): string = return $(timeInfoToTime(timeInfo)) - proc `$`(time: TTime): string = return $time.toLocaleString() + proc `$`(timeInfo: TimeInfo): string = return $(timeInfoToTime(timeInfo)) + proc `$`(time: Time): string = return $time.toLocaleString() - proc `-` (a, b: TTime): int64 = + proc `-` (a, b: Time): int64 = return a.getTime() - b.getTime() var @@ -526,40 +527,40 @@ elif defined(JS): ## get the miliseconds from the start of the program return int(getTime() - startMilsecs) - proc valueOf(time: TTime): float {.importcpp: "getTime", tags:[]} + proc valueOf(time: Time): float {.importcpp: "getTime", tags:[]} - proc fromSeconds(since1970: float): TTime = result = newDate(since1970) + proc fromSeconds(since1970: float): Time = result = newDate(since1970) - proc toSeconds(time: TTime): float = result = time.valueOf() / 1000 + proc toSeconds(time: Time): float = result = time.valueOf() / 1000 proc getTimezone(): int = result = newDate().getTimezoneOffset() -proc getDateStr*(): string {.rtl, extern: "nt$1", tags: [FTime].} = +proc getDateStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} = ## gets the current date as a string of the format ``YYYY-MM-DD``. var ti = getLocalTime(getTime()) result = $ti.year & '-' & intToStr(ord(ti.month)+1, 2) & '-' & intToStr(ti.monthday, 2) -proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [FTime].} = +proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} = ## gets the current clock time as a string of the format ``HH:MM:SS``. var ti = getLocalTime(getTime()) result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) & ':' & intToStr(ti.second, 2) -proc `$`*(day: TWeekDay): string = - ## stingify operator for ``TWeekDay``. - const lookup: array[TWeekDay, string] = ["Monday", "Tuesday", "Wednesday", +proc `$`*(day: WeekDay): string = + ## stingify operator for ``WeekDay``. + const lookup: array[WeekDay, string] = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] return lookup[day] -proc `$`*(m: TMonth): string = +proc `$`*(m: Month): string = ## stingify operator for ``TMonth``. - const lookup: array[TMonth, string] = ["January", "February", "March", + const lookup: array[Month, string] = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] return lookup[m] -proc format_token(info: TTimeInfo, token: string, buf: var string) = +proc formatToken(info: TimeInfo, token: string, buf: var string) = ## Helper of the format proc to parse individual tokens. ## ## Pass the found token in the user input string, and the buffer where the @@ -669,10 +670,10 @@ proc format_token(info: TTimeInfo, token: string, buf: var string) = of "": discard else: - raise newException(EInvalidValue, "Invalid format string: " & token) + raise newException(ValueError, "Invalid format string: " & token) -proc format*(info: TTimeInfo, f: string): string = +proc format*(info: TimeInfo, f: string): string = ## This function formats `info` as specified by `f`. The following format ## specifiers are available: ## @@ -718,7 +719,7 @@ proc format*(info: TTimeInfo, f: string): string = while true: case f[i] of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',': - format_token(info, currentF, result) + formatToken(info, currentF, result) currentF = "" if f[i] == '\0': break @@ -735,7 +736,7 @@ proc format*(info: TTimeInfo, f: string): string = if currentF.len < 1 or currentF[high(currentF)] == f[i]: currentF.add(f[i]) else: - format_token(info, currentF, result) + formatToken(info, currentF, result) dec(i) # Move position back to re-process the character separately. currentF = "" @@ -764,7 +765,7 @@ when isMainModule: " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == "27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 0 00 00:00 UTC" - when not defined(JS) and sizeof(TTime) == 8: + when not defined(JS) and sizeof(Time) == 8: var t3 = getGMTime(fromSeconds(889067643645)) # Fri 7 Jun 19:20:45 BST 30143 assert t3.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 3203ee699..2c3d872df 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -1,7 +1,7 @@ # # -# Nimrod's Runtime Library -# (c) Copyright 2012 Nimrod Contributors +# Nim's Runtime Library +# (c) Copyright 2012 Nim Contributors # # See the file "copying.txt", included in this # distribution, for details about the copyright. diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 6e73eea3f..c2eb001f6 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -14,13 +14,15 @@ include "system/inclrtl" type - IRune = int # underlying type of TRune - TRune* = distinct IRune ## type that can hold any Unicode character - TRune16* = distinct int16 ## 16 bit Unicode character + RuneImpl = int # underlying type of Rune + Rune* = distinct RuneImpl ## type that can hold any Unicode character + Rune16* = distinct int16 ## 16 bit Unicode character + +{.deprecated: [TRune: Rune, TRune16: Rune16].} -proc `<=%`*(a, b: TRune): bool = return int(a) <=% int(b) -proc `<%`*(a, b: TRune): bool = return int(a) <% int(b) -proc `==`*(a, b: TRune): bool = return int(a) == int(b) +proc `<=%`*(a, b: Rune): bool = return int(a) <=% int(b) +proc `<%`*(a, b: Rune): bool = return int(a) <% int(b) +proc `==`*(a, b: Rune): bool = return int(a) == int(b) template ones(n: expr): expr = ((1 shl n)-1) @@ -52,17 +54,17 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) = ## `i` is incremented by the number of bytes that have been processed. bind ones if ord(s[i]) <=% 127: - result = TRune(ord(s[i])) + result = Rune(ord(s[i])) when doInc: inc(i) elif ord(s[i]) shr 5 == 0b110: # assert(ord(s[i+1]) shr 6 == 0b10) - result = TRune((ord(s[i]) and (ones(5))) shl 6 or - (ord(s[i+1]) and ones(6))) + result = Rune((ord(s[i]) and (ones(5))) shl 6 or + (ord(s[i+1]) and ones(6))) when doInc: inc(i, 2) elif ord(s[i]) shr 4 == 0b1110: # assert(ord(s[i+1]) shr 6 == 0b10) # assert(ord(s[i+2]) shr 6 == 0b10) - result = TRune((ord(s[i]) and ones(4)) shl 12 or + result = Rune((ord(s[i]) and ones(4)) shl 12 or (ord(s[i+1]) and ones(6)) shl 6 or (ord(s[i+2]) and ones(6))) when doInc: inc(i, 3) @@ -70,7 +72,7 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) = # assert(ord(s[i+1]) shr 6 == 0b10) # assert(ord(s[i+2]) shr 6 == 0b10) # assert(ord(s[i+3]) shr 6 == 0b10) - result = TRune((ord(s[i]) and ones(3)) shl 18 or + result = Rune((ord(s[i]) and ones(3)) shl 18 or (ord(s[i+1]) and ones(6)) shl 12 or (ord(s[i+2]) and ones(6)) shl 6 or (ord(s[i+3]) and ones(6))) @@ -80,7 +82,7 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) = # assert(ord(s[i+2]) shr 6 == 0b10) # assert(ord(s[i+3]) shr 6 == 0b10) # assert(ord(s[i+4]) shr 6 == 0b10) - result = TRune((ord(s[i]) and ones(2)) shl 24 or + result = Rune((ord(s[i]) and ones(2)) shl 24 or (ord(s[i+1]) and ones(6)) shl 18 or (ord(s[i+2]) and ones(6)) shl 12 or (ord(s[i+3]) and ones(6)) shl 6 or @@ -92,7 +94,7 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) = # assert(ord(s[i+3]) shr 6 == 0b10) # assert(ord(s[i+4]) shr 6 == 0b10) # assert(ord(s[i+5]) shr 6 == 0b10) - result = TRune((ord(s[i]) and ones(1)) shl 30 or + result = Rune((ord(s[i]) and ones(1)) shl 30 or (ord(s[i+1]) and ones(6)) shl 24 or (ord(s[i+2]) and ones(6)) shl 18 or (ord(s[i+3]) and ones(6)) shl 12 or @@ -100,16 +102,16 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) = (ord(s[i+5]) and ones(6))) when doInc: inc(i, 6) else: - result = TRune(ord(s[i])) + result = Rune(ord(s[i])) when doInc: inc(i) -proc runeAt*(s: string, i: int): TRune = +proc runeAt*(s: string, i: int): Rune = ## returns the unicode character in `s` at byte index `i` fastRuneAt(s, i, result, false) -proc toUTF8*(c: TRune): string {.rtl, extern: "nuc$1".} = +proc toUTF8*(c: Rune): string {.rtl, extern: "nuc$1".} = ## converts a rune into its UTF8 representation - var i = IRune(c) + var i = RuneImpl(c) if i <=% 127: result = newString(1) result[0] = chr(i) @@ -132,11 +134,11 @@ proc toUTF8*(c: TRune): string {.rtl, extern: "nuc$1".} = result = newString(1) result[0] = chr(i) -proc `$`*(rune: TRune): string = +proc `$`*(rune: Rune): string = ## converts a rune to a string rune.toUTF8 -proc `$`*(runes: seq[TRune]): string = +proc `$`*(runes: seq[Rune]): string = ## converts a sequence of runes to a string result = "" for rune in runes: result.add(rune.toUTF8) @@ -1100,7 +1102,7 @@ const 0x01f1, 501, # 0x01f3, 499] # -proc binarySearch(c: IRune, tab: openArray[IRune], len, stride: int): int = +proc binarySearch(c: RuneImpl, tab: openArray[RuneImpl], len, stride: int): int = var n = len var t = 0 while n > 1: @@ -1115,41 +1117,41 @@ proc binarySearch(c: IRune, tab: openArray[IRune], len, stride: int): int = return t return -1 -proc toLower*(c: TRune): TRune {.rtl, extern: "nuc$1", procvar.} = +proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = ## Converts `c` into lower case. This works for any Unicode character. ## If possible, prefer `toLower` over `toUpper`. - var c = IRune(c) + var c = RuneImpl(c) var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3) if p >= 0 and c >= tolowerRanges[p] and c <= tolowerRanges[p+1]: - return TRune(c + tolowerRanges[p+2] - 500) + return Rune(c + tolowerRanges[p+2] - 500) p = binarySearch(c, tolowerSinglets, len(tolowerSinglets) div 2, 2) if p >= 0 and c == tolowerSinglets[p]: - return TRune(c + tolowerSinglets[p+1] - 500) - return TRune(c) + return Rune(c + tolowerSinglets[p+1] - 500) + return Rune(c) -proc toUpper*(c: TRune): TRune {.rtl, extern: "nuc$1", procvar.} = +proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = ## Converts `c` into upper case. This works for any Unicode character. ## If possible, prefer `toLower` over `toUpper`. - var c = IRune(c) + var c = RuneImpl(c) var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3) if p >= 0 and c >= toupperRanges[p] and c <= toupperRanges[p+1]: - return TRune(c + toupperRanges[p+2] - 500) + return Rune(c + toupperRanges[p+2] - 500) p = binarySearch(c, toupperSinglets, len(toupperSinglets) div 2, 2) if p >= 0 and c == toupperSinglets[p]: - return TRune(c + toupperSinglets[p+1] - 500) - return TRune(c) + return Rune(c + toupperSinglets[p+1] - 500) + return Rune(c) -proc toTitle*(c: TRune): TRune {.rtl, extern: "nuc$1", procvar.} = - var c = IRune(c) +proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = + var c = RuneImpl(c) var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2) if p >= 0 and c == toTitleSinglets[p]: - return TRune(c + toTitleSinglets[p+1] - 500) - return TRune(c) + return Rune(c + toTitleSinglets[p+1] - 500) + return Rune(c) -proc isLower*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} = +proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = ## returns true iff `c` is a lower case Unicode character ## If possible, prefer `isLower` over `isUpper`. - var c = IRune(c) + var c = RuneImpl(c) # Note: toUpperRanges is correct here! var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3) if p >= 0 and c >= toupperRanges[p] and c <= toupperRanges[p+1]: @@ -1158,10 +1160,10 @@ proc isLower*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} = if p >= 0 and c == toupperSinglets[p]: return true -proc isUpper*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} = +proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = ## returns true iff `c` is a upper case Unicode character ## If possible, prefer `isLower` over `isUpper`. - var c = IRune(c) + var c = RuneImpl(c) # Note: toLowerRanges is correct here! var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3) if p >= 0 and c >= tolowerRanges[p] and c <= tolowerRanges[p+1]: @@ -1170,11 +1172,11 @@ proc isUpper*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} = if p >= 0 and c == tolowerSinglets[p]: return true -proc isAlpha*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} = +proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = ## returns true iff `c` is an *alpha* Unicode character (i.e. a letter) if isUpper(c) or isLower(c): return true - var c = IRune(c) + var c = RuneImpl(c) var p = binarySearch(c, alphaRanges, len(alphaRanges) div 2, 2) if p >= 0 and c >= alphaRanges[p] and c <= alphaRanges[p+1]: return true @@ -1182,21 +1184,21 @@ proc isAlpha*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} = if p >= 0 and c == alphaSinglets[p]: return true -proc isTitle*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} = +proc isTitle*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = return isUpper(c) and isLower(c) -proc isWhiteSpace*(c: TRune): bool {.rtl, extern: "nuc$1", procvar.} = +proc isWhiteSpace*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = ## returns true iff `c` is a Unicode whitespace character - var c = IRune(c) + var c = RuneImpl(c) var p = binarySearch(c, spaceRanges, len(spaceRanges) div 2, 2) if p >= 0 and c >= spaceRanges[p] and c <= spaceRanges[p+1]: return true -iterator runes*(s: string): TRune = +iterator runes*(s: string): Rune = ## iterates over any unicode character of the string `s`. var i = 0 - result: TRune + result: Rune while i < len(s): fastRuneAt(s, i, result, true) yield result @@ -1209,12 +1211,12 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} = ## | > 0 iff a > b var i = 0 var j = 0 - var ar, br: TRune + var ar, br: Rune while i < a.len and j < b.len: # slow path: fastRuneAt(a, i, ar) fastRuneAt(b, j, br) - result = IRune(toLower(ar)) - IRune(toLower(br)) + result = RuneImpl(toLower(ar)) - RuneImpl(toLower(br)) if result != 0: return result = a.len - b.len diff --git a/lib/pure/unidecode/unidecode.nim b/lib/pure/unidecode/unidecode.nim index 1d36d11b5..798eef5d0 100644 --- a/lib/pure/unidecode/unidecode.nim +++ b/lib/pure/unidecode/unidecode.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -55,7 +55,7 @@ proc unidecode*(s: string): string = ## ## Example: ## - ## ..code-block:: nimrod + ## ..code-block:: nim ## ## unidecode("\x53\x17\x4E\xB0") ## diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 7cc95f0ad..21efea3bc 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -1,7 +1,7 @@ # # -# Nimrod's Runtime Library -# (c) Copyright 2012 Nimrod Contributors +# Nim's Runtime Library +# (c) Copyright 2012 Nim Contributors # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -26,19 +26,22 @@ when not defined(ECMAScript): import terminal type - TTestStatus* = enum OK, FAILED - TOutputLevel* = enum PRINT_ALL, PRINT_FAILURES, PRINT_NONE + TestStatus* = enum OK, FAILED + OutputLevel* = enum PRINT_ALL, PRINT_FAILURES, PRINT_NONE + +{.deprecated: [TTestStatus: TestStatus, TOutputLevel: OutputLevel]} var - # XXX: These better be thread-local - AbortOnError*: bool - OutputLevel*: TOutputLevel - ColorOutput*: bool - - checkpoints: seq[string] = @[] + abortOnError* {.threadvar.}: bool + outputLevel* {.threadvar.}: OutputLevel + colorOutput* {.threadvar.}: bool -template TestSetupIMPL*: stmt {.immediate, dirty.} = discard -template TestTeardownIMPL*: stmt {.immediate, dirty.} = discard + checkpoints {.threadvar.}: seq[string] + +checkpoints = @[] + +template testSetupIMPL*: stmt {.immediate, dirty.} = discard +template testTeardownIMPL*: stmt {.immediate, dirty.} = discard proc shouldRun(testName: string): bool = result = true @@ -46,37 +49,37 @@ proc shouldRun(testName: string): bool = template suite*(name: expr, body: stmt): stmt {.immediate, dirty.} = block: template setup*(setupBody: stmt): stmt {.immediate, dirty.} = - template TestSetupIMPL: stmt {.immediate, dirty.} = setupBody + template testSetupIMPL: stmt {.immediate, dirty.} = setupBody template teardown*(teardownBody: stmt): stmt {.immediate, dirty.} = - template TestTeardownIMPL: stmt {.immediate, dirty.} = teardownBody + template testTeardownIMPL: stmt {.immediate, dirty.} = teardownBody body -proc testDone(name: string, s: TTestStatus) = +proc testDone(name: string, s: TestStatus) = if s == FAILED: - program_result += 1 + programResult += 1 - if OutputLevel != PRINT_NONE and (OutputLevel == PRINT_ALL or s == FAILED): - template rawPrint() = echo("[", $s, "] ", name, "\n") + if outputLevel != PRINT_NONE and (outputLevel == PRINT_ALL or s == FAILED): + template rawPrint() = echo("[", $s, "] ", name) when not defined(ECMAScript): - if ColorOutput and not defined(ECMAScript): + if colorOutput and not defined(ECMAScript): var color = (if s == OK: fgGreen else: fgRed) - styledEcho styleBright, color, "[", $s, "] ", fgWhite, name, "\n" + styledEcho styleBright, color, "[", $s, "] ", fgWhite, name else: rawPrint() else: rawPrint() - + template test*(name: expr, body: stmt): stmt {.immediate, dirty.} = bind shouldRun, checkpoints, testDone if shouldRun(name): checkpoints = @[] - var TestStatusIMPL {.inject.} = OK - + var testStatusIMPL {.inject.} = OK + try: - TestSetupIMPL() + testSetupIMPL() body except: @@ -84,8 +87,8 @@ template test*(name: expr, body: stmt): stmt {.immediate, dirty.} = fail() finally: - TestTeardownIMPL() - testDone name, TestStatusIMPL + testTeardownIMPL() + testDone name, testStatusIMPL proc checkpoint*(msg: string) = checkpoints.add(msg) @@ -94,21 +97,23 @@ proc checkpoint*(msg: string) = template fail* = bind checkpoints for msg in items(checkpoints): - echo msg + # this used to be 'echo' which now breaks due to a bug. XXX will revisit + # this issue later. + stdout.writeln msg when not defined(ECMAScript): - if AbortOnError: quit(1) - - when declared(TestStatusIMPL): - TestStatusIMPL = FAILED + if abortOnError: quit(1) + + when declared(testStatusIMPL): + testStatusIMPL = FAILED else: - program_result += 1 + programResult += 1 checkpoints = @[] macro check*(conditions: stmt): stmt {.immediate.} = let checked = callsite()[1] - + var argsAsgns = newNimNode(nnkStmtList) argsPrintOuts = newNimNode(nnkStmtList) @@ -117,7 +122,7 @@ macro check*(conditions: stmt): stmt {.immediate.} = template asgn(a, value: expr): stmt = var a = value # XXX: we need "var: var" here in order to # preserve the semantics of var params - + template print(name, value: expr): stmt = when compiles(string($value)): checkpoint(name & " was " & $value) @@ -143,7 +148,7 @@ macro check*(conditions: stmt): stmt {.immediate.} = checkpoint(lineInfoLit & ": Check failed: " & callLit) argPrintOuts fail() - + var checkedStr = checked.toStrLit inspectArgs(checked) result = getAst(rewrite(checked, checked.lineinfo, checkedStr, @@ -192,15 +197,15 @@ when declared(stdout): ## Reading settings var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string - AbortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR") - ColorOutput = not existsEnv("NIMTEST_NO_COLOR") + abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR") + colorOutput = not existsEnv("NIMTEST_NO_COLOR") else: var envOutLvl = "" # TODO - ColorOutput = false + colorOutput = false if envOutLvl.len > 0: - for opt in countup(low(TOutputLevel), high(TOutputLevel)): + for opt in countup(low(OutputLevel), high(OutputLevel)): if $opt == envOutLvl: - OutputLevel = opt + outputLevel = opt break diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index 7ec823033..368802dc2 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Dominik Picheta # # See the file "copying.txt", included in this @@ -11,20 +11,22 @@ import strutils, parseutils type - TUrl* = distinct string + Url* = distinct string - TUri* = object + Uri* = object scheme*, username*, password*: string hostname*, port*, path*, query*, anchor*: string -proc `$`*(url: TUrl): string {.deprecated.} = - ## **Deprecated since 0.9.6**: Use ``TUri`` instead. +{.deprecated: [TUrl: Url, TUri: Uri].} + +proc `$`*(url: Url): string {.deprecated.} = + ## **Deprecated since 0.9.6**: Use ``Uri`` instead. return string(url) -proc `/`*(a, b: TUrl): TUrl {.deprecated.} = +proc `/`*(a, b: Url): Url {.deprecated.} = ## Joins two URLs together, separating them with / if needed. ## - ## **Deprecated since 0.9.6**: Use ``TUri`` instead. + ## **Deprecated since 0.9.6**: Use ``Uri`` instead. var urlS = $a var bS = $b if urlS == "": return b @@ -34,15 +36,15 @@ proc `/`*(a, b: TUrl): TUrl {.deprecated.} = urlS.add(bS.substr(1)) else: urlS.add(bs) - result = TUrl(urlS) + result = Url(urlS) -proc add*(url: var TUrl, a: TUrl) {.deprecated.} = +proc add*(url: var Url, a: Url) {.deprecated.} = ## Appends url to url. ## - ## **Deprecated since 0.9.6**: Use ``TUri`` instead. + ## **Deprecated since 0.9.6**: Use ``Uri`` instead. url = url / a -proc parseAuthority(authority: string, result: var TUri) = +proc parseAuthority(authority: string, result: var Uri) = var i = 0 var inPort = false while true: @@ -63,12 +65,12 @@ proc parseAuthority(authority: string, result: var TUri) = result.hostname.add(authority[i]) i.inc -proc parsePath(uri: string, i: var int, result: var TUri) = +proc parsePath(uri: string, i: var int, result: var Uri) = i.inc parseUntil(uri, result.path, {'?', '#'}, i) # The 'mailto' scheme's PATH actually contains the hostname/username - if result.scheme.ToLower() == "mailto": + if result.scheme.toLower == "mailto": parseAuthority(result.path, result) result.path = "" @@ -80,11 +82,11 @@ proc parsePath(uri: string, i: var int, result: var TUri) = i.inc # Skip '#' i.inc parseUntil(uri, result.anchor, {}, i) -proc initUri(): TUri = - result = TUri(scheme: "", username: "", password: "", hostname: "", port: "", +proc initUri(): Uri = + result = Uri(scheme: "", username: "", password: "", hostname: "", port: "", path: "", query: "", anchor: "") -proc parseUri*(uri: string): TUri = +proc parseUri*(uri: string): Uri = ## Parses a URI. result = initUri() @@ -111,7 +113,7 @@ proc parseUri*(uri: string): TUri = var authority = "" i.inc parseUntil(uri, authority, {'/', '?', '#'}, i) if authority == "": - raise newException(EInvalidValue, "Expected authority got nothing.") + raise newException(ValueError, "Expected authority got nothing.") parseAuthority(authority, result) # Path @@ -148,7 +150,7 @@ proc removeDotSegments(path: string): string = result = collection.join("/") if endsWithSlash: result.add '/' -proc merge(base, reference: TUri): string = +proc merge(base, reference: Uri): string = # http://tools.ietf.org/html/rfc3986#section-5.2.3 if base.hostname != "" and base.path == "": '/' & reference.path @@ -159,7 +161,7 @@ proc merge(base, reference: TUri): string = else: base.path[0 .. lastSegment] & reference.path -proc combine*(base: TUri, reference: TUri): TUri = +proc combine*(base: Uri, reference: Uri): Uri = ## Combines a base URI with a reference URI. ## ## This uses the algorithm specified in @@ -172,7 +174,7 @@ proc combine*(base: TUri, reference: TUri): TUri = ## ## Examples: ## - ## .. code-block:: nimrod + ## .. code-block:: ## let foo = combine(parseUri("http://example.com/foo/bar"), parseUri("/baz")) ## assert foo.path == "/baz" ## @@ -214,13 +216,13 @@ proc combine*(base: TUri, reference: TUri): TUri = result.scheme = base.scheme result.anchor = reference.anchor -proc combine*(uris: varargs[TUri]): TUri = +proc combine*(uris: varargs[Uri]): Uri = ## Combines multiple URIs together. result = uris[0] for i in 1 .. 0: diff --git a/lib/pure/xmldom.nim b/lib/pure/xmldom.nim index d63b6c5dd..660932d92 100644 --- a/lib/pure/xmldom.nim +++ b/lib/pure/xmldom.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2010 Dominik Picheta # # See the file "copying.txt", included in this @@ -9,14 +9,15 @@ import strutils -## This module implements XML DOM Level 2 Core specification(http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html) +## This module implements XML DOM Level 2 Core +## specification (http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html) #http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html #Exceptions type - EDOMException* = object of EInvalidValue ## Base exception object for all DOM Exceptions + EDOMException* = object of ValueError ## Base exception object for all DOM Exceptions EDOMStringSizeErr* = object of EDOMException ## If the specified range of text does not fit into a DOMString ## Currently not used(Since DOMString is just string) EHierarchyRequestErr* = object of EDOMException ## If any node is inserted somewhere it doesn't belong @@ -55,24 +56,24 @@ type Feature = tuple[name: string, version: string] PDOMImplementation* = ref DOMImplementation DOMImplementation = object - Features: seq[Feature] # Read-Only + features: seq[Feature] # Read-Only PNode* = ref Node - Node = object of TObject + Node = object of RootObj attributes*: seq[PAttr] childNodes*: seq[PNode] - FLocalName: string # Read-only - FNamespaceURI: string # Read-only - FNodeName: string # Read-only + fLocalName: string # Read-only + fNamespaceURI: string # Read-only + fNodeName: string # Read-only nodeValue*: string - FNodeType: int # Read-only - FOwnerDocument: PDocument # Read-Only - FParentNode: PNode # Read-Only + fNodeType: int # Read-only + fOwnerDocument: PDocument # Read-Only + fParentNode: PNode # Read-Only prefix*: string # Setting this should change some values... TODO! PElement* = ref Element Element = object of Node - FTagName: string # Read-only + fTagName: string # Read-only PCharacterData* = ref CharacterData CharacterData = object of Node @@ -80,15 +81,15 @@ type PDocument* = ref Document Document = object of Node - FImplementation: PDOMImplementation # Read-only - FDocumentElement: PElement # Read-only + fImplementation: PDOMImplementation # Read-only + fDocumentElement: PElement # Read-only PAttr* = ref Attr Attr = object of Node - FName: string # Read-only - FSpecified: bool # Read-only + fName: string # Read-only + fSpecified: bool # Read-only value*: string - FOwnerElement: PElement # Read-only + fOwnerElement: PElement # Read-only PDocumentFragment* = ref DocumentFragment DocumentFragment = object of Node @@ -96,7 +97,7 @@ type PText* = ref Text Text = object of CharacterData - PComment* = ref comment + PComment* = ref Comment Comment = object of CharacterData PCDataSection* = ref CDataSection @@ -105,13 +106,13 @@ type PProcessingInstruction* = ref ProcessingInstruction ProcessingInstruction = object of Node data*: string - FTarget: string # Read-only + fTarget: string # Read-only # DOMImplementation proc getDOM*(): PDOMImplementation = ## Returns a DOMImplementation new(result) - result.Features = @[(name: "core", version: "2.0"), + result.features = @[(name: "core", version: "2.0"), (name: "core", version: "1.0"), (name: "XML", version: "2.0")] @@ -119,15 +120,15 @@ proc createDocument*(dom: PDOMImplementation, namespaceURI: string, qualifiedNam ## Creates an XML Document object of the specified type with its document element. var doc: PDocument new(doc) - doc.FNamespaceURI = namespaceURI - doc.FImplementation = dom + doc.fNamespaceURI = namespaceURI + doc.fImplementation = dom var elTag: PElement new(elTag) - elTag.FTagName = qualifiedName - elTag.FNodeName = qualifiedName - doc.FDocumentElement = elTag - doc.FNodeType = DocumentNode + elTag.fTagName = qualifiedName + elTag.fNodeName = qualifiedName + doc.fDocumentElement = elTag + doc.fNodeType = DocumentNode return doc @@ -137,32 +138,32 @@ proc createDocument*(dom: PDOMImplementation, n: PElement): PDocument = # This procedure is not in the specification, it's provided for the parser. var doc: PDocument new(doc) - doc.FDocumentElement = n - doc.FImplementation = dom - doc.FNodeType = DocumentNode + doc.fDocumentElement = n + doc.fImplementation = dom + doc.fNodeType = DocumentNode return doc proc hasFeature*(dom: PDOMImplementation, feature: string, version: string = ""): bool = ## Returns ``true`` if this ``version`` of the DomImplementation implements ``feature``, otherwise ``false`` - for iName, iVersion in items(dom.Features): + for iName, iVersion in items(dom.features): if iName == feature: if version == "": - return True + return true else: if iVersion == version: - return True - return False + return true + return false # Document # Attributes proc implementation*(doc: PDocument): PDOMImplementation = - return doc.FImplementation + return doc.fImplementation proc documentElement*(doc: PDocument): PElement = - return doc.FDocumentElement + return doc.fDocumentElement # Internal procedures proc findNodes(nl: PNode, name: string): seq[PNode] = @@ -172,8 +173,8 @@ proc findNodes(nl: PNode, name: string): seq[PNode] = if nl.childNodes.len() == 0: return @[] for i in items(nl.childNodes): - if i.FNodeType == ElementNode: - if i.FNodeName == name or name == "*": + if i.fNodeType == ElementNode: + if i.fNodeName == name or name == "*": r.add(i) if not isNil(i.childNodes): @@ -189,8 +190,8 @@ proc findNodesNS(nl: PNode, namespaceURI: string, localName: string): seq[PNode] if nl.childNodes.len() == 0: return @[] for i in items(nl.childNodes): - if i.FNodeType == ElementNode: - if (i.FNamespaceURI == namespaceURI or namespaceURI == "*") and (i.FLocalName == localName or localName == "*"): + if i.fNodeType == ElementNode: + if (i.fNamespaceURI == namespaceURI or namespaceURI == "*") and (i.fLocalName == localName or localName == "*"): r.add(i) if not isNil(i.childNodes): @@ -209,16 +210,16 @@ proc createAttribute*(doc: PDocument, name: string): PAttr = if illegalChars in name: raise newException(EInvalidCharacterErr, "Invalid character") - var AttrNode: PAttr - new(AttrNode) - AttrNode.FName = name - AttrNode.FNodeName = name - AttrNode.FLocalName = nil - AttrNode.prefix = nil - AttrNode.FNamespaceURI = nil - AttrNode.value = "" - AttrNode.FSpecified = False - return AttrNode + var attrNode: PAttr + new(attrNode) + attrNode.fName = name + attrNode.fNodeName = name + attrNode.fLocalName = nil + attrNode.prefix = nil + attrNode.fNamespaceURI = nil + attrNode.value = "" + attrNode.fSpecified = false + return attrNode proc createAttributeNS*(doc: PDocument, namespaceURI: string, qualifiedName: string): PAttr = ## Creates an attribute of the given qualified name and namespace URI @@ -237,48 +238,48 @@ proc createAttributeNS*(doc: PDocument, namespaceURI: string, qualifiedName: str raise newException(ENamespaceErr, "When the namespace prefix is \"xmlns\" namespaceURI has to be \"http://www.w3.org/2000/xmlns/\"") - var AttrNode: PAttr - new(AttrNode) - AttrNode.FName = qualifiedName - AttrNode.FNodeName = qualifiedName - AttrNode.FSpecified = False - AttrNode.FNamespaceURI = namespaceURI + var attrNode: PAttr + new(attrNode) + attrNode.fName = qualifiedName + attrNode.fNodeName = qualifiedName + attrNode.fSpecified = false + attrNode.fNamespaceURI = namespaceURI if qualifiedName.contains(':'): - AttrNode.prefix = qualifiedName.split(':')[0] - AttrNode.FLocalName = qualifiedName.split(':')[1] + attrNode.prefix = qualifiedName.split(':')[0] + attrNode.fLocalName = qualifiedName.split(':')[1] else: - AttrNode.prefix = nil - AttrNode.FLocalName = qualifiedName - AttrNode.value = "" + attrNode.prefix = nil + attrNode.fLocalName = qualifiedName + attrNode.value = "" - AttrNode.FNodeType = AttributeNode - return AttrNode + attrNode.fNodeType = AttributeNode + return attrNode -proc createCDATASection*(doc: PDocument, data: string): PCDATASection = +proc createCDATASection*(doc: PDocument, data: string): PCDataSection = ## Creates a CDATASection node whose value is the specified string. - var CData: PCDATASection - new(CData) - CData.data = data - CData.nodeValue = data - CData.FNodeName = "#text" # Not sure about this, but this is technically a TextNode - CData.FNodeType = CDataSectionNode - return CData + var cData: PCDataSection + new(cData) + cData.data = data + cData.nodeValue = data + cData.fNodeName = "#text" # Not sure about this, but this is technically a TextNode + cData.fNodeType = CDataSectionNode + return cData proc createComment*(doc: PDocument, data: string): PComment = ## Creates a Comment node given the specified string. - var Comm: PComment - new(Comm) - Comm.data = data - Comm.nodeValue = data + var comm: PComment + new(comm) + comm.data = data + comm.nodeValue = data - Comm.FNodeType = CommentNode - return Comm + comm.fNodeType = CommentNode + return comm proc createDocumentFragment*(doc: PDocument): PDocumentFragment = ## Creates an empty DocumentFragment object. - var DF: PDocumentFragment - new(DF) - return DF + var df: PDocumentFragment + new(df) + return df proc createElement*(doc: PDocument, tagName: string): PElement = ## Creates an element of the type specified. @@ -289,22 +290,22 @@ proc createElement*(doc: PDocument, tagName: string): PElement = var elNode: PElement new(elNode) - elNode.FTagName = tagName - elNode.FNodeName = tagName - elNode.FLocalName = nil + elNode.fTagName = tagName + elNode.fNodeName = tagName + elNode.fLocalName = nil elNode.prefix = nil - elNode.FNamespaceURI = nil + elNode.fNamespaceURI = nil elNode.childNodes = @[] elNode.attributes = @[] - elNode.FNodeType = ElementNode + elNode.fNodeType = ElementNode return elNode proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: string): PElement = ## Creates an element of the given qualified name and namespace URI. if qualifiedName.contains(':'): - if isNIl(namespaceURI): + if isNil(namespaceURI): raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil") elif qualifiedName.split(':')[0].toLower() == "xml" and namespaceURI != "http://www.w3.org/XML/1998/namespace": raise newException(ENamespaceErr, @@ -316,19 +317,19 @@ proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: strin var elNode: PElement new(elNode) - elNode.FTagName = qualifiedName - elNode.FNodeName = qualifiedName + elNode.fTagName = qualifiedName + elNode.fNodeName = qualifiedName if qualifiedName.contains(':'): elNode.prefix = qualifiedName.split(':')[0] - elNode.FLocalName = qualifiedName.split(':')[1] + elNode.fLocalName = qualifiedName.split(':')[1] else: elNode.prefix = nil - elNode.FLocalName = qualifiedName - elNode.FNamespaceURI = namespaceURI + elNode.fLocalName = qualifiedName + elNode.fNamespaceURI = namespaceURI elNode.childNodes = @[] elNode.attributes = @[] - elNode.FNodeType = ElementNode + elNode.fNodeType = ElementNode return elNode @@ -339,12 +340,12 @@ proc createProcessingInstruction*(doc: PDocument, target: string, data: string): if illegalChars in target: raise newException(EInvalidCharacterErr, "Invalid character") - var PI: PProcessingInstruction - new(PI) - PI.FTarget = target - PI.data = data - PI.FNodeType = ProcessingInstructionNode - return PI + var pi: PProcessingInstruction + new(pi) + pi.fTarget = target + pi.data = data + pi.fNodeType = ProcessingInstructionNode + return pi proc createTextNode*(doc: PDocument, data: string): PText = #Propably TextNode ## Creates a Text node given the specified string. @@ -352,9 +353,9 @@ proc createTextNode*(doc: PDocument, data: string): PText = #Propably TextNode new(txtNode) txtNode.data = data txtNode.nodeValue = data - txtNode.FNodeName = "#text" + txtNode.fNodeName = "#text" - txtNode.FNodeType = TextNode + txtNode.fNodeType = TextNode return txtNode discard """proc getElementById*(doc: PDocument, elementId: string): PElement = @@ -365,42 +366,42 @@ proc getElementsByTagName*(doc: PDocument, tagName: string): seq[PNode] = ## Returns a NodeList of all the Elements with a given tag name in ## the order in which they are encountered in a preorder traversal of the Document tree. var result: seq[PNode] = @[] - if doc.FDocumentElement.FNodeName == tagName or tagName == "*": - result.add(doc.FDocumentElement) + if doc.fDocumentElement.fNodeName == tagName or tagName == "*": + result.add(doc.fDocumentElement) - result.add(doc.FDocumentElement.findNodes(tagName)) + result.add(doc.fDocumentElement.findNodes(tagName)) return result proc getElementsByTagNameNS*(doc: PDocument, namespaceURI: string, localName: string): seq[PNode] = ## Returns a NodeList of all the Elements with a given localName and namespaceURI ## in the order in which they are encountered in a preorder traversal of the Document tree. var result: seq[PNode] = @[] - if doc.FDocumentElement.FLocalName == localName or localName == "*": - if doc.FDocumentElement.FNamespaceURI == namespaceURI or namespaceURI == "*": - result.add(doc.FDocumentElement) + if doc.fDocumentElement.fLocalName == localName or localName == "*": + if doc.fDocumentElement.fNamespaceURI == namespaceURI or namespaceURI == "*": + result.add(doc.fDocumentElement) - result.add(doc.FDocumentElement.findNodesNS(namespaceURI, localName)) + result.add(doc.fDocumentElement.findNodesNS(namespaceURI, localName)) return result proc importNode*(doc: PDocument, importedNode: PNode, deep: bool): PNode = ## Imports a node from another document to this document - case importedNode.FNodeType + case importedNode.fNodeType of AttributeNode: var nAttr: PAttr = PAttr(importedNode) - nAttr.FOwnerDocument = doc - nAttr.FParentNode = nil - nAttr.FOwnerElement = nil - nAttr.FSpecified = True + nAttr.fOwnerDocument = doc + nAttr.fParentNode = nil + nAttr.fOwnerElement = nil + nAttr.fSpecified = true return nAttr of DocumentFragmentNode: var n: PNode new(n) n = importedNode - n.FOwnerDocument = doc - n.FParentNode = nil + n.fOwnerDocument = doc + n.fParentNode = nil - n.FOwnerDocument = doc - n.FParentNode = nil + n.fOwnerDocument = doc + n.fParentNode = nil var tmp: seq[PNode] = n.childNodes n.childNodes = @[] if deep: @@ -412,8 +413,8 @@ proc importNode*(doc: PDocument, importedNode: PNode, deep: bool): PNode = var n: PNode new(n) n = importedNode - n.FOwnerDocument = doc - n.FParentNode = nil + n.fOwnerDocument = doc + n.fParentNode = nil var tmpA: seq[PAttr] = n.attributes n.attributes = @[] @@ -432,8 +433,8 @@ proc importNode*(doc: PDocument, importedNode: PNode, deep: bool): PNode = var n: PNode new(n) n = importedNode - n.FOwnerDocument = doc - n.FParentNode = nil + n.fOwnerDocument = doc + n.fParentNode = nil return n else: raise newException(ENotSupportedErr, "The type of node being imported is not supported") @@ -461,58 +462,58 @@ proc lastChild*(n: PNode): PNode = proc localName*(n: PNode): string = ## Returns this nodes local name - return n.FLocalName + return n.fLocalName proc namespaceURI*(n: PNode): string = ## Returns this nodes namespace URI - return n.FNamespaceURI + return n.fNamespaceURI proc `namespaceURI=`*(n: PNode, value: string) = - n.FNamespaceURI = value + n.fNamespaceURI = value proc nextSibling*(n: PNode): PNode = ## Returns the next sibling of this node - if isNil(n.FParentNode) or isNil(n.FParentNode.childNodes): + if isNil(n.fParentNode) or isNil(n.fParentNode.childNodes): return nil - var nLow: int = low(n.FParentNode.childNodes) - var nHigh: int = high(n.FParentNode.childNodes) + var nLow: int = low(n.fParentNode.childNodes) + var nHigh: int = high(n.fParentNode.childNodes) for i in nLow..nHigh: - if n.FParentNode.childNodes[i] == n: - return n.FParentNode.childNodes[i + 1] + if n.fParentNode.childNodes[i] == n: + return n.fParentNode.childNodes[i + 1] return nil proc nodeName*(n: PNode): string = ## Returns the name of this node - return n.FNodeName + return n.fNodeName proc nodeType*(n: PNode): int = ## Returns the type of this node - return n.FNodeType + return n.fNodeType proc ownerDocument*(n: PNode): PDocument = ## Returns the owner document of this node - return n.FOwnerDocument + return n.fOwnerDocument proc parentNode*(n: PNode): PNode = ## Returns the parent node of this node - return n.FParentNode + return n.fParentNode proc previousSibling*(n: PNode): PNode = ## Returns the previous sibling of this node - if isNil(n.FParentNode) or isNil(n.FParentNode.childNodes): + if isNil(n.fParentNode) or isNil(n.fParentNode.childNodes): return nil - var nLow: int = low(n.FParentNode.childNodes) - var nHigh: int = high(n.FParentNode.childNodes) + var nLow: int = low(n.fParentNode.childNodes) + var nHigh: int = high(n.fParentNode.childNodes) for i in nLow..nHigh: - if n.FParentNode.childNodes[i] == n: - return n.FParentNode.childNodes[i - 1] + if n.fParentNode.childNodes[i] == n: + return n.fParentNode.childNodes[i - 1] return nil proc `prefix=`*(n: PNode, value: string) = @@ -523,25 +524,25 @@ proc `prefix=`*(n: PNode, value: string) = if illegalChars in value: raise newException(EInvalidCharacterErr, "Invalid character") - if isNil(n.FNamespaceURI): + if isNil(n.fNamespaceURI): raise newException(ENamespaceErr, "namespaceURI cannot be nil") - elif value.toLower() == "xml" and n.FNamespaceURI != "http://www.w3.org/XML/1998/namespace": + elif value.toLower() == "xml" and n.fNamespaceURI != "http://www.w3.org/XML/1998/namespace": raise newException(ENamespaceErr, "When the namespace prefix is \"xml\" namespaceURI has to be \"http://www.w3.org/XML/1998/namespace\"") - elif value.toLower() == "xmlns" and n.FNamespaceURI != "http://www.w3.org/2000/xmlns/": + elif value.toLower() == "xmlns" and n.fNamespaceURI != "http://www.w3.org/2000/xmlns/": raise newException(ENamespaceErr, "When the namespace prefix is \"xmlns\" namespaceURI has to be \"http://www.w3.org/2000/xmlns/\"") - elif value.toLower() == "xmlns" and n.FNodeType == AttributeNode: + elif value.toLower() == "xmlns" and n.fNodeType == AttributeNode: raise newException(ENamespaceErr, "An AttributeNode cannot have a prefix of \"xmlns\"") - n.FNodeName = value & ":" & n.FLocalName + n.fNodeName = value & ":" & n.fLocalName if n.nodeType == ElementNode: var el: PElement = PElement(n) - el.FTagName = value & ":" & n.FLocalName + el.fTagName = value & ":" & n.fLocalName elif n.nodeType == AttributeNode: var attr: PAttr = PAttr(n) - attr.FName = value & ":" & n.FLocalName + attr.fName = value & ":" & n.fLocalName # Procedures proc appendChild*(n: PNode, newChild: PNode) = @@ -555,7 +556,7 @@ proc appendChild*(n: PNode, newChild: PNode) = raise newException(EHierarchyRequestErr, "The node to append is already in this nodes children.") # Check if newChild is from this nodes document - if n.FOwnerDocument != newChild.FOwnerDocument: + if n.fOwnerDocument != newChild.fOwnerDocument: raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.") if n == newChild: @@ -566,7 +567,7 @@ proc appendChild*(n: PNode, newChild: PNode) = if isNil(n.childNodes): n.childNodes = @[] - newChild.FParentNode = n + newChild.fParentNode = n for i in low(n.childNodes)..high(n.childNodes): if n.childNodes[i] == newChild: n.childNodes[i] = newChild @@ -575,13 +576,13 @@ proc appendChild*(n: PNode, newChild: PNode) = proc cloneNode*(n: PNode, deep: bool): PNode = ## Returns a duplicate of this node, if ``deep`` is `true`, Element node's children are copied - case n.FNodeType + case n.fNodeType of AttributeNode: var newNode: PAttr new(newNode) newNode = PAttr(n) - newNode.FSpecified = True - newNode.FOwnerElement = nil + newNode.fSpecified = true + newNode.fOwnerElement = nil return newNode of ElementNode: var newNode: PElement @@ -613,32 +614,32 @@ proc insertBefore*(n: PNode, newChild: PNode, refChild: PNode): PNode = ## If ``refChild`` is nil, insert ``newChild`` at the end of the list of children. # Check if newChild is from this nodes document - if n.FOwnerDocument != newChild.FOwnerDocument: + if n.fOwnerDocument != newChild.fOwnerDocument: raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.") if isNil(n.childNodes): - n.ChildNodes = @[] + n.childNodes = @[] for i in low(n.childNodes)..high(n.childNodes): if n.childNodes[i] == refChild: n.childNodes.insert(newChild, i - 1) return - n.ChildNodes.add(newChild) + n.childNodes.add(newChild) proc isSupported*(n: PNode, feature: string, version: string): bool = ## Tests whether the DOM implementation implements a specific ## feature and that feature is supported by this node. - return n.FOwnerDocument.FImplementation.hasFeature(feature, version) + return n.fOwnerDocument.fImplementation.hasFeature(feature, version) proc isEmpty(s: string): bool = if isNil(s) or s == "": - return True + return true for i in items(s): if i != ' ': - return False - return True + return false + return true proc normalize*(n: PNode) = ## Merges all seperated TextNodes together, and removes any empty TextNodes @@ -646,7 +647,7 @@ proc normalize*(n: PNode) = var i: int = 0 var newChildNodes: seq[PNode] = @[] - while True: + while true: if isNil(n.childNodes) or i >= n.childNodes.len: break if n.childNodes[i].nodeType == TextNode: @@ -684,7 +685,7 @@ proc replaceChild*(n: PNode, newChild: PNode, oldChild: PNode): PNode = ## Replaces the child node ``oldChild`` with ``newChild`` in the list of children, and returns the ``oldChild`` node. # Check if newChild is from this nodes document - if n.FOwnerDocument != newChild.FOwnerDocument: + if n.fOwnerDocument != newChild.fOwnerDocument: raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.") if not isNil(n.childNodes): @@ -698,159 +699,159 @@ proc replaceChild*(n: PNode, newChild: PNode, oldChild: PNode): PNode = # NamedNodeMap -proc getNamedItem*(NList: seq[PNode], name: string): PNode = +proc getNamedItem*(nList: seq[PNode], name: string): PNode = ## Retrieves a node specified by ``name``. If this node cannot be found returns ``nil`` - for i in items(NList): + for i in items(nList): if i.nodeName() == name: return i return nil -proc getNamedItem*(NList: seq[PAttr], name: string): PAttr = +proc getNamedItem*(nList: seq[PAttr], name: string): PAttr = ## Retrieves a node specified by ``name``. If this node cannot be found returns ``nil`` - for i in items(NList): + for i in items(nList): if i.nodeName() == name: return i return nil -proc getNamedItemNS*(NList: seq[PNode], namespaceURI: string, localName: string): PNode = +proc getNamedItemNS*(nList: seq[PNode], namespaceURI: string, localName: string): PNode = ## Retrieves a node specified by ``localName`` and ``namespaceURI``. If this node cannot be found returns ``nil`` - for i in items(NList): + for i in items(nList): if i.namespaceURI() == namespaceURI and i.localName() == localName: return i return nil -proc getNamedItemNS*(NList: seq[PAttr], namespaceURI: string, localName: string): PAttr = +proc getNamedItemNS*(nList: seq[PAttr], namespaceURI: string, localName: string): PAttr = ## Retrieves a node specified by ``localName`` and ``namespaceURI``. If this node cannot be found returns ``nil`` - for i in items(NList): - if i.NamespaceURI() == namespaceURI and i.LocalName() == localName: + for i in items(nList): + if i.namespaceURI() == namespaceURI and i.localName() == localName: return i return nil -proc item*(NList: seq[PNode], index: int): PNode = +proc item*(nList: seq[PNode], index: int): PNode = ## Returns the ``index`` th item in the map. ## If ``index`` is greater than or equal to the number of nodes in this map, this returns ``nil``. - if index >= NList.len(): return nil - else: return NList[index] + if index >= nList.len(): return nil + else: return nList[index] -proc removeNamedItem*(NList: var seq[PNode], name: string): PNode = +proc removeNamedItem*(nList: var seq[PNode], name: string): PNode = ## Removes a node specified by ``name`` ## Raises the ``ENotFoundErr`` exception, if the node was not found - for i in low(NList)..high(NList): - if NList[i].FNodeName == name: - result = NList[i] - NList.delete(i) + for i in low(nList)..high(nList): + if nList[i].fNodeName == name: + result = nList[i] + nList.delete(i) return result raise newException(ENotFoundErr, "Node not found") -proc removeNamedItemNS*(NList: var seq[PNode], namespaceURI: string, localName: string): PNode = +proc removeNamedItemNS*(nList: var seq[PNode], namespaceURI: string, localName: string): PNode = ## Removes a node specified by local name and namespace URI - for i in low(NList)..high(NList): - if NList[i].FLocalName == localName and NList[i].FNamespaceURI == namespaceURI: - result = NList[i] - NList.delete(i) + for i in low(nList)..high(nList): + if nList[i].fLocalName == localName and nList[i].fNamespaceURI == namespaceURI: + result = nList[i] + nList.delete(i) return result raise newException(ENotFoundErr, "Node not found") -proc setNamedItem*(NList: var seq[PNode], arg: PNode): PNode = +proc setNamedItem*(nList: var seq[PNode], arg: PNode): PNode = ## Adds ``arg`` as a ``Node`` to the ``NList`` ## If a node with the same name is already present in this map, it is replaced by the new one. - if not isNil(NList): - if NList.len() > 0: + if not isNil(nList): + if nList.len() > 0: #Check if newChild is from this nodes document - if NList[0].FOwnerDocument != arg.FOwnerDocument: + if nList[0].fOwnerDocument != arg.fOwnerDocument: raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.") #Exceptions End - var item: PNode = NList.getNamedItem(arg.NodeName()) + var item: PNode = nList.getNamedItem(arg.nodeName()) if isNil(item): - NList.add(arg) + nList.add(arg) return nil else: # Node with the same name exists var index: int = 0 - for i in low(NList)..high(NList): - if NList[i] == item: + for i in low(nList)..high(nList): + if nList[i] == item: index = i break - NList[index] = arg + nList[index] = arg return item # Return the replaced node -proc setNamedItem*(NList: var seq[PAttr], arg: PAttr): PAttr = +proc setNamedItem*(nList: var seq[PAttr], arg: PAttr): PAttr = ## Adds ``arg`` as a ``Node`` to the ``NList`` ## If a node with the same name is already present in this map, it is replaced by the new one. - if not isNil(NList): - if NList.len() > 0: + if not isNil(nList): + if nList.len() > 0: # Check if newChild is from this nodes document - if NList[0].FOwnerDocument != arg.FOwnerDocument: + if nList[0].fOwnerDocument != arg.fOwnerDocument: raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.") - if not isNil(arg.FOwnerElement): + if not isNil(arg.fOwnerElement): raise newException(EInuseAttributeErr, "This attribute is in use by another element, use cloneNode") # Exceptions end - var item: PAttr = NList.getNamedItem(arg.nodeName()) + var item: PAttr = nList.getNamedItem(arg.nodeName()) if isNil(item): - NList.add(arg) + nList.add(arg) return nil else: # Node with the same name exists var index: int = 0 - for i in low(NList)..high(NList): - if NList[i] == item: + for i in low(nList)..high(nList): + if nList[i] == item: index = i break - NList[index] = arg + nList[index] = arg return item # Return the replaced node -proc setNamedItemNS*(NList: var seq[PNode], arg: PNode): PNode = +proc setNamedItemNS*(nList: var seq[PNode], arg: PNode): PNode = ## Adds a node using its ``namespaceURI`` and ``localName`` - if not isNil(NList): - if NList.len() > 0: + if not isNil(nList): + if nList.len() > 0: # Check if newChild is from this nodes document - if NList[0].FOwnerDocument != arg.FOwnerDocument: + if nList[0].fOwnerDocument != arg.fOwnerDocument: raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.") #Exceptions end - var item: PNode = NList.getNamedItemNS(arg.namespaceURI(), arg.localName()) + var item: PNode = nList.getNamedItemNS(arg.namespaceURI(), arg.localName()) if isNil(item): - NList.add(arg) + nList.add(arg) return nil else: # Node with the same name exists var index: int = 0 - for i in low(NList)..high(NList): - if NList[i] == item: + for i in low(nList)..high(nList): + if nList[i] == item: index = i break - NList[index] = arg + nList[index] = arg return item # Return the replaced node -proc setNamedItemNS*(NList: var seq[PAttr], arg: PAttr): PAttr = +proc setNamedItemNS*(nList: var seq[PAttr], arg: PAttr): PAttr = ## Adds a node using its ``namespaceURI`` and ``localName`` - if not isNil(NList): - if NList.len() > 0: + if not isNil(nList): + if nList.len() > 0: # Check if newChild is from this nodes document - if NList[0].FOwnerDocument != arg.FOwnerDocument: + if nList[0].fOwnerDocument != arg.fOwnerDocument: raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.") - if not isNil(arg.FOwnerElement): + if not isNil(arg.fOwnerElement): raise newException(EInuseAttributeErr, "This attribute is in use by another element, use cloneNode") # Exceptions end - var item: PAttr = NList.getNamedItemNS(arg.namespaceURI(), arg.localName()) + var item: PAttr = nList.getNamedItemNS(arg.namespaceURI(), arg.localName()) if isNil(item): - NList.add(arg) + nList.add(arg) return nil else: # Node with the same name exists var index: int = 0 - for i in low(NList)..high(NList): - if NList[i] == item: + for i in low(nList)..high(nList): + if nList[i] == item: index = i break - NList[index] = arg + nList[index] = arg return item # Return the replaced node # CharacterData - Decided to implement this, @@ -861,17 +862,17 @@ proc setNamedItemNS*(NList: var seq[PAttr], arg: PAttr): PAttr = proc name*(a: PAttr): string = ## Returns the name of the Attribute - return a.FName + return a.fName proc specified*(a: PAttr): bool = ## Specifies whether this attribute was specified in the original document - return a.FSpecified + return a.fSpecified proc ownerElement*(a: PAttr): PElement = ## Returns this Attributes owner element - return a.FOwnerElement + return a.fOwnerElement # Element # Attributes @@ -879,7 +880,7 @@ proc ownerElement*(a: PAttr): PElement = proc tagName*(el: PElement): string = ## Returns the Element Tag Name - return el.FTagName + return el.fTagName # Procedures proc getAttribute*(el: PElement, name: string): string = @@ -945,15 +946,15 @@ proc removeAttribute*(el: PElement, name: string) = ## Removes an attribute by ``name`` if not isNil(el.attributes): for i in low(el.attributes)..high(el.attributes): - if el.attributes[i].FName == name: + if el.attributes[i].fName == name: el.attributes.delete(i) proc removeAttributeNS*(el: PElement, namespaceURI: string, localName: string) = ## Removes an attribute by ``localName`` and ``namespaceURI`` if not isNil(el.attributes): for i in low(el.attributes)..high(el.attributes): - if el.attributes[i].FNamespaceURI == namespaceURI and - el.attributes[i].FLocalName == localName: + if el.attributes[i].fNamespaceURI == namespaceURI and + el.attributes[i].fLocalName == localName: el.attributes.delete(i) proc removeAttributeNode*(el: PElement, oldAttr: PAttr): PAttr = @@ -974,11 +975,11 @@ proc setAttributeNode*(el: PElement, newAttr: PAttr): PAttr = ## returned, otherwise ``nil`` is returned. # Check if newAttr is from this nodes document - if el.FOwnerDocument != newAttr.FOwnerDocument: + if el.fOwnerDocument != newAttr.fOwnerDocument: raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.") - if not isNil(newAttr.FOwnerElement): + if not isNil(newAttr.fOwnerElement): raise newException(EInuseAttributeErr, "This attribute is in use by another element, use cloneNode") # Exceptions end @@ -992,11 +993,11 @@ proc setAttributeNodeNS*(el: PElement, newAttr: PAttr): PAttr = ## and the replaced attribute is returned, otherwise ``nil`` is returned. # Check if newAttr is from this nodes document - if el.FOwnerDocument != newAttr.FOwnerDocument: + if el.fOwnerDocument != newAttr.fOwnerDocument: raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.") - if not isNil(newAttr.FOwnerElement): + if not isNil(newAttr.fOwnerElement): raise newException(EInuseAttributeErr, "This attribute is in use by another element, use cloneNode") # Exceptions end @@ -1010,17 +1011,17 @@ proc setAttribute*(el: PElement, name: string, value: string) = ## value is changed to be that of the value parameter ## Raises the EInvalidCharacterErr if the specified ``name`` contains ## illegal characters - var AttrNode = el.FOwnerDocument.createAttribute(name) + var attrNode = el.fOwnerDocument.createAttribute(name) # Check if name contains illegal characters if illegalChars in name: raise newException(EInvalidCharacterErr, "Invalid character") - discard el.setAttributeNode(AttrNode) + discard el.setAttributeNode(attrNode) # Set the info later, the setAttributeNode checks # if FOwnerElement is nil, and if it isn't it raises an exception - AttrNode.FOwnerElement = el - AttrNode.FSpecified = True - AttrNode.value = value + attrNode.fOwnerElement = el + attrNode.fSpecified = true + attrNode.value = value proc setAttributeNS*(el: PElement, namespaceURI, localName, value: string) = ## Adds a new attribute, as specified by ``namespaceURI``, ``localName`` @@ -1030,43 +1031,43 @@ proc setAttributeNS*(el: PElement, namespaceURI, localName, value: string) = if illegalChars in namespaceURI or illegalChars in localName: raise newException(EInvalidCharacterErr, "Invalid character") - var AttrNode = el.FOwnerDocument.createAttributeNS(namespaceURI, localName) + var attrNode = el.fOwnerDocument.createAttributeNS(namespaceURI, localName) - discard el.setAttributeNodeNS(AttrNode) + discard el.setAttributeNodeNS(attrNode) # Set the info later, the setAttributeNode checks # if FOwnerElement is nil, and if it isn't it raises an exception - AttrNode.FOwnerElement = el - AttrNode.FSpecified = True - AttrNode.value = value + attrNode.fOwnerElement = el + attrNode.fSpecified = true + attrNode.value = value # Text -proc splitData*(TextNode: PText, offset: int): PText = +proc splitData*(textNode: PText, offset: int): PText = ## Breaks this node into two nodes at the specified offset, ## keeping both in the tree as siblings. - if offset > TextNode.data.len(): + if offset > textNode.data.len(): raise newException(EIndexSizeErr, "Index out of bounds") - var left: string = TextNode.data.substr(0, offset) - TextNode.data = left - var right: string = TextNode.data.substr(offset, TextNode.data.len()) + var left: string = textNode.data.substr(0, offset) + textNode.data = left + var right: string = textNode.data.substr(offset, textNode.data.len()) - if not isNil(TextNode.FParentNode) and not isNil(TextNode.FParentNode.childNodes): - for i in low(TextNode.FParentNode.childNodes)..high(TextNode.FParentNode.childNodes): - if TextNode.FParentNode.childNodes[i] == TextNode: - var newNode: PText = TextNode.FOwnerDocument.createTextNode(right) - TextNode.FParentNode.childNodes.insert(newNode, i) + if not isNil(textNode.fParentNode) and not isNil(textNode.fParentNode.childNodes): + for i in low(textNode.fParentNode.childNodes)..high(textNode.fParentNode.childNodes): + if textNode.fParentNode.childNodes[i] == textNode: + var newNode: PText = textNode.fOwnerDocument.createTextNode(right) + textNode.fParentNode.childNodes.insert(newNode, i) return newNode else: - var newNode: PText = TextNode.FOwnerDocument.createTextNode(right) + var newNode: PText = textNode.fOwnerDocument.createTextNode(right) return newNode # ProcessingInstruction -proc target*(PI: PProcessingInstruction): string = +proc target*(pi: PProcessingInstruction): string = ## Returns the Processing Instructions target - return PI.FTarget + return pi.fTarget # --Other stuff-- diff --git a/lib/pure/xmldomparser.nim b/lib/pure/xmldomparser.nim index fda46bac0..7f34d72a8 100644 --- a/lib/pure/xmldomparser.nim +++ b/lib/pure/xmldomparser.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2010 Dominik Picheta # # See the file "copying.txt", included in this @@ -15,13 +15,13 @@ import xmldom, os, streams, parsexml, strutils type # Parsing errors - EMismatchedTag* = object of EInvalidValue ## Raised when a tag is not properly closed - EParserError* = object of EInvalidValue ## Raised when an unexpected XML Parser event occurs + EMismatchedTag* = object of ValueError ## Raised when a tag is not properly closed + EParserError* = object of ValueError ## Raised when an unexpected XML Parser event occurs # For namespaces - xmlnsAttr = tuple[name, value: string, ownerElement: PElement] + XmlnsAttr = tuple[name, value: string, ownerElement: PElement] -var nsList: seq[xmlnsAttr] = @[] # Used for storing namespaces +var nsList: seq[XmlnsAttr] = @[] # Used for storing namespaces proc getNS(prefix: string): string = var defaultNS: seq[string] = @[] @@ -43,13 +43,13 @@ proc getNS(prefix: string): string = return "" -proc parseText(x: var TXmlParser, doc: var PDocument): PText = +proc parseText(x: var XmlParser, doc: var PDocument): PText = result = doc.createTextNode(x.charData()) -proc parseElement(x: var TXmlParser, doc: var PDocument): PElement = +proc parseElement(x: var XmlParser, doc: var PDocument): PElement = var n = doc.createElement("") - while True: + while true: case x.kind() of xmlEof: break @@ -102,10 +102,10 @@ proc parseElement(x: var TXmlParser, doc: var PDocument): PElement = of xmlComment: n.appendChild(doc.createComment(x.charData())) of xmlPI: - n.appendChild(doc.createProcessingInstruction(x.PIName(), x.PIRest())) + n.appendChild(doc.createProcessingInstruction(x.piName(), x.piRest())) of xmlWhitespace, xmlElementClose, xmlEntity, xmlSpecial: - # Unused 'events' + discard " Unused \'events\'" else: raise newException(EParserError, "Unexpected XML Parser event") @@ -114,30 +114,30 @@ proc parseElement(x: var TXmlParser, doc: var PDocument): PElement = raise newException(EMismatchedTag, "Mismatched tag at line " & $x.getLine() & " column " & $x.getColumn) -proc loadXMLStream*(stream: PStream): PDocument = +proc loadXMLStream*(stream: Stream): PDocument = ## Loads and parses XML from a stream specified by ``stream``, and returns ## a ``PDocument`` - var x: TXmlParser + var x: XmlParser open(x, stream, nil, {reportComments}) - var XmlDoc: PDocument - var DOM: PDOMImplementation = getDOM() + var xmlDoc: PDocument + var dom: PDOMImplementation = getDOM() - while True: + while true: x.next() case x.kind() of xmlEof: break of xmlElementStart, xmlElementOpen: - var el: PElement = parseElement(x, XmlDoc) - XmlDoc = dom.createDocument(el) + var el: PElement = parseElement(x, xmlDoc) + xmlDoc = dom.createDocument(el) of xmlWhitespace, xmlElementClose, xmlEntity, xmlSpecial: - # Unused 'events' + discard " Unused \'events\'" else: raise newException(EParserError, "Unexpected XML Parser event") - return XmlDoc + return xmlDoc proc loadXML*(xml: string): PDocument = ## Loads and parses XML from a string specified by ``xml``, and returns @@ -151,12 +151,12 @@ proc loadXMLFile*(path: string): PDocument = ## a ``PDocument`` var s = newFileStream(path, fmRead) - if s == nil: raise newException(EIO, "Unable to read file " & path) + if s == nil: raise newException(IOError, "Unable to read file " & path) return loadXMLStream(s) when isMainModule: - var xml = loadXMLFile(r"C:\Users\Dominik\Desktop\Code\Nimrod\xmldom\test.xml") + var xml = loadXMLFile("nim/xmldom/test.xml") #echo(xml.getElementsByTagName("m:test2")[0].namespaceURI) #echo(xml.getElementsByTagName("bla:test")[0].namespaceURI) #echo(xml.getElementsByTagName("test")[0].namespaceURI) diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim index 8b8bb3b03..8591e894c 100644 --- a/lib/pure/xmlparser.nim +++ b/lib/pure/xmlparser.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2010 Andreas Rumpf # # See the file "copying.txt", included in this @@ -12,23 +12,25 @@ import streams, parsexml, strtabs, xmltree type - EInvalidXml* = object of EInvalidValue ## exception that is raised - ## for invalid XML - errors*: seq[string] ## all detected parsing errors + XmlError* = object of ValueError ## exception that is raised + ## for invalid XML + errors*: seq[string] ## all detected parsing errors + +{.deprecated: [EInvalidXml: XmlError].} proc raiseInvalidXml(errors: seq[string]) = - var e: ref EInvalidXml + var e: ref XmlError new(e) e.msg = errors[0] e.errors = errors raise e -proc addNode(father, son: PXmlNode) = +proc addNode(father, son: XmlNode) = if son != nil: add(father, son) -proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode +proc parse(x: var XmlParser, errors: var seq[string]): XmlNode -proc untilElementEnd(x: var TXmlParser, result: PXmlNode, +proc untilElementEnd(x: var XmlParser, result: XmlNode, errors: var seq[string]) = while true: case x.kind @@ -45,7 +47,7 @@ proc untilElementEnd(x: var TXmlParser, result: PXmlNode, else: result.addNode(parse(x, errors)) -proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode = +proc parse(x: var XmlParser, errors: var seq[string]): XmlNode = case x.kind of xmlComment: result = newComment(x.charData) @@ -98,11 +100,11 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode = next(x) of xmlEof: discard -proc parseXml*(s: PStream, filename: string, - errors: var seq[string]): PXmlNode = +proc parseXml*(s: Stream, filename: string, + errors: var seq[string]): XmlNode = ## parses the XML from stream `s` and returns a ``PXmlNode``. Every ## occured parsing error is added to the `errors` sequence. - var x: TXmlParser + var x: XmlParser open(x, s, filename, {reportComments}) while true: x.next() @@ -118,28 +120,28 @@ proc parseXml*(s: PStream, filename: string, break close(x) -proc parseXml*(s: PStream): PXmlNode = +proc parseXml*(s: Stream): XmlNode = ## parses the XTML from stream `s` and returns a ``PXmlNode``. All parsing ## errors are turned into an ``EInvalidXML`` exception. var errors: seq[string] = @[] result = parseXml(s, "unknown_html_doc", errors) - if errors.len > 0: raiseInvalidXMl(errors) + if errors.len > 0: raiseInvalidXml(errors) -proc loadXml*(path: string, errors: var seq[string]): PXmlNode = +proc loadXml*(path: string, errors: var seq[string]): XmlNode = ## Loads and parses XML from file specified by ``path``, and returns ## a ``PXmlNode``. Every occured parsing error is added to the `errors` ## sequence. var s = newFileStream(path, fmRead) - if s == nil: raise newException(EIO, "Unable to read file: " & path) + if s == nil: raise newException(IOError, "Unable to read file: " & path) result = parseXml(s, path, errors) -proc loadXml*(path: string): PXmlNode = +proc loadXml*(path: string): XmlNode = ## Loads and parses XML from file specified by ``path``, and returns ## a ``PXmlNode``. All parsing errors are turned into an ``EInvalidXML`` ## exception. var errors: seq[string] = @[] result = loadXml(path, errors) - if errors.len > 0: raiseInvalidXMl(errors) + if errors.len > 0: raiseInvalidXml(errors) when isMainModule: import os @@ -148,7 +150,7 @@ when isMainModule: var x = loadXml(paramStr(1), errors) for e in items(errors): echo e - var f: TFile + var f: File if open(f, "xmltest.txt", fmWrite): f.write($x) f.close() diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index 1af7db7d5..b84da9586 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -12,76 +12,79 @@ import macros, strtabs type - PXmlNode* = ref TXmlNode ## an XML tree consists of ``PXmlNode``'s. + XmlNode* = ref XmlNodeObj ## an XML tree consists of ``PXmlNode``'s. - TXmlNodeKind* = enum ## different kinds of ``PXmlNode``'s + XmlNodeKind* = enum ## different kinds of ``PXmlNode``'s xnText, ## a text element xnElement, ## an element with 0 or more children xnCData, ## a CDATA node xnEntity, ## an entity (like ``&thing;``) xnComment ## an XML comment - PXmlAttributes* = PStringTable ## an alias for a string to string mapping + XmlAttributes* = StringTableRef ## an alias for a string to string mapping - TXmlNode {.pure, final, acyclic.} = object - case k: TXmlNodeKind # private, use the kind() proc to read this field. + XmlNodeObj {.acyclic.} = object + case k: XmlNodeKind # private, use the kind() proc to read this field. of xnText, xnComment, xnCData, xnEntity: fText: string of xnElement: fTag: string - s: seq[PXmlNode] - fAttr: PXmlAttributes + s: seq[XmlNode] + fAttr: XmlAttributes fClientData: int ## for other clients - -proc newXmlNode(kind: TXmlNodeKind): PXmlNode = - ## creates a new ``PXmlNode``. + +{.deprecated: [PXmlNode: XmlNode, TXmlNodeKind: XmlNodeKind, PXmlAttributes: + XmlAttributes, TXmlNode: XmlNodeObj].} + +proc newXmlNode(kind: XmlNodeKind): XmlNode = + ## creates a new ``XmlNode``. new(result) result.k = kind -proc newElement*(tag: string): PXmlNode = +proc newElement*(tag: string): XmlNode = ## creates a new ``PXmlNode`` of kind ``xnText`` with the given `tag`. result = newXmlNode(xnElement) result.fTag = tag result.s = @[] # init attributes lazily to safe memory -proc newText*(text: string): PXmlNode = +proc newText*(text: string): XmlNode = ## creates a new ``PXmlNode`` of kind ``xnText`` with the text `text`. result = newXmlNode(xnText) result.fText = text -proc newComment*(comment: string): PXmlNode = +proc newComment*(comment: string): XmlNode = ## creates a new ``PXmlNode`` of kind ``xnComment`` with the text `comment`. result = newXmlNode(xnComment) result.fText = comment -proc newCData*(cdata: string): PXmlNode = +proc newCData*(cdata: string): XmlNode = ## creates a new ``PXmlNode`` of kind ``xnComment`` with the text `cdata`. result = newXmlNode(xnCData) result.fText = cdata -proc newEntity*(entity: string): PXmlNode = +proc newEntity*(entity: string): XmlNode = ## creates a new ``PXmlNode`` of kind ``xnEntity`` with the text `entity`. result = newXmlNode(xnCData) result.fText = entity -proc text*(n: PXmlNode): string {.inline.} = +proc text*(n: XmlNode): string {.inline.} = ## gets the associated text with the node `n`. `n` can be a CDATA, Text, ## comment, or entity node. assert n.k in {xnText, xnComment, xnCData, xnEntity} result = n.fText -proc rawText*(n: PXmlNode): string {.inline.} = +proc rawText*(n: XmlNode): string {.inline.} = ## returns the underlying 'text' string by reference. ## This is only used for speed hacks. shallowCopy(result, n.fText) -proc rawTag*(n: PXmlNode): string {.inline.} = +proc rawTag*(n: XmlNode): string {.inline.} = ## returns the underlying 'tag' string by reference. ## This is only used for speed hacks. shallowCopy(result, n.fTag) -proc innerText*(n: PXmlNode): string = +proc innerText*(n: XmlNode): string = ## gets the inner text of `n`. `n` has to be an ``xnElement`` node. Only ## ``xnText`` and ``xnEntity`` nodes are considered part of `n`'s inner text, ## other child nodes are silently ignored. @@ -90,55 +93,55 @@ proc innerText*(n: PXmlNode): string = for i in 0 .. n.s.len-1: if n.s[i].k in {xnText, xnEntity}: result.add(n.s[i].fText) -proc tag*(n: PXmlNode): string {.inline.} = +proc tag*(n: XmlNode): string {.inline.} = ## gets the tag name of `n`. `n` has to be an ``xnElement`` node. assert n.k == xnElement result = n.fTag -proc add*(father, son: PXmlNode) {.inline.} = +proc add*(father, son: XmlNode) {.inline.} = ## adds the child `son` to `father`. add(father.s, son) -proc len*(n: PXmlNode): int {.inline.} = +proc len*(n: XmlNode): int {.inline.} = ## returns the number `n`'s children. if n.k == xnElement: result = len(n.s) -proc kind*(n: PXmlNode): TXmlNodeKind {.inline.} = +proc kind*(n: XmlNode): XmlNodeKind {.inline.} = ## returns `n`'s kind. result = n.k -proc `[]`* (n: PXmlNode, i: int): PXmlNode {.inline.} = +proc `[]`* (n: XmlNode, i: int): XmlNode {.inline.} = ## returns the `i`'th child of `n`. assert n.k == xnElement result = n.s[i] -iterator items*(n: PXmlNode): PXmlNode {.inline.} = +iterator items*(n: XmlNode): XmlNode {.inline.} = ## iterates over any child of `n`. assert n.k == xnElement for i in 0 .. n.len-1: yield n[i] -proc attrs*(n: PXmlNode): PXmlAttributes {.inline.} = +proc attrs*(n: XmlNode): XmlAttributes {.inline.} = ## gets the attributes belonging to `n`. ## Returns `nil` if attributes have not been initialised for this node. assert n.k == xnElement result = n.fAttr -proc `attrs=`*(n: PXmlNode, attr: PXmlAttributes) {.inline.} = +proc `attrs=`*(n: XmlNode, attr: XmlAttributes) {.inline.} = ## sets the attributes belonging to `n`. assert n.k == xnElement n.fAttr = attr -proc attrsLen*(n: PXmlNode): int {.inline.} = +proc attrsLen*(n: XmlNode): int {.inline.} = ## returns the number of `n`'s attributes. assert n.k == xnElement if not isNil(n.fAttr): result = len(n.fAttr) -proc clientData*(n: PXmlNode): int {.inline.} = +proc clientData*(n: XmlNode): int {.inline.} = ## gets the client data of `n`. The client data field is used by the HTML ## parser and generator. result = n.fClientData -proc `clientData=`*(n: PXmlNode, data: int) {.inline.} = +proc `clientData=`*(n: XmlNode, data: int) {.inline.} = ## sets the client data of `n`. The client data field is used by the HTML ## parser and generator. n.fClientData = data @@ -176,13 +179,13 @@ proc addIndent(result: var string, indent: int) = result.add("\n") for i in 1..indent: result.add(' ') -proc noWhitespace(n: PXmlNode): bool = +proc noWhitespace(n: XmlNode): bool = #for i in 1..n.len-1: # if n[i].kind != n[0].kind: return true for i in 0..n.len-1: if n[i].kind in {xnText, xnEntity}: return true -proc add*(result: var string, n: PXmlNode, indent = 0, indWidth = 2) = +proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2) = ## adds the textual representation of `n` to `result`. if n == nil: return case n.k @@ -222,7 +225,7 @@ proc add*(result: var string, n: PXmlNode, indent = 0, indWidth = 2) = result.add("") - of xnCDATA: + of xnCData: result.add("") @@ -235,14 +238,14 @@ const xmlHeader* = "\n" ## header to use for complete XML output -proc `$`*(n: PXmlNode): string = +proc `$`*(n: XmlNode): string = ## converts `n` into its string representation. No ``<$xml ...$>`` declaration ## is produced, so that the produced XML fragments are composable. result = "" result.add(n) -proc newXmlTree*(tag: string, children: openArray[PXmlNode], - attributes: PXmlAttributes = nil): PXmlNode = +proc newXmlTree*(tag: string, children: openArray[XmlNode], + attributes: XmlAttributes = nil): XmlNode = ## creates a new XML tree with `tag`, `children` and `attributes` result = newXmlNode(xnElement) result.fTag = tag @@ -276,17 +279,17 @@ proc xmlConstructor(e: PNimrodNode): PNimrodNode {.compileTime.} = macro `<>`*(x: expr): expr {.immediate.} = ## Constructor macro for XML. Example usage: ## - ## .. code-block:: nimrod - ## <>a(href="http://nimrod-code.org", newText("Nimrod rules.")) + ## .. code-block:: nim + ## <>a(href="http://nim-code.org", newText("Nim rules.")) ## ## Produces an XML tree for:: ## - ## Nimrod rules. + ## Nim rules. ## let x = callsite() result = xmlConstructor(x) -proc child*(n: PXmlNode, name: string): PXmlNode = +proc child*(n: XmlNode, name: string): XmlNode = ## Finds the first child element of `n` with a name of `name`. ## Returns `nil` on failure. assert n.kind == xnElement @@ -295,23 +298,23 @@ proc child*(n: PXmlNode, name: string): PXmlNode = if i.tag == name: return i -proc attr*(n: PXmlNode, name: string): string = +proc attr*(n: XmlNode, name: string): string = ## Finds the first attribute of `n` with a name of `name`. ## Returns "" on failure. assert n.kind == xnElement if n.attrs == nil: return "" return n.attrs[name] -proc findAll*(n: PXmlNode, tag: string, result: var seq[PXmlNode]) = +proc findAll*(n: XmlNode, tag: string, result: var seq[XmlNode]) = ## Iterates over all the children of `n` returning those matching `tag`. ## ## Found nodes satisfying the condition will be appended to the `result` ## sequence, which can't be nil or the proc will crash. Usage example: ## - ## .. code-block:: nimrod + ## .. code-block:: ## var - ## html: PXmlNode - ## tags: seq[PXmlNode] = @[] + ## html: XmlNode + ## tags: seq[XmlNode] = @[] ## ## html = buildHtml() ## findAll(html, "img", tags) @@ -327,11 +330,11 @@ proc findAll*(n: PXmlNode, tag: string, result: var seq[PXmlNode]) = elif child.k == xnElement: child.findAll(tag, result) -proc findAll*(n: PXmlNode, tag: string): seq[PXmlNode] = +proc findAll*(n: XmlNode, tag: string): seq[XmlNode] = ## Shortcut version to assign in let blocks. Example: ## - ## .. code-block:: nimrod - ## var html: PXmlNode + ## .. code-block:: + ## var html: XmlNode ## ## html = buildHtml(html) ## for imgTag in html.findAll("img"): @@ -340,5 +343,5 @@ proc findAll*(n: PXmlNode, tag: string): seq[PXmlNode] = findAll(n, tag, result) when isMainModule: - assert """Nimrod rules.""" == - $(<>a(href="http://nimrod-code.org", newText("Nimrod rules."))) + assert """Nim rules.""" == + $(<>a(href="http://nim-code.org", newText("Nim rules."))) diff --git a/lib/stdlib.babel b/lib/stdlib.nimble similarity index 71% rename from lib/stdlib.babel rename to lib/stdlib.nimble index f22598aba..0805ead54 100644 --- a/lib/stdlib.babel +++ b/lib/stdlib.nimble @@ -2,5 +2,5 @@ name = "stdlib" version = "0.9.0" author = "Dominik Picheta" -description = "Nimrod's standard library." +description = "Nim's standard library." license = "MIT" diff --git a/lib/system.nim b/lib/system.nim index 0df8849f5..ca85bf1ad 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1,7 +1,7 @@ # # -# Nimrod's Runtime Library -# (c) Copyright 2013 Andreas Rumpf +# Nim's Runtime Library +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -77,44 +77,53 @@ type auto* = expr any* = distinct auto - TSignedInt* = int|int8|int16|int32|int64 + SomeSignedInt* = int|int8|int16|int32|int64 ## type class matching all signed integer types - TUnsignedInt* = uint|uint8|uint16|uint32|uint64 + SomeUnsignedInt* = uint|uint8|uint16|uint32|uint64 ## type class matching all unsigned integer types - TInteger* = TSignedInt|TUnsignedInt + SomeInteger* = SomeSignedInt|SomeUnsignedInt ## type class matching all integer types - TOrdinal* = int|int8|int16|int32|int64|bool|enum|uint8|uint16|uint32 + SomeOrdinal* = int|int8|int16|int32|int64|bool|enum|uint8|uint16|uint32 ## type class matching all ordinal types; however this includes enums with ## holes. - TReal* = float|float32|float64 + SomeReal* = float|float32|float64 ## type class matching all floating point number types - TNumber* = TInteger|TReal + SomeNumber* = SomeInteger|SomeReal ## type class matching all number types proc defined*(x: expr): bool {.magic: "Defined", noSideEffect.} ## Special compile-time procedure that checks whether `x` is ## defined. ## `x` is an external symbol introduced through the compiler's - ## `-d:x switch `_ to enable build time + ## `-d:x switch `_ to enable build time ## conditionals: ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## when not defined(release): ## # Do here programmer friendly expensive sanity checks. ## # Put here the normal code +when defined(nimalias): + {.deprecated: [ + TSignedInt: SomeSignedInt, + TUnsignedInt: SomeUnsignedInt, + TInteger: SomeInteger, + TReal: SomeReal, + TNumber: SomeNumber, + TOrdinal: SomeOrdinal].} + proc declared*(x: expr): bool {.magic: "Defined", noSideEffect.} ## Special compile-time procedure that checks whether `x` is ## declared. `x` has to be an identifier or a qualified identifier. ## This can be used to check whether a library provides a certain ## feature or not: ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## when not defined(strutils.toUpper): ## # provide our own toUpper proc here, because strutils is ## # missing it. @@ -183,6 +192,7 @@ proc high*[T](x: T): T {.magic: "High", noSideEffect.} ## returns the highest possible index of an array, a sequence, a string or ## the highest possible value of an ordinal value `x`. As a special ## semantic rule, `x` may also be a type identifier. + ## ``high(int)`` is Nim's way of writing `INT_MAX`:idx: or `MAX_INT`:idx:. proc low*[T](x: T): T {.magic: "Low", noSideEffect.} ## returns the lowest possible index of an array, a sequence, a string or @@ -202,10 +212,13 @@ type set*{.magic: "Set".}[T] ## Generic type to construct bit sets. type - TSlice* {.final, pure.}[T] = object ## builtin slice type - a*, b*: T ## the bounds + Slice* {.final, pure.}[T] = object ## builtin slice type + a*, b*: T ## the bounds -proc `..`*[T](a, b: T): TSlice[T] {.noSideEffect, inline.} = +when defined(nimalias): + {.deprecated: [TSlice: Slice].} + +proc `..`*[T](a, b: T): Slice[T] {.noSideEffect, inline.} = ## `slice`:idx: operator that constructs an interval ``[a, b]``, both `a` ## and `b` are inclusive. Slices can also be used in the set constructor ## and in ordinal case statements, but then they are special-cased by the @@ -213,7 +226,7 @@ proc `..`*[T](a, b: T): TSlice[T] {.noSideEffect, inline.} = result.a = a result.b = b -proc `..`*[T](b: T): TSlice[T] {.noSideEffect, inline.} = +proc `..`*[T](b: T): Slice[T] {.noSideEffect, inline.} = ## `slice`:idx: operator that constructs an interval ``[default(T), b]`` result.b = b @@ -296,7 +309,7 @@ when not defined(JS) and not defined(NimrodVM): include "system/hti" type - Byte* = uint8 ## this is an alias for ``uint8``, that is an unsigned + byte* = uint8 ## this is an alias for ``uint8``, that is an unsigned ## int 8 bits wide. Natural* = range[0..high(int)] @@ -307,29 +320,29 @@ type ## is an int type ranging from one to the maximum value ## of an int. This type is often useful for documentation and debugging. - TObject* {.exportc: "TNimObject", inheritable.} = - object ## the root of Nimrod's object hierarchy. Objects should + RootObj* {.exportc: "TNimObject", inheritable.} = + object ## the root of Nim's object hierarchy. Objects should ## inherit from TObject or one of its descendants. However, ## objects that have no ancestor are allowed. - PObject* = ref TObject ## reference to TObject + RootRef* = ref RootObj ## reference to RootObj - TEffect* {.compilerproc.} = object of TObject ## \ + RootEffect* {.compilerproc.} = object of RootObj ## \ ## base effect class; each effect should ## inherit from `TEffect` unless you know what ## you doing. - FTime* = object of TEffect ## Time effect. - FIO* = object of TEffect ## IO effect. - FReadIO* = object of FIO ## Effect describing a read IO operation. - FWriteIO* = object of FIO ## Effect describing a write IO operation. - FExecIO* = object of FIO ## Effect describing an executing IO operation. + TimeEffect* = object of RootEffect ## Time effect. + IOEffect* = object of RootEffect ## IO effect. + ReadIOEffect* = object of IOEffect ## Effect describing a read IO operation. + WriteIOEffect* = object of IOEffect ## Effect describing a write IO operation. + ExecIOEffect* = object of IOEffect ## Effect describing an executing IO operation. - E_Base* {.compilerproc.} = object of TObject ## \ + Exception* {.compilerproc.} = object of RootObj ## \ ## Base exception class. ## - ## Each exception has to inherit from `E_Base`. See the full `exception + ## Each exception has to inherit from `Exception`. See the full `exception ## hierarchy`_. - parent: ref E_Base ## parent exception (can be used as a stack) - name: cstring ## The exception's name is its Nimrod identifier. + parent: ref Exception ## parent exception (can be used as a stack) + name: cstring ## The exception's name is its Nim identifier. ## This field is filled automatically in the ## ``raise`` statement. msg* {.exportc: "message".}: string ## the exception's message. Not @@ -337,143 +350,156 @@ type ## is bad style. trace: string - EAsynch* = object of E_Base ## \ - ## Abstract exception class for *asynchronous exceptions* (interrupts). - ## - ## This is rarely needed: most exception types inherit from `ESynch - ## <#ESynch>`_. See the full `exception hierarchy`_. - EControlC* = object of EAsynch ## \ - ## Raised for Ctrl+C key presses in console applications. - ## - ## See the full `exception hierarchy`_. - ESynch* = object of E_Base ## \ - ## Abstract exception class for *synchronous exceptions*. - ## - ## Most exceptions should be inherited (directly or indirectly) from - ## `ESynch` instead of from `EAsynch <#EAsynch>`_. See the full `exception - ## hierarchy`_. - ESystem* = object of ESynch ## \ + SystemError* = object of Exception ## \ ## Abstract class for exceptions that the runtime system raises. ## ## See the full `exception hierarchy`_. - EIO* = object of ESystem ## \ + IOError* = object of SystemError ## \ ## Raised if an IO error occured. ## ## See the full `exception hierarchy`_. - EOS* = object of ESystem ## \ + OSError* = object of SystemError ## \ ## Raised if an operating system service failed. ## ## See the full `exception hierarchy`_. errorCode*: int32 ## OS-defined error code describing this error. - EInvalidLibrary* = object of EOS ## \ + LibraryError* = object of OSError ## \ ## Raised if a dynamic library could not be loaded. ## ## See the full `exception hierarchy`_. - EResourceExhausted* = object of ESystem ## \ + ResourceExhaustedError* = object of SystemError ## \ ## Raised if a resource request could not be fullfilled. ## ## See the full `exception hierarchy`_. - EArithmetic* = object of ESynch ## \ + ArithmeticError* = object of Exception ## \ ## Raised if any kind of arithmetic error occured. ## ## See the full `exception hierarchy`_. - EDivByZero* {.compilerproc.} = object of EArithmetic ## \ + DivByZeroError* = object of ArithmeticError ## \ ## Raised for runtime integer divide-by-zero errors. ## ## See the full `exception hierarchy`_. - EOverflow* {.compilerproc.} = object of EArithmetic ## \ + + OverflowError* = object of ArithmeticError ## \ ## Raised for runtime integer overflows. ## ## This happens for calculations whose results are too large to fit in the ## provided bits. See the full `exception hierarchy`_. - EAccessViolation* {.compilerproc.} = object of ESynch ## \ + AccessViolationError* = object of Exception ## \ ## Raised for invalid memory access errors ## ## See the full `exception hierarchy`_. - EAssertionFailed* {.compilerproc.} = object of ESynch ## \ + AssertionError* = object of Exception ## \ ## Raised when assertion is proved wrong. ## ## Usually the result of using the `assert() template <#assert>`_. See the ## full `exception hierarchy`_. - EInvalidValue* = object of ESynch ## \ + ValueError* = object of Exception ## \ ## Raised for string and object conversion errors. - EInvalidKey* = object of EInvalidValue ## \ + KeyError* = object of ValueError ## \ ## Raised if a key cannot be found in a table. ## ## Mostly used by the `tables `_ module, it can also be raised ## by other collection modules like `sets `_ or `strtabs ## `_. See the full `exception hierarchy`_. - EOutOfMemory* = object of ESystem ## \ + OutOfMemError* = object of SystemError ## \ ## Raised for unsuccessful attempts to allocate memory. ## ## See the full `exception hierarchy`_. - EInvalidIndex* = object of ESynch ## \ + IndexError* = object of Exception ## \ ## Raised if an array index is out of bounds. ## ## See the full `exception hierarchy`_. - EInvalidField* = object of ESynch ## \ + + FieldError* = object of Exception ## \ ## Raised if a record field is not accessible because its dicriminant's ## value does not fit. ## ## See the full `exception hierarchy`_. - EOutOfRange* = object of ESynch ## \ + RangeError* = object of Exception ## \ ## Raised if a range check error occurred. ## ## See the full `exception hierarchy`_. - EStackOverflow* = object of ESystem ## \ + StackOverflowError* = object of SystemError ## \ ## Raised if the hardware stack used for subroutine calls overflowed. ## ## See the full `exception hierarchy`_. - ENoExceptionToReraise* = object of ESynch ## \ + ReraiseError* = object of Exception ## \ ## Raised if there is no exception to reraise. ## ## See the full `exception hierarchy`_. - EInvalidObjectAssignment* = object of ESynch ## \ + ObjectAssignmentError* = object of Exception ## \ ## Raised if an object gets assigned to its parent's object. ## ## See the full `exception hierarchy`_. - EInvalidObjectConversion* = object of ESynch ## \ + ObjectConversionError* = object of Exception ## \ ## Raised if an object is converted to an incompatible object type. ## ## See the full `exception hierarchy`_. - EFloatingPoint* = object of ESynch ## \ + FloatingPointError* = object of Exception ## \ ## Base class for floating point exceptions. ## ## See the full `exception hierarchy`_. - EFloatInvalidOp* {.compilerproc.} = object of EFloatingPoint ## \ + FloatInvalidOpError* = object of FloatingPointError ## \ ## Raised by invalid operations according to IEEE. ## ## Raised by ``0.0/0.0``, for example. See the full `exception ## hierarchy`_. - EFloatDivByZero* {.compilerproc.} = object of EFloatingPoint ## \ + FloatDivByZeroError* = object of FloatingPointError ## \ ## Raised by division by zero. ## ## Divisor is zero and dividend is a finite nonzero number. See the full ## `exception hierarchy`_. - EFloatOverflow* {.compilerproc.} = object of EFloatingPoint ## \ + FloatOverflowError* = object of FloatingPointError ## \ ## Raised for overflows. ## ## The operation produced a result that exceeds the range of the exponent. ## See the full `exception hierarchy`_. - EFloatUnderflow* {.compilerproc.} = object of EFloatingPoint ## \ + FloatUnderflowError* = object of FloatingPointError ## \ ## Raised for underflows. ## ## The operation produced a result that is too small to be represented as a ## normal number. See the full `exception hierarchy`_. - EFloatInexact* {.compilerproc.} = object of EFloatingPoint ## \ + FloatInexactError* = object of FloatingPointError ## \ ## Raised for inexact results. ## ## The operation produced a result that cannot be represented with infinite ## precision -- for example: ``2.0 / 3.0, log(1.1)`` ## - ## **NOTE**: Nimrod currently does not detect these! See the full + ## **NOTE**: Nim currently does not detect these! See the full ## `exception hierarchy`_. - EDeadThread* = object of ESynch ## \ + DeadThreadError* = object of Exception ## \ ## Raised if it is attempted to send a message to a dead thread. ## ## See the full `exception hierarchy`_. - TResult* = enum Failure, Success + TResult* {.deprecated.} = enum Failure, Success + +{.deprecated: [TObject: RootObj, PObject: RootRef, TEffect: RootEffect, + FTime: TimeEffect, FIO: IOEffect, FReadIO: ReadIOEffect, + FWriteIO: WriteIOEffect, FExecIO: ExecIOEffect, + + E_Base: Exception, ESystem: SystemError, EIO: IOError, + EOS: OSError, EInvalidLibrary: LibraryError, + EResourceExhausted: ResourceExhaustedError, + EArithmetic: ArithmeticError, EDivByZero: DivByZeroError, + EOverflow: OverflowError, EAccessViolation: AccessViolationError, + EAssertionFailed: AssertionError, EInvalidValue: ValueError, + EInvalidKey: KeyError, EOutOfMemory: OutOfMemError, + EInvalidIndex: IndexError, EInvalidField: FieldError, + EOutOfRange: RangeError, EStackOverflow: StackOverflowError, + ENoExceptionToReraise: ReraiseError, + EInvalidObjectAssignment: ObjectAssignmentError, + EInvalidObjectConversion: ObjectConversionError, + EDeadThread: DeadThreadError, + EFloatInexact: FloatInexactError, + EFloatUnderflow: FloatUnderflowError, + EFloatingPoint: FloatingPointError, + EFloatInvalidOp: FloatInvalidOpError, + EFloatDivByZero: FloatDivByZeroError, + EFloatOverflow: FloatOverflowError, + ESynch: Exception +].} proc sizeof*[T](x: T): Natural {.magic: "SizeOf", noSideEffect.} ## returns the size of ``x`` in bytes. Since this is a low-level proc, @@ -484,7 +510,7 @@ proc sizeof*[T](x: T): Natural {.magic: "SizeOf", noSideEffect.} proc `<`*[T](x: Ordinal[T]): T {.magic: "UnaryLt", noSideEffect.} ## unary ``<`` that can be used for nice looking excluding ranges: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## for i in 0 .. <10: echo i ## ## Semantically this is the same as ``pred``. @@ -519,7 +545,7 @@ proc newSeq*[T](s: var seq[T], len: int) {.magic: "NewSeq", noSideEffect.} ## ``nil``. After the creation of the sequence you should assign entries to ## the sequence instead of adding them. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## var inputStrings : seq[string] ## newSeq(inputStrings, 3) ## inputStrings[0] = "The fourth" @@ -535,7 +561,7 @@ proc newSeq*[T](len = 0): seq[T] = ## ``nil``. After the creation of the sequence you should assign entries to ## the sequence instead of adding them. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## var inputStrings = newSeq[string](3) ## inputStrings[0] = "The fourth" ## inputStrings[1] = "assignment" @@ -666,7 +692,7 @@ proc `div` *(x, y: int32): int32 {.magic: "DivI", noSideEffect.} proc `div` *(x, y: int64): int64 {.magic: "DivI64", noSideEffect.} ## computes the integer division. This is roughly the same as ## ``floor(x/y)``. - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## 1 div 2 == 0 ## 2 div 2 == 1 ## 3 div 2 == 1 @@ -685,7 +711,7 @@ proc `shr` *(x, y: int16): int16 {.magic: "ShrI", noSideEffect.} proc `shr` *(x, y: int32): int32 {.magic: "ShrI", noSideEffect.} proc `shr` *(x, y: int64): int64 {.magic: "ShrI64", noSideEffect.} ## computes the `shift right` operation of `x` and `y`. - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## 0b0001_0000'i8 shr 2 == 0b0100_0000'i8 ## 0b1000_0000'i8 shr 2 == 0b0000_0000'i8 ## 0b0000_0001'i8 shr 9 == 0b0000_0000'i8 @@ -816,20 +842,17 @@ proc `+` *[T](x, y: set[T]): set[T] {.magic: "PlusSet", noSideEffect.} ## This operator computes the union of two sets. proc `-` *[T](x, y: set[T]): set[T] {.magic: "MinusSet", noSideEffect.} ## This operator computes the difference of two sets. -proc `-+-` *[T](x, y: set[T]): set[T] {.magic: "SymDiffSet", noSideEffect.} - ## computes the symmetric set difference. This is the same as - ## ``(A - B) + (B - A)``, but more efficient. proc contains*[T](x: set[T], y: T): bool {.magic: "InSet", noSideEffect.} ## One should overload this proc if one wants to overload the ``in`` operator. ## The parameters are in reverse order! ``a in b`` is a template for ## ``contains(b, a)``. - ## This is because the unification algorithm that Nimrod uses for overload + ## This is because the unification algorithm that Nim uses for overload ## resolution works from left to right. ## But for the ``in`` operator that would be the wrong direction for this ## piece of code: ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## var s: set[range['a'..'z']] = {'a'..'c'} ## writeln(stdout, 'b' in s) ## @@ -839,11 +862,11 @@ proc contains*[T](x: set[T], y: T): bool {.magic: "InSet", noSideEffect.} ## is achieved by reversing the parameters for ``contains``; ``in`` then ## passes its arguments in reverse order. -proc contains*[T](s: TSlice[T], value: T): bool {.noSideEffect, inline.} = +proc contains*[T](s: Slice[T], value: T): bool {.noSideEffect, inline.} = ## Checks if `value` is withing the range of `s`; returns true iff ## `value >= s.a and value <= s.b` ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## assert((1..3).contains(1) == true) ## assert((1..3).contains(2) == true) ## assert((1..3).contains(4) == false) @@ -852,20 +875,20 @@ proc contains*[T](s: TSlice[T], value: T): bool {.noSideEffect, inline.} = template `in` * (x, y: expr): expr {.immediate, dirty.} = contains(y, x) ## Sugar for contains ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## assert(1 in (1..3) == true) ## assert(5 in (1..3) == false) template `notin` * (x, y: expr): expr {.immediate, dirty.} = not contains(y, x) ## Sugar for not containing ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## assert(1 notin (1..3) == false) ## assert(5 notin (1..3) == true) proc `is` *[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.} ## Checks if T is of the same type as S ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## proc test[T](a: T): int = ## when (T is int): ## return a @@ -880,10 +903,9 @@ template `isnot` *(x, y: expr): expr {.immediate.} = not (x is y) proc `of` *[T, S](x: T, y: S): bool {.magic: "Of", noSideEffect.} ## Checks if `x` has a type of `y` ## - ## .. code-block:: Nimrod - ## assert(EFloatingPoint of EBase) - ## assert(EIO of ESystem) - ## assert(EDivByZero of EBase) + ## .. code-block:: Nim + ## assert(FloatingPointError of Exception) + ## assert(DivByZeroError of Exception) proc cmp*[T](x, y: T): int {.procvar.} = ## Generic compare proc. Returns a value < 0 iff x < y, a value > 0 iff x > y @@ -936,25 +958,25 @@ proc `&` * (x: string, y: char): string {. magic: "ConStrStr", noSideEffect, merge.} ## Concatenates `x` with `y` ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## assert("ab" & 'c' == "abc") proc `&` * (x: char, y: char): string {. magic: "ConStrStr", noSideEffect, merge.} ## Concatenates `x` and `y` into a string ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## assert('a' & 'b' == "ab") proc `&` * (x, y: string): string {. magic: "ConStrStr", noSideEffect, merge.} ## Concatenates `x` and `y` ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## assert("ab" & "cd" == "abcd") proc `&` * (x: char, y: string): string {. magic: "ConStrStr", noSideEffect, merge.} ## Concatenates `x` with `y` ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## assert('a' & "bc" == "abc") # implementation note: These must all have the same magic value "ConStrStr" so @@ -963,7 +985,7 @@ proc `&` * (x: char, y: string): string {. proc add*(x: var string, y: char) {.magic: "AppendStrCh", noSideEffect.} ## Appends `y` to `x` in place ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## var tmp = "" ## tmp.add('a') ## tmp.add('b') @@ -971,14 +993,14 @@ proc add*(x: var string, y: char) {.magic: "AppendStrCh", noSideEffect.} proc add*(x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.} ## Concatenates `x` and `y` in place ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## var tmp = "" ## tmp.add("ab") ## tmp.add("cd") ## assert(tmp == "abcd") type - TEndian* = enum ## is a type describing the endianness of a processor. + Endianness* = enum ## is a type describing the endianness of a processor. littleEndian, bigEndian const @@ -994,43 +1016,27 @@ const ## is the time of compilation as a string of the form ## ``HH:MM:SS``. This works thanks to compiler magic. - NimrodVersion* {.magic: "NimrodVersion"}: string = "0.0.0" - ## is the version of Nimrod as a string. - ## This works thanks to compiler magic. - - NimrodMajor* {.magic: "NimrodMajor"}: int = 0 - ## is the major number of Nimrod's version. - ## This works thanks to compiler magic. - - NimrodMinor* {.magic: "NimrodMinor"}: int = 0 - ## is the minor number of Nimrod's version. - ## This works thanks to compiler magic. - - NimrodPatch* {.magic: "NimrodPatch"}: int = 0 - ## is the patch number of Nimrod's version. - ## This works thanks to compiler magic. - - cpuEndian* {.magic: "CpuEndian"}: TEndian = littleEndian + cpuEndian* {.magic: "CpuEndian"}: Endianness = littleEndian ## is the endianness of the target CPU. This is a valuable piece of ## information for low-level code only. This works thanks to compiler ## magic. - - hostOS* {.magic: "HostOS"}: string = "" + + hostOS* {.magic: "HostOS".}: string = "" ## a string that describes the host operating system. Possible values: ## "windows", "macosx", "linux", "netbsd", "freebsd", "openbsd", "solaris", ## "aix", "standalone". - - hostCPU* {.magic: "HostCPU"}: string = "" + + hostCPU* {.magic: "HostCPU".}: string = "" ## a string that describes the host CPU. Possible values: - ## "i386", "alpha", "powerpc", "sparc", "amd64", "mips", "arm". - + ## "i386", "alpha", "powerpc", "powerpc64", "sparc", "amd64", "mips", "arm". + seqShallowFlag = low(int) - + proc compileOption*(option: string): bool {. magic: "CompileOption", noSideEffect.} ## can be used to determine an on|off compile-time option. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## when compileOption("floatchecks"): ## echo "compiled with floating point NaN and Inf checks" @@ -1038,7 +1044,7 @@ proc compileOption*(option, arg: string): bool {. magic: "CompileOptionArg", noSideEffect.} ## can be used to determine an enum compile-time option. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## when compileOption("opt", "size") and compileOption("gc", "boehm"): ## echo "compiled with optimization for size and uses Boehm's GC" @@ -1073,11 +1079,11 @@ else: const QuitSuccess* = 0 - ## is the value that should be passed to ``quit`` to indicate + ## is the value that should be passed to `quit <#quit>`_ to indicate ## success. QuitFailure* = 1 - ## is the value that should be passed to ``quit`` to indicate + ## is the value that should be passed to `quit <#quit>`_ to indicate ## failure. var programResult* {.exportc: "nim_program_result".}: int @@ -1090,18 +1096,20 @@ proc quit*(errorcode: int = QuitSuccess) {. ## Stops the program immediately with an exit code. ## ## Before stopping the program the "quit procedures" are called in the - ## opposite order they were added with ``addQuitProc``. ``quit`` never - ## returns and ignores any exception that may have been raised by the quit - ## procedures. It does *not* call the garbage collector to free all the - ## memory, unless a quit procedure calls ``GC_collect``. + ## opposite order they were added with `addQuitProc <#addQuitProc>`_. + ## ``quit`` never returns and ignores any exception that may have been raised + ## by the quit procedures. It does *not* call the garbage collector to free + ## all the memory, unless a quit procedure calls `GC_fullCollect + ## <#GC_fullCollect>`_. ## - ## The proc ``quit(QuitSuccess)`` is called implicitly when your nimrod + ## The proc ``quit(QuitSuccess)`` is called implicitly when your nim ## program finishes without incident. A raised unhandled exception is ## equivalent to calling ``quit(QuitFailure)``. ## ## Note that this is a *runtime* call and using ``quit`` inside a macro won't ## have any compile time effect. If you need to stop the compiler inside a - ## macro, use the ``error`` or ``fatal`` pragmas. + ## macro, use the `error `_ or `fatal + ## `_ pragmas. template sysAssert(cond: bool, msg: string) = when defined(useSysAssert): @@ -1117,7 +1125,7 @@ proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} = ## Generic proc for adding a data item `y` to a container `x`. ## For containers that have an order, `add` means *append*. New generic ## containers should also call their adding proc `add` for consistency. - ## Generic code becomes much easier to write if the Nimrod naming scheme is + ## Generic code becomes much easier to write if the Nim naming scheme is ## respected. let xl = x.len setLen(x, xl + y.len) @@ -1155,25 +1163,27 @@ proc insert*[T](x: var seq[T], item: T, i = 0) {.noSideEffect.} = x[i] = item proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.} - ## takes any Nimrod variable and returns its string representation. It + ## takes any Nim variable and returns its string representation. It ## works even for complex data graphs with cycles. This is a great ## debugging tool. type - TAddress* = int + ByteAddress* = int ## is the signed integer type that should be used for converting ## pointers to integer addresses for readability. BiggestInt* = int64 - ## is an alias for the biggest signed integer type the Nimrod compiler + ## is an alias for the biggest signed integer type the Nim compiler ## supports. Currently this is ``int64``, but it is platform-dependant ## in general. BiggestFloat* = float64 - ## is an alias for the biggest floating point type the Nimrod + ## is an alias for the biggest floating point type the Nim ## compiler supports. Currently this is ``float64``, but it is ## platform-dependant in general. +{.deprecated: [TAddress: ByteAddress].} + when defined(windows): type clong* {.importc: "long", nodecl.} = int32 @@ -1206,7 +1216,7 @@ type # these work for most platforms: ## This is the same as the type ``double`` in *C*. clongdouble* {.importc: "long double", nodecl.} = BiggestFloat ## This is the same as the type ``long double`` in *C*. - ## This C type is not supported by Nimrod's code generator + ## This C type is not supported by Nim's code generator cuchar* {.importc: "unsigned char", nodecl.} = char ## This is the same as the type ``unsigned char`` in *C*. @@ -1253,11 +1263,12 @@ proc toBiggestInt*(f: BiggestFloat): BiggestInt {. proc addQuitProc*(QuitProc: proc() {.noconv.}) {. importc: "atexit", header: "".} - ## adds/registers a quit procedure. Each call to ``addQuitProc`` - ## registers another quit procedure. Up to 30 procedures can be - ## registered. They are executed on a last-in, first-out basis - ## (that is, the last function registered is the first to be executed). - ## ``addQuitProc`` raises an EOutOfIndex if ``quitProc`` cannot be + ## Adds/registers a quit procedure. + ## + ## Each call to ``addQuitProc`` registers another quit procedure. Up to 30 + ## procedures can be registered. They are executed on a last-in, first-out + ## basis (that is, the last function registered is the first to be executed). + ## ``addQuitProc`` raises an EOutOfIndex exception if ``QuitProc`` cannot be ## registered. # Support for addQuitProc() is done by Ansi C's facilities here. @@ -1287,20 +1298,20 @@ proc substr*(s: string, first, last: int): string {. ## or `limit`:idx: a string's length. when not defined(nimrodVM): - proc zeroMem*(p: pointer, size: int) {.importc, noDecl, gcsafe.} + proc zeroMem*(p: pointer, size: int) {.importc, noDecl, benign.} ## overwrites the contents of the memory at ``p`` with the value 0. ## Exactly ``size`` bytes will be overwritten. Like any procedure ## dealing with raw memory this is *unsafe*. proc copyMem*(dest, source: pointer, size: int) {. - importc: "memcpy", header: "", gcsafe.} + importc: "memcpy", header: "", benign.} ## copies the contents from the memory at ``source`` to the memory ## at ``dest``. Exactly ``size`` bytes will be copied. The memory ## regions may not overlap. Like any procedure dealing with raw ## memory this is *unsafe*. proc moveMem*(dest, source: pointer, size: int) {. - importc: "memmove", header: "", gcsafe.} + importc: "memmove", header: "", benign.} ## copies the contents from the memory at ``source`` to the memory ## at ``dest``. Exactly ``size`` bytes will be copied. The memory ## regions may overlap, ``moveMem`` handles this case appropriately @@ -1315,14 +1326,14 @@ when not defined(nimrodVM): ## *unsafe*. when hostOS != "standalone": - proc alloc*(size: int): pointer {.noconv, rtl, tags: [], gcsafe.} + proc alloc*(size: int): pointer {.noconv, rtl, tags: [], benign.} ## allocates a new memory block with at least ``size`` bytes. The ## block has to be freed with ``realloc(block, 0)`` or ## ``dealloc(block)``. The block is not initialized, so reading ## from it before writing to it is undefined behaviour! ## The allocated memory belongs to its allocating thread! ## Use `allocShared` to allocate from a shared heap. - proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline, gcsafe.} = + proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline, benign.} = ## allocates a new memory block with at least ``T.sizeof * size`` ## bytes. The block has to be freed with ``resize(block, 0)`` or ## ``free(block)``. The block is not initialized, so reading @@ -1330,14 +1341,14 @@ when not defined(nimrodVM): ## The allocated memory belongs to its allocating thread! ## Use `createSharedU` to allocate from a shared heap. cast[ptr T](alloc(T.sizeof * size)) - proc alloc0*(size: int): pointer {.noconv, rtl, tags: [], gcsafe.} + proc alloc0*(size: int): pointer {.noconv, rtl, tags: [], benign.} ## allocates a new memory block with at least ``size`` bytes. The ## block has to be freed with ``realloc(block, 0)`` or ## ``dealloc(block)``. The block is initialized with all bytes ## containing zero, so it is somewhat safer than ``alloc``. ## The allocated memory belongs to its allocating thread! ## Use `allocShared0` to allocate from a shared heap. - proc create*(T: typedesc, size = 1.Positive): ptr T {.inline, gcsafe.} = + proc create*(T: typedesc, size = 1.Positive): ptr T {.inline, benign.} = ## allocates a new memory block with at least ``T.sizeof * size`` ## bytes. The block has to be freed with ``resize(block, 0)`` or ## ``free(block)``. The block is initialized with all bytes @@ -1346,7 +1357,7 @@ when not defined(nimrodVM): ## Use `createShared` to allocate from a shared heap. cast[ptr T](alloc0(T.sizeof * size)) proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [], - gcsafe.} + benign.} ## grows or shrinks a given memory block. If p is **nil** then a new ## memory block is returned. In either way the block has at least ## ``newSize`` bytes. If ``newSize == 0`` and p is not **nil** @@ -1354,7 +1365,7 @@ when not defined(nimrodVM): ## be freed with ``dealloc``. ## The allocated memory belongs to its allocating thread! ## Use `reallocShared` to reallocate from a shared heap. - proc resize*[T](p: ptr T, newSize: Natural): ptr T {.inline, gcsafe.} = + proc resize*[T](p: ptr T, newSize: Natural): ptr T {.inline, benign.} = ## grows or shrinks a given memory block. If p is **nil** then a new ## memory block is returned. In either way the block has at least ## ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is not @@ -1363,7 +1374,7 @@ when not defined(nimrodVM): ## its allocating thread! ## Use `resizeShared` to reallocate from a shared heap. cast[ptr T](realloc(p, T.sizeof * newSize)) - proc dealloc*(p: pointer) {.noconv, rtl, tags: [], gcsafe.} + proc dealloc*(p: pointer) {.noconv, rtl, tags: [], benign.} ## frees the memory allocated with ``alloc``, ``alloc0`` or ## ``realloc``. This procedure is dangerous! If one forgets to ## free the memory a leak occurs; if one tries to access freed @@ -1371,23 +1382,23 @@ when not defined(nimrodVM): ## or other memory may be corrupted. ## The freed memory must belong to its allocating thread! ## Use `deallocShared` to deallocate from a shared heap. - proc free*[T](p: ptr T) {.inline, gcsafe.} = + proc free*[T](p: ptr T) {.inline, benign.} = dealloc(p) - proc allocShared*(size: int): pointer {.noconv, rtl, gcsafe.} + proc allocShared*(size: int): pointer {.noconv, rtl, benign.} ## allocates a new memory block on the shared heap with at ## least ``size`` bytes. The block has to be freed with ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block ## is not initialized, so reading from it before writing to it is ## undefined behaviour! proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline, - gcsafe.} = + benign.} = ## allocates a new memory block on the shared heap with at ## least ``T.sizeof * size`` bytes. The block has to be freed with ## ``resizeShared(block, 0)`` or ``freeShared(block)``. The block ## is not initialized, so reading from it before writing to it is ## undefined behaviour! cast[ptr T](allocShared(T.sizeof * size)) - proc allocShared0*(size: int): pointer {.noconv, rtl, gcsafe.} + proc allocShared0*(size: int): pointer {.noconv, rtl, benign.} ## allocates a new memory block on the shared heap with at ## least ``size`` bytes. The block has to be freed with ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. @@ -1401,7 +1412,7 @@ when not defined(nimrodVM): ## containing zero, so it is somewhat safer than ``createSharedU``. cast[ptr T](allocShared0(T.sizeof * size)) proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl, - gcsafe.} + benign.} ## grows or shrinks a given memory block on the heap. If p is **nil** ## then a new memory block is returned. In either way the block has at ## least ``newSize`` bytes. If ``newSize == 0`` and p is not **nil** @@ -1414,13 +1425,13 @@ when not defined(nimrodVM): ## not **nil** ``resizeShared`` calls ``freeShared(p)``. In other ## cases the block has to be freed with ``freeShared``. cast[ptr T](reallocShared(p, T.sizeof * newSize)) - proc deallocShared*(p: pointer) {.noconv, rtl, gcsafe.} + proc deallocShared*(p: pointer) {.noconv, rtl, benign.} ## frees the memory allocated with ``allocShared``, ``allocShared0`` or ## ``reallocShared``. This procedure is dangerous! If one forgets to ## free the memory a leak occurs; if one tries to access freed ## memory (or just freeing it twice!) a core dump may happen ## or other memory may be corrupted. - proc freeShared*[T](p: ptr T) {.inline, gcsafe.} = + proc freeShared*[T](p: ptr T) {.inline, benign.} = ## frees the memory allocated with ``createShared``, ``createSharedU`` or ## ``resizeShared``. This procedure is dangerous! If one forgets to ## free the memory a leak occurs; if one tries to access freed @@ -1440,11 +1451,11 @@ template `>%` *(x, y: expr): expr {.immediate.} = y <% x ## treats `x` and `y` as unsigned and compares them. ## Returns true iff ``unsigned(x) > unsigned(y)``. -proc `$` *(x: int): string {.magic: "IntToStr", noSideEffect.} +proc `$`*(x: int): string {.magic: "IntToStr", noSideEffect.} ## The stringify operator for an integer argument. Returns `x` ## converted to a decimal string. -proc `$` *(x: int64): string {.magic: "Int64ToStr", noSideEffect.} +proc `$`*(x: int64): string {.magic: "Int64ToStr", noSideEffect.} ## The stringify operator for an integer argument. Returns `x` ## converted to a decimal string. @@ -1499,6 +1510,20 @@ const ## that you cannot compare a floating point value to this value ## and expect a reasonable result - use the `classify` procedure ## in the module ``math`` for checking for NaN. + NimMajor*: int = 0 + ## is the major number of Nim's version. + + NimMinor*: int = 10 + ## is the minor number of Nim's version. + + NimPatch*: int = 2 + ## is the patch number of Nim's version. + + NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch + ## is the version of Nim as a string. + +{.deprecated: [TEndian: Endianness, NimrodVersion: NimVersion, + NimrodMajor: NimMajor, NimrodMinor: NimMinor, NimrodPatch: NimPatch].} # GC interface: @@ -1560,7 +1585,7 @@ iterator `||`*[S, T](a: S, b: T, annotation=""): T {. ## Note that the compiler maps that to ## the ``#pragma omp parallel for`` construct of `OpenMP`:idx: and as ## such isn't aware of the parallelism in your code! Be careful! Later - ## versions of ``||`` will get proper support by Nimrod's code generator + ## versions of ``||`` will get proper support by Nim's code generator ## and GC. discard @@ -1612,7 +1637,7 @@ proc max*(x, y: float): float {.magic: "MaxF64", noSideEffect.} = proc clamp*[T](x, a, b: T): T = ## limits the value ``x`` within the interval [a, b] ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## assert((1.4).clamp(0.0, 1.0) == 1.0) ## assert((0.5).clamp(0.0, 1.0) == 0.5) if x < a: return a @@ -1716,7 +1741,7 @@ proc `&` *[T](x, y: seq[T]): seq[T] {.noSideEffect.} = ## Concatenates two sequences. ## Requires copying of the sequences. ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## assert(@[1, 2, 3, 4] & @[5, 6] == @[1, 2, 3, 4, 5, 6]) newSeq(result, x.len + y.len) for i in 0..x.len-1: @@ -1728,7 +1753,7 @@ proc `&` *[T](x: seq[T], y: T): seq[T] {.noSideEffect.} = ## Appends element y to the end of the sequence. ## Requires copying of the sequence ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## assert(@[1, 2, 3] & 4 == @[1, 2, 3, 4]) newSeq(result, x.len + 1) for i in 0..x.len-1: @@ -1739,7 +1764,7 @@ proc `&` *[T](x: T, y: seq[T]): seq[T] {.noSideEffect.} = ## Prepends the element x to the beginning of the sequence. ## Requires copying of the sequence ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## assert(1 & @[2, 3, 4] == @[1, 2, 3, 4]) newSeq(result, y.len + 1) result[0] = x @@ -1810,7 +1835,7 @@ proc map*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}): seq[S] = ## Since the input is not modified you can use this version of ``map`` to ## transform the type of the elements in the input sequence. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## let ## a = @[1, 2, 3, 4] ## b = map(a, proc(x: int): string = $x) @@ -1824,7 +1849,7 @@ proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) = ## Note that this version of ``map`` requires your input and output types to ## be the same, since they are modified in-place. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## var a = @["1", "2", "3", "4"] ## echo repr(a) ## # --> ["1", "2", "3", "4"] @@ -1833,7 +1858,7 @@ proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) = ## # --> ["142", "242", "342", "442"] for i in 0..data.len-1: op(data[i]) -iterator fields*[T: tuple|object](x: T): TObject {. +iterator fields*[T: tuple|object](x: T): RootObj {. magic: "Fields", noSideEffect.} ## iterates over every field of `x`. Warning: This really transforms ## the 'for' and unrolls the loop. The current implementation also has a bug @@ -1844,7 +1869,7 @@ iterator fields*[S:tuple|object, T:tuple|object](x: S, y: T): tuple[a,b: expr] { ## Warning: This is really transforms the 'for' and unrolls the loop. ## The current implementation also has a bug that affects symbol binding ## in the loop body. -iterator fieldPairs*[T: tuple|object](x: T): TObject {. +iterator fieldPairs*[T: tuple|object](x: T): RootObj {. magic: "FieldPairs", noSideEffect.} ## Iterates over every field of `x` returning their name and value. ## @@ -1853,7 +1878,7 @@ iterator fieldPairs*[T: tuple|object](x: T): TObject {. ## you want to run for each type. To perform the comparison use the `is ## operator `_. Example: ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## ## type ## Custom = object @@ -1915,7 +1940,7 @@ proc `$`*[T: tuple|object](x: T): string = ## generic ``$`` operator for tuples that is lifted from the components ## of `x`. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## $(23, 45) == "(23, 45)" ## $() == "()" result = "(" @@ -1941,7 +1966,7 @@ proc `$`*[T](x: set[T]): string = ## generic ``$`` operator for sets that is lifted from the components ## of `x`. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ${23, 45} == "{23, 45}" collectionToString(x, "{", "}") @@ -1949,7 +1974,7 @@ proc `$`*[T](x: seq[T]): string = ## generic ``$`` operator for seqs that is lifted from the components ## of `x`. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## $(@[23, 45]) == "@[23, 45]" collectionToString(x, "@[", "]") @@ -1962,41 +1987,44 @@ when false: # ----------------- GC interface --------------------------------------------- when not defined(nimrodVM) and hostOS != "standalone": - proc GC_disable*() {.rtl, inl.} + proc GC_disable*() {.rtl, inl, benign.} ## disables the GC. If called n-times, n calls to `GC_enable` are needed to ## reactivate the GC. Note that in most circumstances one should only disable ## the mark and sweep phase with `GC_disableMarkAndSweep`. - proc GC_enable*() {.rtl, inl.} + proc GC_enable*() {.rtl, inl, benign.} ## enables the GC again. - proc GC_fullCollect*() {.rtl.} + proc GC_fullCollect*() {.rtl, benign.} ## forces a full garbage collection pass. ## Ordinary code does not need to call this (and should not). type - TGC_Strategy* = enum ## the strategy the GC should use for the application + GC_Strategy* = enum ## the strategy the GC should use for the application gcThroughput, ## optimize for throughput gcResponsiveness, ## optimize for responsiveness (default) gcOptimizeTime, ## optimize for speed gcOptimizeSpace ## optimize for memory footprint - proc GC_setStrategy*(strategy: TGC_Strategy) {.rtl, deprecated.} + {.deprecated: [TGC_Strategy: GC_Strategy].} + + proc GC_setStrategy*(strategy: GC_Strategy) {.rtl, deprecated, benign.} ## tells the GC the desired strategy for the application. ## **Deprecated** since version 0.8.14. This has always been a nop. - proc GC_enableMarkAndSweep*() {.rtl.} - proc GC_disableMarkAndSweep*() {.rtl.} + proc GC_enableMarkAndSweep*() {.rtl, benign.} + proc GC_disableMarkAndSweep*() {.rtl, benign.} ## the current implementation uses a reference counting garbage collector ## with a seldomly run mark and sweep phase to free cycles. The mark and ## sweep phase may take a long time and is not needed if the application ## does not create cycles. Thus the mark and sweep phase can be deactivated ## and activated separately from the rest of the GC. - proc GC_getStatistics*(): string {.rtl.} + proc GC_getStatistics*(): string {.rtl, benign.} ## returns an informative string about the GC's activity. This may be useful ## for tweaking. + # XXX mark these as 'locks: 0' once 0.10.0 has been released proc GC_ref*[T](x: ref T) {.magic: "GCref", gcsafe.} proc GC_ref*[T](x: seq[T]) {.magic: "GCref", gcsafe.} proc GC_ref*(x: string) {.magic: "GCref", gcsafe.} @@ -2015,7 +2043,7 @@ template accumulateResult*(iter: expr) = for x in iter: add(result, x) # we have to compute this here before turning it off in except.nim anyway ... -const nimrodStackTrace = compileOption("stacktrace") +const NimStackTrace = compileOption("stacktrace") {.push checks: off.} # obviously we cannot generate checking operations here :-) @@ -2024,7 +2052,7 @@ const nimrodStackTrace = compileOption("stacktrace") # of the code var - globalRaiseHook*: proc (e: ref E_Base): bool {.nimcall, gcsafe.} + globalRaiseHook*: proc (e: ref Exception): bool {.nimcall, benign.} ## with this hook you can influence exception handling on a global level. ## If not nil, every 'raise' statement ends up calling this hook. Ordinary ## application code should never set this hook! You better know what you @@ -2032,7 +2060,7 @@ var ## exception is caught and does not propagate further through the call ## stack. - localRaiseHook* {.threadvar.}: proc (e: ref E_Base): bool {.nimcall, gcsafe.} + localRaiseHook* {.threadvar.}: proc (e: ref Exception): bool {.nimcall, benign.} ## with this hook you can influence exception handling on a ## thread local level. ## If not nil, every 'raise' statement ends up calling this hook. Ordinary @@ -2040,13 +2068,13 @@ var ## do when setting this. If ``localRaiseHook`` returns false, the exception ## is caught and does not propagate further through the call stack. - outOfMemHook*: proc () {.nimcall, tags: [], gcsafe.} + outOfMemHook*: proc () {.nimcall, tags: [], benign.} ## set this variable to provide a procedure that should be called ## in case of an `out of memory`:idx: event. The standard handler ## writes an error message and terminates the program. `outOfMemHook` can ## be used to raise an exception in case of OOM like so: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## var gOutOfMem: ref EOutOfMemory ## new(gOutOfMem) # need to be allocated *before* OOM really happened! @@ -2092,7 +2120,7 @@ elif hostOS != "standalone": inc(i) {.pop.} -proc echo*[T](x: varargs[T, `$`]) {.magic: "Echo", tags: [FWriteIO], gcsafe.} +proc echo*(x: varargs[expr, `$`]) {.magic: "Echo", tags: [WriteIOEffect], benign.} ## Writes and flushes the parameters to the standard output. ## ## Special built-in that takes a variable number of arguments. Each argument @@ -2107,8 +2135,8 @@ proc echo*[T](x: varargs[T, `$`]) {.magic: "Echo", tags: [FWriteIO], gcsafe.} ## `_ you can use `debugEcho <#debugEcho>`_ ## instead. -proc debugEcho*[T](x: varargs[T, `$`]) {.magic: "Echo", noSideEffect, - tags: [], raises: [].} +proc debugEcho*(x: varargs[expr, `$`]) {.magic: "Echo", noSideEffect, + tags: [], raises: [].} ## Same as `echo <#echo>`_, but as a special semantic rule, ``debugEcho`` ## pretends to be free of side effects, so that it can be used for debugging ## routines marked as `noSideEffect `_. @@ -2145,9 +2173,14 @@ when not declared(sysFatal): e.msg = message & arg raise e -proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", gcsafe.} - ## get type information for `x`. Ordinary code should not use this, but - ## the `typeinfo` module instead. +when defined(nimlocks): + proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", gcsafe, locks: 0.} + ## get type information for `x`. Ordinary code should not use this, but + ## the `typeinfo` module instead. +else: + proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", gcsafe.} + ## get type information for `x`. Ordinary code should not use this, but + ## the `typeinfo` module instead. {.push stackTrace: off.} proc abs*(x: int): int {.magic: "AbsI", noSideEffect.} = @@ -2224,9 +2257,9 @@ when not defined(JS): #and not defined(NimrodVM): type CFile {.importc: "FILE", header: "", final, incompletestruct.} = object - TFile* = ptr CFile ## The type representing a file handle. + File* = ptr CFile ## The type representing a file handle. - TFileMode* = enum ## The file mode when opening a file. + FileMode* = enum ## The file mode when opening a file. fmRead, ## Open the file for read access only. fmWrite, ## Open the file for write access only. fmReadWrite, ## Open the file for read and write access. @@ -2238,103 +2271,108 @@ when not defined(JS): #and not defined(NimrodVM): fmAppend ## Open the file for writing only; append data ## at the end. - TFileHandle* = cint ## type that represents an OS file handle; this is - ## useful for low-level file access + FileHandle* = cint ## type that represents an OS file handle; this is + ## useful for low-level file access + + {.deprecated: [TFile: File, TFileHandle: FileHandle, TFileMode: FileMode].} # text file handling: var - stdin* {.importc: "stdin", header: "".}: TFile + stdin* {.importc: "stdin", header: "".}: File ## The standard input stream. - stdout* {.importc: "stdout", header: "".}: TFile + stdout* {.importc: "stdout", header: "".}: File ## The standard output stream. - stderr* {.importc: "stderr", header: "".}: TFile + stderr* {.importc: "stderr", header: "".}: File ## The standard error stream. when defined(useStdoutAsStdmsg): - template stdmsg*: TFile = stdout + template stdmsg*: File = stdout else: - template stdmsg*: TFile = stderr + template stdmsg*: File = stderr ## Template which expands to either stdout or stderr depending on ## `useStdoutAsStdmsg` compile-time switch. - proc open*(f: var TFile, filename: string, - mode: TFileMode = fmRead, bufSize: int = -1): bool {.tags: [], - gcsafe.} + proc open*(f: var File, filename: string, + mode: FileMode = fmRead, bufSize: int = -1): bool {.tags: [], + benign.} ## Opens a file named `filename` with given `mode`. ## ## Default mode is readonly. Returns true iff the file could be opened. ## This throws no exception if the file could not be opened. - proc open*(f: var TFile, filehandle: TFileHandle, - mode: TFileMode = fmRead): bool {.tags: [], gcsafe.} + proc open*(f: var File, filehandle: FileHandle, + mode: FileMode = fmRead): bool {.tags: [], benign.} ## Creates a ``TFile`` from a `filehandle` with given `mode`. ## ## Default mode is readonly. Returns true iff the file could be opened. proc open*(filename: string, - mode: TFileMode = fmRead, bufSize: int = -1): TFile = + mode: FileMode = fmRead, bufSize: int = -1): File = ## Opens a file named `filename` with given `mode`. ## ## Default mode is readonly. Raises an ``IO`` exception if the file ## could not be opened. if not open(result, filename, mode, bufSize): - sysFatal(EIO, "cannot open: ", filename) + sysFatal(IOError, "cannot open: ", filename) - proc reopen*(f: TFile, filename: string, mode: TFileMode = fmRead): bool {. - tags: [], gcsafe.} + proc reopen*(f: File, filename: string, mode: FileMode = fmRead): bool {. + tags: [], benign.} ## reopens the file `f` with given `filename` and `mode`. This ## is often used to redirect the `stdin`, `stdout` or `stderr` ## file variables. ## ## Default mode is readonly. Returns true iff the file could be reopened. - proc close*(f: TFile) {.importc: "fclose", header: "", tags: [].} + proc close*(f: File) {.importc: "fclose", header: "", tags: [].} ## Closes the file. - proc endOfFile*(f: TFile): bool {.tags: [], gcsafe.} + proc endOfFile*(f: File): bool {.tags: [], benign.} ## Returns true iff `f` is at the end. - proc readChar*(f: TFile): char {. - importc: "fgetc", header: "", tags: [FReadIO].} + proc readChar*(f: File): char {. + importc: "fgetc", header: "", tags: [ReadIOEffect].} ## Reads a single character from the stream `f`. - proc flushFile*(f: TFile) {. - importc: "fflush", header: "", tags: [FWriteIO].} + proc flushFile*(f: File) {. + importc: "fflush", header: "", tags: [WriteIOEffect].} ## Flushes `f`'s buffer. - proc readAll*(file: TFile): TaintedString {.tags: [FReadIO], gcsafe.} + proc readAll*(file: File): TaintedString {.tags: [ReadIOEffect], benign.} ## Reads all data from the stream `file`. ## ## Raises an IO exception in case of an error. It is an error if the ## current file position is not at the beginning of the file. - proc readFile*(filename: string): TaintedString {.tags: [FReadIO], gcsafe.} - ## Opens a file named `filename` for reading. Then calls `readAll` - ## and closes the file afterwards. Returns the string. - ## Raises an IO exception in case of an error. + proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.} + ## Opens a file named `filename` for reading. + ## + ## Then calls `readAll <#readAll>`_ and closes the file afterwards. + ## Returns the string. Raises an IO exception in case of an error. If + ## you need to call this inside a compile time macro you can use + ## `staticRead <#staticRead>`_. - proc writeFile*(filename, content: string) {.tags: [FWriteIO], gcsafe.} + proc writeFile*(filename, content: string) {.tags: [WriteIOEffect], benign.} ## Opens a file named `filename` for writing. Then writes the ## `content` completely to the file and closes the file afterwards. ## Raises an IO exception in case of an error. - proc write*(f: TFile, r: float32) {.tags: [FWriteIO], gcsafe.} - proc write*(f: TFile, i: int) {.tags: [FWriteIO], gcsafe.} - proc write*(f: TFile, i: BiggestInt) {.tags: [FWriteIO], gcsafe.} - proc write*(f: TFile, r: BiggestFloat) {.tags: [FWriteIO], gcsafe.} - proc write*(f: TFile, s: string) {.tags: [FWriteIO], gcsafe.} - proc write*(f: TFile, b: bool) {.tags: [FWriteIO], gcsafe.} - proc write*(f: TFile, c: char) {.tags: [FWriteIO], gcsafe.} - proc write*(f: TFile, c: cstring) {.tags: [FWriteIO], gcsafe.} - proc write*(f: TFile, a: varargs[string, `$`]) {.tags: [FWriteIO], gcsafe.} + proc write*(f: File, r: float32) {.tags: [WriteIOEffect], benign.} + proc write*(f: File, i: int) {.tags: [WriteIOEffect], benign.} + proc write*(f: File, i: BiggestInt) {.tags: [WriteIOEffect], benign.} + proc write*(f: File, r: BiggestFloat) {.tags: [WriteIOEffect], benign.} + proc write*(f: File, s: string) {.tags: [WriteIOEffect], benign.} + proc write*(f: File, b: bool) {.tags: [WriteIOEffect], benign.} + proc write*(f: File, c: char) {.tags: [WriteIOEffect], benign.} + proc write*(f: File, c: cstring) {.tags: [WriteIOEffect], benign.} + proc write*(f: File, a: varargs[string, `$`]) {.tags: [WriteIOEffect], benign.} ## Writes a value to the file `f`. May throw an IO exception. - proc readLine*(f: TFile): TaintedString {.tags: [FReadIO], gcsafe.} + proc readLine*(f: File): TaintedString {.tags: [ReadIOEffect], benign.} ## reads a line of text from the file `f`. May throw an IO exception. ## A line of text may be delimited by ``CR``, ``LF`` or ## ``CRLF``. The newline character(s) are not part of the returned string. - proc readLine*(f: TFile, line: var TaintedString): bool {.tags: [FReadIO], - gcsafe.} + proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect], + benign.} ## reads a line of text from the file `f` into `line`. `line` must not be ## ``nil``! May throw an IO exception. ## A line of text may be delimited by ``CR``, ``LF`` or @@ -2343,66 +2381,69 @@ when not defined(JS): #and not defined(NimrodVM): ## otherwise. If ``false`` is returned `line` contains no new data. when not defined(booting): - proc writeln*[Ty](f: TFile, x: varargs[Ty, `$`]) {.inline, - tags: [FWriteIO], gcsafe.} + proc writeln*[Ty](f: File, x: varargs[Ty, `$`]) {.inline, + tags: [WriteIOEffect], gcsafe, locks: 0.} ## writes the values `x` to `f` and then writes "\n". ## May throw an IO exception. else: - proc writeln*[Ty](f: TFile, x: varargs[Ty, `$`]) {.inline, - tags: [FWriteIO].} + proc writeln*[Ty](f: File, x: varargs[Ty, `$`]) {.inline, + tags: [WriteIOEffect].} - proc getFileSize*(f: TFile): int64 {.tags: [FReadIO], gcsafe.} + proc getFileSize*(f: File): int64 {.tags: [ReadIOEffect], benign.} ## retrieves the file size (in bytes) of `f`. - proc readBytes*(f: TFile, a: var openArray[int8], start, len: int): int {. - tags: [FReadIO], gcsafe.} + proc readBytes*(f: File, a: var openArray[int8], start, len: int): int {. + tags: [ReadIOEffect], benign.} ## reads `len` bytes into the buffer `a` starting at ``a[start]``. Returns ## the actual number of bytes that have been read which may be less than ## `len` (if not as many bytes are remaining), but not greater. - proc readChars*(f: TFile, a: var openArray[char], start, len: int): int {. - tags: [FReadIO], gcsafe.} + proc readChars*(f: File, a: var openArray[char], start, len: int): int {. + tags: [ReadIOEffect], benign.} ## reads `len` bytes into the buffer `a` starting at ``a[start]``. Returns ## the actual number of bytes that have been read which may be less than ## `len` (if not as many bytes are remaining), but not greater. - proc readBuffer*(f: TFile, buffer: pointer, len: int): int {. - tags: [FReadIO], gcsafe.} + proc readBuffer*(f: File, buffer: pointer, len: int): int {. + tags: [ReadIOEffect], benign.} ## reads `len` bytes into the buffer pointed to by `buffer`. Returns ## the actual number of bytes that have been read which may be less than ## `len` (if not as many bytes are remaining), but not greater. - proc writeBytes*(f: TFile, a: openArray[int8], start, len: int): int {. - tags: [FWriteIO], gcsafe.} + proc writeBytes*(f: File, a: openArray[int8], start, len: int): int {. + tags: [WriteIOEffect], benign.} ## writes the bytes of ``a[start..start+len-1]`` to the file `f`. Returns ## the number of actual written bytes, which may be less than `len` in case ## of an error. - proc writeChars*(f: TFile, a: openArray[char], start, len: int): int {. - tags: [FWriteIO], gcsafe.} + proc writeChars*(f: File, a: openArray[char], start, len: int): int {. + tags: [WriteIOEffect], benign.} ## writes the bytes of ``a[start..start+len-1]`` to the file `f`. Returns ## the number of actual written bytes, which may be less than `len` in case ## of an error. - proc writeBuffer*(f: TFile, buffer: pointer, len: int): int {. - tags: [FWriteIO], gcsafe.} + proc writeBuffer*(f: File, buffer: pointer, len: int): int {. + tags: [WriteIOEffect], benign.} ## writes the bytes of buffer pointed to by the parameter `buffer` to the ## file `f`. Returns the number of actual written bytes, which may be less ## than `len` in case of an error. - proc setFilePos*(f: TFile, pos: int64) {.gcsafe.} + proc setFilePos*(f: File, pos: int64) {.benign.} ## sets the position of the file pointer that is used for read/write ## operations. The file's first byte has the index zero. - proc getFilePos*(f: TFile): int64 {.gcsafe.} + proc getFilePos*(f: File): int64 {.benign.} ## retrieves the current position of the file pointer that is used to ## read from the file `f`. The file's first byte has the index zero. - proc fileHandle*(f: TFile): TFileHandle {.importc: "fileno", - header: ""} + proc getFileHandle*(f: File): FileHandle {.importc: "fileno", + header: ""} ## returns the OS file handle of the file ``f``. This is only useful for ## platform specific programming. + when not defined(nimfix): + {.deprecated: [fileHandle: getFileHandle].} + proc cstringArrayToSeq*(a: cstringArray, len: int): seq[string] = ## converts a ``cstringArray`` to a ``seq[string]``. `a` is supposed to be ## of length ``len``. @@ -2438,11 +2479,11 @@ when not defined(JS): #and not defined(NimrodVM): when not defined(NimrodVM): proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, - discardable, gcsafe.} + discardable, benign.} ## atomic increment of `memLoc`. Returns the value after the operation. proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, - discardable, gcsafe.} + discardable, benign.} ## atomic decrement of `memLoc`. Returns the value after the operation. include "system/atomics" @@ -2454,7 +2495,7 @@ when not defined(JS): #and not defined(NimrodVM): status: int context: C_JmpBuf hasRaiseAction: bool - raiseAction: proc (e: ref E_Base): bool {.closure.} + raiseAction: proc (e: ref Exception): bool {.closure.} when declared(initAllocator): initAllocator() @@ -2470,14 +2511,14 @@ when not defined(JS): #and not defined(NimrodVM): ## allows you to override the behaviour of your application when CTRL+C ## is pressed. Only one such hook is supported. - proc writeStackTrace*() {.tags: [FWriteIO].} + proc writeStackTrace*() {.tags: [WriteIOEffect].} ## writes the current stack trace to ``stderr``. This is only works ## for debug builds. when hostOS != "standalone": proc getStackTrace*(): string ## gets the current stack trace. This only works for debug builds. - proc getStackTrace*(e: ref E_Base): string + proc getStackTrace*(e: ref Exception): string ## gets the stack trace associated with `e`, which is the stack that ## lead to the ``raise`` statement. This only works for debug builds. @@ -2505,7 +2546,7 @@ when not defined(JS): #and not defined(NimrodVM): proc getDiscriminant(aa: pointer, n: ptr TNimNode): int = sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase") var d: int - var a = cast[TAddress](aa) + var a = cast[ByteAddress](aa) case n.typ.size of 1: d = ze(cast[ptr int8](a +% n.offset)[]) of 2: d = ze(cast[ptr int16](a +% n.offset)[]) @@ -2534,13 +2575,13 @@ when not defined(JS): #and not defined(NimrodVM): include "system/sysio" when hostOS != "standalone": - iterator lines*(filename: string): TaintedString {.tags: [FReadIO].} = + iterator lines*(filename: string): TaintedString {.tags: [ReadIOEffect].} = ## Iterates over any line in the file named `filename`. ## ## If the file does not exist `EIO` is raised. The trailing newline ## character(s) are removed from the iterated lines. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## import strutils ## ## proc transformLetters(filename: string) = @@ -2553,14 +2594,14 @@ when not defined(JS): #and not defined(NimrodVM): while f.readLine(res): yield res close(f) - iterator lines*(f: TFile): TaintedString {.tags: [FReadIO].} = + iterator lines*(f: File): TaintedString {.tags: [ReadIOEffect].} = ## Iterate over any line in the file `f`. ## ## The trailing newline character(s) are removed from the iterated lines. ## Example: ## - ## .. code-block:: nimrod - ## proc countZeros(filename: TFile): tuple[lines, zeros: int] = + ## .. code-block:: nim + ## proc countZeros(filename: File): tuple[lines, zeros: int] = ## for line in filename.lines: ## for letter in line: ## if letter == '0': @@ -2573,17 +2614,17 @@ when not defined(JS): #and not defined(NimrodVM): include "system/assign" include "system/repr" - proc getCurrentException*(): ref E_Base {.compilerRtl, inl, gcsafe.} = + proc getCurrentException*(): ref Exception {.compilerRtl, inl, benign.} = ## retrieves the current exception; if there is none, nil is returned. result = currException - proc getCurrentExceptionMsg*(): string {.inline, gcsafe.} = + proc getCurrentExceptionMsg*(): string {.inline, benign.} = ## retrieves the error message that was attached to the current ## exception; if there is none, "" is returned. var e = getCurrentException() return if e == nil: "" else: e.msg - proc onRaise*(action: proc(e: ref E_Base): bool{.closure.}) = + proc onRaise*(action: proc(e: ref Exception): bool{.closure.}) = ## can be used in a ``try`` statement to setup a Lisp-like ## `condition system`:idx:\: This prevents the 'raise' statement to ## raise an exception but instead calls ``action``. @@ -2593,6 +2634,12 @@ when not defined(JS): #and not defined(NimrodVM): excHandler.hasRaiseAction = true excHandler.raiseAction = action + proc setCurrentException*(exc: ref Exception) {.inline, benign.} = + ## sets the current exception. + ## + ## **Warning**: Only use this if you know what you are doing. + currException = exc + {.push stack_trace: off, profiler:off.} when defined(endb) and not defined(NimrodVM): include "system/debugger" @@ -2609,7 +2656,7 @@ when not defined(JS): #and not defined(NimrodVM): ## platforms this can help the processor predict better which branch is ## going to be run. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## for value in inputValues: ## if likely(value <= 100): ## process(value) @@ -2623,7 +2670,7 @@ when not defined(JS): #and not defined(NimrodVM): ## platforms this can help the processor predict better which branch is ## going to be run. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## for value in inputValues: ## if unlikely(value > 100): ## echo "Value too big!" @@ -2657,7 +2704,7 @@ elif defined(JS): proc GC_disable() = discard proc GC_enable() = discard proc GC_fullCollect() = discard - proc GC_setStrategy(strategy: TGC_Strategy) = discard + proc GC_setStrategy(strategy: GC_Strategy) = discard proc GC_enableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard proc GC_getStatistics(): string = return "" @@ -2669,7 +2716,7 @@ elif defined(JS): proc dealloc(p: pointer) = discard proc alloc(size: int): pointer = discard proc alloc0(size: int): pointer = discard - proc realloc(p: Pointer, newsize: int): pointer = discard + proc realloc(p: pointer, newsize: int): pointer = discard proc allocShared(size: int): pointer = discard proc allocShared0(size: int): pointer = discard @@ -2721,16 +2768,16 @@ template spliceImpl(s, a, L, b: expr): stmt {.immediate.} = for i in 0 .. `_. proc staticRead*(filename: string): string {.magic: "Slurp".} - ## Compile-time ``readFile`` proc for easy `resource`:idx: embedding: + ## Compile-time `readFile <#readFile>`_ proc for easy `resource`:idx: + ## embedding: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## const myResource = staticRead"mydatafile.bin" ## - ## ``slurp`` is an alias for ``staticRead``. + ## `slurp <#slurp>`_ is an alias for ``staticRead``. proc gorge*(command: string, input = ""): string {. magic: "StaticExec".} = discard - ## This is an alias for ``staticExec``. + ## This is an alias for `staticExec <#staticExec>`_. proc staticExec*(command: string, input = ""): string {. magic: "StaticExec".} = discard @@ -2822,21 +2870,21 @@ proc staticExec*(command: string, input = ""): string {. ## if `input` is not an empty string, it will be passed as a standard input ## to the executed program. ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") & ## "\nCompiled on " & staticExec("uname -v") ## - ## ``gorge`` is an alias for ``staticExec``. Note that you can use this proc - ## inside a pragma like `passC `_ or `passL - ## `_. + ## `gorge <#gorge>`_ is an alias for ``staticExec``. Note that you can use + ## this proc inside a pragma like `passC `_ or `passL + ## `_. -proc `+=`*[T: TOrdinal|uint|uint64](x: var T, y: T) {.magic: "Inc", noSideEffect.} +proc `+=`*[T: SomeOrdinal|uint|uint64](x: var T, y: T) {.magic: "Inc", noSideEffect.} ## Increments an ordinal -proc `-=`*[T: TOrdinal|uint|uint64](x: var T, y: T) {.magic: "Dec", noSideEffect.} +proc `-=`*[T: SomeOrdinal|uint|uint64](x: var T, y: T) {.magic: "Dec", noSideEffect.} ## Decrements an ordinal -proc `*=`*[T: TOrdinal|uint|uint64](x: var T, y: T) {.inline, noSideEffect.} = +proc `*=`*[T: SomeOrdinal|uint|uint64](x: var T, y: T) {.inline, noSideEffect.} = ## Binary `*=` operator for ordinals x = x * y @@ -2852,7 +2900,11 @@ proc `*=`*[T: float|float32|float64] (x: var T, y: T) {.inline, noSideEffect.} = ## Multiplies in place a floating point number x = x * y -proc `/=`*[T: float|float32|float64] (x: var T, y: T) {.inline, noSideEffect.} = +proc `/=`*(x: var float64, y: float64) {.inline, noSideEffect.} = + ## Divides in place a floating point number + x = x / y + +proc `/=`*[T: float|float32](x: var T, y: T) {.inline, noSideEffect.} = ## Divides in place a floating point number x = x / y @@ -2870,7 +2922,7 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[ ## to retrieve information about the current filename and line number. ## Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## import strutils ## ## template testException(exception, code: expr): stmt = @@ -2897,7 +2949,7 @@ template currentSourcePath*: string = instantiationInfo(-1, true).filename ## returns the full file-system path of the current source proc raiseAssert*(msg: string) {.noinline.} = - sysFatal(EAssertionFailed, msg) + sysFatal(AssertionError, msg) when true: proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} = @@ -2908,16 +2960,13 @@ when true: THide(raiseAssert)(msg) template assert*(cond: bool, msg = "") = - ## Raises ``EAssertionFailure`` with `msg` if `cond` is false. - ## - ## Provides a means to implement `programming by contracts`:idx: in Nimrod. - ## ``assert`` evaluates expression ``cond`` and if ``cond`` is false, it - ## raises an ``EAssertionFailure`` exception. However, the compiler may not - ## generate any code at all for ``assert`` if it is advised to do so through - ## the ``-d:release`` or ``--assertions:off`` `command line switches - ## `_. - ## - ## Use ``assert`` for debugging purposes only. + ## Raises ``AssertionError`` with `msg` if `cond` is false. Note + ## that ``AssertionError`` is hidden from the effect system, so it doesn't + ## produce ``{.raises: [AssertionError].}``. This exception is only supposed + ## to be caught by unit testing frameworks. + ## The compiler may not generate any code at all for ``assert`` if it is + ## advised to do so through the ``-d:release`` or ``--assertions:off`` + ## `command line switches `_. bind instantiationInfo mixin failedAssertImpl when compileOption("assertions"): @@ -2958,7 +3007,7 @@ template onFailedAssert*(msg: expr, code: stmt): stmt {.dirty, immediate.} = ## statements following `onFailedAssert` in the current lexical scope. ## Can be defined multiple times in a single function. ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## proc example(x: int): TErrorCode = ## onFailedAssert(msg): @@ -2995,7 +3044,7 @@ proc shallow*(s: var string) {.noSideEffect, inline.} = type TNimrodNode {.final.} = object PNimrodNode* {.magic: "PNimrodNode".} = ref TNimrodNode - ## represents a Nimrod AST node. Macros operate on this type. + ## represents a Nim AST node. Macros operate on this type. when false: template eval*(blk: stmt): stmt = @@ -3024,7 +3073,7 @@ proc compiles*(x): bool {.magic: "Compiles", noSideEffect.} = ## without any semantic error. ## This can be used to check whether a type supports some operation: ## - ## .. code-block:: Nimrod + ## .. code-block:: Nim ## when not compiles(3 + 4): ## echo "'+' for integers is available" discard @@ -3046,14 +3095,14 @@ when hostOS != "standalone": if x == nil: x = y else: x.add(y) -proc locals*(): TObject {.magic: "Locals", noSideEffect.} = +proc locals*(): RootObj {.magic: "Locals", noSideEffect.} = ## generates a tuple constructor expression listing all the local variables ## in the current scope. This is quite fast as it does not rely ## on any debug or runtime information. Note that in constrast to what - ## the official signature says, the return type is not ``TObject`` but a + ## the official signature says, the return type is not ``RootObj`` but a ## tuple of a structure that depends on the current scope. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## proc testLocals() = ## var ## a = "something" @@ -3078,4 +3127,13 @@ when hostOS != "standalone" and not defined(NimrodVM) and not defined(JS): include "system/deepcopy" +proc procCall*(x: expr) {.magic: "ProcCall".} = + ## special magic to prohibit dynamic binding for `method`:idx: calls. + ## This is similar to `super`:idx: in ordinary OO languages. + ## + ## .. code-block:: nim + ## # 'someMethod' will be resolved fully statically: + ## procCall someMethod(a, b) + discard + {.pop.} #{.push warning[GcMem]: off.} diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 602e5c7fa..fd3ced832 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -1,13 +1,13 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -# Low level allocator for Nimrod. Has been designed to support the GC. +# Low level allocator for Nim. Has been designed to support the GC. # TODO: # - eliminate "used" field # - make searching for block O(1) @@ -218,7 +218,7 @@ proc llAlloc(a: var TMemRegion, size: int): pointer = a.llmem.size = PageSize - sizeof(TLLChunk) a.llmem.acc = sizeof(TLLChunk) a.llmem.next = old - result = cast[pointer](cast[TAddress](a.llmem) + a.llmem.acc) + result = cast[pointer](cast[ByteAddress](a.llmem) + a.llmem.acc) dec(a.llmem.size, size) inc(a.llmem.acc, size) zeroMem(result, size) @@ -321,7 +321,7 @@ iterator allObjects(m: TMemRegion): pointer {.inline.} = var c = cast[PSmallChunk](c) let size = c.size - var a = cast[TAddress](addr(c.data)) + var a = cast[ByteAddress](addr(c.data)) let limit = a + c.acc while a <% limit: yield cast[pointer](a) @@ -335,27 +335,27 @@ proc isCell(p: pointer): bool {.inline.} = # ------------- chunk management ---------------------------------------------- proc pageIndex(c: PChunk): int {.inline.} = - result = cast[TAddress](c) shr PageShift + result = cast[ByteAddress](c) shr PageShift proc pageIndex(p: pointer): int {.inline.} = - result = cast[TAddress](p) shr PageShift + result = cast[ByteAddress](p) shr PageShift proc pageAddr(p: pointer): PChunk {.inline.} = - result = cast[PChunk](cast[TAddress](p) and not PageMask) + result = cast[PChunk](cast[ByteAddress](p) and not PageMask) #sysAssert(Contains(allocator.chunkStarts, pageIndex(result))) proc requestOsChunks(a: var TMemRegion, size: int): PBigChunk = incCurrMem(a, size) inc(a.freeMem, size) result = cast[PBigChunk](osAllocPages(size)) - sysAssert((cast[TAddress](result) and PageMask) == 0, "requestOsChunks 1") + sysAssert((cast[ByteAddress](result) and PageMask) == 0, "requestOsChunks 1") #zeroMem(result, size) result.next = nil result.prev = nil result.used = false result.size = size # update next.prevSize: - var nxt = cast[TAddress](result) +% size + var nxt = cast[ByteAddress](result) +% size sysAssert((nxt and PageMask) == 0, "requestOsChunks 2") var next = cast[PChunk](nxt) if pageIndex(next) in a.chunkStarts: @@ -363,7 +363,7 @@ proc requestOsChunks(a: var TMemRegion, size: int): PBigChunk = next.prevSize = size # set result.prevSize: var lastSize = if a.lastSize != 0: a.lastSize else: PageSize - var prv = cast[TAddress](result) -% lastSize + var prv = cast[ByteAddress](result) -% lastSize sysAssert((nxt and PageMask) == 0, "requestOsChunks 3") var prev = cast[PChunk](prv) if pageIndex(prev) in a.chunkStarts and prev.size == lastSize: @@ -376,7 +376,7 @@ proc requestOsChunks(a: var TMemRegion, size: int): PBigChunk = proc freeOsChunks(a: var TMemRegion, p: pointer, size: int) = # update next.prevSize: var c = cast[PChunk](p) - var nxt = cast[TAddress](p) +% c.size + var nxt = cast[ByteAddress](p) +% c.size sysAssert((nxt and PageMask) == 0, "freeOsChunks") var next = cast[PChunk](nxt) if pageIndex(next) in a.chunkStarts: @@ -429,8 +429,8 @@ proc listRemove[T](head: var T, c: T) {.inline.} = proc updatePrevSize(a: var TMemRegion, c: PBigChunk, prevSize: int) {.inline.} = - var ri = cast[PChunk](cast[TAddress](c) +% c.size) - sysAssert((cast[TAddress](ri) and PageMask) == 0, "updatePrevSize") + var ri = cast[PChunk](cast[ByteAddress](c) +% c.size) + sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "updatePrevSize") if isAccessible(a, ri): ri.prevSize = prevSize @@ -439,8 +439,8 @@ proc freeBigChunk(a: var TMemRegion, c: PBigChunk) = sysAssert(c.size >= PageSize, "freeBigChunk") inc(a.freeMem, c.size) when coalescRight: - var ri = cast[PChunk](cast[TAddress](c) +% c.size) - sysAssert((cast[TAddress](ri) and PageMask) == 0, "freeBigChunk 2") + var ri = cast[PChunk](cast[ByteAddress](c) +% c.size) + sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "freeBigChunk 2") if isAccessible(a, ri) and chunkUnused(ri): sysAssert(not isSmallChunk(ri), "freeBigChunk 3") if not isSmallChunk(ri): @@ -449,8 +449,8 @@ proc freeBigChunk(a: var TMemRegion, c: PBigChunk) = excl(a.chunkStarts, pageIndex(ri)) when coalescLeft: if c.prevSize != 0: - var le = cast[PChunk](cast[TAddress](c) -% c.prevSize) - sysAssert((cast[TAddress](le) and PageMask) == 0, "freeBigChunk 4") + var le = cast[PChunk](cast[ByteAddress](c) -% c.prevSize) + sysAssert((cast[ByteAddress](le) and PageMask) == 0, "freeBigChunk 4") if isAccessible(a, le) and chunkUnused(le): sysAssert(not isSmallChunk(le), "freeBigChunk 5") if not isSmallChunk(le): @@ -468,7 +468,7 @@ proc freeBigChunk(a: var TMemRegion, c: PBigChunk) = freeOsChunks(a, c, c.size) proc splitChunk(a: var TMemRegion, c: PBigChunk, size: int) = - var rest = cast[PBigChunk](cast[TAddress](c) +% size) + var rest = cast[PBigChunk](cast[ByteAddress](c) +% size) sysAssert(rest notin a.freeChunksList, "splitChunk") rest.size = c.size - size rest.used = false @@ -514,7 +514,7 @@ proc getSmallChunk(a: var TMemRegion): PSmallChunk = result = cast[PSmallChunk](res) # ----------------------------------------------------------------------------- -proc isAllocatedPtr(a: TMemRegion, p: pointer): bool +proc isAllocatedPtr(a: TMemRegion, p: pointer): bool {.benign.} proc allocInv(a: TMemRegion): bool = ## checks some (not all yet) invariants of the allocator's data structures. @@ -559,7 +559,7 @@ proc rawAlloc(a: var TMemRegion, requestedSize: int): pointer = c.prev = nil listAdd(a.freeSmallChunks[s], c) result = addr(c.data) - sysAssert((cast[TAddress](result) and (MemAlign-1)) == 0, "rawAlloc 4") + sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 4") else: sysAssert(allocInv(a), "rawAlloc: begin c != nil") sysAssert c.next != c, "rawAlloc 5" @@ -569,21 +569,21 @@ proc rawAlloc(a: var TMemRegion, requestedSize: int): pointer = if c.freeList == nil: sysAssert(c.acc + smallChunkOverhead() + size <= SmallChunkSize, "rawAlloc 7") - result = cast[pointer](cast[TAddress](addr(c.data)) +% c.acc) + result = cast[pointer](cast[ByteAddress](addr(c.data)) +% c.acc) inc(c.acc, size) else: result = c.freeList sysAssert(c.freeList.zeroField == 0, "rawAlloc 8") c.freeList = c.freeList.next dec(c.free, size) - sysAssert((cast[TAddress](result) and (MemAlign-1)) == 0, "rawAlloc 9") + sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 9") sysAssert(allocInv(a), "rawAlloc: end c != nil") sysAssert(allocInv(a), "rawAlloc: before c.free < size") if c.free < size: sysAssert(allocInv(a), "rawAlloc: before listRemove test") listRemove(a.freeSmallChunks[s], c) sysAssert(allocInv(a), "rawAlloc: end listRemove test") - sysAssert(((cast[TAddress](result) and PageMask) - smallChunkOverhead()) %% + sysAssert(((cast[ByteAddress](result) and PageMask) - smallChunkOverhead()) %% size == 0, "rawAlloc 21") sysAssert(allocInv(a), "rawAlloc: end small size") else: @@ -594,9 +594,9 @@ proc rawAlloc(a: var TMemRegion, requestedSize: int): pointer = sysAssert c.next == nil, "rawAlloc 11" sysAssert c.size == size, "rawAlloc 12" result = addr(c.data) - sysAssert((cast[TAddress](result) and (MemAlign-1)) == 0, "rawAlloc 13") + sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 13") if a.root == nil: a.root = bottom - add(a, a.root, cast[TAddress](result), cast[TAddress](result)+%size) + add(a, a.root, cast[ByteAddress](result), cast[ByteAddress](result)+%size) sysAssert(isAccessible(a, result), "rawAlloc 14") sysAssert(allocInv(a), "rawAlloc: end") when logAlloc: cprintf("rawAlloc: %ld %p\n", requestedSize, result) @@ -613,7 +613,7 @@ proc rawDealloc(a: var TMemRegion, p: pointer) = # `p` is within a small chunk: var c = cast[PSmallChunk](c) var s = c.size - sysAssert(((cast[TAddress](p) and PageMask) - smallChunkOverhead()) %% + sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %% s == 0, "rawDealloc 3") var f = cast[ptr TFreeCell](p) #echo("setting to nil: ", $cast[TAddress](addr(f.zeroField))) @@ -636,7 +636,7 @@ proc rawDealloc(a: var TMemRegion, p: pointer) = listRemove(a.freeSmallChunks[s div MemAlign], c) c.size = SmallChunkSize freeBigChunk(a, cast[PBigChunk](c)) - sysAssert(((cast[TAddress](p) and PageMask) - smallChunkOverhead()) %% + sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %% s == 0, "rawDealloc 2") else: # set to 0xff to check for usage after free bugs: @@ -655,7 +655,7 @@ proc isAllocatedPtr(a: TMemRegion, p: pointer): bool = if not chunkUnused(c): if isSmallChunk(c): var c = cast[PSmallChunk](c) - var offset = (cast[TAddress](p) and (PageSize-1)) -% + 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) @@ -673,12 +673,12 @@ proc interiorAllocatedPtr(a: TMemRegion, p: pointer): pointer = if not chunkUnused(c): if isSmallChunk(c): var c = cast[PSmallChunk](c) - var offset = (cast[TAddress](p) and (PageSize-1)) -% + var offset = (cast[ByteAddress](p) and (PageSize-1)) -% smallChunkOverhead() if c.acc >% offset: - sysAssert(cast[TAddress](addr(c.data)) +% offset == - cast[TAddress](p), "offset is not what you think it is") - var d = cast[ptr TFreeCell](cast[TAddress](addr(c.data)) +% + 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)) +% offset -% (offset %% c.size)) if d.zeroField >% 1: result = d @@ -704,7 +704,7 @@ proc interiorAllocatedPtr(a: TMemRegion, p: pointer): pointer = sysAssert isAllocatedPtr(a, result), " result wrong pointer!" proc ptrSize(p: pointer): int = - var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell)) + var x = cast[pointer](cast[ByteAddress](p) -% sizeof(TFreeCell)) var c = pageAddr(p) sysAssert(not chunkUnused(c), "ptrSize") result = c.size -% sizeof(TFreeCell) @@ -715,7 +715,7 @@ proc alloc(allocator: var TMemRegion, size: int): pointer = result = rawAlloc(allocator, size+sizeof(TFreeCell)) cast[ptr TFreeCell](result).zeroField = 1 # mark it as used sysAssert(not isAllocatedPtr(allocator, result), "alloc") - result = cast[pointer](cast[TAddress](result) +% sizeof(TFreeCell)) + result = cast[pointer](cast[ByteAddress](result) +% sizeof(TFreeCell)) proc alloc0(allocator: var TMemRegion, size: int): pointer = result = alloc(allocator, size) @@ -723,7 +723,7 @@ proc alloc0(allocator: var TMemRegion, size: int): pointer = proc dealloc(allocator: var TMemRegion, p: pointer) = sysAssert(p != nil, "dealloc 0") - var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell)) + var x = cast[pointer](cast[ByteAddress](p) -% sizeof(TFreeCell)) sysAssert(x != nil, "dealloc 1") sysAssert(isAccessible(allocator, x), "is not accessible") sysAssert(cast[ptr TFreeCell](x).zeroField == 1, "dealloc 2") @@ -769,7 +769,7 @@ template instantiateForRegion(allocator: expr) = result = interiorAllocatedPtr(allocator, p) proc isAllocatedPtr*(p: pointer): bool = - let p = cast[pointer](cast[TAddress](p)-%TAddress(sizeof(TCell))) + let p = cast[pointer](cast[ByteAddress](p)-%ByteAddress(sizeof(TCell))) result = isAllocatedPtr(allocator, p) proc deallocOsPages = deallocOsPages(allocator) @@ -784,7 +784,7 @@ template instantiateForRegion(allocator: expr) = dealloc(allocator, p) proc realloc(p: pointer, newsize: int): pointer = - result = realloc(allocator, p, newsize) + result = realloc(allocator, p, newSize) when false: proc countFreeMem(): int = @@ -833,7 +833,7 @@ template instantiateForRegion(allocator: expr) = result = realloc(sharedHeap, p, newsize) releaseSys(heapLock) else: - result = realloc(p, newsize) + result = realloc(p, newSize) when hasThreadSupport: diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 673d55582..9406f26c9 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -8,7 +8,7 @@ # # This include file contains headers of Ansi C procs -# and definitions of Ansi C types in Nimrod syntax +# and definitions of Ansi C types in Nim syntax # All symbols are prefixed with 'c_' to avoid ambiguities {.push hints:off} @@ -33,10 +33,11 @@ type C_JmpBuf {.importc: "jmp_buf", header: "".} = object -var - c_stdin {.importc: "stdin", nodecl.}: C_TextFileStar - c_stdout {.importc: "stdout", nodecl.}: C_TextFileStar - c_stderr {.importc: "stderr", nodecl.}: C_TextFileStar +when not defined(vm): + var + c_stdin {.importc: "stdin", nodecl.}: C_TextFileStar + c_stdout {.importc: "stdout", nodecl.}: C_TextFileStar + c_stderr {.importc: "stderr", nodecl.}: C_TextFileStar # constants faked as variables: when not declared(SIGINT): diff --git a/lib/system/arithm.nim b/lib/system/arithm.nim index 7672947cd..c4df287cf 100644 --- a/lib/system/arithm.nim +++ b/lib/system/arithm.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -12,10 +12,10 @@ proc raiseOverflow {.compilerproc, noinline, noreturn.} = # a single proc to reduce code size to a minimum - sysFatal(EOverflow, "over- or underflow") + sysFatal(OverflowError, "over- or underflow") proc raiseDivByZero {.compilerproc, noinline, noreturn.} = - sysFatal(EDivByZero, "divison by zero") + sysFatal(DivByZeroError, "divison by zero") proc addInt64(a, b: int64): int64 {.compilerProc, inline.} = result = a +% b @@ -328,16 +328,16 @@ when not declared(mulInt): # written in other languages. proc raiseFloatInvalidOp {.noinline, noreturn.} = - sysFatal(EFloatInvalidOp, "FPU operation caused a NaN result") + sysFatal(FloatInvalidOpError, "FPU operation caused a NaN result") proc nanCheck(x: float64) {.compilerProc, inline.} = if x != x: raiseFloatInvalidOp() proc raiseFloatOverflow(x: float64) {.noinline, noreturn.} = if x > 0.0: - sysFatal(EFloatOverflow, "FPU operation caused an overflow") + sysFatal(FloatOverflowError, "FPU operation caused an overflow") else: - sysFatal(EFloatUnderflow, "FPU operations caused an underflow") + sysFatal(FloatUnderflowError, "FPU operations caused an underflow") proc infCheck(x: float64) {.compilerProc, inline.} = if x != 0.0 and x*0.5 == x: raiseFloatOverflow(x) diff --git a/lib/system/assign.nim b/lib/system/assign.nim index 0e27eb57f..429a92d34 100644 --- a/lib/system/assign.nim +++ b/lib/system/assign.nim @@ -1,20 +1,20 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -proc genericResetAux(dest: pointer, n: ptr TNimNode) {.gcsafe.} +proc genericResetAux(dest: pointer, n: ptr TNimNode) {.benign.} -proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) {.gcsafe.} +proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) {.benign.} proc genericAssignAux(dest, src: pointer, n: ptr TNimNode, - shallow: bool) {.gcsafe.} = + shallow: bool) {.benign.} = var - d = cast[TAddress](dest) - s = cast[TAddress](src) + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) case n.kind of nkSlot: genericAssignAux(cast[pointer](d +% n.offset), @@ -40,8 +40,8 @@ proc genericAssignAux(dest, src: pointer, n: ptr TNimNode, proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = var - d = cast[TAddress](dest) - s = cast[TAddress](src) + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) sysAssert(mt != nil, "genericAssignAux 2") case mt.kind of tyString: @@ -62,21 +62,25 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = return sysAssert(dest != nil, "genericAssignAux 3") unsureAsgnRef(x, newSeq(mt, seq.len)) - var dst = cast[TAddress](cast[PPointer](dest)[]) + var dst = cast[ByteAddress](cast[PPointer](dest)[]) for i in 0..seq.len-1: genericAssignAux( cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize), - cast[pointer](cast[TAddress](s2) +% i *% mt.base.size +% + cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +% GenericSeqSize), mt.base, shallow) of tyObject: - # we need to copy m_type field for tyObject, as it could be empty for - # sequence reallocations: - var pint = cast[ptr PNimType](dest) - pint[] = cast[ptr PNimType](src)[] if mt.base != nil: genericAssignAux(dest, src, mt.base, shallow) genericAssignAux(dest, src, mt.node, shallow) + # we need to copy m_type field for tyObject, as it could be empty for + # sequence reallocations: + var pint = cast[ptr PNimType](dest) + # We need to copy the *static* type not the dynamic type: + # if p of TB: + # var tbObj = TB(p) + # tbObj of TC # needs to be false! + pint[] = mt # cast[ptr PNimType](src)[] of tyTuple: genericAssignAux(dest, src, mt.node, shallow) of tyArray, tyArrayConstr: @@ -130,15 +134,15 @@ proc genericSeqAssign(dest, src: pointer, mt: PNimType) {.compilerProc.} = proc genericAssignOpenArray(dest, src: pointer, len: int, mt: PNimType) {.compilerproc.} = var - d = cast[TAddress](dest) - s = cast[TAddress](src) + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) for i in 0..len-1: genericAssign(cast[pointer](d +% i*% mt.base.size), cast[pointer](s +% i*% mt.base.size), mt.base) -proc objectInit(dest: pointer, typ: PNimType) {.compilerProc, gcsafe.} -proc objectInitAux(dest: pointer, n: ptr TNimNode) {.gcsafe.} = - var d = cast[TAddress](dest) +proc objectInit(dest: pointer, typ: PNimType) {.compilerProc, benign.} +proc objectInitAux(dest: pointer, n: ptr TNimNode) {.benign.} = + var d = cast[ByteAddress](dest) case n.kind of nkNone: sysAssert(false, "objectInitAux") of nkSlot: objectInit(cast[pointer](d +% n.offset), n.typ) @@ -152,7 +156,7 @@ proc objectInitAux(dest: pointer, n: ptr TNimNode) {.gcsafe.} = proc objectInit(dest: pointer, typ: PNimType) = # the generic init proc that takes care of initialization of complex # objects on the stack or heap - var d = cast[TAddress](dest) + var d = cast[ByteAddress](dest) case typ.kind of tyObject: # iterate over any structural type @@ -182,9 +186,9 @@ else: mixin destroy for i in countup(0, r.len - 1): destroy(r[i]) -proc genericReset(dest: pointer, mt: PNimType) {.compilerProc, gcsafe.} +proc genericReset(dest: pointer, mt: PNimType) {.compilerProc, benign.} proc genericResetAux(dest: pointer, n: ptr TNimNode) = - var d = cast[TAddress](dest) + var d = cast[ByteAddress](dest) case n.kind of nkNone: sysAssert(false, "genericResetAux") of nkSlot: genericReset(cast[pointer](d +% n.offset), n.typ) @@ -196,7 +200,7 @@ proc genericResetAux(dest: pointer, n: ptr TNimNode) = zeroMem(cast[pointer](d +% n.offset), n.typ.size) proc genericReset(dest: pointer, mt: PNimType) = - var d = cast[TAddress](dest) + var d = cast[ByteAddress](dest) sysAssert(mt != nil, "genericReset 2") case mt.kind of tyString, tyRef, tySequence: @@ -223,4 +227,4 @@ proc FieldDiscriminantCheck(oldDiscVal, newDiscVal: int, var oldBranch = selectBranch(oldDiscVal, L, a) var newBranch = selectBranch(newDiscVal, L, a) if newBranch != oldBranch and oldDiscVal != 0: - sysFatal(EInvalidField, "assignment to discriminant changes object branch") + sysFatal(FieldError, "assignment to discriminant changes object branch") diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim index 3ef9d00ec..f816c8201 100644 --- a/lib/system/atomics.nim +++ b/lib/system/atomics.nim @@ -1,13 +1,13 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -# Atomic operations for Nimrod. +# Atomic operations for Nim. {.push stackTrace:off.} const someGcc = defined(gcc) or defined(llvm_gcc) or defined(clang) @@ -37,7 +37,7 @@ when someGcc and hasThreadSupport: ## and release stores in all threads. type - TAtomType* = TNumber|pointer|ptr|char + TAtomType* = SomeNumber|pointer|ptr|char|bool ## Type Class representing valid types for use with atomic procs proc atomicLoadN*[T: TAtomType](p: ptr T, mem: AtomMemModel): T {. @@ -165,6 +165,7 @@ when someGcc and hasThreadSupport: elif defined(vcc) and hasThreadSupport: proc addAndFetch*(p: ptr int, val: int): int {. importc: "NimXadd", nodecl.} + proc fence*() {.importc: "_ReadWriteBarrier", header: "".} else: proc addAndFetch*(p: ptr int, val: int): int {.inline.} = @@ -203,16 +204,16 @@ else: when (defined(x86) or defined(amd64)) and someGcc: - proc cpuRelax {.inline.} = + proc cpuRelax* {.inline.} = {.emit: """asm volatile("pause" ::: "memory");""".} elif (defined(x86) or defined(amd64)) and defined(vcc): - proc cpuRelax {.importc: "YieldProcessor", header: "".} + proc cpuRelax* {.importc: "YieldProcessor", header: "".} elif defined(icl): - proc cpuRelax {.importc: "_mm_pause", header: "xmmintrin.h".} + proc cpuRelax* {.importc: "_mm_pause", header: "xmmintrin.h".} elif false: from os import sleep - proc cpuRelax {.inline.} = os.sleep(1) + proc cpuRelax* {.inline.} = os.sleep(1) when not declared(fence) and hasThreadSupport: # XXX fixme diff --git a/lib/system/avltree.nim b/lib/system/avltree.nim index bced15d6a..292097062 100644 --- a/lib/system/avltree.nim +++ b/lib/system/avltree.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -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) {.gcsafe.} = +proc add(a: var TMemRegion, 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) {.gcsafe.} = skew(t) split(t) -proc del(a: var TMemRegion, t: var PAvlNode, x: int) {.gcsafe.} = +proc del(a: var TMemRegion, t: var PAvlNode, x: int) {.benign.} = if t == bottom: return a.last = t if x <% t.key: diff --git a/lib/system/cellsets.nim b/lib/system/cellsets.nim index 3825e5b47..0e3a01eba 100644 --- a/lib/system/cellsets.nim +++ b/lib/system/cellsets.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -27,7 +27,7 @@ type TBitIndex = range[0..UnitsPerPage-1] TPageDesc {.final, pure.} = object next: PPageDesc # all nodes are connected with this pointer - key: TAddress # start address at bit 0 + key: ByteAddress # start address at bit 0 bits: array[TBitIndex, int] # a bit vector PPageDescArray = ptr array[0..1000_000, PPageDesc] @@ -98,7 +98,7 @@ 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: TAddress): PPageDesc = +proc cellSetGet(t: TCellSet, 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] @@ -123,7 +123,7 @@ proc cellSetEnlarge(t: var TCellSet) = dealloc(t.data) t.data = n -proc cellSetPut(t: var TCellSet, key: TAddress): PPageDesc = +proc cellSetPut(t: var TCellSet, key: ByteAddress): PPageDesc = var h = cast[int](key) and t.max while true: var x = t.data[h] @@ -147,7 +147,7 @@ proc cellSetPut(t: var TCellSet, key: TAddress): PPageDesc = # ---------- slightly higher level procs -------------------------------------- proc contains(s: TCellSet, cell: PCell): bool = - var u = cast[TAddress](cell) + var u = cast[ByteAddress](cell) var t = cellSetGet(s, u shr PageShift) if t != nil: u = (u %% PageSize) /% MemAlign @@ -156,13 +156,13 @@ proc contains(s: TCellSet, cell: PCell): bool = result = false proc incl(s: var TCellSet, cell: PCell) {.noinline.} = - var u = cast[TAddress](cell) + 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) = - var u = cast[TAddress](cell) + var u = cast[ByteAddress](cell) var t = cellSetGet(s, u shr PageShift) if t != nil: u = (u %% PageSize) /% MemAlign @@ -170,7 +170,7 @@ proc excl(s: var TCellSet, cell: PCell) = not (1 shl (u and IntMask))) proc containsOrIncl(s: var TCellSet, cell: PCell): bool = - var u = cast[TAddress](cell) + var u = cast[ByteAddress](cell) var t = cellSetGet(s, u shr PageShift) if t != nil: u = (u %% PageSize) /% MemAlign diff --git a/lib/system/cgprocs.nim b/lib/system/cgprocs.nim index d483c61bd..089846578 100644 --- a/lib/system/cgprocs.nim +++ b/lib/system/cgprocs.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -9,7 +9,7 @@ # Headers for procs that the code generator depends on ("compilerprocs") -proc addChar(s: NimString, c: char): NimString {.compilerProc, gcsafe.} +proc addChar(s: NimString, c: char): NimString {.compilerProc, benign.} type TLibHandle = pointer # private type @@ -21,5 +21,5 @@ proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr {.compilerproc.} proc nimLoadLibraryError(path: string) {.compilerproc, noinline.} -proc setStackBottom(theStackBottom: pointer) {.compilerRtl, noinline, gcsafe.} +proc setStackBottom(theStackBottom: pointer) {.compilerRtl, noinline, benign.} diff --git a/lib/system/channels.nim b/lib/system/channels.nim index a5d5c0802..3e5ca0795 100644 --- a/lib/system/channels.nim +++ b/lib/system/channels.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -13,9 +13,9 @@ ## ## **Note:** The current implementation of message passing is slow and does ## not work with cyclic data structures. - -when not declared(NimString): - {.error: "You must not import this module explicitly".} + +when not declared(NimString): + {.error: "You must not import this module explicitly".} type pbytes = ptr array[0.. 0xffff, byte] @@ -49,12 +49,12 @@ proc deinitRawChannel(p: pointer) = deinitSysCond(c.cond) proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel, - mode: TLoadStoreMode) {.gcsafe.} + mode: TLoadStoreMode) {.benign.} proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel, - mode: TLoadStoreMode) {.gcsafe.} = + mode: TLoadStoreMode) {.benign.} = var - d = cast[TAddress](dest) - s = cast[TAddress](src) + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) case n.kind of nkSlot: storeAux(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset), n.typ, t, mode) @@ -70,14 +70,14 @@ proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel, proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel, mode: TLoadStoreMode) = var - d = cast[TAddress](dest) - s = cast[TAddress](src) + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) sysAssert(mt != nil, "mt == nil") - case mt.Kind + case mt.kind of tyString: if mode == mStore: - var x = cast[ppointer](dest) - var s2 = cast[ppointer](s)[] + var x = cast[PPointer](dest) + var s2 = cast[PPointer](s)[] if s2 == nil: x[] = nil else: @@ -86,17 +86,17 @@ proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel, copyMem(ns, ss, ss.len+1 + GenericSeqSize) x[] = ns else: - var x = cast[ppointer](dest) - var s2 = cast[ppointer](s)[] + var x = cast[PPointer](dest) + var s2 = cast[PPointer](s)[] if s2 == nil: unsureAsgnRef(x, s2) else: unsureAsgnRef(x, copyString(cast[NimString](s2))) dealloc(t.region, s2) of tySequence: - var s2 = cast[ppointer](src)[] + var s2 = cast[PPointer](src)[] var seq = cast[PGenericSeq](s2) - var x = cast[ppointer](dest) + var x = cast[PPointer](dest) if s2 == nil: if mode == mStore: x[] = nil @@ -108,13 +108,13 @@ proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel, x[] = alloc(t.region, seq.len *% mt.base.size +% GenericSeqSize) else: unsureAsgnRef(x, newObj(mt, seq.len * mt.base.size + GenericSeqSize)) - var dst = cast[taddress](cast[ppointer](dest)[]) + var dst = cast[ByteAddress](cast[PPointer](dest)[]) for i in 0..seq.len-1: storeAux( cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize), - cast[pointer](cast[TAddress](s2) +% i *% mt.base.size +% + cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +% GenericSeqSize), - mt.Base, t, mode) + mt.base, t, mode) var dstseq = cast[PGenericSeq](dst) dstseq.len = seq.len dstseq.reserved = seq.len @@ -123,8 +123,8 @@ proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel, # copy type field: var pint = cast[ptr PNimType](dest) # XXX use dynamic type here! - pint[] = mt - if mt.base != nil: + pint[] = mt + if mt.base != nil: storeAux(dest, src, mt.base, t, mode) storeAux(dest, src, mt.node, t, mode) of tyTuple: @@ -134,8 +134,8 @@ proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel, storeAux(cast[pointer](d +% i*% mt.base.size), cast[pointer](s +% i*% mt.base.size), mt.base, t, mode) of tyRef: - var s = cast[ppointer](src)[] - var x = cast[ppointer](dest) + var s = cast[PPointer](src)[] + var x = cast[PPointer](dest) if s == nil: if mode == mStore: x[] = nil @@ -192,7 +192,7 @@ template lockChannel(q: expr, action: stmt) {.immediate.} = template sendImpl(q: expr) {.immediate.} = if q.mask == ChannelDeadMask: - sysFatal(EDeadThread, "cannot send message; thread died") + sysFatal(DeadThreadError, "cannot send message; thread died") acquireSys(q.lock) var m: TMsg shallowCopy(m, msg) @@ -215,7 +215,7 @@ proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) = q.ready = false if typ != q.elemType: releaseSys(q.lock) - sysFatal(EInvalidValue, "cannot receive message of wrong type") + sysFatal(ValueError, "cannot receive message of wrong type") rawRecv(q, res, typ) proc recv*[TMsg](c: var TChannel[TMsg]): TMsg = @@ -225,21 +225,21 @@ proc recv*[TMsg](c: var TChannel[TMsg]): TMsg = acquireSys(q.lock) llRecv(q, addr(result), cast[PNimType](getTypeInfo(result))) releaseSys(q.lock) - -proc tryRecv*[TMsg](c: var TChannel[TMsg]): tuple[dataAvailable: bool, - msg: TMsg] = - ## try to receives a message from the channel `c` if available. Otherwise - ## it returns ``(false, default(msg))``. + +proc tryRecv*[TMsg](c: var TChannel[TMsg]): tuple[dataAvailable: bool, + msg: TMsg] = + ## try to receives a message from the channel `c` if available. Otherwise + ## it returns ``(false, default(msg))``. var q = cast[PRawChannel](addr(c)) - if q.mask != ChannelDeadMask: + if q.mask != ChannelDeadMask: if tryAcquireSys(q.lock): - llRecv(q, addr(result.msg), cast[PNimType](getTypeInfo(result.msg))) + llRecv(q, addr(result.msg), cast[PNimType](getTypeInfo(result.msg))) result.dataAvailable = true releaseSys(q.lock) proc peek*[TMsg](c: var TChannel[TMsg]): int = ## returns the current number of messages in the channel `c`. Returns -1 - ## if the channel has been closed. **Note**: This is dangerous to use + ## if the channel has been closed. **Note**: This is dangerous to use ## as it encourages races. It's much better to use ``tryRecv`` instead. var q = cast[PRawChannel](addr(c)) if q.mask != ChannelDeadMask: diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim index 387b54ef1..5c32a307a 100644 --- a/lib/system/chcks.nim +++ b/lib/system/chcks.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -13,13 +13,13 @@ proc raiseRangeError(val: BiggestInt) {.compilerproc, noreturn, noinline.} = when hostOS == "standalone": sysFatal(EOutOfRange, "value out of range") else: - sysFatal(EOutOfRange, "value out of range: ", $val) + sysFatal(RangeError, "value out of range: ", $val) proc raiseIndexError() {.compilerproc, noreturn, noinline.} = - sysFatal(EInvalidIndex, "index out of bounds") + sysFatal(IndexError, "index out of bounds") proc raiseFieldError(f: string) {.compilerproc, noreturn, noinline.} = - sysFatal(EInvalidField, f, " is not accessible") + sysFatal(FieldError, f, " is not accessible") proc chckIndx(i, a, b: int): int = if i >= a and i <= b: @@ -46,11 +46,11 @@ proc chckRangeF(x, a, b: float): float = when hostOS == "standalone": sysFatal(EOutOfRange, "value out of range") else: - sysFatal(EOutOfRange, "value out of range: ", $x) + sysFatal(RangeError, "value out of range: ", $x) proc chckNil(p: pointer) = if p == nil: - sysFatal(EInvalidValue, "attempt to write to a nil address") + sysFatal(ValueError, "attempt to write to a nil address") #c_raise(SIGSEGV) proc chckObj(obj, subclass: PNimType) {.compilerproc.} = @@ -59,13 +59,13 @@ proc chckObj(obj, subclass: PNimType) {.compilerproc.} = if x == subclass: return # optimized fast path while x != subclass: if x == nil: - sysFatal(EInvalidObjectConversion, "invalid object conversion") + sysFatal(ObjectConversionError, "invalid object conversion") break x = x.base proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} = if a != b: - sysFatal(EInvalidObjectAssignment, "invalid object assignment") + sysFatal(ObjectAssignmentError, "invalid object assignment") type ObjCheckCache = array[0..1, PNimType] diff --git a/lib/system/debugger.nim b/lib/system/debugger.nim index af7b6d515..7b5169344 100644 --- a/lib/system/debugger.nim +++ b/lib/system/debugger.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -157,8 +157,8 @@ type oldValue: THash var - Watchpoints: array [0..99, TWatchpoint] - WatchpointsLen: int + watchpoints: array [0..99, TWatchpoint] + watchpointsLen: int proc `!&`(h: THash, val: int): THash {.inline.} = result = h +% val @@ -189,7 +189,7 @@ proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool, h: THash): THash proc genericHashAux(dest: pointer, n: ptr TNimNode, shallow: bool, h: THash): THash = - var d = cast[TAddress](dest) + var d = cast[ByteAddress](dest) case n.kind of nkSlot: result = genericHashAux(cast[pointer](d +% n.offset), n.typ, shallow, h) @@ -206,9 +206,9 @@ proc genericHashAux(dest: pointer, n: ptr TNimNode, shallow: bool, proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool, h: THash): THash = sysAssert(mt != nil, "genericHashAux 2") - case mt.Kind + case mt.kind of tyString: - var x = cast[ppointer](dest)[] + var x = cast[PPointer](dest)[] result = h if x != nil: let s = cast[NimString](x) @@ -217,29 +217,29 @@ proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool, else: result = result !& hash(x, s.len) of tySequence: - var x = cast[ppointer](dest) - var dst = cast[taddress](cast[ppointer](dest)[]) + var x = cast[PPointer](dest) + var dst = cast[ByteAddress](cast[PPointer](dest)[]) result = h if dst != 0: when defined(trackGcHeaders): - result = result !& hashGcHeader(cast[ppointer](dest)[]) + result = result !& hashGcHeader(cast[PPointer](dest)[]) else: - for i in 0..cast[pgenericseq](dst).len-1: + for i in 0..cast[PGenericSeq](dst).len-1: result = result !& genericHashAux( cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize), - mt.Base, shallow, result) + mt.base, shallow, result) of tyObject, tyTuple: # we don't need to copy m_type field for tyObject, as they are equal anyway result = genericHashAux(dest, mt.node, shallow, h) of tyArray, tyArrayConstr: - let d = cast[TAddress](dest) + let d = cast[ByteAddress](dest) result = h for i in 0..(mt.size div mt.base.size)-1: result = result !& genericHashAux(cast[pointer](d +% i*% mt.base.size), mt.base, shallow, result) of tyRef: when defined(trackGcHeaders): - var s = cast[ppointer](dest)[] + var s = cast[PPointer](dest)[] if s != nil: result = result !& hashGcHeader(s) else: @@ -247,7 +247,7 @@ proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool, result = h !& hash(dest, mt.size) else: result = h - var s = cast[ppointer](dest)[] + var s = cast[PPointer](dest)[] if s != nil: result = result !& genericHashAux(s, mt.base, shallow, result) else: @@ -258,23 +258,23 @@ proc genericHash(dest: pointer, mt: PNimType): int = proc dbgRegisterWatchpoint(address: pointer, name: cstring, typ: PNimType) {.compilerproc.} = - let L = WatchpointsLen + let L = watchPointsLen for i in 0.. = watchPoints.high: #debugOut("[Warning] cannot register watchpoint") return - Watchpoints[L].name = name - Watchpoints[L].address = address - Watchpoints[L].typ = typ - Watchpoints[L].oldValue = genericHash(address, typ) - inc WatchpointsLen + watchPoints[L].name = name + watchPoints[L].address = address + watchPoints[L].typ = typ + watchPoints[L].oldValue = genericHash(address, typ) + inc watchPointsLen proc dbgUnregisterWatchpoints*() = - WatchpointsLen = 0 + watchPointsLen = 0 var dbgLineHook*: proc () {.nimcall.} @@ -285,15 +285,15 @@ var dbgWatchpointHook*: proc (watchpointName: cstring) {.nimcall.} proc checkWatchpoints = - let L = WatchpointsLen + let L = watchPointsLen for i in 0.. 0: break @@ -355,7 +355,7 @@ proc dbgWriteStackTrace(f: PFrame) proc commandPrompt() = # if we return from this routine, user code executes again var - again = True + again = true dbgFramePtr = framePtr # for going down and up the stack dbgDown = 0 # how often we did go down dbgTemp: TStaticStr @@ -390,7 +390,7 @@ proc commandPrompt() = dbgHelp() elif ?"q" or ?"quit": dbgState = dbQuiting - dbgAborting = True + dbgAborting = true again = false quit(1) # BUGFIX: quit with error code > 0 elif ?"e" or ?"eval": @@ -402,9 +402,9 @@ proc commandPrompt() = elif ?"w" or ?"where": dbgShowExecutionPoint() elif ?"l" or ?"locals": - ListLocals(stdout, dbgFramePtr) + listLocals(stdout, dbgFramePtr) elif ?"g" or ?"globals": - ListGlobals(stdout) + listGlobals(stdout) elif ?"u" or ?"up": if dbgDown <= 0: debugOut("[Warning] cannot go up any further ") diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 3c5436afb..237b42482 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -11,7 +11,7 @@ # use the heap (and nor exceptions) do not include the GC or memory allocator. var - errorMessageWriter*: (proc(msg: string) {.tags: [FWriteIO], gcsafe.}) + errorMessageWriter*: (proc(msg: string) {.tags: [WriteIOEffect], benign.}) ## Function that will be called ## instead of stdmsg.write when printing stacktrace. ## Unstable API. @@ -32,17 +32,17 @@ proc showErrorMessage(data: cstring) = else: writeToStdErr(data) -proc chckIndx(i, a, b: int): int {.inline, compilerproc, gcsafe.} -proc chckRange(i, a, b: int): int {.inline, compilerproc, gcsafe.} -proc chckRangeF(x, a, b: float): float {.inline, compilerproc, gcsafe.} -proc chckNil(p: pointer) {.noinline, compilerproc, gcsafe.} +proc chckIndx(i, a, b: int): int {.inline, compilerproc, benign.} +proc chckRange(i, a, b: int): int {.inline, compilerproc, benign.} +proc chckRangeF(x, a, b: float): float {.inline, compilerproc, benign.} +proc chckNil(p: pointer) {.noinline, compilerproc, benign.} var framePtr {.threadvar.}: PFrame excHandler {.threadvar.}: PSafePoint # list of exception handlers # a global variable for the root of all try blocks - currException {.threadvar.}: ref E_Base + currException {.threadvar.}: ref Exception proc popFrame {.compilerRtl, inl.} = framePtr = framePtr.prev @@ -58,7 +58,7 @@ proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = proc popSafePoint {.compilerRtl, inl.} = excHandler = excHandler.prev -proc pushCurrentException(e: ref E_Base) {.compilerRtl, inl.} = +proc pushCurrentException(e: ref Exception) {.compilerRtl, inl.} = e.parent = currException currException = e @@ -68,8 +68,8 @@ proc popCurrentException {.compilerRtl, inl.} = # some platforms have native support for stack traces: const nativeStackTraceSupported* = (defined(macosx) or defined(linux)) and - not nimrodStackTrace - hasSomeStackTrace = nimrodStackTrace or + not NimStackTrace + hasSomeStackTrace = NimStackTrace or defined(nativeStackTrace) and nativeStackTraceSupported when defined(nativeStacktrace) and nativeStackTraceSupported: @@ -177,7 +177,7 @@ proc auxWriteStackTrace(f: PFrame, s: var string) = when hasSomeStackTrace: proc rawWriteStackTrace(s: var string) = - when nimrodStackTrace: + when NimStackTrace: if framePtr == nil: add(s, "No stack traceback available\n") else: @@ -195,7 +195,7 @@ proc quitOrDebug() {.inline.} = else: endbStep() # call the debugger -proc raiseExceptionAux(e: ref E_Base) = +proc raiseExceptionAux(e: ref Exception) = if localRaiseHook != nil: if not localRaiseHook(e): return if globalRaiseHook != nil: @@ -204,7 +204,7 @@ proc raiseExceptionAux(e: ref E_Base) = if not excHandler.hasRaiseAction or excHandler.raiseAction(e): pushCurrentException(e) c_longjmp(excHandler.context, 1) - elif e[] of EOutOfMemory: + elif e[] of OutOfMemError: showErrorMessage(e.name) quitOrDebug() else: @@ -236,7 +236,7 @@ proc raiseExceptionAux(e: ref E_Base) = showErrorMessage(buf) quitOrDebug() -proc raiseException(e: ref E_Base, ename: cstring) {.compilerRtl.} = +proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} = e.name = ename when hasSomeStackTrace: e.trace = "" @@ -245,7 +245,7 @@ proc raiseException(e: ref E_Base, ename: cstring) {.compilerRtl.} = proc reraiseException() {.compilerRtl.} = if currException == nil: - sysFatal(ENoExceptionToReraise, "no exception to reraise") + sysFatal(ReraiseError, "no exception to reraise") else: raiseExceptionAux(currException) @@ -264,7 +264,7 @@ proc getStackTrace(): string = else: result = "No stack traceback available\n" -proc getStackTrace(e: ref E_Base): string = +proc getStackTrace(e: ref Exception): string = if not isNil(e) and not isNil(e.trace): result = e.trace else: @@ -296,15 +296,15 @@ when not defined(noSignalHandler): template processSignal(s, action: expr) {.immediate, dirty.} = if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n") elif s == SIGSEGV: - action("SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n") + action("SIGSEGV: Illegal storage access. (Try to compile with -d:useSysAssert -d:useGcAssert for details.)\n") elif s == SIGABRT: when defined(endb): if dbgAborting: return # the debugger wants to abort action("SIGABRT: Abnormal termination.\n") elif s == SIGFPE: action("SIGFPE: Arithmetic error.\n") elif s == SIGILL: action("SIGILL: Illegal operation.\n") - elif s == SIGBUS: - action("SIGBUS: Illegal storage access. (Attempt to read from nil?)\n") + elif s == SIGBUS: + action("SIGBUS: Illegal storage access. (Try to compile with -d:useSysAssert -d:useGcAssert for details.)\n") else: block platformSpecificSignal: when declared(SIGPIPE): @@ -318,7 +318,7 @@ when not defined(noSignalHandler): GC_disable() var buf = newStringOfCap(2000) rawWriteStackTrace(buf) - processSignal(sig, buf.add) # nice hu? currying a la nimrod :-) + processSignal(sig, buf.add) # nice hu? currying a la Nim :-) showErrorMessage(buf) GC_enable() else: @@ -326,7 +326,7 @@ when not defined(noSignalHandler): template asgn(y: expr) = msg = y processSignal(sig, asgn) showErrorMessage(msg) - when defined(endb): dbgAborting = True + when defined(endb): dbgAborting = true quit(1) # always quit when SIGABRT proc registerSignalHandler() = @@ -343,5 +343,5 @@ when not defined(noSignalHandler): proc setControlCHook(hook: proc () {.noconv.}) = # ugly cast, but should work on all architectures: - type TSignalHandler = proc (sig: cint) {.noconv, gcsafe.} + type TSignalHandler = proc (sig: cint) {.noconv, benign.} c_signal(SIGINT, cast[TSignalHandler](hook)) diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 0c1fc7748..e0db3fba4 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -51,7 +51,7 @@ type waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack, waCollectWhite, - TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, gcsafe.} + TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} # A ref type can have a finalizer that is called before the object's # storage is freed. @@ -111,11 +111,11 @@ proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} = proc cellToUsr(cell: PCell): pointer {.inline.} = # convert object (=pointer to refcount) to pointer to userdata - result = cast[pointer](cast[TAddress](cell)+%TAddress(sizeof(TCell))) + result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(TCell))) proc usrToCell(usr: pointer): PCell {.inline.} = # convert pointer to userdata to object (=pointer to refcount) - result = cast[PCell](cast[TAddress](usr)-%TAddress(sizeof(TCell))) + result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(TCell))) proc canbeCycleRoot(c: PCell): bool {.inline.} = result = ntfAcyclic notin c.typ.flags @@ -152,11 +152,11 @@ template gcTrace(cell, state: expr): stmt {.immediate.} = when traceGC: traceCell(cell, state) # forward declarations: -proc collectCT(gch: var TGcHeap) {.gcsafe.} -proc isOnStack*(p: pointer): bool {.noinline, gcsafe.} -proc forAllChildren(cell: PCell, op: TWalkOp) {.gcsafe.} -proc doOperation(p: pointer, op: TWalkOp) {.gcsafe.} -proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) {.gcsafe.} +proc collectCT(gch: var TGcHeap) {.benign.} +proc isOnStack*(p: pointer): bool {.noinline, benign.} +proc forAllChildren(cell: PCell, op: TWalkOp) {.benign.} +proc doOperation(p: pointer, op: TWalkOp) {.benign.} +proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) {.benign.} # we need the prototype here for debugging purposes when hasThreadSupport and hasSharedHeap: @@ -292,9 +292,23 @@ proc initGC() = when useMarkForDebug or useBackupGc: init(gch.marked) +var + localGcInitialized {.rtlThreadVar.}: bool + +proc setupForeignThreadGc*() = + ## call this if you registered a callback that will be run from a thread not + ## under your control. This has a cheap thread-local guard, so the GC for + ## this thread will only be initialized once per thread, no matter how often + ## it is called. + if not localGcInitialized: + localGcInitialized = true + var stackTop {.volatile.}: pointer + setStackBottom(addr(stackTop)) + initGC() + when useMarkForDebug or useBackupGc: type - TGlobalMarkerProc = proc () {.nimcall, gcsafe.} + TGlobalMarkerProc = proc () {.nimcall, benign.} var globalMarkersLen: int globalMarkers: array[0.. 7_000, TGlobalMarkerProc] @@ -311,8 +325,8 @@ proc cellsetReset(s: var TCellSet) = deinit(s) init(s) -proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.gcsafe.} = - var d = cast[TAddress](dest) +proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.benign.} = + var d = cast[ByteAddress](dest) case n.kind of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op) of nkList: @@ -332,7 +346,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.gcsafe.} = of nkNone: sysAssert(false, "forAllSlotsAux") proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) = - var d = cast[TAddress](dest) + var d = cast[ByteAddress](dest) if dest == nil: return # nothing to do if ntfNoRefs notin mt.flags: case mt.kind @@ -358,7 +372,7 @@ proc forAllChildren(cell: PCell, op: TWalkOp) = of tyRef: # common case forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) of tySequence: - var d = cast[TAddress](cellToUsr(cell)) + var d = cast[ByteAddress](cellToUsr(cell)) var s = cast[PGenericSeq](d) if s != nil: for i in 0..s.len-1: @@ -424,7 +438,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer = gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") collectCT(gch) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell))) - gcAssert((cast[TAddress](res) and (MemAlign-1)) == 0, "newObj: 2") + gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") # now it is buffered in the ZCT res.typ = typ when leakDetector and not hasThreadSupport: @@ -470,7 +484,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell))) sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc") - sysAssert((cast[TAddress](res) and (MemAlign-1)) == 0, "newObj: 2") + sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") # now it is buffered in the ZCT res.typ = typ when leakDetector and not hasThreadSupport: @@ -511,9 +525,9 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize copyMem(res, ol, oldsize + sizeof(TCell)) - zeroMem(cast[pointer](cast[TAddress](res)+% oldsize +% sizeof(TCell)), + zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)), newsize-oldsize) - sysAssert((cast[TAddress](res) and (MemAlign-1)) == 0, "growObj: 3") + sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") sysAssert(res.refcount shr rcShift <=% 1, "growObj: 4") #if res.refcount <% rcIncrement: # add(gch.zct, res) @@ -680,11 +694,11 @@ proc doOperation(p: pointer, op: TWalkOp) = proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = doOperation(d, TWalkOp(op)) -proc collectZCT(gch: var TGcHeap): bool {.gcsafe.} +proc collectZCT(gch: var TGcHeap): bool {.benign.} when useMarkForDebug or useBackupGc: proc markStackAndRegistersForSweep(gch: var TGcHeap) {.noinline, cdecl, - gcsafe.} + benign.} proc collectRoots(gch: var TGcHeap) = for s in elements(gch.cycleRoots): @@ -728,7 +742,7 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} = # the addresses are not as cells on the stack, so turn them to cells: sysAssert(allocInv(gch.region), "gcMark begin") var cell = usrToCell(p) - var c = cast[TAddress](cell) + var c = cast[ByteAddress](cell) if c >% PageSize: # fast check: does it look like a cell? var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell)) @@ -778,8 +792,8 @@ when not defined(useNimRtl): # the first init must be the one that defines the stack bottom: if gch.stackBottom == nil: gch.stackBottom = theStackBottom else: - var a = cast[TAddress](theStackBottom) # and not PageMask - PageSize*2 - var b = cast[TAddress](gch.stackBottom) + var a = cast[ByteAddress](theStackBottom) # and not PageMask - PageSize*2 + var b = cast[ByteAddress](gch.stackBottom) #c_fprintf(c_stdout, "old: %p new: %p;\n",gch.stackBottom,theStackBottom) when stackIncreases: gch.stackBottom = cast[pointer](min(a, b)) @@ -854,9 +868,9 @@ else: proc isOnStack(p: pointer): bool = var stackTop {.volatile.}: pointer stackTop = addr(stackTop) - var b = cast[TAddress](gch.stackBottom) - var a = cast[TAddress](stackTop) - var x = cast[TAddress](p) + var b = cast[ByteAddress](gch.stackBottom) + var a = cast[ByteAddress](stackTop) + var x = cast[ByteAddress](p) result = a <=% x and x <=% b template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} = @@ -866,8 +880,8 @@ else: type PStackSlice = ptr array [0..7, pointer] var registers {.noinit.}: C_JmpBuf if c_setjmp(registers) == 0'i32: # To fill the C stack with registers. - var max = cast[TAddress](gch.stackBottom) - var sp = cast[TAddress](addr(registers)) + var max = cast[ByteAddress](gch.stackBottom) + var sp = cast[ByteAddress](addr(registers)) # loop unrolled: while sp <% max - 8*sizeof(pointer): gcMark(gch, cast[PStackSlice](sp)[0]) @@ -1040,7 +1054,7 @@ when not defined(useNimRtl): else: dec(gch.recGcLock) - proc GC_setStrategy(strategy: TGC_Strategy) = + proc GC_setStrategy(strategy: GC_Strategy) = discard proc GC_enableMarkAndSweep() = diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim index 132da9885..ee52b54f5 100644 --- a/lib/system/gc2.nim +++ b/lib/system/gc2.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index 05bcdcc82..0eafd3255 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -1,13 +1,13 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -# A simple mark&sweep garbage collector for Nimrod. Define the +# A simple mark&sweep garbage collector for Nim. Define the # symbol ``gcUseBitvectors`` to generate a variant of this GC. {.push profiler:off.} @@ -32,11 +32,11 @@ type # local waMarkPrecise # fast precise marking - TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, gcsafe.} + TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} # A ref type can have a finalizer that is called before the object's # storage is freed. - TGlobalMarkerProc = proc () {.nimcall, gcsafe.} + TGlobalMarkerProc = proc () {.nimcall, benign.} TGcStat = object collections: int # number of performed full collections @@ -80,11 +80,11 @@ template gcAssert(cond: bool, msg: string) = proc cellToUsr(cell: PCell): pointer {.inline.} = # convert object (=pointer to refcount) to pointer to userdata - result = cast[pointer](cast[TAddress](cell)+%TAddress(sizeof(TCell))) + result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(TCell))) proc usrToCell(usr: pointer): PCell {.inline.} = # convert pointer to userdata to object (=pointer to refcount) - result = cast[PCell](cast[TAddress](usr)-%TAddress(sizeof(TCell))) + result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(TCell))) proc canbeCycleRoot(c: PCell): bool {.inline.} = result = ntfAcyclic notin c.typ.flags @@ -116,11 +116,11 @@ when BitsPerPage mod (sizeof(int)*8) != 0: {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".} # forward declarations: -proc collectCT(gch: var TGcHeap) {.gcsafe.} -proc isOnStack*(p: pointer): bool {.noinline, gcsafe.} -proc forAllChildren(cell: PCell, op: TWalkOp) {.gcsafe.} -proc doOperation(p: pointer, op: TWalkOp) {.gcsafe.} -proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) {.gcsafe.} +proc collectCT(gch: var TGcHeap) {.benign.} +proc isOnStack*(p: pointer): bool {.noinline, benign.} +proc forAllChildren(cell: PCell, op: TWalkOp) {.benign.} +proc doOperation(p: pointer, op: TWalkOp) {.benign.} +proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) {.benign.} # we need the prototype here for debugging purposes proc prepareDealloc(cell: PCell) = @@ -168,8 +168,22 @@ proc initGC() = Init(gch.allocated) init(gch.marked) -proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.gcsafe.} = - var d = cast[TAddress](dest) +var + localGcInitialized {.rtlThreadVar.}: bool + +proc setupForeignThreadGc*() = + ## call this if you registered a callback that will be run from a thread not + ## under your control. This has a cheap thread-local guard, so the GC for + ## this thread will only be initialized once per thread, no matter how often + ## it is called. + if not localGcInitialized: + localGcInitialized = true + var stackTop {.volatile.}: pointer + setStackBottom(addr(stackTop)) + initGC() + +proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.benign.} = + var d = cast[ByteAddress](dest) case n.kind of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op) of nkList: @@ -181,7 +195,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.gcsafe.} = of nkNone: sysAssert(false, "forAllSlotsAux") proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) = - var d = cast[TAddress](dest) + var d = cast[ByteAddress](dest) if dest == nil: return # nothing to do if ntfNoRefs notin mt.flags: case mt.kind @@ -206,7 +220,7 @@ proc forAllChildren(cell: PCell, op: TWalkOp) = of tyRef: # common case forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) of tySequence: - var d = cast[TAddress](cellToUsr(cell)) + var d = cast[ByteAddress](cellToUsr(cell)) var s = cast[PGenericSeq](d) if s != nil: for i in 0..s.len-1: @@ -220,7 +234,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer = gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") collectCT(gch) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell))) - gcAssert((cast[TAddress](res) and (MemAlign-1)) == 0, "newObj: 2") + gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") # now it is buffered in the ZCT res.typ = typ when leakDetector and not hasThreadSupport: @@ -280,9 +294,9 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize copyMem(res, ol, oldsize + sizeof(TCell)) - zeroMem(cast[pointer](cast[TAddress](res)+% oldsize +% sizeof(TCell)), + zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)), newsize-oldsize) - sysAssert((cast[TAddress](res) and (MemAlign-1)) == 0, "growObj: 3") + sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") when withBitvectors: excl(gch.allocated, ol) when reallyDealloc: rawDealloc(gch.region, ol) else: @@ -379,7 +393,7 @@ proc markGlobals(gch: var TGcHeap) = proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} = # the addresses are not as cells on the stack, so turn them to cells: var cell = usrToCell(p) - var c = cast[TAddress](cell) + var c = cast[ByteAddress](cell) if c >% PageSize: # fast check: does it look like a cell? var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell)) @@ -404,8 +418,8 @@ when not defined(useNimRtl): # the first init must be the one that defines the stack bottom: if gch.stackBottom == nil: gch.stackBottom = theStackBottom else: - var a = cast[TAddress](theStackBottom) # and not PageMask - PageSize*2 - var b = cast[TAddress](gch.stackBottom) + var a = cast[ByteAddress](theStackBottom) # and not PageMask - PageSize*2 + var b = cast[ByteAddress](gch.stackBottom) #c_fprintf(c_stdout, "old: %p new: %p;\n",gch.stackBottom,theStackBottom) when stackIncreases: gch.stackBottom = cast[pointer](min(a, b)) @@ -421,9 +435,9 @@ when defined(sparc): # For SPARC architecture. proc isOnStack(p: pointer): bool = var stackTop {.volatile.}: pointer stackTop = addr(stackTop) - var b = cast[TAddress](gch.stackBottom) - var a = cast[TAddress](stackTop) - var x = cast[TAddress](p) + var b = cast[ByteAddress](gch.stackBottom) + var a = cast[ByteAddress](stackTop) + var x = cast[ByteAddress](p) result = a <=% x and x <=% b proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = @@ -440,7 +454,7 @@ when defined(sparc): # For SPARC architecture. # Addresses decrease as the stack grows. while sp <= max: gcMark(gch, sp[]) - sp = cast[ppointer](cast[TAddress](sp) +% sizeof(pointer)) + sp = cast[ppointer](cast[ByteAddress](sp) +% sizeof(pointer)) elif defined(ELATE): {.error: "stack marking code is to be written for this architecture".} @@ -452,9 +466,9 @@ elif stackIncreases: proc isOnStack(p: pointer): bool = var stackTop {.volatile.}: pointer stackTop = addr(stackTop) - var a = cast[TAddress](gch.stackBottom) - var b = cast[TAddress](stackTop) - var x = cast[TAddress](p) + var a = cast[ByteAddress](gch.stackBottom) + var b = cast[ByteAddress](stackTop) + var x = cast[ByteAddress](p) result = a <=% x and x <=% b var @@ -465,8 +479,8 @@ elif stackIncreases: proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = var registers: C_JmpBuf if c_setjmp(registers) == 0'i32: # To fill the C stack with registers. - var max = cast[TAddress](gch.stackBottom) - var sp = cast[TAddress](addr(registers)) +% jmpbufSize -% sizeof(pointer) + var max = cast[ByteAddress](gch.stackBottom) + var sp = cast[ByteAddress](addr(registers)) +% jmpbufSize -% sizeof(pointer) # sp will traverse the JMP_BUF as well (jmp_buf size is added, # otherwise sp would be below the registers structure). while sp >=% max: @@ -480,9 +494,9 @@ else: proc isOnStack(p: pointer): bool = var stackTop {.volatile.}: pointer stackTop = addr(stackTop) - var b = cast[TAddress](gch.stackBottom) - var a = cast[TAddress](stackTop) - var x = cast[TAddress](p) + var b = cast[ByteAddress](gch.stackBottom) + var a = cast[ByteAddress](stackTop) + var x = cast[ByteAddress](p) result = a <=% x and x <=% b proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = @@ -492,8 +506,8 @@ else: type PStackSlice = ptr array [0..7, pointer] var registers {.noinit.}: C_JmpBuf if c_setjmp(registers) == 0'i32: # To fill the C stack with registers. - var max = cast[TAddress](gch.stackBottom) - var sp = cast[TAddress](addr(registers)) + var max = cast[ByteAddress](gch.stackBottom) + var sp = cast[ByteAddress](addr(registers)) # loop unrolled: while sp <% max - 8*sizeof(pointer): gcMark(gch, cast[PStackSlice](sp)[0]) @@ -546,7 +560,7 @@ when not defined(useNimRtl): else: dec(gch.recGcLock) - proc GC_setStrategy(strategy: TGC_Strategy) = discard + proc GC_setStrategy(strategy: GC_Strategy) = discard proc GC_enableMarkAndSweep() = gch.cycleThreshold = InitialThreshold diff --git a/lib/system/hti.nim b/lib/system/hti.nim index ef8f50831..e599668a7 100644 --- a/lib/system/hti.nim +++ b/lib/system/hti.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -85,8 +85,8 @@ type base: ptr TNimType node: ptr TNimNode # valid for tyRecord, tyObject, tyTuple, tyEnum finalizer: pointer # the finalizer for the type - marker: proc (p: pointer, op: int) {.nimcall, gcsafe.} # marker proc for GC - deepcopy: proc (p: pointer): pointer {.nimcall, gcsafe.} + marker: proc (p: pointer, op: int) {.nimcall, benign.} # marker proc for GC + deepcopy: proc (p: pointer): pointer {.nimcall, benign.} PNimType = ptr TNimType # node.len may be the ``first`` element of a set diff --git a/lib/system/inclrtl.nim b/lib/system/inclrtl.nim index 5c82db4da..aac802229 100644 --- a/lib/system/inclrtl.nim +++ b/lib/system/inclrtl.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -46,3 +46,8 @@ else: when not defined(nimsuperops): {.pragma: operator.} + +when defined(nimlocks): + {.pragma: benign, gcsafe, locks: 0.} +else: + {.pragma: benign, gcsafe.} diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 423f63e2a..9b4a7d556 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -18,7 +18,7 @@ type PSafePoint = ptr TSafePoint TSafePoint {.compilerproc, final.} = object prev: PSafePoint # points to next safe point - exc: ref E_Base + exc: ref Exception PCallFrame = ptr TCallFrame TCallFrame {.importc, nodecl, final.} = object @@ -97,13 +97,13 @@ proc rawWriteStackTrace(): string = else: result = "No stack traceback available\n" -proc raiseException(e: ref E_Base, ename: cstring) {. +proc raiseException(e: ref Exception, ename: cstring) {. compilerproc, asmNoStackFrame.} = e.name = ename if excHandler != nil: excHandler.exc = e else: - when nimrodStackTrace: + when NimStackTrace: var buf = rawWriteStackTrace() else: var buf = "" @@ -120,24 +120,24 @@ proc raiseException(e: ref E_Base, ename: cstring) {. proc reraiseException() {.compilerproc, asmNoStackFrame.} = if excHandler == nil: - raise newException(ENoExceptionToReraise, "no exception to reraise") + raise newException(ReraiseError, "no exception to reraise") else: asm """throw excHandler.exc;""" proc raiseOverflow {.exportc: "raiseOverflow", noreturn.} = - raise newException(EOverflow, "over- or underflow") + raise newException(OverflowError, "over- or underflow") proc raiseDivByZero {.exportc: "raiseDivByZero", noreturn.} = - raise newException(EDivByZero, "divison by zero") + raise newException(DivByZeroError, "divison by zero") proc raiseRangeError() {.compilerproc, noreturn.} = - raise newException(EOutOfRange, "value out of range") + raise newException(RangeError, "value out of range") proc raiseIndexError() {.compilerproc, noreturn.} = - raise newException(EInvalidIndex, "index out of bounds") + raise newException(IndexError, "index out of bounds") proc raiseFieldError(f: string) {.compilerproc, noreturn.} = - raise newException(EInvalidField, f & " is not accessible") + raise newException(FieldError, f & " is not accessible") proc SetConstr() {.varargs, asmNoStackFrame, compilerproc.} = asm """ @@ -260,7 +260,7 @@ proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} = """ type - TDocument {.importc.} = object of TObject + TDocument {.importc.} = object of RootObj write: proc (text: cstring) {.nimcall.} writeln: proc (text: cstring) {.nimcall.} createAttribute: proc (identifier: cstring): ref TNode {.nimcall.} @@ -283,7 +283,7 @@ type DocumentTypeNode, DocumentFragmentNode, NotationNode - TNode* {.importc.} = object of TObject + TNode* {.importc.} = object of RootObj attributes*: seq[ref TNode] childNodes*: seq[ref TNode] data*: cstring @@ -345,11 +345,12 @@ else: node.appendChild(document.createTextNode(x)) node.appendChild(document.createElement("br")) else: - raise newException(EInvalidValue, " element does not exist yet!") + raise newException(ValueError, " element does not exist yet!") proc rawEcho {.compilerproc.} = var node = document.getElementsByTagName("body")[0] - if node == nil: raise newException(EIO, " element does not exist yet!") + if node == nil: + raise newException(IOError, " element does not exist yet!") asm """ for (var i = 0; i < arguments.length; ++i) { var x = `toJSStr`(arguments[i]); @@ -621,7 +622,7 @@ proc chckObj(obj, subclass: PNimType) {.compilerproc.} = if x == subclass: return # optimized fast path while x != subclass: if x == nil: - raise newException(EInvalidObjectConversion, "invalid object conversion") + raise newException(ObjectConversionError, "invalid object conversion") x = x.base proc isObj(obj, subclass: PNimType): bool {.compilerproc.} = diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 606743f51..e091c0889 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -1,14 +1,14 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -# Nimrod high-level memory manager: It supports Boehm's GC, no GC and the -# native Nimrod GC. The native Nimrod GC is the default. +# Nim high-level memory manager: It supports Boehm's GC, no GC and the +# native Nim GC. The native Nim GC is the default. #{.push checks:on, assertions:on.} {.push checks:off.} @@ -34,7 +34,7 @@ const type PPointer = ptr pointer - TByteArray = array[0..1000_0000, Byte] + TByteArray = array[0..1000_0000, byte] PByte = ptr TByteArray PString = ptr string @@ -267,7 +267,7 @@ elif defined(nogc) and defined(useMalloc): elif defined(nogc): # Even though we don't want the GC, we cannot simply use C's memory manager - # because Nimrod's runtime wants ``realloc`` to zero out the additional + # because Nim's runtime wants ``realloc`` to zero out the additional # space which C's ``realloc`` does not. And we cannot get the old size of an # object, because C does not support this operation... Even though every # possible implementation has to have a way to determine the object's size. @@ -308,7 +308,7 @@ elif defined(nogc): dest[] = src var allocator {.rtlThreadVar.}: TMemRegion - InstantiateForRegion(allocator) + instantiateForRegion(allocator) include "system/cellsets" diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim new file mode 100644 index 000000000..3ec6a270e --- /dev/null +++ b/lib/system/platforms.nim @@ -0,0 +1,74 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2014 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Platform detection for Nim. This module is included by the system module! +## Do not import it directly! + +type + CpuPlatform* {.pure.} = enum ## the CPU this program will run on. + none, ## unknown CPU + i386, ## 32 bit x86 compatible CPU + m68k, ## M68k based processor + alpha, ## Alpha processor + powerpc, ## 32 bit PowerPC + powerpc64, ## 64 bit PowerPC + sparc, ## Sparc based processor + ia64, ## Intel Itanium + amd64, ## x86_64 (AMD64); 64 bit x86 compatible CPU + mips, ## Mips based processor + arm, ## ARM based processor + vm, ## Some Virtual machine: Nim's VM or JavaScript + avr ## AVR based processor + + OsPlatform* {.pure.} = enum ## the OS this program will run on. + none, dos, windows, os2, linux, morphos, skyos, solaris, + irix, netbsd, freebsd, openbsd, aix, palmos, qnx, amiga, + atari, netware, macos, macosx, haiku, js, nimVM, standalone + +const + targetOS* = when defined(windows): OsPlatform.windows + elif defined(dos): OsPlatform.dos + elif defined(os2): OsPlatform.os2 + elif defined(linux): OsPlatform.linux + elif defined(morphos): OsPlatform.morphos + elif defined(skyos): OsPlatform.skyos + elif defined(solaris): OsPlatform.solaris + elif defined(irix): OsPlatform.irix + elif defined(netbsd): OsPlatform.netbsd + elif defined(freebsd): OsPlatform.freebsd + elif defined(openbsd): OsPlatform.openbsd + elif defined(aix): OsPlatform.aix + elif defined(palmos): OsPlatform.palmos + elif defined(qnx): OsPlatform.qnx + elif defined(amiga): OsPlatform.amiga + elif defined(atari): OsPlatform.atari + elif defined(netware): OsPlatform.netware + elif defined(macosx): OsPlatform.macosx + elif defined(macos): OsPlatform.macos + elif defined(haiku): OsPlatform.haiku + elif defined(js): OsPlatform.js + elif defined(nimrodVM): OsPlatform.nimVM + elif defined(standalone): OsPlatform.standalone + else: OsPlatform.none + ## the OS this program will run on. + + targetCPU* = when defined(i386): CpuPlatform.i386 + elif defined(m68k): CpuPlatform.m68k + elif defined(alpha): CpuPlatform.alpha + elif defined(powerpc): CpuPlatform.powerpc + elif defined(powerpc64): CpuPlatform.powerpc64 + elif defined(sparc): CpuPlatform.sparc + elif defined(ia64): CpuPlatform.ia64 + elif defined(amd64): CpuPlatform.amd64 + elif defined(mips): CpuPlatform.mips + elif defined(arm): CpuPlatform.arm + elif defined(vm): CpuPlatform.vm + elif defined(avr): CpuPlatform.avr + else: CpuPlatform.none + ## the CPU this program will run on. diff --git a/lib/system/profiler.nim b/lib/system/profiler.nim index 8e4c51dd9..96ab6abc7 100644 --- a/lib/system/profiler.nim +++ b/lib/system/profiler.nim @@ -1,13 +1,13 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -# This file implements the Nimrod profiler. The profiler needs support by the +# This file implements the Nim profiler. The profiler needs support by the # code generator. The idea is to inject the instruction stream # with 'nimProfile()' calls. These calls are injected at every loop end # (except perhaps loops that have no side-effects). At every Nth call a diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 8e1bc5f26..2de603cea 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -155,7 +155,7 @@ when not defined(useNimRtl): var bs = typ.base.size for i in 0..typ.size div bs - 1: if i > 0: add result, ", " - reprAux(result, cast[pointer](cast[TAddress](p) + i*bs), typ.base, cl) + reprAux(result, cast[pointer](cast[ByteAddress](p) + i*bs), typ.base, cl) add result, "]" proc reprSequence(result: var string, p: pointer, typ: PNimType, @@ -167,7 +167,7 @@ when not defined(useNimRtl): var bs = typ.base.size for i in 0..cast[PGenericSeq](p).len-1: if i > 0: add result, ", " - reprAux(result, cast[pointer](cast[TAddress](p) + GenericSeqSize + i*bs), + reprAux(result, cast[pointer](cast[ByteAddress](p) + GenericSeqSize + i*bs), typ.base, cl) add result, "]" @@ -178,14 +178,14 @@ when not defined(useNimRtl): of nkSlot: add result, $n.name add result, " = " - reprAux(result, cast[pointer](cast[TAddress](p) + n.offset), n.typ, cl) + reprAux(result, cast[pointer](cast[ByteAddress](p) + n.offset), n.typ, cl) of nkList: for i in 0..n.len-1: if i > 0: add result, ",\n" reprRecordAux(result, p, n.sons[i], cl) of nkCase: var m = selectBranch(p, n) - reprAux(result, cast[pointer](cast[TAddress](p) + n.offset), n.typ, cl) + reprAux(result, cast[pointer](cast[ByteAddress](p) + n.offset), n.typ, cl) if m != nil: reprRecordAux(result, p, m, cl) proc reprRecord(result: var string, p: pointer, typ: PNimType, @@ -265,7 +265,7 @@ proc reprOpenArray(p: pointer, length: int, elemtyp: PNimType): string {. var bs = elemtyp.size for i in 0..length - 1: if i > 0: add result, ", " - reprAux(result, cast[pointer](cast[TAddress](p) + i*bs), elemtyp, cl) + reprAux(result, cast[pointer](cast[ByteAddress](p) + i*bs), elemtyp, cl) add result, "]" deinitReprClosure(cl) diff --git a/lib/system/reprjs.nim b/lib/system/reprjs.nim index fd1cb5c8b..57237cfff 100644 --- a/lib/system/reprjs.nim +++ b/lib/system/reprjs.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/lib/system/sets.nim b/lib/system/sets.nim index 794c65cb8..626d43c33 100644 --- a/lib/system/sets.nim +++ b/lib/system/sets.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index 32d4c3e91..7908fbe4d 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -8,7 +8,7 @@ # -# Nimrod's standard IO library. It contains high-performance +# Nim's standard IO library. It contains high-performance # routines for reading and writing data to (buffered) files or # TTYs. @@ -16,33 +16,33 @@ # of the standard library! -proc fputs(c: cstring, f: TFile) {.importc: "fputs", header: "", - tags: [FWriteIO].} -proc fgets(c: cstring, n: int, f: TFile): cstring {. - importc: "fgets", header: "", tags: [FReadIO].} -proc fgetc(stream: TFile): cint {.importc: "fgetc", header: "", - tags: [FReadIO].} -proc ungetc(c: cint, f: TFile) {.importc: "ungetc", header: "", +proc fputs(c: cstring, f: File) {.importc: "fputs", header: "", + tags: [WriteIOEffect].} +proc fgets(c: cstring, n: int, f: File): cstring {. + importc: "fgets", header: "", tags: [ReadIOEffect].} +proc fgetc(stream: File): cint {.importc: "fgetc", header: "", + tags: [ReadIOEffect].} +proc ungetc(c: cint, f: File) {.importc: "ungetc", header: "", tags: [].} -proc putc(c: char, stream: TFile) {.importc: "putc", header: "", - tags: [FWriteIO].} -proc fprintf(f: TFile, frmt: cstring) {.importc: "fprintf", - header: "", varargs, tags: [FWriteIO].} +proc putc(c: char, stream: File) {.importc: "putc", header: "", + tags: [WriteIOEffect].} +proc fprintf(f: File, frmt: cstring) {.importc: "fprintf", + header: "", varargs, tags: [WriteIOEffect].} proc strlen(c: cstring): int {. importc: "strlen", header: "", tags: [].} # C routine that is used here: -proc fread(buf: pointer, size, n: int, f: TFile): int {. - importc: "fread", header: "", tags: [FReadIO].} -proc fseek(f: TFile, offset: clong, whence: int): int {. +proc fread(buf: pointer, size, n: int, f: File): int {. + importc: "fread", header: "", tags: [ReadIOEffect].} +proc fseek(f: File, offset: clong, whence: int): int {. importc: "fseek", header: "", tags: [].} -proc ftell(f: TFile): int {.importc: "ftell", header: "", tags: [].} -proc setvbuf(stream: TFile, buf: pointer, typ, size: cint): cint {. +proc ftell(f: File): int {.importc: "ftell", header: "", tags: [].} +proc setvbuf(stream: File, buf: pointer, typ, size: cint): cint {. importc, header: "", tags: [].} {.push stackTrace:off, profiler:off.} -proc write(f: TFile, c: cstring) = fputs(c, f) +proc write(f: File, c: cstring) = fputs(c, f) {.pop.} when NoFakeVars: @@ -65,9 +65,9 @@ const BufSize = 4000 proc raiseEIO(msg: string) {.noinline, noreturn.} = - sysFatal(EIO, msg) + sysFatal(IOError, msg) -proc readLine(f: TFile, line: var TaintedString): bool = +proc readLine(f: File, line: var TaintedString): bool = # of course this could be optimized a bit; but IO is slow anyway... # and it was difficult to get this CORRECT with Ansi C's methods setLen(line.string, 0) # reuse the buffer! @@ -84,34 +84,34 @@ proc readLine(f: TFile, line: var TaintedString): bool = add line.string, chr(int(c)) result = true -proc readLine(f: TFile): TaintedString = +proc readLine(f: File): TaintedString = result = TaintedString(newStringOfCap(80)) if not readLine(f, result): raiseEIO("EOF reached") -proc write(f: TFile, i: int) = +proc write(f: File, i: int) = when sizeof(int) == 8: fprintf(f, "%lld", i) else: fprintf(f, "%ld", i) -proc write(f: TFile, i: BiggestInt) = +proc write(f: File, i: BiggestInt) = when sizeof(BiggestInt) == 8: fprintf(f, "%lld", i) else: fprintf(f, "%ld", i) -proc write(f: TFile, b: bool) = +proc write(f: File, b: bool) = if b: write(f, "true") else: write(f, "false") -proc write(f: TFile, r: float32) = fprintf(f, "%g", r) -proc write(f: TFile, r: BiggestFloat) = fprintf(f, "%g", r) +proc write(f: File, r: float32) = fprintf(f, "%g", r) +proc write(f: File, r: BiggestFloat) = fprintf(f, "%g", r) -proc write(f: TFile, c: char) = putc(c, f) -proc write(f: TFile, a: varargs[string, `$`]) = +proc write(f: File, c: char) = putc(c, f) +proc write(f: File, a: varargs[string, `$`]) = for x in items(a): write(f, x) -proc readAllBuffer(file: TFile): string = - # This proc is for TFile we want to read but don't know how many +proc readAllBuffer(file: File): string = + # This proc is for File we want to read but don't know how many # bytes we need to read before the buffer is empty. result = "" var buffer = newString(BufSize) @@ -124,27 +124,27 @@ proc readAllBuffer(file: TFile): string = result.add(buffer) break -proc rawFileSize(file: TFile): int = +proc rawFileSize(file: File): int = # this does not raise an error opposed to `getFileSize` var oldPos = ftell(file) discard fseek(file, 0, 2) # seek the end of the file result = ftell(file) discard fseek(file, clong(oldPos), 0) -proc readAllFile(file: TFile, len: int): string = +proc readAllFile(file: File, len: int): string = # We aquire the filesize beforehand and hope it doesn't change. # Speeds things up. result = newString(int(len)) if readBuffer(file, addr(result[0]), int(len)) != len: raiseEIO("error while reading from file") -proc readAllFile(file: TFile): string = +proc readAllFile(file: File): string = var len = rawFileSize(file) result = readAllFile(file, len) -proc readAll(file: TFile): TaintedString = +proc readAll(file: File): TaintedString = # Separate handling needed because we need to buffer when we - # don't know the overall length of the TFile. + # don't know the overall length of the File. var len = rawFileSize(file) if len >= 0: result = readAllFile(file, len).TaintedString @@ -165,13 +165,13 @@ proc writeFile(filename, content: string) = finally: close(f) -proc endOfFile(f: TFile): bool = +proc endOfFile(f: File): bool = # do not blame me; blame the ANSI C standard this is so brain-damaged var c = fgetc(f) ungetc(c, f) return c < 0'i32 -proc writeln[Ty](f: TFile, x: varargs[Ty, `$`]) = +proc writeln[Ty](f: File, x: varargs[Ty, `$`]) = for i in items(x): write(f, i) write(f, "\n") @@ -186,7 +186,7 @@ when (defined(windows) and not defined(useWinAnsi)) or defined(nimdoc): when defined(windows) and not defined(useWinAnsi): proc wfopen(filename, mode: WideCString): pointer {. importc: "_wfopen", nodecl.} - proc wfreopen(filename, mode: WideCString, stream: TFile): TFile {. + proc wfreopen(filename, mode: WideCString, stream: File): File {. importc: "_wfreopen", nodecl.} proc fopen(filename, mode: cstring): pointer = @@ -194,82 +194,82 @@ when defined(windows) and not defined(useWinAnsi): var m = newWideCString(mode) result = wfopen(f, m) - proc freopen(filename, mode: cstring, stream: TFile): TFile = + proc freopen(filename, mode: cstring, stream: File): File = var f = newWideCString(filename) var m = newWideCString(mode) result = wfreopen(f, m, stream) else: proc fopen(filename, mode: cstring): pointer {.importc: "fopen", noDecl.} - proc freopen(filename, mode: cstring, stream: TFile): TFile {. + proc freopen(filename, mode: cstring, stream: File): File {. importc: "freopen", nodecl.} const - FormatOpen: array [TFileMode, string] = ["rb", "wb", "w+b", "r+b", "ab"] + FormatOpen: array [FileMode, string] = ["rb", "wb", "w+b", "r+b", "ab"] #"rt", "wt", "w+t", "r+t", "at" - # we always use binary here as for Nimrod the OS line ending + # we always use binary here as for Nim the OS line ending # should not be translated. -proc open(f: var TFile, filename: string, - mode: TFileMode = fmRead, +proc open(f: var File, filename: string, + mode: FileMode = fmRead, bufSize: int = -1): bool = var p: pointer = fopen(filename, FormatOpen[mode]) - result = (p != nil) - f = cast[TFile](p) - if bufSize > 0 and bufSize <= high(cint).int: - if setvbuf(f, nil, IOFBF, bufSize.cint) != 0'i32: - sysFatal(EOutOfMemory, "out of memory") - elif bufSize == 0: - discard setvbuf(f, nil, IONBF, 0) + if p != nil: + result = true + f = cast[File](p) + if bufSize > 0 and bufSize <= high(cint).int: + discard setvbuf(f, nil, IOFBF, bufSize.cint) + elif bufSize == 0: + discard setvbuf(f, nil, IONBF, 0) -proc reopen(f: TFile, filename: string, mode: TFileMode = fmRead): bool = +proc reopen(f: File, filename: string, mode: FileMode = fmRead): bool = var p: pointer = freopen(filename, FormatOpen[mode], f) result = p != nil -proc fdopen(filehandle: TFileHandle, mode: cstring): TFile {. +proc fdopen(filehandle: FileHandle, mode: cstring): File {. importc: pccHack & "fdopen", header: "".} -proc open(f: var TFile, filehandle: TFileHandle, mode: TFileMode): bool = +proc open(f: var File, filehandle: FileHandle, mode: FileMode): bool = f = fdopen(filehandle, FormatOpen[mode]) result = f != nil -proc fwrite(buf: pointer, size, n: int, f: TFile): int {. +proc fwrite(buf: pointer, size, n: int, f: File): int {. importc: "fwrite", noDecl.} -proc readBuffer(f: TFile, buffer: pointer, len: int): int = +proc readBuffer(f: File, buffer: pointer, len: int): int = result = fread(buffer, 1, len, f) -proc readBytes(f: TFile, a: var openArray[int8], start, len: int): int = +proc readBytes(f: File, a: var openArray[int8], start, len: int): int = result = readBuffer(f, addr(a[start]), len) -proc readChars(f: TFile, a: var openArray[char], start, len: int): int = +proc readChars(f: File, a: var openArray[char], start, len: int): int = result = readBuffer(f, addr(a[start]), len) {.push stackTrace:off, profiler:off.} -proc writeBytes(f: TFile, a: openArray[int8], start, len: int): int = +proc writeBytes(f: File, a: openArray[int8], start, len: int): int = var x = cast[ptr array[0..1000_000_000, int8]](a) result = writeBuffer(f, addr(x[start]), len) -proc writeChars(f: TFile, a: openArray[char], start, len: int): int = +proc writeChars(f: File, a: openArray[char], start, len: int): int = var x = cast[ptr array[0..1000_000_000, int8]](a) result = writeBuffer(f, addr(x[start]), len) -proc writeBuffer(f: TFile, buffer: pointer, len: int): int = +proc writeBuffer(f: File, buffer: pointer, len: int): int = result = fwrite(buffer, 1, len, f) -proc write(f: TFile, s: string) = +proc write(f: File, s: string) = if writeBuffer(f, cstring(s), s.len) != s.len: raiseEIO("cannot write string to file") {.pop.} -proc setFilePos(f: TFile, pos: int64) = +proc setFilePos(f: File, pos: int64) = if fseek(f, clong(pos), 0) != 0: raiseEIO("cannot set file position") -proc getFilePos(f: TFile): int64 = +proc getFilePos(f: File): int64 = result = ftell(f) if result < 0: raiseEIO("cannot retrieve file position") -proc getFileSize(f: TFile): int64 = +proc getFileSize(f: File): int64 = var oldPos = getFilePos(f) discard fseek(f, 0, 2) # seek the end of the file result = getFilePos(f) diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim index b8ed29cfc..8b38f34f3 100644 --- a/lib/system/syslocks.nim +++ b/lib/system/syslocks.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -31,7 +31,7 @@ when defined(Windows): ## Tries to acquire the lock `L`. proc tryAcquireSys(L: var TSysLock): bool {.inline.} = - result = TryAcquireSysAux(L) != 0'i32 + result = tryAcquireSysAux(L) != 0'i32 proc acquireSys(L: var TSysLock) {.stdcall, noSideEffect, dynlib: "kernel32", importc: "EnterCriticalSection".} diff --git a/lib/system/sysspawn.nim b/lib/system/sysspawn.nim index 5161104a9..04f30872d 100644 --- a/lib/system/sysspawn.nim +++ b/lib/system/sysspawn.nim @@ -1,13 +1,13 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## Implements Nimrod's 'spawn'. +## Implements Nim's 'spawn'. when not declared(NimString): {.error: "You must not import this module explicitly".} diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index bc79bb254..440d040a5 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -119,7 +119,7 @@ proc addChar(s: NimString, c: char): NimString = inc(result.len) # These routines should be used like following: -# +# # s &= "Hello " & name & ", how do you feel?" # # @@ -130,7 +130,7 @@ proc addChar(s: NimString, c: char): NimString = # appendString(s, strLit3); # } # -# +# # s = "Hello " & name & ", how do you feel?" # # @@ -143,7 +143,7 @@ proc addChar(s: NimString, c: char): NimString = # s = tmp0; # } # -# +# # s = "" # # @@ -217,7 +217,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {. gch.tempStack.len = len0 else: for i in newLen..result.len-1: - forAllChildrenAux(cast[pointer](cast[TAddress](result) +% + forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +% GenericSeqSize +% (i*%elemSize)), extGetCellType(result).base, waZctDecRef) @@ -227,7 +227,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {. # presense of user defined destructors, the user will expect the cell to be # "destroyed" thus creating the same problem. We can destoy the cell in the # finalizer of the sequence, but this makes destruction non-deterministic. - zeroMem(cast[pointer](cast[TAddress](result) +% GenericSeqSize +% + zeroMem(cast[pointer](cast[ByteAddress](result) +% GenericSeqSize +% (newLen*%elemSize)), (result.len-%newLen) *% elemSize) result.len = newLen @@ -251,7 +251,7 @@ proc nimIntToStr(x: int): string {.compilerRtl.} = swap(result[j], result[i-j-1]) proc nimFloatToStr(f: float): string {.compilerproc.} = - var buf: array [0..64, char] + var buf: array[0..64, char] var n: int = c_sprintf(buf, "%.16g", f) var hasDot = false for i in 0..n-1: @@ -292,7 +292,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat, var i = start sign = 1.0 - t: array[128, char] + t: array[500, char] # flaviu says: 325 is the longest reasonable literal ti = 0 hasdigits = false diff --git a/lib/system/threads.nim b/lib/system/threads.nim index c30c57fb9..496c31af1 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -79,12 +79,20 @@ when defined(windows): type TThreadVarSlot = distinct int32 - proc threadVarAlloc(): TThreadVarSlot {. - importc: "TlsAlloc", stdcall, dynlib: "kernel32".} - proc threadVarSetValue(dwTlsIndex: TThreadVarSlot, lpTlsValue: pointer) {. - importc: "TlsSetValue", stdcall, dynlib: "kernel32".} - proc threadVarGetValue(dwTlsIndex: TThreadVarSlot): pointer {. - importc: "TlsGetValue", stdcall, dynlib: "kernel32".} + when true: + proc threadVarAlloc(): TThreadVarSlot {. + importc: "TlsAlloc", stdcall, header: "".} + proc threadVarSetValue(dwTlsIndex: TThreadVarSlot, lpTlsValue: pointer) {. + importc: "TlsSetValue", stdcall, header: "".} + proc threadVarGetValue(dwTlsIndex: TThreadVarSlot): pointer {. + importc: "TlsGetValue", stdcall, header: "".} + else: + proc threadVarAlloc(): TThreadVarSlot {. + importc: "TlsAlloc", stdcall, dynlib: "kernel32".} + proc threadVarSetValue(dwTlsIndex: TThreadVarSlot, lpTlsValue: pointer) {. + importc: "TlsSetValue", stdcall, dynlib: "kernel32".} + proc threadVarGetValue(dwTlsIndex: TThreadVarSlot): pointer {. + importc: "TlsGetValue", stdcall, dynlib: "kernel32".} else: {.passL: "-pthread".} @@ -116,16 +124,6 @@ else: proc pthread_cancel(a1: TSysThread): cint {. importc: "pthread_cancel", header: "".} - proc acquireSysTimeoutAux(L: var TSysLock, timeout: var Ttimespec): cint {. - importc: "pthread_mutex_timedlock", header: "".} - - proc acquireSysTimeout(L: var TSysLock, msTimeout: int) {.inline.} = - var a: Ttimespec - a.tv_sec = msTimeout div 1000 - a.tv_nsec = (msTimeout mod 1000) * 1000 - var res = acquireSysTimeoutAux(L, a) - if res != 0'i32: raise newException(EResourceExhausted, $strerror(res)) - type TThreadVarSlot {.importc: "pthread_key_t", pure, final, header: "".} = object @@ -184,7 +182,18 @@ type # XXX it'd be more efficient to not use a global variable for the # thread storage slot, but to rely on the implementation to assign slot X # for us... ;-) -var globalsSlot = threadVarAlloc() +var globalsSlot: TThreadVarSlot + +when not defined(useNimRtl): + when not useStackMaskHack: + var mainThread: TGcThread + +proc initThreadVarsEmulation() {.compilerProc, inline.} = + when not defined(useNimRtl): + globalsSlot = threadVarAlloc() + when declared(mainThread): + threadVarSetValue(globalsSlot, addr(mainThread)) + #const globalsSlot = TThreadVarSlot(0) #sysAssert checkSlot.int == globalsSlot.int @@ -202,11 +211,8 @@ when useStackMaskHack: # create for the main thread. Note: do not insert this data into the list # of all threads; it's not to be stopped etc. when not defined(useNimRtl): - when not useStackMaskHack: - var mainThread: TGcThread - threadVarSetValue(globalsSlot, addr(mainThread)) - when not defined(createNimRtl): initStackBottom() + #when not defined(createNimRtl): initStackBottom() initGC() when emulatedThreadVars: @@ -353,8 +359,7 @@ when hostOS == "windows": t.sys = createThread(nil, ThreadStackSize, threadProcWrapper[TArg], addr(t), 0'i32, dummyThreadId) if t.sys <= 0: - raise newException(EResourceExhausted, "cannot create thread") - + raise newException(ResourceExhaustedError, "cannot create thread") else: proc createThread*[TArg](t: var TThread[TArg], tp: proc (arg: TArg) {.thread.}, @@ -369,7 +374,7 @@ else: pthread_attr_init(a) pthread_attr_setstacksize(a, ThreadStackSize) if pthread_create(t.sys, a, threadProcWrapper[TArg], addr(t)) != 0: - raise newException(EResourceExhausted, "cannot create thread") + raise newException(ResourceExhaustedError, "cannot create thread") proc threadId*[TArg](t: var TThread[TArg]): TThreadId[TArg] {.inline.} = ## returns the thread ID of `t`. diff --git a/lib/system/timers.nim b/lib/system/timers.nim index fa1a13a5f..e58ff7adc 100644 --- a/lib/system/timers.nim +++ b/lib/system/timers.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim index cd64ff410..1e8bc6791 100644 --- a/lib/system/widestrs.nim +++ b/lib/system/widestrs.nim @@ -1,13 +1,13 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -# Nimrod support for C/C++'s `wide strings`:idx:. This is part of the system +# Nim support for C/C++'s `wide strings`:idx:. This is part of the system # module! Do not import it directly! when not declared(NimString): diff --git a/lib/windows/mmsystem.nim b/lib/windows/mmsystem.nim index 91279a5ef..45613d8e2 100644 --- a/lib/windows/mmsystem.nim +++ b/lib/windows/mmsystem.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2006 Andreas Rumpf # # See the file "copying.txt", included in this @@ -27,15 +27,15 @@ import windows type - MMRESULT* = UINT - MMVERSION* = UINT + MMRESULT* = uint32 + MMVERSION* = uint32 HWAVEOUT* = THandle LPHWAVEOUT* = ptr HWAVEOUT HWAVEIN* = THandle LPHWAVEIN* = ptr HWAVEOUT HWAVE* = THandle LPHWAVE* = ptr THandle - LPUINT* = ptr UINT + LPUINT* = ptr uint32 const MAXPNAMELEN* = 32 @@ -178,18 +178,18 @@ const DRV_MCI_LAST* = (DRV_RESERVED + 0x00000FFF) type - PDRVCALLBACK* = proc (hdrvr: tHandle, uMsg: UINT, dwUser, dw1, dw2: DWORD){. + PDRVCALLBACK* = proc (hdrvr: THandle, uMsg: uint32, dwUser, dw1, dw2: DWORD){. stdcall.} -proc sndPlaySoundA*(Name: LPCSTR, flags: UINT): BOOL{.stdcall, +proc sndPlaySoundA*(Name: LPCSTR, flags: uint32): bool{.stdcall, dynlib: "winmm.dll", importc: "sndPlaySoundA".} -proc sndPlaySoundW*(Name: LPCWSTR, flags: UINT): BOOL{.stdcall, +proc sndPlaySoundW*(Name: LPCWSTR, flags: uint32): bool{.stdcall, dynlib: "winmm.dll", importc: "sndPlaySoundW".} when defined(winUNICODE): - proc sndPlaySound*(Name: cstring, flags: UINT): BOOL{.stdcall, + proc sndPlaySound*(Name: cstring, flags: uint32): bool{.stdcall, dynlib: "winmm.dll", importc: "sndPlaySoundW".} else: - proc sndPlaySound*(Name: cstring, flags: UINT): BOOL{.stdcall, + proc sndPlaySound*(Name: cstring, flags: uint32): bool{.stdcall, dynlib: "winmm.dll", importc: "sndPlaySoundA".} const SND_NODEFAULT* = 2 @@ -225,12 +225,12 @@ const WIM_OPEN* = MM_WIM_OPEN WIM_CLOSE* = MM_WIM_CLOSE WIM_DATA* = MM_WIM_DATA - WAVE_MAPPER* = UINT(- 1) + WAVE_MAPPER* = uint32(- 1) WAVE_FORMAT_QUERY* = 1 WAVE_ALLOWSYNC* = 2 WAVE_MAPPED* = 4 WAVE_FORMAT_DIRECT* = 8 - WAVE_FORMAT_DIRECT_QUERY* = (WAVE_FORMAT_QUERY Or WAVE_FORMAT_DIRECT) + WAVE_FORMAT_DIRECT_QUERY* = (WAVE_FORMAT_QUERY or WAVE_FORMAT_DIRECT) MIM_OPEN* = MM_MIM_OPEN MIM_CLOSE* = MM_MIM_CLOSE MIM_DATA* = MM_MIM_DATA @@ -242,7 +242,7 @@ const MOM_DONE* = MM_MOM_DONE MIM_MOREDATA* = MM_MIM_MOREDATA MOM_POSITIONCB* = MM_MOM_POSITIONCB - MIDIMAPPER* = UINT(- 1) + MIDIMAPPER* = uint32(- 1) MIDI_IO_STATUS* = 32 MIDI_CACHE_ALL* = 1 MIDI_CACHE_BESTFIT* = 2 @@ -364,53 +364,53 @@ const MIXERCONTROL_CT_UNITS_DECIBELS* = 0x00040000 MIXERCONTROL_CT_UNITS_PERCENT* = 0x00050000 MIXERCONTROL_CONTROLTYPE_CUSTOM* = ( - MIXERCONTROL_CT_CLASS_CUSTOM Or MIXERCONTROL_CT_UNITS_CUSTOM) - MIXERCONTROL_CONTROLTYPE_BOOLEANMETER* = (MIXERCONTROL_CT_CLASS_METER Or - MIXERCONTROL_CT_SC_METER_POLLED Or MIXERCONTROL_CT_UNITS_BOOLEAN) - MIXERCONTROL_CONTROLTYPE_SIGNEDMETER* = (MIXERCONTROL_CT_CLASS_METER Or - MIXERCONTROL_CT_SC_METER_POLLED Or MIXERCONTROL_CT_UNITS_SIGNED) + MIXERCONTROL_CT_CLASS_CUSTOM or MIXERCONTROL_CT_UNITS_CUSTOM) + MIXERCONTROL_CONTROLTYPE_BOOLEANMETER* = (MIXERCONTROL_CT_CLASS_METER or + MIXERCONTROL_CT_SC_METER_POLLED or MIXERCONTROL_CT_UNITS_BOOLEAN) + MIXERCONTROL_CONTROLTYPE_SIGNEDMETER* = (MIXERCONTROL_CT_CLASS_METER or + MIXERCONTROL_CT_SC_METER_POLLED or MIXERCONTROL_CT_UNITS_SIGNED) MIXERCONTROL_CONTROLTYPE_PEAKMETER* = ( MIXERCONTROL_CONTROLTYPE_SIGNEDMETER + 1) - MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER* = (MIXERCONTROL_CT_CLASS_METER Or - MIXERCONTROL_CT_SC_METER_POLLED Or MIXERCONTROL_CT_UNITS_UNSIGNED) - MIXERCONTROL_CONTROLTYPE_BOOLEAN* = (MIXERCONTROL_CT_CLASS_SWITCH Or - MIXERCONTROL_CT_SC_SWITCH_BOOLEAN Or MIXERCONTROL_CT_UNITS_BOOLEAN) + MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER* = (MIXERCONTROL_CT_CLASS_METER or + MIXERCONTROL_CT_SC_METER_POLLED or MIXERCONTROL_CT_UNITS_UNSIGNED) + MIXERCONTROL_CONTROLTYPE_BOOLEAN* = (MIXERCONTROL_CT_CLASS_SWITCH or + MIXERCONTROL_CT_SC_SWITCH_BOOLEAN or MIXERCONTROL_CT_UNITS_BOOLEAN) MIXERCONTROL_CONTROLTYPE_ONOFF* = (MIXERCONTROL_CONTROLTYPE_BOOLEAN + 1) MIXERCONTROL_CONTROLTYPE_MUTE* = (MIXERCONTROL_CONTROLTYPE_BOOLEAN + 2) MIXERCONTROL_CONTROLTYPE_MONO* = (MIXERCONTROL_CONTROLTYPE_BOOLEAN + 3) MIXERCONTROL_CONTROLTYPE_LOUDNESS* = (MIXERCONTROL_CONTROLTYPE_BOOLEAN + 4) MIXERCONTROL_CONTROLTYPE_STEREOENH* = (MIXERCONTROL_CONTROLTYPE_BOOLEAN + 5) - MIXERCONTROL_CONTROLTYPE_BUTTON* = (MIXERCONTROL_CT_CLASS_SWITCH Or - MIXERCONTROL_CT_SC_SWITCH_BUTTON Or MIXERCONTROL_CT_UNITS_BOOLEAN) + MIXERCONTROL_CONTROLTYPE_BUTTON* = (MIXERCONTROL_CT_CLASS_SWITCH or + MIXERCONTROL_CT_SC_SWITCH_BUTTON or MIXERCONTROL_CT_UNITS_BOOLEAN) MIXERCONTROL_CONTROLTYPE_DECIBELS* = ( - MIXERCONTROL_CT_CLASS_NUMBER Or MIXERCONTROL_CT_UNITS_DECIBELS) + MIXERCONTROL_CT_CLASS_NUMBER or MIXERCONTROL_CT_UNITS_DECIBELS) MIXERCONTROL_CONTROLTYPE_SIGNED* = ( - MIXERCONTROL_CT_CLASS_NUMBER Or MIXERCONTROL_CT_UNITS_SIGNED) + MIXERCONTROL_CT_CLASS_NUMBER or MIXERCONTROL_CT_UNITS_SIGNED) MIXERCONTROL_CONTROLTYPE_UNSIGNED* = ( - MIXERCONTROL_CT_CLASS_NUMBER Or MIXERCONTROL_CT_UNITS_UNSIGNED) + MIXERCONTROL_CT_CLASS_NUMBER or MIXERCONTROL_CT_UNITS_UNSIGNED) MIXERCONTROL_CONTROLTYPE_PERCENT* = ( - MIXERCONTROL_CT_CLASS_NUMBER Or MIXERCONTROL_CT_UNITS_PERCENT) + MIXERCONTROL_CT_CLASS_NUMBER or MIXERCONTROL_CT_UNITS_PERCENT) MIXERCONTROL_CONTROLTYPE_SLIDER* = ( - MIXERCONTROL_CT_CLASS_SLIDER Or MIXERCONTROL_CT_UNITS_SIGNED) + MIXERCONTROL_CT_CLASS_SLIDER or MIXERCONTROL_CT_UNITS_SIGNED) MIXERCONTROL_CONTROLTYPE_PAN* = (MIXERCONTROL_CONTROLTYPE_SLIDER + 1) MIXERCONTROL_CONTROLTYPE_QSOUNDPAN* = (MIXERCONTROL_CONTROLTYPE_SLIDER + 2) MIXERCONTROL_CONTROLTYPE_FADER* = ( - MIXERCONTROL_CT_CLASS_FADER Or MIXERCONTROL_CT_UNITS_UNSIGNED) + MIXERCONTROL_CT_CLASS_FADER or MIXERCONTROL_CT_UNITS_UNSIGNED) MIXERCONTROL_CONTROLTYPE_VOLUME* = (MIXERCONTROL_CONTROLTYPE_FADER + 1) MIXERCONTROL_CONTROLTYPE_BASS* = (MIXERCONTROL_CONTROLTYPE_FADER + 2) MIXERCONTROL_CONTROLTYPE_TREBLE* = (MIXERCONTROL_CONTROLTYPE_FADER + 3) MIXERCONTROL_CONTROLTYPE_EQUALIZER* = (MIXERCONTROL_CONTROLTYPE_FADER + 4) - MIXERCONTROL_CONTROLTYPE_SINGLESELECT* = (MIXERCONTROL_CT_CLASS_LIST Or - MIXERCONTROL_CT_SC_LIST_SINGLE Or MIXERCONTROL_CT_UNITS_BOOLEAN) + MIXERCONTROL_CONTROLTYPE_SINGLESELECT* = (MIXERCONTROL_CT_CLASS_LIST or + MIXERCONTROL_CT_SC_LIST_SINGLE or MIXERCONTROL_CT_UNITS_BOOLEAN) MIXERCONTROL_CONTROLTYPE_MUX* = (MIXERCONTROL_CONTROLTYPE_SINGLESELECT + 1) - MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT* = (MIXERCONTROL_CT_CLASS_LIST Or - MIXERCONTROL_CT_SC_LIST_MULTIPLE Or MIXERCONTROL_CT_UNITS_BOOLEAN) + MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT* = (MIXERCONTROL_CT_CLASS_LIST or + MIXERCONTROL_CT_SC_LIST_MULTIPLE or MIXERCONTROL_CT_UNITS_BOOLEAN) MIXERCONTROL_CONTROLTYPE_MIXER* = (MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT + 1) - MIXERCONTROL_CONTROLTYPE_MICROTIME* = (MIXERCONTROL_CT_CLASS_TIME Or - MIXERCONTROL_CT_SC_TIME_MICROSECS Or MIXERCONTROL_CT_UNITS_UNSIGNED) - MIXERCONTROL_CONTROLTYPE_MILLITIME* = (MIXERCONTROL_CT_CLASS_TIME Or - MIXERCONTROL_CT_SC_TIME_MILLISECS Or MIXERCONTROL_CT_UNITS_UNSIGNED) + MIXERCONTROL_CONTROLTYPE_MICROTIME* = (MIXERCONTROL_CT_CLASS_TIME or + MIXERCONTROL_CT_SC_TIME_MICROSECS or MIXERCONTROL_CT_UNITS_UNSIGNED) + MIXERCONTROL_CONTROLTYPE_MILLITIME* = (MIXERCONTROL_CT_CLASS_TIME or + MIXERCONTROL_CT_SC_TIME_MILLISECS or MIXERCONTROL_CT_UNITS_UNSIGNED) MIXER_SHORT_NAME_CHARS* = 16 MIXER_LONG_NAME_CHARS* = 64 MIXERR_INVALLINE* = (MIXERR_BASE + 0) @@ -419,15 +419,15 @@ const MIXERR_LASTERROR* = (MIXERR_BASE + 2) MIXER_OBJECTF_HANDLE* = 0x80000000 MIXER_OBJECTF_MIXER* = 0 - MIXER_OBJECTF_HMIXER* = (MIXER_OBJECTF_HANDLE Or MIXER_OBJECTF_MIXER) + MIXER_OBJECTF_HMIXER* = (MIXER_OBJECTF_HANDLE or MIXER_OBJECTF_MIXER) MIXER_OBJECTF_WAVEOUT* = 0x10000000 - MIXER_OBJECTF_HWAVEOUT* = (MIXER_OBJECTF_HANDLE Or MIXER_OBJECTF_WAVEOUT) + MIXER_OBJECTF_HWAVEOUT* = (MIXER_OBJECTF_HANDLE or MIXER_OBJECTF_WAVEOUT) MIXER_OBJECTF_WAVEIN* = 0x20000000 - MIXER_OBJECTF_HWAVEIN* = (MIXER_OBJECTF_HANDLE Or MIXER_OBJECTF_WAVEIN) + MIXER_OBJECTF_HWAVEIN* = (MIXER_OBJECTF_HANDLE or MIXER_OBJECTF_WAVEIN) MIXER_OBJECTF_MIDIOUT* = 0x30000000 - MIXER_OBJECTF_HMIDIOUT* = (MIXER_OBJECTF_HANDLE Or MIXER_OBJECTF_MIDIOUT) + MIXER_OBJECTF_HMIDIOUT* = (MIXER_OBJECTF_HANDLE or MIXER_OBJECTF_MIDIOUT) MIXER_OBJECTF_MIDIIN* = 0x40000000 - MIXER_OBJECTF_HMIDIIN* = (MIXER_OBJECTF_HANDLE Or MIXER_OBJECTF_MIDIIN) + MIXER_OBJECTF_HMIDIIN* = (MIXER_OBJECTF_HANDLE or MIXER_OBJECTF_MIDIIN) MIXER_OBJECTF_AUX* = 0x50000000 MIXER_GETCONTROLDETAILSF_VALUE* = 0 MIXER_GETCONTROLDETAILSF_LISTTEXT* = 1 @@ -492,8 +492,8 @@ const JOY_RETURNPOVCTS* = 512 JOY_RETURNCENTERED* = 0x00000400 JOY_USEDEADZONE* = 0x00000800 - JOY_RETURNALL* = (JOY_RETURNX Or JOY_RETURNY Or JOY_RETURNZ Or JOY_RETURNR Or - JOY_RETURNU Or JOY_RETURNV Or JOY_RETURNPOV Or JOY_RETURNBUTTONS) + JOY_RETURNALL* = (JOY_RETURNX or JOY_RETURNY or JOY_RETURNZ or JOY_RETURNR or + JOY_RETURNU or JOY_RETURNV or JOY_RETURNPOV or JOY_RETURNBUTTONS) JOY_CAL_READALWAYS* = 0x00010000 JOY_CAL_READXYONLY* = 0x00020000 JOY_CAL_READ3* = 0x00040000 @@ -982,17 +982,17 @@ const #///////////////////////////////////////////////////////// type - mmtime* {.final.} = object - wType*: UINT + MMTIME* {.final.} = object + wType*: uint32 hour*, min*, sec*, frame*, fps*, dummy*: int8 pad*: array[0..1, int8] - PMMTIME* = ptr mmtime - NPMMTIME* = ptr mmtime - LPMMTIME* = ptr mmtime - PWAVEHDR* = ptr wavehdr - TMMTime* = mmtime - wavehdr* {.final.} = object + PMMTIME* = ptr MMTIME + NPMMTIME* = ptr MMTIME + LPMMTIME* = ptr MMTIME + PWAVEHDR* = ptr WAVEHDR + TMMTime* = MMTIME + WAVEHDR* {.final.} = object lpData*: cstring dwBufferLength*: DWORD dwBytesRecorded*: DWORD @@ -1003,13 +1003,13 @@ type reserved*: DWORD TWAVEHDR* = WAVEHDR - NPWAVEHDR* = ptr wavehdr - LPWAVEHDR* = ptr wavehdr + NPWAVEHDR* = ptr WAVEHDR + LPWAVEHDR* = ptr WAVEHDR WAVEOUTCAPSA* {.final.} = object wMid*: int16 wPid*: int16 vDriverVersion*: MMVERSION - szPname*: array[0..Pred(MAXPNAMELEN), CHAR] + szPname*: array[0..pred(MAXPNAMELEN), char] dwFormats*: DWORD wChannels*: int16 wReserved1*: int16 @@ -1023,7 +1023,7 @@ type wMid*: int16 wPid*: int16 vDriverVersion*: MMVERSION - szPname*: array[0..Pred(MAXPNAMELEN), WCHAR] + szPname*: array[0..pred(MAXPNAMELEN), WCHAR] dwFormats*: DWORD wChannels*: int16 wReserved1*: int16 @@ -1052,7 +1052,7 @@ type wMid*: int16 wPid*: int16 vDriverVersion*: MMVERSION - szPname*: array[0..Pred(MAXPNAMELEN), CHAR] + szPname*: array[0..pred(MAXPNAMELEN), char] dwFormats*: DWORD wChannels*: int16 wReserved1*: int16 @@ -1065,7 +1065,7 @@ type wMid*: int16 wPid*: int16 vDriverVersion*: MMVERSION - szPname*: array[0..Pred(MAXPNAMELEN), WCHAR] + szPname*: array[0..pred(MAXPNAMELEN), WCHAR] dwFormats*: DWORD wChannels*: int16 wReserved1*: int16 @@ -1089,29 +1089,29 @@ else: LPWAVEINCAPS* = LPWAVEINCAPSA type TWAVEINCAPS* = WAVEINCAPS - waveformat* {.final.} = object + WAVEFORMAT* {.final.} = object wFormatTag*: int16 nChannels*: int16 nSamplesPerSec*: DWORD nAvgBytesPerSec*: DWORD nBlockAlign*: int16 - PWAVEFORMAT* = ptr waveformat - NPWAVEFORMAT* = ptr waveformat - LPWAVEFORMAT* = ptr waveformat - TWAVEFORMAT* = waveformat + PWAVEFORMAT* = ptr WAVEFORMAT + NPWAVEFORMAT* = ptr WAVEFORMAT + LPWAVEFORMAT* = ptr WAVEFORMAT + TWAVEFORMAT* = WAVEFORMAT const WAVE_FORMAT_PCM* = 1 type - pcmwaveformat* {.final.} = object + PCMWAVEFORMAT* {.final.} = object wf*: WAVEFORMAT wBitsPerSample*: int16 - PPCMWAVEFORMAT* = ptr pcmwaveformat - NPPCMWAVEFORMAT* = ptr pcmwaveformat - LPPCMWAVEFORMAT* = ptr pcmwaveformat + PPCMWAVEFORMAT* = ptr PCMWAVEFORMAT + NPPCMWAVEFORMAT* = ptr PCMWAVEFORMAT + LPPCMWAVEFORMAT* = ptr PCMWAVEFORMAT TPCMWAVEFORMAT* = PCMWAVEFORMAT WAVEFORMATEX* {.final.} = object wFormatTag*: int16 @@ -1141,15 +1141,15 @@ const MIDIPATCHSIZE* = 128 type - PATCHARRAY* = array[0..Pred(MIDIPATCHSIZE), int16] + PATCHARRAY* = array[0..pred(MIDIPATCHSIZE), int16] LPPATCHARRAY* = ptr int16 - KEYARRAY* = array[0..Pred(MIDIPATCHSIZE), int16] + KEYARRAY* = array[0..pred(MIDIPATCHSIZE), int16] LPKEYARRAY* = ptr int16 MIDIOUTCAPSA* {.final.} = object wMid*: int16 wPid*: int16 vDriverVersion*: MMVERSION - szPname*: array[0..Pred(MAXPNAMELEN), CHAR] + szPname*: array[0..pred(MAXPNAMELEN), char] wTechnology*: int16 wVoices*: int16 wNotes*: int16 @@ -1164,7 +1164,7 @@ type wMid*: int16 wPid*: int16 vDriverVersion*: MMVERSION - szPname*: array[0..Pred(MAXPNAMELEN), WCHAR] + szPname*: array[0..pred(MAXPNAMELEN), Wchar] wTechnology*: int16 wVoices*: int16 wNotes*: int16 @@ -1179,7 +1179,7 @@ type wMid*: int16 wPid*: int16 vDriverVersion*: MMVERSION - szPname*: array[0..Pred(MAXPNAMELEN), CHAR] + szPname*: array[0..pred(MAXPNAMELEN), char] dwSupport*: DWORD PMIDIINCAPSA* = ptr MIDIINCAPSA @@ -1190,7 +1190,7 @@ type wMid*: int16 wPid*: int16 vDriverVersion*: MMVERSION - szPname*: array[0..Pred(MAXPNAMELEN), WCHAR] + szPname*: array[0..pred(MAXPNAMELEN), Wchar] dwSupport*: DWORD PMIDIINCAPSW* = ptr MIDIINCAPSW @@ -1220,8 +1220,8 @@ else: LPMIDIINCAPS* = LPMIDIINCAPSA type TMIDIINCAPS* = MIDIINCAPS - PMIDIHDR* = ptr midihdr - midihdr* {.final.} = object + PMIDIHDR* = ptr MIDIHDR + MIDIHDR* {.final.} = object lpData*: cstring dwBufferLength*: DWORD dwBytesRecorded*: DWORD @@ -1230,19 +1230,19 @@ type lpNext*: PMIDIHDR reserved*: DWORD dwOffset*: DWORD - dwReserved*: array[0..Pred(8), DWORD] + dwReserved*: array[0..pred(8), DWORD] - NPMIDIHDR* = ptr midihdr - LPMIDIHDR* = ptr midihdr + NPMIDIHDR* = ptr MIDIHDR + LPMIDIHDR* = ptr MIDIHDR TMIDIHDR* = MIDIHDR - midievent* {.final.} = object + MIDIEVENT* {.final.} = object dwDeltaTime*: DWORD dwStreamID*: DWORD dwEvent*: DWORD - dwParms*: array[0..Pred(1), DWORD] + dwParms*: array[0..pred(1), DWORD] TMIDIEVENT* = MIDIEVENT - midistrmbuffver* {.final.} = object + MIDISTRMBUFFVER* {.final.} = object dwVersion*: DWORD dwMid*: DWORD dwOEMVersion*: DWORD @@ -1262,7 +1262,7 @@ type wMid*: int16 wPid*: int16 vDriverVersion*: MMVERSION - szPname*: array[0..Pred(MAXPNAMELEN), CHAR] + szPname*: array[0..pred(MAXPNAMELEN), char] wTechnology*: int16 wReserved1*: int16 dwSupport*: DWORD @@ -1275,7 +1275,7 @@ type wMid*: int16 wPid*: int16 vDriverVersion*: MMVERSION - szPname*: array[0..Pred(MAXPNAMELEN), WCHAR] + szPname*: array[0..pred(MAXPNAMELEN), Wchar] wTechnology*: int16 wReserved1*: int16 dwSupport*: DWORD @@ -1304,14 +1304,14 @@ type HMIXER* = THandle LPHMIXER* = ptr HMIXER -proc mixerGetNumDevs*(): UINT{.stdcall, dynlib: "winmm.dll", +proc mixerGetNumDevs*(): uint32{.stdcall, dynlib: "winmm.dll", importc: "mixerGetNumDevs".} type MIXERCAPSA* {.final.} = object wMid*: int16 wPid*: int16 vDriverVersion*: MMVERSION - szPname*: array[0..Pred(MAXPNAMELEN), CHAR] + szPname*: array[0..pred(MAXPNAMELEN), char] fdwSupport*: DWORD cDestinations*: DWORD @@ -1322,7 +1322,7 @@ type wMid*: int16 wPid*: int16 vDriverVersion*: MMVERSION - szPname*: array[0..Pred(MAXPNAMELEN), WCHAR] + szPname*: array[0..pred(MAXPNAMELEN), Wchar] fdwSupport*: DWORD cDestinations*: DWORD @@ -1353,12 +1353,12 @@ type cChannels*: DWORD cConnections*: DWORD cControls*: DWORD - szShortName*: array[0..Pred(MIXER_SHORT_NAME_CHARS), CHAR] - szName*: array[0..Pred(MIXER_LONG_NAME_CHARS), CHAR] + szShortName*: array[0..pred(MIXER_SHORT_NAME_CHARS), char] + szName*: array[0..pred(MIXER_LONG_NAME_CHARS), char] dwType*, dwDeviceID*: DWORD wMid*, wPid*: int16 vDriverVersion*: MMVERSION - szPname*: array[0..pred(MAXPNAMELEN), Char] + szPname*: array[0..pred(MAXPNAMELEN), char] PMIXERLINEA* = ptr MIXERLINEA LPMIXERLINEA* = ptr MIXERLINEA @@ -1374,8 +1374,8 @@ type cChannels*: DWORD cConnections*: DWORD cControls*: DWORD - szShortName*: array[0..Pred(MIXER_SHORT_NAME_CHARS), WCHAR] - szName*: array[0..Pred(MIXER_LONG_NAME_CHARS), WCHAR] + szShortName*: array[0..pred(MIXER_SHORT_NAME_CHARS), WCHAR] + szName*: array[0..pred(MIXER_LONG_NAME_CHARS), WCHAR] dwType*, dwDeviceID*: DWORD wMid*, wPid*: int16 vDriverVersion*: MMVERSION @@ -1403,8 +1403,8 @@ type dwControlType*: DWORD fdwControl*: DWORD cMultipleItems*: DWORD - szShortName*: array[0..Pred(MIXER_SHORT_NAME_CHARS), CHAR] - szName*: array[0..Pred(MIXER_LONG_NAME_CHARS), CHAR] + szShortName*: array[0..pred(MIXER_SHORT_NAME_CHARS), char] + szName*: array[0..pred(MIXER_LONG_NAME_CHARS), char] dwMinimum*, dwMaximum*: DWORD dwReserved*: array[0..3, DWORD] cSteps*: DWORD @@ -1419,8 +1419,8 @@ type dwControlType*: DWORD fdwControl*: DWORD cMultipleItems*: DWORD - szShortName*: array[0..Pred(MIXER_SHORT_NAME_CHARS), WCHAR] - szName*: array[0..Pred(MIXER_LONG_NAME_CHARS), WCHAR] + szShortName*: array[0..pred(MIXER_SHORT_NAME_CHARS), WCHAR] + szName*: array[0..pred(MIXER_LONG_NAME_CHARS), WCHAR] dwMinimum*, dwMaximum*: DWORD dwReserved*: array[0..3, DWORD] cSteps*: DWORD @@ -1478,15 +1478,15 @@ type dwControlID*: DWORD cChannels*: DWORD cMultipleItems*, cbDetails*: DWORD - paDetails*: Pointer + paDetails*: pointer - MIXERCONTROLDETAILS* = tMIXERCONTROLDETAILS - PMIXERCONTROLDETAILS* = ptr tMIXERCONTROLDETAILS - LPMIXERCONTROLDETAILS* = ptr tMIXERCONTROLDETAILS + MIXERCONTROLDETAILS* = TMIXERCONTROLDETAILS + PMIXERCONTROLDETAILS* = ptr TMIXERCONTROLDETAILS + LPMIXERCONTROLDETAILS* = ptr TMIXERCONTROLDETAILS MIXERCONTROLDETAILS_LISTTEXTA* {.final.} = object dwParam1*: DWORD dwParam2*: DWORD - szName*: array[0..Pred(MIXER_LONG_NAME_CHARS), CHAR] + szName*: array[0..pred(MIXER_LONG_NAME_CHARS), char] PMIXERCONTROLDETAILS_LISTTEXTA* = ptr MIXERCONTROLDETAILS_LISTTEXTA LPMIXERCONTROLDETAILS_LISTTEXTA* = ptr MIXERCONTROLDETAILS_LISTTEXTA @@ -1494,7 +1494,7 @@ type MIXERCONTROLDETAILS_LISTTEXTW* {.final.} = object dwParam1*: DWORD dwParam2*: DWORD - szName*: array[0..Pred(MIXER_LONG_NAME_CHARS), WCHAR] + szName*: array[0..pred(MIXER_LONG_NAME_CHARS), WCHAR] PMIXERCONTROLDETAILS_LISTTEXTW* = ptr MIXERCONTROLDETAILS_LISTTEXTW LPMIXERCONTROLDETAILS_LISTTEXTW* = ptr MIXERCONTROLDETAILS_LISTTEXTW @@ -1530,42 +1530,42 @@ type PMIXERCONTROLDETAILS_UNSIGNED* = ptr MIXERCONTROLDETAILS_UNSIGNED LPMIXERCONTROLDETAILS_UNSIGNED* = ptr MIXERCONTROLDETAILS_UNSIGNED TMIXERCONTROLDETAILS_UNSIGNED* = MIXERCONTROLDETAILS_UNSIGNED - LPTIMECALLBACK* = proc (uTimerID, uMsg: UINT, dwUser, dw1, dw2: DWORD){. + LPTIMECALLBACK* = proc (uTimerID, uMsg: uint32, dwUser, dw1, dw2: DWORD){. stdcall.} TTIMECALLBACK* = LPTIMECALLBACK - timecaps* {.final.} = object - wPeriodMin*: UINT - wPeriodMax*: UINT + TIMECAPS* {.final.} = object + wPeriodMin*: uint32 + wPeriodMax*: uint32 - PTIMECAPS* = ptr timecaps - NPTIMECAPS* = ptr timecaps - LPTIMECAPS* = ptr timecaps + PTIMECAPS* = ptr TIMECAPS + NPTIMECAPS* = ptr TIMECAPS + LPTIMECAPS* = ptr TIMECAPS TTIMECAS* = TIMECAPS JOYCAPSA* {.final.} = object wMid*: int16 wPid*: int16 - szPname*: array[0..Pred(MAXPNAMELEN), CHAR] - wXmin*: UINT - wXmax*: UINT - wYmin*: UINT - wYmax*: UINT - wZmin*: UINT - wZmax*: UINT - wNumButtons*: UINT - wPeriodMin*: UINT - wPeriodMax*: UINT - wRmin*: UINT - wRmax*: UINT - wUmin*: UINT - wUmax*: UINT - wVmin*: UINT - wVmax*: UINT - wCaps*: UINT - wMaxAxes*: UINT - wNumAxes*: UINT - wMaxButtons*: UINT - szRegKey*: array[0..Pred(MAXPNAMELEN), CHAR] - szOEMVxD*: array[0..Pred(MAX_JOYSTICKOEMVXDNAME), CHAR] + szPname*: array[0..pred(MAXPNAMELEN), char] + wXmin*: uint32 + wXmax*: uint32 + wYmin*: uint32 + wYmax*: uint32 + wZmin*: uint32 + wZmax*: uint32 + wNumButtons*: uint32 + wPeriodMin*: uint32 + wPeriodMax*: uint32 + wRmin*: uint32 + wRmax*: uint32 + wUmin*: uint32 + wUmax*: uint32 + wVmin*: uint32 + wVmax*: uint32 + wCaps*: uint32 + wMaxAxes*: uint32 + wNumAxes*: uint32 + wMaxButtons*: uint32 + szRegKey*: array[0..pred(MAXPNAMELEN), char] + szOEMVxD*: array[0..pred(MAX_JOYSTICKOEMVXDNAME), char] PJOYCAPSA* = ptr JOYCAPSA NPJOYCAPSA* = ptr JOYCAPSA @@ -1574,28 +1574,28 @@ type JOYCAPSW* {.final.} = object wMid*: int16 wPid*: int16 - szPname*: array[0..Pred(MAXPNAMELEN), WCHAR] - wXmin*: UINT - wXmax*: UINT - wYmin*: UINT - wYmax*: UINT - wZmin*: UINT - wZmax*: UINT - wNumButtons*: UINT - wPeriodMin*: UINT - wPeriodMax*: UINT - wRmin*: UINT - wRmax*: UINT - wUmin*: UINT - wUmax*: UINT - wVmin*: UINT - wVmax*: UINT - wCaps*: UINT - wMaxAxes*: UINT - wNumAxes*: UINT - wMaxButtons*: UINT - szRegKey*: array[0..Pred(MAXPNAMELEN), WCHAR] - szOEMVxD*: array[0..Pred(MAX_JOYSTICKOEMVXDNAME), WCHAR] + szPname*: array[0..pred(MAXPNAMELEN), WCHAR] + wXmin*: uint32 + wXmax*: uint32 + wYmin*: uint32 + wYmax*: uint32 + wZmin*: uint32 + wZmax*: uint32 + wNumButtons*: uint32 + wPeriodMin*: uint32 + wPeriodMax*: uint32 + wRmin*: uint32 + wRmax*: uint32 + wUmin*: uint32 + wUmax*: uint32 + wVmin*: uint32 + wVmax*: uint32 + wCaps*: uint32 + wMaxAxes*: uint32 + wNumAxes*: uint32 + wMaxButtons*: uint32 + szRegKey*: array[0..pred(MAXPNAMELEN), WCHAR] + szOEMVxD*: array[0..pred(MAX_JOYSTICKOEMVXDNAME), WCHAR] PJOYCAPSW* = ptr JOYCAPSW NPJOYCAPSW* = ptr JOYCAPSW @@ -1616,45 +1616,45 @@ else: LPJOYCAPS* = LPJOYCAPSA type TJOYCAPS* = JOYCAPS - joyinfo* {.final.} = object - wXpos*: UINT - wYpos*: UINT - wZpos*: UINT - wButtons*: UINT + JOYINFO* {.final.} = object + wXpos*: uint32 + wYpos*: uint32 + wZpos*: uint32 + wButtons*: uint32 - PJOYINFO* = ptr joyinfo - NPJOYINFO* = ptr joyinfo - LPJOYINFO* = ptr joyinfo + PJOYINFO* = ptr JOYINFO + NPJOYINFO* = ptr JOYINFO + LPJOYINFO* = ptr JOYINFO TJOYINFO* = JOYINFO - joyinfoex* {.final.} = object + JOYINFOEX* {.final.} = object dwSize*: DWORD dwFlags*: DWORD - wXpos*: UINT - wYpos*: UINT - wZpos*: UINT + wXpos*: uint32 + wYpos*: uint32 + wZpos*: uint32 dwRpos*: DWORD dwUpos*: DWORD dwVpos*: DWORD - wButtons*: UINT + wButtons*: uint32 dwButtonNumber*: DWORD dwPOV*: DWORD dwReserved1*: DWORD dwReserved2*: DWORD - PJOYINFOEX* = ptr joyinfoex - NPJOYINFOEX* = ptr joyinfoex - LPJOYINFOEX* = ptr joyinfoex + PJOYINFOEX* = ptr JOYINFOEX + NPJOYINFOEX* = ptr JOYINFOEX + LPJOYINFOEX* = ptr JOYINFOEX TJOYINFOEX* = JOYINFOEX FOURCC* = DWORD HPSTR* = cstring HMMIO* = THandle - LPMMIOPROC* = proc (x1: LPSTR, x2: UINT, x3, x4: LPARAM): LRESULT{.stdcall.} + LPMMIOPROC* = proc (x1: LPSTR, x2: uint32, x3, x4: LPARAM): LRESULT{.stdcall.} TMMIOPROC* = LPMMIOPROC MMIOINFO* {.final.} = object dwFlags*: DWORD fccIOProc*: FOURCC pIOProc*: LPMMIOPROC - wErrorRet*: UINT + wErrorRet*: uint32 htask*: HTASK cchBuffer*: int32 pchBuffer*: HPSTR @@ -1663,7 +1663,7 @@ type pchEndWrite*: HPSTR lBufOffset*: int32 lDiskOffset*: int32 - adwInfo*: array[0..Pred(3), DWORD] + adwInfo*: array[0..pred(3), DWORD] dwReserved1*: DWORD dwReserved2*: DWORD hmmio*: HMMIO @@ -1686,8 +1686,8 @@ type LPCMMCKINFO* = ptr MMCKINFO TMMCKINFO* = MMCKINFO MCIERROR* = DWORD - MCIDEVICEID* = UINT - YIELDPROC* = proc (mciId: MCIDEVICEID, dwYieldData: DWORD): UINT{.stdcall.} + MCIDEVICEID* = uint32 + YIELDPROC* = proc (mciId: MCIDEVICEID, dwYieldData: DWORD): uint32{.stdcall.} TYIELDPROC* = YIELDPROC MCI_GENERIC_PARMS* {.final.} = object dwCallback*: DWORD @@ -1790,7 +1790,7 @@ type lpstrReturn*: cstring dwRetSize*: DWORD dwNumber*: DWORD - wDeviceType*: UINT + wDeviceType*: uint32 PMCI_SYSINFO_PARMSA* = ptr MCI_SYSINFO_PARMSA LPMCI_SYSINFO_PARMSA* = ptr MCI_SYSINFO_PARMSA @@ -1800,7 +1800,7 @@ type lpstrReturn*: LPWSTR dwRetSize*: DWORD dwNumber*: DWORD - wDeviceType*: UINT + wDeviceType*: uint32 PMCI_SYSINFO_PARMSW* = ptr MCI_SYSINFO_PARMSW LPMCI_SYSINFO_PARMSW* = ptr MCI_SYSINFO_PARMSW @@ -1984,8 +1984,8 @@ type dwCallback*: DWORD dwTimeFormat*: DWORD dwAudio*: DWORD - wInput*: UINT - wOutput*: UINT + wInput*: uint32 + wOutput*: uint32 wFormatTag*: int16 wReserved2*: int16 nChannels*: int16 @@ -2052,7 +2052,7 @@ type MCI_ANIM_WINDOW_PARMSW* {.final.} = object dwCallback*: DWORD hWnd*: HWND - nCmdShow*: UINT + nCmdShow*: uint32 lpstrText*: LPCWSTR PMCI_ANIM_WINDOW_PARMSW* = ptr MCI_ANIM_WINDOW_PARMSW @@ -2068,7 +2068,7 @@ type MCI_ANIM_WINDOW_PARMSA* {.final.} = object dwCallback*: DWORD hWnd*: HWND - nCmdShow*: UINT + nCmdShow*: uint32 lpstrText*: LPCSTR PMCI_ANIM_WINDOW_PARMSA* = ptr MCI_ANIM_WINDOW_PARMSA @@ -2149,7 +2149,7 @@ type MCI_OVLY_WINDOW_PARMSA* {.final.} = object dwCallback*: DWORD hWnd*: HWND - nCmdShow*: UINT + nCmdShow*: uint32 lpstrText*: LPCSTR PMCI_OVLY_WINDOW_PARMSA* = ptr MCI_OVLY_WINDOW_PARMSA @@ -2158,7 +2158,7 @@ type MCI_OVLY_WINDOW_PARMSW* {.final.} = object dwCallback*: DWORD hWnd*: HWND - nCmdShow*: UINT + nCmdShow*: uint32 lpstrText*: LPCWSTR PMCI_OVLY_WINDOW_PARMSW* = ptr MCI_OVLY_WINDOW_PARMSW @@ -2243,14 +2243,11 @@ else: type TMCI_OVLY_LOAD_PARMS* = MCI_OVLY_LOAD_PARMS -type - pcmwaveformat_tag* = PCMWAVEFORMAT - -proc mmioStringToFOURCCA*(x1: LPCSTR, x2: UINT): FOURCC{.stdcall, +proc mmioStringToFOURCCA*(x1: LPCSTR, x2: uint32): FOURCC{.stdcall, dynlib: "winmm.dll", importc: "mmioStringToFOURCCA".} -proc mmioStringToFOURCCW*(x1: LPCWSTR, x2: UINT): FOURCC{.stdcall, +proc mmioStringToFOURCCW*(x1: LPCWSTR, x2: uint32): FOURCC{.stdcall, dynlib: "winmm.dll", importc: "mmioStringToFOURCCW".} -proc mmioStringToFOURCC*(x1: cstring, x2: UINT): FOURCC{.stdcall, +proc mmioStringToFOURCC*(x1: cstring, x2: uint32): FOURCC{.stdcall, dynlib: "winmm.dll", importc: "mmioStringToFOURCCA".} proc mmioInstallIOProcA*(x1: FOURCC, x2: LPMMIOPROC, x3: DWORD): LPMMIOPROC{. stdcall, dynlib: "winmm.dll", importc: "mmioInstallIOProcA".} @@ -2270,7 +2267,7 @@ proc mmioRenameW*(x1: LPCWSTR, x2: LPCWSTR, x3: LPCMMIOINFO, x4: DWORD): MMRESUL stdcall, dynlib: "winmm.dll", importc: "mmioRenameW".} proc mmioRename*(x1: cstring, x2: cstring, x3: LPCMMIOINFO, x4: DWORD): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "mmioRenameA".} -proc mmioClose*(x1: HMMIO, x2: UINT): MMRESULT{.stdcall, dynlib: "winmm.dll", +proc mmioClose*(x1: HMMIO, x2: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "mmioClose".} proc mmioRead*(x1: HMMIO, x2: HPSTR, x3: LONG): LONG{.stdcall, dynlib: "winmm.dll", importc: "mmioRead".} @@ -2278,35 +2275,35 @@ proc mmioWrite*(x1: HMMIO, x2: cstring, x3: LONG): LONG{.stdcall, dynlib: "winmm.dll", importc: "mmioWrite".} proc mmioSeek*(x1: HMMIO, x2: LONG, x3: WINT): LONG{.stdcall, dynlib: "winmm.dll", importc: "mmioSeek".} -proc mmioGetInfo*(x1: HMMIO, x2: LPMMIOINFO, x3: UINT): MMRESULT{.stdcall, +proc mmioGetInfo*(x1: HMMIO, x2: LPMMIOINFO, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "mmioGetInfo".} -proc mmioSetInfo*(x1: HMMIO, x2: LPCMMIOINFO, x3: UINT): MMRESULT{.stdcall, +proc mmioSetInfo*(x1: HMMIO, x2: LPCMMIOINFO, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "mmioSetInfo".} -proc mmioSetBuffer*(x1: HMMIO, x2: LPSTR, x3: LONG, x4: UINT): MMRESULT{. +proc mmioSetBuffer*(x1: HMMIO, x2: LPSTR, x3: LONG, x4: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "mmioSetBuffer".} -proc mmioFlush*(x1: HMMIO, x2: UINT): MMRESULT{.stdcall, dynlib: "winmm.dll", +proc mmioFlush*(x1: HMMIO, x2: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "mmioFlush".} -proc mmioAdvance*(x1: HMMIO, x2: LPMMIOINFO, x3: UINT): MMRESULT{.stdcall, +proc mmioAdvance*(x1: HMMIO, x2: LPMMIOINFO, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "mmioAdvance".} -proc mmioSendMessage*(x1: HMMIO, x2: UINT, x3: LPARAM, x4: LPARAM): LRESULT{. +proc mmioSendMessage*(x1: HMMIO, x2: uint32, x3: LPARAM, x4: LPARAM): LRESULT{. stdcall, dynlib: "winmm.dll", importc: "mmioSendMessage".} -proc mmioDescend*(x1: HMMIO, x2: LPMMCKINFO, x3: PMMCKINFO, x4: UINT): MMRESULT{. +proc mmioDescend*(x1: HMMIO, x2: LPMMCKINFO, x3: PMMCKINFO, x4: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "mmioDescend".} -proc mmioAscend*(x1: HMMIO, x2: LPMMCKINFO, x3: UINT): MMRESULT{.stdcall, +proc mmioAscend*(x1: HMMIO, x2: LPMMCKINFO, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "mmioAscend".} -proc mmioCreateChunk*(x1: HMMIO, x2: LPMMCKINFO, x3: UINT): MMRESULT{.stdcall, +proc mmioCreateChunk*(x1: HMMIO, x2: LPMMCKINFO, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "mmioCreateChunk".} -proc mciSendCommandA*(x1: MCIDEVICEID, x2: UINT, x3: DWORD, x4: DWORD): MCIERROR{. +proc mciSendCommandA*(x1: MCIDEVICEID, x2: uint32, x3: DWORD, x4: DWORD): MCIERROR{. stdcall, dynlib: "winmm.dll", importc: "mciSendCommandA".} -proc mciSendCommandW*(x1: MCIDEVICEID, x2: UINT, x3: DWORD, x4: DWORD): MCIERROR{. +proc mciSendCommandW*(x1: MCIDEVICEID, x2: uint32, x3: DWORD, x4: DWORD): MCIERROR{. stdcall, dynlib: "winmm.dll", importc: "mciSendCommandW".} -proc mciSendCommand*(x1: MCIDEVICEID, x2: UINT, x3: DWORD, x4: DWORD): MCIERROR{. +proc mciSendCommand*(x1: MCIDEVICEID, x2: uint32, x3: DWORD, x4: DWORD): MCIERROR{. stdcall, dynlib: "winmm.dll", importc: "mciSendCommandA".} -proc mciSendStringA*(x1: LPCSTR, x2: LPSTR, x3: UINT, x4: HWND): MCIERROR{. +proc mciSendStringA*(x1: LPCSTR, x2: LPSTR, x3: uint32, x4: HWND): MCIERROR{. stdcall, dynlib: "winmm.dll", importc: "mciSendStringA".} -proc mciSendStringW*(x1: LPCWSTR, x2: LPWSTR, x3: UINT, x4: HWND): MCIERROR{. +proc mciSendStringW*(x1: LPCWSTR, x2: LPWSTR, x3: uint32, x4: HWND): MCIERROR{. stdcall, dynlib: "winmm.dll", importc: "mciSendStringW".} -proc mciSendString*(x1: cstring, x2: cstring, x3: UINT, x4: HWND): MCIERROR{. +proc mciSendString*(x1: cstring, x2: cstring, x3: uint32, x4: HWND): MCIERROR{. stdcall, dynlib: "winmm.dll", importc: "mciSendStringA".} proc mciGetDeviceIDA*(x1: LPCSTR): MCIDEVICEID{.stdcall, dynlib: "winmm.dll", importc: "mciGetDeviceIDA".} @@ -2320,60 +2317,60 @@ proc mciGetDeviceIDFromElementIDW*(x1: DWORD, x2: LPCWSTR): MCIDEVICEID{. stdcall, dynlib: "winmm.dll", importc: "mciGetDeviceIDFromElementIDW".} proc mciGetDeviceIDFromElementID*(x1: DWORD, x2: cstring): MCIDEVICEID{.stdcall, dynlib: "winmm.dll", importc: "mciGetDeviceIDFromElementIDA".} -proc mciGetErrorStringA*(x1: MCIERROR, x2: LPSTR, x3: UINT): BOOL{.stdcall, +proc mciGetErrorStringA*(x1: MCIERROR, x2: LPSTR, x3: uint32): bool{.stdcall, dynlib: "winmm.dll", importc: "mciGetErrorStringA".} -proc mciGetErrorStringW*(x1: MCIERROR, x2: LPWSTR, x3: UINT): BOOL{.stdcall, +proc mciGetErrorStringW*(x1: MCIERROR, x2: LPWSTR, x3: uint32): bool{.stdcall, dynlib: "winmm.dll", importc: "mciGetErrorStringW".} -proc mciGetErrorString*(x1: MCIERROR, x2: cstring, x3: UINT): BOOL{.stdcall, +proc mciGetErrorString*(x1: MCIERROR, x2: cstring, x3: uint32): bool{.stdcall, dynlib: "winmm.dll", importc: "mciGetErrorStringA".} -proc mciSetYieldProc*(x1: MCIDEVICEID, x2: YIELDPROC, x3: DWORD): BOOL{.stdcall, +proc mciSetYieldProc*(x1: MCIDEVICEID, x2: YIELDPROC, x3: DWORD): bool{.stdcall, dynlib: "winmm.dll", importc: "mciSetYieldProc".} proc mciGetCreatorTask*(x1: MCIDEVICEID): HTASK{.stdcall, dynlib: "winmm.dll", importc: "mciGetCreatorTask".} proc mciGetYieldProc*(x1: MCIDEVICEID, x2: LPDWORD): YIELDPROC{.stdcall, dynlib: "winmm.dll", importc: "mciGetYieldProc".} -proc mciExecute*(x1: LPCSTR): BOOL{.stdcall, dynlib: "winmm.dll", +proc mciExecute*(x1: LPCSTR): bool{.stdcall, dynlib: "winmm.dll", importc: "mciExecute".} -proc joyGetPos*(x1: UINT, x2: LPJOYINFO): MMRESULT{.stdcall, +proc joyGetPos*(x1: uint32, x2: LPJOYINFO): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "joyGetPos".} -proc joyGetPosEx*(x1: UINT, x2: LPJOYINFOEX): MMRESULT{.stdcall, +proc joyGetPosEx*(x1: uint32, x2: LPJOYINFOEX): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "joyGetPosEx".} -proc joyGetThreshold*(x1: UINT, x2: LPUINT): MMRESULT{.stdcall, +proc joyGetThreshold*(x1: uint32, x2: LPUINT): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "joyGetThreshold".} -proc joyReleaseCapture*(x1: UINT): MMRESULT{.stdcall, dynlib: "winmm.dll", +proc joyReleaseCapture*(x1: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "joyReleaseCapture".} -proc joySetCapture*(x1: HWND, x2: UINT, x3: UINT, x4: BOOL): MMRESULT{.stdcall, +proc joySetCapture*(x1: HWND, x2: uint32, x3: uint32, x4: bool): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "joySetCapture".} -proc joySetThreshold*(x1: UINT, x2: UINT): MMRESULT{.stdcall, +proc joySetThreshold*(x1: uint32, x2: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "joySetThreshold".} -proc waveOutGetNumDevs*(): UINT{.stdcall, dynlib: "winmm.dll", +proc waveOutGetNumDevs*(): uint32{.stdcall, dynlib: "winmm.dll", importc: "waveOutGetNumDevs".} -proc waveOutGetDevCapsA*(x1: UINT, x2: LPWAVEOUTCAPSA, x3: UINT): MMRESULT{. +proc waveOutGetDevCapsA*(x1: uint32, x2: LPWAVEOUTCAPSA, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "waveOutGetDevCapsA".} -proc waveOutGetDevCapsW*(x1: UINT, x2: LPWAVEOUTCAPSW, x3: UINT): MMRESULT{. +proc waveOutGetDevCapsW*(x1: uint32, x2: LPWAVEOUTCAPSW, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "waveOutGetDevCapsW".} -proc waveOutGetDevCaps*(x1: UINT, x2: LPWAVEOUTCAPS, x3: UINT): MMRESULT{. +proc waveOutGetDevCaps*(x1: uint32, x2: LPWAVEOUTCAPS, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "waveOutGetDevCapsA".} proc waveOutGetVolume*(x1: HWAVEOUT, x2: LPDWORD): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveOutGetVolume".} proc waveOutSetVolume*(x1: HWAVEOUT, x2: DWORD): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveOutSetVolume".} -proc waveOutGetErrorTextA*(x1: MMRESULT, x2: LPSTR, x3: UINT): MMRESULT{. +proc waveOutGetErrorTextA*(x1: MMRESULT, x2: LPSTR, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "waveOutGetErrorTextA".} -proc waveOutGetErrorTextW*(x1: MMRESULT, x2: LPWSTR, x3: UINT): MMRESULT{. +proc waveOutGetErrorTextW*(x1: MMRESULT, x2: LPWSTR, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "waveOutGetErrorTextW".} -proc waveOutGetErrorText*(x1: MMRESULT, x2: cstring, x3: UINT): MMRESULT{. +proc waveOutGetErrorText*(x1: MMRESULT, x2: cstring, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "waveOutGetErrorTextA".} -proc waveOutOpen*(x1: LPHWAVEOUT, x2: UINT, x3: LPCWAVEFORMATEX, x4: DWORD, +proc waveOutOpen*(x1: LPHWAVEOUT, x2: uint32, x3: LPCWAVEFORMATEX, x4: DWORD, x5: DWORD, x6: DWORD): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveOutOpen".} proc waveOutClose*(x1: HWAVEOUT): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveOutClose".} -proc waveOutPrepareHeader*(x1: HWAVEOUT, x2: LPWAVEHDR, x3: UINT): MMRESULT{. +proc waveOutPrepareHeader*(x1: HWAVEOUT, x2: LPWAVEHDR, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "waveOutPrepareHeader".} -proc waveOutUnprepareHeader*(x1: HWAVEOUT, x2: LPWAVEHDR, x3: UINT): MMRESULT{. +proc waveOutUnprepareHeader*(x1: HWAVEOUT, x2: LPWAVEHDR, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "waveOutUnprepareHeader".} -proc waveOutWrite*(x1: HWAVEOUT, x2: LPWAVEHDR, x3: UINT): MMRESULT{.stdcall, +proc waveOutWrite*(x1: HWAVEOUT, x2: LPWAVEHDR, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveOutWrite".} proc waveOutPause*(x1: HWAVEOUT): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveOutPause".} @@ -2383,7 +2380,7 @@ proc waveOutReset*(x1: HWAVEOUT): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveOutReset".} proc waveOutBreakLoop*(x1: HWAVEOUT): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveOutBreakLoop".} -proc waveOutGetPosition*(x1: HWAVEOUT, x2: LPMMTIME, x3: UINT): MMRESULT{. +proc waveOutGetPosition*(x1: HWAVEOUT, x2: LPMMTIME, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "waveOutGetPosition".} proc waveOutGetPitch*(x1: HWAVEOUT, x2: LPDWORD): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveOutGetPitch".} @@ -2395,32 +2392,32 @@ proc waveOutSetPlaybackRate*(x1: HWAVEOUT, x2: DWORD): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveOutSetPlaybackRate".} proc waveOutGetID*(x1: HWAVEOUT, x2: LPUINT): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveOutGetID".} -proc waveOutMessage*(x1: HWAVEOUT, x2: UINT, x3: DWORD, x4: DWORD): MMRESULT{. +proc waveOutMessage*(x1: HWAVEOUT, x2: uint32, x3: DWORD, x4: DWORD): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "waveOutMessage".} -proc waveInGetNumDevs*(): UINT{.stdcall, dynlib: "winmm.dll", +proc waveInGetNumDevs*(): uint32{.stdcall, dynlib: "winmm.dll", importc: "waveInGetNumDevs".} -proc waveInGetDevCapsA*(x1: UINT, x2: LPWAVEINCAPSA, x3: UINT): MMRESULT{. +proc waveInGetDevCapsA*(x1: uint32, x2: LPWAVEINCAPSA, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "waveInGetDevCapsA".} -proc waveInGetDevCapsW*(x1: UINT, x2: LPWAVEINCAPSW, x3: UINT): MMRESULT{. +proc waveInGetDevCapsW*(x1: uint32, x2: LPWAVEINCAPSW, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "waveInGetDevCapsW".} -proc waveInGetDevCaps*(x1: UINT, x2: LPWAVEINCAPS, x3: UINT): MMRESULT{.stdcall, +proc waveInGetDevCaps*(x1: uint32, x2: LPWAVEINCAPS, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveInGetDevCapsA".} -proc waveInGetErrorTextA*(x1: MMRESULT, x2: LPSTR, x3: UINT): MMRESULT{.stdcall, +proc waveInGetErrorTextA*(x1: MMRESULT, x2: LPSTR, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveInGetErrorTextA".} -proc waveInGetErrorTextW*(x1: MMRESULT, x2: LPWSTR, x3: UINT): MMRESULT{. +proc waveInGetErrorTextW*(x1: MMRESULT, x2: LPWSTR, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "waveInGetErrorTextW".} -proc waveInGetErrorText*(x1: MMRESULT, x2: cstring, x3: UINT): MMRESULT{. +proc waveInGetErrorText*(x1: MMRESULT, x2: cstring, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "waveInGetErrorTextA".} -proc waveInOpen*(x1: LPHWAVEIN, x2: UINT, x3: LPCWAVEFORMATEX, x4: DWORD, +proc waveInOpen*(x1: LPHWAVEIN, x2: uint32, x3: LPCWAVEFORMATEX, x4: DWORD, x5: DWORD, x6: DWORD): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveInOpen".} proc waveInClose*(x1: HWAVEIN): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveInClose".} -proc waveInPrepareHeader*(x1: HWAVEIN, x2: LPWAVEHDR, x3: UINT): MMRESULT{. +proc waveInPrepareHeader*(x1: HWAVEIN, x2: LPWAVEHDR, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "waveInPrepareHeader".} -proc waveInUnprepareHeader*(x1: HWAVEIN, x2: LPWAVEHDR, x3: UINT): MMRESULT{. +proc waveInUnprepareHeader*(x1: HWAVEIN, x2: LPWAVEHDR, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "waveInUnprepareHeader".} -proc waveInAddBuffer*(x1: HWAVEIN, x2: LPWAVEHDR, x3: UINT): MMRESULT{.stdcall, +proc waveInAddBuffer*(x1: HWAVEIN, x2: LPWAVEHDR, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveInAddBuffer".} proc waveInStart*(x1: HWAVEIN): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveInStart".} @@ -2428,11 +2425,11 @@ proc waveInStop*(x1: HWAVEIN): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveInStop".} proc waveInReset*(x1: HWAVEIN): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveInReset".} -proc waveInGetPosition*(x1: HWAVEIN, x2: LPMMTIME, x3: UINT): MMRESULT{.stdcall, +proc waveInGetPosition*(x1: HWAVEIN, x2: LPMMTIME, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveInGetPosition".} proc waveInGetID*(x1: HWAVEIN, x2: LPUINT): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "waveInGetID".} -proc waveInMessage*(x1: HWAVEIN, x2: UINT, x3: DWORD, x4: DWORD): MMRESULT{. +proc waveInMessage*(x1: HWAVEIN, x2: uint32, x3: DWORD, x4: DWORD): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "waveInMessage".} proc mixerGetLineControlsA*(x1: HMIXEROBJ, x2: LPMIXERLINECONTROLSA, x3: DWORD): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "mixerGetLineControlsA".} @@ -2440,13 +2437,13 @@ proc mixerGetLineControlsW*(x1: HMIXEROBJ, x2: LPMIXERLINECONTROLSW, x3: DWORD): stdcall, dynlib: "winmm.dll", importc: "mixerGetLineControlsW".} proc mixerGetLineControls*(x1: HMIXEROBJ, x2: LPMIXERLINECONTROLS, x3: DWORD): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "mixerGetLineControlsA".} -proc joyGetNumDevs*(): UINT{.stdcall, dynlib: "winmm.dll", +proc joyGetNumDevs*(): uint32{.stdcall, dynlib: "winmm.dll", importc: "joyGetNumDevs".} -proc joyGetDevCapsA*(x1: UINT, x2: LPJOYCAPSA, x3: UINT): MMRESULT{.stdcall, +proc joyGetDevCapsA*(x1: uint32, x2: LPJOYCAPSA, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "joyGetDevCapsA".} -proc joyGetDevCapsW*(x1: UINT, x2: LPJOYCAPSW, x3: UINT): MMRESULT{.stdcall, +proc joyGetDevCapsW*(x1: uint32, x2: LPJOYCAPSW, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "joyGetDevCapsW".} -proc joyGetDevCaps*(x1: UINT, x2: LPJOYCAPS, x3: UINT): MMRESULT{.stdcall, +proc joyGetDevCaps*(x1: uint32, x2: LPJOYCAPS, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "joyGetDevCapsA".} proc mixerGetControlDetailsA*(x1: HMIXEROBJ, x2: LPMIXERCONTROLDETAILS, x3: DWORD): MMRESULT{.stdcall, @@ -2456,46 +2453,46 @@ proc mixerGetControlDetailsW*(x1: HMIXEROBJ, x2: LPMIXERCONTROLDETAILS, dynlib: "winmm.dll", importc: "mixerGetControlDetailsW".} proc mixerGetControlDetails*(x1: HMIXEROBJ, x2: LPMIXERCONTROLDETAILS, x3: DWORD): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "mixerGetControlDetailsA".} -proc timeGetSystemTime*(x1: LPMMTIME, x2: UINT): MMRESULT{.stdcall, +proc timeGetSystemTime*(x1: LPMMTIME, x2: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "timeGetSystemTime".} proc timeGetTime*(): DWORD{.stdcall, dynlib: "winmm.dll", importc: "timeGetTime".} -proc timeSetEvent*(x1: UINT, x2: UINT, x3: LPTIMECALLBACK, x4: DWORD, x5: UINT): MMRESULT{. +proc timeSetEvent*(x1: uint32, x2: uint32, x3: LPTIMECALLBACK, x4: DWORD, x5: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "timeSetEvent".} -proc timeKillEvent*(x1: UINT): MMRESULT{.stdcall, dynlib: "winmm.dll", +proc timeKillEvent*(x1: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "timeKillEvent".} -proc timeGetDevCaps*(x1: LPTIMECAPS, x2: UINT): MMRESULT{.stdcall, +proc timeGetDevCaps*(x1: LPTIMECAPS, x2: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "timeGetDevCaps".} -proc timeBeginPeriod*(x1: UINT): MMRESULT{.stdcall, dynlib: "winmm.dll", +proc timeBeginPeriod*(x1: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "timeBeginPeriod".} -proc timeEndPeriod*(x1: UINT): MMRESULT{.stdcall, dynlib: "winmm.dll", +proc timeEndPeriod*(x1: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "timeEndPeriod".} -proc mixerGetDevCapsA*(x1: UINT, x2: LPMIXERCAPSA, x3: UINT): MMRESULT{.stdcall, +proc mixerGetDevCapsA*(x1: uint32, x2: LPMIXERCAPSA, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "mixerGetDevCapsA".} -proc mixerGetDevCapsW*(x1: UINT, x2: LPMIXERCAPSW, x3: UINT): MMRESULT{.stdcall, +proc mixerGetDevCapsW*(x1: uint32, x2: LPMIXERCAPSW, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "mixerGetDevCapsW".} -proc mixerGetDevCaps*(x1: UINT, x2: LPMIXERCAPS, x3: UINT): MMRESULT{.stdcall, +proc mixerGetDevCaps*(x1: uint32, x2: LPMIXERCAPS, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "mixerGetDevCapsA".} -proc mixerOpen*(x1: LPHMIXER, x2: UINT, x3: DWORD, x4: DWORD, x5: DWORD): MMRESULT{. +proc mixerOpen*(x1: LPHMIXER, x2: uint32, x3: DWORD, x4: DWORD, x5: DWORD): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "mixerOpen".} proc mixerClose*(x1: HMIXER): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "mixerClose".} -proc mixerMessage*(x1: HMIXER, x2: UINT, x3: DWORD, x4: DWORD): DWORD{.stdcall, +proc mixerMessage*(x1: HMIXER, x2: uint32, x3: DWORD, x4: DWORD): DWORD{.stdcall, dynlib: "winmm.dll", importc: "mixerMessage".} -proc auxGetNumDevs*(): UINT{.stdcall, dynlib: "winmm.dll", +proc auxGetNumDevs*(): uint32{.stdcall, dynlib: "winmm.dll", importc: "auxGetNumDevs".} -proc auxGetDevCapsA*(x1: UINT, x2: LPAUXCAPSA, x3: UINT): MMRESULT{.stdcall, +proc auxGetDevCapsA*(x1: uint32, x2: LPAUXCAPSA, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "auxGetDevCapsA".} -proc auxGetDevCapsW*(x1: UINT, x2: LPAUXCAPSW, x3: UINT): MMRESULT{.stdcall, +proc auxGetDevCapsW*(x1: uint32, x2: LPAUXCAPSW, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "auxGetDevCapsW".} -proc auxGetDevCaps*(x1: UINT, x2: LPAUXCAPS, x3: UINT): MMRESULT{.stdcall, +proc auxGetDevCaps*(x1: uint32, x2: LPAUXCAPS, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "auxGetDevCapsA".} -proc auxSetVolume*(x1: UINT, x2: DWORD): MMRESULT{.stdcall, dynlib: "winmm.dll", +proc auxSetVolume*(x1: uint32, x2: DWORD): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "auxSetVolume".} -proc auxGetVolume*(x1: UINT, x2: LPDWORD): MMRESULT{.stdcall, +proc auxGetVolume*(x1: uint32, x2: LPDWORD): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "auxGetVolume".} -proc auxOutMessage*(x1: UINT, x2: UINT, x3: DWORD, x4: DWORD): MMRESULT{. +proc auxOutMessage*(x1: uint32, x2: uint32, x3: DWORD, x4: DWORD): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "auxOutMessage".} -proc midiOutGetNumDevs*(): UINT{.stdcall, dynlib: "winmm.dll", +proc midiOutGetNumDevs*(): uint32{.stdcall, dynlib: "winmm.dll", importc: "midiOutGetNumDevs".} proc midiStreamOpen*(x1: LPHMIDISTRM, x2: LPUINT, x3: DWORD, x4: DWORD, x5: DWORD, x6: DWORD): MMRESULT{.stdcall, @@ -2504,9 +2501,9 @@ proc midiStreamClose*(x1: HMIDISTRM): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiStreamClose".} proc midiStreamProperty*(x1: HMIDISTRM, x2: LPBYTE, x3: DWORD): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiStreamProperty".} -proc midiStreamPosition*(x1: HMIDISTRM, x2: LPMMTIME, x3: UINT): MMRESULT{. +proc midiStreamPosition*(x1: HMIDISTRM, x2: LPMMTIME, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiStreamPosition".} -proc midiStreamOut*(x1: HMIDISTRM, x2: LPMIDIHDR, x3: UINT): MMRESULT{.stdcall, +proc midiStreamOut*(x1: HMIDISTRM, x2: LPMIDIHDR, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiStreamOut".} proc midiStreamPause*(x1: HMIDISTRM): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiStreamPause".} @@ -2518,67 +2515,67 @@ proc midiConnect*(x1: HMIDI, x2: HMIDIOUT, x3: pointer): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiConnect".} proc midiDisconnect*(x1: HMIDI, x2: HMIDIOUT, x3: pointer): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiDisconnect".} -proc midiOutGetDevCapsA*(x1: UINT, x2: LPMIDIOUTCAPSA, x3: UINT): MMRESULT{. +proc midiOutGetDevCapsA*(x1: uint32, x2: LPMIDIOUTCAPSA, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiOutGetDevCapsA".} -proc midiOutGetDevCapsW*(x1: UINT, x2: LPMIDIOUTCAPSW, x3: UINT): MMRESULT{. +proc midiOutGetDevCapsW*(x1: uint32, x2: LPMIDIOUTCAPSW, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiOutGetDevCapsW".} -proc midiOutGetDevCaps*(x1: UINT, x2: LPMIDIOUTCAPS, x3: UINT): MMRESULT{. +proc midiOutGetDevCaps*(x1: uint32, x2: LPMIDIOUTCAPS, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiOutGetDevCapsA".} proc midiOutGetVolume*(x1: HMIDIOUT, x2: LPDWORD): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiOutGetVolume".} proc midiOutSetVolume*(x1: HMIDIOUT, x2: DWORD): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiOutSetVolume".} -proc midiOutGetErrorTextA*(x1: MMRESULT, x2: LPSTR, x3: UINT): MMRESULT{. +proc midiOutGetErrorTextA*(x1: MMRESULT, x2: LPSTR, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiOutGetErrorTextA".} -proc midiOutGetErrorTextW*(x1: MMRESULT, x2: LPWSTR, x3: UINT): MMRESULT{. +proc midiOutGetErrorTextW*(x1: MMRESULT, x2: LPWSTR, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiOutGetErrorTextW".} -proc midiOutGetErrorText*(x1: MMRESULT, x2: cstring, x3: UINT): MMRESULT{. +proc midiOutGetErrorText*(x1: MMRESULT, x2: cstring, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiOutGetErrorTextA".} -proc midiOutOpen*(x1: LPHMIDIOUT, x2: UINT, x3: DWORD, x4: DWORD, x5: DWORD): MMRESULT{. +proc midiOutOpen*(x1: LPHMIDIOUT, x2: uint32, x3: DWORD, x4: DWORD, x5: DWORD): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiOutOpen".} proc midiOutClose*(x1: HMIDIOUT): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiOutClose".} -proc midiOutPrepareHeader*(x1: HMIDIOUT, x2: LPMIDIHDR, x3: UINT): MMRESULT{. +proc midiOutPrepareHeader*(x1: HMIDIOUT, x2: LPMIDIHDR, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiOutPrepareHeader".} -proc midiOutUnprepareHeader*(x1: HMIDIOUT, x2: LPMIDIHDR, x3: UINT): MMRESULT{. +proc midiOutUnprepareHeader*(x1: HMIDIOUT, x2: LPMIDIHDR, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiOutUnprepareHeader".} proc midiOutShortMsg*(x1: HMIDIOUT, x2: DWORD): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiOutShortMsg".} -proc midiOutLongMsg*(x1: HMIDIOUT, x2: LPMIDIHDR, x3: UINT): MMRESULT{.stdcall, +proc midiOutLongMsg*(x1: HMIDIOUT, x2: LPMIDIHDR, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiOutLongMsg".} proc midiOutReset*(x1: HMIDIOUT): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiOutReset".} -proc midiOutCachePatches*(x1: HMIDIOUT, x2: UINT, x3: LPWORD, x4: UINT): MMRESULT{. +proc midiOutCachePatches*(x1: HMIDIOUT, x2: uint32, x3: LPWORD, x4: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiOutCachePatches".} -proc midiOutCacheDrumPatches*(x1: HMIDIOUT, x2: UINT, x3: LPWORD, x4: UINT): MMRESULT{. +proc midiOutCacheDrumPatches*(x1: HMIDIOUT, x2: uint32, x3: LPWORD, x4: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiOutCacheDrumPatches".} proc midiOutGetID*(x1: HMIDIOUT, x2: LPUINT): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiOutGetID".} -proc midiOutMessage*(x1: HMIDIOUT, x2: UINT, x3: DWORD, x4: DWORD): MMRESULT{. +proc midiOutMessage*(x1: HMIDIOUT, x2: uint32, x3: DWORD, x4: DWORD): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiOutMessage".} -proc midiInGetNumDevs*(): UINT{.stdcall, dynlib: "winmm.dll", +proc midiInGetNumDevs*(): uint32{.stdcall, dynlib: "winmm.dll", importc: "midiInGetNumDevs".} -proc midiInGetDevCapsA*(x1: UINT, x2: LPMIDIINCAPSA, x3: UINT): MMRESULT{. +proc midiInGetDevCapsA*(x1: uint32, x2: LPMIDIINCAPSA, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiInGetDevCapsA".} -proc midiInGetDevCapsW*(x1: UINT, x2: LPMIDIINCAPSW, x3: UINT): MMRESULT{. +proc midiInGetDevCapsW*(x1: uint32, x2: LPMIDIINCAPSW, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiInGetDevCapsW".} -proc midiInGetDevCaps*(x1: UINT, x2: LPMIDIINCAPS, x3: UINT): MMRESULT{.stdcall, +proc midiInGetDevCaps*(x1: uint32, x2: LPMIDIINCAPS, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiInGetDevCapsA".} -proc midiInGetErrorTextA*(x1: MMRESULT, x2: LPSTR, x3: UINT): MMRESULT{.stdcall, +proc midiInGetErrorTextA*(x1: MMRESULT, x2: LPSTR, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiInGetErrorTextA".} -proc midiInGetErrorTextW*(x1: MMRESULT, x2: LPWSTR, x3: UINT): MMRESULT{. +proc midiInGetErrorTextW*(x1: MMRESULT, x2: LPWSTR, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiInGetErrorTextW".} -proc midiInGetErrorText*(x1: MMRESULT, x2: cstring, x3: UINT): MMRESULT{. +proc midiInGetErrorText*(x1: MMRESULT, x2: cstring, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiInGetErrorTextA".} -proc midiInOpen*(x1: LPHMIDIIN, x2: UINT, x3: DWORD, x4: DWORD, x5: DWORD): MMRESULT{. +proc midiInOpen*(x1: LPHMIDIIN, x2: uint32, x3: DWORD, x4: DWORD, x5: DWORD): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiInOpen".} proc midiInClose*(x1: HMIDIIN): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiInClose".} -proc midiInPrepareHeader*(x1: HMIDIIN, x2: LPMIDIHDR, x3: UINT): MMRESULT{. +proc midiInPrepareHeader*(x1: HMIDIIN, x2: LPMIDIHDR, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiInPrepareHeader".} -proc midiInUnprepareHeader*(x1: HMIDIIN, x2: LPMIDIHDR, x3: UINT): MMRESULT{. +proc midiInUnprepareHeader*(x1: HMIDIIN, x2: LPMIDIHDR, x3: uint32): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiInUnprepareHeader".} -proc midiInAddBuffer*(x1: HMIDIIN, x2: LPMIDIHDR, x3: UINT): MMRESULT{.stdcall, +proc midiInAddBuffer*(x1: HMIDIIN, x2: LPMIDIHDR, x3: uint32): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiInAddBuffer".} proc midiInStart*(x1: HMIDIIN): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiInStart".} @@ -2588,7 +2585,7 @@ proc midiInReset*(x1: HMIDIIN): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiInReset".} proc midiInGetID*(x1: HMIDIIN, x2: LPUINT): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "midiInGetID".} -proc midiInMessage*(x1: HMIDIIN, x2: UINT, x3: DWORD, x4: DWORD): MMRESULT{. +proc midiInMessage*(x1: HMIDIIN, x2: uint32, x3: DWORD, x4: DWORD): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "midiInMessage".} proc mixerGetLineInfoA*(x1: HMIXEROBJ, x2: LPMIXERLINEA, x3: DWORD): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "mixerGetLineInfoA".} @@ -2596,13 +2593,13 @@ proc mixerGetLineInfoW*(x1: HMIXEROBJ, x2: LPMIXERLINEW, x3: DWORD): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "mixerGetLineInfoW".} proc mixerGetLineInfo*(x1: HMIXEROBJ, x2: LPMIXERLINE, x3: DWORD): MMRESULT{. stdcall, dynlib: "winmm.dll", importc: "mixerGetLineInfoA".} -proc mixerGetID*(x1: HMIXEROBJ, x2: var UINT, x3: DWORD): MMRESULT{.stdcall, +proc mixerGetID*(x1: HMIXEROBJ, x2: var uint32, x3: DWORD): MMRESULT{.stdcall, dynlib: "winmm.dll", importc: "mixerGetID".} -proc PlaySoundA*(x1: LPCSTR, x2: HMODULE, x3: DWORD): BOOL{.stdcall, +proc PlaySoundA*(x1: LPCSTR, x2: HMODULE, x3: DWORD): bool{.stdcall, dynlib: "winmm.dll", importc: "PlaySoundA".} -proc PlaySoundW*(x1: LPCWSTR, x2: HMODULE, x3: DWORD): BOOL{.stdcall, +proc PlaySoundW*(x1: LPCWSTR, x2: HMODULE, x3: DWORD): bool{.stdcall, dynlib: "winmm.dll", importc: "PlaySoundW".} -proc PlaySound*(x1: cstring, x2: HMODULE, x3: DWORD): BOOL{.stdcall, +proc PlaySound*(x1: cstring, x2: HMODULE, x3: DWORD): bool{.stdcall, dynlib: "winmm.dll", importc: "PlaySoundA".} # implementation @@ -2610,7 +2607,7 @@ proc MEVT_EVENTTYPE(x: int8): int8 = result = toU8(x shr 24) proc MEVT_EVENTPARM(x: DWORD): DWORD = - result = x And 0x00FFFFFF + result = x and 0x00FFFFFF proc MCI_MSF_MINUTE(msf: int32): int8 = result = toU8(msf and 0xff) @@ -2648,8 +2645,8 @@ proc MCI_MAKE_HMS(h, m, s: int8): int32 = proc MCI_TMSF_FRAME(tmsf: int32): int8 = result = toU8(tmsf shr 24) -proc mci_Make_TMSF(t, m, s, f: int8): int32 = +proc MCI_MAKE_TMSF(t, m, s, f: int8): int32 = result = (ze(t) or ze(m) shl 8 or ze(s) shl 16 or ze(f) shl 24).int32 proc DIBINDEX(n: int32): int32 = - result = n Or 0x000010FF'i32 shl 16'i32 + result = n or 0x000010FF'i32 shl 16'i32 diff --git a/lib/windows/nb30.nim b/lib/windows/nb30.nim index a7fd526aa..2e0c679ae 100644 --- a/lib/windows/nb30.nim +++ b/lib/windows/nb30.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2006 Andreas Rumpf # # See the file "copying.txt", included in this @@ -13,7 +13,7 @@ {.deadCodeElim: on.} import # Data structure templates - Windows + windows const NCBNAMSZ* = 16 # absolute length of a net name @@ -24,20 +24,20 @@ type # Network Control Block TNCBPostProc* = proc (P: PNCB) {.stdcall.} TNCB* {.final.} = object # Structure returned to the NCB command NCBASTAT is ADAPTER_STATUS followed # by an array of NAME_BUFFER structures. - ncb_command*: Char # command code - ncb_retcode*: Char # return code - ncb_lsn*: Char # local session number - ncb_num*: Char # number of our network name + ncb_command*: char # command code + ncb_retcode*: char # return code + ncb_lsn*: char # local session number + ncb_num*: char # number of our network name ncb_buffer*: cstring # address of message buffer ncb_length*: int16 # size of message buffer ncb_callname*: array[0..NCBNAMSZ - 1, char] # blank-padded name of remote ncb_name*: array[0..NCBNAMSZ - 1, char] # our blank-padded netname - ncb_rto*: Char # rcv timeout/retry count - ncb_sto*: Char # send timeout/sys timeout + ncb_rto*: char # rcv timeout/retry count + ncb_sto*: char # send timeout/sys timeout ncb_post*: TNCBPostProc # POST routine address - ncb_lana_num*: Char # lana (adapter) number - ncb_cmd_cplt*: Char # 0xff => commmand pending - ncb_reserve*: array[0..9, Char] # reserved, used by BIOS + ncb_lana_num*: char # lana (adapter) number + ncb_cmd_cplt*: char # 0xff => commmand pending + ncb_reserve*: array[0..9, char] # reserved, used by BIOS ncb_event*: THandle # HANDLE to Win32 event which # will be set to the signalled # state when an ASYNCH command @@ -45,11 +45,11 @@ type # Network Control Block PAdapterStatus* = ptr TAdapterStatus TAdapterStatus* {.final.} = object - adapter_address*: array[0..5, Char] - rev_major*: Char - reserved0*: Char - adapter_type*: Char - rev_minor*: Char + adapter_address*: array[0..5, char] + rev_major*: char + reserved0*: char + adapter_type*: char + rev_minor*: char duration*: int16 frmr_recv*: int16 frmr_xmit*: int16 @@ -75,9 +75,9 @@ type # Network Control Block PNameBuffer* = ptr TNameBuffer TNameBuffer* {.final.} = object - name*: array[0..NCBNAMSZ - 1, Char] - name_num*: Char - name_flags*: Char + name*: array[0..NCBNAMSZ - 1, char] + name_num*: char + name_flags*: char const # values for name_flags bits. @@ -96,19 +96,19 @@ type # Structure returned to the NCB command NCBSSTAT is SESSION_HEADER followed # status for all names. PSessionHeader* = ptr TSessionHeader TSessionHeader* {.final.} = object - sess_name*: Char - num_sess*: Char - rcv_dg_outstanding*: Char - rcv_any_outstanding*: Char + sess_name*: char + num_sess*: char + rcv_dg_outstanding*: char + rcv_any_outstanding*: char PSessionBuffer* = ptr TSessionBuffer TSessionBuffer* {.final.} = object - lsn*: Char - state*: Char - local_name*: array[0..NCBNAMSZ - 1, Char] - remote_name*: array[0..NCBNAMSZ - 1, Char] - rcvs_outstanding*: Char - sends_outstanding*: Char + lsn*: char + state*: char + local_name*: array[0..NCBNAMSZ - 1, char] + remote_name*: array[0..NCBNAMSZ - 1, char] + rcvs_outstanding*: char + sends_outstanding*: char const # Values for state @@ -125,24 +125,24 @@ type # Structure returned to the NCB command NCBENUM. PLanaEnum* = ptr TLanaEnum TLanaEnum* {.final.} = object # Structure returned to the NCB command NCBFINDNAME is FIND_NAME_HEADER followed # by an array of FIND_NAME_BUFFER structures. - len*: Char # Number of valid entries in lana[] - lana*: array[0..MAX_LANA, Char] + len*: char # Number of valid entries in lana[] + lana*: array[0..MAX_LANA, char] PFindNameHeader* = ptr TFindNameHeader TFindNameHeader* {.final.} = object node_count*: int16 - reserved*: Char - unique_group*: Char + reserved*: char + unique_group*: char PFindNameBuffer* = ptr TFindNameBuffer TFindNameBuffer* {.final.} = object # Structure provided with NCBACTION. The purpose of NCBACTION is to provide # transport specific extensions to netbios. - len*: Char - access_control*: Char - frame_control*: Char - destination_addr*: array[0..5, Char] - source_addr*: array[0..5, Char] - routing_info*: array[0..17, Char] + len*: char + access_control*: char + frame_control*: char + destination_addr*: array[0..5, char] + source_addr*: array[0..5, char] + routing_info*: array[0..17, char] PActionHeader* = ptr TActionHeader TActionHeader* {.final.} = object @@ -227,6 +227,6 @@ const # NCB Command codes # main user entry point for NetBIOS 3.0 # Usage: Result = Netbios( pncb ); -proc Netbios*(P: PNCB): Char{.stdcall, dynlib: "netapi32.dll", +proc Netbios*(P: PNCB): char{.stdcall, dynlib: "netapi32.dll", importc: "Netbios".} # implementation diff --git a/lib/windows/psapi.nim b/lib/windows/psapi.nim index 7d53cf7ca..fd1dcada8 100644 --- a/lib/windows/psapi.nim +++ b/lib/windows/psapi.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2009 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/lib/windows/shellapi.nim b/lib/windows/shellapi.nim index 41f2a60d5..079257680 100644 --- a/lib/windows/shellapi.nim +++ b/lib/windows/shellapi.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2006 Andreas Rumpf # # See the file "copying.txt", included in this @@ -28,14 +28,14 @@ # Copyright (c) Microsoft Corporation. All rights reserved. import - Windows + windows type HDROP* = THandle - UINT_PTR* = ptr UINT + UINT_PTR* = ptr uint32 DWORD_PTR* = ptr DWORD - pHICON* = ptr HICON - pBool* = ptr BOOL + PHICON* = ptr HICON + PBool* = ptr bool STARTUPINFOW* {.final.} = object # a guess. Omission should get fixed in Windows. cb*: DWORD lpReserved*: LPTSTR @@ -60,27 +60,27 @@ type TSTARTUPINFOW* = STARTUPINFOW PSTARTUPINFOW* = ptr STARTUPINFOW #unicode -proc DragQueryFileA*(arg1: HDROP, arg2: UINT, arg3: LPSTR, arg4: UINT): UINT{. +proc DragQueryFileA*(arg1: HDROP, arg2: uint32, arg3: LPSTR, arg4: uint32): uint32{. stdcall, dynlib: "shell32.dll", importc: "DragQueryFileA".} -proc DragQueryFileW*(arg1: HDROP, arg2: UINT, arg3: LPWSTR, arg4: UINT): UINT{. +proc DragQueryFileW*(arg1: HDROP, arg2: uint32, arg3: LPWSTR, arg4: uint32): uint32{. stdcall, dynlib: "shell32.dll", importc: "DragQueryFileW".} -proc DragQueryFile*(arg1: HDROP, arg2: UINT, arg3: LPSTR, arg4: UINT): UINT{. +proc DragQueryFile*(arg1: HDROP, arg2: uint32, arg3: LPSTR, arg4: uint32): uint32{. stdcall, dynlib: "shell32.dll", importc: "DragQueryFileA".} -proc DragQueryFile*(arg1: HDROP, arg2: UINT, arg3: LPWSTR, arg4: UINT): UINT{. +proc DragQueryFile*(arg1: HDROP, arg2: uint32, arg3: LPWSTR, arg4: uint32): uint32{. stdcall, dynlib: "shell32.dll", importc: "DragQueryFileW".} -proc DragQueryPoint*(arg1: HDROP, arg2: LPPOINT): BOOL{.stdcall, +proc DragQueryPoint*(arg1: HDROP, arg2: LPPOINT): bool{.stdcall, dynlib: "shell32.dll", importc: "DragQueryPoint".} proc DragFinish*(arg1: HDROP){.stdcall, dynlib: "shell32.dll", importc: "DragFinish".} -proc DragAcceptFiles*(hwnd: HWND, arg2: BOOL){.stdcall, dynlib: "shell32.dll", +proc DragAcceptFiles*(hwnd: HWND, arg2: bool){.stdcall, dynlib: "shell32.dll", importc: "DragAcceptFiles".} -proc ShellExecuteA*(HWND: hwnd, lpOperation: LPCSTR, lpFile: LPCSTR, +proc ShellExecuteA*(hwnd: HWND, lpOperation: LPCSTR, lpFile: LPCSTR, lpParameters: LPCSTR, lpDirectory: LPCSTR, nShowCmd: int32): HInst{. stdcall, dynlib: "shell32.dll", importc: "ShellExecuteA".} proc ShellExecuteW*(hwnd: HWND, lpOperation: LPCWSTR, lpFile: LPCWSTR, lpParameters: LPCWSTR, lpDirectory: LPCWSTR, nShowCmd: int32): HInst{. stdcall, dynlib: "shell32.dll", importc: "ShellExecuteW".} -proc ShellExecute*(HWND: hwnd, lpOperation: LPCSTR, lpFile: LPCSTR, +proc ShellExecute*(hwnd: HWND, lpOperation: LPCSTR, lpFile: LPCSTR, lpParameters: LPCSTR, lpDirectory: LPCSTR, nShowCmd: int32): HInst{. stdcall, dynlib: "shell32.dll", importc: "ShellExecuteA".} proc ShellExecute*(hwnd: HWND, lpOperation: LPCWSTR, lpFile: LPCWSTR, @@ -94,16 +94,16 @@ proc FindExecutable*(lpFile: LPCSTR, lpDirectory: LPCSTR, lpResult: LPSTR): HIns stdcall, dynlib: "shell32.dll", importc: "FindExecutableA".} proc FindExecutable*(lpFile: LPCWSTR, lpDirectory: LPCWSTR, lpResult: LPWSTR): HInst{. stdcall, dynlib: "shell32.dll", importc: "FindExecutableW".} -proc CommandLineToArgvW*(lpCmdLine: LPCWSTR, pNumArgs: ptr int32): pLPWSTR{. +proc CommandLineToArgvW*(lpCmdLine: LPCWSTR, pNumArgs: ptr int32): PLPWSTR{. stdcall, dynlib: "shell32.dll", importc: "CommandLineToArgvW".} -proc ShellAboutA*(HWND: hWnd, szApp: LPCSTR, szOtherStuff: LPCSTR, HICON: hIcon): int32{. +proc ShellAboutA*(hwnd: HWND, szApp: LPCSTR, szOtherStuff: LPCSTR, hIcon: HICON): int32{. stdcall, dynlib: "shell32.dll", importc: "ShellAboutA".} -proc ShellAboutW*(HWND: hWnd, szApp: LPCWSTR, szOtherStuff: LPCWSTR, - HICON: hIcon): int32{.stdcall, dynlib: "shell32.dll", +proc ShellAboutW*(hwnd: HWND, szApp: LPCWSTR, szOtherStuff: LPCWSTR, + hIcon: HICON): int32{.stdcall, dynlib: "shell32.dll", importc: "ShellAboutW".} -proc ShellAbout*(HWND: hWnd, szApp: LPCSTR, szOtherStuff: LPCSTR, HICON: hIcon): int32{. +proc ShellAbout*(hwnd: HWND, szApp: LPCSTR, szOtherStuff: LPCSTR, hIcon: HICON): int32{. stdcall, dynlib: "shell32.dll", importc: "ShellAboutA".} -proc ShellAbout*(HWND: hWnd, szApp: LPCWSTR, szOtherStuff: LPCWSTR, HICON: hIcon): int32{. +proc ShellAbout*(hwnd: HWND, szApp: LPCWSTR, szOtherStuff: LPCWSTR, hIcon: HICON): int32{. stdcall, dynlib: "shell32.dll", importc: "ShellAboutW".} proc DuplicateIcon*(inst: HINST, icon: HICON): HIcon{.stdcall, dynlib: "shell32.dll", importc: "DuplicateIcon".} @@ -115,29 +115,29 @@ proc ExtractAssociatedIcon*(hInst: HINST, lpIconPath: LPSTR, lpiIcon: LPWORD): H stdcall, dynlib: "shell32.dll", importc: "ExtractAssociatedIconA".} proc ExtractAssociatedIcon*(hInst: HINST, lpIconPath: LPWSTR, lpiIcon: LPWORD): HICON{. stdcall, dynlib: "shell32.dll", importc: "ExtractAssociatedIconW".} -proc ExtractIconA*(hInst: HINST, lpszExeFileName: LPCSTR, nIconIndex: UINT): HICON{. +proc ExtractIconA*(hInst: HINST, lpszExeFileName: LPCSTR, nIconIndex: uint32): HICON{. stdcall, dynlib: "shell32.dll", importc: "ExtractIconA".} -proc ExtractIconW*(hInst: HINST, lpszExeFileName: LPCWSTR, nIconIndex: UINT): HICON{. +proc ExtractIconW*(hInst: HINST, lpszExeFileName: LPCWSTR, nIconIndex: uint32): HICON{. stdcall, dynlib: "shell32.dll", importc: "ExtractIconW".} -proc ExtractIcon*(hInst: HINST, lpszExeFileName: LPCSTR, nIconIndex: UINT): HICON{. +proc ExtractIcon*(hInst: HINST, lpszExeFileName: LPCSTR, nIconIndex: uint32): HICON{. stdcall, dynlib: "shell32.dll", importc: "ExtractIconA".} -proc ExtractIcon*(hInst: HINST, lpszExeFileName: LPCWSTR, nIconIndex: UINT): HICON{. +proc ExtractIcon*(hInst: HINST, lpszExeFileName: LPCWSTR, nIconIndex: uint32): HICON{. stdcall, dynlib: "shell32.dll", importc: "ExtractIconW".} # if(WINVER >= 0x0400) type # init with sizeof(DRAGINFO) DRAGINFOA* {.final.} = object - uSize*: UINT + uSize*: uint32 pt*: POINT - fNC*: BOOL + fNC*: bool lpFileList*: LPSTR grfKeyState*: DWORD TDRAGINFOA* = DRAGINFOA LPDRAGINFOA* = ptr DRAGINFOA # init with sizeof(DRAGINFO) DRAGINFOW* {.final.} = object - uSize*: UINT + uSize*: uint32 pt*: POINT - fNC*: BOOL + fNC*: bool lpFileList*: LPWSTR grfKeyState*: DWORD @@ -184,8 +184,8 @@ type AppBarData* {.final.} = object cbSize*: DWORD hWnd*: HWND - uCallbackMessage*: UINT - uEdge*: UINT + uCallbackMessage*: uint32 + uEdge*: uint32 rc*: RECT lParam*: LPARAM # message specific @@ -197,41 +197,41 @@ proc SHAppBarMessage*(dwMessage: DWORD, pData: APPBARDATA): UINT_PTR{.stdcall, # # EndAppBar # -proc DoEnvironmentSubstA*(szString: LPSTR, cchString: UINT): DWORD{.stdcall, +proc DoEnvironmentSubstA*(szString: LPSTR, cchString: uint32): DWORD{.stdcall, dynlib: "shell32.dll", importc: "DoEnvironmentSubstA".} -proc DoEnvironmentSubstW*(szString: LPWSTR, cchString: UINT): DWORD{.stdcall, +proc DoEnvironmentSubstW*(szString: LPWSTR, cchString: uint32): DWORD{.stdcall, dynlib: "shell32.dll", importc: "DoEnvironmentSubstW".} -proc DoEnvironmentSubst*(szString: LPSTR, cchString: UINT): DWORD{.stdcall, +proc DoEnvironmentSubst*(szString: LPSTR, cchString: uint32): DWORD{.stdcall, dynlib: "shell32.dll", importc: "DoEnvironmentSubstA".} -proc DoEnvironmentSubst*(szString: LPWSTR, cchString: UINT): DWORD{.stdcall, +proc DoEnvironmentSubst*(szString: LPWSTR, cchString: uint32): DWORD{.stdcall, dynlib: "shell32.dll", importc: "DoEnvironmentSubstW".} #Macro proc EIRESID*(x: int32): int32 -proc ExtractIconExA*(lpszFile: LPCSTR, nIconIndex: int32, phiconLarge: pHICON, - phiconSmall: pHIcon, nIcons: UINT): UINT{.stdcall, +proc ExtractIconExA*(lpszFile: LPCSTR, nIconIndex: int32, phiconLarge: PHICON, + phiconSmall: PHICON, nIcons: uint32): uint32{.stdcall, dynlib: "shell32.dll", importc: "ExtractIconExA".} -proc ExtractIconExW*(lpszFile: LPCWSTR, nIconIndex: int32, phiconLarge: pHICON, - phiconSmall: pHIcon, nIcons: UINT): UINT{.stdcall, +proc ExtractIconExW*(lpszFile: LPCWSTR, nIconIndex: int32, phiconLarge: PHICON, + phiconSmall: PHICON, nIcons: uint32): uint32{.stdcall, dynlib: "shell32.dll", importc: "ExtractIconExW".} proc ExtractIconExA*(lpszFile: LPCSTR, nIconIndex: int32, phiconLarge: var HICON, phiconSmall: var HIcon, - nIcons: UINT): UINT{.stdcall, dynlib: "shell32.dll", + nIcons: uint32): uint32{.stdcall, dynlib: "shell32.dll", importc: "ExtractIconExA".} proc ExtractIconExW*(lpszFile: LPCWSTR, nIconIndex: int32, phiconLarge: var HICON, phiconSmall: var HIcon, - nIcons: UINT): UINT{.stdcall, dynlib: "shell32.dll", + nIcons: uint32): uint32{.stdcall, dynlib: "shell32.dll", importc: "ExtractIconExW".} -proc ExtractIconEx*(lpszFile: LPCSTR, nIconIndex: int32, phiconLarge: pHICON, - phiconSmall: pHIcon, nIcons: UINT): UINT{.stdcall, +proc ExtractIconEx*(lpszFile: LPCSTR, nIconIndex: int32, phiconLarge: PHICON, + phiconSmall: PHICON, nIcons: uint32): uint32{.stdcall, dynlib: "shell32.dll", importc: "ExtractIconExA".} -proc ExtractIconEx*(lpszFile: LPCWSTR, nIconIndex: int32, phiconLarge: pHICON, - phiconSmall: pHIcon, nIcons: UINT): UINT{.stdcall, +proc ExtractIconEx*(lpszFile: LPCWSTR, nIconIndex: int32, phiconLarge: PHICON, + phiconSmall: PHICON, nIcons: uint32): uint32{.stdcall, dynlib: "shell32.dll", importc: "ExtractIconExW".} proc ExtractIconEx*(lpszFile: LPCSTR, nIconIndex: int32, phiconLarge: var HICON, - phiconSmall: var HIcon, nIcons: UINT): UINT{.stdcall, + phiconSmall: var HIcon, nIcons: uint32): uint32{.stdcall, dynlib: "shell32.dll", importc: "ExtractIconExA".} proc ExtractIconEx*(lpszFile: LPCWSTR, nIconIndex: int32, - phiconLarge: var HICON, phiconSmall: var HIcon, nIcons: UINT): UINT{. + phiconLarge: var HICON, phiconSmall: var HIcon, nIcons: uint32): uint32{. stdcall, dynlib: "shell32.dll", importc: "ExtractIconExW".} # # Shell File Operations @@ -291,11 +291,11 @@ type type SHFILEOPSTRUCTA* {.final.} = object hwnd*: HWND - wFunc*: UINT + wFunc*: uint32 pFrom*: LPCSTR pTo*: LPCSTR fFlags*: FILEOP_FLAGS - fAnyOperationsAborted*: BOOL + fAnyOperationsAborted*: bool hNameMappings*: LPVOID lpszProgressTitle*: LPCSTR # only used if FOF_SIMPLEPROGRESS @@ -303,11 +303,11 @@ type LPSHFILEOPSTRUCTA* = ptr SHFILEOPSTRUCTA SHFILEOPSTRUCTW* {.final.} = object hwnd*: HWND - wFunc*: UINT + wFunc*: uint32 pFrom*: LPCWSTR pTo*: LPCWSTR fFlags*: FILEOP_FLAGS - fAnyOperationsAborted*: BOOL + fAnyOperationsAborted*: bool hNameMappings*: LPVOID lpszProgressTitle*: LPCWSTR @@ -430,10 +430,10 @@ type cbSize*: DWORD fMask*: ULONG hwnd*: HWND - lpVerb*: lpcwstr - lpFile*: lpcwstr - lpParameters*: lpcwstr - lpDirectory*: lpcwstr + lpVerb*: LPCWSTR + lpFile*: LPCWSTR + lpParameters*: LPCWSTR + lpDirectory*: LPCWSTR nShow*: int32 hInstApp*: HINST lpIDList*: LPVOID @@ -456,24 +456,24 @@ else: SHELLEXECUTEINFO* = SHELLEXECUTEINFOA TSHELLEXECUTEINFO* = SHELLEXECUTEINFOA LPSHELLEXECUTEINFO* = LPSHELLEXECUTEINFOA -proc ShellExecuteExA*(lpExecInfo: LPSHELLEXECUTEINFOA): Bool{.stdcall, +proc ShellExecuteExA*(lpExecInfo: LPSHELLEXECUTEINFOA): bool{.stdcall, dynlib: "shell32.dll", importc: "ShellExecuteExA".} -proc ShellExecuteExW*(lpExecInfo: LPSHELLEXECUTEINFOW): Bool{.stdcall, +proc ShellExecuteExW*(lpExecInfo: LPSHELLEXECUTEINFOW): bool{.stdcall, dynlib: "shell32.dll", importc: "ShellExecuteExW".} -proc ShellExecuteEx*(lpExecInfo: LPSHELLEXECUTEINFOA): Bool{.stdcall, +proc ShellExecuteEx*(lpExecInfo: LPSHELLEXECUTEINFOA): bool{.stdcall, dynlib: "shell32.dll", importc: "ShellExecuteExA".} -proc ShellExecuteEx*(lpExecInfo: LPSHELLEXECUTEINFOW): Bool{.stdcall, +proc ShellExecuteEx*(lpExecInfo: LPSHELLEXECUTEINFOW): bool{.stdcall, dynlib: "shell32.dll", importc: "ShellExecuteExW".} -proc WinExecErrorA*(HWND: hwnd, error: int32, lpstrFileName: LPCSTR, +proc WinExecErrorA*(hwnd: HWND, error: int32, lpstrFileName: LPCSTR, lpstrTitle: LPCSTR){.stdcall, dynlib: "shell32.dll", importc: "WinExecErrorA".} -proc WinExecErrorW*(HWND: hwnd, error: int32, lpstrFileName: LPCWSTR, +proc WinExecErrorW*(hwnd: HWND, error: int32, lpstrFileName: LPCWSTR, lpstrTitle: LPCWSTR){.stdcall, dynlib: "shell32.dll", importc: "WinExecErrorW".} -proc WinExecError*(HWND: hwnd, error: int32, lpstrFileName: LPCSTR, +proc WinExecError*(hwnd: HWND, error: int32, lpstrFileName: LPCSTR, lpstrTitle: LPCSTR){.stdcall, dynlib: "shell32.dll", importc: "WinExecErrorA".} -proc WinExecError*(HWND: hwnd, error: int32, lpstrFileName: LPCWSTR, +proc WinExecError*(hwnd: HWND, error: int32, lpstrFileName: LPCWSTR, lpstrTitle: LPCWSTR){.stdcall, dynlib: "shell32.dll", importc: "WinExecErrorW".} type @@ -487,7 +487,7 @@ type hUserToken*: HANDLE lpProcessAttributes*: LPSECURITY_ATTRIBUTES lpThreadAttributes*: LPSECURITY_ATTRIBUTES - bInheritHandles*: BOOL + bInheritHandles*: bool dwCreationFlags*: DWORD lpStartupInfo*: LPSTARTUPINFOW lpProcessInformation*: LPPROCESS_INFORMATION @@ -495,7 +495,7 @@ type TSHCREATEPROCESSINFOW* = SHCREATEPROCESSINFOW PSHCREATEPROCESSINFOW* = ptr SHCREATEPROCESSINFOW -proc SHCreateProcessAsUserW*(pscpi: PSHCREATEPROCESSINFOW): Bool{.stdcall, +proc SHCreateProcessAsUserW*(pscpi: PSHCREATEPROCESSINFOW): bool{.stdcall, dynlib: "shell32.dll", importc: "SHCreateProcessAsUserW".} # # End ShellExecuteEx and family } @@ -544,16 +544,16 @@ type NOTIFYICONDATAA* {.final.} = object cbSize*: DWORD hWnd*: HWND - uID*: UINT - uFlags*: UINT - uCallbackMessage*: UINT + uID*: uint32 + uFlags*: uint32 + uCallbackMessage*: uint32 hIcon*: HICON - szTip*: array[0..127, CHAR] + szTip*: array[0..127, char] dwState*: DWORD dwStateMask*: DWORD - szInfo*: array[0..255, CHAR] - uTimeout*: UINT # also: uVersion - szInfoTitle*: array[0..63, CHAR] + szInfo*: array[0..255, char] + uTimeout*: uint32 # also: uVersion + szInfoTitle*: array[0..63, char] dwInfoFlags*: DWORD guidItem*: TGUID @@ -562,16 +562,16 @@ type NOTIFYICONDATAW* {.final.} = object cbSize*: DWORD hWnd*: HWND - uID*: UINT - uFlags*: UINT - uCallbackMessage*: UINT + uID*: uint32 + uFlags*: uint32 + uCallbackMessage*: uint32 hIcon*: HICON - szTip*: array[0..127, WCHAR] + szTip*: array[0..127, Wchar] dwState*: DWORD dwStateMask*: DWORD - szInfo*: array[0..255, WCHAR] - uTimeout*: UINT # also uVersion : UINT - szInfoTitle*: array[0..63, CHAR] + szInfo*: array[0..255, Wchar] + uTimeout*: uint32 # also uVersion : UINT + szInfoTitle*: array[0..63, char] dwInfoFlags*: DWORD guidItem*: TGUID @@ -619,13 +619,13 @@ const NIIF_ICON_MASK* = 0x0000000F NIIF_NOSOUND* = 0x00000010 -proc Shell_NotifyIconA*(dwMessage: Dword, lpData: PNOTIFYICONDATAA): Bool{. +proc Shell_NotifyIconA*(dwMessage: Dword, lpData: PNOTIFYICONDATAA): bool{. stdcall, dynlib: "shell32.dll", importc: "Shell_NotifyIconA".} -proc Shell_NotifyIconW*(dwMessage: Dword, lpData: PNOTIFYICONDATAW): Bool{. +proc Shell_NotifyIconW*(dwMessage: Dword, lpData: PNOTIFYICONDATAW): bool{. stdcall, dynlib: "shell32.dll", importc: "Shell_NotifyIconW".} -proc Shell_NotifyIcon*(dwMessage: Dword, lpData: PNOTIFYICONDATAA): Bool{. +proc Shell_NotifyIcon*(dwMessage: Dword, lpData: PNOTIFYICONDATAA): bool{. stdcall, dynlib: "shell32.dll", importc: "Shell_NotifyIconA".} -proc Shell_NotifyIcon*(dwMessage: Dword, lpData: PNOTIFYICONDATAW): Bool{. +proc Shell_NotifyIcon*(dwMessage: Dword, lpData: PNOTIFYICONDATAW): bool{. stdcall, dynlib: "shell32.dll", importc: "Shell_NotifyIconW".} # # The SHGetFileInfo API provides an easy way to get attributes @@ -652,20 +652,20 @@ type hIcon*: HICON # out: icon iIcon*: int32 # out: icon index dwAttributes*: DWORD # out: SFGAO_ flags - szDisplayName*: array[0..(MAX_PATH) - 1, CHAR] # out: display name (or path) - szTypeName*: array[0..79, CHAR] # out: type name + szDisplayName*: array[0..(MAX_PATH) - 1, char] # out: display name (or path) + szTypeName*: array[0..79, char] # out: type name TSHFILEINFOA* = SHFILEINFOA - pSHFILEINFOA* = ptr SHFILEINFOA + PSHFILEINFOA* = ptr SHFILEINFOA SHFILEINFOW* {.final.} = object hIcon*: HICON # out: icon iIcon*: int32 # out: icon index dwAttributes*: DWORD # out: SFGAO_ flags - szDisplayName*: array[0..(MAX_PATH) - 1, WCHAR] # out: display name (or path) - szTypeName*: array[0..79, WCHAR] # out: type name + szDisplayName*: array[0..(MAX_PATH) - 1, Wchar] # out: display name (or path) + szTypeName*: array[0..79, Wchar] # out: type name TSHFILEINFOW* = SHFILEINFOW - pSHFILEINFOW* = ptr SHFILEINFOW + PSHFILEINFOW* = ptr SHFILEINFOW when defined(UNICODE): type @@ -701,67 +701,67 @@ const # in the upper 8 bits of the iIcon proc SHGetFileInfoA*(pszPath: LPCSTR, dwFileAttributes: DWORD, - psfi: pSHFILEINFOA, cbFileInfo, UFlags: UINT): DWORD{. + psfi: PSHFILEINFOA, cbFileInfo, UFlags: uint32): DWORD{. stdcall, dynlib: "shell32.dll", importc: "SHGetFileInfoA".} proc SHGetFileInfoW*(pszPath: LPCWSTR, dwFileAttributes: DWORD, - psfi: pSHFILEINFOW, cbFileInfo, UFlags: UINT): DWORD{. + psfi: PSHFILEINFOW, cbFileInfo, UFlags: uint32): DWORD{. stdcall, dynlib: "shell32.dll", importc: "SHGetFileInfoW".} proc SHGetFileInfo*(pszPath: LPCSTR, dwFileAttributes: DWORD, - psfi: pSHFILEINFOA, cbFileInfo, UFlags: UINT): DWORD{. + psfi: PSHFILEINFOA, cbFileInfo, UFlags: uint32): DWORD{. stdcall, dynlib: "shell32.dll", importc: "SHGetFileInfoA".} proc SHGetFileInfoA*(pszPath: LPCSTR, dwFileAttributes: DWORD, - psfi: var TSHFILEINFOA, cbFileInfo, UFlags: UINT): DWORD{. + psfi: var TSHFILEINFOA, cbFileInfo, UFlags: uint32): DWORD{. stdcall, dynlib: "shell32.dll", importc: "SHGetFileInfoA".} proc SHGetFileInfoW*(pszPath: LPCWSTR, dwFileAttributes: DWORD, - psfi: var TSHFILEINFOW, cbFileInfo, UFlags: UINT): DWORD{. + psfi: var TSHFILEINFOW, cbFileInfo, UFlags: uint32): DWORD{. stdcall, dynlib: "shell32.dll", importc: "SHGetFileInfoW".} proc SHGetFileInfo*(pszPath: LPCSTR, dwFileAttributes: DWORD, - psfi: var TSHFILEINFOA, cbFileInfo, UFlags: UINT): DWORD{. + psfi: var TSHFILEINFOA, cbFileInfo, UFlags: uint32): DWORD{. stdcall, dynlib: "shell32.dll", importc: "SHGetFileInfoA".} proc SHGetFileInfo*(pszPath: LPCWSTR, dwFileAttributes: DWORD, - psfi: var TSHFILEINFOW, cbFileInfo, UFlags: UINT): DWORD{. + psfi: var TSHFILEINFOW, cbFileInfo, UFlags: uint32): DWORD{. stdcall, dynlib: "shell32.dll", importc: "SHGetFileInfoW".} proc SHGetDiskFreeSpaceExA*(pszDirectoryName: LPCSTR, - pulFreeBytesAvailableToCaller: pULARGE_INTEGER, - pulTotalNumberOfBytes: pULARGE_INTEGER, - pulTotalNumberOfFreeBytes: pULARGE_INTEGER): Bool{. + pulFreeBytesAvailableToCaller: PULARGE_INTEGER, + pulTotalNumberOfBytes: PULARGE_INTEGER, + pulTotalNumberOfFreeBytes: PULARGE_INTEGER): bool{. stdcall, dynlib: "shell32.dll", importc: "SHGetDiskFreeSpaceExA".} proc SHGetDiskFreeSpaceExW*(pszDirectoryName: LPCWSTR, - pulFreeBytesAvailableToCaller: pULARGE_INTEGER, - pulTotalNumberOfBytes: pULARGE_INTEGER, - pulTotalNumberOfFreeBytes: pULARGE_INTEGER): Bool{. + pulFreeBytesAvailableToCaller: PULARGE_INTEGER, + pulTotalNumberOfBytes: PULARGE_INTEGER, + pulTotalNumberOfFreeBytes: PULARGE_INTEGER): bool{. stdcall, dynlib: "shell32.dll", importc: "SHGetDiskFreeSpaceExW".} proc SHGetDiskFreeSpaceEx*(pszDirectoryName: LPCSTR, - pulFreeBytesAvailableToCaller: pULARGE_INTEGER, - pulTotalNumberOfBytes: pULARGE_INTEGER, - pulTotalNumberOfFreeBytes: pULARGE_INTEGER): Bool{. + pulFreeBytesAvailableToCaller: PULARGE_INTEGER, + pulTotalNumberOfBytes: PULARGE_INTEGER, + pulTotalNumberOfFreeBytes: PULARGE_INTEGER): bool{. stdcall, dynlib: "shell32.dll", importc: "SHGetDiskFreeSpaceExA".} proc SHGetDiskFreeSpace*(pszDirectoryName: LPCSTR, - pulFreeBytesAvailableToCaller: pULARGE_INTEGER, - pulTotalNumberOfBytes: pULARGE_INTEGER, - pulTotalNumberOfFreeBytes: pULARGE_INTEGER): Bool{. + pulFreeBytesAvailableToCaller: PULARGE_INTEGER, + pulTotalNumberOfBytes: PULARGE_INTEGER, + pulTotalNumberOfFreeBytes: PULARGE_INTEGER): bool{. stdcall, dynlib: "shell32.dll", importc: "SHGetDiskFreeSpaceExA".} proc SHGetDiskFreeSpaceEx*(pszDirectoryName: LPCWSTR, - pulFreeBytesAvailableToCaller: pULARGE_INTEGER, - pulTotalNumberOfBytes: pULARGE_INTEGER, - pulTotalNumberOfFreeBytes: pULARGE_INTEGER): Bool{. + pulFreeBytesAvailableToCaller: PULARGE_INTEGER, + pulTotalNumberOfBytes: PULARGE_INTEGER, + pulTotalNumberOfFreeBytes: PULARGE_INTEGER): bool{. stdcall, dynlib: "shell32.dll", importc: "SHGetDiskFreeSpaceExW".} proc SHGetDiskFreeSpace*(pszDirectoryName: LPCWSTR, - pulFreeBytesAvailableToCaller: pULARGE_INTEGER, - pulTotalNumberOfBytes: pULARGE_INTEGER, - pulTotalNumberOfFreeBytes: pULARGE_INTEGER): Bool{. + pulFreeBytesAvailableToCaller: PULARGE_INTEGER, + pulTotalNumberOfBytes: PULARGE_INTEGER, + pulTotalNumberOfFreeBytes: PULARGE_INTEGER): bool{. stdcall, dynlib: "shell32.dll", importc: "SHGetDiskFreeSpaceExW".} proc SHGetNewLinkInfoA*(pszLinkTo: LPCSTR, pszDir: LPCSTR, pszName: LPSTR, - pfMustCopy: pBool, uFlags: UINT): Bool{.stdcall, + pfMustCopy: PBool, uFlags: uint32): bool{.stdcall, dynlib: "shell32.dll", importc: "SHGetNewLinkInfoA".} proc SHGetNewLinkInfoW*(pszLinkTo: LPCWSTR, pszDir: LPCWSTR, pszName: LPWSTR, - pfMustCopy: pBool, uFlags: UINT): Bool{.stdcall, + pfMustCopy: PBool, uFlags: uint32): bool{.stdcall, dynlib: "shell32.dll", importc: "SHGetNewLinkInfoW".} proc SHGetNewLinkInfo*(pszLinkTo: LPCSTR, pszDir: LPCSTR, pszName: LPSTR, - pfMustCopy: pBool, uFlags: UINT): Bool{.stdcall, + pfMustCopy: PBool, uFlags: uint32): bool{.stdcall, dynlib: "shell32.dll", importc: "SHGetNewLinkInfoA".} proc SHGetNewLinkInfo*(pszLinkTo: LPCWSTR, pszDir: LPCWSTR, pszName: LPWSTR, - pfMustCopy: pBool, uFlags: UINT): Bool{.stdcall, + pfMustCopy: PBool, uFlags: uint32): bool{.stdcall, dynlib: "shell32.dll", importc: "SHGetNewLinkInfoW".} const SHGNLI_PIDL* = 0x00000001 # pszLinkTo is a pidl @@ -777,17 +777,17 @@ const PRINTACTION_DOCUMENTDEFAULTS* = 6 PRINTACTION_SERVERPROPERTIES* = 7 -proc SHInvokePrinterCommandA*(HWND: hwnd, uAction: UINT, lpBuf1: LPCSTR, - lpBuf2: LPCSTR, fModal: Bool): Bool{.stdcall, +proc SHInvokePrinterCommandA*(hwnd: HWND, uAction: uint32, lpBuf1: LPCSTR, + lpBuf2: LPCSTR, fModal: bool): bool{.stdcall, dynlib: "shell32.dll", importc: "SHInvokePrinterCommandA".} -proc SHInvokePrinterCommandW*(HWND: hwnd, uAction: UINT, lpBuf1: LPCWSTR, - lpBuf2: LPCWSTR, fModal: Bool): Bool{.stdcall, +proc SHInvokePrinterCommandW*(hwnd: HWND, uAction: uint32, lpBuf1: LPCWSTR, + lpBuf2: LPCWSTR, fModal: bool): bool{.stdcall, dynlib: "shell32.dll", importc: "SHInvokePrinterCommandW".} -proc SHInvokePrinterCommand*(HWND: hwnd, uAction: UINT, lpBuf1: LPCSTR, - lpBuf2: LPCSTR, fModal: Bool): Bool{.stdcall, +proc SHInvokePrinterCommand*(hwnd: HWND, uAction: uint32, lpBuf1: LPCSTR, + lpBuf2: LPCSTR, fModal: bool): bool{.stdcall, dynlib: "shell32.dll", importc: "SHInvokePrinterCommandA".} -proc SHInvokePrinterCommand*(HWND: hwnd, uAction: UINT, lpBuf1: LPCWSTR, - lpBuf2: LPCWSTR, fModal: Bool): Bool{.stdcall, +proc SHInvokePrinterCommand*(hwnd: HWND, uAction: uint32, lpBuf1: LPCWSTR, + lpBuf2: LPCWSTR, fModal: bool): bool{.stdcall, dynlib: "shell32.dll", importc: "SHInvokePrinterCommandW".} proc SHLoadNonloadedIconOverlayIdentifiers*(): HResult{.stdcall, dynlib: "shell32.dll", importc: "SHInvokePrinterCommandW".} diff --git a/lib/windows/shfolder.nim b/lib/windows/shfolder.nim index 253b1c77a..886d757eb 100644 --- a/lib/windows/shfolder.nim +++ b/lib/windows/shfolder.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2006 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/lib/windows/windows.nim b/lib/windows/windows.nim index 5fd9127b3..f0895217b 100644 --- a/lib/windows/windows.nim +++ b/lib/windows/windows.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2010 Andreas Rumpf # # See the file "copying.txt", included in this @@ -49,15 +49,15 @@ type # BaseTsd.h -- Type definitions for the basic sized types DWORD64* = int64 PDWORD64* = ptr DWORD64 # int32 on Win32, int64 on Win64 - INT_PTR* = TAddress - UINT_PTR* = TAddress - LONG_PTR* = TAddress - ULONG_PTR* = TAddress - SIZE_T* = TAddress - SSIZE_T* = TAddress - DWORD_PTR* = TAddress + INT_PTR* = ByteAddress + UINT_PTR* = ByteAddress + LONG_PTR* = ByteAddress + ULONG_PTR* = ByteAddress + SIZE_T* = ByteAddress + SSIZE_T* = ByteAddress + DWORD_PTR* = ByteAddress # Thread affinity - KAFFINITY* = TAddress + KAFFINITY* = ByteAddress PKAFFINITY* = ptr KAFFINITY type # WinDef.h -- Basic Windows Type Definitions @@ -74,8 +74,8 @@ type # WinDef.h -- Basic Windows Type Definitions DWORD* = int32 WINBOOL* = int32 WORD* = int16 - # FLOAT* = float - PFLOAT* = ptr FLOAT + #FLOAT* = float + PFLOAT* = ptr float32 PWINBOOL* = ptr WINBOOL LPWINBOOL* = ptr WINBOOL PBYTE* = ptr int8 @@ -383,7 +383,7 @@ type stdcall.} LPCFHOOKPROC* = proc (para1: HWND, para2: WINUINT, para3: WPARAM, para4: LPARAM): WINUINT{. stdcall.} - PTHREAD_START_ROUTINE* = Pointer + PTHREAD_START_ROUTINE* = pointer LPTHREAD_START_ROUTINE* = PTHREAD_START_ROUTINE EDITSTREAMCALLBACK* = proc (para1: DWORD, para2: LPBYTE, para3: LONG, para4: LONG): DWORD{.stdcall.} @@ -6743,13 +6743,13 @@ const EXCEPTION_READ_FAULT* = 0 # Access violation was caused by a read EXCEPTION_WRITE_FAULT* = 1 # Access violation was caused by a write -when defined(cpuia64): +when hostCPU == "ia64": const EXCEPTION_EXECUTE_FAULT* = 2 # Access violation was caused by an instruction fetch else: const EXCEPTION_EXECUTE_FAULT* = 8 -when defined(cpupowerpc32): +when hostCPU == "powerpc": # ppc const CONTEXT_CONTROL* = 1 @@ -6758,7 +6758,7 @@ when defined(cpupowerpc32): CONTEXT_DEBUG_REGISTERS* = 8 CONTEXT_FULL* = CONTEXT_CONTROL or CONTEXT_FLOATING_POINT or CONTEXT_INTEGER CONTEXT_DEBUGGER* = CONTEXT_FULL -when defined(cpui386): +when hostCPU == "i386": # x86 # The doc refered me to winnt.h, so I had to look... const @@ -6776,7 +6776,7 @@ when defined(cpui386): CONTEXT_DEBUG_REGISTERS or CONTEXT_EXTENDED_REGISTERS # our own invention FLAG_TRACE_BIT* = 0x00000100 CONTEXT_DEBUGGER* = CONTEXT_FULL or CONTEXT_FLOATING_POINT -when defined(cpux86_64): +when hostCPU == "amd64": const INITIAL_MXCSR* = 0x00001F80 # initial MXCSR value INITIAL_FPCSR* = 0x0000027F # initial FPCSR value @@ -7778,7 +7778,7 @@ elif defined(x86_64): LastExceptionToRip*: DWORD64 LastExceptionFromRip*: DWORD64 -elif defined(powerpc32): +elif hostCPU == "powerpc": # ppc # Floating point registers returned when CONTEXT_FLOATING_POINT is set # Integer registers returned when CONTEXT_INTEGER is set. @@ -8415,34 +8415,34 @@ type # dmDisplayFixedOutput: DWORD; LPDEVMODE* = ptr DEVMODE - devicemode* = DEVMODE - tdevicemode* = DEVMODE - tdevicemodeA* = DEVMODE + Devicemode* = DEVMODE + TDevicemode* = DEVMODE + TDevicemodeA* = DEVMODE PDeviceModeA* = LPDEVMODE PDeviceMode* = LPDEVMODE TDEVMODE* = DEVMODE PDEVMODE* = LPDEVMODE - devmodeW* {.final, pure.} = object + DEVMODEW* {.final, pure.} = object dmDeviceName*: array[0..CCHDEVICENAME - 1, WCHAR] dmSpecVersion*: int16 dmDriverVersion*: int16 dmSize*: int16 dmDriverExtra*: int16 dmFields*: DWORD - dmOrientation*: short - dmPaperSize*: short - dmPaperLength*: short - dmPaperWidth*: short - dmScale*: short - dmCopies*: short - dmDefaultSource*: short - dmPrintQuality*: short - dmColor*: short - dmDuplex*: short - dmYResolution*: short - dmTTOption*: short - dmCollate*: short - dmFormName*: array[0..CCHFORMNAME - 1, wchar] + dmOrientation*: SHORT + dmPaperSize*: SHORT + dmPaperLength*: SHORT + dmPaperWidth*: SHORT + dmScale*: SHORT + dmCopies*: SHORT + dmDefaultSource*: SHORT + dmPrintQuality*: SHORT + dmColor*: SHORT + dmDuplex*: SHORT + dmYResolution*: SHORT + dmTTOption*: SHORT + dmCollate*: SHORT + dmFormName*: array[0..CCHFORMNAME - 1, WCHAR] dmLogPixels*: int16 dmBitsPerPel*: DWORD dmPelsWidth*: DWORD @@ -8459,7 +8459,7 @@ type dmPanningHeight*: DWORD LPDEVMODEW* = ptr DEVMODEW - devicemodeW* = DEVMODEW + DevicemodeW* = DEVMODEW TDeviceModeW* = DEVMODEW PDeviceModeW* = LPDEVMODEW TDEVMODEW* = DEVMODEW @@ -8497,7 +8497,7 @@ type LARGE_INTEGER* = int64 ULARGE_INTEGER* = int64 PLARGE_INTEGER* = ptr LARGE_INTEGER - TLargeInteger* = Int64 + TLargeInteger* = int64 PULARGE_INTEGER* = ptr ULARGE_INTEGER TULargeInteger* = int64 DISK_GEOMETRY* {.final, pure.} = object @@ -10469,7 +10469,7 @@ type ncb_name*: array[0..(NCBNAMSZ) - 1, UCHAR] ncb_rto*: UCHAR ncb_sto*: UCHAR - ncb_post*: proc (para1: p_NCB){.CDECL.} + ncb_post*: proc (para1: p_NCB){.cdecl.} ncb_lana_num*: UCHAR ncb_cmd_cplt*: UCHAR ncb_reserve*: array[0..9, UCHAR] @@ -10681,7 +10681,7 @@ type nErrCode*: int16 Reserved1*: int16 Reserved2*: int16 - szPathName*: array[0..(OFS_MAXPATHNAME) - 1, CHAR] + szPathName*: array[0..(OFS_MAXPATHNAME) - 1, char] LPOFSTRUCT* = ptr OFSTRUCT TOFSTRUCT* = OFSTRUCT @@ -10725,7 +10725,7 @@ type nMaxFileTitle*: DWORD lpstrInitialDir*: LPCTSTR lpstrTitle*: LPCTSTR - Flags*: DWORD + flags*: DWORD nFileOffset*: int16 nFileExtension*: int16 lpstrDefExt*: LPCTSTR @@ -10733,8 +10733,8 @@ type lpfnHook*: LPOFNHOOKPROC lpTemplateName*: LPCTSTR pvReserved*: pointer - dwreserved*: dword - FlagsEx*: dword + dwreserved*: DWORD + FlagsEx*: DWORD LPOPENFILENAME* = ptr TOPENFILENAME POPENFILENAME* = ptr TOPENFILENAME @@ -11280,8 +11280,8 @@ type dwSize*: DWORD hrasconn*: HRASCONN szEntryName*: array[0..(RAS_MaxEntryName + 1) - 1, TCHAR] - szDeviceType*: array[0..(RAS_MaxDeviceType + 1) - 1, CHAR] - szDeviceName*: array[0..(RAS_MaxDeviceName + 1) - 1, CHAR] + szDeviceType*: array[0..(RAS_MaxDeviceType + 1) - 1, char] + szDeviceName*: array[0..(RAS_MaxDeviceName + 1) - 1, char] TRASCONN* = RASCONN PRASCONN* = ptr RASCONN @@ -11344,9 +11344,9 @@ type TRASPPPNBF* = RASPPPNBF PRASPPPNBF* = ptr RASPPPNBF RASTERIZER_STATUS* {.final, pure.} = object - nSize*: short - wFlags*: short - nLanguageID*: short + nSize*: SHORT + wFlags*: SHORT + nLanguageID*: SHORT LPRASTERIZER_STATUS* = ptr RASTERIZER_STATUS TRASTERIZERSTATUS* = RASTERIZER_STATUS @@ -11592,7 +11592,7 @@ type PSTRRET* = ptr STRRET STYLEBUF* {.final, pure.} = object dwStyle*: DWORD - szDescription*: array[0..31, CHAR] + szDescription*: array[0..31, char] LPSTYLEBUF* = ptr STYLEBUF TSTYLEBUF* = STYLEBUF @@ -11801,7 +11801,7 @@ type TTOGGLEKEYS* = TOGGLEKEYS PTOGGLEKEYS* = ptr TOGGLEKEYS TTOKEN_SOURCE* {.final, pure.} = object - SourceName*: array[0..7, CHAR] + SourceName*: array[0..7, char] SourceIdentifier*: LUID PTOKENSOURCE* = ptr TTOKEN_SOURCE @@ -12366,7 +12366,7 @@ type uFlags*: WINUINT uCallbackMessage*: WINUINT hIcon*: HICON - szTip*: array[0..63, Char] + szTip*: array[0..63, char] NOTIFYICONDATA* = NOTIFYICONDATAA NOTIFYICONDATAW* {.final, pure.} = object @@ -13575,7 +13575,7 @@ type TWMSysKeyUp* = TWMKey TWMMenuChar* {.final, pure.} = object Msg*: WINUINT - User*: Char + User*: char MenuFlag*: int16 Menu*: HMENU Result*: LRESULT @@ -14432,19 +14432,19 @@ proc CharNextA*(lpsz: LPCSTR): LPSTR{.stdcall, dynlib: "user32", importc: "CharNextA".} proc CharPrevA*(lpszStart: LPCSTR, lpszCurrent: LPCSTR): LPSTR{.stdcall, dynlib: "user32", importc: "CharPrevA".} -proc IsCharAlphaA*(ch: CHAR): WINBOOL{.stdcall, dynlib: "user32", +proc IsCharAlphaA*(ch: char): WINBOOL{.stdcall, dynlib: "user32", importc: "IsCharAlphaA".} -proc IsCharAlphaNumericA*(ch: CHAR): WINBOOL{.stdcall, dynlib: "user32", +proc IsCharAlphaNumericA*(ch: char): WINBOOL{.stdcall, dynlib: "user32", importc: "IsCharAlphaNumericA".} -proc IsCharUpperA*(ch: CHAR): WINBOOL{.stdcall, dynlib: "user32", +proc IsCharUpperA*(ch: char): WINBOOL{.stdcall, dynlib: "user32", importc: "IsCharUpperA".} -proc IsCharLowerA*(ch: CHAR): WINBOOL{.stdcall, dynlib: "user32", +proc IsCharLowerA*(ch: char): WINBOOL{.stdcall, dynlib: "user32", importc: "IsCharLowerA".} proc GetKeyNameTextA*(lParam: LONG, lpString: LPSTR, nSize: int32): int32{. stdcall, dynlib: "user32", importc: "GetKeyNameTextA".} -proc VkKeyScanA*(ch: CHAR): SHORT{.stdcall, dynlib: "user32", +proc VkKeyScanA*(ch: char): SHORT{.stdcall, dynlib: "user32", importc: "VkKeyScanA".} -proc VkKeyScanExA*(ch: CHAR, dwhkl: HKL): SHORT{.stdcall, dynlib: "user32", +proc VkKeyScanExA*(ch: char, dwhkl: HKL): SHORT{.stdcall, dynlib: "user32", importc: "VkKeyScanExA".} proc MapVirtualKeyA*(uCode: WINUINT, uMapType: WINUINT): WINUINT{.stdcall, dynlib: "user32", importc: "MapVirtualKeyA".} @@ -14772,7 +14772,7 @@ proc DialogBoxA*(hInstance: HINST, lpTemplateName: LPCSTR, hWndParent: HWND, lpDialogFunc: DLGPROC): int32 proc DialogBoxIndirectA*(hInstance: HINST, hDialogTemplate: LPCDLGTEMPLATE, hWndParent: HWND, lpDialogFunc: DLGPROC): int32 -proc CreateDCA*(para1: LPCSTR, para2: LPCSTR, para3: LPCSTR, para4: pDEVMODE): HDC{. +proc CreateDCA*(para1: LPCSTR, para2: LPCSTR, para3: LPCSTR, para4: PDEVMODE): HDC{. stdcall, dynlib: "gdi32", importc: "CreateDCA".} proc VerInstallFileA*(uFlags: DWORD, szSrcFileName: LPSTR, szDestFileName: LPSTR, szSrcDir: LPSTR, szDestDir: LPSTR, @@ -14933,7 +14933,7 @@ proc WriteConsoleOutputCharacterA*(hConsoleOutput: HANDLE, lpCharacter: LPCSTR, nLength: DWORD, dwWriteCoord: COORD, lpNumberOfCharsWritten: LPDWORD): WINBOOL{. stdcall, dynlib: "kernel32", importc: "WriteConsoleOutputCharacterA".} -proc FillConsoleOutputCharacterA*(hConsoleOutput: HANDLE, cCharacter: CHAR, +proc FillConsoleOutputCharacterA*(hConsoleOutput: HANDLE, cCharacter: char, nLength: DWORD, dwWriteCoord: COORD, lpNumberOfCharsWritten: LPDWORD): WINBOOL{. stdcall, dynlib: "kernel32", importc: "FillConsoleOutputCharacterA".} @@ -15896,7 +15896,7 @@ proc DialogBoxW*(hInstance: HINST, lpTemplate: LPCWSTR, hWndParent: HWND, lpDialogFunc: DLGPROC): int32 proc DialogBoxIndirectW*(hInstance: HINST, lpTemplate: LPCDLGTEMPLATE, hWndParent: HWND, lpDialogFunc: DLGPROC): int32 -proc CreateDCW*(para1: LPCWSTR, para2: LPCWSTR, para3: LPCWSTR, para4: pDEVMODEW): HDC{. +proc CreateDCW*(para1: LPCWSTR, para2: LPCWSTR, para3: LPCWSTR, para4: PDEVMODEW): HDC{. stdcall, dynlib: "gdi32", importc: "CreateDCW".} proc VerInstallFileW*(uFlags: DWORD, szSrcFileName: LPWSTR, szDestFileName: LPWSTR, szSrcDir: LPWSTR, @@ -17038,7 +17038,7 @@ when defined(winUnicode): lpDialogFunc: DLGPROC): int32 proc DialogBoxIndirect*(hInstance: HINST, lpTemplate: LPCDLGTEMPLATE, hWndParent: HWND, lpDialogFunc: DLGPROC): int32 - proc CreateDC*(para1: LPCWSTR, para2: LPCWSTR, para3: LPCWSTR, para4: pDEVMODE): HDC{. + proc CreateDC*(para1: LPCWSTR, para2: LPCWSTR, para3: LPCWSTR, para4: PDEVMODE): HDC{. stdcall, dynlib: "gdi32", importc: "CreateDCW".} proc VerInstallFile*(uFlags: DWORD, szSrcFileName: LPWSTR, szDestFileName: LPWSTR, szSrcDir: LPWSTR, @@ -17825,19 +17825,19 @@ else: importc: "CharNextA".} proc CharPrev*(lpszStart: LPCSTR, lpszCurrent: LPCSTR): LPSTR{.stdcall, dynlib: "user32", importc: "CharPrevA".} - proc IsCharAlpha*(ch: CHAR): WINBOOL{.stdcall, dynlib: "user32", + proc IsCharAlpha*(ch: char): WINBOOL{.stdcall, dynlib: "user32", importc: "IsCharAlphaA".} - proc IsCharAlphaNumeric*(ch: CHAR): WINBOOL{.stdcall, dynlib: "user32", + proc IsCharAlphaNumeric*(ch: char): WINBOOL{.stdcall, dynlib: "user32", importc: "IsCharAlphaNumericA".} - proc IsCharUpper*(ch: CHAR): WINBOOL{.stdcall, dynlib: "user32", + proc IsCharUpper*(ch: char): WINBOOL{.stdcall, dynlib: "user32", importc: "IsCharUpperA".} - proc IsCharLower*(ch: CHAR): WINBOOL{.stdcall, dynlib: "user32", + proc IsCharLower*(ch: char): WINBOOL{.stdcall, dynlib: "user32", importc: "IsCharLowerA".} proc GetKeyNameText*(lParam: LONG, lpString: LPSTR, nSize: int32): int32{. stdcall, dynlib: "user32", importc: "GetKeyNameTextA".} - proc VkKeyScan*(ch: CHAR): SHORT{.stdcall, dynlib: "user32", + proc VkKeyScan*(ch: char): SHORT{.stdcall, dynlib: "user32", importc: "VkKeyScanA".} - proc VkKeyScanEx*(ch: CHAR, dwhkl: HKL): SHORT{.stdcall, dynlib: "user32", + proc VkKeyScanEx*(ch: char, dwhkl: HKL): SHORT{.stdcall, dynlib: "user32", importc: "VkKeyScanExA".} proc MapVirtualKey*(uCode: WINUINT, uMapType: WINUINT): WINUINT{.stdcall, dynlib: "user32", importc: "MapVirtualKeyA".} @@ -18170,7 +18170,7 @@ else: lpDialogFunc: DLGPROC): int32 proc DialogBoxIndirect*(hInstance: HINST, hDialogTemplate: LPCDLGTEMPLATE, hWndParent: HWND, lpDialogFunc: DLGPROC): int32 - proc CreateDC*(para1: LPCSTR, para2: LPCSTR, para3: LPCSTR, para4: pDEVMODE): HDC{. + proc CreateDC*(para1: LPCSTR, para2: LPCSTR, para3: LPCSTR, para4: PDEVMODE): HDC{. stdcall, dynlib: "gdi32", importc: "CreateDCA".} proc VerInstallFile*(uFlags: DWORD, szSrcFileName: LPSTR, szDestFileName: LPSTR, szSrcDir: LPSTR, szDestDir: LPSTR, @@ -18332,7 +18332,7 @@ else: nLength: DWORD, dwWriteCoord: COORD, lpNumberOfCharsWritten: LPDWORD): WINBOOL{. stdcall, dynlib: "kernel32", importc: "WriteConsoleOutputCharacterA".} - proc FillConsoleOutputCharacter*(hConsoleOutput: HANDLE, cCharacter: CHAR, + proc FillConsoleOutputCharacter*(hConsoleOutput: HANDLE, cCharacter: char, nLength: DWORD, dwWriteCoord: COORD, lpNumberOfCharsWritten: LPDWORD): WINBOOL{. stdcall, dynlib: "kernel32", importc: "FillConsoleOutputCharacterA".} @@ -18513,9 +18513,9 @@ proc DisableThreadLibraryCalls*(hLibModule: HMODULE): WINBOOL{.stdcall, proc GetProcAddress*(hModule: HINST, lpProcName: LPCSTR): FARPROC{.stdcall, dynlib: "kernel32", importc: "GetProcAddress".} proc GetVersion*(): DWORD{.stdcall, dynlib: "kernel32", importc: "GetVersion".} -proc GlobalAlloc*(uFlags: INT, dwBytes: DWORD): HGLOBAL{.stdcall, +proc GlobalAlloc*(uFlags: int32, dwBytes: DWORD): HGLOBAL{.stdcall, dynlib: "kernel32", importc: "GlobalAlloc".} -proc GlobalReAlloc*(hMem: HGLOBAL, dwBytes: DWORD, uFlags: INT): HGLOBAL{. +proc GlobalReAlloc*(hMem: HGLOBAL, dwBytes: DWORD, uFlags: int32): HGLOBAL{. stdcall, dynlib: "kernel32", importc: "GlobalReAlloc".} proc GlobalSize*(hMem: HGLOBAL): DWORD{.stdcall, dynlib: "kernel32", importc: "GlobalSize".} @@ -19678,7 +19678,7 @@ proc GetSysColor*(nIndex: int32): DWORD{.stdcall, dynlib: "user32", importc: "GetSysColor".} proc GetSysColorBrush*(nIndex: int32): HBRUSH{.stdcall, dynlib: "user32", importc: "GetSysColorBrush".} -proc SetSysColors*(cElements: int32, lpaElements: var wINT, +proc SetSysColors*(cElements: int32, lpaElements: var WINT, lpaRgbValues: var COLORREF): WINBOOL{.stdcall, dynlib: "user32", importc: "SetSysColors".} proc DrawFocusRect*(hDC: HDC, lprc: var RECT): WINBOOL{.stdcall, @@ -19878,7 +19878,7 @@ proc CreatePen*(para1: int32, para2: int32, para3: COLORREF): HPEN{.stdcall, dynlib: "gdi32", importc: "CreatePen".} proc CreatePenIndirect*(para1: var LOGPEN): HPEN{.stdcall, dynlib: "gdi32", importc: "CreatePenIndirect".} -proc CreatePolyPolygonRgn*(para1: var POINT, para2: var wINT, para3: int32, +proc CreatePolyPolygonRgn*(para1: var POINT, para2: var WINT, para3: int32, para4: int32): HRGN{.stdcall, dynlib: "gdi32", importc: "CreatePolyPolygonRgn".} proc CreatePatternBrush*(para1: HBITMAP): HBRUSH{.stdcall, dynlib: "gdi32", @@ -20049,7 +20049,7 @@ proc PlayMetaFile*(para1: HDC, para2: HMETAFILE): WINBOOL{.stdcall, dynlib: "gdi32", importc: "PlayMetaFile".} proc PaintRgn*(para1: HDC, para2: HRGN): WINBOOL{.stdcall, dynlib: "gdi32", importc: "PaintRgn".} -proc PolyPolygon*(para1: HDC, para2: var POINT, para3: var wINT, para4: int32): WINBOOL{. +proc PolyPolygon*(para1: HDC, para2: var POINT, para3: var WINT, para4: int32): WINBOOL{. stdcall, dynlib: "gdi32", importc: "PolyPolygon".} proc PtInRegion*(para1: HRGN, para2: int32, para3: int32): WINBOOL{.stdcall, dynlib: "gdi32", importc: "PtInRegion".} @@ -20165,7 +20165,7 @@ proc GetEnhMetaFilePaletteEntries*(para1: HENHMETAFILE, para2: WINUINT, proc GetEnhMetaFileBits*(para1: HENHMETAFILE, para2: WINUINT, para3: LPBYTE): WINUINT{. stdcall, dynlib: "gdi32", importc: "GetEnhMetaFileBits".} proc GetWinMetaFileBits*(para1: HENHMETAFILE, para2: WINUINT, para3: LPBYTE, - para4: wINT, para5: HDC): WINUINT{.stdcall, + para4: WINT, para5: HDC): WINUINT{.stdcall, dynlib: "gdi32", importc: "GetWinMetaFileBits".} proc PlayEnhMetaFile*(para1: HDC, para2: HENHMETAFILE, para3: RECT): WINBOOL{. stdcall, dynlib: "gdi32", importc: "PlayEnhMetaFile".} @@ -20748,7 +20748,7 @@ proc ListView_GetColumn*(wnd: HWND, iCol: int32, col: var LV_COLUMN): LRESULT proc ListView_GetColumnWidth*(wnd: HWND, iCol: int32): LRESULT proc ListView_GetCountPerPage*(hwndLV: HWND): LRESULT proc ListView_GetEditControl*(hwndLV: HWND): LRESULT -proc ListView_GetImageList*(wnd: HWND, iImageList: wINT): LRESULT +proc ListView_GetImageList*(wnd: HWND, iImageList: WINT): LRESULT proc ListView_GetISearchString*(hwndLV: HWND, lpsz: LPTSTR): LRESULT proc ListView_GetItem*(wnd: HWND, item: var LV_ITEM): LRESULT proc ListView_GetItemCount*(wnd: HWND): LRESULT @@ -20867,13 +20867,13 @@ proc CommDlg_OpenSave_HideControl*(hdlg: HWND, id: int32): LRESULT proc CommDlg_OpenSave_SetDefExt*(hdlg: HWND, pszext: LPSTR): LRESULT proc GetNextWindow*(wnd: HWND, uCmd: WINUINT): HWND{.stdcall, dynlib: "user32", importc: "GetWindow".} -proc GlobalAllocPtr*(flags, cb: DWord): Pointer -proc GlobalFreePtr*(lp: Pointer): Pointer -proc GlobalUnlockPtr*(lp: pointer): Pointer -proc GlobalLockPtr*(lp: pointer): Pointer -proc GlobalReAllocPtr*(lp: Pointer, cbNew, flags: DWord): Pointer -proc GlobalPtrHandle*(lp: pointer): Pointer -proc SetLayeredWindowAttributes*(HWND: hwnd, crKey: COLORREF, bAlpha: int8, +proc GlobalAllocPtr*(flags, cb: DWord): pointer +proc GlobalFreePtr*(lp: pointer): pointer +proc GlobalUnlockPtr*(lp: pointer): pointer +proc GlobalLockPtr*(lp: pointer): pointer +proc GlobalReAllocPtr*(lp: pointer, cbNew, flags: DWord): pointer +proc GlobalPtrHandle*(lp: pointer): pointer +proc SetLayeredWindowAttributes*(hwnd: HWND, crKey: COLORREF, bAlpha: int8, dwFlags: DWORD): WINBOOL{.stdcall, dynlib: "user32", importc: "SetLayeredWindowAttributes".} type @@ -21059,21 +21059,21 @@ proc AllocateAndInitializeSid*(pIdentifierAuthority: TSIDIdentifierAuthority, nSubAuthorityCount: int8, nSubAuthority0, nSubAuthority1: DWORD, nSubAuthority2, nSubAuthority3, nSubAuthority4: DWORD, nSubAuthority5, - nSubAuthority6, nSubAuthority7: DWORD, pSid: var Pointer): WINBOOL{.stdcall, + nSubAuthority6, nSubAuthority7: DWORD, pSid: var pointer): WINBOOL{.stdcall, dynlib: "advapi32", importc: "AllocateAndInitializeSid".} proc AllocateLocallyUniqueId*(Luid: var TLargeInteger): WINBOOL{.stdcall, dynlib: "advapi32", importc: "AllocateLocallyUniqueId".} proc BackupRead*(hFile: THandle, lpBuffer: PByte, nNumberOfBytesToRead: DWORD, lpNumberOfBytesRead: var DWORD, bAbort: WINBOOL, - bProcessSecurity: WINBOOL, lpContext: var Pointer): WINBOOL{. + bProcessSecurity: WINBOOL, lpContext: var pointer): WINBOOL{. stdcall, dynlib: "kernel32", importc: "BackupRead".} proc BackupSeek*(hFile: THandle, dwLowBytesToSeek, dwHighBytesToSeek: DWORD, lpdwLowByteSeeked, lpdwHighByteSeeked: var DWORD, - lpContext: Pointer): WINBOOL{.stdcall, dynlib: "kernel32", + lpContext: pointer): WINBOOL{.stdcall, dynlib: "kernel32", importc: "BackupSeek".} proc BackupWrite*(hFile: THandle, lpBuffer: PByte, nNumberOfBytesToWrite: DWORD, lpNumberOfBytesWritten: var DWORD, - bAbort, bProcessSecurity: WINBOOL, lpContext: var Pointer): WINBOOL{. + bAbort, bProcessSecurity: WINBOOL, lpContext: var pointer): WINBOOL{. stdcall, dynlib: "kernel32", importc: "BackupWrite".} proc BeginPaint*(wnd: HWND, lpPaint: var TPaintStruct): HDC{.stdcall, dynlib: "user32", importc: "BeginPaint".} @@ -21098,18 +21098,18 @@ proc CallMsgFilterA*(lpMsg: var TMsg, nCode: int): WINBOOL{.stdcall, dynlib: "user32", importc: "CallMsgFilterA".} proc CallMsgFilterW*(lpMsg: var TMsg, nCode: int): WINBOOL{.stdcall, dynlib: "user32", importc: "CallMsgFilterW".} -proc CallNamedPipe*(lpNamedPipeName: cstring, lpInBuffer: Pointer, - nInBufferSize: DWORD, lpOutBuffer: Pointer, +proc CallNamedPipe*(lpNamedPipeName: cstring, lpInBuffer: pointer, + nInBufferSize: DWORD, lpOutBuffer: pointer, nOutBufferSize: DWORD, lpBytesRead: var DWORD, nTimeOut: DWORD): WINBOOL{.stdcall, dynlib: "kernel32", importc: "CallNamedPipeA".} -proc CallNamedPipeA*(lpNamedPipeName: LPCSTR, lpInBuffer: Pointer, - nInBufferSize: DWORD, lpOutBuffer: Pointer, +proc CallNamedPipeA*(lpNamedPipeName: LPCSTR, lpInBuffer: pointer, + nInBufferSize: DWORD, lpOutBuffer: pointer, nOutBufferSize: DWORD, lpBytesRead: var DWORD, nTimeOut: DWORD): WINBOOL{.stdcall, dynlib: "kernel32", importc: "CallNamedPipeA".} -proc CallNamedPipeW*(lpNamedPipeName: LPWSTR, lpInBuffer: Pointer, - nInBufferSize: DWORD, lpOutBuffer: Pointer, +proc CallNamedPipeW*(lpNamedPipeName: LPWSTR, lpInBuffer: pointer, + nInBufferSize: DWORD, lpOutBuffer: pointer, nOutBufferSize: DWORD, lpBytesRead: var DWORD, nTimeOut: DWORD): WINBOOL{.stdcall, dynlib: "kernel32", importc: "CallNamedPipeW".} @@ -21122,15 +21122,15 @@ proc ChangeDisplaySettingsA*(lpDevMode: var TDeviceModeA, dwFlags: DWORD): int32 stdcall, dynlib: "user32", importc: "ChangeDisplaySettingsA".} proc ChangeDisplaySettingsEx*(lpszDeviceName: cstring, lpDevMode: var TDeviceMode, wnd: HWND, - dwFlags: DWORD, lParam: Pointer): int32{.stdcall, + dwFlags: DWORD, lParam: pointer): int32{.stdcall, dynlib: "user32", importc: "ChangeDisplaySettingsExA".} proc ChangeDisplaySettingsExA*(lpszDeviceName: LPCSTR, lpDevMode: var TDeviceModeA, wnd: HWND, - dwFlags: DWORD, lParam: Pointer): int32{.stdcall, + dwFlags: DWORD, lParam: pointer): int32{.stdcall, dynlib: "user32", importc: "ChangeDisplaySettingsExA".} proc ChangeDisplaySettingsExW*(lpszDeviceName: LPWSTR, lpDevMode: var TDeviceModeW, wnd: HWND, - dwFlags: DWORD, lParam: Pointer): int32{.stdcall, + dwFlags: DWORD, lParam: pointer): int32{.stdcall, dynlib: "user32", importc: "ChangeDisplaySettingsExW".} proc ChangeDisplaySettingsW*(lpDevMode: var TDeviceModeW, dwFlags: DWORD): int32{. stdcall, dynlib: "user32", importc: "ChangeDisplaySettingsW".} @@ -21184,8 +21184,8 @@ proc CreateDialogIndirectParam*(hInstance: HINST, lpTemplate: TDlgTemplate, #function CreateDialogIndirectParamA(hInstance: HINST; const lpTemplate: TDlgTemplate; hWndParent: HWND; lpDialogFunc: TFNDlgProc; dwInitParam: LPARAM): HWND; stdcall; external 'user32' name 'CreateDialogIndirectParamA'; #function CreateDialogIndirectParamW(hInstance: HINST; const lpTemplate: TDlgTemplate; hWndParent: HWND; lpDialogFunc: TFNDlgProc; dwInitParam: LPARAM): HWND; stdcall; external 'user32' name 'CreateDialogIndirectParamW'; #function CreateDIBitmap(DC: HDC; var InfoHeader: TBitmapInfoHeader; dwUsage: DWORD; InitBits: PChar; var InitInfo: TBitmapInfo; wUsage: WINUINT): HBITMAP; stdcall; external 'gdi32' name 'CreateDIBitmap'; - #function CreateDIBPatternBrushPt(const p1: Pointer; p2: WINUINT): HBRUSH; stdcall; external 'gdi32' name 'CreateDIBPatternBrushPt'; - #function CreateDIBSection(DC: HDC; const p2: TBitmapInfo; p3: WINUINT; var p4: Pointer; p5: THandle; p6: DWORD): HBITMAP; stdcall; external 'gdi32' name 'CreateDIBSection'; + #function CreateDIBPatternBrushPt(const p1: pointer; p2: WINUINT): HBRUSH; stdcall; external 'gdi32' name 'CreateDIBPatternBrushPt'; + #function CreateDIBSection(DC: HDC; const p2: TBitmapInfo; p3: WINUINT; var p4: pointer; p5: THandle; p6: DWORD): HBITMAP; stdcall; external 'gdi32' name 'CreateDIBSection'; #function CreateEllipticRgnIndirect(const p1: TRect): HRGN; stdcall; external 'gdi32' name 'CreateEllipticRgnIndirect'; #function CreateFontIndirect(const p1: TLogFont): HFONT;stdcall; external 'gdi32' name 'CreateFontIndirectA'; #function CreateFontIndirectA(const p1: TLogFontA): HFONT; stdcall; external 'gdi32' name 'CreateFontIndirectA'; @@ -21211,39 +21211,39 @@ proc CreatePolyPolygonRgn*(pPtStructs: pointer, pIntArray: pointer, p3, p4: int) proc CreateProcess*(lpApplicationName: cstring, lpCommandLine: cstring, lpProcessAttributes, lpThreadAttributes: PSecurityAttributes, bInheritHandles: WINBOOL, dwCreationFlags: DWORD, - lpEnvironment: Pointer, lpCurrentDirectory: cstring, + lpEnvironment: pointer, lpCurrentDirectory: cstring, lpStartupInfo: TStartupInfo, lpProcessInformation: var TProcessInformation): WINBOOL{. stdcall, dynlib: "kernel32", importc: "CreateProcessA".} proc CreateProcessA*(lpApplicationName: LPCSTR, lpCommandLine: LPCSTR, lpProcessAttributes, lpThreadAttributes: PSecurityAttributes, bInheritHandles: WINBOOL, dwCreationFlags: DWORD, - lpEnvironment: Pointer, lpCurrentDirectory: LPCSTR, + lpEnvironment: pointer, lpCurrentDirectory: LPCSTR, lpStartupInfo: TStartupInfo, lpProcessInformation: var TProcessInformation): WINBOOL{. stdcall, dynlib: "kernel32", importc: "CreateProcessA".} #function CreateProcessAsUser(hToken: THandle; lpApplicationName: PChar; lpCommandLine: PChar; lpProcessAttributes: PSecurityAttributes; lpThreadAttributes: PSecurityAttributes; bInheritHandles: WINBOOL; dwCreationFlags: DWORD; - # lpEnvironment: Pointer; lpCurrentDirectory: PChar; const lpStartupInfo: TStartupInfo; var lpProcessInformation: TProcessInformation): WINBOOL;stdcall; external 'advapi32' name 'CreateProcessAsUserA'; + # lpEnvironment: pointer; lpCurrentDirectory: PChar; const lpStartupInfo: TStartupInfo; var lpProcessInformation: TProcessInformation): WINBOOL;stdcall; external 'advapi32' name 'CreateProcessAsUserA'; #function CreateProcessAsUserA(hToken: THandle; lpApplicationName: LPCSTR; lpCommandLine: LPCSTR; lpProcessAttributes: PSecurityAttributes; lpThreadAttributes: PSecurityAttributes; bInheritHandles: WINBOOL; dwCreationFlags: DWORD; - # lpEnvironment: Pointer; lpCurrentDirectory: LPCSTR; const lpStartupInfo: TStartupInfo; var lpProcessInformation: TProcessInformation): WINBOOL; stdcall; external 'advapi32' name 'CreateProcessAsUserA'; + # lpEnvironment: pointer; lpCurrentDirectory: LPCSTR; const lpStartupInfo: TStartupInfo; var lpProcessInformation: TProcessInformation): WINBOOL; stdcall; external 'advapi32' name 'CreateProcessAsUserA'; #function CreateProcessAsUserW(hToken: THandle; lpApplicationName: LPWSTR; lpCommandLine: LPWSTR; lpProcessAttributes: PSecurityAttributes; lpThreadAttributes: PSecurityAttributes; bInheritHandles: WINBOOL; dwCreationFlags: DWORD; - # lpEnvironment: Pointer; lpCurrentDirectory: LPWSTR; const lpStartupInfo: TStartupInfo; var lpProcessInformation: TProcessInformation): WINBOOL; stdcall; external 'advapi32' name 'CreateProcessAsUserW'; + # lpEnvironment: pointer; lpCurrentDirectory: LPWSTR; const lpStartupInfo: TStartupInfo; var lpProcessInformation: TProcessInformation): WINBOOL; stdcall; external 'advapi32' name 'CreateProcessAsUserW'; proc CreateProcessW*(lpApplicationName: LPWSTR, lpCommandLine: LPWSTR, lpProcessAttributes, lpThreadAttributes: PSecurityAttributes, bInheritHandles: WINBOOL, dwCreationFlags: DWORD, - lpEnvironment: Pointer, lpCurrentDirectory: LPWSTR, + lpEnvironment: pointer, lpCurrentDirectory: LPWSTR, lpStartupInfo: TStartupInfo, lpProcessInformation: var TProcessInformation): WINBOOL{. stdcall, dynlib: "kernel32", importc: "CreateProcessW".} #function CreateRectRgnIndirect(const p1: TRect): HRGN; stdcall; external 'gdi32' name 'CreateRectRgnIndirect'; -proc CreateRemoteThread*(hProcess: THandle, lpThreadAttributes: Pointer, +proc CreateRemoteThread*(hProcess: THandle, lpThreadAttributes: pointer, dwStackSize: DWORD, lpStartAddress: TFNThreadStartRoutine, - lpParameter: Pointer, dwCreationFlags: DWORD, + lpParameter: pointer, dwCreationFlags: DWORD, lpThreadId: var DWORD): THandle{.stdcall, dynlib: "kernel32", importc: "CreateRemoteThread".} -proc CreateThread*(lpThreadAttributes: Pointer, dwStackSize: DWORD, - lpStartAddress: TFNThreadStartRoutine, lpParameter: Pointer, +proc CreateThread*(lpThreadAttributes: pointer, dwStackSize: DWORD, + lpStartAddress: TFNThreadStartRoutine, lpParameter: pointer, dwCreationFlags: DWORD, lpThreadId: var DWORD): THandle{. stdcall, dynlib: "kernel32", importc: "CreateThread".} proc DdeSetQualityOfService*(hWndClient: HWnd, @@ -21256,8 +21256,8 @@ proc DescribePixelFormat*(DC: HDC, p2: int, p3: WINUINT, dynlib: "gdi32", importc: "DescribePixelFormat".} #function DestroyPrivateObjectSecurity(var ObjectDescriptor: PSecurityDescriptor): WINBOOL; stdcall; external 'advapi32' name 'DestroyPrivateObjectSecurity'; proc DeviceIoControl*(hDevice: THandle, dwIoControlCode: DWORD, - lpInBuffer: Pointer, nInBufferSize: DWORD, - lpOutBuffer: Pointer, nOutBufferSize: DWORD, + lpInBuffer: pointer, nInBufferSize: DWORD, + lpOutBuffer: pointer, nOutBufferSize: DWORD, lpBytesReturned: var DWORD, lpOverlapped: POverlapped): WINBOOL{. stdcall, dynlib: "kernel32", importc: "DeviceIoControl".} proc DialogBoxIndirectParam*(hInstance: HINST, lpDialogTemplate: TDlgTemplate, @@ -21311,9 +21311,9 @@ proc DrawTextW*(hDC: HDC, lpString: LPWSTR, nCount: int, lpRect: var TRect, # stdcall; external 'advapi32' name 'DuplicateTokenEx'; proc EndPaint*(wnd: HWND, lpPaint: TPaintStruct): WINBOOL{.stdcall, dynlib: "user32", importc: "EndPaint".} - #function EnumDisplayDevices(Unused: Pointer; iDevNum: DWORD; var lpDisplayDevice: TDisplayDevice; dwFlags: DWORD): WINBOOL;stdcall; external 'user32' name 'EnumDisplayDevicesA'; - #function EnumDisplayDevicesA(Unused: Pointer; iDevNum: DWORD; var lpDisplayDevice: TDisplayDeviceA; dwFlags: DWORD): WINBOOL;stdcall; external 'user32' name 'EnumDisplayDevicesA'; - #function EnumDisplayDevicesW(Unused: Pointer; iDevNum: DWORD; var lpDisplayDevice: TDisplayDeviceW; dwFlags: DWORD): WINBOOL;stdcall; external 'user32' name 'EnumDisplayDevicesW'; + #function EnumDisplayDevices(Unused: pointer; iDevNum: DWORD; var lpDisplayDevice: TDisplayDevice; dwFlags: DWORD): WINBOOL;stdcall; external 'user32' name 'EnumDisplayDevicesA'; + #function EnumDisplayDevicesA(Unused: pointer; iDevNum: DWORD; var lpDisplayDevice: TDisplayDeviceA; dwFlags: DWORD): WINBOOL;stdcall; external 'user32' name 'EnumDisplayDevicesA'; + #function EnumDisplayDevicesW(Unused: pointer; iDevNum: DWORD; var lpDisplayDevice: TDisplayDeviceW; dwFlags: DWORD): WINBOOL;stdcall; external 'user32' name 'EnumDisplayDevicesW'; proc EnumDisplaySettings*(lpszDeviceName: cstring, iModeNum: DWORD, lpDevMode: var TDeviceMode): WINBOOL{.stdcall, dynlib: "user32", importc: "EnumDisplaySettingsA".} @@ -21323,13 +21323,13 @@ proc EnumDisplaySettingsA*(lpszDeviceName: LPCSTR, iModeNum: DWORD, proc EnumDisplaySettingsW*(lpszDeviceName: LPWSTR, iModeNum: DWORD, lpDevMode: var TDeviceModeW): WINBOOL{.stdcall, dynlib: "user32", importc: "EnumDisplaySettingsW".} - #function EnumEnhMetaFile(DC: HDC; p2: HENHMETAFILE; p3: TFNEnhMFEnumProc; p4: Pointer; const p5: TRect): WINBOOL; stdcall; external 'gdi32' name 'EnumEnhMetaFile'; + #function EnumEnhMetaFile(DC: HDC; p2: HENHMETAFILE; p3: TFNEnhMFEnumProc; p4: pointer; const p5: TRect): WINBOOL; stdcall; external 'gdi32' name 'EnumEnhMetaFile'; #function EnumFontFamiliesEx(DC: HDC; var p2: TLogFont; p3: TFNFontEnumProc; p4: LPARAM; p5: DWORD): WINBOOL;stdcall; external 'gdi32' name 'EnumFontFamiliesExA'; #function EnumFontFamiliesExA(DC: HDC; var p2: TLogFontA; p3: TFNFontEnumProcA; p4: LPARAM; p5: DWORD): WINBOOL; stdcall; external 'gdi32' name 'EnumFontFamiliesExA'; #function EnumFontFamiliesExW(DC: HDC; var p2: TLogFontW; p3: TFNFontEnumProcW; p4: LPARAM; p5: DWORD): WINBOOL; stdcall; external 'gdi32' name 'EnumFontFamiliesExW'; #function EqualRect(const lprc1, lprc2: TRect): WINBOOL; stdcall; external 'user32' name 'EqualRect'; proc ExtCreatePen*(PenStyle, Width: DWORD, Brush: TLogBrush, StyleCount: DWORD, - Style: Pointer): HPEN{.stdcall, dynlib: "gdi32", + Style: pointer): HPEN{.stdcall, dynlib: "gdi32", importc: "ExtCreatePen".} proc ExtCreateRegion*(p1: PXForm, p2: DWORD, p3: TRgnData): HRGN{.stdcall, dynlib: "gdi32", importc: "ExtCreateRegion".} @@ -21346,7 +21346,7 @@ proc FillConsoleOutputAttribute*(hConsoleOutput: THandle, wAttribute: int16, nLength: DWORD, dwWriteCoord: TCoord, lpNumberOfAttrsWritten: var DWORD): WINBOOL{. stdcall, dynlib: "kernel32", importc: "FillConsoleOutputAttribute".} -proc FillConsoleOutputCharacter*(hConsoleOutput: THandle, cCharacter: Char, +proc FillConsoleOutputCharacter*(hConsoleOutput: THandle, cCharacter: char, nLength: DWORD, dwWriteCoord: TCoord, lpNumberOfCharsWritten: var DWORD): WINBOOL{. stdcall, dynlib: "kernel32", importc: "FillConsoleOutputCharacterA".} @@ -21365,18 +21365,18 @@ proc FindFirstFileA*(lpFileName: LPCSTR, lpFindFileData: var TWIN32FindDataA): T stdcall, dynlib: "kernel32", importc: "FindFirstFileA".} proc FindFirstFileW*(lpFileName: LPWSTR, lpFindFileData: var TWIN32FindDataW): THandle{. stdcall, dynlib: "kernel32", importc: "FindFirstFileW".} - #function FindFirstFreeAce(var pAcl: TACL; var pAce: Pointer): WINBOOL; stdcall; external 'advapi32' name 'FindFirstFreeAce'; + #function FindFirstFreeAce(var pAcl: TACL; var pAce: pointer): WINBOOL; stdcall; external 'advapi32' name 'FindFirstFreeAce'; proc FindNextFile*(hFindFile: THandle, lpFindFileData: var TWIN32FindData): WINBOOL{. stdcall, dynlib: "kernel32", importc: "FindNextFileA".} proc FindNextFileA*(hFindFile: THandle, lpFindFileData: var TWIN32FindDataA): WINBOOL{. stdcall, dynlib: "kernel32", importc: "FindNextFileA".} proc FindNextFileW*(hFindFile: THandle, lpFindFileData: var TWIN32FindDataW): WINBOOL{. stdcall, dynlib: "kernel32", importc: "FindNextFileW".} - #function FlushInstructionCache(hProcess: THandle; const lpBaseAddress: Pointer; dwSize: DWORD): WINBOOL; stdcall; external 'kernel32' name 'FlushInstructionCache'; - #function FlushViewOfFile(const lpBaseAddress: Pointer; dwNumberOfBytesToFlush: DWORD): WINBOOL; stdcall; external 'kernel32' name 'FlushViewOfFile'; + #function FlushInstructionCache(hProcess: THandle; const lpBaseAddress: pointer; dwSize: DWORD): WINBOOL; stdcall; external 'kernel32' name 'FlushInstructionCache'; + #function FlushViewOfFile(const lpBaseAddress: pointer; dwNumberOfBytesToFlush: DWORD): WINBOOL; stdcall; external 'kernel32' name 'FlushViewOfFile'; #function FrameRect(hDC: HDC; const lprc: TRect; hbr: HBRUSH): Integer; stdcall; external 'user32' name 'FrameRect'; - #function GetAce(const pAcl: TACL; dwAceIndex: DWORD; var pAce: Pointer): WINBOOL; stdcall; external 'advapi32' name 'GetAce'; - #function GetAclInformation(const pAcl: TACL; pAclInformation: Pointer; nAclInformationLength: DWORD; dwAclInformationClass: TAclInformationClass): WINBOOL; stdcall; external 'advapi32' name 'GetAclInformation'; + #function GetAce(const pAcl: TACL; dwAceIndex: DWORD; var pAce: pointer): WINBOOL; stdcall; external 'advapi32' name 'GetAce'; + #function GetAclInformation(const pAcl: TACL; pAclInformation: pointer; nAclInformationLength: DWORD; dwAclInformationClass: TAclInformationClass): WINBOOL; stdcall; external 'advapi32' name 'GetAclInformation'; #function GetAltTabInfo(wnd: HWND; iItem: Integer; var pati: TAltTabInfo; pszItemText: PChar; cchItemText: WINUINT): WINBOOL;stdcall; external 'user32' name 'GetAltTabInfoA'; #function GetAltTabInfoA(wnd: HWND; iItem: Integer; var pati: TAltTabInfo; pszItemText: LPCSTR; cchItemText: WINUINT): WINBOOL;stdcall; external 'user32' name 'GetAltTabInfoA'; #function GetAltTabInfoW(wnd: HWND; iItem: Integer; var pati: TAltTabInfo; pszItemText: LPWSTR; cchItemText: WINUINT): WINBOOL;stdcall; external 'user32' name 'GetAltTabInfoW'; @@ -21507,7 +21507,7 @@ proc GetDefaultCommConfigW*(lpszName: LPWSTR, lpCC: var TCommConfig, proc GetDIBColorTable*(DC: HDC, p2, p3: WINUINT, RGBQuadStructs: pointer): WINUINT{. stdcall, dynlib: "gdi32", importc: "GetDIBColorTable".} proc GetDIBits*(DC: HDC, Bitmap: HBitmap, StartScan, NumScans: WINUINT, - Bits: Pointer, BitInfo: var TBitmapInfo, Usage: WINUINT): int{. + Bits: pointer, BitInfo: var TBitmapInfo, Usage: WINUINT): int{. stdcall, dynlib: "gdi32", importc: "GetDIBits".} proc GetDiskFreeSpace*(lpRootPathName: cstring, lpSectorsPerCluster, lpBytesPerSector, lpNumberOfFreeClusters, lpTotalNumberOfClusters: var DWORD): WINBOOL{. @@ -21531,13 +21531,13 @@ proc GetDiskFreeSpaceW*(lpRootPathName: LPWSTR, lpSectorsPerCluster, lpBytesPerSector, lpNumberOfFreeClusters, lpTotalNumberOfClusters: var DWORD): WINBOOL{. stdcall, dynlib: "kernel32", importc: "GetDiskFreeSpaceW".} proc GetDiskFreeSpaceEx*(lpDirectoryName: cstring, lpFreeBytesAvailableToCaller, - lpTotalNumberOfBytes: pLargeInteger, lpTotalNumberOfFreeBytes: PLargeInteger): WINBOOL{. + lpTotalNumberOfBytes: PLargeInteger, lpTotalNumberOfFreeBytes: PLargeInteger): WINBOOL{. stdcall, dynlib: "kernel32", importc: "GetDiskFreeSpaceExA".} proc GetDiskFreeSpaceExA*(lpDirectoryName: LPCSTR, lpFreeBytesAvailableToCaller, - lpTotalNumberOfBytes: pLargeInteger, lpTotalNumberOfFreeBytes: PLargeInteger): WINBOOL{. + lpTotalNumberOfBytes: PLargeInteger, lpTotalNumberOfFreeBytes: PLargeInteger): WINBOOL{. stdcall, dynlib: "kernel32", importc: "GetDiskFreeSpaceExA".} proc GetDiskFreeSpaceExW*(lpDirectoryName: LPWSTR, lpFreeBytesAvailableToCaller, - lpTotalNumberOfBytes: pLargeInteger, lpTotalNumberOfFreeBytes: PLargeInteger): WINBOOL{. + lpTotalNumberOfBytes: PLargeInteger, lpTotalNumberOfFreeBytes: PLargeInteger): WINBOOL{. stdcall, dynlib: "kernel32", importc: "GetDiskFreeSpaceExW".} #function GetEnhMetaFilePixelFormat(p1: HENHMETAFILE; p2: Cardinal; var p3: TPixelFormatDescriptor): WINUINT;stdcall; external 'gdi32' name 'GetEnhMetaFilePixelFormat'; proc GetExitCodeProcess*(hProcess: THandle, lpExitCode: var DWORD): WINBOOL{. @@ -21560,13 +21560,13 @@ proc GetFileVersionInfoSizeW*(lptstrFilename: LPWSTR, lpdwHandle: var DWORD): DW # function GetFullPathNameA(lpFileName: LPCSTR; nBufferLength: DWORD; lpBuffer: LPCSTR; var lpFilePart: LPCSTR): DWORD; stdcall; external 'kernel32' name 'GetFullPathNameA'; # function GetFullPathNameW(lpFileName: LPWSTR; nBufferLength: DWORD; lpBuffer: LPWSTR; var lpFilePart: LPWSTR): DWORD; stdcall; external 'kernel32' name 'GetFullPathNameW'; proc GetGlyphOutline*(DC: HDC, p2, p3: WINUINT, p4: TGlyphMetrics, p5: DWORD, - p6: Pointer, p7: TMat2): DWORD{.stdcall, dynlib: "gdi32", + p6: pointer, p7: TMat2): DWORD{.stdcall, dynlib: "gdi32", importc: "GetGlyphOutlineA".} proc GetGlyphOutlineA*(DC: HDC, p2, p3: WINUINT, p4: TGlyphMetrics, p5: DWORD, - p6: Pointer, p7: TMat2): DWORD{.stdcall, dynlib: "gdi32", + p6: pointer, p7: TMat2): DWORD{.stdcall, dynlib: "gdi32", importc: "GetGlyphOutlineA".} proc GetGlyphOutlineW*(DC: HDC, p2, p3: WINUINT, p4: TGlyphMetrics, p5: DWORD, - p6: Pointer, p7: TMat2): DWORD{.stdcall, dynlib: "gdi32", + p6: pointer, p7: TMat2): DWORD{.stdcall, dynlib: "gdi32", importc: "GetGlyphOutlineW".} #function GetGUIThreadInfo(idThread: DWORD; var pgui: TGUIThreadinfo): WINBOOL;stdcall; external 'user32' name 'GetGUIThreadInfo'; proc GetHandleInformation*(hObject: THandle, lpdwFlags: var DWORD): WINBOOL{. @@ -21600,9 +21600,9 @@ proc GetLogColorSpaceA*(p1: HCOLORSPACE, ColorSpace: var TLogColorSpaceA, Size: DWORD): WINBOOL{.stdcall, dynlib: "gdi32", importc: "GetLogColorSpaceA".} #function GetLogColorSpaceW(p1: HCOLORSPACE; var ColorSpace: TLogColorSpaceW; Size: DWORD): WINBOOL; stdcall; external 'gdi32' name 'GetLogColorSpaceW'; -proc GetMailslotInfo*(hMailslot: THandle, lpMaxMessageSize: Pointer, +proc GetMailslotInfo*(hMailslot: THandle, lpMaxMessageSize: pointer, lpNextSize: var DWORD, - lpMessageCount, lpReadTimeout: Pointer): WINBOOL{.stdcall, + lpMessageCount, lpReadTimeout: pointer): WINBOOL{.stdcall, dynlib: "kernel32", importc: "GetMailslotInfo".} #function GetMenuBarInfo(hend: HWND; idObject, idItem: Longint; var pmbi: TMenuBarInfo): WINBOOL;stdcall; external 'user32' name 'GetMenuBarInfo'; #function GetMenuInfo(menu: HMENU; var lpmi: TMenuInfo): WINBOOL;stdcall; external 'user32' name 'GetMenuInfo'; @@ -21625,7 +21625,7 @@ proc GetMiterLimit*(DC: HDC, Limit: var float32): WINBOOL{.stdcall, dynlib: "gdi32", importc: "GetMiterLimit".} #function GetMouseMovePoints(cbSize: WINUINT; var lppt, lpptBuf: TMouseMovePoint; nBufPoints: Integer; resolution: DWORD): Integer;stdcall; external 'user32' name 'GetMouseMovePoints'; proc GetNamedPipeInfo*(hNamedPipe: THandle, lpFlags: var DWORD, - lpOutBufferSize, lpInBufferSize, lpMaxInstances: Pointer): WINBOOL{. + lpOutBufferSize, lpInBufferSize, lpMaxInstances: pointer): WINBOOL{. stdcall, dynlib: "kernel32", importc: "GetNamedPipeInfo".} proc GetNumberOfConsoleInputEvents*(hConsoleInput: THandle, lpNumberOfEvents: var DWORD): WINBOOL{. @@ -21738,11 +21738,11 @@ proc GetTabbedTextExtentW*(hDC: HDC, lpString: LPWSTR, lpnTabStopPositions: pointer): DWORD{.stdcall, dynlib: "user32", importc: "GetTabbedTextExtentW".} proc GetTapeParameters*(hDevice: THandle, dwOperation: DWORD, - lpdwSize: var DWORD, lpTapeInformation: Pointer): DWORD{. + lpdwSize: var DWORD, lpTapeInformation: pointer): DWORD{. stdcall, dynlib: "kernel32", importc: "GetTapeParameters".} proc GetTapePosition*(hDevice: THandle, dwPositionType: DWORD, lpdwPartition, lpdwOffsetLow: var DWORD, - lpdwOffsetHigh: Pointer): DWORD{.stdcall, + lpdwOffsetHigh: pointer): DWORD{.stdcall, dynlib: "kernel32", importc: "GetTapePosition".} proc GetTextExtentExPoint*(DC: HDC, p2: cstring, p3, p4: int, p5, p6: PInteger, p7: var TSize): WINBOOL{.stdcall, dynlib: "gdi32", @@ -21784,7 +21784,7 @@ proc GetThreadTimes*(hThread: THandle, lpCreationTime, lpExitTime, lpKernelTime, proc GetTimeZoneInformation*(lpTimeZoneInformation: var TTimeZoneInformation): DWORD{. stdcall, dynlib: "kernel32", importc: "GetTimeZoneInformation".} #function GetTitleBarInfo(wnd: HWND; var pti: TTitleBarInfo): WINBOOL;stdcall; external 'user32' name 'GetTitleBarInfo'; - #function GetTokenInformation(TokenHandle: THandle; TokenInformationClass: TTokenInformationClass; TokenInformation: Pointer; TokenInformationLength: DWORD; var ReturnLength: DWORD): WINBOOL; stdcall; external 'advapi32' name 'GetTokenInformation'; + #function GetTokenInformation(TokenHandle: THandle; TokenInformationClass: TTokenInformationClass; TokenInformation: pointer; TokenInformationLength: DWORD; var ReturnLength: DWORD): WINBOOL; stdcall; external 'advapi32' name 'GetTokenInformation'; proc GetUpdateRect*(wnd: HWND, lpRect: var TRect, bErase: WINBOOL): WINBOOL{. stdcall, dynlib: "user32", importc: "GetUpdateRect".} proc GetUserName*(lpBuffer: cstring, nSize: var DWORD): WINBOOL{.stdcall, @@ -21793,13 +21793,13 @@ proc GetUserNameA*(lpBuffer: LPCSTR, nSize: var DWORD): WINBOOL{.stdcall, dynlib: "advapi32", importc: "GetUserNameA".} proc GetUserNameW*(lpBuffer: LPWSTR, nSize: var DWORD): WINBOOL{.stdcall, dynlib: "advapi32", importc: "GetUserNameW".} -proc GetUserObjectInformation*(hObj: THandle, nIndex: int, pvInfo: Pointer, +proc GetUserObjectInformation*(hObj: THandle, nIndex: int, pvInfo: pointer, nLength: DWORD, lpnLengthNeeded: var DWORD): WINBOOL{. stdcall, dynlib: "user32", importc: "GetUserObjectInformationA".} -proc GetUserObjectInformationA*(hObj: THandle, nIndex: int, pvInfo: Pointer, +proc GetUserObjectInformationA*(hObj: THandle, nIndex: int, pvInfo: pointer, nLength: DWORD, lpnLengthNeeded: var DWORD): WINBOOL{. stdcall, dynlib: "user32", importc: "GetUserObjectInformationA".} -proc GetUserObjectInformationW*(hObj: THandle, nIndex: int, pvInfo: Pointer, +proc GetUserObjectInformationW*(hObj: THandle, nIndex: int, pvInfo: pointer, nLength: DWORD, lpnLengthNeeded: var DWORD): WINBOOL{. stdcall, dynlib: "user32", importc: "GetUserObjectInformationW".} proc GetUserObjectSecurity*(hObj: THandle, pSIRequested: var DWORD, @@ -21845,7 +21845,7 @@ proc GetWindowRect*(wnd: HWND, lpRect: var TRect): WINBOOL{.stdcall, dynlib: "user32", importc: "GetWindowRect".} proc GetWorldTransform*(DC: HDC, p2: var TXForm): WINBOOL{.stdcall, dynlib: "gdi32", importc: "GetWorldTransform".} - #function GradientFill(DC: HDC; var p2: TTriVertex; p3: ULONG; p4: Pointer; p5, p6: ULONG): WINBOOL;stdcall; external 'gdi32' name 'GradientFill'; + #function GradientFill(DC: HDC; var p2: TTriVertex; p3: ULONG; p4: pointer; p5, p6: ULONG): WINBOOL;stdcall; external 'gdi32' name 'GradientFill'; proc GlobalMemoryStatus*(Buffer: var MEMORYSTATUS){.stdcall, dynlib: "kernel32", importc: "GlobalMemoryStatus".} proc HeapWalk*(hHeap: THandle, lpEntry: var TProcessHeapEntry): WINBOOL{. @@ -21856,7 +21856,7 @@ proc InflateRect*(lprc: var TRect, dx, dy: int): WINBOOL{.stdcall, dynlib: "user32", importc: "InflateRect".} proc InitializeAcl*(pAcl: var TACL, nAclLength, dwAclRevision: DWORD): WINBOOL{. stdcall, dynlib: "advapi32", importc: "InitializeAcl".} -proc InitializeSid*(Sid: Pointer, pIdentifierAuthority: TSIDIdentifierAuthority, +proc InitializeSid*(Sid: pointer, pIdentifierAuthority: TSIDIdentifierAuthority, nSubAuthorityCount: int8): WINBOOL{.stdcall, dynlib: "advapi32", importc: "InitializeSid".} proc InsertMenuItemA*(p1: HMENU, p2: WINUINT, p3: WINBOOL, p4: TMenuItemInfoA): WINBOOL{. @@ -21958,7 +21958,7 @@ proc MakeAbsoluteSD*(pSelfRelativeSecurityDescriptor: PSecurityDescriptor, pDacl: var TACL, lpdwDaclSize: var DWORD, pSacl: var TACL, lpdwSaclSize: var DWORD, pOwner: PSID, - lpdwOwnerSize: var DWORD, pPrimaryGroup: Pointer, + lpdwOwnerSize: var DWORD, pPrimaryGroup: pointer, lpdwPrimaryGroupSize: var DWORD): WINBOOL{.stdcall, dynlib: "advapi32", importc: "MakeAbsoluteSD".} proc MakeSelfRelativeSD*(pAbsoluteSecurityDescriptor: PSecurityDescriptor, @@ -21983,7 +21983,7 @@ proc MsgWaitForMultipleObjectsEx*(nCount: DWORD, pHandles: pointer, dwMilliseconds, dwWakeMask, dwFlags: DWORD): DWORD{. stdcall, dynlib: "user32", importc: "MsgWaitForMultipleObjectsEx".} # function MultiByteToWideChar(CodePage: WINUINT; dwFlags: DWORD; const lpMultiByteStr: LPCSTR; cchMultiByte: Integer; lLPWSTRStr: LPWSTR; cchWideChar: Integer): Integer; stdcall; external 'kernel32' name 'MultiByteToWideChar'; -proc ObjectOpenAuditAlarm*(SubsystemName: cstring, HandleId: Pointer, +proc ObjectOpenAuditAlarm*(SubsystemName: cstring, HandleId: pointer, ObjectTypeName: cstring, ObjectName: cstring, pSecurityDescriptor: PSecurityDescriptor, ClientToken: THandle, @@ -21992,7 +21992,7 @@ proc ObjectOpenAuditAlarm*(SubsystemName: cstring, HandleId: Pointer, ObjectCreation, AccessGranted: WINBOOL, GenerateOnClose: var WINBOOL): WINBOOL{.stdcall, dynlib: "advapi32", importc: "ObjectOpenAuditAlarmA".} -proc ObjectOpenAuditAlarmA*(SubsystemName: LPCSTR, HandleId: Pointer, +proc ObjectOpenAuditAlarmA*(SubsystemName: LPCSTR, HandleId: pointer, ObjectTypeName: LPCSTR, ObjectName: LPCSTR, pSecurityDescriptor: PSecurityDescriptor, ClientToken: THandle, @@ -22001,7 +22001,7 @@ proc ObjectOpenAuditAlarmA*(SubsystemName: LPCSTR, HandleId: Pointer, ObjectCreation, AccessGranted: WINBOOL, GenerateOnClose: var WINBOOL): WINBOOL{.stdcall, dynlib: "advapi32", importc: "ObjectOpenAuditAlarmA".} -proc ObjectOpenAuditAlarmW*(SubsystemName: LPWSTR, HandleId: Pointer, +proc ObjectOpenAuditAlarmW*(SubsystemName: LPWSTR, HandleId: pointer, ObjectTypeName: LPWSTR, ObjectName: LPWSTR, pSecurityDescriptor: PSecurityDescriptor, ClientToken: THandle, @@ -22010,17 +22010,17 @@ proc ObjectOpenAuditAlarmW*(SubsystemName: LPWSTR, HandleId: Pointer, ObjectCreation, AccessGranted: WINBOOL, GenerateOnClose: var WINBOOL): WINBOOL{.stdcall, dynlib: "advapi32", importc: "ObjectOpenAuditAlarmW".} -proc ObjectPrivilegeAuditAlarm*(SubsystemName: cstring, HandleId: Pointer, +proc ObjectPrivilegeAuditAlarm*(SubsystemName: cstring, HandleId: pointer, ClientToken: THandle, DesiredAccess: DWORD, Privileges: var TPrivilegeSet, AccessGranted: WINBOOL): WINBOOL{.stdcall, dynlib: "advapi32", importc: "ObjectPrivilegeAuditAlarmA".} -proc ObjectPrivilegeAuditAlarmA*(SubsystemName: LPCSTR, HandleId: Pointer, +proc ObjectPrivilegeAuditAlarmA*(SubsystemName: LPCSTR, HandleId: pointer, ClientToken: THandle, DesiredAccess: DWORD, Privileges: var TPrivilegeSet, AccessGranted: WINBOOL): WINBOOL{.stdcall, dynlib: "advapi32", importc: "ObjectPrivilegeAuditAlarmA".} -proc ObjectPrivilegeAuditAlarmW*(SubsystemName: LPWSTR, HandleId: Pointer, +proc ObjectPrivilegeAuditAlarmW*(SubsystemName: LPWSTR, HandleId: pointer, ClientToken: THandle, DesiredAccess: DWORD, Privileges: var TPrivilegeSet, AccessGranted: WINBOOL): WINBOOL{.stdcall, @@ -22111,21 +22111,21 @@ proc QueryPerformanceCounter*(lpPerformanceCount: var TLargeInteger): WINBOOL{. stdcall, dynlib: "kernel32", importc: "QueryPerformanceCounter".} proc QueryPerformanceFrequency*(lpFrequency: var TLargeInteger): WINBOOL{. stdcall, dynlib: "kernel32", importc: "QueryPerformanceFrequency".} - #function QueryRecoveryAgents(p1: PChar; var p2: Pointer; var p3: TRecoveryAgentInformation): DWORD;stdcall; external 'kernel32' name 'QueryRecoveryAgentsA'; - #function QueryRecoveryAgentsA(p1: LPCSTR; var p2: Pointer; var p3: TRecoveryAgentInformationA): DWORD;stdcall; external 'kernel32' name 'QueryRecoveryAgentsA'; - #function QueryRecoveryAgentsW(p1: LPWSTR; var p2: Pointer; var p3: TRecoveryAgentInformationW): DWORD;stdcall; external 'kernel32' name 'QueryRecoveryAgentsW'; + #function QueryRecoveryAgents(p1: PChar; var p2: pointer; var p3: TRecoveryAgentInformation): DWORD;stdcall; external 'kernel32' name 'QueryRecoveryAgentsA'; + #function QueryRecoveryAgentsA(p1: LPCSTR; var p2: pointer; var p3: TRecoveryAgentInformationA): DWORD;stdcall; external 'kernel32' name 'QueryRecoveryAgentsA'; + #function QueryRecoveryAgentsW(p1: LPWSTR; var p2: pointer; var p3: TRecoveryAgentInformationW): DWORD;stdcall; external 'kernel32' name 'QueryRecoveryAgentsW'; proc RaiseException*(dwExceptionCode: DWORD, dwExceptionFlags: DWORD, nNumberOfArguments: DWORD, lpArguments: var DWORD){. stdcall, dynlib: "kernel32", importc: "RaiseException".} proc UnhandledExceptionFilter*(ExceptionInfo: var emptyrecord): LONG{.stdcall, dynlib: "kernel32", importc: "UnhandledExceptionFilter".} -proc ReadConsole*(hConsoleInput: THandle, lpBuffer: Pointer, +proc ReadConsole*(hConsoleInput: THandle, lpBuffer: pointer, nNumberOfCharsToRead: DWORD, lpNumberOfCharsRead: var DWORD, - lpReserved: Pointer): WINBOOL{.stdcall, dynlib: "kernel32", + lpReserved: pointer): WINBOOL{.stdcall, dynlib: "kernel32", importc: "ReadConsoleA".} -proc ReadConsoleA*(hConsoleInput: THandle, lpBuffer: Pointer, +proc ReadConsoleA*(hConsoleInput: THandle, lpBuffer: pointer, nNumberOfCharsToRead: DWORD, lpNumberOfCharsRead: var DWORD, - lpReserved: Pointer): WINBOOL{.stdcall, dynlib: "kernel32", + lpReserved: pointer): WINBOOL{.stdcall, dynlib: "kernel32", importc: "ReadConsoleA".} proc ReadConsoleInput*(hConsoleInput: THandle, lpBuffer: var TInputRecord, nLength: DWORD, lpNumberOfEventsRead: var DWORD): WINBOOL{. @@ -22136,15 +22136,15 @@ proc ReadConsoleInputA*(hConsoleInput: THandle, lpBuffer: var TInputRecord, proc ReadConsoleInputW*(hConsoleInput: THandle, lpBuffer: var TInputRecord, nLength: DWORD, lpNumberOfEventsRead: var DWORD): WINBOOL{. stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".} -proc ReadConsoleOutput*(hConsoleOutput: THandle, lpBuffer: Pointer, +proc ReadConsoleOutput*(hConsoleOutput: THandle, lpBuffer: pointer, dwBufferSize, dwBufferCoord: TCoord, lpReadRegion: var TSmallRect): WINBOOL{.stdcall, dynlib: "kernel32", importc: "ReadConsoleOutputA".} -proc ReadConsoleOutputA*(hConsoleOutput: THandle, lpBuffer: Pointer, +proc ReadConsoleOutputA*(hConsoleOutput: THandle, lpBuffer: pointer, dwBufferSize, dwBufferCoord: TCoord, lpReadRegion: var TSmallRect): WINBOOL{.stdcall, dynlib: "kernel32", importc: "ReadConsoleOutputA".} -proc ReadConsoleOutputAttribute*(hConsoleOutput: THandle, lpAttribute: Pointer, +proc ReadConsoleOutputAttribute*(hConsoleOutput: THandle, lpAttribute: pointer, nLength: DWORD, dwReadCoord: TCoord, lpNumberOfAttrsRead: var DWORD): WINBOOL{. stdcall, dynlib: "kernel32", importc: "ReadConsoleOutputAttribute".} @@ -22160,31 +22160,31 @@ proc ReadConsoleOutputCharacterW*(hConsoleOutput: THandle, lpCharacter: LPCSTR, nLength: DWORD, dwReadCoord: TCoord, lpNumberOfCharsRead: var DWORD): WINBOOL{. stdcall, dynlib: "kernel32", importc: "ReadConsoleOutputCharacterW".} -proc ReadConsoleOutputW*(hConsoleOutput: THandle, lpBuffer: Pointer, +proc ReadConsoleOutputW*(hConsoleOutput: THandle, lpBuffer: pointer, dwBufferSize, dwBufferCoord: TCoord, lpReadRegion: var TSmallRect): WINBOOL{.stdcall, dynlib: "kernel32", importc: "ReadConsoleOutputW".} -proc ReadConsoleW*(hConsoleInput: THandle, lpBuffer: Pointer, +proc ReadConsoleW*(hConsoleInput: THandle, lpBuffer: pointer, nNumberOfCharsToRead: DWORD, lpNumberOfCharsRead: var DWORD, - lpReserved: Pointer): WINBOOL{.stdcall, dynlib: "kernel32", + lpReserved: pointer): WINBOOL{.stdcall, dynlib: "kernel32", importc: "ReadConsoleW".} proc ReadEventLog*(hEventLog: THandle, dwReadFlags, dwRecordOffset: DWORD, - lpBuffer: Pointer, nNumberOfBytesToRead: DWORD, + lpBuffer: pointer, nNumberOfBytesToRead: DWORD, pnBytesRead, pnMinNumberOfBytesNeeded: var DWORD): WINBOOL{. stdcall, dynlib: "advapi32", importc: "ReadEventLogA".} proc ReadEventLogA*(hEventLog: THandle, dwReadFlags, dwRecordOffset: DWORD, - lpBuffer: Pointer, nNumberOfBytesToRead: DWORD, + lpBuffer: pointer, nNumberOfBytesToRead: DWORD, pnBytesRead, pnMinNumberOfBytesNeeded: var DWORD): WINBOOL{. stdcall, dynlib: "advapi32", importc: "ReadEventLogA".} proc ReadEventLogW*(hEventLog: THandle, dwReadFlags, dwRecordOffset: DWORD, - lpBuffer: Pointer, nNumberOfBytesToRead: DWORD, + lpBuffer: pointer, nNumberOfBytesToRead: DWORD, pnBytesRead, pnMinNumberOfBytesNeeded: var DWORD): WINBOOL{. stdcall, dynlib: "advapi32", importc: "ReadEventLogW".} proc ReadFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToRead: DWORD, lpNumberOfBytesRead: var DWORD, lpOverlapped: POverlapped): WINBOOL{. stdcall, dynlib: "kernel32", importc: "ReadFile".} -proc ReadProcessMemory*(hProcess: THandle, lpBaseAddress: Pointer, - lpBuffer: Pointer, nSize: DWORD, +proc ReadProcessMemory*(hProcess: THandle, lpBaseAddress: pointer, + lpBuffer: pointer, nSize: DWORD, lpNumberOfBytesRead: var DWORD): WINBOOL{.stdcall, dynlib: "kernel32", importc: "ReadProcessMemory".} #function RectInRegion(RGN: HRGN; const p2: TRect): WINBOOL; stdcall; external 'gdi32' name 'RectInRegion'; @@ -22217,27 +22217,27 @@ proc RegCreateKeyExW*(key: HKEY, lpSubKey: LPWSTR, Reserved: DWORD, proc RegCreateKeyW*(key: HKEY, lpSubKey: LPWSTR, phkResult: var HKEY): int32{. stdcall, dynlib: "advapi32", importc: "RegCreateKeyW".} proc RegEnumKeyEx*(key: HKEY, dwIndex: DWORD, lpName: cstring, - lpcbName: var DWORD, lpReserved: Pointer, lpClass: cstring, + lpcbName: var DWORD, lpReserved: pointer, lpClass: cstring, lpcbClass: PDWORD, lpftLastWriteTime: PFileTime): int32{. stdcall, dynlib: "advapi32", importc: "RegEnumKeyExA".} proc RegEnumKeyExA*(key: HKEY, dwIndex: DWORD, lpName: LPCSTR, - lpcbName: var DWORD, lpReserved: Pointer, lpClass: LPCSTR, + lpcbName: var DWORD, lpReserved: pointer, lpClass: LPCSTR, lpcbClass: PDWORD, lpftLastWriteTime: PFileTime): int32{. stdcall, dynlib: "advapi32", importc: "RegEnumKeyExA".} proc RegEnumKeyExW*(key: HKEY, dwIndex: DWORD, lpName: LPWSTR, - lpcbName: var DWORD, lpReserved: Pointer, lpClass: LPWSTR, + lpcbName: var DWORD, lpReserved: pointer, lpClass: LPWSTR, lpcbClass: PDWORD, lpftLastWriteTime: PFileTime): int32{. stdcall, dynlib: "advapi32", importc: "RegEnumKeyExW".} proc RegEnumValue*(key: HKEY, dwIndex: DWORD, lpValueName: cstring, - lpcbValueName: var DWORD, lpReserved: Pointer, + lpcbValueName: var DWORD, lpReserved: pointer, lpType: PDWORD, lpData: PByte, lpcbData: PDWORD): int32{. stdcall, dynlib: "advapi32", importc: "RegEnumValueA".} proc RegEnumValueA*(key: HKEY, dwIndex: DWORD, lpValueName: cstring, - lpcbValueName: var DWORD, lpReserved: Pointer, + lpcbValueName: var DWORD, lpReserved: pointer, lpType: PDWORD, lpData: PByte, lpcbData: PDWORD): int32{. stdcall, dynlib: "advapi32", importc: "RegEnumValueA".} proc RegEnumValueW*(key: HKEY, dwIndex: DWORD, lpValueName: cstring, - lpcbValueName: var DWORD, lpReserved: Pointer, + lpcbValueName: var DWORD, lpReserved: pointer, lpType: PDWORD, lpData: PByte, lpcbData: PDWORD): int32{. stdcall, dynlib: "advapi32", importc: "RegEnumValueW".} proc RegGetKeySecurity*(key: HKEY, SecurityInformation: SECURITY_INFORMATION, @@ -22324,10 +22324,10 @@ proc ScrollConsoleScreenBufferW*(hConsoleOutput: THandle, lpFill: var TCharInfo): WINBOOL{.stdcall, dynlib: "kernel32", importc: "ScrollConsoleScreenBufferW".} proc ScrollWindow*(wnd: HWND, XAmount: int32, YAmount: int32, rect: LPRECT, - lpClipRect: lpRECT): WINBOOL{.stdcall, dynlib: "user32", + lpClipRect: LPRECT): WINBOOL{.stdcall, dynlib: "user32", importc: "ScrollWindow".} -proc ScrollWindowEx*(wnd: HWND, dx: int32, dy: int32, prcScroll: lpRECT, - prcClip: lpRECT, hrgnUpdate: HRGN, prcUpdate: LPRECT, +proc ScrollWindowEx*(wnd: HWND, dx: int32, dy: int32, prcScroll: LPRECT, + prcClip: LPRECT, hrgnUpdate: HRGN, prcUpdate: LPRECT, flags: WINUINT): int32{.stdcall, dynlib: "user32", importc: "ScrollWindowEx".} #function ScrollDC(DC: HDC; DX, DY: Integer; var Scroll, Clip: TRect; Rgn: HRGN; Update: PRect): WINBOOL; stdcall; external 'user32' name 'ScrollDC'; @@ -22344,7 +22344,7 @@ proc SendMessageTimeoutA*(wnd: HWND, Msg: WINUINT, wp: WPARAM, lp: LPARAM, proc SendMessageTimeoutW*(wnd: HWND, Msg: WINUINT, wp: WPARAM, lp: LPARAM, fuFlags, uTimeout: WINUINT, lpdwResult: var DWORD): LRESULT{. stdcall, dynlib: "user32", importc: "SendMessageTimeoutW".} - #function SetAclInformation(var pAcl: TACL; pAclInformation: Pointer; nAclInformationLength: DWORD; dwAclInformationClass: TAclInformationClass): WINBOOL; stdcall; external 'advapi32' name 'SetAclInformation'; + #function SetAclInformation(var pAcl: TACL; pAclInformation: pointer; nAclInformationLength: DWORD; dwAclInformationClass: TAclInformationClass): WINBOOL; stdcall; external 'advapi32' name 'SetAclInformation'; #function SetColorAdjustment(DC: HDC; const p2: TColorAdjustment): WINBOOL; stdcall; external 'gdi32' name 'SetColorAdjustment'; proc SetCommConfig*(hCommDev: THandle, lpCC: TCommConfig, dwSize: DWORD): WINBOOL{. stdcall, dynlib: "kernel32", importc: "SetCommConfig".} @@ -22359,9 +22359,9 @@ proc SetConsoleCursorInfo*(hConsoleOutput: THandle, proc SetDIBColorTable*(DC: HDC, p2, p3: WINUINT, RGBQuadSTructs: pointer): WINUINT{. stdcall, dynlib: "gdi32", importc: "SetDIBColorTable".} proc SetDIBits*(DC: HDC, Bitmap: HBITMAP, StartScan, NumScans: WINUINT, - Bits: Pointer, BitsInfo: var TBitmapInfo, Usage: WINUINT): int{. + Bits: pointer, BitsInfo: var TBitmapInfo, Usage: WINUINT): int{. stdcall, dynlib: "gdi32", importc: "SetDIBits".} - #function SetDIBitsToDevice(DC: HDC; DestX, DestY: Integer; Width, Height: DWORD; SrcX, SrcY: Integer; nStartScan, NumScans: WINUINT; Bits: Pointer; var BitsInfo: TBitmapInfo; Usage: WINUINT): Integer; stdcall; external 'gdi32' name 'SetDIBitsToDevice'; + #function SetDIBitsToDevice(DC: HDC; DestX, DestY: Integer; Width, Height: DWORD; SrcX, SrcY: Integer; nStartScan, NumScans: WINUINT; Bits: pointer; var BitsInfo: TBitmapInfo; Usage: WINUINT): Integer; stdcall; external 'gdi32' name 'SetDIBitsToDevice'; proc SetEnhMetaFileBits*(para1: WINUINT, para2: pointer): HENHMETAFILE{.stdcall, dynlib: "gdi32", importc: "SetEnhMetaFileBits".} proc SetFileTime*(hFile: HANDLE, lpCreationTime: var FILETIME, @@ -22378,7 +22378,7 @@ proc SetMenuItemInfoA*(p1: HMENU, p2: WINUINT, p3: WINBOOL, p4: TMenuItemInfoA): proc SetMetaFileBitsEx*(p1: WINUINT, p2: cstring): HMETAFILE{.stdcall, dynlib: "gdi32", importc: "SetMetaFileBitsEx".} proc SetNamedPipeHandleState*(hNamedPipe: THandle, lpMode: var DWORD, - lpMaxCollectionCount, lpCollectDataTimeout: Pointer): WINBOOL{.stdcall, + lpMaxCollectionCount, lpCollectDataTimeout: pointer): WINBOOL{.stdcall, dynlib: "kernel32", importc: "SetNamedPipeHandleState".} proc SetPaletteEntries*(Palette: HPALETTE, StartIndex, NumEntries: WINUINT, PaletteEntries: pointer): WINUINT{.stdcall, @@ -22405,7 +22405,7 @@ proc SetUserObjectSecurity*(hObj: THandle, pSIRequested: var DWORD, dynlib: "user32", importc: "SetUserObjectSecurity".} proc SetWaitableTimer*(hTimer: THandle, lpDueTime: var TLargeInteger, lPeriod: int32, pfnCompletionRoutine: TFNTimerAPCRoutine, - lpArgToCompletionRoutine: Pointer, fResume: WINBOOL): WINBOOL{. + lpArgToCompletionRoutine: pointer, fResume: WINBOOL): WINBOOL{. stdcall, dynlib: "kernel32", importc: "SetWaitableTimer".} proc SetWinMetaFileBits*(p1: WINUINT, p2: cstring, p3: HDC, p4: TMetaFilePict): HENHMETAFILE{. stdcall, dynlib: "gdi32", importc: "SetWinMetaFileBits".} @@ -22415,7 +22415,7 @@ proc StartDoc*(DC: HDC, p2: TDocInfo): int{.stdcall, dynlib: "gdi32", proc StartDocA*(DC: HDC, p2: TDocInfoA): int{.stdcall, dynlib: "gdi32", importc: "StartDocA".} #function StartDocW(DC: HDC; const p2: TDocInfoW): Integer; stdcall; external 'gdi32' name 'StartDocW'; - #function StretchDIBits(DC: HDC; DestX, DestY, DestWidth, DestHegiht, SrcX, SrcY, SrcWidth, SrcHeight: Integer; Bits: Pointer; var BitsInfo: TBitmapInfo; Usage: WINUINT; Rop: DWORD): Integer; stdcall; external 'gdi32' name 'StretchDIBits'; + #function StretchDIBits(DC: HDC; DestX, DestY, DestWidth, DestHegiht, SrcX, SrcY, SrcWidth, SrcHeight: Integer; Bits: pointer; var BitsInfo: TBitmapInfo; Usage: WINUINT; Rop: DWORD): Integer; stdcall; external 'gdi32' name 'StretchDIBits'; proc SubtractRect*(lprcDst: var TRect, lprcSrc1, lprcSrc2: TRect): WINBOOL{. stdcall, dynlib: "user32", importc: "SubtractRect".} proc SystemTimeToFileTime*(lpSystemTime: TSystemTime, lpFileTime: var TFileTime): WINBOOL{. @@ -22446,8 +22446,8 @@ proc TrackMouseEvent*(lpEventTrack: PTrackMouseEvent): WINBOOL{.stdcall, proc TrackPopupMenu*(menu: HMENU, uFlags: WINUINT, x: int32, y: int32, nReserved: int32, wnd: HWND, prcRect: PRect): WINBOOL{. stdcall, dynlib: "user32", importc: "TrackPopupMenu".} -proc TransactNamedPipe*(hNamedPipe: THandle, lpInBuffer: Pointer, - nInBufferSize: DWORD, lpOutBuffer: Pointer, +proc TransactNamedPipe*(hNamedPipe: THandle, lpInBuffer: pointer, + nInBufferSize: DWORD, lpOutBuffer: pointer, nOutBufferSize: DWORD, lpBytesRead: var DWORD, lpOverlapped: POverlapped): WINBOOL{.stdcall, dynlib: "kernel32", importc: "TransactNamedPipe".} @@ -22464,8 +22464,8 @@ proc TranslateMDISysAccel*(hWndClient: HWND, lpMsg: TMsg): WINBOOL{.stdcall, dynlib: "user32", importc: "TranslateMDISysAccel".} proc TranslateMessage*(lpMsg: TMsg): WINBOOL{.stdcall, dynlib: "user32", importc: "TranslateMessage".} - #function TransparentDIBits(DC: HDC; p2, p3, p4, p5: Integer; const p6: Pointer; const p7: PBitmapInfo; p8: WINUINT; p9, p10, p11, p12: Integer; p13: WINUINT): WINBOOL;stdcall; external 'gdi32' name 'TransparentDIBits'; -proc UnhandledExceptionFilter*(ExceptionInfo: TExceptionPointers): int32{. + #function TransparentDIBits(DC: HDC; p2, p3, p4, p5: Integer; const p6: pointer; const p7: PBitmapInfo; p8: WINUINT; p9, p10, p11, p12: Integer; p13: WINUINT): WINBOOL;stdcall; external 'gdi32' name 'TransparentDIBits'; +proc UnhandledExceptionFilter*(ExceptionInfo: TExceptionpointers): int32{. stdcall, dynlib: "kernel32", importc: "UnhandledExceptionFilter".} proc UnionRect*(lprcDst: var TRect, lprcSrc1, lprcSrc2: TRect): WINBOOL{. stdcall, dynlib: "user32", importc: "UnionRect".} @@ -22499,19 +22499,19 @@ proc VerInstallFileW*(uFlags: DWORD, szSrcFileName, szDestFileName, szSrcDir, szDestDir, szCurDir, szTmpFile: LPWSTR, lpuTmpFileLen: var WINUINT): DWORD{.stdcall, dynlib: "version", importc: "VerInstallFileW".} -proc VerQueryValue*(pBlock: Pointer, lpSubBlock: cstring, - lplpBuffer: var Pointer, puLen: var WINUINT): WINBOOL{.stdcall, +proc VerQueryValue*(pBlock: pointer, lpSubBlock: cstring, + lplpBuffer: var pointer, puLen: var WINUINT): WINBOOL{.stdcall, dynlib: "version", importc: "VerQueryValueA".} -proc VerQueryValueA*(pBlock: Pointer, lpSubBlock: LPCSTR, - lplpBuffer: var Pointer, puLen: var WINUINT): WINBOOL{. +proc VerQueryValueA*(pBlock: pointer, lpSubBlock: LPCSTR, + lplpBuffer: var pointer, puLen: var WINUINT): WINBOOL{. stdcall, dynlib: "version", importc: "VerQueryValueA".} -proc VerQueryValueW*(pBlock: Pointer, lpSubBlock: LPWSTR, - lplpBuffer: var Pointer, puLen: var WINUINT): WINBOOL{. +proc VerQueryValueW*(pBlock: pointer, lpSubBlock: LPWSTR, + lplpBuffer: var pointer, puLen: var WINUINT): WINBOOL{. stdcall, dynlib: "version", importc: "VerQueryValueW".} -proc VirtualQuery*(lpAddress: Pointer, lpBuffer: var TMemoryBasicInformation, +proc VirtualQuery*(lpAddress: pointer, lpBuffer: var TMemoryBasicInformation, dwLength: DWORD): DWORD{.stdcall, dynlib: "kernel32", importc: "VirtualQuery".} -proc VirtualQueryEx*(hProcess: THandle, lpAddress: Pointer, +proc VirtualQueryEx*(hProcess: THandle, lpAddress: pointer, lpBuffer: var TMemoryBasicInformation, dwLength: DWORD): DWORD{. stdcall, dynlib: "kernel32", importc: "VirtualQueryEx".} proc WaitCommEvent*(hFile: THandle, lpEvtMask: var DWORD, @@ -22528,7 +22528,7 @@ proc wglSetLayerPaletteEntries*(p1: HDC, p2, p3, p4: int, pcr: pointer): int{. stdcall, dynlib: "opengl32", importc: "wglSetLayerPaletteEntries".} #function wglSwapMultipleBuffers(p1: WINUINT; const p2: PWGLSwap): DWORD;stdcall; external 'opengl32' name 'wglSwapMultipleBuffers'; #function WinSubmitCertificate(var lpCertificate: TWinCertificate): WINBOOL;stdcall; external 'imaghlp' name 'WinSubmitCertificate'; - #function WinVerifyTrust(wnd: HWND; const ActionID: TGUID; ActionData: Pointer): Longint;stdcall; external 'imaghlp' name 'WinVerifyTrust'; + #function WinVerifyTrust(wnd: HWND; const ActionID: TGUID; ActionData: pointer): Longint;stdcall; external 'imaghlp' name 'WinVerifyTrust'; proc WNetAddConnection2*(lpNetResource: var TNetResource, lpPassword, lpUserName: cstring, dwFlags: DWORD): DWORD{. stdcall, dynlib: "mpr", importc: "WNetAddConnection2A".} @@ -22553,13 +22553,13 @@ proc WNetDisconnectDialog1*(lpConnDlgStruct: var TDiscDlgStruct): DWORD{. proc WNetDisconnectDialog1A*(lpConnDlgStruct: var TDiscDlgStructA): DWORD{. stdcall, dynlib: "mpr", importc: "WNetDisconnectDialog1A".} #function WNetDisconnectDialog1W(var lpConnDlgStruct: TDiscDlgStructW): DWORD; stdcall; external 'mpr' name 'WNetDisconnectDialog1W'; -proc WNetEnumResource*(hEnum: THandle, lpcCount: var DWORD, lpBuffer: Pointer, +proc WNetEnumResource*(hEnum: THandle, lpcCount: var DWORD, lpBuffer: pointer, lpBufferSize: var DWORD): DWORD{.stdcall, dynlib: "mpr", importc: "WNetEnumResourceA".} -proc WNetEnumResourceA*(hEnum: THandle, lpcCount: var DWORD, lpBuffer: Pointer, +proc WNetEnumResourceA*(hEnum: THandle, lpcCount: var DWORD, lpBuffer: pointer, lpBufferSize: var DWORD): DWORD{.stdcall, dynlib: "mpr", importc: "WNetEnumResourceA".} -proc WNetEnumResourceW*(hEnum: THandle, lpcCount: var DWORD, lpBuffer: Pointer, +proc WNetEnumResourceW*(hEnum: THandle, lpcCount: var DWORD, lpBuffer: pointer, lpBufferSize: var DWORD): DWORD{.stdcall, dynlib: "mpr", importc: "WNetEnumResourceW".} proc WNetGetConnection*(lpLocalName: cstring, lpRemoteName: cstring, @@ -22601,21 +22601,21 @@ proc WNetGetProviderNameA*(dwNetType: DWORD, lpProviderName: LPCSTR, proc WNetGetProviderNameW*(dwNetType: DWORD, lpProviderName: LPWSTR, lpBufferSize: var DWORD): DWORD{.stdcall, dynlib: "mpr", importc: "WNetGetProviderNameW".} -proc WNetGetResourceParent*(lpNetResource: PNetResource, lpBuffer: Pointer, +proc WNetGetResourceParent*(lpNetResource: PNetResource, lpBuffer: pointer, cbBuffer: var DWORD): DWORD{.stdcall, dynlib: "mpr", importc: "WNetGetResourceParentA".} -proc WNetGetResourceParentA*(lpNetResource: PNetResourceA, lpBuffer: Pointer, +proc WNetGetResourceParentA*(lpNetResource: PNetResourceA, lpBuffer: pointer, cbBuffer: var DWORD): DWORD{.stdcall, dynlib: "mpr", importc: "WNetGetResourceParentA".} - #function WNetGetResourceParentW(lpNetResource: PNetResourceW; lpBuffer: Pointer; var cbBuffer: DWORD): DWORD;stdcall; external 'mpr' name 'WNetGetResourceParentW'; + #function WNetGetResourceParentW(lpNetResource: PNetResourceW; lpBuffer: pointer; var cbBuffer: DWORD): DWORD;stdcall; external 'mpr' name 'WNetGetResourceParentW'; proc WNetGetUniversalName*(lpLocalPath: cstring, dwInfoLevel: DWORD, - lpBuffer: Pointer, lpBufferSize: var DWORD): DWORD{. + lpBuffer: pointer, lpBufferSize: var DWORD): DWORD{. stdcall, dynlib: "mpr", importc: "WNetGetUniversalNameA".} proc WNetGetUniversalNameA*(lpLocalPath: LPCSTR, dwInfoLevel: DWORD, - lpBuffer: Pointer, lpBufferSize: var DWORD): DWORD{. + lpBuffer: pointer, lpBufferSize: var DWORD): DWORD{. stdcall, dynlib: "mpr", importc: "WNetGetUniversalNameA".} proc WNetGetUniversalNameW*(lpLocalPath: LPWSTR, dwInfoLevel: DWORD, - lpBuffer: Pointer, lpBufferSize: var DWORD): DWORD{. + lpBuffer: pointer, lpBufferSize: var DWORD): DWORD{. stdcall, dynlib: "mpr", importc: "WNetGetUniversalNameW".} proc WNetGetUser*(lpName: cstring, lpUserName: cstring, lpnLength: var DWORD): DWORD{. stdcall, dynlib: "mpr", importc: "WNetGetUserA".} @@ -22641,13 +22641,13 @@ proc WNetUseConnectionA*(hwndOwner: HWND, lpNetResource: var TNetResourceA, lpResult: var DWORD): DWORD{.stdcall, dynlib: "mpr", importc: "WNetUseConnectionA".} #function WNetUseConnectionW(hwndOwner: HWND; var lpNetResource: TNetResourceW; lpUserID: LPWSTR; lpPassword: LPWSTR; dwFlags: DWORD; lpAccessName: LPWSTR; var lpBufferSize: DWORD; var lpResult: DWORD): DWORD; stdcall; external 'mpr' name 'WNetUseConnectionW'; -proc WriteConsole*(hConsoleOutput: THandle, lpBuffer: Pointer, +proc WriteConsole*(hConsoleOutput: THandle, lpBuffer: pointer, nNumberOfCharsToWrite: DWORD, - lpNumberOfCharsWritten: var DWORD, lpReserved: Pointer): WINBOOL{. + lpNumberOfCharsWritten: var DWORD, lpReserved: pointer): WINBOOL{. stdcall, dynlib: "kernel32", importc: "WriteConsoleA".} -proc WriteConsoleA*(hConsoleOutput: THandle, lpBuffer: Pointer, +proc WriteConsoleA*(hConsoleOutput: THandle, lpBuffer: pointer, nNumberOfCharsToWrite: DWORD, - lpNumberOfCharsWritten: var DWORD, lpReserved: Pointer): WINBOOL{. + lpNumberOfCharsWritten: var DWORD, lpReserved: pointer): WINBOOL{. stdcall, dynlib: "kernel32", importc: "WriteConsoleA".} proc WriteConsoleInput*(hConsoleInput: THandle, lpBuffer: TInputRecord, nLength: DWORD, lpNumberOfEventsWritten: var DWORD): WINBOOL{. @@ -22658,15 +22658,15 @@ proc WriteConsoleInputA*(hConsoleInput: THandle, lpBuffer: TInputRecord, proc WriteConsoleInputW*(hConsoleInput: THandle, lpBuffer: TInputRecord, nLength: DWORD, lpNumberOfEventsWritten: var DWORD): WINBOOL{. stdcall, dynlib: "kernel32", importc: "WriteConsoleInputW".} -proc WriteConsoleOutput*(hConsoleOutput: THandle, lpBuffer: Pointer, +proc WriteConsoleOutput*(hConsoleOutput: THandle, lpBuffer: pointer, dwBufferSize, dwBufferCoord: TCoord, lpWriteRegion: var TSmallRect): WINBOOL{.stdcall, dynlib: "kernel32", importc: "WriteConsoleOutputA".} -proc WriteConsoleOutputA*(hConsoleOutput: THandle, lpBuffer: Pointer, +proc WriteConsoleOutputA*(hConsoleOutput: THandle, lpBuffer: pointer, dwBufferSize, dwBufferCoord: TCoord, lpWriteRegion: var TSmallRect): WINBOOL{.stdcall, dynlib: "kernel32", importc: "WriteConsoleOutputA".} -proc WriteConsoleOutputAttribute*(hConsoleOutput: THandle, lpAttribute: Pointer, +proc WriteConsoleOutputAttribute*(hConsoleOutput: THandle, lpAttribute: pointer, nLength: DWORD, dwWriteCoord: TCoord, lpNumberOfAttrsWritten: var DWORD): WINBOOL{. stdcall, dynlib: "kernel32", importc: "WriteConsoleOutputAttribute".} @@ -22682,18 +22682,18 @@ proc WriteConsoleOutputCharacterW*(hConsoleOutput: THandle, lpCharacter: LPWSTR, nLength: DWORD, dwWriteCoord: TCoord, lpNumberOfCharsWritten: var DWORD): WINBOOL{. stdcall, dynlib: "kernel32", importc: "WriteConsoleOutputCharacterW".} -proc WriteConsoleOutputW*(hConsoleOutput: THandle, lpBuffer: Pointer, +proc WriteConsoleOutputW*(hConsoleOutput: THandle, lpBuffer: pointer, dwBufferSize, dwBufferCoord: TCoord, lpWriteRegion: var TSmallRect): WINBOOL{.stdcall, dynlib: "kernel32", importc: "WriteConsoleOutputW".} -proc WriteConsoleW*(hConsoleOutput: THandle, lpBuffer: Pointer, +proc WriteConsoleW*(hConsoleOutput: THandle, lpBuffer: pointer, nNumberOfCharsToWrite: DWORD, - lpNumberOfCharsWritten: var DWORD, lpReserved: Pointer): WINBOOL{. + lpNumberOfCharsWritten: var DWORD, lpReserved: pointer): WINBOOL{. stdcall, dynlib: "kernel32", importc: "WriteConsoleW".} proc WriteFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToWrite: DWORD, lpNumberOfBytesWritten: var DWORD, lpOverlapped: POverlapped): WINBOOL{. stdcall, dynlib: "kernel32", importc: "WriteFile".} -proc WriteFileEx*(hFile: THandle, lpBuffer: Pointer, +proc WriteFileEx*(hFile: THandle, lpBuffer: pointer, nNumberOfBytesToWrite: DWORD, lpOverlapped: TOverlapped, lpCompletionRoutine: FARPROC): WINBOOL{.stdcall, dynlib: "kernel32", importc: "WriteFileEx".} @@ -22707,8 +22707,8 @@ proc WritePrivateProfileStructW*(lpszSection, lpszKey: LPCWSTR, proc WritePrivateProfileStruct*(lpszSection, lpszKey: LPCTSTR, lpStruct: LPVOID, uSizeStruct: WINUINT, szFile: LPCTSTR): WINBOOL{. stdcall, dynlib: "kernel32", importc: "WritePrivateProfileStructA".} -proc WriteProcessMemory*(hProcess: THandle, lpBaseAddress: Pointer, - lpBuffer: Pointer, nSize: DWORD, +proc WriteProcessMemory*(hProcess: THandle, lpBaseAddress: pointer, + lpBuffer: pointer, nSize: DWORD, lpNumberOfBytesWritten: var DWORD): WINBOOL{.stdcall, dynlib: "kernel32", importc: "WriteProcessMemory".} proc SHFileOperation*(para1: var SHFILEOPSTRUCT): int32{.stdcall, @@ -22821,7 +22821,7 @@ proc INDEXTOSTATEIMAGEMASK*(i: int32): int32 = result = i shl 12'i32 proc MAKEINTATOM*(i: int32): LPTSTR = - result = cast[LPTSTR](cast[ULONG_PTR](ToU16(i))) + result = cast[LPTSTR](cast[ULONG_PTR](toU16(i))) proc MAKELANGID*(p, s: int32): int32 = # return type might be wrong @@ -22849,10 +22849,10 @@ proc MAKEROP4*(fore, back: int32): DWORD = proc MAKEWPARAM*(L, h: int32): WPARAM = result = WPARAM(MAKELONG(L, h)) -proc GET_X_LPARAM*(lp: Windows.LParam): int32 = +proc GET_X_LPARAM*(lp: windows.LParam): int32 = result = LOWORD(lp.int32) -proc GET_Y_LPARAM*(lp: Windows.LParam): int32 = +proc GET_Y_LPARAM*(lp: windows.LParam): int32 = result = HIWORD(lp.int32) proc UNICODE_NULL*(): WCHAR = @@ -23229,10 +23229,10 @@ proc set_fAckReq(a: var DDEUP, fAckReq: int16) = a.flag0 = a.flag0 or ((fAckReq shl bp_DDEUP_fAckReq) and bm_DDEUP_fAckReq) proc CreateWindowA(lpClassName: LPCSTR, lpWindowName: LPCSTR, dwStyle: DWORD, - X: int32, Y: int32, nWidth: int32, nHeight: int32, + X, Y, nWidth, nHeight: int32, hWndParent: HWND, menu: HMENU, hInstance: HINST, lpParam: LPVOID): HWND = - result = CreateWindowExA(0, lpClassName, lpWindowName, dwStyle, x, y, nWidth, + result = CreateWindowExA(0, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, menu, hInstance, lpParam) proc CreateDialogA(hInstance: HINST, lpTemplateName: LPCSTR, hWndParent: HWND, @@ -23259,7 +23259,7 @@ proc CreateWindowW(lpClassName: LPCWSTR, lpWindowName: LPCWSTR, dwStyle: DWORD, X: int32, Y: int32, nWidth: int32, nHeight: int32, hWndParent: HWND, menu: HMENU, hInstance: HINST, lpParam: LPVOID): HWND = - result = CreateWindowExW(0, lpClassName, lpWindowName, dwStyle, x, y, nWidth, + result = CreateWindowExW(0, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, menu, hInstance, lpParam) proc CreateDialogW(hInstance: HINST, lpName: LPCWSTR, hWndParent: HWND, @@ -23285,7 +23285,7 @@ when defined(winUnicode): X: int32, Y: int32, nWidth: int32, nHeight: int32, hWndParent: HWND, menu: HMENU, hInstance: HINST, lpParam: LPVOID): HWND = - result = CreateWindowEx(0, lpClassName, lpWindowName, dwStyle, x, y, nWidth, + result = CreateWindowEx(0, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam) proc CreateDialog(hInstance: HINST, lpName: LPCWSTR, hWndParent: HWND, @@ -23311,7 +23311,7 @@ else: X: int32, Y: int32, nWidth: int32, nHeight: int32, hWndParent: HWND, menu: HMENU, hInstance: HINST, lpParam: LPVOID): HWND = - result = CreateWindowEx(0, lpClassName, lpWindowName, dwStyle, x, y, nWidth, + result = CreateWindowEx(0, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, menu, hInstance, lpParam) proc CreateDialog(hInstance: HINST, lpTemplateName: LPCSTR, hWndParent: HWND, @@ -23334,24 +23334,24 @@ else: result = DialogBoxIndirectParam(hInstance, hDialogTemplate, hWndParent, lpDialogFunc, 0) -proc GlobalAllocPtr(flags, cb: DWord): Pointer = +proc GlobalAllocPtr(flags, cb: DWord): pointer = result = GlobalLock(GlobalAlloc(flags, cb)) -proc GlobalFreePtr(lp: Pointer): Pointer = - result = cast[Pointer](GlobalFree(cast[HWND](GlobalUnlockPtr(lp)))) +proc GlobalFreePtr(lp: pointer): pointer = + result = cast[pointer](GlobalFree(cast[HWND](GlobalUnlockPtr(lp)))) -proc GlobalUnlockPtr(lp: pointer): Pointer = +proc GlobalUnlockPtr(lp: pointer): pointer = discard GlobalUnlock(GlobalHandle(lp)) result = lp -proc GlobalLockPtr(lp: pointer): Pointer = +proc GlobalLockPtr(lp: pointer): pointer = result = GlobalLock(GlobalHandle(lp)) -proc GlobalReAllocPtr(lp: Pointer, cbNew, flags: DWord): Pointer = +proc GlobalReAllocPtr(lp: pointer, cbNew, flags: DWord): pointer = result = GlobalLock(GlobalReAlloc(cast[HWND](GlobalUnlockPtr(lp)), cbNew, flags)) -proc GlobalPtrHandle(lp: pointer): Pointer = - result = cast[Pointer](GlobalHandle(lp)) +proc GlobalPtrHandle(lp: pointer): pointer = + result = cast[pointer](GlobalHandle(lp)) proc ImageList_AddIcon(himl: HIMAGELIST, hicon: HICON): int32 = result = ImageList_ReplaceIcon(himl, -1, hicon) @@ -23502,7 +23502,7 @@ proc ListView_GetCountPerPage(hwndLV: HWND): LRESULT = proc ListView_GetEditControl(hwndLV: HWND): LRESULT = result = SendMessage(hwndLV, LVM_GETEDITCONTROL, 0, 0) -proc ListView_GetImageList(wnd: HWND, iImageList: wINT): LRESULT = +proc ListView_GetImageList(wnd: HWND, iImageList: WINT): LRESULT = result = SendMessage(wnd, LVM_GETIMAGELIST, WPARAM(iImageList), 0) proc ListView_GetISearchString(hwndLV: HWND, lpsz: LPTSTR): LRESULT = @@ -23702,7 +23702,7 @@ proc TreeView_SetItem(wnd: HWND, item: var TV_ITEM): LRESULT = result = SendMessage(wnd, TVM_SETITEM, 0, cast[LPARAM](addr(item))) proc TreeView_EditLabel(wnd: HWND, hitem: HTREEITEM): LRESULT = - Result = SendMessage(wnd, TVM_EDITLABEL, 0, cast[LPARAM](hitem)) + result = SendMessage(wnd, TVM_EDITLABEL, 0, cast[LPARAM](hitem)) proc TreeView_GetEditControl(wnd: HWND): LRESULT = result = SendMessage(wnd, TVM_GETEDITCONTROL, 0, 0) @@ -23854,10 +23854,10 @@ proc CommDlg_OpenSave_SetDefExt(hdlg: HWND, pszext: LPSTR): LRESULT = proc InternalGetLargestConsoleWindowSize(hConsoleOutput: HANDLE): DWord{. stdcall, dynlib: "kernel32", importc: "GetLargestConsoleWindowSize".} proc GetLargestConsoleWindowSize(hConsoleOutput: HANDLE): COORD = - var res: dword + var res: DWORD res = InternalGetLargestConsoleWindowSize(hConsoleOutput) - result.y = toU16(res and 0x0000ffff) # XXX: correct? - result.x = toU16(res shr 16) + result.Y = toU16(res and 0x0000ffff) # XXX: correct? + result.X = toU16(res shr 16) proc Succeeded(Status: HRESULT): WINBOOL = result = (Status and 0x80000000).WinBool diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 09696b67f..76d17bc4a 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -8,7 +8,7 @@ # ## This module implements a small wrapper for some needed Win API procedures, -## so that the Nimrod compiler does not depend on the huge Windows module. +## so that the Nim compiler does not depend on the huge Windows module. const useWinUnicode* = not defined(useWinAnsi) @@ -104,11 +104,11 @@ proc closeHandle*(hObject: THandle): WINBOOL {.stdcall, dynlib: "kernel32", importc: "CloseHandle".} proc readFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToRead: int32, - lpNumberOfBytesRead: var int32, lpOverlapped: pointer): WINBOOL{. + lpNumberOfBytesRead: ptr int32, lpOverlapped: pointer): WINBOOL{. stdcall, dynlib: "kernel32", importc: "ReadFile".} proc writeFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToWrite: int32, - lpNumberOfBytesWritten: var int32, + lpNumberOfBytesWritten: ptr int32, lpOverlapped: pointer): WINBOOL{. stdcall, dynlib: "kernel32", importc: "WriteFile".} @@ -199,14 +199,14 @@ else: importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.} proc setCurrentDirectoryA*(lpPathName: cstring): int32 {. importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.} - proc createDirectoryA*(pathName: cstring, security: Pointer=nil): int32 {. + proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {. importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.} proc removeDirectoryA*(lpPathName: cstring): int32 {. importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.} proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {. stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".} - proc getModuleFileNameA*(handle: THandle, buf: CString, size: int32): int32 {. + proc getModuleFileNameA*(handle: THandle, buf: cstring, size: int32): int32 {. importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.} when useWinUnicode: @@ -304,7 +304,7 @@ else: dwFileAttributes: int32): WINBOOL {. stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".} - proc copyFileA*(lpExistingFileName, lpNewFileName: CString, + proc copyFileA*(lpExistingFileName, lpNewFileName: cstring, bFailIfExists: cint): cint {. importc: "CopyFileA", stdcall, dynlib: "kernel32".} @@ -363,48 +363,50 @@ const proc wsaGetLastError*(): cint {.importc: "WSAGetLastError", dynlib: ws2dll.} type - TSocketHandle* = distinct int + SocketHandle* = distinct int + +{.deprecated: [TSocketHandle: SocketHandle].} type - TWSAData* {.pure, final, importc: "WSADATA", header: "Winsock2.h".} = object + WSAData* {.importc: "WSADATA", header: "Winsock2.h".} = object wVersion, wHighVersion: int16 szDescription: array[0..WSADESCRIPTION_LEN, char] szSystemStatus: array[0..WSASYS_STATUS_LEN, char] iMaxSockets, iMaxUdpDg: int16 lpVendorInfo: cstring - TSockAddr* {.pure, final, importc: "SOCKADDR", header: "Winsock2.h".} = object + SockAddr* {.importc: "SOCKADDR", header: "Winsock2.h".} = object sa_family*: int16 # unsigned sa_data: array[0..13, char] - TInAddr* {.pure, final, importc: "IN_ADDR", header: "Winsock2.h".} = object + InAddr* {.importc: "IN_ADDR", header: "Winsock2.h".} = object s_addr*: int32 # IP address - Tsockaddr_in* {.pure, final, importc: "SOCKADDR_IN", + Sockaddr_in* {.importc: "SOCKADDR_IN", header: "Winsock2.h".} = object sin_family*: int16 sin_port*: int16 # unsigned - sin_addr*: TInAddr + sin_addr*: InAddr sin_zero*: array[0..7, char] - Tin6_addr* {.pure, final, importc: "IN6_ADDR", header: "Winsock2.h".} = object + In6_addr* {.importc: "IN6_ADDR", header: "Winsock2.h".} = object bytes*: array[0..15, char] - Tsockaddr_in6* {.pure, final, importc: "SOCKADDR_IN6", + Sockaddr_in6* {.importc: "SOCKADDR_IN6", header: "Winsock2.h".} = object sin6_family*: int16 sin6_port*: int16 # unsigned sin6_flowinfo*: int32 # unsigned - sin6_addr*: Tin6_addr + sin6_addr*: In6_addr sin6_scope_id*: int32 # unsigned - Tsockaddr_in6_old* {.pure, final.} = object + Sockaddr_in6_old* = object sin6_family*: int16 sin6_port*: int16 # unsigned sin6_flowinfo*: int32 # unsigned - sin6_addr*: Tin6_addr + sin6_addr*: In6_addr - TServent* {.pure, final.} = object + Servent* = object s_name*: cstring s_aliases*: cstringArray when defined(cpu64): @@ -414,35 +416,42 @@ type s_port*: int16 s_proto*: cstring - Thostent* {.pure, final.} = object + Hostent* = object h_name*: cstring h_aliases*: cstringArray h_addrtype*: int16 h_length*: int16 h_addr_list*: cstringArray - TFdSet* {.pure, final.} = object + TFdSet* = object fd_count*: cint # unsigned - fd_array*: array[0..FD_SETSIZE-1, TSocketHandle] + fd_array*: array[0..FD_SETSIZE-1, SocketHandle] - TTimeval* {.pure, final.} = object + Timeval* = object tv_sec*, tv_usec*: int32 - TAddrInfo* {.pure, final.} = object + AddrInfo* = object ai_flags*: cint ## Input flags. ai_family*: cint ## Address family of socket. ai_socktype*: cint ## Socket type. ai_protocol*: cint ## Protocol of socket. ai_addrlen*: int ## Length of socket address. ai_canonname*: cstring ## Canonical name of service location. - ai_addr*: ptr TSockAddr ## Socket address of socket. - ai_next*: ptr TAddrInfo ## Pointer to next in list. + ai_addr*: ptr SockAddr ## Socket address of socket. + ai_next*: ptr AddrInfo ## Pointer to next in list. + + SockLen* = cuint + +{.deprecated: [TSockaddr_in: Sockaddr_in, TAddrinfo: AddrInfo, + TSockAddr: SockAddr, TSockLen: SockLen, TTimeval: Timeval, + TWSADATA: WSADATA, Thostent: Hostent, TServent: Servent, + TInAddr: InAddr, Tin6_addr: In6_addr, Tsockaddr_in6: Sockaddr_in6, + Tsockaddr_in6_old: Sockaddr_in6_old].} - TSockLen* = cuint var SOMAXCONN* {.importc, header: "Winsock2.h".}: cint - INVALID_SOCKET* {.importc, header: "Winsock2.h".}: TSocketHandle + INVALID_SOCKET* {.importc, header: "Winsock2.h".}: SocketHandle SOL_SOCKET* {.importc, header: "Winsock2.h".}: cint SO_DEBUG* {.importc, header: "Winsock2.h".}: cint ## turn on debugging info recording SO_ACCEPTCONN* {.importc, header: "Winsock2.h".}: cint # socket has had listen() @@ -458,94 +467,94 @@ var SO_EXCLUSIVEADDRUSE* {.importc, header: "Winsock2.h".}: cint # disallow local address reuse SO_ERROR* {.importc, header: "Winsock2.h".}: cint -proc `==`*(x, y: TSocketHandle): bool {.borrow.} +proc `==`*(x, y: SocketHandle): bool {.borrow.} -proc getservbyname*(name, proto: cstring): ptr TServent {. +proc getservbyname*(name, proto: cstring): ptr Servent {. stdcall, importc: "getservbyname", dynlib: ws2dll.} -proc getservbyport*(port: cint, proto: cstring): ptr TServent {. +proc getservbyport*(port: cint, proto: cstring): ptr Servent {. stdcall, importc: "getservbyport", dynlib: ws2dll.} -proc gethostbyaddr*(ip: ptr TInAddr, len: cuint, theType: cint): ptr Thostent {. +proc gethostbyaddr*(ip: ptr InAddr, len: cuint, theType: cint): ptr Hostent {. stdcall, importc: "gethostbyaddr", dynlib: ws2dll.} -proc gethostbyname*(name: cstring): ptr Thostent {. +proc gethostbyname*(name: cstring): ptr Hostent {. stdcall, importc: "gethostbyname", dynlib: ws2dll.} -proc socket*(af, typ, protocol: cint): TSocketHandle {. +proc socket*(af, typ, protocol: cint): SocketHandle {. stdcall, importc: "socket", dynlib: ws2dll.} -proc closesocket*(s: TSocketHandle): cint {. +proc closesocket*(s: SocketHandle): cint {. stdcall, importc: "closesocket", dynlib: ws2dll.} -proc accept*(s: TSocketHandle, a: ptr TSockAddr, addrlen: ptr TSockLen): TSocketHandle {. +proc accept*(s: SocketHandle, a: ptr SockAddr, addrlen: ptr SockLen): SocketHandle {. stdcall, importc: "accept", dynlib: ws2dll.} -proc bindSocket*(s: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint {. +proc bindSocket*(s: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint {. stdcall, importc: "bind", dynlib: ws2dll.} -proc connect*(s: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint {. +proc connect*(s: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint {. stdcall, importc: "connect", dynlib: ws2dll.} -proc getsockname*(s: TSocketHandle, name: ptr TSockAddr, - namelen: ptr TSockLen): cint {. +proc getsockname*(s: SocketHandle, name: ptr SockAddr, + namelen: ptr SockLen): cint {. stdcall, importc: "getsockname", dynlib: ws2dll.} -proc getsockopt*(s: TSocketHandle, level, optname: cint, optval: pointer, - optlen: ptr TSockLen): cint {. +proc getsockopt*(s: SocketHandle, level, optname: cint, optval: pointer, + optlen: ptr SockLen): cint {. stdcall, importc: "getsockopt", dynlib: ws2dll.} -proc setsockopt*(s: TSocketHandle, level, optname: cint, optval: pointer, - optlen: TSockLen): cint {. +proc setsockopt*(s: SocketHandle, level, optname: cint, optval: pointer, + optlen: SockLen): cint {. stdcall, importc: "setsockopt", dynlib: ws2dll.} -proc listen*(s: TSocketHandle, backlog: cint): cint {. +proc listen*(s: SocketHandle, backlog: cint): cint {. stdcall, importc: "listen", dynlib: ws2dll.} -proc recv*(s: TSocketHandle, buf: pointer, len, flags: cint): cint {. +proc recv*(s: SocketHandle, buf: pointer, len, flags: cint): cint {. stdcall, importc: "recv", dynlib: ws2dll.} -proc recvfrom*(s: TSocketHandle, buf: cstring, len, flags: cint, - fromm: ptr TSockAddr, fromlen: ptr TSockLen): cint {. +proc recvfrom*(s: SocketHandle, buf: cstring, len, flags: cint, + fromm: ptr SockAddr, fromlen: ptr SockLen): cint {. stdcall, importc: "recvfrom", dynlib: ws2dll.} proc select*(nfds: cint, readfds, writefds, exceptfds: ptr TFdSet, - timeout: ptr TTimeval): cint {. + timeout: ptr Timeval): cint {. stdcall, importc: "select", dynlib: ws2dll.} -proc send*(s: TSocketHandle, buf: pointer, len, flags: cint): cint {. +proc send*(s: SocketHandle, buf: pointer, len, flags: cint): cint {. stdcall, importc: "send", dynlib: ws2dll.} -proc sendto*(s: TSocketHandle, buf: pointer, len, flags: cint, - to: ptr TSockAddr, tolen: TSockLen): cint {. +proc sendto*(s: SocketHandle, buf: pointer, len, flags: cint, + to: ptr SockAddr, tolen: SockLen): cint {. stdcall, importc: "sendto", dynlib: ws2dll.} -proc shutdown*(s: TSocketHandle, how: cint): cint {. +proc shutdown*(s: SocketHandle, how: cint): cint {. stdcall, importc: "shutdown", dynlib: ws2dll.} -proc getnameinfo*(a1: ptr TSockAddr, a2: TSockLen, - a3: cstring, a4: TSockLen, a5: cstring, - a6: TSockLen, a7: cint): cint {. +proc getnameinfo*(a1: ptr SockAddr, a2: SockLen, + a3: cstring, a4: SockLen, a5: cstring, + a6: SockLen, a7: cint): cint {. stdcall, importc: "getnameinfo", dynlib: ws2dll.} proc inet_addr*(cp: cstring): int32 {. stdcall, importc: "inet_addr", dynlib: ws2dll.} -proc WSAFDIsSet(s: TSocketHandle, FDSet: var TFdSet): bool {. +proc WSAFDIsSet(s: SocketHandle, set: var TFdSet): bool {. stdcall, importc: "__WSAFDIsSet", dynlib: ws2dll, noSideEffect.} -proc FD_ISSET*(Socket: TSocketHandle, FDSet: var TFdSet): cint = - result = if WSAFDIsSet(Socket, FDSet): 1'i32 else: 0'i32 +proc FD_ISSET*(socket: SocketHandle, set: var TFdSet): cint = + result = if WSAFDIsSet(socket, set): 1'i32 else: 0'i32 -proc FD_SET*(Socket: TSocketHandle, FDSet: var TFdSet) = - if FDSet.fd_count < FD_SETSIZE: - FDSet.fd_array[int(FDSet.fd_count)] = Socket - inc(FDSet.fd_count) +proc FD_SET*(socket: SocketHandle, s: var TFdSet) = + if s.fd_count < FD_SETSIZE: + s.fd_array[int(s.fd_count)] = socket + inc(s.fd_count) -proc FD_ZERO*(FDSet: var TFdSet) = - FDSet.fd_count = 0 +proc FD_ZERO*(s: var TFdSet) = + s.fd_count = 0 -proc wsaStartup*(wVersionRequired: int16, WSData: ptr TWSAData): cint {. +proc wsaStartup*(wVersionRequired: int16, WSData: ptr WSAData): cint {. stdcall, importc: "WSAStartup", dynlib: ws2dll.} -proc getaddrinfo*(nodename, servname: cstring, hints: ptr TAddrInfo, - res: var ptr TAddrInfo): cint {. +proc getaddrinfo*(nodename, servname: cstring, hints: ptr AddrInfo, + res: var ptr AddrInfo): cint {. stdcall, importc: "getaddrinfo", dynlib: ws2dll.} -proc freeaddrinfo*(ai: ptr TAddrInfo) {. +proc freeaddrinfo*(ai: ptr AddrInfo) {. stdcall, importc: "freeaddrinfo", dynlib: ws2dll.} -proc inet_ntoa*(i: TInAddr): cstring {. +proc inet_ntoa*(i: InAddr): cstring {. stdcall, importc, dynlib: ws2dll.} const @@ -564,12 +573,14 @@ proc waitForMultipleObjects*(nCount: DWORD, lpHandles: PWOHandleArray, const GENERIC_READ* = 0x80000000'i32 + GENERIC_WRITE* = 0x40000000'i32 GENERIC_ALL* = 0x10000000'i32 FILE_SHARE_READ* = 1'i32 FILE_SHARE_DELETE* = 4'i32 FILE_SHARE_WRITE* = 2'i32 CREATE_ALWAYS* = 2'i32 + CREATE_NEW* = 1'i32 OPEN_EXISTING* = 3'i32 FILE_BEGIN* = 0'i32 INVALID_SET_FILE_POINTER* = -1'i32 @@ -586,6 +597,7 @@ const # Error Constants const ERROR_ACCESS_DENIED* = 5 + ERROR_HANDLE_EOF* = 38 when useWinUnicode: proc createFileW*(lpFileName: WideCString, dwDesiredAccess, dwShareMode: DWORD, @@ -640,11 +652,11 @@ proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall, type TOVERLAPPED* {.pure, inheritable.} = object - Internal*: PULONG - InternalHigh*: PULONG - Offset*: DWORD - OffsetHigh*: DWORD - hEvent*: THANDLE + internal*: PULONG + internalHigh*: PULONG + offset*: DWORD + offsetHigh*: DWORD + hEvent*: THandle POVERLAPPED* = ptr TOVERLAPPED @@ -659,6 +671,7 @@ type const ERROR_IO_PENDING* = 997 # a.k.a WSA_IO_PENDING + FILE_FLAG_OVERLAPPED* = 1073741824 WSAECONNABORTED* = 10053 WSAECONNRESET* = 10054 WSAEDISCON* = 10101 @@ -666,17 +679,21 @@ const WSAETIMEDOUT* = 10060 ERROR_NETNAME_DELETED* = 64 -proc CreateIoCompletionPort*(FileHandle: THANDLE, ExistingCompletionPort: THANDLE, +proc createIoCompletionPort*(FileHandle: THandle, ExistingCompletionPort: THandle, CompletionKey: DWORD, - NumberOfConcurrentThreads: DWORD): THANDLE{.stdcall, + NumberOfConcurrentThreads: DWORD): THandle{.stdcall, dynlib: "kernel32", importc: "CreateIoCompletionPort".} -proc GetQueuedCompletionStatus*(CompletionPort: THandle, +proc getQueuedCompletionStatus*(CompletionPort: THandle, lpNumberOfBytesTransferred: PDWORD, lpCompletionKey: PULONG, - lpOverlapped: ptr POverlapped, + lpOverlapped: ptr POVERLAPPED, dwMilliseconds: DWORD): WINBOOL{.stdcall, dynlib: "kernel32", importc: "GetQueuedCompletionStatus".} +proc getOverlappedResult*(hFile: THandle, lpOverlapped: TOVERLAPPED, + lpNumberOfBytesTransferred: var DWORD, bWait: WINBOOL): WINBOOL{. + stdcall, dynlib: "kernel32", importc: "GetOverlappedResult".} + const IOC_OUT* = 0x40000000 IOC_IN* = 0x80000000 @@ -697,9 +714,9 @@ var WSAID_GETACCEPTEXSOCKADDRS*: TGUID = TGUID(D1: 0xb5367df2'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [ 0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8]) -proc WSAIoctl*(s: TSocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer, +proc WSAIoctl*(s: SocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer, cbInBuffer: DWORD, lpvOutBuffer: pointer, cbOutBuffer: DWORD, - lpcbBytesReturned: PDword, lpOverlapped: POVERLAPPED, + lpcbBytesReturned: PDWORD, lpOverlapped: POVERLAPPED, lpCompletionRoutine: POVERLAPPED_COMPLETION_ROUTINE): cint {.stdcall, importc: "WSAIoctl", dynlib: "Ws2_32.dll".} @@ -708,17 +725,17 @@ type len*: ULONG buf*: cstring -proc WSARecv*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD, - bytesReceived, flags: PDWORD, lpOverlapped: POverlapped, +proc WSARecv*(s: SocketHandle, buf: ptr TWSABuf, bufCount: DWORD, + bytesReceived, flags: PDWORD, lpOverlapped: POVERLAPPED, completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {. stdcall, importc: "WSARecv", dynlib: "Ws2_32.dll".} -proc WSASend*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD, - bytesSent: PDWord, flags: DWORD, lpOverlapped: POverlapped, +proc WSASend*(s: SocketHandle, buf: ptr TWSABuf, bufCount: DWORD, + bytesSent: PDWORD, flags: DWORD, lpOverlapped: POVERLAPPED, completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {. stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".} -proc get_osfhandle*(fd:TFileHandle): THandle {. +proc get_osfhandle*(fd:FileHandle): THandle {. importc: "_get_osfhandle", header:"".} proc getSystemTimes*(lpIdleTime, lpKernelTime, diff --git a/lib/wrappers/expat.nim b/lib/wrappers/expat.nim index 3400dfdf7..a3d888201 100644 --- a/lib/wrappers/expat.nim +++ b/lib/wrappers/expat.nim @@ -77,8 +77,8 @@ type type TContent*{.pure, final.} = object - typ*: TContent_Type - quant*: TContent_Quant + typ*: TContentType + quant*: TContentQuant name*: cstring numchildren*: cint children*: ptr TContent @@ -93,7 +93,7 @@ type TElementDeclHandler* = proc (userData: pointer, name: cstring, model: ptr TContent){.cdecl.} -proc SetElementDeclHandler*(parser: PParser, eldecl: TElementDeclHandler){. +proc setElementDeclHandler*(parser: PParser, eldecl: TElementDeclHandler){. cdecl, importc: "XML_SetElementDeclHandler", dynlib: expatDll.} # The Attlist declaration handler is called for *each* attribute. So # a single Attlist declaration with multiple attributes declared will @@ -106,10 +106,10 @@ proc SetElementDeclHandler*(parser: PParser, eldecl: TElementDeclHandler){. type TAttlistDeclHandler* = proc (userData: pointer, elname: cstring, - attname: cstring, att_type: cstring, + attname: cstring, attType: cstring, dflt: cstring, isrequired: cint){.cdecl.} -proc SetAttlistDeclHandler*(parser: PParser, attdecl: TAttlistDeclHandler){. +proc setAttlistDeclHandler*(parser: PParser, attdecl: TAttlistDeclHandler){. cdecl, importc: "XML_SetAttlistDeclHandler", dynlib: expatDll.} # The XML declaration handler is called for *both* XML declarations # and text declarations. The way to distinguish is that the version @@ -124,20 +124,20 @@ type TXmlDeclHandler* = proc (userData: pointer, version: cstring, encoding: cstring, standalone: cint){.cdecl.} -proc SetXmlDeclHandler*(parser: PParser, xmldecl: TXmlDeclHandler){.cdecl, +proc setXmlDeclHandler*(parser: PParser, xmldecl: TXmlDeclHandler){.cdecl, importc: "XML_SetXmlDeclHandler", dynlib: expatDll.} type TMemory_Handling_Suite*{.pure, final.} = object - malloc_fcn*: proc (size: int): pointer{.cdecl.} - realloc_fcn*: proc (p: pointer, size: int): pointer{.cdecl.} - free_fcn*: proc (p: pointer){.cdecl.} + mallocFcn*: proc (size: int): pointer{.cdecl.} + reallocFcn*: proc (p: pointer, size: int): pointer{.cdecl.} + freeFcn*: proc (p: pointer){.cdecl.} # Constructs a new parser; encoding is the encoding specified by the # external protocol or NULL if there is none specified. # -proc ParserCreate*(encoding: cstring): PParser{.cdecl, +proc parserCreate*(encoding: cstring): PParser{.cdecl, importc: "XML_ParserCreate", dynlib: expatDll.} # Constructs a new parser and namespace processor. Element type # names and attribute names that belong to a namespace will be @@ -151,7 +151,7 @@ proc ParserCreate*(encoding: cstring): PParser{.cdecl, # triplets (see XML_SetReturnNSTriplet). # -proc ParserCreateNS*(encoding: cstring, namespaceSeparator: char): PParser{. +proc parserCreateNS*(encoding: cstring, namespaceSeparator: char): PParser{. cdecl, importc: "XML_ParserCreateNS", dynlib: expatDll.} # Constructs a new parser using the memory management suite referred to # by memsuite. If memsuite is NULL, then use the standard library memory @@ -163,7 +163,7 @@ proc ParserCreateNS*(encoding: cstring, namespaceSeparator: char): PParser{. # the given suite. # -proc ParserCreate_MM*(encoding: cstring, memsuite: ptr TMemory_Handling_Suite, +proc parserCreateMM*(encoding: cstring, memsuite: ptr TMemoryHandlingSuite, namespaceSeparator: cstring): PParser{.cdecl, importc: "XML_ParserCreate_MM", dynlib: expatDll.} # Prepare a parser object to be re-used. This is particularly @@ -176,7 +176,7 @@ proc ParserCreate_MM*(encoding: cstring, memsuite: ptr TMemory_Handling_Suite, # Added in Expat 1.95.3. # -proc ParserReset*(parser: PParser, encoding: cstring): Bool{.cdecl, +proc parserReset*(parser: PParser, encoding: cstring): bool{.cdecl, importc: "XML_ParserReset", dynlib: expatDll.} # atts is array of name/value pairs, terminated by 0; # names and values are 0 terminated. @@ -230,7 +230,7 @@ type type TStartDoctypeDeclHandler* = proc (userData: pointer, doctypeName: cstring, sysid: cstring, pubid: cstring, - has_internal_subset: cint){.cdecl.} + hasInternalSubset: cint){.cdecl.} # This is called for the start of the DOCTYPE declaration when the # closing > is encountered, but after processing any external @@ -261,12 +261,12 @@ type type TEntityDeclHandler* = proc (userData: pointer, entityName: cstring, - is_parameter_entity: cint, value: cstring, - value_length: cint, base: cstring, + isParameterEntity: cint, value: cstring, + valueLength: cint, base: cstring, systemId: cstring, publicId: cstring, notationName: cstring){.cdecl.} -proc SetEntityDeclHandler*(parser: PParser, handler: TEntityDeclHandler){.cdecl, +proc setEntityDeclHandler*(parser: PParser, handler: TEntityDeclHandler){.cdecl, importc: "XML_SetEntityDeclHandler", dynlib: expatDll.} # OBSOLETE -- OBSOLETE -- OBSOLETE # This handler has been superceded by the EntityDeclHandler above. @@ -372,7 +372,7 @@ type type TSkippedEntityHandler* = proc (userData: pointer, entityName: cstring, - is_parameter_entity: cint){.cdecl.} + isParameterEntity: cint){.cdecl.} # This structure is filled in by the XML_UnknownEncodingHandler to # provide information to the parser about encodings that are unknown @@ -455,27 +455,27 @@ type TUnknownEncodingHandler* = proc (encodingHandlerData: pointer, name: cstring, info: ptr TEncoding): cint{.cdecl.} -proc SetElementHandler*(parser: PParser, start: TStartElementHandler, +proc setElementHandler*(parser: PParser, start: TStartElementHandler, endHandler: TEndElementHandler){.cdecl, importc: "XML_SetElementHandler", dynlib: expatDll.} -proc SetStartElementHandler*(parser: PParser, handler: TStartElementHandler){. +proc setStartElementHandler*(parser: PParser, handler: TStartElementHandler){. cdecl, importc: "XML_SetStartElementHandler", dynlib: expatDll.} -proc SetEndElementHandler*(parser: PParser, handler: TEndElementHandler){.cdecl, +proc setEndElementHandler*(parser: PParser, handler: TEndElementHandler){.cdecl, importc: "XML_SetEndElementHandler", dynlib: expatDll.} -proc SetCharacterDataHandler*(parser: PParser, handler: TCharacterDataHandler){. +proc setCharacterDataHandler*(parser: PParser, handler: TCharacterDataHandler){. cdecl, importc: "XML_SetCharacterDataHandler", dynlib: expatDll.} -proc SetProcessingInstructionHandler*(parser: PParser, +proc setProcessingInstructionHandler*(parser: PParser, handler: TProcessingInstructionHandler){. cdecl, importc: "XML_SetProcessingInstructionHandler", dynlib: expatDll.} -proc SetCommentHandler*(parser: PParser, handler: TCommentHandler){.cdecl, +proc setCommentHandler*(parser: PParser, handler: TCommentHandler){.cdecl, importc: "XML_SetCommentHandler", dynlib: expatDll.} -proc SetCdataSectionHandler*(parser: PParser, start: TStartCdataSectionHandler, +proc setCdataSectionHandler*(parser: PParser, start: TStartCdataSectionHandler, endHandler: TEndCdataSectionHandler){.cdecl, importc: "XML_SetCdataSectionHandler", dynlib: expatDll.} -proc SetStartCdataSectionHandler*(parser: PParser, +proc setStartCdataSectionHandler*(parser: PParser, start: TStartCdataSectionHandler){.cdecl, importc: "XML_SetStartCdataSectionHandler", dynlib: expatDll.} -proc SetEndCdataSectionHandler*(parser: PParser, +proc setEndCdataSectionHandler*(parser: PParser, endHandler: TEndCdataSectionHandler){.cdecl, importc: "XML_SetEndCdataSectionHandler", dynlib: expatDll.} # This sets the default handler and also inhibits expansion of @@ -483,42 +483,42 @@ proc SetEndCdataSectionHandler*(parser: PParser, # default handler, or to the skipped entity handler, if one is set. # -proc SetDefaultHandler*(parser: PParser, handler: TDefaultHandler){.cdecl, +proc setDefaultHandler*(parser: PParser, handler: TDefaultHandler){.cdecl, importc: "XML_SetDefaultHandler", dynlib: expatDll.} # This sets the default handler but does not inhibit expansion of # internal entities. The entity reference will not be passed to the # default handler. # -proc SetDefaultHandlerExpand*(parser: PParser, handler: TDefaultHandler){.cdecl, +proc setDefaultHandlerExpand*(parser: PParser, handler: TDefaultHandler){.cdecl, importc: "XML_SetDefaultHandlerExpand", dynlib: expatDll.} -proc SetDoctypeDeclHandler*(parser: PParser, start: TStartDoctypeDeclHandler, +proc setDoctypeDeclHandler*(parser: PParser, start: TStartDoctypeDeclHandler, endHandler: TEndDoctypeDeclHandler){.cdecl, importc: "XML_SetDoctypeDeclHandler", dynlib: expatDll.} -proc SetStartDoctypeDeclHandler*(parser: PParser, +proc setStartDoctypeDeclHandler*(parser: PParser, start: TStartDoctypeDeclHandler){.cdecl, importc: "XML_SetStartDoctypeDeclHandler", dynlib: expatDll.} -proc SetEndDoctypeDeclHandler*(parser: PParser, +proc setEndDoctypeDeclHandler*(parser: PParser, endHandler: TEndDoctypeDeclHandler){.cdecl, importc: "XML_SetEndDoctypeDeclHandler", dynlib: expatDll.} -proc SetUnparsedEntityDeclHandler*(parser: PParser, +proc setUnparsedEntityDeclHandler*(parser: PParser, handler: TUnparsedEntityDeclHandler){.cdecl, importc: "XML_SetUnparsedEntityDeclHandler", dynlib: expatDll.} -proc SetNotationDeclHandler*(parser: PParser, handler: TNotationDeclHandler){. +proc setNotationDeclHandler*(parser: PParser, handler: TNotationDeclHandler){. cdecl, importc: "XML_SetNotationDeclHandler", dynlib: expatDll.} -proc SetNamespaceDeclHandler*(parser: PParser, +proc setNamespaceDeclHandler*(parser: PParser, start: TStartNamespaceDeclHandler, endHandler: TEndNamespaceDeclHandler){.cdecl, importc: "XML_SetNamespaceDeclHandler", dynlib: expatDll.} -proc SetStartNamespaceDeclHandler*(parser: PParser, +proc setStartNamespaceDeclHandler*(parser: PParser, start: TStartNamespaceDeclHandler){.cdecl, importc: "XML_SetStartNamespaceDeclHandler", dynlib: expatDll.} -proc SetEndNamespaceDeclHandler*(parser: PParser, +proc setEndNamespaceDeclHandler*(parser: PParser, endHandler: TEndNamespaceDeclHandler){.cdecl, importc: "XML_SetEndNamespaceDeclHandler", dynlib: expatDll.} -proc SetNotStandaloneHandler*(parser: PParser, handler: TNotStandaloneHandler){. +proc setNotStandaloneHandler*(parser: PParser, handler: TNotStandaloneHandler){. cdecl, importc: "XML_SetNotStandaloneHandler", dynlib: expatDll.} -proc SetExternalEntityRefHandler*(parser: PParser, +proc setExternalEntityRefHandler*(parser: PParser, handler: TExternalEntityRefHandler){.cdecl, importc: "XML_SetExternalEntityRefHandler", dynlib: expatDll.} # If a non-NULL value for arg is specified here, then it will be @@ -526,11 +526,11 @@ proc SetExternalEntityRefHandler*(parser: PParser, # instead of the parser object. # -proc SetExternalEntityRefHandlerArg*(parser: PParser, arg: pointer){.cdecl, +proc setExternalEntityRefHandlerArg*(parser: PParser, arg: pointer){.cdecl, importc: "XML_SetExternalEntityRefHandlerArg", dynlib: expatDll.} -proc SetSkippedEntityHandler*(parser: PParser, handler: TSkippedEntityHandler){. +proc setSkippedEntityHandler*(parser: PParser, handler: TSkippedEntityHandler){. cdecl, importc: "XML_SetSkippedEntityHandler", dynlib: expatDll.} -proc SetUnknownEncodingHandler*(parser: PParser, +proc setUnknownEncodingHandler*(parser: PParser, handler: TUnknownEncodingHandler, encodingHandlerData: pointer){.cdecl, importc: "XML_SetUnknownEncodingHandler", dynlib: expatDll.} @@ -539,7 +539,7 @@ proc SetUnknownEncodingHandler*(parser: PParser, # corresponding markup to be passed to the default handler. # -proc DefaultCurrent*(parser: PParser){.cdecl, importc: "XML_DefaultCurrent", +proc defaultCurrent*(parser: PParser){.cdecl, importc: "XML_DefaultCurrent", dynlib: expatDll.} # If do_nst is non-zero, and namespace processing is in effect, and # a name has a prefix (i.e. an explicit namespace qualifier) then @@ -555,15 +555,15 @@ proc DefaultCurrent*(parser: PParser){.cdecl, importc: "XML_DefaultCurrent", # XML_ParseBuffer has no effect. # -proc SetReturnNSTriplet*(parser: PParser, do_nst: cint){.cdecl, +proc setReturnNSTriplet*(parser: PParser, doNst: cint){.cdecl, importc: "XML_SetReturnNSTriplet", dynlib: expatDll.} # This value is passed as the userData argument to callbacks. -proc SetUserData*(parser: PParser, userData: pointer){.cdecl, +proc setUserData*(parser: PParser, userData: pointer){.cdecl, importc: "XML_SetUserData", dynlib: expatDll.} # Returns the last value set by XML_SetUserData or NULL. -template GetUserData*(parser: expr): expr = +template getUserData*(parser: expr): expr = (cast[ptr pointer]((parser))[] ) # This is equivalent to supplying an encoding argument to @@ -573,14 +573,14 @@ template GetUserData*(parser: expr): expr = # has no effect and returns XML_STATUS_ERROR. # -proc SetEncoding*(parser: PParser, encoding: cstring): TStatus{.cdecl, +proc setEncoding*(parser: PParser, encoding: cstring): TStatus{.cdecl, importc: "XML_SetEncoding", dynlib: expatDll.} # If this function is called, then the parser will be passed as the # first argument to callbacks instead of userData. The userData will # still be accessible using XML_GetUserData. # -proc UseParserAsHandlerArg*(parser: PParser){.cdecl, +proc useParserAsHandlerArg*(parser: PParser){.cdecl, importc: "XML_UseParserAsHandlerArg", dynlib: expatDll.} # If useDTD == XML_TRUE is passed to this function, then the parser # will assume that there is an external subset, even if none is @@ -601,7 +601,7 @@ proc UseParserAsHandlerArg*(parser: PParser){.cdecl, # XML_ERROR_FEATURE_REQUIRES_XML_DTD. # -proc UseForeignDTD*(parser: PParser, useDTD: Bool): TError{.cdecl, +proc useForeignDTD*(parser: PParser, useDTD: bool): TError{.cdecl, importc: "XML_UseForeignDTD", dynlib: expatDll.} # Sets the base to be used for resolving relative URIs in system # identifiers in declarations. Resolving relative identifiers is @@ -612,9 +612,9 @@ proc UseForeignDTD*(parser: PParser, useDTD: Bool): TError{.cdecl, # XML_STATUS_OK otherwise. # -proc SetBase*(parser: PParser, base: cstring): TStatus{.cdecl, +proc setBase*(parser: PParser, base: cstring): TStatus{.cdecl, importc: "XML_SetBase", dynlib: expatDll.} -proc GetBase*(parser: PParser): cstring{.cdecl, importc: "XML_GetBase", +proc getBase*(parser: PParser): cstring{.cdecl, importc: "XML_GetBase", dynlib: expatDll.} # Returns the number of the attribute/value pairs passed in last call # to the XML_StartElementHandler that were specified in the start-tag @@ -623,7 +623,7 @@ proc GetBase*(parser: PParser): cstring{.cdecl, importc: "XML_GetBase", # XML_StartElementHandler. # -proc GetSpecifiedAttributeCount*(parser: PParser): cint{.cdecl, +proc getSpecifiedAttributeCount*(parser: PParser): cint{.cdecl, importc: "XML_GetSpecifiedAttributeCount", dynlib: expatDll.} # Returns the index of the ID attribute passed in the last call to # XML_StartElementHandler, or -1 if there is no ID attribute. Each @@ -631,7 +631,7 @@ proc GetSpecifiedAttributeCount*(parser: PParser): cint{.cdecl, # index into the atts array passed to the XML_StartElementHandler. # -proc GetIdAttributeIndex*(parser: PParser): cint{.cdecl, +proc getIdAttributeIndex*(parser: PParser): cint{.cdecl, importc: "XML_GetIdAttributeIndex", dynlib: expatDll.} # Parses some input. Returns XML_STATUS_ERROR if a fatal error is # detected. The last call to XML_Parse must have isFinal true; len @@ -643,11 +643,11 @@ proc GetIdAttributeIndex*(parser: PParser): cint{.cdecl, # values. # -proc Parse*(parser: PParser, s: cstring, len: cint, isFinal: cint): TStatus{. +proc parse*(parser: PParser, s: cstring, len: cint, isFinal: cint): TStatus{. cdecl, importc: "XML_Parse", dynlib: expatDll.} -proc GetBuffer*(parser: PParser, len: cint): pointer{.cdecl, +proc getBuffer*(parser: PParser, len: cint): pointer{.cdecl, importc: "XML_GetBuffer", dynlib: expatDll.} -proc ParseBuffer*(parser: PParser, len: cint, isFinal: cint): TStatus{.cdecl, +proc parseBuffer*(parser: PParser, len: cint, isFinal: cint): TStatus{.cdecl, importc: "XML_ParseBuffer", dynlib: expatDll.} # Stops parsing, causing XML_Parse() or XML_ParseBuffer() to return. # Must be called from within a call-back handler, except when aborting @@ -681,7 +681,7 @@ proc ParseBuffer*(parser: PParser, len: cint, isFinal: cint): TStatus{.cdecl, # When suspended, parsing can be resumed by calling XML_ResumeParser(). # -proc StopParser*(parser: PParser, resumable: Bool): TStatus{.cdecl, +proc stopParser*(parser: PParser, resumable: bool): TStatus{.cdecl, importc: "XML_StopParser", dynlib: expatDll.} # Resumes parsing after it has been suspended with XML_StopParser(). # Must not be called from within a handler call-back. Returns same @@ -696,14 +696,14 @@ proc StopParser*(parser: PParser, resumable: Bool): TStatus{.cdecl, # application to call XML_ResumeParser() on it at the appropriate moment. # -proc ResumeParser*(parser: PParser): TStatus{.cdecl, +proc resumeParser*(parser: PParser): TStatus{.cdecl, importc: "XML_ResumeParser", dynlib: expatDll.} type TParsing* = enum INITIALIZED, PARSING, FINISHED, SUSPENDED TParsingStatus*{.pure, final.} = object parsing*: TParsing - finalBuffer*: Bool + finalBuffer*: bool # Returns status of parser with respect to being initialized, parsing, @@ -712,7 +712,7 @@ type # XXX with XML_FINISHED_OK or XML_FINISHED_ERROR replacing XML_FINISHED # -proc GetParsingStatus*(parser: PParser, status: ptr TParsingStatus){.cdecl, +proc getParsingStatus*(parser: PParser, status: ptr TParsingStatus){.cdecl, importc: "XML_GetParsingStatus", dynlib: expatDll.} # Creates an XML_Parser object that can parse an external general # entity; context is a '\0'-terminated string specifying the parse @@ -731,7 +731,7 @@ proc GetParsingStatus*(parser: PParser, status: ptr TParsingStatus){.cdecl, # Otherwise returns a new XML_Parser object. # -proc ExternalEntityParserCreate*(parser: PParser, context: cstring, +proc externalEntityParserCreate*(parser: PParser, context: cstring, encoding: cstring): PParser{.cdecl, importc: "XML_ExternalEntityParserCreate", dynlib: expatDll.} type @@ -763,13 +763,13 @@ type # XML_ParseBuffer, then it has no effect and will always return 0. # -proc SetParamEntityParsing*(parser: PParser, parsing: TParamEntityParsing): cint{. +proc setParamEntityParsing*(parser: PParser, parsing: TParamEntityParsing): cint{. cdecl, importc: "XML_SetParamEntityParsing", dynlib: expatDll.} # If XML_Parse or XML_ParseBuffer have returned XML_STATUS_ERROR, then # XML_GetErrorCode returns information about the error. # -proc GetErrorCode*(parser: PParser): TError{.cdecl, importc: "XML_GetErrorCode", +proc getErrorCode*(parser: PParser): TError{.cdecl, importc: "XML_GetErrorCode", dynlib: expatDll.} # These functions return information about the current parse # location. They may be called from any callback called to report @@ -788,17 +788,17 @@ proc GetErrorCode*(parser: PParser): TError{.cdecl, importc: "XML_GetErrorCode", # parse event, as described above. # -proc GetCurrentLineNumber*(parser: PParser): int{.cdecl, +proc getCurrentLineNumber*(parser: PParser): int{.cdecl, importc: "XML_GetCurrentLineNumber", dynlib: expatDll.} -proc GetCurrentColumnNumber*(parser: PParser): int{.cdecl, +proc getCurrentColumnNumber*(parser: PParser): int{.cdecl, importc: "XML_GetCurrentColumnNumber", dynlib: expatDll.} -proc GetCurrentByteIndex*(parser: PParser): int{.cdecl, +proc getCurrentByteIndex*(parser: PParser): int{.cdecl, importc: "XML_GetCurrentByteIndex", dynlib: expatDll.} # Return the number of bytes in the current event. # Returns 0 if the event is in an internal entity. # -proc GetCurrentByteCount*(parser: PParser): cint{.cdecl, +proc getCurrentByteCount*(parser: PParser): cint{.cdecl, importc: "XML_GetCurrentByteCount", dynlib: expatDll.} # If XML_CONTEXT_BYTES is defined, returns the input buffer, sets # the integer pointed to by offset to the offset within this buffer @@ -811,31 +811,31 @@ proc GetCurrentByteCount*(parser: PParser): cint{.cdecl, # the handler that makes the call. # -proc GetInputContext*(parser: PParser, offset: ptr cint, size: ptr cint): cstring{. +proc getInputContext*(parser: PParser, offset: ptr cint, size: ptr cint): cstring{. cdecl, importc: "XML_GetInputContext", dynlib: expatDll.} # Frees the content model passed to the element declaration handler -proc FreeContentModel*(parser: PParser, model: ptr TContent){.cdecl, +proc freeContentModel*(parser: PParser, model: ptr TContent){.cdecl, importc: "XML_FreeContentModel", dynlib: expatDll.} # Exposing the memory handling functions used in Expat -proc MemMalloc*(parser: PParser, size: int): pointer{.cdecl, +proc memMalloc*(parser: PParser, size: int): pointer{.cdecl, importc: "XML_MemMalloc", dynlib: expatDll.} -proc MemRealloc*(parser: PParser, p: pointer, size: int): pointer{.cdecl, +proc memRealloc*(parser: PParser, p: pointer, size: int): pointer{.cdecl, importc: "XML_MemRealloc", dynlib: expatDll.} -proc MemFree*(parser: PParser, p: pointer){.cdecl, importc: "XML_MemFree", +proc memFree*(parser: PParser, p: pointer){.cdecl, importc: "XML_MemFree", dynlib: expatDll.} # Frees memory used by the parser. -proc ParserFree*(parser: PParser){.cdecl, importc: "XML_ParserFree", +proc parserFree*(parser: PParser){.cdecl, importc: "XML_ParserFree", dynlib: expatDll.} # Returns a string describing the error. -proc ErrorString*(code: TError): cstring{.cdecl, importc: "XML_ErrorString", +proc errorString*(code: TError): cstring{.cdecl, importc: "XML_ErrorString", dynlib: expatDll.} # Return a string containing the version number of this expat -proc ExpatVersion*(): cstring{.cdecl, importc: "XML_ExpatVersion", +proc expatVersion*(): cstring{.cdecl, importc: "XML_ExpatVersion", dynlib: expatDll.} type TExpat_Version*{.pure, final.} = object @@ -848,7 +848,7 @@ type # number information for this version of expat. # -proc ExpatVersionInfo*(): TExpat_Version{.cdecl, +proc expatVersionInfo*(): TExpatVersion{.cdecl, importc: "XML_ExpatVersionInfo", dynlib: expatDll.} # Added in Expat 1.95.5. @@ -863,7 +863,7 @@ type value*: int -proc GetFeatureList*(): ptr TFeature{.cdecl, importc: "XML_GetFeatureList", +proc getFeatureList*(): ptr TFeature{.cdecl, importc: "XML_GetFeatureList", dynlib: expatDll.} # Expat follows the GNU/Linux convention of odd number minor version for # beta/development releases and even number minor version for stable diff --git a/lib/wrappers/iup.nim b/lib/wrappers/iup.nim index 2ee1ca3ee..27ab7d870 100644 --- a/lib/wrappers/iup.nim +++ b/lib/wrappers/iup.nim @@ -46,321 +46,321 @@ const constIUP_VERSION_DATE* = "2009/07/18" type - Ihandle {.pure.} = object + Ihandle = object PIhandle* = ptr Ihandle Icallback* = proc (arg: PIhandle): cint {.cdecl.} -# pre-definided dialogs -proc FileDlg*: PIhandle {.importc: "IupFileDlg", dynlib: dllname, cdecl.} -proc MessageDlg*: PIhandle {.importc: "IupMessageDlg", dynlib: dllname, cdecl.} -proc ColorDlg*: PIhandle {.importc: "IupColorDlg", dynlib: dllname, cdecl.} -proc FontDlg*: PIhandle {.importc: "IupFontDlg", dynlib: dllname, cdecl.} +# pre-defineded dialogs +proc fileDlg*: PIhandle {.importc: "IupFileDlg", dynlib: dllname, cdecl.} +proc messageDlg*: PIhandle {.importc: "IupMessageDlg", dynlib: dllname, cdecl.} +proc colorDlg*: PIhandle {.importc: "IupColorDlg", dynlib: dllname, cdecl.} +proc fontDlg*: PIhandle {.importc: "IupFontDlg", dynlib: dllname, cdecl.} -proc GetFile*(arq: cstring): cint {. +proc getFile*(arq: cstring): cint {. importc: "IupGetFile", dynlib: dllname, cdecl.} -proc Message*(title, msg: cstring) {. +proc message*(title, msg: cstring) {. importc: "IupMessage", dynlib: dllname, cdecl.} -proc Messagef*(title, format: cstring) {. +proc messagef*(title, format: cstring) {. importc: "IupMessagef", dynlib: dllname, cdecl, varargs.} -proc Alarm*(title, msg, b1, b2, b3: cstring): cint {. +proc alarm*(title, msg, b1, b2, b3: cstring): cint {. importc: "IupAlarm", dynlib: dllname, cdecl.} -proc Scanf*(format: cstring): cint {. +proc scanf*(format: cstring): cint {. importc: "IupScanf", dynlib: dllname, cdecl, varargs.} -proc ListDialog*(theType: cint, title: cstring, size: cint, - list: cstringArray, op, max_col, max_lin: cint, +proc listDialog*(theType: cint, title: cstring, size: cint, + list: cstringArray, op, maxCol, maxLin: cint, marks: ptr cint): cint {. importc: "IupListDialog", dynlib: dllname, cdecl.} -proc GetText*(title, text: cstring): cint {. +proc getText*(title, text: cstring): cint {. importc: "IupGetText", dynlib: dllname, cdecl.} -proc GetColor*(x, y: cint, r, g, b: var byte): cint {. +proc getColor*(x, y: cint, r, g, b: var byte): cint {. importc: "IupGetColor", dynlib: dllname, cdecl.} type - Iparamcb* = proc (dialog: PIhandle, param_index: cint, - user_data: pointer): cint {.cdecl.} + Iparamcb* = proc (dialog: PIhandle, paramIndex: cint, + userData: pointer): cint {.cdecl.} -proc GetParam*(title: cstring, action: Iparamcb, user_data: pointer, +proc getParam*(title: cstring, action: Iparamcb, userData: pointer, format: cstring): cint {. importc: "IupGetParam", cdecl, varargs, dynlib: dllname.} -proc GetParamv*(title: cstring, action: Iparamcb, user_data: pointer, - format: cstring, param_count, param_extra: cint, - param_data: pointer): cint {. +proc getParamv*(title: cstring, action: Iparamcb, userData: pointer, + format: cstring, paramCount, paramExtra: cint, + paramData: pointer): cint {. importc: "IupGetParamv", cdecl, dynlib: dllname.} # Functions -proc Open*(argc: ptr cint, argv: ptr cstringArray): cint {. +proc open*(argc: ptr cint, argv: ptr cstringArray): cint {. importc: "IupOpen", cdecl, dynlib: dllname.} -proc Close*() {.importc: "IupClose", cdecl, dynlib: dllname.} -proc ImageLibOpen*() {.importc: "IupImageLibOpen", cdecl, dynlib: dllname.} +proc close*() {.importc: "IupClose", cdecl, dynlib: dllname.} +proc imageLibOpen*() {.importc: "IupImageLibOpen", cdecl, dynlib: dllname.} -proc MainLoop*(): cint {.importc: "IupMainLoop", cdecl, dynlib: dllname, +proc mainLoop*(): cint {.importc: "IupMainLoop", cdecl, dynlib: dllname, discardable.} -proc LoopStep*(): cint {.importc: "IupLoopStep", cdecl, dynlib: dllname, +proc loopStep*(): cint {.importc: "IupLoopStep", cdecl, dynlib: dllname, discardable.} -proc MainLoopLevel*(): cint {.importc: "IupMainLoopLevel", cdecl, +proc mainLoopLevel*(): cint {.importc: "IupMainLoopLevel", cdecl, dynlib: dllname, discardable.} -proc Flush*() {.importc: "IupFlush", cdecl, dynlib: dllname.} -proc ExitLoop*() {.importc: "IupExitLoop", cdecl, dynlib: dllname.} +proc flush*() {.importc: "IupFlush", cdecl, dynlib: dllname.} +proc exitLoop*() {.importc: "IupExitLoop", cdecl, dynlib: dllname.} -proc Update*(ih: PIhandle) {.importc: "IupUpdate", cdecl, dynlib: dllname.} -proc UpdateChildren*(ih: PIhandle) {.importc: "IupUpdateChildren", cdecl, dynlib: dllname.} -proc Redraw*(ih: PIhandle, children: cint) {.importc: "IupRedraw", cdecl, dynlib: dllname.} -proc Refresh*(ih: PIhandle) {.importc: "IupRefresh", cdecl, dynlib: dllname.} +proc update*(ih: PIhandle) {.importc: "IupUpdate", cdecl, dynlib: dllname.} +proc updateChildren*(ih: PIhandle) {.importc: "IupUpdateChildren", cdecl, dynlib: dllname.} +proc redraw*(ih: PIhandle, children: cint) {.importc: "IupRedraw", cdecl, dynlib: dllname.} +proc refresh*(ih: PIhandle) {.importc: "IupRefresh", cdecl, dynlib: dllname.} -proc MapFont*(iupfont: cstring): cstring {.importc: "IupMapFont", cdecl, dynlib: dllname.} -proc UnMapFont*(driverfont: cstring): cstring {.importc: "IupUnMapFont", cdecl, dynlib: dllname.} -proc Help*(url: cstring): cint {.importc: "IupHelp", cdecl, dynlib: dllname.} -proc Load*(filename: cstring): cstring {.importc: "IupLoad", cdecl, dynlib: dllname.} +proc mapFont*(iupfont: cstring): cstring {.importc: "IupMapFont", cdecl, dynlib: dllname.} +proc unMapFont*(driverfont: cstring): cstring {.importc: "IupUnMapFont", cdecl, dynlib: dllname.} +proc help*(url: cstring): cint {.importc: "IupHelp", cdecl, dynlib: dllname.} +proc load*(filename: cstring): cstring {.importc: "IupLoad", cdecl, dynlib: dllname.} -proc IupVersion*(): cstring {.importc: "IupVersion", cdecl, dynlib: dllname.} -proc IupVersionDate*(): cstring {.importc: "IupVersionDate", cdecl, dynlib: dllname.} -proc IupVersionNumber*(): cint {.importc: "IupVersionNumber", cdecl, dynlib: dllname.} -proc SetLanguage*(lng: cstring) {.importc: "IupSetLanguage", cdecl, dynlib: dllname.} -proc GetLanguage*(): cstring {.importc: "IupGetLanguage", cdecl, dynlib: dllname.} +proc iupVersion*(): cstring {.importc: "IupVersion", cdecl, dynlib: dllname.} +proc iupVersionDate*(): cstring {.importc: "IupVersionDate", cdecl, dynlib: dllname.} +proc iupVersionNumber*(): cint {.importc: "IupVersionNumber", cdecl, dynlib: dllname.} +proc setLanguage*(lng: cstring) {.importc: "IupSetLanguage", cdecl, dynlib: dllname.} +proc getLanguage*(): cstring {.importc: "IupGetLanguage", cdecl, dynlib: dllname.} -proc Destroy*(ih: PIhandle) {.importc: "IupDestroy", cdecl, dynlib: dllname.} -proc Detach*(child: PIhandle) {.importc: "IupDetach", cdecl, dynlib: dllname.} -proc Append*(ih, child: PIhandle): PIhandle {. +proc destroy*(ih: PIhandle) {.importc: "IupDestroy", cdecl, dynlib: dllname.} +proc detach*(child: PIhandle) {.importc: "IupDetach", cdecl, dynlib: dllname.} +proc append*(ih, child: PIhandle): PIhandle {. importc: "IupAppend", cdecl, dynlib: dllname, discardable.} -proc Insert*(ih, ref_child, child: PIhandle): PIhandle {. +proc insert*(ih, refChild, child: PIhandle): PIhandle {. importc: "IupInsert", cdecl, dynlib: dllname, discardable.} -proc GetChild*(ih: PIhandle, pos: cint): PIhandle {. +proc getChild*(ih: PIhandle, pos: cint): PIhandle {. importc: "IupGetChild", cdecl, dynlib: dllname.} -proc GetChildPos*(ih, child: PIhandle): cint {. +proc getChildPos*(ih, child: PIhandle): cint {. importc: "IupGetChildPos", cdecl, dynlib: dllname.} -proc GetChildCount*(ih: PIhandle): cint {. +proc getChildCount*(ih: PIhandle): cint {. importc: "IupGetChildCount", cdecl, dynlib: dllname.} -proc GetNextChild*(ih, child: PIhandle): PIhandle {. +proc getNextChild*(ih, child: PIhandle): PIhandle {. importc: "IupGetNextChild", cdecl, dynlib: dllname.} -proc GetBrother*(ih: PIhandle): PIhandle {. +proc getBrother*(ih: PIhandle): PIhandle {. importc: "IupGetBrother", cdecl, dynlib: dllname.} -proc GetParent*(ih: PIhandle): PIhandle {. +proc getParent*(ih: PIhandle): PIhandle {. importc: "IupGetParent", cdecl, dynlib: dllname.} -proc GetDialog*(ih: PIhandle): PIhandle {. +proc getDialog*(ih: PIhandle): PIhandle {. importc: "IupGetDialog", cdecl, dynlib: dllname.} -proc GetDialogChild*(ih: PIhandle, name: cstring): PIhandle {. +proc getDialogChild*(ih: PIhandle, name: cstring): PIhandle {. importc: "IupGetDialogChild", cdecl, dynlib: dllname.} -proc Reparent*(ih, new_parent: PIhandle): cint {. +proc reparent*(ih, newParent: PIhandle): cint {. importc: "IupReparent", cdecl, dynlib: dllname.} -proc Popup*(ih: PIhandle, x, y: cint): cint {. +proc popup*(ih: PIhandle, x, y: cint): cint {. importc: "IupPopup", cdecl, dynlib: dllname, discardable.} -proc Show*(ih: PIhandle): cint {. +proc show*(ih: PIhandle): cint {. importc: "IupShow", cdecl, dynlib: dllname, discardable.} -proc ShowXY*(ih: PIhandle, x, y: cint): cint {. +proc showXY*(ih: PIhandle, x, y: cint): cint {. importc: "IupShowXY", cdecl, dynlib: dllname, discardable.} -proc Hide*(ih: PIhandle): cint {. +proc hide*(ih: PIhandle): cint {. importc: "IupHide", cdecl, dynlib: dllname, discardable.} -proc Map*(ih: PIhandle): cint {. +proc map*(ih: PIhandle): cint {. importc: "IupMap", cdecl, dynlib: dllname, discardable.} -proc Unmap*(ih: PIhandle) {. +proc unmap*(ih: PIhandle) {. importc: "IupUnmap", cdecl, dynlib: dllname, discardable.} -proc SetAttribute*(ih: PIhandle, name, value: cstring) {. +proc setAttribute*(ih: PIhandle, name, value: cstring) {. importc: "IupSetAttribute", cdecl, dynlib: dllname.} -proc StoreAttribute*(ih: PIhandle, name, value: cstring) {. +proc storeAttribute*(ih: PIhandle, name, value: cstring) {. importc: "IupStoreAttribute", cdecl, dynlib: dllname.} -proc SetAttributes*(ih: PIhandle, str: cstring): PIhandle {. +proc setAttributes*(ih: PIhandle, str: cstring): PIhandle {. importc: "IupSetAttributes", cdecl, dynlib: dllname.} -proc GetAttribute*(ih: PIhandle, name: cstring): cstring {. +proc getAttribute*(ih: PIhandle, name: cstring): cstring {. importc: "IupGetAttribute", cdecl, dynlib: dllname.} -proc GetAttributes*(ih: PIhandle): cstring {. +proc getAttributes*(ih: PIhandle): cstring {. importc: "IupGetAttributes", cdecl, dynlib: dllname.} -proc GetInt*(ih: PIhandle, name: cstring): cint {. +proc getInt*(ih: PIhandle, name: cstring): cint {. importc: "IupGetInt", cdecl, dynlib: dllname.} -proc GetInt2*(ih: PIhandle, name: cstring): cint {. +proc getInt2*(ih: PIhandle, name: cstring): cint {. importc: "IupGetInt2", cdecl, dynlib: dllname.} -proc GetIntInt*(ih: PIhandle, name: cstring, i1, i2: var cint): cint {. +proc getIntInt*(ih: PIhandle, name: cstring, i1, i2: var cint): cint {. importc: "IupGetIntInt", cdecl, dynlib: dllname.} -proc GetFloat*(ih: PIhandle, name: cstring): cfloat {. +proc getFloat*(ih: PIhandle, name: cstring): cfloat {. importc: "IupGetFloat", cdecl, dynlib: dllname.} -proc SetfAttribute*(ih: PIhandle, name, format: cstring) {. +proc setfAttribute*(ih: PIhandle, name, format: cstring) {. importc: "IupSetfAttribute", cdecl, dynlib: dllname, varargs.} -proc GetAllAttributes*(ih: PIhandle, names: cstringArray, n: cint): cint {. +proc getAllAttributes*(ih: PIhandle, names: cstringArray, n: cint): cint {. importc: "IupGetAllAttributes", cdecl, dynlib: dllname.} -proc SetAtt*(handle_name: cstring, ih: PIhandle, name: cstring): PIhandle {. +proc setAtt*(handleName: cstring, ih: PIhandle, name: cstring): PIhandle {. importc: "IupSetAtt", cdecl, dynlib: dllname, varargs, discardable.} -proc SetGlobal*(name, value: cstring) {. +proc setGlobal*(name, value: cstring) {. importc: "IupSetGlobal", cdecl, dynlib: dllname.} -proc StoreGlobal*(name, value: cstring) {. +proc storeGlobal*(name, value: cstring) {. importc: "IupStoreGlobal", cdecl, dynlib: dllname.} -proc GetGlobal*(name: cstring): cstring {. +proc getGlobal*(name: cstring): cstring {. importc: "IupGetGlobal", cdecl, dynlib: dllname.} -proc SetFocus*(ih: PIhandle): PIhandle {. +proc setFocus*(ih: PIhandle): PIhandle {. importc: "IupSetFocus", cdecl, dynlib: dllname.} -proc GetFocus*(): PIhandle {. +proc getFocus*(): PIhandle {. importc: "IupGetFocus", cdecl, dynlib: dllname.} -proc PreviousField*(ih: PIhandle): PIhandle {. +proc previousField*(ih: PIhandle): PIhandle {. importc: "IupPreviousField", cdecl, dynlib: dllname.} -proc NextField*(ih: PIhandle): PIhandle {. +proc nextField*(ih: PIhandle): PIhandle {. importc: "IupNextField", cdecl, dynlib: dllname.} -proc GetCallback*(ih: PIhandle, name: cstring): Icallback {. +proc getCallback*(ih: PIhandle, name: cstring): Icallback {. importc: "IupGetCallback", cdecl, dynlib: dllname.} -proc SetCallback*(ih: PIhandle, name: cstring, func: Icallback): Icallback {. +proc setCallback*(ih: PIhandle, name: cstring, fn: Icallback): Icallback {. importc: "IupSetCallback", cdecl, dynlib: dllname, discardable.} -proc SetCallbacks*(ih: PIhandle, name: cstring, func: Icallback): PIhandle {. +proc setCallbacks*(ih: PIhandle, name: cstring, fn: Icallback): PIhandle {. importc: "IupSetCallbacks", cdecl, dynlib: dllname, varargs, discardable.} -proc GetFunction*(name: cstring): Icallback {. +proc getFunction*(name: cstring): Icallback {. importc: "IupGetFunction", cdecl, dynlib: dllname.} -proc SetFunction*(name: cstring, func: Icallback): Icallback {. +proc setFunction*(name: cstring, fn: Icallback): Icallback {. importc: "IupSetFunction", cdecl, dynlib: dllname, discardable.} -proc GetActionName*(): cstring {. +proc getActionName*(): cstring {. importc: "IupGetActionName", cdecl, dynlib: dllname.} -proc GetHandle*(name: cstring): PIhandle {. +proc getHandle*(name: cstring): PIhandle {. importc: "IupGetHandle", cdecl, dynlib: dllname.} -proc SetHandle*(name: cstring, ih: PIhandle): PIhandle {. +proc setHandle*(name: cstring, ih: PIhandle): PIhandle {. importc: "IupSetHandle", cdecl, dynlib: dllname.} -proc GetAllNames*(names: cstringArray, n: cint): cint {. +proc getAllNames*(names: cstringArray, n: cint): cint {. importc: "IupGetAllNames", cdecl, dynlib: dllname.} -proc GetAllDialogs*(names: cstringArray, n: cint): cint {. +proc getAllDialogs*(names: cstringArray, n: cint): cint {. importc: "IupGetAllDialogs", cdecl, dynlib: dllname.} -proc GetName*(ih: PIhandle): cstring {. +proc getName*(ih: PIhandle): cstring {. importc: "IupGetName", cdecl, dynlib: dllname.} -proc SetAttributeHandle*(ih: PIhandle, name: cstring, ih_named: PIhandle) {. +proc setAttributeHandle*(ih: PIhandle, name: cstring, ihNamed: PIhandle) {. importc: "IupSetAttributeHandle", cdecl, dynlib: dllname.} -proc GetAttributeHandle*(ih: PIhandle, name: cstring): PIhandle {. +proc getAttributeHandle*(ih: PIhandle, name: cstring): PIhandle {. importc: "IupGetAttributeHandle", cdecl, dynlib: dllname.} -proc GetClassName*(ih: PIhandle): cstring {. +proc getClassName*(ih: PIhandle): cstring {. importc: "IupGetClassName", cdecl, dynlib: dllname.} -proc GetClassType*(ih: PIhandle): cstring {. +proc getClassType*(ih: PIhandle): cstring {. importc: "IupGetClassType", cdecl, dynlib: dllname.} -proc GetClassAttributes*(classname: cstring, names: cstringArray, +proc getClassAttributes*(classname: cstring, names: cstringArray, n: cint): cint {. importc: "IupGetClassAttributes", cdecl, dynlib: dllname.} -proc SaveClassAttributes*(ih: PIhandle) {. +proc saveClassAttributes*(ih: PIhandle) {. importc: "IupSaveClassAttributes", cdecl, dynlib: dllname.} -proc SetClassDefaultAttribute*(classname, name, value: cstring) {. +proc setClassDefaultAttribute*(classname, name, value: cstring) {. importc: "IupSetClassDefaultAttribute", cdecl, dynlib: dllname.} -proc Create*(classname: cstring): PIhandle {. +proc create*(classname: cstring): PIhandle {. importc: "IupCreate", cdecl, dynlib: dllname.} -proc Createv*(classname: cstring, params: pointer): PIhandle {. +proc createv*(classname: cstring, params: pointer): PIhandle {. importc: "IupCreatev", cdecl, dynlib: dllname.} -proc Createp*(classname: cstring, first: pointer): PIhandle {. +proc createp*(classname: cstring, first: pointer): PIhandle {. importc: "IupCreatep", cdecl, dynlib: dllname, varargs.} -proc Fill*(): PIhandle {.importc: "IupFill", cdecl, dynlib: dllname.} -proc Radio*(child: PIhandle): PIhandle {. +proc fill*(): PIhandle {.importc: "IupFill", cdecl, dynlib: dllname.} +proc radio*(child: PIhandle): PIhandle {. importc: "IupRadio", cdecl, dynlib: dllname.} -proc Vbox*(child: PIhandle): PIhandle {. +proc vbox*(child: PIhandle): PIhandle {. importc: "IupVbox", cdecl, dynlib: dllname, varargs.} -proc Vboxv*(children: ptr PIhandle): PIhandle {. +proc vboxv*(children: ptr PIhandle): PIhandle {. importc: "IupVboxv", cdecl, dynlib: dllname.} -proc Zbox*(child: PIhandle): PIhandle {. +proc zbox*(child: PIhandle): PIhandle {. importc: "IupZbox", cdecl, dynlib: dllname, varargs.} -proc Zboxv*(children: ptr PIhandle): PIhandle {. +proc zboxv*(children: ptr PIhandle): PIhandle {. importc: "IupZboxv", cdecl, dynlib: dllname.} -proc Hbox*(child: PIhandle): PIhandle {. +proc hbox*(child: PIhandle): PIhandle {. importc: "IupHbox", cdecl, dynlib: dllname, varargs.} -proc Hboxv*(children: ptr PIhandle): PIhandle {. +proc hboxv*(children: ptr PIhandle): PIhandle {. importc: "IupHboxv", cdecl, dynlib: dllname.} -proc Normalizer*(ih_first: PIhandle): PIhandle {. +proc normalizer*(ihFirst: PIhandle): PIhandle {. importc: "IupNormalizer", cdecl, dynlib: dllname, varargs.} -proc Normalizerv*(ih_list: ptr PIhandle): PIhandle {. +proc normalizerv*(ihList: ptr PIhandle): PIhandle {. importc: "IupNormalizerv", cdecl, dynlib: dllname.} -proc Cbox*(child: PIhandle): PIhandle {. +proc cbox*(child: PIhandle): PIhandle {. importc: "IupCbox", cdecl, dynlib: dllname, varargs.} -proc Cboxv*(children: ptr PIhandle): PIhandle {. +proc cboxv*(children: ptr PIhandle): PIhandle {. importc: "IupCboxv", cdecl, dynlib: dllname.} -proc Sbox*(child: PIhandle): PIhandle {. +proc sbox*(child: PIhandle): PIhandle {. importc: "IupSbox", cdecl, dynlib: dllname.} -proc Frame*(child: PIhandle): PIhandle {. +proc frame*(child: PIhandle): PIhandle {. importc: "IupFrame", cdecl, dynlib: dllname.} -proc Image*(width, height: cint, pixmap: pointer): PIhandle {. +proc image*(width, height: cint, pixmap: pointer): PIhandle {. importc: "IupImage", cdecl, dynlib: dllname.} -proc ImageRGB*(width, height: cint, pixmap: pointer): PIhandle {. +proc imageRGB*(width, height: cint, pixmap: pointer): PIhandle {. importc: "IupImageRGB", cdecl, dynlib: dllname.} -proc ImageRGBA*(width, height: cint, pixmap: pointer): PIhandle {. +proc imageRGBA*(width, height: cint, pixmap: pointer): PIhandle {. importc: "IupImageRGBA", cdecl, dynlib: dllname.} -proc Item*(title, action: cstring): PIhandle {. +proc item*(title, action: cstring): PIhandle {. importc: "IupItem", cdecl, dynlib: dllname.} -proc Submenu*(title: cstring, child: PIhandle): PIhandle {. +proc submenu*(title: cstring, child: PIhandle): PIhandle {. importc: "IupSubmenu", cdecl, dynlib: dllname.} -proc Separator*(): PIhandle {. +proc separator*(): PIhandle {. importc: "IupSeparator", cdecl, dynlib: dllname.} -proc Menu*(child: PIhandle): PIhandle {. +proc menu*(child: PIhandle): PIhandle {. importc: "IupMenu", cdecl, dynlib: dllname, varargs.} -proc Menuv*(children: ptr PIhandle): PIhandle {. +proc menuv*(children: ptr PIhandle): PIhandle {. importc: "IupMenuv", cdecl, dynlib: dllname.} -proc Button*(title, action: cstring): PIhandle {. +proc button*(title, action: cstring): PIhandle {. importc: "IupButton", cdecl, dynlib: dllname.} -proc Canvas*(action: cstring): PIhandle {. +proc canvas*(action: cstring): PIhandle {. importc: "IupCanvas", cdecl, dynlib: dllname.} -proc Dialog*(child: PIhandle): PIhandle {. +proc dialog*(child: PIhandle): PIhandle {. importc: "IupDialog", cdecl, dynlib: dllname.} -proc User*(): PIhandle {. +proc user*(): PIhandle {. importc: "IupUser", cdecl, dynlib: dllname.} -proc Label*(title: cstring): PIhandle {. +proc label*(title: cstring): PIhandle {. importc: "IupLabel", cdecl, dynlib: dllname.} -proc List*(action: cstring): PIhandle {. +proc list*(action: cstring): PIhandle {. importc: "IupList", cdecl, dynlib: dllname.} -proc Text*(action: cstring): PIhandle {. +proc text*(action: cstring): PIhandle {. importc: "IupText", cdecl, dynlib: dllname.} -proc MultiLine*(action: cstring): PIhandle {. +proc multiLine*(action: cstring): PIhandle {. importc: "IupMultiLine", cdecl, dynlib: dllname.} -proc Toggle*(title, action: cstring): PIhandle {. +proc toggle*(title, action: cstring): PIhandle {. importc: "IupToggle", cdecl, dynlib: dllname.} -proc Timer*(): PIhandle {. +proc timer*(): PIhandle {. importc: "IupTimer", cdecl, dynlib: dllname.} -proc ProgressBar*(): PIhandle {. +proc progressBar*(): PIhandle {. importc: "IupProgressBar", cdecl, dynlib: dllname.} -proc Val*(theType: cstring): PIhandle {. +proc val*(theType: cstring): PIhandle {. importc: "IupVal", cdecl, dynlib: dllname.} -proc Tabs*(child: PIhandle): PIhandle {. +proc tabs*(child: PIhandle): PIhandle {. importc: "IupTabs", cdecl, dynlib: dllname, varargs.} -proc Tabsv*(children: ptr PIhandle): PIhandle {. +proc tabsv*(children: ptr PIhandle): PIhandle {. importc: "IupTabsv", cdecl, dynlib: dllname.} -proc Tree*(): PIhandle {.importc: "IupTree", cdecl, dynlib: dllname.} +proc tree*(): PIhandle {.importc: "IupTree", cdecl, dynlib: dllname.} -proc Spin*(): PIhandle {.importc: "IupSpin", cdecl, dynlib: dllname.} -proc Spinbox*(child: PIhandle): PIhandle {. +proc spin*(): PIhandle {.importc: "IupSpin", cdecl, dynlib: dllname.} +proc spinbox*(child: PIhandle): PIhandle {. importc: "IupSpinbox", cdecl, dynlib: dllname.} # IupText utilities -proc TextConvertLinColToPos*(ih: PIhandle, lin, col: cint, pos: var cint) {. +proc textConvertLinColToPos*(ih: PIhandle, lin, col: cint, pos: var cint) {. importc: "IupTextConvertLinColToPos", cdecl, dynlib: dllname.} -proc TextConvertPosToLinCol*(ih: PIhandle, pos: cint, lin, col: var cint) {. +proc textConvertPosToLinCol*(ih: PIhandle, pos: cint, lin, col: var cint) {. importc: "IupTextConvertPosToLinCol", cdecl, dynlib: dllname.} -proc ConvertXYToPos*(ih: PIhandle, x, y: cint): cint {. +proc convertXYToPos*(ih: PIhandle, x, y: cint): cint {. importc: "IupConvertXYToPos", cdecl, dynlib: dllname.} # IupTree utilities -proc TreeSetUserId*(ih: PIhandle, id: cint, userid: pointer): cint {. +proc treeSetUserId*(ih: PIhandle, id: cint, userid: pointer): cint {. importc: "IupTreeSetUserId", cdecl, dynlib: dllname, discardable.} -proc TreeGetUserId*(ih: PIhandle, id: cint): pointer {. +proc treeGetUserId*(ih: PIhandle, id: cint): pointer {. importc: "IupTreeGetUserId", cdecl, dynlib: dllname.} -proc TreeGetId*(ih: PIhandle, userid: pointer): cint {. +proc treeGetId*(ih: PIhandle, userid: pointer): cint {. importc: "IupTreeGetId", cdecl, dynlib: dllname.} -proc TreeSetAttribute*(ih: PIhandle, name: cstring, id: cint, value: cstring) {. +proc treeSetAttribute*(ih: PIhandle, name: cstring, id: cint, value: cstring) {. importc: "IupTreeSetAttribute", cdecl, dynlib: dllname.} -proc TreeStoreAttribute*(ih: PIhandle, name: cstring, id: cint, value: cstring) {. +proc treeStoreAttribute*(ih: PIhandle, name: cstring, id: cint, value: cstring) {. importc: "IupTreeStoreAttribute", cdecl, dynlib: dllname.} -proc TreeGetAttribute*(ih: PIhandle, name: cstring, id: cint): cstring {. +proc treeGetAttribute*(ih: PIhandle, name: cstring, id: cint): cstring {. importc: "IupTreeGetAttribute", cdecl, dynlib: dllname.} -proc TreeGetInt*(ih: PIhandle, name: cstring, id: cint): cint {. +proc treeGetInt*(ih: PIhandle, name: cstring, id: cint): cint {. importc: "IupTreeGetInt", cdecl, dynlib: dllname.} -proc TreeGetFloat*(ih: PIhandle, name: cstring, id: cint): cfloat {. +proc treeGetFloat*(ih: PIhandle, name: cstring, id: cint): cfloat {. importc: "IupTreeGetFloat", cdecl, dynlib: dllname.} -proc TreeSetfAttribute*(ih: PIhandle, name: cstring, id: cint, format: cstring) {. +proc treeSetfAttribute*(ih: PIhandle, name: cstring, id: cint, format: cstring) {. importc: "IupTreeSetfAttribute", cdecl, dynlib: dllname, varargs.} @@ -434,516 +434,516 @@ const IUP_MASK_INT* = "[+/-]?/d+" IUP_MASK_UINT* = "/d+" -# from 32 to 126, all character sets are equal, -# the key code i the same as the character code. -const - K_SP* = cint(ord(' ')) - K_exclam* = cint(ord('!')) - K_quotedbl* = cint(ord('\"')) - K_numbersign* = cint(ord('#')) - K_dollar* = cint(ord('$')) - K_percent* = cint(ord('%')) - K_ampersand* = cint(ord('&')) - K_apostrophe* = cint(ord('\'')) - K_parentleft* = cint(ord('(')) - K_parentright* = cint(ord(')')) - K_asterisk* = cint(ord('*')) - K_plus* = cint(ord('+')) - K_comma* = cint(ord(',')) - K_minus* = cint(ord('-')) - K_period* = cint(ord('.')) - K_slash* = cint(ord('/')) - K_0* = cint(ord('0')) - K_1* = cint(ord('1')) - K_2* = cint(ord('2')) - K_3* = cint(ord('3')) - K_4* = cint(ord('4')) - K_5* = cint(ord('5')) - K_6* = cint(ord('6')) - K_7* = cint(ord('7')) - K_8* = cint(ord('8')) - K_9* = cint(ord('9')) - K_colon* = cint(ord(':')) - K_semicolon* = cint(ord(';')) - K_less* = cint(ord('<')) - K_equal* = cint(ord('=')) - K_greater* = cint(ord('>')) - K_question* = cint(ord('?')) - K_at* = cint(ord('@')) - K_upperA* = cint(ord('A')) - K_upperB* = cint(ord('B')) - K_upperC* = cint(ord('C')) - K_upperD* = cint(ord('D')) - K_upperE* = cint(ord('E')) - K_upperF* = cint(ord('F')) - K_upperG* = cint(ord('G')) - K_upperH* = cint(ord('H')) - K_upperI* = cint(ord('I')) - K_upperJ* = cint(ord('J')) - K_upperK* = cint(ord('K')) - K_upperL* = cint(ord('L')) - K_upperM* = cint(ord('M')) - K_upperN* = cint(ord('N')) - K_upperO* = cint(ord('O')) - K_upperP* = cint(ord('P')) - K_upperQ* = cint(ord('Q')) - K_upperR* = cint(ord('R')) - K_upperS* = cint(ord('S')) - K_upperT* = cint(ord('T')) - K_upperU* = cint(ord('U')) - K_upperV* = cint(ord('V')) - K_upperW* = cint(ord('W')) - K_upperX* = cint(ord('X')) - K_upperY* = cint(ord('Y')) - K_upperZ* = cint(ord('Z')) - K_bracketleft* = cint(ord('[')) - K_backslash* = cint(ord('\\')) - K_bracketright* = cint(ord(']')) - K_circum* = cint(ord('^')) - K_underscore* = cint(ord('_')) - K_grave* = cint(ord('`')) - K_lowera* = cint(ord('a')) - K_lowerb* = cint(ord('b')) - K_lowerc* = cint(ord('c')) - K_lowerd* = cint(ord('d')) - K_lowere* = cint(ord('e')) - K_lowerf* = cint(ord('f')) - K_lowerg* = cint(ord('g')) - K_lowerh* = cint(ord('h')) - K_loweri* = cint(ord('i')) - K_lowerj* = cint(ord('j')) - K_lowerk* = cint(ord('k')) - K_lowerl* = cint(ord('l')) - K_lowerm* = cint(ord('m')) - K_lowern* = cint(ord('n')) - K_lowero* = cint(ord('o')) - K_lowerp* = cint(ord('p')) - K_lowerq* = cint(ord('q')) - K_lowerr* = cint(ord('r')) - K_lowers* = cint(ord('s')) - K_lowert* = cint(ord('t')) - K_loweru* = cint(ord('u')) - K_lowerv* = cint(ord('v')) - K_lowerw* = cint(ord('w')) - K_lowerx* = cint(ord('x')) - K_lowery* = cint(ord('y')) - K_lowerz* = cint(ord('z')) - K_braceleft* = cint(ord('{')) - K_bar* = cint(ord('|')) - K_braceright* = cint(ord('}')) - K_tilde* = cint(ord('~')) - -proc isPrint*(c: cint): bool = return c > 31 and c < 127 - -# also define the escape sequences that have keys associated -const - K_BS* = cint(ord('\b')) - K_TAB* = cint(ord('\t')) - K_LF* = cint(10) - K_CR* = cint(13) - -# IUP Extended Key Codes, range start at 128 -# Modifiers use 256 interval -# These key code definitions are specific to IUP - -proc isXkey*(c: cint): bool = return c > 128 -proc isShiftXkey*(c: cint): bool = return c > 256 and c < 512 -proc isCtrlXkey*(c: cint): bool = return c > 512 and c < 768 -proc isAltXkey*(c: cint): bool = return c > 768 and c < 1024 -proc isSysXkey*(c: cint): bool = return c > 1024 and c < 1280 - -proc IUPxCODE*(c: cint): cint = return c + cint(128) # Normal (must be above 128) -proc IUPsxCODE*(c: cint): cint = +# from 32 to 126, all character sets are equal, +# the key code i the same as the character code. +const + K_SP* = cint(ord(' ')) + K_exclam* = cint(ord('!')) + K_quotedbl* = cint(ord('\"')) + K_numbersign* = cint(ord('#')) + K_dollar* = cint(ord('$')) + K_percent* = cint(ord('%')) + K_ampersand* = cint(ord('&')) + K_apostrophe* = cint(ord('\'')) + K_parentleft* = cint(ord('(')) + K_parentright* = cint(ord(')')) + K_asterisk* = cint(ord('*')) + K_plus* = cint(ord('+')) + K_comma* = cint(ord(',')) + K_minus* = cint(ord('-')) + K_period* = cint(ord('.')) + K_slash* = cint(ord('/')) + K_0* = cint(ord('0')) + K_1* = cint(ord('1')) + K_2* = cint(ord('2')) + K_3* = cint(ord('3')) + K_4* = cint(ord('4')) + K_5* = cint(ord('5')) + K_6* = cint(ord('6')) + K_7* = cint(ord('7')) + K_8* = cint(ord('8')) + K_9* = cint(ord('9')) + K_colon* = cint(ord(':')) + K_semicolon* = cint(ord(';')) + K_less* = cint(ord('<')) + K_equal* = cint(ord('=')) + K_greater* = cint(ord('>')) + K_question* = cint(ord('?')) + K_at* = cint(ord('@')) + K_upperA* = cint(ord('A')) + K_upperB* = cint(ord('B')) + K_upperC* = cint(ord('C')) + K_upperD* = cint(ord('D')) + K_upperE* = cint(ord('E')) + K_upperF* = cint(ord('F')) + K_upperG* = cint(ord('G')) + K_upperH* = cint(ord('H')) + K_upperI* = cint(ord('I')) + K_upperJ* = cint(ord('J')) + K_upperK* = cint(ord('K')) + K_upperL* = cint(ord('L')) + K_upperM* = cint(ord('M')) + K_upperN* = cint(ord('N')) + K_upperO* = cint(ord('O')) + K_upperP* = cint(ord('P')) + K_upperQ* = cint(ord('Q')) + K_upperR* = cint(ord('R')) + K_upperS* = cint(ord('S')) + K_upperT* = cint(ord('T')) + K_upperU* = cint(ord('U')) + K_upperV* = cint(ord('V')) + K_upperW* = cint(ord('W')) + K_upperX* = cint(ord('X')) + K_upperY* = cint(ord('Y')) + K_upperZ* = cint(ord('Z')) + K_bracketleft* = cint(ord('[')) + K_backslash* = cint(ord('\\')) + K_bracketright* = cint(ord(']')) + K_circum* = cint(ord('^')) + K_underscore* = cint(ord('_')) + K_grave* = cint(ord('`')) + K_lowera* = cint(ord('a')) + K_lowerb* = cint(ord('b')) + K_lowerc* = cint(ord('c')) + K_lowerd* = cint(ord('d')) + K_lowere* = cint(ord('e')) + K_lowerf* = cint(ord('f')) + K_lowerg* = cint(ord('g')) + K_lowerh* = cint(ord('h')) + K_loweri* = cint(ord('i')) + K_lowerj* = cint(ord('j')) + K_lowerk* = cint(ord('k')) + K_lowerl* = cint(ord('l')) + K_lowerm* = cint(ord('m')) + K_lowern* = cint(ord('n')) + K_lowero* = cint(ord('o')) + K_lowerp* = cint(ord('p')) + K_lowerq* = cint(ord('q')) + K_lowerr* = cint(ord('r')) + K_lowers* = cint(ord('s')) + K_lowert* = cint(ord('t')) + K_loweru* = cint(ord('u')) + K_lowerv* = cint(ord('v')) + K_lowerw* = cint(ord('w')) + K_lowerx* = cint(ord('x')) + K_lowery* = cint(ord('y')) + K_lowerz* = cint(ord('z')) + K_braceleft* = cint(ord('{')) + K_bar* = cint(ord('|')) + K_braceright* = cint(ord('}')) + K_tilde* = cint(ord('~')) + +proc isPrint*(c: cint): bool = return c > 31 and c < 127 + +# also define the escape sequences that have keys associated +const + K_BS* = cint(ord('\b')) + K_TAB* = cint(ord('\t')) + K_LF* = cint(10) + K_CR* = cint(13) + +# IUP Extended Key Codes, range start at 128 +# Modifiers use 256 interval +# These key code definitions are specific to IUP + +proc isXkey*(c: cint): bool = return c > 128 +proc isShiftXkey*(c: cint): bool = return c > 256 and c < 512 +proc isCtrlXkey*(c: cint): bool = return c > 512 and c < 768 +proc isAltXkey*(c: cint): bool = return c > 768 and c < 1024 +proc isSysXkey*(c: cint): bool = return c > 1024 and c < 1280 + +proc iUPxCODE*(c: cint): cint = return c + cint(128) # Normal (must be above 128) +proc iUPsxCODE*(c: cint): cint = return c + cint(256) # Shift (must have range to include the standard keys and the normal # extended keys, so must be above 256 -proc IUPcxCODE*(c: cint): cint = return c + cint(512) # Ctrl -proc IUPmxCODE*(c: cint): cint = return c + cint(768) # Alt -proc IUPyxCODE*(c: cint): cint = return c + cint(1024) # Sys (Win or Apple) +proc iUPcxCODE*(c: cint): cint = return c + cint(512) # Ctrl +proc iUPmxCODE*(c: cint): cint = return c + cint(768) # Alt +proc iUPyxCODE*(c: cint): cint = return c + cint(1024) # Sys (Win or Apple) -const - IUP_NUMMAXCODES* = 1280 ## 5*256=1280 Normal+Shift+Ctrl+Alt+Sys - - K_HOME* = IUPxCODE(1) - K_UP* = IUPxCODE(2) - K_PGUP* = IUPxCODE(3) - K_LEFT* = IUPxCODE(4) - K_MIDDLE* = IUPxCODE(5) - K_RIGHT* = IUPxCODE(6) - K_END* = IUPxCODE(7) - K_DOWN* = IUPxCODE(8) - K_PGDN* = IUPxCODE(9) - K_INS* = IUPxCODE(10) - K_DEL* = IUPxCODE(11) - K_PAUSE* = IUPxCODE(12) - K_ESC* = IUPxCODE(13) - K_ccedilla* = IUPxCODE(14) - K_F1* = IUPxCODE(15) - K_F2* = IUPxCODE(16) - K_F3* = IUPxCODE(17) - K_F4* = IUPxCODE(18) - K_F5* = IUPxCODE(19) - K_F6* = IUPxCODE(20) - K_F7* = IUPxCODE(21) - K_F8* = IUPxCODE(22) - K_F9* = IUPxCODE(23) - K_F10* = IUPxCODE(24) - K_F11* = IUPxCODE(25) - K_F12* = IUPxCODE(26) - K_Print* = IUPxCODE(27) - K_Menu* = IUPxCODE(28) - - K_acute* = IUPxCODE(29) # no Shift/Ctrl/Alt - - K_sHOME* = IUPsxCODE(K_HOME) - K_sUP* = IUPsxCODE(K_UP) - K_sPGUP* = IUPsxCODE(K_PGUP) - K_sLEFT* = IUPsxCODE(K_LEFT) - K_sMIDDLE* = IUPsxCODE(K_MIDDLE) - K_sRIGHT* = IUPsxCODE(K_RIGHT) - K_sEND* = IUPsxCODE(K_END) - K_sDOWN* = IUPsxCODE(K_DOWN) - K_sPGDN* = IUPsxCODE(K_PGDN) - K_sINS* = IUPsxCODE(K_INS) - K_sDEL* = IUPsxCODE(K_DEL) - K_sSP* = IUPsxCODE(K_SP) - K_sTAB* = IUPsxCODE(K_TAB) - K_sCR* = IUPsxCODE(K_CR) - K_sBS* = IUPsxCODE(K_BS) - K_sPAUSE* = IUPsxCODE(K_PAUSE) - K_sESC* = IUPsxCODE(K_ESC) - K_sCcedilla* = IUPsxCODE(K_ccedilla) - K_sF1* = IUPsxCODE(K_F1) - K_sF2* = IUPsxCODE(K_F2) - K_sF3* = IUPsxCODE(K_F3) - K_sF4* = IUPsxCODE(K_F4) - K_sF5* = IUPsxCODE(K_F5) - K_sF6* = IUPsxCODE(K_F6) - K_sF7* = IUPsxCODE(K_F7) - K_sF8* = IUPsxCODE(K_F8) - K_sF9* = IUPsxCODE(K_F9) - K_sF10* = IUPsxCODE(K_F10) - K_sF11* = IUPsxCODE(K_F11) - K_sF12* = IUPsxCODE(K_F12) - K_sPrint* = IUPsxCODE(K_Print) - K_sMenu* = IUPsxCODE(K_Menu) - - K_cHOME* = IUPcxCODE(K_HOME) - K_cUP* = IUPcxCODE(K_UP) - K_cPGUP* = IUPcxCODE(K_PGUP) - K_cLEFT* = IUPcxCODE(K_LEFT) - K_cMIDDLE* = IUPcxCODE(K_MIDDLE) - K_cRIGHT* = IUPcxCODE(K_RIGHT) - K_cEND* = IUPcxCODE(K_END) - K_cDOWN* = IUPcxCODE(K_DOWN) - K_cPGDN* = IUPcxCODE(K_PGDN) - K_cINS* = IUPcxCODE(K_INS) - K_cDEL* = IUPcxCODE(K_DEL) - K_cSP* = IUPcxCODE(K_SP) - K_cTAB* = IUPcxCODE(K_TAB) - K_cCR* = IUPcxCODE(K_CR) - K_cBS* = IUPcxCODE(K_BS) - K_cPAUSE* = IUPcxCODE(K_PAUSE) - K_cESC* = IUPcxCODE(K_ESC) - K_cCcedilla* = IUPcxCODE(K_ccedilla) - K_cF1* = IUPcxCODE(K_F1) - K_cF2* = IUPcxCODE(K_F2) - K_cF3* = IUPcxCODE(K_F3) - K_cF4* = IUPcxCODE(K_F4) - K_cF5* = IUPcxCODE(K_F5) - K_cF6* = IUPcxCODE(K_F6) - K_cF7* = IUPcxCODE(K_F7) - K_cF8* = IUPcxCODE(K_F8) - K_cF9* = IUPcxCODE(K_F9) - K_cF10* = IUPcxCODE(K_F10) - K_cF11* = IUPcxCODE(K_F11) - K_cF12* = IUPcxCODE(K_F12) - K_cPrint* = IUPcxCODE(K_Print) - K_cMenu* = IUPcxCODE(K_Menu) - - K_mHOME* = IUPmxCODE(K_HOME) - K_mUP* = IUPmxCODE(K_UP) - K_mPGUP* = IUPmxCODE(K_PGUP) - K_mLEFT* = IUPmxCODE(K_LEFT) - K_mMIDDLE* = IUPmxCODE(K_MIDDLE) - K_mRIGHT* = IUPmxCODE(K_RIGHT) - K_mEND* = IUPmxCODE(K_END) - K_mDOWN* = IUPmxCODE(K_DOWN) - K_mPGDN* = IUPmxCODE(K_PGDN) - K_mINS* = IUPmxCODE(K_INS) - K_mDEL* = IUPmxCODE(K_DEL) - K_mSP* = IUPmxCODE(K_SP) - K_mTAB* = IUPmxCODE(K_TAB) - K_mCR* = IUPmxCODE(K_CR) - K_mBS* = IUPmxCODE(K_BS) - K_mPAUSE* = IUPmxCODE(K_PAUSE) - K_mESC* = IUPmxCODE(K_ESC) - K_mCcedilla* = IUPmxCODE(K_ccedilla) - K_mF1* = IUPmxCODE(K_F1) - K_mF2* = IUPmxCODE(K_F2) - K_mF3* = IUPmxCODE(K_F3) - K_mF4* = IUPmxCODE(K_F4) - K_mF5* = IUPmxCODE(K_F5) - K_mF6* = IUPmxCODE(K_F6) - K_mF7* = IUPmxCODE(K_F7) - K_mF8* = IUPmxCODE(K_F8) - K_mF9* = IUPmxCODE(K_F9) - K_mF10* = IUPmxCODE(K_F10) - K_mF11* = IUPmxCODE(K_F11) - K_mF12* = IUPmxCODE(K_F12) - K_mPrint* = IUPmxCODE(K_Print) - K_mMenu* = IUPmxCODE(K_Menu) - - K_yHOME* = IUPyxCODE(K_HOME) - K_yUP* = IUPyxCODE(K_UP) - K_yPGUP* = IUPyxCODE(K_PGUP) - K_yLEFT* = IUPyxCODE(K_LEFT) - K_yMIDDLE* = IUPyxCODE(K_MIDDLE) - K_yRIGHT* = IUPyxCODE(K_RIGHT) - K_yEND* = IUPyxCODE(K_END) - K_yDOWN* = IUPyxCODE(K_DOWN) - K_yPGDN* = IUPyxCODE(K_PGDN) - K_yINS* = IUPyxCODE(K_INS) - K_yDEL* = IUPyxCODE(K_DEL) - K_ySP* = IUPyxCODE(K_SP) - K_yTAB* = IUPyxCODE(K_TAB) - K_yCR* = IUPyxCODE(K_CR) - K_yBS* = IUPyxCODE(K_BS) - K_yPAUSE* = IUPyxCODE(K_PAUSE) - K_yESC* = IUPyxCODE(K_ESC) - K_yCcedilla* = IUPyxCODE(K_ccedilla) - K_yF1* = IUPyxCODE(K_F1) - K_yF2* = IUPyxCODE(K_F2) - K_yF3* = IUPyxCODE(K_F3) - K_yF4* = IUPyxCODE(K_F4) - K_yF5* = IUPyxCODE(K_F5) - K_yF6* = IUPyxCODE(K_F6) - K_yF7* = IUPyxCODE(K_F7) - K_yF8* = IUPyxCODE(K_F8) - K_yF9* = IUPyxCODE(K_F9) - K_yF10* = IUPyxCODE(K_F10) - K_yF11* = IUPyxCODE(K_F11) - K_yF12* = IUPyxCODE(K_F12) - K_yPrint* = IUPyxCODE(K_Print) - K_yMenu* = IUPyxCODE(K_Menu) - - K_sPlus* = IUPsxCODE(K_plus) - K_sComma* = IUPsxCODE(K_comma) - K_sMinus* = IUPsxCODE(K_minus) - K_sPeriod* = IUPsxCODE(K_period) - K_sSlash* = IUPsxCODE(K_slash) - K_sAsterisk* = IUPsxCODE(K_asterisk) - - K_cupperA* = IUPcxCODE(K_upperA) - K_cupperB* = IUPcxCODE(K_upperB) - K_cupperC* = IUPcxCODE(K_upperC) - K_cupperD* = IUPcxCODE(K_upperD) - K_cupperE* = IUPcxCODE(K_upperE) - K_cupperF* = IUPcxCODE(K_upperF) - K_cupperG* = IUPcxCODE(K_upperG) - K_cupperH* = IUPcxCODE(K_upperH) - K_cupperI* = IUPcxCODE(K_upperI) - K_cupperJ* = IUPcxCODE(K_upperJ) - K_cupperK* = IUPcxCODE(K_upperK) - K_cupperL* = IUPcxCODE(K_upperL) - K_cupperM* = IUPcxCODE(K_upperM) - K_cupperN* = IUPcxCODE(K_upperN) - K_cupperO* = IUPcxCODE(K_upperO) - K_cupperP* = IUPcxCODE(K_upperP) - K_cupperQ* = IUPcxCODE(K_upperQ) - K_cupperR* = IUPcxCODE(K_upperR) - K_cupperS* = IUPcxCODE(K_upperS) - K_cupperT* = IUPcxCODE(K_upperT) - K_cupperU* = IUPcxCODE(K_upperU) - K_cupperV* = IUPcxCODE(K_upperV) - K_cupperW* = IUPcxCODE(K_upperW) - K_cupperX* = IUPcxCODE(K_upperX) - K_cupperY* = IUPcxCODE(K_upperY) - K_cupperZ* = IUPcxCODE(K_upperZ) - K_c1* = IUPcxCODE(K_1) - K_c2* = IUPcxCODE(K_2) - K_c3* = IUPcxCODE(K_3) - K_c4* = IUPcxCODE(K_4) - K_c5* = IUPcxCODE(K_5) - K_c6* = IUPcxCODE(K_6) - K_c7* = IUPcxCODE(K_7) - K_c8* = IUPcxCODE(K_8) - K_c9* = IUPcxCODE(K_9) - K_c0* = IUPcxCODE(K_0) - K_cPlus* = IUPcxCODE(K_plus) - K_cComma* = IUPcxCODE(K_comma) - K_cMinus* = IUPcxCODE(K_minus) - K_cPeriod* = IUPcxCODE(K_period) - K_cSlash* = IUPcxCODE(K_slash) - K_cSemicolon* = IUPcxCODE(K_semicolon) - K_cEqual* = IUPcxCODE(K_equal) - K_cBracketleft* = IUPcxCODE(K_bracketleft) - K_cBracketright* = IUPcxCODE(K_bracketright) - K_cBackslash* = IUPcxCODE(K_backslash) - K_cAsterisk* = IUPcxCODE(K_asterisk) - - K_mupperA* = IUPmxCODE(K_upperA) - K_mupperB* = IUPmxCODE(K_upperB) - K_mupperC* = IUPmxCODE(K_upperC) - K_mupperD* = IUPmxCODE(K_upperD) - K_mupperE* = IUPmxCODE(K_upperE) - K_mupperF* = IUPmxCODE(K_upperF) - K_mupperG* = IUPmxCODE(K_upperG) - K_mupperH* = IUPmxCODE(K_upperH) - K_mupperI* = IUPmxCODE(K_upperI) - K_mupperJ* = IUPmxCODE(K_upperJ) - K_mupperK* = IUPmxCODE(K_upperK) - K_mupperL* = IUPmxCODE(K_upperL) - K_mupperM* = IUPmxCODE(K_upperM) - K_mupperN* = IUPmxCODE(K_upperN) - K_mupperO* = IUPmxCODE(K_upperO) - K_mupperP* = IUPmxCODE(K_upperP) - K_mupperQ* = IUPmxCODE(K_upperQ) - K_mupperR* = IUPmxCODE(K_upperR) - K_mupperS* = IUPmxCODE(K_upperS) - K_mupperT* = IUPmxCODE(K_upperT) - K_mupperU* = IUPmxCODE(K_upperU) - K_mupperV* = IUPmxCODE(K_upperV) - K_mupperW* = IUPmxCODE(K_upperW) - K_mupperX* = IUPmxCODE(K_upperX) - K_mupperY* = IUPmxCODE(K_upperY) - K_mupperZ* = IUPmxCODE(K_upperZ) - K_m1* = IUPmxCODE(K_1) - K_m2* = IUPmxCODE(K_2) - K_m3* = IUPmxCODE(K_3) - K_m4* = IUPmxCODE(K_4) - K_m5* = IUPmxCODE(K_5) - K_m6* = IUPmxCODE(K_6) - K_m7* = IUPmxCODE(K_7) - K_m8* = IUPmxCODE(K_8) - K_m9* = IUPmxCODE(K_9) - K_m0* = IUPmxCODE(K_0) - K_mPlus* = IUPmxCODE(K_plus) - K_mComma* = IUPmxCODE(K_comma) - K_mMinus* = IUPmxCODE(K_minus) - K_mPeriod* = IUPmxCODE(K_period) - K_mSlash* = IUPmxCODE(K_slash) - K_mSemicolon* = IUPmxCODE(K_semicolon) - K_mEqual* = IUPmxCODE(K_equal) - K_mBracketleft* = IUPmxCODE(K_bracketleft) - K_mBracketright* = IUPmxCODE(K_bracketright) - K_mBackslash* = IUPmxCODE(K_backslash) - K_mAsterisk* = IUPmxCODE(K_asterisk) - - K_yA* = IUPyxCODE(K_upperA) - K_yB* = IUPyxCODE(K_upperB) - K_yC* = IUPyxCODE(K_upperC) - K_yD* = IUPyxCODE(K_upperD) - K_yE* = IUPyxCODE(K_upperE) - K_yF* = IUPyxCODE(K_upperF) - K_yG* = IUPyxCODE(K_upperG) - K_yH* = IUPyxCODE(K_upperH) - K_yI* = IUPyxCODE(K_upperI) - K_yJ* = IUPyxCODE(K_upperJ) - K_yK* = IUPyxCODE(K_upperK) - K_yL* = IUPyxCODE(K_upperL) - K_yM* = IUPyxCODE(K_upperM) - K_yN* = IUPyxCODE(K_upperN) - K_yO* = IUPyxCODE(K_upperO) - K_yP* = IUPyxCODE(K_upperP) - K_yQ* = IUPyxCODE(K_upperQ) - K_yR* = IUPyxCODE(K_upperR) - K_yS* = IUPyxCODE(K_upperS) - K_yT* = IUPyxCODE(K_upperT) - K_yU* = IUPyxCODE(K_upperU) - K_yV* = IUPyxCODE(K_upperV) - K_yW* = IUPyxCODE(K_upperW) - K_yX* = IUPyxCODE(K_upperX) - K_yY* = IUPyxCODE(K_upperY) - K_yZ* = IUPyxCODE(K_upperZ) - K_y1* = IUPyxCODE(K_1) - K_y2* = IUPyxCODE(K_2) - K_y3* = IUPyxCODE(K_3) - K_y4* = IUPyxCODE(K_4) - K_y5* = IUPyxCODE(K_5) - K_y6* = IUPyxCODE(K_6) - K_y7* = IUPyxCODE(K_7) - K_y8* = IUPyxCODE(K_8) - K_y9* = IUPyxCODE(K_9) - K_y0* = IUPyxCODE(K_0) - K_yPlus* = IUPyxCODE(K_plus) - K_yComma* = IUPyxCODE(K_comma) - K_yMinus* = IUPyxCODE(K_minus) - K_yPeriod* = IUPyxCODE(K_period) - K_ySlash* = IUPyxCODE(K_slash) - K_ySemicolon* = IUPyxCODE(K_semicolon) - K_yEqual* = IUPyxCODE(K_equal) - K_yBracketleft* = IUPyxCODE(K_bracketleft) - K_yBracketright* = IUPyxCODE(K_bracketright) - K_yBackslash* = IUPyxCODE(K_backslash) - K_yAsterisk* = IUPyxCODE(K_asterisk) +const + IUP_NUMMAXCODES* = 1280 ## 5*256=1280 Normal+Shift+Ctrl+Alt+Sys -proc ControlsOpen*(): cint {.cdecl, importc: "IupControlsOpen", dynlib: dllname.} -proc ControlsClose*() {.cdecl, importc: "IupControlsClose", dynlib: dllname.} - -proc OldValOpen*() {.cdecl, importc: "IupOldValOpen", dynlib: dllname.} -proc OldTabsOpen*() {.cdecl, importc: "IupOldTabsOpen", dynlib: dllname.} - -proc Colorbar*(): PIhandle {.cdecl, importc: "IupColorbar", dynlib: dllname.} -proc Cells*(): PIhandle {.cdecl, importc: "IupCells", dynlib: dllname.} -proc ColorBrowser*(): PIhandle {.cdecl, importc: "IupColorBrowser", dynlib: dllname.} -proc Gauge*(): PIhandle {.cdecl, importc: "IupGauge", dynlib: dllname.} -proc Dial*(theType: cstring): PIhandle {.cdecl, importc: "IupDial", dynlib: dllname.} -proc Matrix*(action: cstring): PIhandle {.cdecl, importc: "IupMatrix", dynlib: dllname.} - -# IupMatrix utilities -proc MatSetAttribute*(ih: PIhandle, name: cstring, lin, col: cint, + K_HOME* = iUPxCODE(1) + K_UP* = iUPxCODE(2) + K_PGUP* = iUPxCODE(3) + K_LEFT* = iUPxCODE(4) + K_MIDDLE* = iUPxCODE(5) + K_RIGHT* = iUPxCODE(6) + K_END* = iUPxCODE(7) + K_DOWN* = iUPxCODE(8) + K_PGDN* = iUPxCODE(9) + K_INS* = iUPxCODE(10) + K_DEL* = iUPxCODE(11) + K_PAUSE* = iUPxCODE(12) + K_ESC* = iUPxCODE(13) + K_ccedilla* = iUPxCODE(14) + K_F1* = iUPxCODE(15) + K_F2* = iUPxCODE(16) + K_F3* = iUPxCODE(17) + K_F4* = iUPxCODE(18) + K_F5* = iUPxCODE(19) + K_F6* = iUPxCODE(20) + K_F7* = iUPxCODE(21) + K_F8* = iUPxCODE(22) + K_F9* = iUPxCODE(23) + K_F10* = iUPxCODE(24) + K_F11* = iUPxCODE(25) + K_F12* = iUPxCODE(26) + K_Print* = iUPxCODE(27) + K_Menu* = iUPxCODE(28) + + K_acute* = iUPxCODE(29) # no Shift/Ctrl/Alt + + K_sHOME* = iUPsxCODE(K_HOME) + K_sUP* = iUPsxCODE(K_UP) + K_sPGUP* = iUPsxCODE(K_PGUP) + K_sLEFT* = iUPsxCODE(K_LEFT) + K_sMIDDLE* = iUPsxCODE(K_MIDDLE) + K_sRIGHT* = iUPsxCODE(K_RIGHT) + K_sEND* = iUPsxCODE(K_END) + K_sDOWN* = iUPsxCODE(K_DOWN) + K_sPGDN* = iUPsxCODE(K_PGDN) + K_sINS* = iUPsxCODE(K_INS) + K_sDEL* = iUPsxCODE(K_DEL) + K_sSP* = iUPsxCODE(K_SP) + K_sTAB* = iUPsxCODE(K_TAB) + K_sCR* = iUPsxCODE(K_CR) + K_sBS* = iUPsxCODE(K_BS) + K_sPAUSE* = iUPsxCODE(K_PAUSE) + K_sESC* = iUPsxCODE(K_ESC) + K_sCcedilla* = iUPsxCODE(K_ccedilla) + K_sF1* = iUPsxCODE(K_F1) + K_sF2* = iUPsxCODE(K_F2) + K_sF3* = iUPsxCODE(K_F3) + K_sF4* = iUPsxCODE(K_F4) + K_sF5* = iUPsxCODE(K_F5) + K_sF6* = iUPsxCODE(K_F6) + K_sF7* = iUPsxCODE(K_F7) + K_sF8* = iUPsxCODE(K_F8) + K_sF9* = iUPsxCODE(K_F9) + K_sF10* = iUPsxCODE(K_F10) + K_sF11* = iUPsxCODE(K_F11) + K_sF12* = iUPsxCODE(K_F12) + K_sPrint* = iUPsxCODE(K_Print) + K_sMenu* = iUPsxCODE(K_Menu) + + K_cHOME* = iUPcxCODE(K_HOME) + K_cUP* = iUPcxCODE(K_UP) + K_cPGUP* = iUPcxCODE(K_PGUP) + K_cLEFT* = iUPcxCODE(K_LEFT) + K_cMIDDLE* = iUPcxCODE(K_MIDDLE) + K_cRIGHT* = iUPcxCODE(K_RIGHT) + K_cEND* = iUPcxCODE(K_END) + K_cDOWN* = iUPcxCODE(K_DOWN) + K_cPGDN* = iUPcxCODE(K_PGDN) + K_cINS* = iUPcxCODE(K_INS) + K_cDEL* = iUPcxCODE(K_DEL) + K_cSP* = iUPcxCODE(K_SP) + K_cTAB* = iUPcxCODE(K_TAB) + K_cCR* = iUPcxCODE(K_CR) + K_cBS* = iUPcxCODE(K_BS) + K_cPAUSE* = iUPcxCODE(K_PAUSE) + K_cESC* = iUPcxCODE(K_ESC) + K_cCcedilla* = iUPcxCODE(K_ccedilla) + K_cF1* = iUPcxCODE(K_F1) + K_cF2* = iUPcxCODE(K_F2) + K_cF3* = iUPcxCODE(K_F3) + K_cF4* = iUPcxCODE(K_F4) + K_cF5* = iUPcxCODE(K_F5) + K_cF6* = iUPcxCODE(K_F6) + K_cF7* = iUPcxCODE(K_F7) + K_cF8* = iUPcxCODE(K_F8) + K_cF9* = iUPcxCODE(K_F9) + K_cF10* = iUPcxCODE(K_F10) + K_cF11* = iUPcxCODE(K_F11) + K_cF12* = iUPcxCODE(K_F12) + K_cPrint* = iUPcxCODE(K_Print) + K_cMenu* = iUPcxCODE(K_Menu) + + K_mHOME* = iUPmxCODE(K_HOME) + K_mUP* = iUPmxCODE(K_UP) + K_mPGUP* = iUPmxCODE(K_PGUP) + K_mLEFT* = iUPmxCODE(K_LEFT) + K_mMIDDLE* = iUPmxCODE(K_MIDDLE) + K_mRIGHT* = iUPmxCODE(K_RIGHT) + K_mEND* = iUPmxCODE(K_END) + K_mDOWN* = iUPmxCODE(K_DOWN) + K_mPGDN* = iUPmxCODE(K_PGDN) + K_mINS* = iUPmxCODE(K_INS) + K_mDEL* = iUPmxCODE(K_DEL) + K_mSP* = iUPmxCODE(K_SP) + K_mTAB* = iUPmxCODE(K_TAB) + K_mCR* = iUPmxCODE(K_CR) + K_mBS* = iUPmxCODE(K_BS) + K_mPAUSE* = iUPmxCODE(K_PAUSE) + K_mESC* = iUPmxCODE(K_ESC) + K_mCcedilla* = iUPmxCODE(K_ccedilla) + K_mF1* = iUPmxCODE(K_F1) + K_mF2* = iUPmxCODE(K_F2) + K_mF3* = iUPmxCODE(K_F3) + K_mF4* = iUPmxCODE(K_F4) + K_mF5* = iUPmxCODE(K_F5) + K_mF6* = iUPmxCODE(K_F6) + K_mF7* = iUPmxCODE(K_F7) + K_mF8* = iUPmxCODE(K_F8) + K_mF9* = iUPmxCODE(K_F9) + K_mF10* = iUPmxCODE(K_F10) + K_mF11* = iUPmxCODE(K_F11) + K_mF12* = iUPmxCODE(K_F12) + K_mPrint* = iUPmxCODE(K_Print) + K_mMenu* = iUPmxCODE(K_Menu) + + K_yHOME* = iUPyxCODE(K_HOME) + K_yUP* = iUPyxCODE(K_UP) + K_yPGUP* = iUPyxCODE(K_PGUP) + K_yLEFT* = iUPyxCODE(K_LEFT) + K_yMIDDLE* = iUPyxCODE(K_MIDDLE) + K_yRIGHT* = iUPyxCODE(K_RIGHT) + K_yEND* = iUPyxCODE(K_END) + K_yDOWN* = iUPyxCODE(K_DOWN) + K_yPGDN* = iUPyxCODE(K_PGDN) + K_yINS* = iUPyxCODE(K_INS) + K_yDEL* = iUPyxCODE(K_DEL) + K_ySP* = iUPyxCODE(K_SP) + K_yTAB* = iUPyxCODE(K_TAB) + K_yCR* = iUPyxCODE(K_CR) + K_yBS* = iUPyxCODE(K_BS) + K_yPAUSE* = iUPyxCODE(K_PAUSE) + K_yESC* = iUPyxCODE(K_ESC) + K_yCcedilla* = iUPyxCODE(K_ccedilla) + K_yF1* = iUPyxCODE(K_F1) + K_yF2* = iUPyxCODE(K_F2) + K_yF3* = iUPyxCODE(K_F3) + K_yF4* = iUPyxCODE(K_F4) + K_yF5* = iUPyxCODE(K_F5) + K_yF6* = iUPyxCODE(K_F6) + K_yF7* = iUPyxCODE(K_F7) + K_yF8* = iUPyxCODE(K_F8) + K_yF9* = iUPyxCODE(K_F9) + K_yF10* = iUPyxCODE(K_F10) + K_yF11* = iUPyxCODE(K_F11) + K_yF12* = iUPyxCODE(K_F12) + K_yPrint* = iUPyxCODE(K_Print) + K_yMenu* = iUPyxCODE(K_Menu) + + K_sPlus* = iUPsxCODE(K_plus) + K_sComma* = iUPsxCODE(K_comma) + K_sMinus* = iUPsxCODE(K_minus) + K_sPeriod* = iUPsxCODE(K_period) + K_sSlash* = iUPsxCODE(K_slash) + K_sAsterisk* = iUPsxCODE(K_asterisk) + + K_cupperA* = iUPcxCODE(K_upperA) + K_cupperB* = iUPcxCODE(K_upperB) + K_cupperC* = iUPcxCODE(K_upperC) + K_cupperD* = iUPcxCODE(K_upperD) + K_cupperE* = iUPcxCODE(K_upperE) + K_cupperF* = iUPcxCODE(K_upperF) + K_cupperG* = iUPcxCODE(K_upperG) + K_cupperH* = iUPcxCODE(K_upperH) + K_cupperI* = iUPcxCODE(K_upperI) + K_cupperJ* = iUPcxCODE(K_upperJ) + K_cupperK* = iUPcxCODE(K_upperK) + K_cupperL* = iUPcxCODE(K_upperL) + K_cupperM* = iUPcxCODE(K_upperM) + K_cupperN* = iUPcxCODE(K_upperN) + K_cupperO* = iUPcxCODE(K_upperO) + K_cupperP* = iUPcxCODE(K_upperP) + K_cupperQ* = iUPcxCODE(K_upperQ) + K_cupperR* = iUPcxCODE(K_upperR) + K_cupperS* = iUPcxCODE(K_upperS) + K_cupperT* = iUPcxCODE(K_upperT) + K_cupperU* = iUPcxCODE(K_upperU) + K_cupperV* = iUPcxCODE(K_upperV) + K_cupperW* = iUPcxCODE(K_upperW) + K_cupperX* = iUPcxCODE(K_upperX) + K_cupperY* = iUPcxCODE(K_upperY) + K_cupperZ* = iUPcxCODE(K_upperZ) + K_c1* = iUPcxCODE(K_1) + K_c2* = iUPcxCODE(K_2) + K_c3* = iUPcxCODE(K_3) + K_c4* = iUPcxCODE(K_4) + K_c5* = iUPcxCODE(K_5) + K_c6* = iUPcxCODE(K_6) + K_c7* = iUPcxCODE(K_7) + K_c8* = iUPcxCODE(K_8) + K_c9* = iUPcxCODE(K_9) + K_c0* = iUPcxCODE(K_0) + K_cPlus* = iUPcxCODE(K_plus) + K_cComma* = iUPcxCODE(K_comma) + K_cMinus* = iUPcxCODE(K_minus) + K_cPeriod* = iUPcxCODE(K_period) + K_cSlash* = iUPcxCODE(K_slash) + K_cSemicolon* = iUPcxCODE(K_semicolon) + K_cEqual* = iUPcxCODE(K_equal) + K_cBracketleft* = iUPcxCODE(K_bracketleft) + K_cBracketright* = iUPcxCODE(K_bracketright) + K_cBackslash* = iUPcxCODE(K_backslash) + K_cAsterisk* = iUPcxCODE(K_asterisk) + + K_mupperA* = iUPmxCODE(K_upperA) + K_mupperB* = iUPmxCODE(K_upperB) + K_mupperC* = iUPmxCODE(K_upperC) + K_mupperD* = iUPmxCODE(K_upperD) + K_mupperE* = iUPmxCODE(K_upperE) + K_mupperF* = iUPmxCODE(K_upperF) + K_mupperG* = iUPmxCODE(K_upperG) + K_mupperH* = iUPmxCODE(K_upperH) + K_mupperI* = iUPmxCODE(K_upperI) + K_mupperJ* = iUPmxCODE(K_upperJ) + K_mupperK* = iUPmxCODE(K_upperK) + K_mupperL* = iUPmxCODE(K_upperL) + K_mupperM* = iUPmxCODE(K_upperM) + K_mupperN* = iUPmxCODE(K_upperN) + K_mupperO* = iUPmxCODE(K_upperO) + K_mupperP* = iUPmxCODE(K_upperP) + K_mupperQ* = iUPmxCODE(K_upperQ) + K_mupperR* = iUPmxCODE(K_upperR) + K_mupperS* = iUPmxCODE(K_upperS) + K_mupperT* = iUPmxCODE(K_upperT) + K_mupperU* = iUPmxCODE(K_upperU) + K_mupperV* = iUPmxCODE(K_upperV) + K_mupperW* = iUPmxCODE(K_upperW) + K_mupperX* = iUPmxCODE(K_upperX) + K_mupperY* = iUPmxCODE(K_upperY) + K_mupperZ* = iUPmxCODE(K_upperZ) + K_m1* = iUPmxCODE(K_1) + K_m2* = iUPmxCODE(K_2) + K_m3* = iUPmxCODE(K_3) + K_m4* = iUPmxCODE(K_4) + K_m5* = iUPmxCODE(K_5) + K_m6* = iUPmxCODE(K_6) + K_m7* = iUPmxCODE(K_7) + K_m8* = iUPmxCODE(K_8) + K_m9* = iUPmxCODE(K_9) + K_m0* = iUPmxCODE(K_0) + K_mPlus* = iUPmxCODE(K_plus) + K_mComma* = iUPmxCODE(K_comma) + K_mMinus* = iUPmxCODE(K_minus) + K_mPeriod* = iUPmxCODE(K_period) + K_mSlash* = iUPmxCODE(K_slash) + K_mSemicolon* = iUPmxCODE(K_semicolon) + K_mEqual* = iUPmxCODE(K_equal) + K_mBracketleft* = iUPmxCODE(K_bracketleft) + K_mBracketright* = iUPmxCODE(K_bracketright) + K_mBackslash* = iUPmxCODE(K_backslash) + K_mAsterisk* = iUPmxCODE(K_asterisk) + + K_yA* = iUPyxCODE(K_upperA) + K_yB* = iUPyxCODE(K_upperB) + K_yC* = iUPyxCODE(K_upperC) + K_yD* = iUPyxCODE(K_upperD) + K_yE* = iUPyxCODE(K_upperE) + K_yF* = iUPyxCODE(K_upperF) + K_yG* = iUPyxCODE(K_upperG) + K_yH* = iUPyxCODE(K_upperH) + K_yI* = iUPyxCODE(K_upperI) + K_yJ* = iUPyxCODE(K_upperJ) + K_yK* = iUPyxCODE(K_upperK) + K_yL* = iUPyxCODE(K_upperL) + K_yM* = iUPyxCODE(K_upperM) + K_yN* = iUPyxCODE(K_upperN) + K_yO* = iUPyxCODE(K_upperO) + K_yP* = iUPyxCODE(K_upperP) + K_yQ* = iUPyxCODE(K_upperQ) + K_yR* = iUPyxCODE(K_upperR) + K_yS* = iUPyxCODE(K_upperS) + K_yT* = iUPyxCODE(K_upperT) + K_yU* = iUPyxCODE(K_upperU) + K_yV* = iUPyxCODE(K_upperV) + K_yW* = iUPyxCODE(K_upperW) + K_yX* = iUPyxCODE(K_upperX) + K_yY* = iUPyxCODE(K_upperY) + K_yZ* = iUPyxCODE(K_upperZ) + K_y1* = iUPyxCODE(K_1) + K_y2* = iUPyxCODE(K_2) + K_y3* = iUPyxCODE(K_3) + K_y4* = iUPyxCODE(K_4) + K_y5* = iUPyxCODE(K_5) + K_y6* = iUPyxCODE(K_6) + K_y7* = iUPyxCODE(K_7) + K_y8* = iUPyxCODE(K_8) + K_y9* = iUPyxCODE(K_9) + K_y0* = iUPyxCODE(K_0) + K_yPlus* = iUPyxCODE(K_plus) + K_yComma* = iUPyxCODE(K_comma) + K_yMinus* = iUPyxCODE(K_minus) + K_yPeriod* = iUPyxCODE(K_period) + K_ySlash* = iUPyxCODE(K_slash) + K_ySemicolon* = iUPyxCODE(K_semicolon) + K_yEqual* = iUPyxCODE(K_equal) + K_yBracketleft* = iUPyxCODE(K_bracketleft) + K_yBracketright* = iUPyxCODE(K_bracketright) + K_yBackslash* = iUPyxCODE(K_backslash) + K_yAsterisk* = iUPyxCODE(K_asterisk) + +proc controlsOpen*(): cint {.cdecl, importc: "IupControlsOpen", dynlib: dllname.} +proc controlsClose*() {.cdecl, importc: "IupControlsClose", dynlib: dllname.} + +proc oldValOpen*() {.cdecl, importc: "IupOldValOpen", dynlib: dllname.} +proc oldTabsOpen*() {.cdecl, importc: "IupOldTabsOpen", dynlib: dllname.} + +proc colorbar*(): PIhandle {.cdecl, importc: "IupColorbar", dynlib: dllname.} +proc cells*(): PIhandle {.cdecl, importc: "IupCells", dynlib: dllname.} +proc colorBrowser*(): PIhandle {.cdecl, importc: "IupColorBrowser", dynlib: dllname.} +proc gauge*(): PIhandle {.cdecl, importc: "IupGauge", dynlib: dllname.} +proc dial*(theType: cstring): PIhandle {.cdecl, importc: "IupDial", dynlib: dllname.} +proc matrix*(action: cstring): PIhandle {.cdecl, importc: "IupMatrix", dynlib: dllname.} + +# IupMatrix utilities +proc matSetAttribute*(ih: PIhandle, name: cstring, lin, col: cint, value: cstring) {. - cdecl, importc: "IupMatSetAttribute", dynlib: dllname.} -proc MatStoreAttribute*(ih: PIhandle, name: cstring, lin, col: cint, + cdecl, importc: "IupMatSetAttribute", dynlib: dllname.} +proc matStoreAttribute*(ih: PIhandle, name: cstring, lin, col: cint, value: cstring) {.cdecl, - importc: "IupMatStoreAttribute", dynlib: dllname.} -proc MatGetAttribute*(ih: PIhandle, name: cstring, lin, col: cint): cstring {. - cdecl, importc: "IupMatGetAttribute", dynlib: dllname.} -proc MatGetInt*(ih: PIhandle, name: cstring, lin, col: cint): cint {. - cdecl, importc: "IupMatGetInt", dynlib: dllname.} -proc MatGetFloat*(ih: PIhandle, name: cstring, lin, col: cint): cfloat {. - cdecl, importc: "IupMatGetFloat", dynlib: dllname.} -proc MatSetfAttribute*(ih: PIhandle, name: cstring, lin, col: cint, + importc: "IupMatStoreAttribute", dynlib: dllname.} +proc matGetAttribute*(ih: PIhandle, name: cstring, lin, col: cint): cstring {. + cdecl, importc: "IupMatGetAttribute", dynlib: dllname.} +proc matGetInt*(ih: PIhandle, name: cstring, lin, col: cint): cint {. + cdecl, importc: "IupMatGetInt", dynlib: dllname.} +proc matGetFloat*(ih: PIhandle, name: cstring, lin, col: cint): cfloat {. + cdecl, importc: "IupMatGetFloat", dynlib: dllname.} +proc matSetfAttribute*(ih: PIhandle, name: cstring, lin, col: cint, format: cstring) {.cdecl, importc: "IupMatSetfAttribute", - dynlib: dllname, varargs.} - -# Used by IupColorbar -const - IUP_PRIMARY* = -1 - IUP_SECONDARY* = -2 - -# Initialize PPlot widget class -proc PPlotOpen*() {.cdecl, importc: "IupPPlotOpen", dynlib: dllname.} - -# Create an PPlot widget instance -proc PPlot*: PIhandle {.cdecl, importc: "IupPPlot", dynlib: dllname.} - -# Add dataset to plot -proc PPlotBegin*(ih: PIhandle, strXdata: cint) {. - cdecl, importc: "IupPPlotBegin", dynlib: dllname.} -proc PPlotAdd*(ih: PIhandle, x, y: cfloat) {. - cdecl, importc: "IupPPlotAdd", dynlib: dllname.} -proc PPlotAddStr*(ih: PIhandle, x: cstring, y: cfloat) {. - cdecl, importc: "IupPPlotAddStr", dynlib: dllname.} -proc PPlotEnd*(ih: PIhandle): cint {. - cdecl, importc: "IupPPlotEnd", dynlib: dllname.} - -proc PPlotInsertStr*(ih: PIhandle, index, sample_index: cint, x: cstring, - y: cfloat) {.cdecl, importc: "IupPPlotInsertStr", - dynlib: dllname.} -proc PPlotInsert*(ih: PIhandle, index, sample_index: cint, - x, y: cfloat) {. - cdecl, importc: "IupPPlotInsert", dynlib: dllname.} - -# convert from plot coordinates to pixels -proc PPlotTransform*(ih: PIhandle, x, y: cfloat, ix, iy: var cint) {. - cdecl, importc: "IupPPlotTransform", dynlib: dllname.} - -# Plot on the given device. Uses a "cdCanvas*". -proc PPlotPaintTo*(ih: PIhandle, cnv: pointer) {. - cdecl, importc: "IupPPlotPaintTo", dynlib: dllname.} - + dynlib: dllname, varargs.} + +# Used by IupColorbar +const + IUP_PRIMARY* = -1 + IUP_SECONDARY* = -2 + +# Initialize PPlot widget class +proc pPlotOpen*() {.cdecl, importc: "IupPPlotOpen", dynlib: dllname.} + +# Create an PPlot widget instance +proc pPlot*: PIhandle {.cdecl, importc: "IupPPlot", dynlib: dllname.} + +# Add dataset to plot +proc pPlotBegin*(ih: PIhandle, strXdata: cint) {. + cdecl, importc: "IupPPlotBegin", dynlib: dllname.} +proc pPlotAdd*(ih: PIhandle, x, y: cfloat) {. + cdecl, importc: "IupPPlotAdd", dynlib: dllname.} +proc pPlotAddStr*(ih: PIhandle, x: cstring, y: cfloat) {. + cdecl, importc: "IupPPlotAddStr", dynlib: dllname.} +proc pPlotEnd*(ih: PIhandle): cint {. + cdecl, importc: "IupPPlotEnd", dynlib: dllname.} + +proc pPlotInsertStr*(ih: PIhandle, index, sampleIndex: cint, x: cstring, + y: cfloat) {.cdecl, importc: "IupPPlotInsertStr", + dynlib: dllname.} +proc pPlotInsert*(ih: PIhandle, index, sampleIndex: cint, + x, y: cfloat) {. + cdecl, importc: "IupPPlotInsert", dynlib: dllname.} + +# convert from plot coordinates to pixels +proc pPlotTransform*(ih: PIhandle, x, y: cfloat, ix, iy: var cint) {. + cdecl, importc: "IupPPlotTransform", dynlib: dllname.} + +# Plot on the given device. Uses a "cdCanvas*". +proc pPlotPaintTo*(ih: PIhandle, cnv: pointer) {. + cdecl, importc: "IupPPlotPaintTo", dynlib: dllname.} + diff --git a/lib/wrappers/libcurl.nim b/lib/wrappers/libcurl.nim index bd8616759..8c962f6cb 100644 --- a/lib/wrappers/libcurl.nim +++ b/lib/wrappers/libcurl.nim @@ -43,10 +43,10 @@ type Pinfotype* = ptr Tinfotype Plock_access* = ptr Tlock_access Plock_data* = ptr Tlock_data - Pmalloc_callback* = ptr tmalloc_callback + Pmalloc_callback* = ptr Tmalloc_callback PNETRC_OPTION* = ptr TNETRC_OPTION Pproxytype* = ptr Tproxytype - Prealloc_callback* = ptr trealloc_callback + Prealloc_callback* = ptr Trealloc_callback Pslist* = ptr Tslist Psocket* = ptr Tsocket PSSL_VERSION* = ptr TSSL_VERSION @@ -309,7 +309,7 @@ type TMsg*{.pure, final.} = object msg*: TMSGEnum easy_handle*: PCurl - whatever*: Pointer #data : record + whatever*: pointer #data : record # case longint of # 0 : ( whatever : pointer ); # 1 : ( result : CURLcode ); @@ -442,7 +442,7 @@ proc slist_append*(slist: Pslist, p: cstring): Pslist{.cdecl, dynlib: libname, importc: "curl_slist_append".} proc slist_free_all*(para1: Pslist){.cdecl, dynlib: libname, importc: "curl_slist_free_all".} -proc getdate*(p: cstring, unused: ptr TTime): TTime{.cdecl, dynlib: libname, +proc getdate*(p: cstring, unused: ptr Time): Time{.cdecl, dynlib: libname, importc: "curl_getdate".} proc share_init*(): PSH{.cdecl, dynlib: libname, importc: "curl_share_init".} proc share_setopt*(para1: PSH, option: TSHoption): TSHcode{.cdecl, varargs, diff --git a/lib/wrappers/libsvm.nim b/lib/wrappers/libsvm.nim index 8dec05bcf..00d5ac73c 100644 --- a/lib/wrappers/libsvm.nim +++ b/lib/wrappers/libsvm.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/lib/wrappers/libuv.nim b/lib/wrappers/libuv.nim index 1b6416982..3189ec408 100644 --- a/lib/wrappers/libuv.nim +++ b/lib/wrappers/libuv.nim @@ -35,7 +35,7 @@ type GetAddrInfoProc* = proc (handle: PGetAddrInfo, status: cint, res: ptr TAddrInfo) ExitProc* = proc (a2: PProcess, exit_status: cint, term_signal: cint) - FsProc* = proc (req: pFS) + FsProc* = proc (req: PFS) WorkProc* = proc (req: PWork) AfterWorkProc* = proc (req: PWork) @@ -338,7 +338,7 @@ type FS_CHMOD, FS_FCHMOD, FS_FSYNC, FS_FDATASYNC, FS_UNLINK, FS_RMDIR, FS_MKDIR, FS_RENAME, FS_READDIR, FS_LINK, FS_SYMLINK, FS_READLINK, FS_CHOWN, FS_FCHOWN - tFS* {.pure, final, importc: "uv_fs_t", header: "uv.h".} = object + TFS* {.pure, final, importc: "uv_fs_t", header: "uv.h".} = object typ* {.importc: "type".}: TReqType data* {.importc: "data".}: pointer loop* {.importc: "loop".}: PLoop @@ -349,7 +349,7 @@ type path* {.importc: "path".}: cstring errorno* {.importc: "errorno".}: cint - pFS* = ptr tFS + PFS* = ptr TFS TReq* {.pure, final, importc: "uv_req_t", header: "uv.h".} = object typ* {.importc: "type".}: TReqType @@ -591,85 +591,85 @@ proc process_kill*(a2: PProcess, signum: cint): cint{. proc queue_work*(loop: PLoop, req: PWork, work_cb: WorkProc, after_work_cb: AfterWorkProc): cint{. importc: "uv_queue_work", header: "uv.h".} -proc req_cleanup*(req: pFS){. +proc req_cleanup*(req: PFS){. importc: "uv_fs_req_cleanup", header: "uv.h".} -proc close*(loop: PLoop, req: pFS, file: TFile, cb: FsProc): cint{. +proc close*(loop: PLoop, req: PFS, file: TFile, cb: FsProc): cint{. importc: "uv_fs_close", header: "uv.h".} -proc open*(loop: PLoop, req: pFS, path: cstring, flags: cint, mode: cint, cb: FsProc): cint{. +proc open*(loop: PLoop, req: PFS, path: cstring, flags: cint, mode: cint, cb: FsProc): cint{. importc: "uv_fs_open", header: "uv.h".} -proc read*(loop: PLoop, req: pFS, file: TFile, buf: pointer, length: csize, offset: coff, cb: FsProc): cint{. +proc read*(loop: PLoop, req: PFS, file: TFile, buf: pointer, length: csize, offset: coff, cb: FsProc): cint{. importc: "uv_fs_read", header: "uv.h".} -proc unlink*(loop: PLoop, req: pFS, path: cstring, cb: FsProc): cint{. +proc unlink*(loop: PLoop, req: PFS, path: cstring, cb: FsProc): cint{. importc: "uv_fs_unlink", header: "uv.h".} -proc write*(loop: PLoop, req: pFS, file: TFile, buf: pointer, length: csize, offset: coff, cb: FsProc): cint{. +proc write*(loop: PLoop, req: PFS, file: TFile, buf: pointer, length: csize, offset: coff, cb: FsProc): cint{. importc: "uv_fs_write", header: "uv.h".} -proc mkdir*(loop: PLoop, req: pFS, path: cstring, mode: cint, cb: FsProc): cint{. +proc mkdir*(loop: PLoop, req: PFS, path: cstring, mode: cint, cb: FsProc): cint{. importc: "uv_fs_mkdir", header: "uv.h".} -proc rmdir*(loop: PLoop, req: pFS, path: cstring, cb: FsProc): cint{. +proc rmdir*(loop: PLoop, req: PFS, path: cstring, cb: FsProc): cint{. importc: "uv_fs_rmdir", header: "uv.h".} -proc readdir*(loop: PLoop, req: pFS, path: cstring, flags: cint, cb: FsProc): cint{. +proc readdir*(loop: PLoop, req: PFS, path: cstring, flags: cint, cb: FsProc): cint{. importc: "uv_fs_readdir", header: "uv.h".} -proc stat*(loop: PLoop, req: pFS, path: cstring, cb: FsProc): cint{. +proc stat*(loop: PLoop, req: PFS, path: cstring, cb: FsProc): cint{. importc: "uv_fs_stat", header: "uv.h".} -proc fstat*(loop: PLoop, req: pFS, file: TFile, cb: FsProc): cint{. +proc fstat*(loop: PLoop, req: PFS, file: TFile, cb: FsProc): cint{. importc: "uv_fs_fstat", header: "uv.h".} -proc rename*(loop: PLoop, req: pFS, path: cstring, new_path: cstring, cb: FsProc): cint{. +proc rename*(loop: PLoop, req: PFS, path: cstring, new_path: cstring, cb: FsProc): cint{. importc: "uv_fs_rename", header: "uv.h".} -proc fsync*(loop: PLoop, req: pFS, file: TFile, cb: FsProc): cint{. +proc fsync*(loop: PLoop, req: PFS, file: TFile, cb: FsProc): cint{. importc: "uv_fs_fsync", header: "uv.h".} -proc fdatasync*(loop: PLoop, req: pFS, file: TFile, cb: FsProc): cint{. +proc fdatasync*(loop: PLoop, req: PFS, file: TFile, cb: FsProc): cint{. importc: "uv_fs_fdatasync", header: "uv.h".} -proc ftruncate*(loop: PLoop, req: pFS, file: TFile, offset: coff, cb: FsProc): cint{. +proc ftruncate*(loop: PLoop, req: PFS, file: TFile, offset: coff, cb: FsProc): cint{. importc: "uv_fs_ftruncate", header: "uv.h".} -proc sendfile*(loop: PLoop, req: pFS, out_fd: TFile, in_fd: TFile, in_offset: coff, length: csize, cb: FsProc): cint{. +proc sendfile*(loop: PLoop, req: PFS, out_fd: TFile, in_fd: TFile, in_offset: coff, length: csize, cb: FsProc): cint{. importc: "uv_fs_sendfile", header: "uv.h".} -proc chmod*(loop: PLoop, req: pFS, path: cstring, mode: cint, cb: FsProc): cint{. +proc chmod*(loop: PLoop, req: PFS, path: cstring, mode: cint, cb: FsProc): cint{. importc: "uv_fs_chmod", header: "uv.h".} -proc utime*(loop: PLoop, req: pFS, path: cstring, atime: cdouble, mtime: cdouble, cb: FsProc): cint{. +proc utime*(loop: PLoop, req: PFS, path: cstring, atime: cdouble, mtime: cdouble, cb: FsProc): cint{. importc: "uv_fs_utime", header: "uv.h".} -proc futime*(loop: PLoop, req: pFS, file: TFile, atime: cdouble, mtime: cdouble, cb: FsProc): cint{. +proc futime*(loop: PLoop, req: PFS, file: TFile, atime: cdouble, mtime: cdouble, cb: FsProc): cint{. importc: "uv_fs_futime", header: "uv.h".} -proc lstat*(loop: PLoop, req: pFS, path: cstring, cb: FsProc): cint{. +proc lstat*(loop: PLoop, req: PFS, path: cstring, cb: FsProc): cint{. importc: "uv_fs_lstat", header: "uv.h".} -proc link*(loop: PLoop, req: pFS, path: cstring, new_path: cstring, cb: FsProc): cint{. +proc link*(loop: PLoop, req: PFS, path: cstring, new_path: cstring, cb: FsProc): cint{. importc: "uv_fs_link", header: "uv.h".} -proc symlink*(loop: PLoop, req: pFS, path: cstring, new_path: cstring, flags: cint, cb: FsProc): cint{. +proc symlink*(loop: PLoop, req: PFS, path: cstring, new_path: cstring, flags: cint, cb: FsProc): cint{. importc: "uv_fs_symlink", header: "uv.h".} -proc readlink*(loop: PLoop, req: pFS, path: cstring, cb: FsProc): cint{. +proc readlink*(loop: PLoop, req: PFS, path: cstring, cb: FsProc): cint{. importc: "uv_fs_readlink", header: "uv.h".} -proc fchmod*(loop: PLoop, req: pFS, file: TFile, mode: cint, cb: FsProc): cint{. +proc fchmod*(loop: PLoop, req: PFS, file: TFile, mode: cint, cb: FsProc): cint{. importc: "uv_fs_fchmod", header: "uv.h".} -proc chown*(loop: PLoop, req: pFS, path: cstring, uid: cint, gid: cint, cb: FsProc): cint{. +proc chown*(loop: PLoop, req: PFS, path: cstring, uid: cint, gid: cint, cb: FsProc): cint{. importc: "uv_fs_chown", header: "uv.h".} -proc fchown*(loop: PLoop, req: pFS, file: TFile, uid: cint, gid: cint, cb: FsProc): cint{. +proc fchown*(loop: PLoop, req: PFS, file: TFile, uid: cint, gid: cint, cb: FsProc): cint{. importc: "uv_fs_fchown", header: "uv.h".} -proc event_init*(loop: PLoop, handle: PFsEvent, filename: cstring, cb: FsEventProc): cint{. +proc event_init*(loop: PLoop, handle: PFSEvent, filename: cstring, cb: FsEventProc): cint{. importc: "uv_fs_event_init", header: "uv.h".} proc ip4_addr*(ip: cstring, port: cint): TSockAddrIn{. diff --git a/lib/wrappers/mysql.nim b/lib/wrappers/mysql.nim index 84d70287f..945e09ecf 100644 --- a/lib/wrappers/mysql.nim +++ b/lib/wrappers/mysql.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2010 Andreas Rumpf # # See the file "copying.txt", included in this @@ -729,7 +729,7 @@ type len*: int # output length pointer is_null*: Pmy_bool # Pointer to null indicator buffer*: pointer # buffer to get/put data - error*: pmy_bool # set this if you want to track data truncations happened during fetch + error*: PMy_bool # set this if you want to track data truncations happened during fetch buffer_type*: Tenum_field_types # buffer type buffer_length*: int # buffer length, must be set for str/binary # Following are for internal use. Set by mysql_stmt_bind_param @@ -904,7 +904,7 @@ proc shutdown*(MySQL: PMySQL, shutdown_level: Tenum_shutdown_level): cint{.stdca dynlib: lib, importc: "mysql_shutdown".} proc dump_debug_info*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, importc: "mysql_dump_debug_info".} -proc refresh*(MySQL: PMySQL, refresh_options: cuint): cint{.stdcall, dynlib: lib, +proc refresh*(sql: PMySQL, refresh_options: cuint): cint{.stdcall, dynlib: lib, importc: "mysql_refresh".} proc kill*(MySQL: PMySQL, pid: int): cint{.stdcall, dynlib: lib, importc: "mysql_kill".} proc set_server_option*(MySQL: PMySQL, option: Tenum_mysql_set_option): cint{.stdcall, @@ -1040,7 +1040,7 @@ const NO_DATA* = 100 DATA_TRUNCATED* = 101 -proc reload*(MySQL: PMySQL): cint +proc reload*(x: PMySQL): cint when defined(USE_OLD_FUNCTIONS): proc connect*(MySQL: PMySQL, host: cstring, user: cstring, passwd: cstring): PMySQL{.stdcall, dynlib: lib, importc: "mysql_connect".} @@ -1059,7 +1059,7 @@ proc IS_NOT_NULL(n: int32): bool = proc IS_BLOB(n: int32): bool = result = (n and BLOB_FLAG) != 0 -proc IS_NUM_FIELD(f: pst_mysql_field): bool = +proc IS_NUM_FIELD(f: Pst_mysql_field): bool = result = (f.flags and NUM_FLAG) != 0 proc IS_NUM(t: Tenum_field_types): bool = @@ -1071,7 +1071,7 @@ proc INTERNAL_NUM_FIELD(f: Pst_mysql_field): bool = ((f.ftype != FIELD_TYPE_TIMESTAMP) or (f.len == 14) or (f.len == 8)) or (f.ftype == FIELD_TYPE_YEAR) -proc reload(mysql: PMySQL): cint = - result = refresh(mysql, REFRESH_GRANT) +proc reload(x: PMySQL): cint = + result = refresh(x, REFRESH_GRANT) {.pop.} diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 4dc71bffd..f091d8f46 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -41,11 +41,13 @@ {.deadCodeElim: on.} -when defined(WINDOWS): +const useWinVersion = defined(Windows) or defined(nimdoc) + +when useWinVersion: const DLLSSLName = "(ssleay32|libssl32).dll" DLLUtilName = "libeay32.dll" - from winlean import TSocketHandle + from winlean import SocketHandle else: const versions = "(|.1.0.0|.0.9.9|.0.9.8|.0.9.7|.0.9.6|.0.9.5|.0.9.4)" @@ -57,20 +59,19 @@ else: const DLLSSLName = "libssl.so" & versions DLLUtilName = "libcrypto.so" & versions - from posix import TSocketHandle + from posix import SocketHandle type SslStruct {.final, pure.} = object SslPtr* = ptr SslStruct PSslPtr* = ptr SslPtr - PSSL_CTX* = SslPtr - PSSL* = SslPtr + SslCtx* = SslPtr PSSL_METHOD* = SslPtr PX509* = SslPtr PX509_NAME* = SslPtr PEVP_MD* = SslPtr PBIO_METHOD* = SslPtr - PBIO* = SslPtr + BIO* = SslPtr EVP_PKEY* = SslPtr PRSA* = SslPtr PASN1_UTCTIME* = SslPtr @@ -85,7 +86,11 @@ type des_key_schedule* = array[1..16, des_ks_struct] +{.deprecated: [PSSL: SslPtr, PSSL_CTX: SslCtx, PBIO: BIO].} + const + SSL_SENT_SHUTDOWN* = 1 + SSL_RECEIVED_SHUTDOWN* = 2 EVP_MAX_MD_SIZE* = 16 + 20 SSL_ERROR_NONE* = 0 SSL_ERROR_SSL* = 1 @@ -206,58 +211,61 @@ proc SSLv2_method*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.} proc SSLv3_method*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.} proc TLSv1_method*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_new*(context: PSSL_CTX): PSSL{.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_free*(ssl: PSSL){.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_CTX_new*(meth: PSSL_METHOD): PSSL_CTX{.cdecl, +proc SSL_new*(context: SslCtx): SslPtr{.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_free*(ssl: SslPtr){.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_CTX_new*(meth: PSSL_METHOD): SslCtx{.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_CTX_load_verify_locations*(ctx: PSSL_CTX, CAfile: cstring, +proc SSL_CTX_load_verify_locations*(ctx: SslCtx, CAfile: cstring, CApath: cstring): cInt{.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_CTX_free*(arg0: PSSL_CTX){.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_CTX_set_verify*(s: PSSL_CTX, mode: int, cb: proc (a: int, b: pointer): int {.cdecl.}){.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_get_verify_result*(ssl: PSSL): int{.cdecl, +proc SSL_CTX_free*(arg0: SslCtx){.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_CTX_set_verify*(s: SslCtx, mode: int, cb: proc (a: int, b: pointer): int {.cdecl.}){.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_get_verify_result*(ssl: SslPtr): int{.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_CTX_set_cipher_list*(s: PSSLCTX, ciphers: cstring): cint{.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_CTX_use_certificate_file*(ctx: PSSL_CTX, filename: cstring, typ: cInt): cInt{. +proc SSL_CTX_set_cipher_list*(s: SslCtx, ciphers: cstring): cint{.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_CTX_use_certificate_file*(ctx: SslCtx, filename: cstring, typ: cInt): cInt{. stdcall, dynlib: DLLSSLName, importc.} -proc SSL_CTX_use_certificate_chain_file*(ctx: PSSL_CTX, filename: cstring): cInt{. +proc SSL_CTX_use_certificate_chain_file*(ctx: SslCtx, filename: cstring): cInt{. stdcall, dynlib: DLLSSLName, importc.} -proc SSL_CTX_use_PrivateKey_file*(ctx: PSSL_CTX, +proc SSL_CTX_use_PrivateKey_file*(ctx: SslCtx, filename: cstring, typ: cInt): cInt{.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_CTX_check_private_key*(ctx: PSSL_CTX): cInt{.cdecl, dynlib: DLLSSLName, +proc SSL_CTX_check_private_key*(ctx: SslCtx): cInt{.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_set_fd*(ssl: PSSL, fd: TSocketHandle): cint{.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_set_fd*(ssl: SslPtr, fd: SocketHandle): cint{.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_shutdown*(ssl: PSSL): cInt{.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_connect*(ssl: PSSL): cint{.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_read*(ssl: PSSL, buf: pointer, num: int): cint{.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_write*(ssl: PSSL, buf: cstring, num: int): cint{.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_get_error*(s: PSSL, ret_code: cInt): cInt{.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_accept*(ssl: PSSL): cInt{.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_pending*(ssl: PSSL): cInt{.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_shutdown*(ssl: SslPtr): cInt{.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_set_shutdown*(ssl: SslPtr, mode: cint) {.cdecl, dynlib: DLLSSLName, importc: "SSL_set_shutdown".} +proc SSL_get_shutdown*(ssl: SslPtr): cint {.cdecl, dynlib: DLLSSLName, importc: "SSL_get_shutdown".} +proc SSL_connect*(ssl: SslPtr): cint{.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_read*(ssl: SslPtr, buf: pointer, num: int): cint{.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_write*(ssl: SslPtr, buf: cstring, num: int): cint{.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_get_error*(s: SslPtr, ret_code: cInt): cInt{.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_accept*(ssl: SslPtr): cInt{.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_pending*(ssl: SslPtr): cInt{.cdecl, dynlib: DLLSSLName, importc.} -proc BIO_new_ssl_connect*(ctx: PSSL_CTX): PBIO{.cdecl, +proc BIO_new_ssl_connect*(ctx: SslCtx): BIO{.cdecl, dynlib: DLLSSLName, importc.} -proc BIO_ctrl*(bio: PBIO, cmd: cint, larg: int, arg: cstring): int{.cdecl, +proc BIO_ctrl*(bio: BIO, cmd: cint, larg: int, arg: cstring): int{.cdecl, dynlib: DLLSSLName, importc.} -proc BIO_get_ssl*(bio: PBIO, ssl: ptr PSSL): int = +proc BIO_get_ssl*(bio: BIO, ssl: ptr SslPtr): int = return BIO_ctrl(bio, BIO_C_GET_SSL, 0, cast[cstring](ssl)) -proc BIO_set_conn_hostname*(bio: PBIO, name: cstring): int = +proc BIO_set_conn_hostname*(bio: BIO, name: cstring): int = return BIO_ctrl(bio, BIO_C_SET_CONNECT, 0, name) -proc BIO_do_handshake*(bio: PBIO): int = - return BIO_ctrl(bio, BIO_C_DO_STATE_MACHINE, 0, NIL) -proc BIO_do_connect*(bio: PBIO): int = +proc BIO_do_handshake*(bio: BIO): int = + return BIO_ctrl(bio, BIO_C_DO_STATE_MACHINE, 0, nil) +proc BIO_do_connect*(bio: BIO): int = return BIO_do_handshake(bio) -proc BIO_read*(b: PBIO, data: cstring, length: cInt): cInt{.cdecl, - dynlib: DLLUtilName, importc.} -proc BIO_write*(b: PBIO, data: cstring, length: cInt): cInt{.cdecl, - dynlib: DLLUtilName, importc.} +when not defined(nimfix): + proc BIO_read*(b: BIO, data: cstring, length: cInt): cInt{.cdecl, + dynlib: DLLUtilName, importc.} + proc BIO_write*(b: BIO, data: cstring, length: cInt): cInt{.cdecl, + dynlib: DLLUtilName, importc.} -proc BIO_free*(b: PBIO): cInt{.cdecl, dynlib: DLLUtilName, importc.} +proc BIO_free*(b: BIO): cInt{.cdecl, dynlib: DLLUtilName, importc.} -proc ERR_print_errors_fp*(fp: TFile){.cdecl, dynlib: DLLSSLName, importc.} +proc ERR_print_errors_fp*(fp: File){.cdecl, dynlib: DLLSSLName, importc.} proc ERR_error_string*(e: cInt, buf: cstring): cstring{.cdecl, dynlib: DLLUtilName, importc.} @@ -268,20 +276,62 @@ proc OpenSSL_add_all_algorithms*(){.cdecl, dynlib: DLLUtilName, importc: "OPENSS proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.} -when not defined(windows): +when not useWinVersion: proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl, dynlib: DLLUtilName, importc.} -proc CRYPTO_malloc_init*() = - when not defined(windows): - CRYPTO_set_mem_functions(alloc, realloc, dealloc) + proc allocWrapper(size: int): pointer {.cdecl.} = alloc(size) + proc reallocWrapper(p: pointer; newsize: int): pointer {.cdecl.} = + if p == nil: + if newSize > 0: result = alloc(newsize) + elif newsize == 0: dealloc(p) + else: result = realloc(p, newsize) + proc deallocWrapper(p: pointer) {.cdecl.} = + if p != nil: dealloc(p) -proc SSL_CTX_ctrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: pointer): int{. +proc CRYPTO_malloc_init*() = + when not useWinVersion: + CRYPTO_set_mem_functions(allocWrapper, reallocWrapper, deallocWrapper) + +proc SSL_CTX_ctrl*(ctx: SslCtx, cmd: cInt, larg: int, parg: pointer): int{. cdecl, dynlib: DLLSSLName, importc.} -proc SSLCTXSetMode*(ctx: PSSL_CTX, mode: int): int = +proc SSLCTXSetMode*(ctx: SslCtx, mode: int): int = result = SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, mode, nil) +proc bioNew*(b: PBIO_METHOD): BIO{.cdecl, dynlib: DLLUtilName, importc: "BIO_new".} +proc bioFreeAll*(b: BIO){.cdecl, dynlib: DLLUtilName, importc: "BIO_free_all".} +proc bioSMem*(): PBIO_METHOD{.cdecl, dynlib: DLLUtilName, importc: "BIO_s_mem".} +proc bioCtrlPending*(b: BIO): cInt{.cdecl, dynlib: DLLUtilName, importc: "BIO_ctrl_pending".} +proc bioRead*(b: BIO, Buf: cstring, length: cInt): cInt{.cdecl, + dynlib: DLLUtilName, importc: "BIO_read".} +proc bioWrite*(b: BIO, Buf: cstring, length: cInt): cInt{.cdecl, + dynlib: DLLUtilName, importc: "BIO_write".} + +proc sslSetConnectState*(s: SslPtr) {.cdecl, + dynlib: DLLSSLName, importc: "SSL_set_connect_state".} +proc sslSetAcceptState*(s: SslPtr) {.cdecl, + dynlib: DLLSSLName, importc: "SSL_set_accept_state".} + +proc sslRead*(ssl: SslPtr, buf: cstring, num: cInt): cInt{.cdecl, + dynlib: DLLSSLName, importc: "SSL_read".} +proc sslPeek*(ssl: SslPtr, buf: cstring, num: cInt): cInt{.cdecl, + dynlib: DLLSSLName, importc: "SSL_peek".} +proc sslWrite*(ssl: SslPtr, buf: cstring, num: cInt): cInt{.cdecl, + dynlib: DLLSSLName, importc: "SSL_write".} + +proc sslSetBio*(ssl: SslPtr, rbio, wbio: BIO) {.cdecl, + dynlib: DLLSSLName, importc: "SSL_set_bio".} + +proc sslDoHandshake*(ssl: SslPtr): cint {.cdecl, + dynlib: DLLSSLName, importc: "SSL_do_handshake".} + + + +proc ErrClearError*(){.cdecl, dynlib: DLLUtilName, importc: "ERR_clear_error".} +proc ErrFreeStrings*(){.cdecl, dynlib: DLLUtilName, importc: "ERR_free_strings".} +proc ErrRemoveState*(pid: cInt){.cdecl, dynlib: DLLUtilName, importc: "ERR_remove_state".} + when true: discard else: @@ -328,12 +378,7 @@ else: proc SslConnect*(ssl: PSSL): cInt{.cdecl, dynlib: DLLSSLName, importc.} - proc SslRead*(ssl: PSSL, buf: SslPtr, num: cInt): cInt{.cdecl, - dynlib: DLLSSLName, importc.} - proc SslPeek*(ssl: PSSL, buf: SslPtr, num: cInt): cInt{.cdecl, - dynlib: DLLSSLName, importc.} - proc SslWrite*(ssl: PSSL, buf: SslPtr, num: cInt): cInt{.cdecl, - dynlib: DLLSSLName, importc.} + proc SslGetVersion*(ssl: PSSL): cstring{.cdecl, dynlib: DLLSSLName, importc.} proc SslGetPeerCertificate*(ssl: PSSL): PX509{.cdecl, dynlib: DLLSSLName, importc.} @@ -387,20 +432,11 @@ else: # function ErrErrorString(e: cInt; buf: PChar): PChar; proc SSLeayversion*(t: cInt): cstring{.cdecl, dynlib: DLLUtilName, importc.} - proc ErrClearError*(){.cdecl, dynlib: DLLUtilName, importc.} - proc ErrFreeStrings*(){.cdecl, dynlib: DLLUtilName, importc.} - proc ErrRemoveState*(pid: cInt){.cdecl, dynlib: DLLUtilName, importc.} + proc OPENSSLaddallalgorithms*(){.cdecl, dynlib: DLLUtilName, importc.} proc CRYPTOcleanupAllExData*(){.cdecl, dynlib: DLLUtilName, importc.} proc RandScreen*(){.cdecl, dynlib: DLLUtilName, importc.} - proc BioNew*(b: PBIO_METHOD): PBIO{.cdecl, dynlib: DLLUtilName, importc.} - proc BioFreeAll*(b: PBIO){.cdecl, dynlib: DLLUtilName, importc.} - proc BioSMem*(): PBIO_METHOD{.cdecl, dynlib: DLLUtilName, importc.} - proc BioCtrlPending*(b: PBIO): cInt{.cdecl, dynlib: DLLUtilName, importc.} - proc BioRead*(b: PBIO, Buf: cstring, length: cInt): cInt{.cdecl, - dynlib: DLLUtilName, importc.} - proc BioWrite*(b: PBIO, Buf: cstring, length: cInt): cInt{.cdecl, - dynlib: DLLUtilName, importc.} + proc d2iPKCS12bio*(b: PBIO, Pkcs12: SslPtr): SslPtr{.cdecl, dynlib: DLLUtilName, importc.} proc PKCS12parse*(p12: SslPtr, pass: cstring, pkey, cert, ca: var SslPtr): cint{. @@ -448,11 +484,11 @@ type {.pragma: ic, importc: "$1".} {.push callconv:cdecl, dynlib:DLLUtilName.} -proc MD5_Init*(c: var MD5_CTX): cint{.ic.} -proc MD5_Update*(c: var MD5_CTX; data: pointer; len: csize): cint{.ic.} -proc MD5_Final*(md: cstring; c: var MD5_CTX): cint{.ic.} -proc MD5*(d: ptr cuchar; n: csize; md: ptr cuchar): ptr cuchar{.ic.} -proc MD5_Transform*(c: var MD5_CTX; b: ptr cuchar){.ic.} +proc md5_Init*(c: var MD5_CTX): cint{.ic.} +proc md5_Update*(c: var MD5_CTX; data: pointer; len: csize): cint{.ic.} +proc md5_Final*(md: cstring; c: var MD5_CTX): cint{.ic.} +proc md5*(d: ptr cuchar; n: csize; md: ptr cuchar): ptr cuchar{.ic.} +proc md5_Transform*(c: var MD5_CTX; b: ptr cuchar){.ic.} {.pop.} from strutils import toHex,toLower @@ -463,7 +499,7 @@ proc hexStr (buf:cstring): string = for i in 0 .. <16: result.add toHex(buf[i].ord, 2).toLower -proc MD5_File* (file: string): string {.raises:[EIO,Ebase].} = +proc md5_File* (file: string): string {.raises: [IOError,Exception].} = ## Generate MD5 hash for a file. Result is a 32 character # hex string with lowercase characters (like the output # of `md5sum` @@ -483,7 +519,7 @@ proc MD5_File* (file: string): string {.raises:[EIO,Ebase].} = result = hexStr(buf) -proc MD5_Str* (str:string): string {.raises:[EIO].} = +proc md5_Str* (str:string): string {.raises:[IOError].} = ##Generate MD5 hash for a string. Result is a 32 character #hex string with lowercase characters var diff --git a/lib/wrappers/pdcurses.nim b/lib/wrappers/pdcurses.nim index a53289bce..bed69648a 100644 --- a/lib/wrappers/pdcurses.nim +++ b/lib/wrappers/pdcurses.nim @@ -741,7 +741,7 @@ proc getbkgd*(a2: ptr TWINDOW): cunsignedlong{.extdecl, importc: "getbkgd", proc getnstr*(a2: cstring; a3: cint): cint{.extdecl, importc: "getnstr", dynlib: pdcursesdll.} proc getstr*(a2: cstring): cint{.extdecl, importc: "getstr", dynlib: pdcursesdll.} -proc getwin*(a2: TFile): ptr TWINDOW{.extdecl, importc: "getwin", +proc getwin*(a2: File): ptr TWINDOW{.extdecl, importc: "getwin", dynlib: pdcursesdll.} proc halfdelay*(a2: cint): cint{.extdecl, importc: "halfdelay", dynlib: pdcursesdll.} @@ -895,7 +895,7 @@ proc mvwvline*(a2: ptr TWINDOW; a3: cint; a4: cint; a5: cunsignedlong; a6: cint) proc napms*(a2: cint): cint{.extdecl, importc: "napms", dynlib: pdcursesdll.} proc newpad*(a2: cint; a3: cint): ptr TWINDOW{.extdecl, importc: "newpad", dynlib: pdcursesdll.} -proc newterm*(a2: cstring; a3: TFile; a4: TFile): ptr TSCREEN{.extdecl, +proc newterm*(a2: cstring; a3: File; a4: File): ptr TSCREEN{.extdecl, importc: "newterm", dynlib: pdcursesdll.} proc newwin*(a2: cint; a3: cint; a4: cint; a5: cint): ptr TWINDOW{.extdecl, importc: "newwin", dynlib: pdcursesdll.} @@ -924,7 +924,7 @@ proc prefresh*(a2: ptr TWINDOW; a3: cint; a4: cint; a5: cint; a6: cint; a7: cint a8: cint): cint{.extdecl, importc: "prefresh", dynlib: pdcursesdll.} proc printw*(a2: cstring): cint{.varargs, extdecl, importc: "printw", dynlib: pdcursesdll.} -proc putwin*(a2: ptr TWINDOW; a3: TFile): cint{.extdecl, importc: "putwin", +proc putwin*(a2: ptr TWINDOW; a3: File): cint{.extdecl, importc: "putwin", dynlib: pdcursesdll.} proc qiflush*(){.extdecl, importc: "qiflush", dynlib: pdcursesdll.} proc raw*(): cint{.extdecl, importc: "raw", dynlib: pdcursesdll.} diff --git a/lib/wrappers/postgres.nim b/lib/wrappers/postgres.nim index ce78d3435..cb39c41bb 100644 --- a/lib/wrappers/postgres.nim +++ b/lib/wrappers/postgres.nim @@ -68,9 +68,9 @@ type dbName*: cstring status*: TConnStatusType errorMessage*: array[0..(ERROR_MSG_LENGTH) - 1, char] - Pfin*: TFile - Pfout*: TFile - Pfdebug*: TFile + Pfin*: File + Pfout*: File + Pfdebug*: File sock*: int32 laddr*: TSockAddr raddr*: TSockAddr @@ -145,184 +145,184 @@ type p*: pointer -proc PQconnectStart*(conninfo: cstring): PPGconn{.cdecl, dynlib: dllName, +proc pqconnectStart*(conninfo: cstring): PPGconn{.cdecl, dynlib: dllName, importc: "PQconnectStart".} -proc PQconnectPoll*(conn: PPGconn): PostgresPollingStatusType{.cdecl, +proc pqconnectPoll*(conn: PPGconn): PostgresPollingStatusType{.cdecl, dynlib: dllName, importc: "PQconnectPoll".} -proc PQconnectdb*(conninfo: cstring): PPGconn{.cdecl, dynlib: dllName, +proc pqconnectdb*(conninfo: cstring): PPGconn{.cdecl, dynlib: dllName, importc: "PQconnectdb".} -proc PQsetdbLogin*(pghost: cstring, pgport: cstring, pgoptions: cstring, +proc pqsetdbLogin*(pghost: cstring, pgport: cstring, pgoptions: cstring, pgtty: cstring, dbName: cstring, login: cstring, pwd: cstring): PPGconn{. cdecl, dynlib: dllName, importc: "PQsetdbLogin".} -proc PQsetdb*(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME: cstring): ppgconn -proc PQfinish*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQfinish".} -proc PQconndefaults*(): PPQconninfoOption{.cdecl, dynlib: dllName, +proc pqsetdb*(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME: cstring): Ppgconn +proc pqfinish*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQfinish".} +proc pqconndefaults*(): PPQconninfoOption{.cdecl, dynlib: dllName, importc: "PQconndefaults".} -proc PQconninfoFree*(connOptions: PPQconninfoOption){.cdecl, dynlib: dllName, +proc pqconninfoFree*(connOptions: PPQconninfoOption){.cdecl, dynlib: dllName, importc: "PQconninfoFree".} -proc PQresetStart*(conn: PPGconn): int32{.cdecl, dynlib: dllName, +proc pqresetStart*(conn: PPGconn): int32{.cdecl, dynlib: dllName, importc: "PQresetStart".} -proc PQresetPoll*(conn: PPGconn): PostgresPollingStatusType{.cdecl, +proc pqresetPoll*(conn: PPGconn): PostgresPollingStatusType{.cdecl, dynlib: dllName, importc: "PQresetPoll".} -proc PQreset*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQreset".} -proc PQrequestCancel*(conn: PPGconn): int32{.cdecl, dynlib: dllName, +proc pqreset*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQreset".} +proc pqrequestCancel*(conn: PPGconn): int32{.cdecl, dynlib: dllName, importc: "PQrequestCancel".} -proc PQdb*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQdb".} -proc PQuser*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQuser".} -proc PQpass*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQpass".} -proc PQhost*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQhost".} -proc PQport*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQport".} -proc PQtty*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQtty".} -proc PQoptions*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, +proc pqdb*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQdb".} +proc pquser*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQuser".} +proc pqpass*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQpass".} +proc pqhost*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQhost".} +proc pqport*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQport".} +proc pqtty*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQtty".} +proc pqoptions*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQoptions".} -proc PQstatus*(conn: PPGconn): TConnStatusType{.cdecl, dynlib: dllName, +proc pqstatus*(conn: PPGconn): TConnStatusType{.cdecl, dynlib: dllName, importc: "PQstatus".} -proc PQtransactionStatus*(conn: PPGconn): PGTransactionStatusType{.cdecl, +proc pqtransactionStatus*(conn: PPGconn): PGTransactionStatusType{.cdecl, dynlib: dllName, importc: "PQtransactionStatus".} -proc PQparameterStatus*(conn: PPGconn, paramName: cstring): cstring{.cdecl, +proc pqparameterStatus*(conn: PPGconn, paramName: cstring): cstring{.cdecl, dynlib: dllName, importc: "PQparameterStatus".} -proc PQprotocolVersion*(conn: PPGconn): int32{.cdecl, dynlib: dllName, +proc pqprotocolVersion*(conn: PPGconn): int32{.cdecl, dynlib: dllName, importc: "PQprotocolVersion".} -proc PQerrorMessage*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, +proc pqerrorMessage*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQerrorMessage".} -proc PQsocket*(conn: PPGconn): int32{.cdecl, dynlib: dllName, +proc pqsocket*(conn: PPGconn): int32{.cdecl, dynlib: dllName, importc: "PQsocket".} -proc PQbackendPID*(conn: PPGconn): int32{.cdecl, dynlib: dllName, +proc pqbackendPID*(conn: PPGconn): int32{.cdecl, dynlib: dllName, importc: "PQbackendPID".} -proc PQclientEncoding*(conn: PPGconn): int32{.cdecl, dynlib: dllName, +proc pqclientEncoding*(conn: PPGconn): int32{.cdecl, dynlib: dllName, importc: "PQclientEncoding".} -proc PQsetClientEncoding*(conn: PPGconn, encoding: cstring): int32{.cdecl, +proc pqsetClientEncoding*(conn: PPGconn, encoding: cstring): int32{.cdecl, dynlib: dllName, importc: "PQsetClientEncoding".} when defined(USE_SSL): # Get the SSL structure associated with a connection - proc PQgetssl*(conn: PPGconn): PSSL{.cdecl, dynlib: dllName, + proc pqgetssl*(conn: PPGconn): PSSL{.cdecl, dynlib: dllName, importc: "PQgetssl".} -proc PQsetErrorVerbosity*(conn: PPGconn, verbosity: PGVerbosity): PGVerbosity{. +proc pqsetErrorVerbosity*(conn: PPGconn, verbosity: PGVerbosity): PGVerbosity{. cdecl, dynlib: dllName, importc: "PQsetErrorVerbosity".} -proc PQtrace*(conn: PPGconn, debug_port: TFile){.cdecl, dynlib: dllName, +proc pqtrace*(conn: PPGconn, debug_port: File){.cdecl, dynlib: dllName, importc: "PQtrace".} -proc PQuntrace*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQuntrace".} -proc PQsetNoticeReceiver*(conn: PPGconn, theProc: PQnoticeReceiver, arg: pointer): PQnoticeReceiver{. +proc pquntrace*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQuntrace".} +proc pqsetNoticeReceiver*(conn: PPGconn, theProc: PQnoticeReceiver, arg: pointer): PQnoticeReceiver{. cdecl, dynlib: dllName, importc: "PQsetNoticeReceiver".} -proc PQsetNoticeProcessor*(conn: PPGconn, theProc: PQnoticeProcessor, +proc pqsetNoticeProcessor*(conn: PPGconn, theProc: PQnoticeProcessor, arg: pointer): PQnoticeProcessor{.cdecl, dynlib: dllName, importc: "PQsetNoticeProcessor".} -proc PQexec*(conn: PPGconn, query: cstring): PPGresult{.cdecl, dynlib: dllName, +proc pqexec*(conn: PPGconn, query: cstring): PPGresult{.cdecl, dynlib: dllName, importc: "PQexec".} -proc PQexecParams*(conn: PPGconn, command: cstring, nParams: int32, +proc pqexecParams*(conn: PPGconn, command: cstring, nParams: int32, paramTypes: POid, paramValues: cstringArray, paramLengths, paramFormats: ptr int32, resultFormat: int32): PPGresult{. cdecl, dynlib: dllName, importc: "PQexecParams".} -proc PQprepare*(conn: PPGconn, stmtName, query: cstring, nParams: int32, +proc pqprepare*(conn: PPGconn, stmtName, query: cstring, nParams: int32, paramTypes: POid): PPGresult{.cdecl, dynlib: dllName, importc: "PQprepare".} -proc PQexecPrepared*(conn: PPGconn, stmtName: cstring, nParams: int32, - paramValues: cstringArray, +proc pqexecPrepared*(conn: PPGconn, stmtName: cstring, nParams: int32, + paramValues: cstringArray, paramLengths, paramFormats: ptr int32, resultFormat: int32): PPGresult{. cdecl, dynlib: dllName, importc: "PQexecPrepared".} -proc PQsendQuery*(conn: PPGconn, query: cstring): int32{.cdecl, dynlib: dllName, +proc pqsendQuery*(conn: PPGconn, query: cstring): int32{.cdecl, dynlib: dllName, importc: "PQsendQuery".} -proc PQsendQueryParams*(conn: PPGconn, command: cstring, nParams: int32, +proc pqsendQueryParams*(conn: PPGconn, command: cstring, nParams: int32, paramTypes: POid, paramValues: cstringArray, paramLengths, paramFormats: ptr int32, resultFormat: int32): int32{.cdecl, dynlib: dllName, importc: "PQsendQueryParams".} -proc PQsendQueryPrepared*(conn: PPGconn, stmtName: cstring, nParams: int32, +proc pqsendQueryPrepared*(conn: PPGconn, stmtName: cstring, nParams: int32, paramValues: cstringArray, paramLengths, paramFormats: ptr int32, resultFormat: int32): int32{.cdecl, dynlib: dllName, importc: "PQsendQueryPrepared".} -proc PQgetResult*(conn: PPGconn): PPGresult{.cdecl, dynlib: dllName, +proc pqgetResult*(conn: PPGconn): PPGresult{.cdecl, dynlib: dllName, importc: "PQgetResult".} -proc PQisBusy*(conn: PPGconn): int32{.cdecl, dynlib: dllName, +proc pqisBusy*(conn: PPGconn): int32{.cdecl, dynlib: dllName, importc: "PQisBusy".} -proc PQconsumeInput*(conn: PPGconn): int32{.cdecl, dynlib: dllName, +proc pqconsumeInput*(conn: PPGconn): int32{.cdecl, dynlib: dllName, importc: "PQconsumeInput".} -proc PQnotifies*(conn: PPGconn): PPGnotify{.cdecl, dynlib: dllName, +proc pqnotifies*(conn: PPGconn): PPGnotify{.cdecl, dynlib: dllName, importc: "PQnotifies".} -proc PQputCopyData*(conn: PPGconn, buffer: cstring, nbytes: int32): int32{. +proc pqputCopyData*(conn: PPGconn, buffer: cstring, nbytes: int32): int32{. cdecl, dynlib: dllName, importc: "PQputCopyData".} -proc PQputCopyEnd*(conn: PPGconn, errormsg: cstring): int32{.cdecl, +proc pqputCopyEnd*(conn: PPGconn, errormsg: cstring): int32{.cdecl, dynlib: dllName, importc: "PQputCopyEnd".} -proc PQgetCopyData*(conn: PPGconn, buffer: cstringArray, async: int32): int32{. +proc pqgetCopyData*(conn: PPGconn, buffer: cstringArray, async: int32): int32{. cdecl, dynlib: dllName, importc: "PQgetCopyData".} -proc PQgetline*(conn: PPGconn, str: cstring, len: int32): int32{.cdecl, +proc pqgetline*(conn: PPGconn, str: cstring, len: int32): int32{.cdecl, dynlib: dllName, importc: "PQgetline".} -proc PQputline*(conn: PPGconn, str: cstring): int32{.cdecl, dynlib: dllName, +proc pqputline*(conn: PPGconn, str: cstring): int32{.cdecl, dynlib: dllName, importc: "PQputline".} -proc PQgetlineAsync*(conn: PPGconn, buffer: cstring, bufsize: int32): int32{. +proc pqgetlineAsync*(conn: PPGconn, buffer: cstring, bufsize: int32): int32{. cdecl, dynlib: dllName, importc: "PQgetlineAsync".} -proc PQputnbytes*(conn: PPGconn, buffer: cstring, nbytes: int32): int32{.cdecl, +proc pqputnbytes*(conn: PPGconn, buffer: cstring, nbytes: int32): int32{.cdecl, dynlib: dllName, importc: "PQputnbytes".} -proc PQendcopy*(conn: PPGconn): int32{.cdecl, dynlib: dllName, +proc pqendcopy*(conn: PPGconn): int32{.cdecl, dynlib: dllName, importc: "PQendcopy".} -proc PQsetnonblocking*(conn: PPGconn, arg: int32): int32{.cdecl, +proc pqsetnonblocking*(conn: PPGconn, arg: int32): int32{.cdecl, dynlib: dllName, importc: "PQsetnonblocking".} -proc PQisnonblocking*(conn: PPGconn): int32{.cdecl, dynlib: dllName, +proc pqisnonblocking*(conn: PPGconn): int32{.cdecl, dynlib: dllName, importc: "PQisnonblocking".} -proc PQflush*(conn: PPGconn): int32{.cdecl, dynlib: dllName, importc: "PQflush".} -proc PQfn*(conn: PPGconn, fnid: int32, result_buf, result_len: ptr int32, +proc pqflush*(conn: PPGconn): int32{.cdecl, dynlib: dllName, importc: "PQflush".} +proc pqfn*(conn: PPGconn, fnid: int32, result_buf, result_len: ptr int32, result_is_int: int32, args: PPQArgBlock, nargs: int32): PPGresult{. cdecl, dynlib: dllName, importc: "PQfn".} -proc PQresultStatus*(res: PPGresult): TExecStatusType{.cdecl, dynlib: dllName, +proc pqresultStatus*(res: PPGresult): TExecStatusType{.cdecl, dynlib: dllName, importc: "PQresultStatus".} -proc PQresStatus*(status: TExecStatusType): cstring{.cdecl, dynlib: dllName, +proc pqresStatus*(status: TExecStatusType): cstring{.cdecl, dynlib: dllName, importc: "PQresStatus".} -proc PQresultErrorMessage*(res: PPGresult): cstring{.cdecl, dynlib: dllName, +proc pqresultErrorMessage*(res: PPGresult): cstring{.cdecl, dynlib: dllName, importc: "PQresultErrorMessage".} -proc PQresultErrorField*(res: PPGresult, fieldcode: int32): cstring{.cdecl, +proc pqresultErrorField*(res: PPGresult, fieldcode: int32): cstring{.cdecl, dynlib: dllName, importc: "PQresultErrorField".} -proc PQntuples*(res: PPGresult): int32{.cdecl, dynlib: dllName, +proc pqntuples*(res: PPGresult): int32{.cdecl, dynlib: dllName, importc: "PQntuples".} -proc PQnfields*(res: PPGresult): int32{.cdecl, dynlib: dllName, +proc pqnfields*(res: PPGresult): int32{.cdecl, dynlib: dllName, importc: "PQnfields".} -proc PQbinaryTuples*(res: PPGresult): int32{.cdecl, dynlib: dllName, +proc pqbinaryTuples*(res: PPGresult): int32{.cdecl, dynlib: dllName, importc: "PQbinaryTuples".} -proc PQfname*(res: PPGresult, field_num: int32): cstring{.cdecl, +proc pqfname*(res: PPGresult, field_num: int32): cstring{.cdecl, dynlib: dllName, importc: "PQfname".} -proc PQfnumber*(res: PPGresult, field_name: cstring): int32{.cdecl, +proc pqfnumber*(res: PPGresult, field_name: cstring): int32{.cdecl, dynlib: dllName, importc: "PQfnumber".} -proc PQftable*(res: PPGresult, field_num: int32): Oid{.cdecl, dynlib: dllName, +proc pqftable*(res: PPGresult, field_num: int32): Oid{.cdecl, dynlib: dllName, importc: "PQftable".} -proc PQftablecol*(res: PPGresult, field_num: int32): int32{.cdecl, +proc pqftablecol*(res: PPGresult, field_num: int32): int32{.cdecl, dynlib: dllName, importc: "PQftablecol".} -proc PQfformat*(res: PPGresult, field_num: int32): int32{.cdecl, +proc pqfformat*(res: PPGresult, field_num: int32): int32{.cdecl, dynlib: dllName, importc: "PQfformat".} -proc PQftype*(res: PPGresult, field_num: int32): Oid{.cdecl, dynlib: dllName, +proc pqftype*(res: PPGresult, field_num: int32): Oid{.cdecl, dynlib: dllName, importc: "PQftype".} -proc PQfsize*(res: PPGresult, field_num: int32): int32{.cdecl, dynlib: dllName, +proc pqfsize*(res: PPGresult, field_num: int32): int32{.cdecl, dynlib: dllName, importc: "PQfsize".} -proc PQfmod*(res: PPGresult, field_num: int32): int32{.cdecl, dynlib: dllName, +proc pqfmod*(res: PPGresult, field_num: int32): int32{.cdecl, dynlib: dllName, importc: "PQfmod".} -proc PQcmdStatus*(res: PPGresult): cstring{.cdecl, dynlib: dllName, +proc pqcmdStatus*(res: PPGresult): cstring{.cdecl, dynlib: dllName, importc: "PQcmdStatus".} -proc PQoidStatus*(res: PPGresult): cstring{.cdecl, dynlib: dllName, +proc pqoidStatus*(res: PPGresult): cstring{.cdecl, dynlib: dllName, importc: "PQoidStatus".} -proc PQoidValue*(res: PPGresult): Oid{.cdecl, dynlib: dllName, +proc pqoidValue*(res: PPGresult): Oid{.cdecl, dynlib: dllName, importc: "PQoidValue".} -proc PQcmdTuples*(res: PPGresult): cstring{.cdecl, dynlib: dllName, +proc pqcmdTuples*(res: PPGresult): cstring{.cdecl, dynlib: dllName, importc: "PQcmdTuples".} -proc PQgetvalue*(res: PPGresult, tup_num: int32, field_num: int32): cstring{. +proc pqgetvalue*(res: PPGresult, tup_num: int32, field_num: int32): cstring{. cdecl, dynlib: dllName, importc: "PQgetvalue".} -proc PQgetlength*(res: PPGresult, tup_num: int32, field_num: int32): int32{. +proc pqgetlength*(res: PPGresult, tup_num: int32, field_num: int32): int32{. cdecl, dynlib: dllName, importc: "PQgetlength".} -proc PQgetisnull*(res: PPGresult, tup_num: int32, field_num: int32): int32{. +proc pqgetisnull*(res: PPGresult, tup_num: int32, field_num: int32): int32{. cdecl, dynlib: dllName, importc: "PQgetisnull".} -proc PQclear*(res: PPGresult){.cdecl, dynlib: dllName, importc: "PQclear".} -proc PQfreemem*(p: pointer){.cdecl, dynlib: dllName, importc: "PQfreemem".} -proc PQmakeEmptyPGresult*(conn: PPGconn, status: TExecStatusType): PPGresult{. +proc pqclear*(res: PPGresult){.cdecl, dynlib: dllName, importc: "PQclear".} +proc pqfreemem*(p: pointer){.cdecl, dynlib: dllName, importc: "PQfreemem".} +proc pqmakeEmptyPGresult*(conn: PPGconn, status: TExecStatusType): PPGresult{. cdecl, dynlib: dllName, importc: "PQmakeEmptyPGresult".} -proc PQescapeString*(till, `from`: cstring, len: int): int{.cdecl, +proc pqescapeString*(till, `from`: cstring, len: int): int{.cdecl, dynlib: dllName, importc: "PQescapeString".} -proc PQescapeBytea*(bintext: cstring, binlen: int, bytealen: var int): cstring{. +proc pqescapeBytea*(bintext: cstring, binlen: int, bytealen: var int): cstring{. cdecl, dynlib: dllName, importc: "PQescapeBytea".} -proc PQunescapeBytea*(strtext: cstring, retbuflen: var int): cstring{.cdecl, +proc pqunescapeBytea*(strtext: cstring, retbuflen: var int): cstring{.cdecl, dynlib: dllName, importc: "PQunescapeBytea".} -proc PQprint*(fout: TFile, res: PPGresult, ps: PPQprintOpt){.cdecl, +proc pqprint*(fout: File, res: PPGresult, ps: PPQprintOpt){.cdecl, dynlib: dllName, importc: "PQprint".} -proc PQdisplayTuples*(res: PPGresult, fp: TFile, fillAlign: int32, +proc pqdisplayTuples*(res: PPGresult, fp: File, fillAlign: int32, fieldSep: cstring, printHeader: int32, quiet: int32){. cdecl, dynlib: dllName, importc: "PQdisplayTuples".} -proc PQprintTuples*(res: PPGresult, fout: TFile, printAttName: int32, +proc pqprintTuples*(res: PPGresult, fout: File, printAttName: int32, terseOutput: int32, width: int32){.cdecl, dynlib: dllName, importc: "PQprintTuples".} proc lo_open*(conn: PPGconn, lobjId: Oid, mode: int32): int32{.cdecl, @@ -345,8 +345,8 @@ proc lo_import*(conn: PPGconn, filename: cstring): Oid{.cdecl, dynlib: dllName, importc: "lo_import".} proc lo_export*(conn: PPGconn, lobjId: Oid, filename: cstring): int32{.cdecl, dynlib: dllName, importc: "lo_export".} -proc PQmblen*(s: cstring, encoding: int32): int32{.cdecl, dynlib: dllName, +proc pqmblen*(s: cstring, encoding: int32): int32{.cdecl, dynlib: dllName, importc: "PQmblen".} -proc PQenv2encoding*(): int32{.cdecl, dynlib: dllName, importc: "PQenv2encoding".} -proc PQsetdb(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME: cstring): ppgconn = - result = PQsetdbLogin(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME, "", "") +proc pqenv2encoding*(): int32{.cdecl, dynlib: dllName, importc: "PQenv2encoding".} +proc pqsetdb(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME: cstring): PPgConn = + result = pqSetdbLogin(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME, "", "") diff --git a/lib/wrappers/readline/history.nim b/lib/wrappers/readline/history.nim index 12dfa2707..caa857ceb 100644 --- a/lib/wrappers/readline/history.nim +++ b/lib/wrappers/readline/history.nim @@ -142,7 +142,7 @@ proc history_get*(a2: cint): ptr THIST_ENTRY{.cdecl, importc: "history_get", # Return the timestamp associated with the HIST_ENTRY * passed as an # argument -proc history_get_time*(a2: ptr THIST_ENTRY): TTime{.cdecl, +proc history_get_time*(a2: ptr THIST_ENTRY): Time{.cdecl, importc: "history_get_time", dynlib: historyDll.} # Return the number of bytes that the primary history entries are using. # This just adds up the lengths of the_history->lines. diff --git a/lib/wrappers/readline/readline.nim b/lib/wrappers/readline/readline.nim index bbe416534..5a319243e 100644 --- a/lib/wrappers/readline/readline.nim +++ b/lib/wrappers/readline/readline.nim @@ -776,7 +776,7 @@ proc execute_next*(a2: cint): cint{.cdecl, importc: "rl_execute_next", proc clear_pending_input*(): cint{.cdecl, importc: "rl_clear_pending_input", dynlib: readlineDll.} proc read_key*(): cint{.cdecl, importc: "rl_read_key", dynlib: readlineDll.} -proc getc*(a2: TFile): cint{.cdecl, importc: "rl_getc", dynlib: readlineDll.} +proc getc*(a2: File): cint{.cdecl, importc: "rl_getc", dynlib: readlineDll.} proc set_keyboard_input_timeout*(a2: cint): cint{.cdecl, importc: "rl_set_keyboard_input_timeout", dynlib: readlineDll.} # `Public' utility functions . @@ -881,8 +881,8 @@ when false: # The name of the terminal to use. var terminal_name*{.importc: "rl_terminal_name", dynlib: readlineDll.}: cstring # The input and output streams. - var instream*{.importc: "rl_instream", dynlib: readlineDll.}: TFile - var outstream*{.importc: "rl_outstream", dynlib: readlineDll.}: TFile + var instream*{.importc: "rl_instream", dynlib: readlineDll.}: File + var outstream*{.importc: "rl_outstream", dynlib: readlineDll.}: File # If non-zero, Readline gives values of LINES and COLUMNS from the environment # greater precedence than values fetched from the kernel when computing the # screen dimensions. @@ -1184,8 +1184,8 @@ type insmode*: cint edmode*: cint kseqlen*: cint - inf*: TFile - outf*: TFile + inf*: File + outf*: File pendingin*: cint theMacro*: cstring # signal state catchsigs*: cint diff --git a/lib/wrappers/readline/rltypedefs.nim b/lib/wrappers/readline/rltypedefs.nim index 202cf925d..847834e80 100644 --- a/lib/wrappers/readline/rltypedefs.nim +++ b/lib/wrappers/readline/rltypedefs.nim @@ -48,7 +48,7 @@ type # Input function type type - Tgetc_func* = proc (a2: TFile): cint{.cdecl.} + Tgetc_func* = proc (a2: File): cint{.cdecl.} # Generic function that takes a character buffer (which could be the readline # line buffer) and an index into it (which could be rl_point) and returns diff --git a/lib/wrappers/sdl/sdl.nim b/lib/wrappers/sdl/sdl.nim index 1c88bc14e..449b651f9 100644 --- a/lib/wrappers/sdl/sdl.nim +++ b/lib/wrappers/sdl/sdl.nim @@ -250,14 +250,14 @@ # SDL_GL_MULTISAMPLESAMPLES # # Add DLL/Shared object functions -# function SDL_LoadObject( const sofile : PChar ) : Pointer; +# function SDL_LoadObject( const sofile : PChar ) : pointer; # -# function SDL_LoadFunction( handle : Pointer; const name : PChar ) : Pointer; +# function SDL_LoadFunction( handle : pointer; const name : PChar ) : pointer; # -# procedure SDL_UnloadObject( handle : Pointer ); +# procedure SDL_UnloadObject( handle : pointer ); # # Added function to create RWops from const memory: SDL_RWFromConstMem() -# function SDL_RWFromConstMem(const mem: Pointer; size: Integer) : PSDL_RWops; +# function SDL_RWFromConstMem(const mem: pointer; size: Integer) : PSDL_RWops; # # Ported SDL_cpuinfo.h so Now you can test for Specific CPU types. # @@ -752,12 +752,12 @@ const # Enumeration of valid key mods (possibly OR'd tog type THandle* = int #SDL_types.h types # Basic data types - TBool* = enum + TBool* = enum sdlFALSE, sdlTRUE PUInt8Array* = ptr TUInt8Array TUInt8Array* = array[0..high(int) shr 1, byte] - PUInt16* = ptr UInt16 - PUInt32* = ptr UInt32 + PUInt16* = ptr uint16 + PUInt32* = ptr uint32 PUInt64* = ptr UInt64 UInt64*{.final.} = object hi*: int32 @@ -771,12 +771,12 @@ type TGrabMode* = int32 # SDL_error.h types Terrorcode* = enum ENOMEM, EFREAD, EFWRITE, EFSEEK, LASTERROR - errorcode* = Terrorcode + Errorcode* = Terrorcode TArg*{.final.} = object buf*: array[0..ERR_MAX_STRLEN - 1, int8] Perror* = ptr Terror - Terror*{.final.} = object # This is a numeric value corresponding to the current error + TError*{.final.} = object # This is a numeric value corresponding to the current error # SDL_rwops.h types # This is the read/write operation structure -- very basic # some helper types to handle the unions @@ -790,8 +790,8 @@ type args*: array[0..ERR_MAX_ARGS - 1, TArg] TStdio*{.final.} = object - autoclose*: int # FILE * is only defined in Kylix so we use a simple Pointer - fp*: Pointer + autoclose*: int # FILE * is only defined in Kylix so we use a simple pointer + fp*: pointer TMem*{.final.} = object base*: ptr byte @@ -800,9 +800,9 @@ type PRWops* = ptr TRWops # now the pointer to function types TSeek* = proc (context: PRWops, offset: int, whence: int): int{.cdecl.} - TRead* = proc (context: PRWops, thePtr: Pointer, size: int, maxnum: int): int{. + TRead* = proc (context: PRWops, thePtr: pointer, size: int, maxnum: int): int{. cdecl.} - TWrite* = proc (context: PRWops, thePtr: Pointer, size: int, num: int): int{. + TWrite* = proc (context: PRWops, thePtr: pointer, size: int, num: int): int{. cdecl.} TClose* = proc (context: PRWops): int{.cdecl.} # the variant record itself TRWops*{.final.} = object @@ -817,27 +817,27 @@ type RWops* = TRWops # SDL_timer.h types # Function prototype for the timer callback function TTimerCallback* = proc (interval: int32): int32{.cdecl.} - TNewTimerCallback* = proc (interval: int32, param: Pointer): int32{.cdecl.} + TNewTimerCallback* = proc (interval: int32, param: pointer): int32{.cdecl.} PTimerID* = ptr TTimerID TTimerID*{.final.} = object interval*: int32 callback*: TNewTimerCallback - param*: Pointer - last_alarm*: int32 + param*: pointer + lastAlarm*: int32 next*: PTimerID - TAudioSpecCallback* = proc (userdata: Pointer, stream: ptr byte, length: int){. + TAudioSpecCallback* = proc (userdata: pointer, stream: ptr byte, length: int){. cdecl.} # SDL_audio.h types # The calculated values in this structure are calculated by SDL_OpenAudio() PAudioSpec* = ptr TAudioSpec TAudioSpec*{.final.} = object # A structure to hold a set of audio conversion filters and buffers freq*: int # DSP frequency -- samples per second - format*: UInt16 # Audio data format + format*: uint16 # Audio data format channels*: byte # Number of channels: 1 mono, 2 stereo silence*: byte # Audio buffer silence value (calculated) - samples*: UInt16 # Audio buffer size in samples - padding*: UInt16 # Necessary for some compile environments + samples*: uint16 # Audio buffer size in samples + padding*: uint16 # Necessary for some compile environments size*: int32 # Audio buffer size in bytes (calculated) # This function is called when the audio device needs more data. # 'stream' is a pointer to the audio data buffer @@ -845,28 +845,28 @@ type # Once the callback returns, the buffer will no longer be valid. # Stereo samples are stored in a LRLRLR ordering. callback*: TAudioSpecCallback - userdata*: Pointer + userdata*: pointer PAudioCVT* = ptr TAudioCVT PAudioCVTFilter* = ptr TAudioCVTFilter TAudioCVTFilter*{.final.} = object cvt*: PAudioCVT - format*: UInt16 + format*: uint16 PAudioCVTFilterArray* = ptr TAudioCVTFilterArray TAudioCVTFilterArray* = array[0..9, PAudioCVTFilter] TAudioCVT*{.final.} = object needed*: int # Set to 1 if conversion possible - src_format*: UInt16 # Source audio format - dst_format*: UInt16 # Target audio format - rate_incr*: float64 # Rate conversion increment + srcFormat*: uint16 # Source audio format + dstFormat*: uint16 # Target audio format + rateIncr*: float64 # Rate conversion increment buf*: ptr byte # Buffer to hold entire audio data length*: int # Length of original audio buffer - len_cvt*: int # Length of converted audio buffer - len_mult*: int # buffer must be len*len_mult big - len_ratio*: float64 # Given len, final size is len*len_ratio + lenCvt*: int # Length of converted audio buffer + lenMult*: int # buffer must be len*len_mult big + lenRatio*: float64 # Given len, final size is len*len_ratio filters*: TAudioCVTFilterArray - filter_index*: int # Current audio conversion function + filterIndex*: int # Current audio conversion function TAudiostatus* = enum # SDL_cdrom.h types AUDIO_STOPPED, AUDIO_PLAYING, AUDIO_PAUSED @@ -876,7 +876,7 @@ type TCDTrack*{.final.} = object # This structure is only current as of the last call to SDL_CDStatus() id*: byte # Track number theType*: byte # Data or audio track - unused*: UInt16 + unused*: uint16 len*: int32 # Length, in frames, of this track offset*: int32 # Offset, in frames, from start of disk @@ -886,8 +886,8 @@ type status*: TCDStatus # Current drive status # The rest of this structure is only valid if there's a CD in drive numtracks*: int # Number of tracks on disk - cur_track*: int # Current track position - cur_frame*: int # Current frame offset within current track + curTrack*: int # Current track position + curFrame*: int # Current frame offset within current track track*: array[0..MAX_TRACKS, TCDTrack] PTransAxis* = ptr TTransAxis @@ -895,7 +895,7 @@ type offset*: int scale*: float32 - PJoystick_hwdata* = ptr TJoystick_hwdata + PJoystickHwdata* = ptr TJoystickHwdata TJoystick_hwdata*{.final.} = object # joystick ID id*: int # values used to translate device-specific coordinates into SDL-standard ranges transaxis*: array[0..5, TTransAxis] @@ -918,8 +918,8 @@ type balls*: PBallDelta # Current ball motion deltas nbuttons*: int # Number of buttons on the joystick buttons*: ptr byte # Current button states - hwdata*: PJoystick_hwdata # Driver dependent information - ref_count*: int # Reference count for multiple opens + hwdata*: PJoystickHwdata # Driver dependent information + refCount*: int # Reference count for multiple opens Pversion* = ptr Tversion Tversion*{.final.} = object # SDL_keyboard.h types @@ -945,7 +945,7 @@ type scancode*: byte # hardware specific scancode sym*: TKey # SDL virtual keysym modifier*: TMod # current key modifiers - unicode*: UInt16 # translated character + unicode*: uint16 # translated character TEventAction* = enum # Application visibility event structure ADDEVENT, PEEKEVENT, GETEVENT @@ -971,7 +971,7 @@ type kind*: TEventKind which*: byte # The mouse device index state*: byte # The current button state - x*, y*: UInt16 # The X/Y coordinates of the mouse + x*, y*: uint16 # The X/Y coordinates of the mouse xrel*: int16 # The relative motion in the X direction yrel*: int16 # The relative motion in the Y direction @@ -982,8 +982,8 @@ type which*: byte # The mouse device index button*: byte # The mouse button index state*: byte # SDL_PRESSED or SDL_RELEASED - x*: UInt16 # The X coordinates of the mouse at press time - y*: UInt16 # The Y coordinates of the mouse at press time + x*: uint16 # The X coordinates of the mouse at press time + y*: uint16 # The Y coordinates of the mouse at press time PJoyAxisEvent* = ptr TJoyAxisEvent TJoyAxisEvent*{.final.} = object # SDL_JOYAXISMOTION @@ -1036,8 +1036,8 @@ type TUserEvent*{.final.} = object # SDL_USEREVENT through SDL_NUMEVENTS-1 kind*: TEventKind code*: cint # User defined event code - data1*: Pointer # User defined data pointer - data2*: Pointer # User defined data pointer + data1*: pointer # User defined data pointer + data2*: pointer # User defined data pointer when defined(Unix): @@ -1051,7 +1051,7 @@ when defined(WINDOWS): version*: Tversion hwnd*: THandle # The window for the message msg*: int # The type of message - w_Param*: int32 # WORD message parameter + wParam*: int32 # WORD message parameter lParam*: int32 # LONG message parameter elif defined(Unix): @@ -1090,8 +1090,8 @@ elif defined(Unix): # any X11 functions using the display variable. # They lock the event thread, so should not be # called around event functions or from event filters. - lock_func*: Pointer - unlock_func*: Pointer # Introduced in SDL 1.0.2 + lock_func*: pointer + unlock_func*: pointer # Introduced in SDL 1.0.2 fswindow*: TWindow # The X11 fullscreen window wmwindow*: TWindow # The X11 managed input window @@ -1135,7 +1135,7 @@ type PRect* = ptr TRect TRect*{.final.} = object x*, y*: int16 - w*, h*: UInt16 + w*, h*: uint16 Rect* = TRect PColor* = ptr TColor @@ -1155,34 +1155,34 @@ type PPixelFormat* = ptr TPixelFormat TPixelFormat*{.final.} = object # The structure passed to the low level blit functions palette*: PPalette - BitsPerPixel*: byte - BytesPerPixel*: byte - Rloss*: byte - Gloss*: byte - Bloss*: byte - Aloss*: byte - Rshift*: byte - Gshift*: byte - Bshift*: byte - Ashift*: byte - RMask*: int32 - GMask*: int32 - BMask*: int32 - AMask*: int32 + bitsPerPixel*: byte + bytesPerPixel*: byte + rloss*: byte + gloss*: byte + bloss*: byte + aloss*: byte + rshift*: byte + gshift*: byte + bshift*: byte + ashift*: byte + rMask*: int32 + gMask*: int32 + bMask*: int32 + aMask*: int32 colorkey*: int32 # RGB color key information alpha*: byte # Alpha value information (per-surface alpha) PBlitInfo* = ptr TBlitInfo TBlitInfo*{.final.} = object # typedef for private surface blitting functions - s_pixels*: ptr byte - s_width*: int - s_height*: int - s_skip*: int - d_pixels*: ptr byte - d_width*: int - d_height*: int - d_skip*: int - aux_data*: Pointer + sPixels*: ptr byte + sWidth*: int + sHeight*: int + sSkip*: int + dPixels*: ptr byte + dWidth*: int + dHeight*: int + dSkip*: int + auxData*: pointer src*: PPixelFormat table*: ptr byte dst*: PPixelFormat @@ -1194,30 +1194,30 @@ type flags*: int32 # Read-only format*: PPixelFormat # Read-only w*, h*: cint # Read-only - pitch*: UInt16 # Read-only - pixels*: Pointer # Read-write + pitch*: uint16 # Read-only + pixels*: pointer # Read-write offset*: cint # Private - hwdata*: Pointer #TPrivate_hwdata; Hardware-specific surface info + hwdata*: pointer #TPrivate_hwdata; Hardware-specific surface info # clipping information: - clip_rect*: TRect # Read-only + clipRect*: TRect # Read-only unused1*: int32 # for binary compatibility # Allow recursive locks locked*: int32 # Private # info for fast blit mapping to other surfaces - Blitmap*: Pointer # PSDL_BlitMap; // Private + blitmap*: pointer # PSDL_BlitMap; // Private # format version, bumped at every change to invalidate blit maps - format_version*: cint # Private + formatVersion*: cint # Private refcount*: cint PVideoInfo* = ptr TVideoInfo TVideoInfo*{.final.} = object # The YUV hardware video overlay - hw_available*: byte - blit_hw*: byte - UnusedBits3*: byte # Unused at this point - video_mem*: int32 # The total amount of video memory (in K) + hwAvailable*: byte + blitHw*: byte + unusedBits3*: byte # Unused at this point + videoMem*: int32 # The total amount of video memory (in K) vfmt*: PPixelFormat # Value: The format of the video surface - current_w*: int32 # Value: The current video mode width - current_h*: int32 # Value: The current video mode height + currentW*: int32 # Value: The current video mode width + currentH*: int32 # Value: The current video mode height POverlay* = ptr TOverlay TOverlay*{.final.} = object # Public enumeration for setting the OpenGL window attributes. @@ -1226,7 +1226,7 @@ type planes*: int # Number of planes in the overlay. Usually either 1 or 3 pitches*: PUInt16 # An array of pitches, one for each plane. Pitch is the length of a row in bytes. pixels*: ptr ptr byte # An array of pointers to the data of each plane. The overlay should be locked before these pointers are used. - hw_overlay*: int32 # This will be set to 1 if the overlay is hardware accelerated. + hwOverlay*: int32 # This will be set to 1 if the overlay is hardware accelerated. TGLAttr* = enum GL_RED_SIZE, GL_GREEN_SIZE, GL_BLUE_SIZE, GL_ALPHA_SIZE, GL_BUFFER_SIZE, @@ -1237,11 +1237,11 @@ type PCursor* = ptr TCursor TCursor*{.final.} = object # SDL_mutex.h types area*: TRect # The area of the mouse cursor - hot_x*, hot_y*: int16 # The "tip" of the cursor + hotX*, hot_y*: int16 # The "tip" of the cursor data*: ptr byte # B/W cursor data mask*: ptr byte # B/W cursor mask save*: array[1..2, ptr byte] # Place to save cursor area - wm_cursor*: Pointer # Window-manager cursor + wmCursor*: pointer # Window-manager cursor type @@ -1250,7 +1250,7 @@ type Psemaphore* = ptr Tsemaphore Tsemaphore*{.final.} = object PSem* = ptr TSem - TSem* = TSemaphore + TSem* = Tsemaphore PCond* = ptr TCond TCond*{.final.} = object # SDL_thread.h types @@ -1267,8 +1267,8 @@ type # This is the system-independent thread info struc threadid*: int32 handle*: TSYS_ThreadHandle status*: int - errbuf*: TError - data*: Pointer + errbuf*: Terror + data*: pointer PKeyStateArr* = ptr TKeyStateArr TKeyStateArr* = array[0..65000, byte] # Types required so we don't need to use Windows.pas @@ -1282,24 +1282,24 @@ type # This is the system-independent thread info struc TWordArray* = array[0..16383, int16] # Generic procedure pointer type TEventSeq = set[TEventKind] -template evconv(procName: expr, ptrName: typeDesc, assertions: TEventSeq): stmt {.immediate.} = +template evconv(procName: expr, ptrName: typedesc, assertions: TEventSeq): stmt {.immediate.} = proc `procName`*(event: PEvent): ptrName = assert(contains(assertions, event.kind)) result = cast[ptrName](event) -evconv(EvActive, PActiveEvent, {ACTIVEEVENT}) -evconv(EvKeyboard, PKeyboardEvent, {KEYDOWN, KEYUP}) -evconv(EvMouseMotion, PMouseMotionEvent, {MOUSEMOTION}) -evconv(EvMouseButton, PMouseButtonEvent, {MOUSEBUTTONDOWN, MOUSEBUTTONUP}) -evconv(EvJoyAxis, PJoyAxisEvent,{JOYAXISMOTION}) -evconv(EvJoyBall, PJoyBallEvent, {JOYBALLMOTION}) -evconv(EvJoyHat, PJoyHatEvent, {JOYHATMOTION}) -evconv(EvJoyButton, PJoyButtonEvent, {JOYBUTTONDOWN, JOYBUTTONUP}) -evconv(EvResize, PResizeEvent, {VIDEORESIZE}) -evconv(EvExpose, PExposeEvent, {VIDEOEXPOSE}) -evconv(EvQuit, PQuitEvent, {QUITEV}) -evconv(EvUser, PUserEvent, {USEREVENT}) -evconv(EvSysWM, PSysWMEvent, {SYSWMEVENT}) +evconv(evActive, PActiveEvent, {ACTIVEEVENT}) +evconv(evKeyboard, PKeyboardEvent, {KEYDOWN, KEYUP}) +evconv(evMouseMotion, PMouseMotionEvent, {MOUSEMOTION}) +evconv(evMouseButton, PMouseButtonEvent, {MOUSEBUTTONDOWN, MOUSEBUTTONUP}) +evconv(evJoyAxis, PJoyAxisEvent,{JOYAXISMOTION}) +evconv(evJoyBall, PJoyBallEvent, {JOYBALLMOTION}) +evconv(evJoyHat, PJoyHatEvent, {JOYHATMOTION}) +evconv(evJoyButton, PJoyButtonEvent, {JOYBUTTONDOWN, JOYBUTTONUP}) +evconv(evResize, PResizeEvent, {VIDEORESIZE}) +evconv(evExpose, PExposeEvent, {VIDEOEXPOSE}) +evconv(evQuit, PQuitEvent, {QUITEV}) +evconv(evUser, PUserEvent, {USEREVENT}) +evconv(evSysWM, PSysWMEvent, {SYSWMEVENT}) #------------------------------------------------------------------------------ # initialization @@ -1309,73 +1309,72 @@ evconv(EvSysWM, PSysWMEvent, {SYSWMEVENT}) # Unless the SDL_INIT_NOPARACHUTE flag is set, it will install cleanup # signal handlers for some commonly ignored fatal signals (like SIGSEGV) -proc Init*(flags: int32): int{.cdecl, importc: "SDL_Init", dynlib: LibName.} +proc init*(flags: int32): int{.cdecl, importc: "SDL_Init", dynlib: LibName.} # This function initializes specific SDL subsystems -proc InitSubSystem*(flags: int32): int{.cdecl, importc: "SDL_InitSubSystem", +proc initSubSystem*(flags: int32): int{.cdecl, importc: "SDL_InitSubSystem", dynlib: LibName.} # This function cleans up specific SDL subsystems -proc QuitSubSystem*(flags: int32){.cdecl, importc: "SDL_QuitSubSystem", +proc quitSubSystem*(flags: int32){.cdecl, importc: "SDL_QuitSubSystem", dynlib: LibName.} # This function returns mask of the specified subsystems which have # been initialized. # If 'flags' is 0, it returns a mask of all initialized subsystems. -proc WasInit*(flags: int32): int32{.cdecl, importc: "SDL_WasInit", +proc wasInit*(flags: int32): int32{.cdecl, importc: "SDL_WasInit", dynlib: LibName.} # This function cleans up all initialized subsystems and unloads the # dynamically linked library. You should call it upon all exit conditions. -proc Quit*(){.cdecl, importc: "SDL_Quit", dynlib: LibName.} +proc quit*(){.cdecl, importc: "SDL_Quit", dynlib: LibName.} when defined(WINDOWS): # This should be called from your WinMain() function, if any - proc RegisterApp*(name: cstring, style: int32, h_Inst: Pointer): int{.cdecl, + proc registerApp*(name: cstring, style: int32, hInst: pointer): int{.cdecl, importc: "SDL_RegisterApp", dynlib: LibName.} -proc TableSize*(table: cstring): int +proc tableSize*(table: cstring): int #------------------------------------------------------------------------------ # error-handling #------------------------------------------------------------------------------ # Public functions -proc GetError*(): cstring{.cdecl, importc: "SDL_GetError", dynlib: LibName.} -proc SetError*(fmt: cstring){.cdecl, importc: "SDL_SetError", dynlib: LibName.} -proc ClearError*(){.cdecl, importc: "SDL_ClearError", dynlib: LibName.} -when not (defined(WINDOWS)): - proc Error*(Code: Terrorcode){.cdecl, importc: "SDL_Error", dynlib: LibName.} -proc OutOfMemory*() +proc getError*(): cstring{.cdecl, importc: "SDL_GetError", dynlib: LibName.} +proc setError*(fmt: cstring){.cdecl, importc: "SDL_SetError", dynlib: LibName.} +proc clearError*(){.cdecl, importc: "SDL_ClearError", dynlib: LibName.} +when not (defined(WINDOWS)): + proc error*(Code: Terrorcode){.cdecl, importc: "SDL_Error", dynlib: LibName.} #------------------------------------------------------------------------------ # io handling #------------------------------------------------------------------------------ # Functions to create SDL_RWops structures from various data sources -proc RWFromFile*(filename, mode: cstring): PRWops{.cdecl, +proc rwFromFile*(filename, mode: cstring): PRWops{.cdecl, importc: "SDL_RWFromFile", dynlib: LibName.} -proc FreeRW*(area: PRWops){.cdecl, importc: "SDL_FreeRW", dynlib: LibName.} +proc freeRW*(area: PRWops){.cdecl, importc: "SDL_FreeRW", dynlib: LibName.} #fp is FILE *fp ??? -proc RWFromFP*(fp: Pointer, autoclose: int): PRWops{.cdecl, +proc rwFromFP*(fp: pointer, autoclose: int): PRWops{.cdecl, importc: "SDL_RWFromFP", dynlib: LibName.} -proc RWFromMem*(mem: Pointer, size: int): PRWops{.cdecl, +proc rwFromMem*(mem: pointer, size: int): PRWops{.cdecl, importc: "SDL_RWFromMem", dynlib: LibName.} -proc RWFromConstMem*(mem: Pointer, size: int): PRWops{.cdecl, +proc rwFromConstMem*(mem: pointer, size: int): PRWops{.cdecl, importc: "SDL_RWFromConstMem", dynlib: LibName.} -proc AllocRW*(): PRWops{.cdecl, importc: "SDL_AllocRW", dynlib: LibName.} -proc RWSeek*(context: PRWops, offset: int, whence: int): int -proc RWTell*(context: PRWops): int -proc RWRead*(context: PRWops, theptr: Pointer, size: int, n: int): int -proc RWWrite*(context: PRWops, theptr: Pointer, size: int, n: int): int -proc RWClose*(context: PRWops): int +proc allocRW*(): PRWops{.cdecl, importc: "SDL_AllocRW", dynlib: LibName.} +proc rwSeek*(context: PRWops, offset: int, whence: int): int +proc rwTell*(context: PRWops): int +proc rwRead*(context: PRWops, theptr: pointer, size: int, n: int): int +proc rwWrite*(context: PRWops, theptr: pointer, size: int, n: int): int +proc rwClose*(context: PRWops): int #------------------------------------------------------------------------------ # time-handling #------------------------------------------------------------------------------ # Get the number of milliseconds since the SDL library initialization. # Note that this value wraps if the program runs for more than ~49 days. -proc GetTicks*(): int32{.cdecl, importc: "SDL_GetTicks", dynlib: LibName.} +proc getTicks*(): int32{.cdecl, importc: "SDL_GetTicks", dynlib: LibName.} # Wait a specified number of milliseconds before returning -proc Delay*(msec: int32){.cdecl, importc: "SDL_Delay", dynlib: LibName.} +proc delay*(msec: int32){.cdecl, importc: "SDL_Delay", dynlib: LibName.} # Add a new timer to the pool of timers already running. # Returns a timer ID, or NULL when an error occurs. -proc AddTimer*(interval: int32, callback: TNewTimerCallback, param: Pointer): PTimerID{. +proc addTimer*(interval: int32, callback: TNewTimerCallback, param: pointer): PTimerID{. cdecl, importc: "SDL_AddTimer", dynlib: LibName.} # Remove one of the multiple timers knowing its ID. # Returns a boolean value indicating success. -proc RemoveTimer*(t: PTimerID): TBool{.cdecl, importc: "SDL_RemoveTimer", +proc removeTimer*(t: PTimerID): TBool{.cdecl, importc: "SDL_RemoveTimer", dynlib: LibName.} -proc SetTimer*(interval: int32, callback: TTimerCallback): int{.cdecl, +proc setTimer*(interval: int32, callback: TTimerCallback): int{.cdecl, importc: "SDL_SetTimer", dynlib: LibName.} #------------------------------------------------------------------------------ # audio-routines @@ -1383,13 +1382,13 @@ proc SetTimer*(interval: int32, callback: TTimerCallback): int{.cdecl, # These functions are used internally, and should not be used unless you # have a specific need to specify the audio driver you want to use. # You should normally use SDL_Init() or SDL_InitSubSystem(). -proc AudioInit*(driver_name: cstring): int{.cdecl, importc: "SDL_AudioInit", +proc audioInit*(driverName: cstring): int{.cdecl, importc: "SDL_AudioInit", dynlib: LibName.} -proc AudioQuit*(){.cdecl, importc: "SDL_AudioQuit", dynlib: LibName.} +proc audioQuit*(){.cdecl, importc: "SDL_AudioQuit", dynlib: LibName.} # This function fills the given character buffer with the name of the - # current audio driver, and returns a Pointer to it if the audio driver has + # current audio driver, and returns a pointer to it if the audio driver has # been initialized. It returns NULL if no driver has been initialized. -proc AudioDriverName*(namebuf: cstring, maxlen: int): cstring{.cdecl, +proc audioDriverName*(namebuf: cstring, maxlen: int): cstring{.cdecl, importc: "SDL_AudioDriverName", dynlib: LibName.} # This function opens the audio device with the desired parameters, and # returns 0 if successful, placing the actual hardware parameters in the @@ -1430,17 +1429,17 @@ proc AudioDriverName*(namebuf: cstring, maxlen: int): cstring{.cdecl, # for your audio callback function to be called. Since the audio driver # may modify the requested size of the audio buffer, you should allocate # any local mixing buffers after you open the audio device. -proc OpenAudio*(desired, obtained: PAudioSpec): int{.cdecl, +proc openAudio*(desired, obtained: PAudioSpec): int{.cdecl, importc: "SDL_OpenAudio", dynlib: LibName.} # Get the current audio state: -proc GetAudioStatus*(): TAudiostatus{.cdecl, importc: "SDL_GetAudioStatus", +proc getAudioStatus*(): TAudiostatus{.cdecl, importc: "SDL_GetAudioStatus", dynlib: LibName.} # This function pauses and unpauses the audio callback processing. # It should be called with a parameter of 0 after opening the audio # device to start playing sound. This is so you can safely initialize # data for your callback function after opening the audio device. # Silence will be written to the audio device during the pause. -proc PauseAudio*(pause_on: int){.cdecl, importc: "SDL_PauseAudio", +proc pauseAudio*(pauseOn: int){.cdecl, importc: "SDL_PauseAudio", dynlib: LibName.} # This function loads a WAVE from the data source, automatically freeing # that source if 'freesrc' is non-zero. For example, to load a WAVE file, @@ -1457,22 +1456,22 @@ proc PauseAudio*(pause_on: int){.cdecl, importc: "SDL_PauseAudio", # This function returns NULL and sets the SDL error message if the # wave file cannot be opened, uses an unknown data format, or is # corrupt. Currently raw and MS-ADPCM WAVE files are supported. -proc LoadWAV_RW*(src: PRWops, freesrc: int, spec: PAudioSpec, audio_buf: ptr byte, +proc loadWAV_RW*(src: PRWops, freesrc: int, spec: PAudioSpec, audioBuf: ptr byte, audiolen: PUInt32): PAudioSpec{.cdecl, importc: "SDL_LoadWAV_RW", dynlib: LibName.} # Compatibility convenience function -- loads a WAV from a file -proc LoadWAV*(filename: cstring, spec: PAudioSpec, audio_buf: ptr byte, +proc loadWAV*(filename: cstring, spec: PAudioSpec, audioBuf: ptr byte, audiolen: PUInt32): PAudioSpec # This function frees data previously allocated with SDL_LoadWAV_RW() -proc FreeWAV*(audio_buf: ptr byte){.cdecl, importc: "SDL_FreeWAV", dynlib: LibName.} +proc freeWAV*(audioBuf: ptr byte){.cdecl, importc: "SDL_FreeWAV", dynlib: LibName.} # This function takes a source format and rate and a destination format # and rate, and initializes the 'cvt' structure with information needed # by SDL_ConvertAudio() to convert a buffer of audio data from one format # to the other. # This function returns 0, or -1 if there was an error. -proc BuildAudioCVT*(cvt: PAudioCVT, src_format: UInt16, src_channels: byte, - src_rate: int, dst_format: UInt16, dst_channels: byte, - dst_rate: int): int{.cdecl, importc: "SDL_BuildAudioCVT", +proc buildAudioCVT*(cvt: PAudioCVT, srcFormat: uint16, srcChannels: byte, + srcRate: int, dstFormat: uint16, dstChannels: byte, + dstRate: int): int{.cdecl, importc: "SDL_BuildAudioCVT", dynlib: LibName.} # Once you have initialized the 'cvt' structure using SDL_BuildAudioCVT(), # created an audio buffer cvt->buf, and filled it with cvt->len bytes of @@ -1481,45 +1480,45 @@ proc BuildAudioCVT*(cvt: PAudioCVT, src_format: UInt16, src_channels: byte, # The data conversion may expand the size of the audio data, so the buffer # cvt->buf should be allocated after the cvt structure is initialized by # SDL_BuildAudioCVT(), and should be cvt->len*cvt->len_mult bytes long. -proc ConvertAudio*(cvt: PAudioCVT): int{.cdecl, importc: "SDL_ConvertAudio", +proc convertAudio*(cvt: PAudioCVT): int{.cdecl, importc: "SDL_ConvertAudio", dynlib: LibName.} # This takes two audio buffers of the playing audio format and mixes # them, performing addition, volume adjustment, and overflow clipping. # The volume ranges from 0 - 128, and should be set to SDL_MIX_MAXVOLUME # for full audio volume. Note this does not change hardware volume. # This is provided for convenience -- you can mix your own audio data. -proc MixAudio*(dst, src: ptr byte, length: int32, volume: int){.cdecl, +proc mixAudio*(dst, src: ptr byte, length: int32, volume: int){.cdecl, importc: "SDL_MixAudio", dynlib: LibName.} # The lock manipulated by these functions protects the callback function. # During a LockAudio/UnlockAudio pair, you can be guaranteed that the # callback function is not running. Do not call these from the callback # function or you will cause deadlock. -proc LockAudio*(){.cdecl, importc: "SDL_LockAudio", dynlib: LibName.} -proc UnlockAudio*(){.cdecl, importc: "SDL_UnlockAudio", dynlib: LibName.} +proc lockAudio*(){.cdecl, importc: "SDL_LockAudio", dynlib: LibName.} +proc unlockAudio*(){.cdecl, importc: "SDL_UnlockAudio", dynlib: LibName.} # This function shuts down audio processing and closes the audio device. -proc CloseAudio*(){.cdecl, importc: "SDL_CloseAudio", dynlib: LibName.} +proc closeAudio*(){.cdecl, importc: "SDL_CloseAudio", dynlib: LibName.} #------------------------------------------------------------------------------ # CD-routines #------------------------------------------------------------------------------ # Returns the number of CD-ROM drives on the system, or -1 if # SDL_Init() has not been called with the SDL_INIT_CDROM flag. -proc CDNumDrives*(): int{.cdecl, importc: "SDL_CDNumDrives", dynlib: LibName.} +proc cdNumDrives*(): int{.cdecl, importc: "SDL_CDNumDrives", dynlib: LibName.} # Returns a human-readable, system-dependent identifier for the CD-ROM. # Example: # "/dev/cdrom" # "E:" # "/dev/disk/ide/1/master" -proc CDName*(drive: int): cstring{.cdecl, importc: "SDL_CDName", dynlib: LibName.} +proc cdName*(drive: int): cstring{.cdecl, importc: "SDL_CDName", dynlib: LibName.} # Opens a CD-ROM drive for access. It returns a drive handle on success, # or NULL if the drive was invalid or busy. This newly opened CD-ROM # becomes the default CD used when other CD functions are passed a NULL # CD-ROM handle. # Drives are numbered starting with 0. Drive 0 is the system default CD-ROM. -proc CDOpen*(drive: int): PCD{.cdecl, importc: "SDL_CDOpen", dynlib: LibName.} +proc cdOpen*(drive: int): PCD{.cdecl, importc: "SDL_CDOpen", dynlib: LibName.} # This function returns the current status of the given drive. # If the drive has a CD in it, the table of contents of the CD and current # play position of the CD will be stored in the SDL_CD structure. -proc CDStatus*(cdrom: PCD): TCDStatus{.cdecl, importc: "SDL_CDStatus", +proc cdStatus*(cdrom: PCD): TCDStatus{.cdecl, importc: "SDL_CDStatus", dynlib: LibName.} # Play the given CD starting at 'start_track' and 'start_frame' for 'ntracks' # tracks and 'nframes' frames. If both 'ntrack' and 'nframe' are 0, play @@ -1542,95 +1541,89 @@ proc CDStatus*(cdrom: PCD): TCDStatus{.cdecl, importc: "SDL_CDStatus", # SDL_CDPlayTracks(cdrom, 0, 0, 2, 10); # # This function returns 0, or -1 if there was an error. -proc CDPlayTracks*(cdrom: PCD, start_track: int, start_frame: int, ntracks: int, +proc cdPlayTracks*(cdrom: PCD, startTrack: int, startFrame: int, ntracks: int, nframes: int): int{.cdecl, importc: "SDL_CDPlayTracks", dynlib: LibName.} # Play the given CD starting at 'start' frame for 'length' frames. # It returns 0, or -1 if there was an error. -proc CDPlay*(cdrom: PCD, start: int, len: int): int{.cdecl, +proc cdPlay*(cdrom: PCD, start: int, len: int): int{.cdecl, importc: "SDL_CDPlay", dynlib: LibName.} # Pause play -- returns 0, or -1 on error -proc CDPause*(cdrom: PCD): int{.cdecl, importc: "SDL_CDPause", dynlib: LibName.} +proc cdPause*(cdrom: PCD): int{.cdecl, importc: "SDL_CDPause", dynlib: LibName.} # Resume play -- returns 0, or -1 on error -proc CDResume*(cdrom: PCD): int{.cdecl, importc: "SDL_CDResume", dynlib: LibName.} +proc cdResume*(cdrom: PCD): int{.cdecl, importc: "SDL_CDResume", dynlib: LibName.} # Stop play -- returns 0, or -1 on error -proc CDStop*(cdrom: PCD): int{.cdecl, importc: "SDL_CDStop", dynlib: LibName.} +proc cdStop*(cdrom: PCD): int{.cdecl, importc: "SDL_CDStop", dynlib: LibName.} # Eject CD-ROM -- returns 0, or -1 on error -proc CDEject*(cdrom: PCD): int{.cdecl, importc: "SDL_CDEject", dynlib: LibName.} +proc cdEject*(cdrom: PCD): int{.cdecl, importc: "SDL_CDEject", dynlib: LibName.} # Closes the handle for the CD-ROM drive -proc CDClose*(cdrom: PCD){.cdecl, importc: "SDL_CDClose", dynlib: LibName.} +proc cdClose*(cdrom: PCD){.cdecl, importc: "SDL_CDClose", dynlib: LibName.} # Given a status, returns true if there's a disk in the drive -proc CDInDrive*(status: TCDStatus): bool - # Conversion functions from frames to Minute/Second/Frames and vice versa -proc FRAMES_TO_MSF*(frames: int, M: var int, S: var int, F: var int) -proc MSF_TO_FRAMES*(M: int, S: int, F: int): int - #------------------------------------------------------------------------------ - # JoyStick-routines - #------------------------------------------------------------------------------ - # Count the number of joysticks attached to the system -proc NumJoysticks*(): int{.cdecl, importc: "SDL_NumJoysticks", dynlib: LibName.} +proc cdInDrive*(status: TCDStatus): bool + +proc numJoysticks*(): int{.cdecl, importc: "SDL_NumJoysticks", dynlib: LibName.} # Get the implementation dependent name of a joystick. # This can be called before any joysticks are opened. # If no name can be found, this function returns NULL. -proc JoystickName*(index: int): cstring{.cdecl, importc: "SDL_JoystickName", +proc joystickName*(index: int): cstring{.cdecl, importc: "SDL_JoystickName", dynlib: LibName.} # Open a joystick for use - the index passed as an argument refers to # the N'th joystick on the system. This index is the value which will # identify this joystick in future joystick events. # # This function returns a joystick identifier, or NULL if an error occurred. -proc JoystickOpen*(index: int): PJoystick{.cdecl, importc: "SDL_JoystickOpen", +proc joystickOpen*(index: int): PJoystick{.cdecl, importc: "SDL_JoystickOpen", dynlib: LibName.} # Returns 1 if the joystick has been opened, or 0 if it has not. -proc JoystickOpened*(index: int): int{.cdecl, importc: "SDL_JoystickOpened", +proc joystickOpened*(index: int): int{.cdecl, importc: "SDL_JoystickOpened", dynlib: LibName.} # Get the device index of an opened joystick. -proc JoystickIndex*(joystick: PJoystick): int{.cdecl, +proc joystickIndex*(joystick: PJoystick): int{.cdecl, importc: "SDL_JoystickIndex", dynlib: LibName.} # Get the number of general axis controls on a joystick -proc JoystickNumAxes*(joystick: PJoystick): int{.cdecl, +proc joystickNumAxes*(joystick: PJoystick): int{.cdecl, importc: "SDL_JoystickNumAxes", dynlib: LibName.} # Get the number of trackballs on a joystick # Joystick trackballs have only relative motion events associated # with them and their state cannot be polled. -proc JoystickNumBalls*(joystick: PJoystick): int{.cdecl, +proc joystickNumBalls*(joystick: PJoystick): int{.cdecl, importc: "SDL_JoystickNumBalls", dynlib: LibName.} # Get the number of POV hats on a joystick -proc JoystickNumHats*(joystick: PJoystick): int{.cdecl, +proc joystickNumHats*(joystick: PJoystick): int{.cdecl, importc: "SDL_JoystickNumHats", dynlib: LibName.} # Get the number of buttons on a joystick -proc JoystickNumButtons*(joystick: PJoystick): int{.cdecl, +proc joystickNumButtons*(joystick: PJoystick): int{.cdecl, importc: "SDL_JoystickNumButtons", dynlib: LibName.} # Update the current state of the open joysticks. # This is called automatically by the event loop if any joystick # events are enabled. -proc JoystickUpdate*(){.cdecl, importc: "SDL_JoystickUpdate", dynlib: LibName.} +proc joystickUpdate*(){.cdecl, importc: "SDL_JoystickUpdate", dynlib: LibName.} # Enable/disable joystick event polling. # If joystick events are disabled, you must call SDL_JoystickUpdate() # yourself and check the state of the joystick when you want joystick # information. # The state can be one of SDL_QUERY, SDL_ENABLE or SDL_IGNORE. -proc JoystickEventState*(state: int): int{.cdecl, +proc joystickEventState*(state: int): int{.cdecl, importc: "SDL_JoystickEventState", dynlib: LibName.} # Get the current state of an axis control on a joystick # The state is a value ranging from -32768 to 32767. # The axis indices start at index 0. -proc JoystickGetAxis*(joystick: PJoystick, axis: int): int16{.cdecl, +proc joystickGetAxis*(joystick: PJoystick, axis: int): int16{.cdecl, importc: "SDL_JoystickGetAxis", dynlib: LibName.} # The hat indices start at index 0. -proc JoystickGetHat*(joystick: PJoystick, hat: int): byte{.cdecl, +proc joystickGetHat*(joystick: PJoystick, hat: int): byte{.cdecl, importc: "SDL_JoystickGetHat", dynlib: LibName.} # Get the ball axis change since the last poll # This returns 0, or -1 if you passed it invalid parameters. # The ball indices start at index 0. -proc JoystickGetBall*(joystick: PJoystick, ball: int, dx: var int, dy: var int): int{. +proc joystickGetBall*(joystick: PJoystick, ball: int, dx: var int, dy: var int): int{. cdecl, importc: "SDL_JoystickGetBall", dynlib: LibName.} # Get the current state of a button on a joystick # The button indices start at index 0. -proc JoystickGetButton*(joystick: PJoystick, Button: int): byte{.cdecl, +proc joystickGetButton*(joystick: PJoystick, button: int): byte{.cdecl, importc: "SDL_JoystickGetButton", dynlib: LibName.} # Close a joystick previously opened with SDL_JoystickOpen() -proc JoystickClose*(joystick: PJoystick){.cdecl, importc: "SDL_JoystickClose", +proc joystickClose*(joystick: PJoystick){.cdecl, importc: "SDL_JoystickClose", dynlib: LibName.} #------------------------------------------------------------------------------ # event-handling @@ -1638,7 +1631,7 @@ proc JoystickClose*(joystick: PJoystick){.cdecl, importc: "SDL_JoystickClose", # Pumps the event loop, gathering events from the input devices. # This function updates the event queue and internal input device state. # This should only be run in the thread that sets the video mode. -proc PumpEvents*(){.cdecl, importc: "SDL_PumpEvents", dynlib: LibName.} +proc pumpEvents*(){.cdecl, importc: "SDL_PumpEvents", dynlib: LibName.} # Checks the event queue for messages and optionally returns them. # If 'action' is SDL_ADDEVENT, up to 'numevents' events will be added to # the back of the event queue. @@ -1650,20 +1643,20 @@ proc PumpEvents*(){.cdecl, importc: "SDL_PumpEvents", dynlib: LibName.} # removed from the queue. # This function returns the number of events actually stored, or -1 # if there was an error. This function is thread-safe. -proc PeepEvents*(events: PEvent, numevents: int, action: Teventaction, +proc peepEvents*(events: PEvent, numevents: int, action: TEventAction, mask: int32): int{.cdecl, importc: "SDL_PeepEvents", dynlib: LibName.} # Polls for currently pending events, and returns 1 if there are any pending # events, or 0 if there are none available. If 'event' is not NULL, the next # event is removed from the queue and stored in that area. -proc PollEvent*(event: PEvent): int{.cdecl, importc: "SDL_PollEvent", +proc pollEvent*(event: PEvent): int{.cdecl, importc: "SDL_PollEvent", dynlib: LibName.} # Waits indefinitely for the next available event, returning 1, or 0 if there # was an error while waiting for events. If 'event' is not NULL, the next # event is removed from the queue and stored in that area. -proc WaitEvent*(event: PEvent): int{.cdecl, importc: "SDL_WaitEvent", +proc waitEvent*(event: PEvent): int{.cdecl, importc: "SDL_WaitEvent", dynlib: LibName.} -proc PushEvent*(event: PEvent): int{.cdecl, importc: "SDL_PushEvent", +proc pushEvent*(event: PEvent): int{.cdecl, importc: "SDL_PushEvent", dynlib: LibName.} # If the filter returns 1, then the event will be added to the internal queue. # If it returns 0, then the event will be dropped from the queue, but the @@ -1679,11 +1672,11 @@ proc PushEvent*(event: PEvent): int{.cdecl, importc: "SDL_PushEvent", # be closed, otherwise the window will remain open if possible. # If the quit event is generated by an interrupt signal, it will bypass the # internal queue and be delivered to the application at the next event poll. -proc SetEventFilter*(filter: TEventFilter){.cdecl, +proc setEventFilter*(filter: TEventFilter){.cdecl, importc: "SDL_SetEventFilter", dynlib: LibName.} # Return the current event filter - can be used to "chain" filters. # If there is no event filter set, this function returns NULL. -proc GetEventFilter*(): TEventFilter{.cdecl, importc: "SDL_GetEventFilter", +proc getEventFilter*(): TEventFilter{.cdecl, importc: "SDL_GetEventFilter", dynlib: LibName.} # This function allows you to set the state of processing certain events. # If 'state' is set to SDL_IGNORE, that event will be automatically dropped @@ -1691,26 +1684,26 @@ proc GetEventFilter*(): TEventFilter{.cdecl, importc: "SDL_GetEventFilter", # If 'state' is set to SDL_ENABLE, that event will be processed normally. # If 'state' is set to SDL_QUERY, SDL_EventState() will return the # current processing state of the specified event. -proc EventState*(theType: byte, state: int): byte{.cdecl, +proc eventState*(theType: byte, state: int): byte{.cdecl, importc: "SDL_EventState", dynlib: LibName.} #------------------------------------------------------------------------------ # Version Routines #------------------------------------------------------------------------------ # This macro can be used to fill a version structure with the compile-time # version of the SDL library. -proc VERSION*(X: var TVersion) +proc version*(x: var Tversion) # This macro turns the version numbers into a numeric value: # (1,2,3) -> (1203) # This assumes that there will never be more than 100 patchlevels -proc VERSIONNUM*(X, Y, Z: int): int +proc versionnum*(x, y, z: int): int # This is the version number macro for the current SDL version -proc COMPILEDVERSION*(): int +proc compiledversion*(): int # This macro will evaluate to true if compiled with SDL at least X.Y.Z -proc VERSION_ATLEAST*(X: int, Y: int, Z: int): bool +proc versionAtleast*(x, y, z: int): bool # This function gets the version of the dynamically linked SDL library. # it should NOT be used to fill a version structure, instead you should # use the SDL_Version() macro. -proc Linked_Version*(): Pversion{.cdecl, importc: "SDL_Linked_Version", +proc linkedVersion*(): PVersion{.cdecl, importc: "SDL_Linked_Version", dynlib: LibName.} #------------------------------------------------------------------------------ # video @@ -1727,25 +1720,25 @@ proc Linked_Version*(): Pversion{.cdecl, importc: "SDL_Linked_Version", # If you use both sound and video in your application, you need to call # SDL_Init() before opening the sound device, otherwise under Win32 DirectX, # you won't be able to set full-screen display modes. -proc VideoInit*(driver_name: cstring, flags: int32): int{.cdecl, +proc videoInit*(driverName: cstring, flags: int32): int{.cdecl, importc: "SDL_VideoInit", dynlib: LibName.} -proc VideoQuit*(){.cdecl, importc: "SDL_VideoQuit", dynlib: LibName.} +proc videoQuit*(){.cdecl, importc: "SDL_VideoQuit", dynlib: LibName.} # This function fills the given character buffer with the name of the # video driver, and returns a pointer to it if the video driver has # been initialized. It returns NULL if no driver has been initialized. -proc VideoDriverName*(namebuf: cstring, maxlen: int): cstring{.cdecl, +proc videoDriverName*(namebuf: cstring, maxlen: int): cstring{.cdecl, importc: "SDL_VideoDriverName", dynlib: LibName.} # This function returns a pointer to the current display surface. # If SDL is doing format conversion on the display surface, this # function returns the publicly visible surface, not the real video # surface. -proc GetVideoSurface*(): PSurface{.cdecl, importc: "SDL_GetVideoSurface", +proc getVideoSurface*(): PSurface{.cdecl, importc: "SDL_GetVideoSurface", dynlib: LibName.} # This function returns a read-only pointer to information about the # video hardware. If this is called before SDL_SetVideoMode(), the 'vfmt' # member of the returned structure will contain the pixel format of the # "best" video mode. -proc GetVideoInfo*(): PVideoInfo{.cdecl, importc: "SDL_GetVideoInfo", +proc getVideoInfo*(): PVideoInfo{.cdecl, importc: "SDL_GetVideoInfo", dynlib: LibName.} # Check to see if a particular video mode is supported. # It returns 0 if the requested mode is not supported under any bit depth, @@ -1756,7 +1749,7 @@ proc GetVideoInfo*(): PVideoInfo{.cdecl, importc: "SDL_GetVideoInfo", # # The arguments to SDL_VideoModeOK() are the same ones you would pass to # SDL_SetVideoMode() -proc VideoModeOK*(width, height, bpp: int, flags: int32): int{.cdecl, +proc videoModeOK*(width, height, bpp: int, flags: int32): int{.cdecl, importc: "SDL_VideoModeOK", importc: "SDL_VideoModeOK", dynlib: LibName.} # Return a pointer to an array of available screen dimensions for the # given format and video flags, sorted largest to smallest. Returns @@ -1765,7 +1758,7 @@ proc VideoModeOK*(width, height, bpp: int, flags: int32): int{.cdecl, # # if 'format' is NULL, the mode list will be for the format given # by SDL_GetVideoInfo( ) - > vfmt -proc ListModes*(format: PPixelFormat, flags: int32): PPSDL_Rect{.cdecl, +proc listModes*(format: PPixelFormat, flags: int32): PPSDL_Rect{.cdecl, importc: "SDL_ListModes", dynlib: LibName.} # Set up a video mode with the specified width, height and bits-per-pixel. # @@ -1808,15 +1801,15 @@ proc ListModes*(format: PPixelFormat, flags: int32): PPSDL_Rect{.cdecl, # applications that redraw the entire screen on every update. # # This function returns the video framebuffer surface, or NULL if it fails. -proc SetVideoMode*(width, height, bpp: int, flags: uint32): PSurface{.cdecl, +proc setVideoMode*(width, height, bpp: int, flags: uint32): PSurface{.cdecl, importc: "SDL_SetVideoMode", dynlib: LibName.} # Makes sure the given list of rectangles is updated on the given screen. # If 'x', 'y', 'w' and 'h' are all 0, SDL_UpdateRect will update the entire # screen. # These functions should not be called while 'screen' is locked. -proc UpdateRects*(screen: PSurface, numrects: int, rects: PRect){.cdecl, +proc updateRects*(screen: PSurface, numrects: int, rects: PRect){.cdecl, importc: "SDL_UpdateRects", dynlib: LibName.} -proc UpdateRect*(screen: PSurface, x, y: int32, w, h: int32){.cdecl, +proc updateRect*(screen: PSurface, x, y: int32, w, h: int32){.cdecl, importc: "SDL_UpdateRect", dynlib: LibName.} # On hardware that supports double-buffering, this function sets up a flip # and returns. The hardware will wait for vertical retrace, and then swap @@ -1826,14 +1819,14 @@ proc UpdateRect*(screen: PSurface, x, y: int32, w, h: int32){.cdecl, # The SDL_DOUBLEBUF flag must have been passed to SDL_SetVideoMode() when # setting the video mode for this function to perform hardware flipping. # This function returns 0 if successful, or -1 if there was an error. -proc Flip*(screen: PSurface): int{.cdecl, importc: "SDL_Flip", dynlib: LibName.} +proc flip*(screen: PSurface): int{.cdecl, importc: "SDL_Flip", dynlib: LibName.} # Set the gamma correction for each of the color channels. # The gamma values range (approximately) between 0.1 and 10.0 # # If this function isn't supported directly by the hardware, it will # be emulated using gamma ramps, if available. If successful, this # function returns 0, otherwise it returns -1. -proc SetGamma*(redgamma: float32, greengamma: float32, bluegamma: float32): int{. +proc setGamma*(redgamma: float32, greengamma: float32, bluegamma: float32): int{. cdecl, importc: "SDL_SetGamma", dynlib: LibName.} # Set the gamma translation table for the red, green, and blue channels # of the video hardware. Each table is an array of 256 16-bit quantities, @@ -1845,7 +1838,7 @@ proc SetGamma*(redgamma: float32, greengamma: float32, bluegamma: float32): int{ # If the call succeeds, it will return 0. If the display driver or # hardware does not support gamma translation, or otherwise fails, # this function will return -1. -proc SetGammaRamp*(redtable: PUInt16, greentable: PUInt16, bluetable: PUInt16): int{. +proc setGammaRamp*(redtable: PUInt16, greentable: PUInt16, bluetable: PUInt16): int{. cdecl, importc: "SDL_SetGammaRamp", dynlib: LibName.} # Retrieve the current values of the gamma translation tables. # @@ -1854,7 +1847,7 @@ proc SetGammaRamp*(redtable: PUInt16, greentable: PUInt16, bluetable: PUInt16): # If the call succeeds, it will return 0. If the display driver or # hardware does not support gamma translation, or otherwise fails, # this function will return -1. -proc GetGammaRamp*(redtable: PUInt16, greentable: PUInt16, bluetable: PUInt16): int{. +proc getGammaRamp*(redtable: PUInt16, greentable: PUInt16, bluetable: PUInt16): int{. cdecl, importc: "SDL_GetGammaRamp", dynlib: LibName.} # Sets a portion of the colormap for the given 8-bit surface. If 'surface' # is not a palettized surface, this function does nothing, returning 0. @@ -1869,7 +1862,7 @@ proc GetGammaRamp*(redtable: PUInt16, greentable: PUInt16, bluetable: PUInt16): # will always return 1, and the palette is guaranteed to be set the way # you desire, even if the window colormap has to be warped or run under # emulation. -proc SetColors*(surface: PSurface, colors: PColor, firstcolor: int, ncolors: int): int{. +proc setColors*(surface: PSurface, colors: PColor, firstcolor: int, ncolors: int): int{. cdecl, importc: "SDL_SetColors", dynlib: LibName.} # Sets a portion of the colormap for a given 8-bit surface. # 'flags' is one or both of: @@ -1885,20 +1878,20 @@ proc SetColors*(surface: PSurface, colors: PColor, firstcolor: int, ncolors: int # # SDL_SetColors() is equivalent to calling this function with # flags = (SDL_LOGPAL or SDL_PHYSPAL). -proc SetPalette*(surface: PSurface, flags: int, colors: PColor, firstcolor: int, +proc setPalette*(surface: PSurface, flags: int, colors: PColor, firstcolor: int, ncolors: int): int{.cdecl, importc: "SDL_SetPalette", dynlib: LibName.} # Maps an RGB triple to an opaque pixel value for a given pixel format -proc MapRGB*(format: PPixelFormat, r: byte, g: byte, b: byte): int32{.cdecl, +proc mapRGB*(format: PPixelFormat, r: byte, g: byte, b: byte): int32{.cdecl, importc: "SDL_MapRGB", dynlib: LibName.} # Maps an RGBA quadruple to a pixel value for a given pixel format -proc MapRGBA*(format: PPixelFormat, r: byte, g: byte, b: byte, a: byte): int32{. +proc mapRGBA*(format: PPixelFormat, r: byte, g: byte, b: byte, a: byte): int32{. cdecl, importc: "SDL_MapRGBA", dynlib: LibName.} # Maps a pixel value into the RGB components for a given pixel format -proc GetRGB*(pixel: int32, fmt: PPixelFormat, r: ptr byte, g: ptr byte, b: ptr byte){. +proc getRGB*(pixel: int32, fmt: PPixelFormat, r: ptr byte, g: ptr byte, b: ptr byte){. cdecl, importc: "SDL_GetRGB", dynlib: LibName.} # Maps a pixel value into the RGBA components for a given pixel format -proc GetRGBA*(pixel: int32, fmt: PPixelFormat, r: ptr byte, g: ptr byte, b: ptr byte, +proc getRGBA*(pixel: int32, fmt: PPixelFormat, r: ptr byte, g: ptr byte, b: ptr byte, a: ptr byte){.cdecl, importc: "SDL_GetRGBA", dynlib: LibName.} # Allocate and free an RGB surface (must be called after SDL_SetVideoMode) # If the depth is 4 or 8 bits, an empty palette is allocated for the surface. @@ -1932,17 +1925,17 @@ proc GetRGBA*(pixel: int32, fmt: PPixelFormat, r: ptr byte, g: ptr byte, b: ptr # will be set in the flags member of the returned surface. If for some # reason the surface could not be placed in video memory, it will not have # the SDL_HWSURFACE flag set, and will be created in system memory instead. -proc AllocSurface*(flags: int32, width, height, depth: int, - RMask, GMask, BMask, AMask: int32): PSurface -proc CreateRGBSurface*(flags: int32, width, height, depth: int, - RMask, GMask, BMask, AMask: int32): PSurface{.cdecl, +proc allocSurface*(flags: int32, width, height, depth: int, + rMask, gMask, bMask, aMask: int32): PSurface +proc createRGBSurface*(flags: int32, width, height, depth: int, + rMask, gMask, bMask, aMask: int32): PSurface{.cdecl, importc: "SDL_CreateRGBSurface", dynlib: LibName.} -proc CreateRGBSurfaceFrom*(pixels: Pointer, width, height, depth, pitch: int, - RMask, GMask, BMask, AMask: int32): PSurface{.cdecl, +proc createRGBSurfaceFrom*(pixels: pointer, width, height, depth, pitch: int, + rMask, gMask, bMask, aMask: int32): PSurface{.cdecl, importc: "SDL_CreateRGBSurfaceFrom", dynlib: LibName.} -proc FreeSurface*(surface: PSurface){.cdecl, importc: "SDL_FreeSurface", +proc freeSurface*(surface: PSurface){.cdecl, importc: "SDL_FreeSurface", dynlib: LibName.} -proc MustLock*(Surface: PSurface): bool +proc mustLock*(surface: PSurface): bool # SDL_LockSurface() sets up a surface for directly accessing the pixels. # Between calls to SDL_LockSurface()/SDL_UnlockSurface(), you can write # to and read from 'surface->pixels', using the pixel format stored in @@ -1959,25 +1952,25 @@ proc MustLock*(Surface: PSurface): bool # pairs, as critical system locks may be held during this time. # # SDL_LockSurface() returns 0, or -1 if the surface couldn't be locked. -proc LockSurface*(surface: PSurface): int{.cdecl, importc: "SDL_LockSurface", +proc lockSurface*(surface: PSurface): int{.cdecl, importc: "SDL_LockSurface", dynlib: LibName.} -proc UnlockSurface*(surface: PSurface){.cdecl, importc: "SDL_UnlockSurface", +proc unlockSurface*(surface: PSurface){.cdecl, importc: "SDL_UnlockSurface", dynlib: LibName.} # Load a surface from a seekable SDL data source (memory or file.) # If 'freesrc' is non-zero, the source will be closed after being read. # Returns the new surface, or NULL if there was an error. # The new surface should be freed with SDL_FreeSurface(). -proc LoadBMP_RW*(src: PRWops, freesrc: int): PSurface{.cdecl, +proc loadBMP_RW*(src: PRWops, freesrc: int): PSurface{.cdecl, importc: "SDL_LoadBMP_RW", dynlib: LibName.} # Convenience macro -- load a surface from a file -proc LoadBMP*(filename: cstring): PSurface +proc loadBMP*(filename: cstring): PSurface # Save a surface to a seekable SDL data source (memory or file.) # If 'freedst' is non-zero, the source will be closed after being written. # Returns 0 if successful or -1 if there was an error. -proc SaveBMP_RW*(surface: PSurface, dst: PRWops, freedst: int): int{.cdecl, +proc saveBMP_RW*(surface: PSurface, dst: PRWops, freedst: int): int{.cdecl, importc: "SDL_SaveBMP_RW", dynlib: LibName.} # Convenience macro -- save a surface to a file -proc SaveBMP*(surface: PSurface, filename: cstring): int +proc saveBMP*(surface: PSurface, filename: cstring): int # Sets the color key (transparent pixel) in a blittable surface. # If 'flag' is SDL_SRCCOLORKEY (optionally OR'd with SDL_RLEACCEL), # 'key' will be the transparent pixel in the source image of a blit. @@ -1985,7 +1978,7 @@ proc SaveBMP*(surface: PSurface, filename: cstring): int # and removes RLE acceleration if absent. # If 'flag' is 0, this function clears any current color key. # This function returns 0, or -1 if there was an error. -proc SetColorKey*(surface: PSurface, flag, key: int32): int{.cdecl, +proc setColorKey*(surface: PSurface, flag, key: int32): int{.cdecl, importc: "SDL_SetColorKey", dynlib: LibName.} # This function sets the alpha value for the entire surface, as opposed to # using the alpha component of each pixel. This value measures the range @@ -1998,7 +1991,7 @@ proc SetColorKey*(surface: PSurface, flag, key: int32): int{.cdecl, # If 'flag' is SDL_SRCALPHA, alpha blending is enabled for the surface. # OR:ing the flag with SDL_RLEACCEL requests RLE acceleration for the # surface; if SDL_RLEACCEL is not specified, the RLE accel will be removed. -proc SetAlpha*(surface: PSurface, flag: int32, alpha: byte): int{.cdecl, +proc setAlpha*(surface: PSurface, flag: int32, alpha: byte): int{.cdecl, importc: "SDL_SetAlpha", dynlib: LibName.} # Sets the clipping rectangle for the destination surface in a blit. # @@ -2010,12 +2003,12 @@ proc SetAlpha*(surface: PSurface, flag: int32, alpha: byte): int{.cdecl, # # Note that blits are automatically clipped to the edges of the source # and destination surfaces. -proc SetClipRect*(surface: PSurface, rect: PRect){.cdecl, +proc setClipRect*(surface: PSurface, rect: PRect){.cdecl, importc: "SDL_SetClipRect", dynlib: LibName.} # Gets the clipping rectangle for the destination surface in a blit. # 'rect' must be a pointer to a valid rectangle which will be filled # with the correct values. -proc GetClipRect*(surface: PSurface, rect: PRect){.cdecl, +proc getClipRect*(surface: PSurface, rect: PRect){.cdecl, importc: "SDL_GetClipRect", dynlib: LibName.} # Creates a new surface of the specified format, and then copies and maps # the given surface to it so the blit of the converted surface will be as @@ -2027,7 +2020,7 @@ proc GetClipRect*(surface: PSurface, rect: PRect){.cdecl, # surface. # # This function is used internally by SDL_DisplayFormat(). -proc ConvertSurface*(src: PSurface, fmt: PPixelFormat, flags: int32): PSurface{. +proc convertSurface*(src: PSurface, fmt: PPixelFormat, flags: int32): PSurface{. cdecl, importc: "SDL_ConvertSurface", dynlib: LibName.} # # This performs a fast blit from the source surface to the destination @@ -2098,14 +2091,14 @@ proc ConvertSurface*(src: PSurface, fmt: PPixelFormat, flags: int32): PSurface{. # to the video memory again. # You should call SDL_BlitSurface() unless you know exactly how SDL # blitting works internally and how to use the other blit functions. -proc BlitSurface*(src: PSurface, srcrect: PRect, dst: PSurface, dstrect: PRect): int +proc blitSurface*(src: PSurface, srcrect: PRect, dst: PSurface, dstrect: PRect): int # This is the public blit function, SDL_BlitSurface(), and it performs # rectangle validation and clipping before passing it to SDL_LowerBlit() -proc UpperBlit*(src: PSurface, srcrect: PRect, dst: PSurface, dstrect: PRect): int{. +proc upperBlit*(src: PSurface, srcrect: PRect, dst: PSurface, dstrect: PRect): int{. cdecl, importc: "SDL_UpperBlit", dynlib: LibName.} # This is a semi-private blit function and it performs low-level surface # blitting only. -proc LowerBlit*(src: PSurface, srcrect: PRect, dst: PSurface, dstrect: PRect): int{. +proc lowerBlit*(src: PSurface, srcrect: PRect, dst: PSurface, dstrect: PRect): int{. cdecl, importc: "SDL_LowerBlit", dynlib: LibName.} # This function performs a fast fill of the given rectangle with 'color' # The given rectangle is clipped to the destination surface clip area @@ -2114,7 +2107,7 @@ proc LowerBlit*(src: PSurface, srcrect: PRect, dst: PSurface, dstrect: PRect): i # The color should be a pixel of the format used by the surface, and # can be generated by the SDL_MapRGB() function. # This function returns 0 on success, or -1 on error. -proc FillRect*(dst: PSurface, dstrect: PRect, color: int32): int{.cdecl, +proc fillRect*(dst: PSurface, dstrect: PRect, color: int32): int{.cdecl, importc: "SDL_FillRect", dynlib: LibName.} # This function takes a surface and copies it to a new surface of the # pixel format and colors of the video framebuffer, suitable for fast @@ -2125,7 +2118,7 @@ proc FillRect*(dst: PSurface, dstrect: PRect, color: int32): int{.cdecl, # calling this function. # # If the conversion fails or runs out of memory, it returns NULL -proc DisplayFormat*(surface: PSurface): PSurface{.cdecl, +proc displayFormat*(surface: PSurface): PSurface{.cdecl, importc: "SDL_DisplayFormat", dynlib: LibName.} # This function takes a surface and copies it to a new surface of the # pixel format and colors of the video framebuffer (if possible), @@ -2137,7 +2130,7 @@ proc DisplayFormat*(surface: PSurface): PSurface{.cdecl, # calling this function. # # If the conversion fails or runs out of memory, it returns NULL -proc DisplayFormatAlpha*(surface: PSurface): PSurface{.cdecl, +proc displayFormatAlpha*(surface: PSurface): PSurface{.cdecl, importc: "SDL_DisplayFormatAlpha", dynlib: LibName.} #* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #* YUV video surface overlay functions */ @@ -2146,23 +2139,23 @@ proc DisplayFormatAlpha*(surface: PSurface): PSurface{.cdecl, # Calling the returned surface an overlay is something of a misnomer because # the contents of the display surface underneath the area where the overlay # is shown is undefined - it may be overwritten with the converted YUV data. -proc CreateYUVOverlay*(width: int, height: int, format: int32, +proc createYUVOverlay*(width: int, height: int, format: int32, display: PSurface): POverlay{.cdecl, importc: "SDL_CreateYUVOverlay", dynlib: LibName.} # Lock an overlay for direct access, and unlock it when you are done -proc LockYUVOverlay*(Overlay: POverlay): int{.cdecl, +proc lockYUVOverlay*(overlay: POverlay): int{.cdecl, importc: "SDL_LockYUVOverlay", dynlib: LibName.} -proc UnlockYUVOverlay*(Overlay: POverlay){.cdecl, +proc unlockYUVOverlay*(overlay: POverlay){.cdecl, importc: "SDL_UnlockYUVOverlay", dynlib: LibName.} # Blit a video overlay to the display surface. # The contents of the video surface underneath the blit destination are # not defined. # The width and height of the destination rectangle may be different from # that of the overlay, but currently only 2x scaling is supported. -proc DisplayYUVOverlay*(Overlay: POverlay, dstrect: PRect): int{.cdecl, +proc displayYUVOverlay*(overlay: POverlay, dstrect: PRect): int{.cdecl, importc: "SDL_DisplayYUVOverlay", dynlib: LibName.} # Free a video overlay -proc FreeYUVOverlay*(Overlay: POverlay){.cdecl, importc: "SDL_FreeYUVOverlay", +proc freeYUVOverlay*(overlay: POverlay){.cdecl, importc: "SDL_FreeYUVOverlay", dynlib: LibName.} #------------------------------------------------------------------------------ # OpenGL Routines @@ -2175,13 +2168,13 @@ proc FreeYUVOverlay*(Overlay: POverlay){.cdecl, importc: "SDL_FreeYUVOverlay", # your program from the dynamic library using SDL_GL_GetProcAddress(). # # This is disabled in default builds of SDL. -proc GL_LoadLibrary*(filename: cstring): int{.cdecl, +proc glLoadLibrary*(filename: cstring): int{.cdecl, importc: "SDL_GL_LoadLibrary", dynlib: LibName.} # Get the address of a GL function (for extension functions) -proc GL_GetProcAddress*(procname: cstring): Pointer{.cdecl, +proc glGetProcAddress*(procname: cstring): pointer{.cdecl, importc: "SDL_GL_GetProcAddress", dynlib: LibName.} # Set an attribute of the OpenGL subsystem before intialization. -proc GL_SetAttribute*(attr: TGLAttr, value: int): int{.cdecl, +proc glSetAttribute*(attr: TGLAttr, value: int): int{.cdecl, importc: "SDL_GL_SetAttribute", dynlib: LibName.} # Get an attribute of the OpenGL subsystem from the windowing # interface, such as glX. This is of course different from getting @@ -2190,34 +2183,34 @@ proc GL_SetAttribute*(attr: TGLAttr, value: int): int{.cdecl, # # Developers should track the values they pass into SDL_GL_SetAttribute # themselves if they want to retrieve these values. -proc GL_GetAttribute*(attr: TGLAttr, value: var int): int{.cdecl, +proc glGetAttribute*(attr: TGLAttr, value: var int): int{.cdecl, importc: "SDL_GL_GetAttribute", dynlib: LibName.} # Swap the OpenGL buffers, if double-buffering is supported. -proc GL_SwapBuffers*(){.cdecl, importc: "SDL_GL_SwapBuffers", dynlib: LibName.} +proc glSwapBuffers*(){.cdecl, importc: "SDL_GL_SwapBuffers", dynlib: LibName.} # Internal functions that should not be called unless you have read # and understood the source code for these functions. -proc GL_UpdateRects*(numrects: int, rects: PRect){.cdecl, +proc glUpdateRects*(numrects: int, rects: PRect){.cdecl, importc: "SDL_GL_UpdateRects", dynlib: LibName.} -proc GL_Lock*(){.cdecl, importc: "SDL_GL_Lock", dynlib: LibName.} -proc GL_Unlock*(){.cdecl, importc: "SDL_GL_Unlock", dynlib: LibName.} +proc glLock*(){.cdecl, importc: "SDL_GL_Lock", dynlib: LibName.} +proc glUnlock*(){.cdecl, importc: "SDL_GL_Unlock", dynlib: LibName.} #* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * #* These functions allow interaction with the window manager, if any. * #* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # Sets/Gets the title and icon text of the display window -proc WM_GetCaption*(title: var cstring, icon: var cstring){.cdecl, +proc wmGetCaption*(title: var cstring, icon: var cstring){.cdecl, importc: "SDL_WM_GetCaption", dynlib: LibName.} -proc WM_SetCaption*(title: cstring, icon: cstring){.cdecl, +proc wmSetCaption*(title: cstring, icon: cstring){.cdecl, importc: "SDL_WM_SetCaption", dynlib: LibName.} # Sets the icon for the display window. # This function must be called before the first call to SDL_SetVideoMode(). # It takes an icon surface, and a mask in MSB format. # If 'mask' is NULL, the entire icon surface will be used as the icon. -proc WM_SetIcon*(icon: PSurface, mask: byte){.cdecl, importc: "SDL_WM_SetIcon", +proc wmSetIcon*(icon: PSurface, mask: byte){.cdecl, importc: "SDL_WM_SetIcon", dynlib: LibName.} # This function iconifies the window, and returns 1 if it succeeded. # If the function succeeds, it generates an SDL_APPACTIVE loss event. # This function is a noop and returns 0 in non-windowed environments. -proc WM_IconifyWindow*(): int{.cdecl, importc: "SDL_WM_IconifyWindow", +proc wmIconifyWindow*(): int{.cdecl, importc: "SDL_WM_IconifyWindow", dynlib: LibName.} # Toggle fullscreen mode without changing the contents of the screen. # If the display surface does not require locking before accessing @@ -2232,12 +2225,12 @@ proc WM_IconifyWindow*(): int{.cdecl, importc: "SDL_WM_IconifyWindow", # set, then the display will be windowed by default where supported. # # This is currently only implemented in the X11 video driver. -proc WM_ToggleFullScreen*(surface: PSurface): int{.cdecl, +proc wmToggleFullScreen*(surface: PSurface): int{.cdecl, importc: "SDL_WM_ToggleFullScreen", dynlib: LibName.} # Grabbing means that the mouse is confined to the application window, # and nearly all keyboard input is passed directly to the application, # and not interpreted by a window manager, if any. -proc WM_GrabInput*(mode: TGrabMode): TGrabMode{.cdecl, +proc wmGrabInput*(mode: TGrabMode): TGrabMode{.cdecl, importc: "SDL_WM_GrabInput", dynlib: LibName.} #------------------------------------------------------------------------------ # mouse-routines @@ -2246,16 +2239,16 @@ proc WM_GrabInput*(mode: TGrabMode): TGrabMode{.cdecl, # The current button state is returned as a button bitmask, which can # be tested using the SDL_BUTTON(X) macros, and x and y are set to the # current mouse cursor position. You can pass NULL for either x or y. -proc GetMouseState*(x: var int, y: var int): byte{.cdecl, +proc getMouseState*(x: var int, y: var int): byte{.cdecl, importc: "SDL_GetMouseState", dynlib: LibName.} # Retrieve the current state of the mouse. # The current button state is returned as a button bitmask, which can # be tested using the SDL_BUTTON(X) macros, and x and y are set to the # mouse deltas since the last call to SDL_GetRelativeMouseState(). -proc GetRelativeMouseState*(x: var int, y: var int): byte{.cdecl, +proc getRelativeMouseState*(x: var int, y: var int): byte{.cdecl, importc: "SDL_GetRelativeMouseState", dynlib: LibName.} # Set the position of the mouse cursor (generates a mouse motion event) -proc WarpMouse*(x, y: UInt16){.cdecl, importc: "SDL_WarpMouse", dynlib: LibName.} +proc warpMouse*(x, y: uint16){.cdecl, importc: "SDL_WarpMouse", dynlib: LibName.} # Create a cursor using the specified data and mask (in MSB format). # The cursor width must be a multiple of 8 bits. # @@ -2267,26 +2260,26 @@ proc WarpMouse*(x, y: UInt16){.cdecl, importc: "SDL_WarpMouse", dynlib: LibName. # 1 0 Inverted color if possible, black if not. # # Cursors created with this function must be freed with SDL_FreeCursor(). -proc CreateCursor*(data, mask: ptr byte, w, h, hot_x, hot_y: int): PCursor{.cdecl, +proc createCursor*(data, mask: ptr byte, w, h, hotX, hotY: int): PCursor{.cdecl, importc: "SDL_CreateCursor", dynlib: LibName.} # Set the currently active cursor to the specified one. # If the cursor is currently visible, the change will be immediately # represented on the display. -proc SetCursor*(cursor: PCursor){.cdecl, importc: "SDL_SetCursor", +proc setCursor*(cursor: PCursor){.cdecl, importc: "SDL_SetCursor", dynlib: LibName.} # Returns the currently active cursor. -proc GetCursor*(): PCursor{.cdecl, importc: "SDL_GetCursor", dynlib: LibName.} +proc getCursor*(): PCursor{.cdecl, importc: "SDL_GetCursor", dynlib: LibName.} # Deallocates a cursor created with SDL_CreateCursor(). -proc FreeCursor*(cursor: PCursor){.cdecl, importc: "SDL_FreeCursor", +proc freeCursor*(cursor: PCursor){.cdecl, importc: "SDL_FreeCursor", dynlib: LibName.} # Toggle whether or not the cursor is shown on the screen. # The cursor start off displayed, but can be turned off. # SDL_ShowCursor() returns 1 if the cursor was being displayed # before the call, or 0 if it was not. You can query the current # state by passing a 'toggle' value of -1. -proc ShowCursor*(toggle: int): int{.cdecl, importc: "SDL_ShowCursor", +proc showCursor*(toggle: int): int{.cdecl, importc: "SDL_ShowCursor", dynlib: LibName.} -proc BUTTON*(Button: int): int +proc button*(b: int): int #------------------------------------------------------------------------------ # Keyboard-routines #------------------------------------------------------------------------------ @@ -2296,12 +2289,12 @@ proc BUTTON*(Button: int): int # If 'enable' is 0, translation is disabled. # If 'enable' is -1, the translation state is not changed. # It returns the previous state of keyboard translation. -proc EnableUNICODE*(enable: int): int{.cdecl, importc: "SDL_EnableUNICODE", +proc enableUNICODE*(enable: int): int{.cdecl, importc: "SDL_EnableUNICODE", dynlib: LibName.} # If 'delay' is set to 0, keyboard repeat is disabled. -proc EnableKeyRepeat*(delay: int, interval: int): int{.cdecl, +proc enableKeyRepeat*(delay: int, interval: int): int{.cdecl, importc: "SDL_EnableKeyRepeat", dynlib: LibName.} -proc GetKeyRepeat*(delay: PInteger, interval: PInteger){.cdecl, +proc getKeyRepeat*(delay: PInteger, interval: PInteger){.cdecl, importc: "SDL_GetKeyRepeat", dynlib: LibName.} # Get a snapshot of the current state of the keyboard. # Returns an array of keystates, indexed by the SDLK_* syms. @@ -2309,16 +2302,16 @@ proc GetKeyRepeat*(delay: PInteger, interval: PInteger){.cdecl, # # byte *keystate = SDL_GetKeyState(NULL); # if ( keystate[SDLK_RETURN] ) ... is pressed -proc GetKeyState*(numkeys: pointer): ptr byte{.cdecl, importc: "SDL_GetKeyState", +proc getKeyState*(numkeys: pointer): ptr byte{.cdecl, importc: "SDL_GetKeyState", dynlib: LibName.} # Get the current key modifier state -proc GetModState*(): TMod{.cdecl, importc: "SDL_GetModState", dynlib: LibName.} +proc getModState*(): TMod{.cdecl, importc: "SDL_GetModState", dynlib: LibName.} # Set the current key modifier state # This does not change the keyboard state, only the key modifier flags. -proc SetModState*(modstate: TMod){.cdecl, importc: "SDL_SetModState", +proc setModState*(modstate: TMod){.cdecl, importc: "SDL_SetModState", dynlib: LibName.} # Get the name of an SDL virtual keysym -proc GetKeyName*(key: TKey): cstring{.cdecl, importc: "SDL_GetKeyName", +proc getKeyName*(key: TKey): cstring{.cdecl, importc: "SDL_GetKeyName", dynlib: LibName.} #------------------------------------------------------------------------------ # Active Routines @@ -2327,96 +2320,96 @@ proc GetKeyName*(key: TKey): cstring{.cdecl, importc: "SDL_GetKeyName", # bitwise combination of SDL_APPMOUSEFOCUS, SDL_APPINPUTFOCUS, and # SDL_APPACTIVE. If SDL_APPACTIVE is set, then the user is able to # see your application, otherwise it has been iconified or disabled. -proc GetAppState*(): byte{.cdecl, importc: "SDL_GetAppState", dynlib: LibName.} +proc getAppState*(): byte{.cdecl, importc: "SDL_GetAppState", dynlib: LibName.} # Mutex functions # Create a mutex, initialized unlocked -proc CreateMutex*(): PMutex{.cdecl, importc: "SDL_CreateMutex", dynlib: LibName.} +proc createMutex*(): PMutex{.cdecl, importc: "SDL_CreateMutex", dynlib: LibName.} # Lock the mutex (Returns 0, or -1 on error) -proc mutexP*(mutex: Pmutex): int{.cdecl, importc: "SDL_mutexP", dynlib: LibName.} -proc LockMutex*(mutex: Pmutex): int +proc mutexP*(mutex: PMutex): int{.cdecl, importc: "SDL_mutexP", dynlib: LibName.} +proc lockMutex*(mutex: PMutex): int # Unlock the mutex (Returns 0, or -1 on error) -proc mutexV*(mutex: Pmutex): int{.cdecl, importc: "SDL_mutexV", dynlib: LibName.} -proc UnlockMutex*(mutex: Pmutex): int +proc mutexV*(mutex: PMutex): int{.cdecl, importc: "SDL_mutexV", dynlib: LibName.} +proc unlockMutex*(mutex: PMutex): int # Destroy a mutex -proc DestroyMutex*(mutex: Pmutex){.cdecl, importc: "SDL_DestroyMutex", +proc destroyMutex*(mutex: PMutex){.cdecl, importc: "SDL_DestroyMutex", dynlib: LibName.} # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # Semaphore functions # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # Create a semaphore, initialized with value, returns NULL on failure. -proc CreateSemaphore*(initial_value: int32): PSem{.cdecl, +proc createSemaphore*(initialValue: int32): PSem{.cdecl, importc: "SDL_CreateSemaphore", dynlib: LibName.} # Destroy a semaphore -proc DestroySemaphore*(sem: Psem){.cdecl, importc: "SDL_DestroySemaphore", +proc destroySemaphore*(sem: PSem){.cdecl, importc: "SDL_DestroySemaphore", dynlib: LibName.} # This function suspends the calling thread until the semaphore pointed # to by sem has a positive count. It then atomically decreases the semaphore # count. -proc SemWait*(sem: Psem): int{.cdecl, importc: "SDL_SemWait", dynlib: LibName.} +proc semWait*(sem: PSem): int{.cdecl, importc: "SDL_SemWait", dynlib: LibName.} # Non-blocking variant of SDL_SemWait(), returns 0 if the wait succeeds, # SDL_MUTEX_TIMEDOUT if the wait would block, and -1 on error. -proc SemTryWait*(sem: Psem): int{.cdecl, importc: "SDL_SemTryWait", +proc semTryWait*(sem: PSem): int{.cdecl, importc: "SDL_SemTryWait", dynlib: LibName.} # Variant of SDL_SemWait() with a timeout in milliseconds, returns 0 if # the wait succeeds, SDL_MUTEX_TIMEDOUT if the wait does not succeed in # the allotted time, and -1 on error. # On some platforms this function is implemented by looping with a delay # of 1 ms, and so should be avoided if possible. -proc SemWaitTimeout*(sem: Psem, ms: int32): int{.cdecl, +proc semWaitTimeout*(sem: PSem, ms: int32): int{.cdecl, importc: "SDL_SemWaitTimeout", dynlib: LibName.} # Atomically increases the semaphore's count (not blocking), returns 0, # or -1 on error. -proc SemPost*(sem: Psem): int{.cdecl, importc: "SDL_SemPost", dynlib: LibName.} +proc semPost*(sem: PSem): int{.cdecl, importc: "SDL_SemPost", dynlib: LibName.} # Returns the current count of the semaphore -proc SemValue*(sem: Psem): int32{.cdecl, importc: "SDL_SemValue", +proc semValue*(sem: PSem): int32{.cdecl, importc: "SDL_SemValue", dynlib: LibName.} # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # Condition variable functions # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # Create a condition variable -proc CreateCond*(): PCond{.cdecl, importc: "SDL_CreateCond", dynlib: LibName.} +proc createCond*(): PCond{.cdecl, importc: "SDL_CreateCond", dynlib: LibName.} # Destroy a condition variable -proc DestroyCond*(cond: PCond){.cdecl, importc: "SDL_DestroyCond", +proc destroyCond*(cond: PCond){.cdecl, importc: "SDL_DestroyCond", dynlib: LibName.} # Restart one of the threads that are waiting on the condition variable, # returns 0 or -1 on error. -proc CondSignal*(cond: Pcond): int{.cdecl, importc: "SDL_CondSignal", +proc condSignal*(cond: PCond): int{.cdecl, importc: "SDL_CondSignal", dynlib: LibName.} # Restart all threads that are waiting on the condition variable, # returns 0 or -1 on error. -proc CondBroadcast*(cond: Pcond): int{.cdecl, importc: "SDL_CondBroadcast", +proc condBroadcast*(cond: PCond): int{.cdecl, importc: "SDL_CondBroadcast", dynlib: LibName.} # Wait on the condition variable, unlocking the provided mutex. # The mutex must be locked before entering this function! # Returns 0 when it is signaled, or -1 on error. -proc CondWait*(cond: Pcond, mut: Pmutex): int{.cdecl, importc: "SDL_CondWait", +proc condWait*(cond: PCond, mut: PMutex): int{.cdecl, importc: "SDL_CondWait", dynlib: LibName.} # Waits for at most 'ms' milliseconds, and returns 0 if the condition # variable is signaled, SDL_MUTEX_TIMEDOUT if the condition is not # signaled in the allotted time, and -1 on error. # On some platforms this function is implemented by looping with a delay # of 1 ms, and so should be avoided if possible. -proc CondWaitTimeout*(cond: Pcond, mut: Pmutex, ms: int32): int{.cdecl, +proc condWaitTimeout*(cond: PCond, mut: PMutex, ms: int32): int{.cdecl, importc: "SDL_CondWaitTimeout", dynlib: LibName.} # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # Condition variable functions # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # Create a thread -proc CreateThread*(fn, data: Pointer): PThread{.cdecl, +proc createThread*(fn, data: pointer): PThread{.cdecl, importc: "SDL_CreateThread", dynlib: LibName.} # Get the 32-bit thread identifier for the current thread -proc ThreadID*(): int32{.cdecl, importc: "SDL_ThreadID", dynlib: LibName.} +proc threadID*(): int32{.cdecl, importc: "SDL_ThreadID", dynlib: LibName.} # Get the 32-bit thread identifier for the specified thread, # equivalent to SDL_ThreadID() if the specified thread is NULL. -proc GetThreadID*(thread: PThread): int32{.cdecl, importc: "SDL_GetThreadID", +proc getThreadID*(thread: PThread): int32{.cdecl, importc: "SDL_GetThreadID", dynlib: LibName.} # Wait for a thread to finish. # The return code for the thread function is placed in the area # pointed to by 'status', if 'status' is not NULL. -proc WaitThread*(thread: PThread, status: var int){.cdecl, +proc waitThread*(thread: PThread, status: var int){.cdecl, importc: "SDL_WaitThread", dynlib: LibName.} # Forcefully kill a thread without worrying about its state -proc KillThread*(thread: PThread){.cdecl, importc: "SDL_KillThread", +proc killThread*(thread: PThread){.cdecl, importc: "SDL_KillThread", dynlib: LibName.} #------------------------------------------------------------------------------ # Get Environment Routines @@ -2427,7 +2420,7 @@ proc KillThread*(thread: PThread){.cdecl, importc: "SDL_KillThread", # * returns 1 if the function is implemented. If it's not implemented, or # * the version member of the 'info' structure is invalid, it returns 0. # * -proc GetWMInfo*(info: PSysWMinfo): int{.cdecl, importc: "SDL_GetWMInfo", +proc getWMInfo*(info: PSysWMinfo): int{.cdecl, importc: "SDL_GetWMInfo", dynlib: LibName.} #------------------------------------------------------------------------------ #SDL_loadso.h @@ -2435,115 +2428,111 @@ proc GetWMInfo*(info: PSysWMinfo): int{.cdecl, importc: "SDL_GetWMInfo", # * to the object handle (or NULL if there was an error). # * The 'sofile' parameter is a system dependent name of the object file. # * -proc LoadObject*(sofile: cstring): Pointer{.cdecl, importc: "SDL_LoadObject", +proc loadObject*(sofile: cstring): pointer{.cdecl, importc: "SDL_LoadObject", dynlib: LibName.} #* Given an object handle, this function looks up the address of the # * named function in the shared object and returns it. This address # * is no longer valid after calling SDL_UnloadObject(). # * -proc LoadFunction*(handle: Pointer, name: cstring): Pointer{.cdecl, +proc loadFunction*(handle: pointer, name: cstring): pointer{.cdecl, importc: "SDL_LoadFunction", dynlib: LibName.} #* Unload a shared object from memory * -proc UnloadObject*(handle: Pointer){.cdecl, importc: "SDL_UnloadObject", +proc unloadObject*(handle: pointer){.cdecl, importc: "SDL_UnloadObject", dynlib: LibName.} #------------------------------------------------------------------------------ -proc Swap32*(D: int32): int32 +proc swap32*(d: int32): int32 # Bitwise Checking functions -proc IsBitOn*(value: int, bit: int8): bool -proc TurnBitOn*(value: int, bit: int8): int -proc TurnBitOff*(value: int, bit: int8): int +proc isBitOn*(value: int, bit: int8): bool +proc turnBitOn*(value: int, bit: int8): int +proc turnBitOff*(value: int, bit: int8): int # implementation -proc TABLESIZE(table: cstring): int = - Result = SizeOf(table) div SizeOf(table[0]) +proc tablesize(table: cstring): int = + result = sizeOf(table) div sizeOf(table[0]) -proc OutOfMemory() = - when not (defined(WINDOWS)): Error(ENOMEM) - -proc RWSeek(context: PRWops, offset: int, whence: int): int = - Result = context.seek(context, offset, whence) +proc rwSeek(context: PRWops, offset: int, whence: int): int = + result = context.seek(context, offset, whence) -proc RWTell(context: PRWops): int = - Result = context.seek(context, 0, 1) +proc rwTell(context: PRWops): int = + result = context.seek(context, 0, 1) -proc RWRead(context: PRWops, theptr: Pointer, size: int, n: int): int = - Result = context.read(context, theptr, size, n) +proc rwRead(context: PRWops, theptr: pointer, size: int, n: int): int = + result = context.read(context, theptr, size, n) -proc RWWrite(context: PRWops, theptr: Pointer, size: int, n: int): int = - Result = context.write(context, theptr, size, n) +proc rwWrite(context: PRWops, theptr: pointer, size: int, n: int): int = + result = context.write(context, theptr, size, n) -proc RWClose(context: PRWops): int = - Result = context.closeFile(context) +proc rwClose(context: PRWops): int = + result = context.closeFile(context) -proc LoadWAV(filename: cstring, spec: PAudioSpec, audio_buf: ptr byte, +proc loadWAV(filename: cstring, spec: PAudioSpec, audioBuf: ptr byte, audiolen: PUInt32): PAudioSpec = - Result = LoadWAV_RW(RWFromFile(filename, "rb"), 1, spec, audio_buf, audiolen) + result = loadWAV_RW(rWFromFile(filename, "rb"), 1, spec, audioBuf, audiolen) -proc CDInDrive(status: TCDStatus): bool = - Result = ord(status) > ord(CD_ERROR) +proc cdInDrive(status: TCDStatus): bool = + result = ord(status) > ord(CD_ERROR) -proc FRAMES_TO_MSF(frames: int, M: var int, S: var int, F: var int) = - var value: int - value = frames - F = value mod CD_FPS +proc framesToMsf*(frames: int; m, s, f: var int) = + var value = frames + f = value mod CD_FPS value = value div CD_FPS - S = value mod 60 + s = value mod 60 value = value div 60 - M = value + m = value -proc MSF_TO_FRAMES(M: int, S: int, F: int): int = - Result = M * 60 * CD_FPS + S * CD_FPS + F +proc msfToFrames*(m, s, f: int): int = + result = m * 60 * CD_FPS + s * CD_FPS + f -proc VERSION(X: var TVersion) = - X.major = MAJOR_VERSION - X.minor = MINOR_VERSION - X.patch = PATCHLEVEL +proc version(x: var Tversion) = + x.major = MAJOR_VERSION + x.minor = MINOR_VERSION + x.patch = PATCHLEVEL -proc VERSIONNUM(X, Y, Z: int): int = - Result = X * 1000 + Y * 100 + Z +proc versionnum(x, y, z: int): int = + result = x * 1000 + y * 100 + z -proc COMPILEDVERSION(): int = - Result = VERSIONNUM(MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL) +proc compiledversion(): int = + result = versionnum(MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL) -proc VERSION_ATLEAST(X, Y, Z: int): bool = - Result = (COMPILEDVERSION() >= VERSIONNUM(X, Y, Z)) +proc versionAtleast(x, y, z: int): bool = + result = (compiledversion() >= versionnum(x, y, z)) -proc LoadBMP(filename: cstring): PSurface = - Result = LoadBMP_RW(RWFromFile(filename, "rb"), 1) +proc loadBMP(filename: cstring): PSurface = + result = loadBMP_RW(rWFromFile(filename, "rb"), 1) -proc SaveBMP(surface: PSurface, filename: cstring): int = - Result = SaveBMP_RW(surface, RWFromFile(filename, "wb"), 1) +proc saveBMP(surface: PSurface, filename: cstring): int = + result = saveBMP_RW(surface, rWFromFile(filename, "wb"), 1) -proc BlitSurface(src: PSurface, srcrect: PRect, dst: PSurface, dstrect: PRect): int = - Result = UpperBlit(src, srcrect, dst, dstrect) +proc blitSurface(src: PSurface, srcrect: PRect, dst: PSurface, dstrect: PRect): int = + result = upperBlit(src, srcrect, dst, dstrect) -proc AllocSurface(flags: int32, width, height, depth: int, - RMask, GMask, BMask, AMask: int32): PSurface = - Result = CreateRGBSurface(flags, width, height, depth, RMask, GMask, BMask, - AMask) +proc allocSurface(flags: int32, width, height, depth: int, + rMask, gMask, bMask, aMask: int32): PSurface = + result = createRGBSurface(flags, width, height, depth, rMask, gMask, bMask, + aMask) -proc MustLock(Surface: PSurface): bool = - Result = ((surface[].offset != 0) or - ((surface[].flags and (HWSURFACE or ASYNCBLIT or RLEACCEL)) != 0)) +proc mustLock(surface: PSurface): bool = + result = ((surface.offset != 0) or + ((surface.flags and (HWSURFACE or ASYNCBLIT or RLEACCEL)) != 0)) -proc LockMutex(mutex: Pmutex): int = - Result = mutexP(mutex) +proc lockMutex(mutex: PMutex): int = + result = mutexP(mutex) -proc UnlockMutex(mutex: Pmutex): int = - Result = mutexV(mutex) +proc unlockMutex(mutex: PMutex): int = + result = mutexV(mutex) -proc BUTTON(Button: int): int = - Result = PRESSED shl (Button - 1) +proc button(b: int): int = + result = PRESSED shl (b - 1) -proc Swap32(D: int32): int32 = - Result = ((D shl 24) or ((D shl 8) and 0x00FF0000) or - ((D shr 8) and 0x0000FF00) or (D shr 24)) +proc swap32(d: int32): int32 = + result = ((d shl 24) or ((d shl 8) and 0x00FF0000) or + ((d shr 8) and 0x0000FF00) or (d shr 24)) -proc IsBitOn(value: int, bit: int8): bool = +proc isBitOn(value: int, bit: int8): bool = result = ((value and (1 shl ze(bit))) != 0) -proc TurnBitOn(value: int, bit: int8): int = +proc turnBitOn(value: int, bit: int8): int = result = (value or (1 shl ze(bit))) -proc TurnBitOff(value: int, bit: int8): int = +proc turnBitOff(value: int, bit: int8): int = result = (value and not (1 shl ze(bit))) diff --git a/lib/wrappers/sdl/sdl_gfx.nim b/lib/wrappers/sdl/sdl_gfx.nim index cf36c4989..5523ad0a2 100644 --- a/lib/wrappers/sdl/sdl_gfx.nim +++ b/lib/wrappers/sdl/sdl_gfx.nim @@ -39,10 +39,10 @@ const # Some rates in Hz type PFPSmanager* = ptr TFPSmanager TFPSmanager*{.final.} = object # ---- Structures - framecount*: Uint32 + framecount*: uint32 rateticks*: float32 - lastticks*: Uint32 - rate*: Uint32 + lastticks*: uint32 + rate*: uint32 PColorRGBA* = ptr TColorRGBA TColorRGBA*{.final.} = object @@ -79,102 +79,102 @@ proc framerateDelay*(manager: PFPSmanager){.cdecl, # # Note: all ___Color routines expect the color to be in format 0xRRGGBBAA # Pixel -proc pixelColor*(dst: PSurface, x: int16, y: int16, color: Uint32): cint{. +proc pixelColor*(dst: PSurface, x: int16, y: int16, color: uint32): cint{. cdecl, importc: "pixelColor", dynlib: gfxLibName.} proc pixelRGBA*(dst: PSurface, x: int16, y: int16, r: byte, g: byte, b: byte, a: byte): cint{.cdecl, importc: "pixelRGBA", dynlib: gfxLibName.} # Horizontal line -proc hlineColor*(dst: PSurface, x1: int16, x2: int16, y: int16, color: Uint32): cint{. +proc hlineColor*(dst: PSurface, x1: int16, x2: int16, y: int16, color: uint32): cint{. cdecl, importc: "hlineColor", dynlib: gfxLibName.} proc hlineRGBA*(dst: PSurface, x1: int16, x2: int16, y: int16, r: byte, g: byte, b: byte, a: byte): cint{.cdecl, importc: "hlineRGBA", dynlib: gfxLibName.} # Vertical line -proc vlineColor*(dst: PSurface, x: int16, y1: int16, y2: int16, color: Uint32): cint{. +proc vlineColor*(dst: PSurface, x: int16, y1: int16, y2: int16, color: uint32): cint{. cdecl, importc: "vlineColor", dynlib: gfxLibName.} proc vlineRGBA*(dst: PSurface, x: int16, y1: int16, y2: int16, r: byte, g: byte, b: byte, a: byte): cint{.cdecl, importc: "vlineRGBA", dynlib: gfxLibName.} # Rectangle proc rectangleColor*(dst: PSurface, x1: int16, y1: int16, x2: int16, - y2: int16, color: Uint32): cint{.cdecl, + y2: int16, color: uint32): cint{.cdecl, importc: "rectangleColor", dynlib: gfxLibName.} proc rectangleRGBA*(dst: PSurface, x1: int16, y1: int16, x2: int16, y2: int16, r: byte, g: byte, b: byte, a: byte): cint{. cdecl, importc: "rectangleRGBA", dynlib: gfxLibName.} # Filled rectangle (Box) proc boxColor*(dst: PSurface, x1: int16, y1: int16, x2: int16, y2: int16, - color: Uint32): cint{.cdecl, importc: "boxColor", + color: uint32): cint{.cdecl, importc: "boxColor", dynlib: gfxLibName.} proc boxRGBA*(dst: PSurface, x1: int16, y1: int16, x2: int16, y2: int16, r: byte, g: byte, b: byte, a: byte): cint{.cdecl, importc: "boxRGBA", dynlib: gfxLibName.} # Line proc lineColor*(dst: PSurface, x1: int16, y1: int16, x2: int16, y2: int16, - color: Uint32): cint{.cdecl, importc: "lineColor", + color: uint32): cint{.cdecl, importc: "lineColor", dynlib: gfxLibName.} proc lineRGBA*(dst: PSurface, x1: int16, y1: int16, x2: int16, y2: int16, r: byte, g: byte, b: byte, a: byte): cint{.cdecl, importc: "lineRGBA", dynlib: gfxLibName.} # AA Line proc aalineColor*(dst: PSurface, x1: int16, y1: int16, x2: int16, y2: int16, - color: Uint32): cint{.cdecl, importc: "aalineColor", + color: uint32): cint{.cdecl, importc: "aalineColor", dynlib: gfxLibName.} proc aalineRGBA*(dst: PSurface, x1: int16, y1: int16, x2: int16, y2: int16, r: byte, g: byte, b: byte, a: byte): cint{.cdecl, importc: "aalineRGBA", dynlib: gfxLibName.} # Circle -proc circleColor*(dst: PSurface, x: int16, y: int16, r: int16, color: Uint32): cint{. +proc circleColor*(dst: PSurface, x: int16, y: int16, r: int16, color: uint32): cint{. cdecl, importc: "circleColor", dynlib: gfxLibName.} proc circleRGBA*(dst: PSurface, x: int16, y: int16, rad: int16, r: byte, g: byte, b: byte, a: byte): cint{.cdecl, importc: "circleRGBA", dynlib: gfxLibName.} # AA Circle proc aacircleColor*(dst: PSurface, x: int16, y: int16, r: int16, - color: Uint32): cint{.cdecl, importc: "aacircleColor", + color: uint32): cint{.cdecl, importc: "aacircleColor", dynlib: gfxLibName.} proc aacircleRGBA*(dst: PSurface, x: int16, y: int16, rad: int16, r: byte, g: byte, b: byte, a: byte): cint{.cdecl, importc: "aacircleRGBA", dynlib: gfxLibName.} # Filled Circle proc filledCircleColor*(dst: PSurface, x: int16, y: int16, r: int16, - color: Uint32): cint{.cdecl, + color: uint32): cint{.cdecl, importc: "filledCircleColor", dynlib: gfxLibName.} proc filledCircleRGBA*(dst: PSurface, x: int16, y: int16, rad: int16, r: byte, g: byte, b: byte, a: byte): cint{.cdecl, importc: "filledCircleRGBA", dynlib: gfxLibName.} # Ellipse proc ellipseColor*(dst: PSurface, x: int16, y: int16, rx: int16, ry: int16, - color: Uint32): cint{.cdecl, importc: "ellipseColor", + color: uint32): cint{.cdecl, importc: "ellipseColor", dynlib: gfxLibName.} proc ellipseRGBA*(dst: PSurface, x: int16, y: int16, rx: int16, ry: int16, r: byte, g: byte, b: byte, a: byte): cint{.cdecl, importc: "ellipseRGBA", dynlib: gfxLibName.} # AA Ellipse proc aaellipseColor*(dst: PSurface, xc: int16, yc: int16, rx: int16, - ry: int16, color: Uint32): cint{.cdecl, + ry: int16, color: uint32): cint{.cdecl, importc: "aaellipseColor", dynlib: gfxLibName.} proc aaellipseRGBA*(dst: PSurface, x: int16, y: int16, rx: int16, ry: int16, r: byte, g: byte, b: byte, a: byte): cint{.cdecl, importc: "aaellipseRGBA", dynlib: gfxLibName.} # Filled Ellipse proc filledEllipseColor*(dst: PSurface, x: int16, y: int16, rx: int16, - ry: int16, color: Uint32): cint{.cdecl, + ry: int16, color: uint32): cint{.cdecl, importc: "filledEllipseColor", dynlib: gfxLibName.} proc filledEllipseRGBA*(dst: PSurface, x: int16, y: int16, rx: int16, ry: int16, r: byte, g: byte, b: byte, a: byte): cint{. cdecl, importc: "filledEllipseRGBA", dynlib: gfxLibName.} # Pie proc pieColor*(dst: PSurface, x: int16, y: int16, rad: int16, start: int16, - finish: int16, color: Uint32): cint{.cdecl, importc: "pieColor", + finish: int16, color: uint32): cint{.cdecl, importc: "pieColor", dynlib: gfxLibName.} proc pieRGBA*(dst: PSurface, x: int16, y: int16, rad: int16, start: int16, finish: int16, r: byte, g: byte, b: byte, a: byte): cint{. cdecl, importc: "pieRGBA", dynlib: gfxLibName.} # Filled Pie proc filledPieColor*(dst: PSurface, x: int16, y: int16, rad: int16, - start: int16, finish: int16, color: Uint32): cint{.cdecl, + start: int16, finish: int16, color: uint32): cint{.cdecl, importc: "filledPieColor", dynlib: gfxLibName.} proc filledPieRGBA*(dst: PSurface, x: int16, y: int16, rad: int16, start: int16, finish: int16, r: byte, g: byte, b: byte, @@ -182,14 +182,14 @@ proc filledPieRGBA*(dst: PSurface, x: int16, y: int16, rad: int16, dynlib: gfxLibName.} # Trigon proc trigonColor*(dst: PSurface, x1: int16, y1: int16, x2: int16, y2: int16, - x3: int16, y3: int16, color: Uint32): cint{.cdecl, + x3: int16, y3: int16, color: uint32): cint{.cdecl, importc: "trigonColor", dynlib: gfxLibName.} proc trigonRGBA*(dst: PSurface, x1: int16, y1: int16, x2: int16, y2: int16, x3: int16, y3: int16, r: byte, g: byte, b: byte, a: byte): cint{. cdecl, importc: "trigonRGBA", dynlib: gfxLibName.} # AA-Trigon proc aatrigonColor*(dst: PSurface, x1: int16, y1: int16, x2: int16, - y2: int16, x3: int16, y3: int16, color: Uint32): cint{. + y2: int16, x3: int16, y3: int16, color: uint32): cint{. cdecl, importc: "aatrigonColor", dynlib: gfxLibName.} proc aatrigonRGBA*(dst: PSurface, x1: int16, y1: int16, x2: int16, y2: int16, x3: int16, y3: int16, r: byte, g: byte, @@ -197,7 +197,7 @@ proc aatrigonRGBA*(dst: PSurface, x1: int16, y1: int16, x2: int16, dynlib: gfxLibName.} # Filled Trigon proc filledTrigonColor*(dst: PSurface, x1: int16, y1: int16, x2: int16, - y2: int16, x3: int16, y3: int16, color: Uint32): cint{. + y2: int16, x3: int16, y3: int16, color: uint32): cint{. cdecl, importc: "filledTrigonColor", dynlib: gfxLibName.} proc filledTrigonRGBA*(dst: PSurface, x1: int16, y1: int16, x2: int16, y2: int16, x3: int16, y3: int16, r: byte, g: byte, @@ -205,21 +205,21 @@ proc filledTrigonRGBA*(dst: PSurface, x1: int16, y1: int16, x2: int16, importc: "filledTrigonRGBA", dynlib: gfxLibName.} # Polygon proc polygonColor*(dst: PSurface, vx: ptr int16, vy: ptr int16, n: cint, - color: Uint32): cint{.cdecl, importc: "polygonColor", + color: uint32): cint{.cdecl, importc: "polygonColor", dynlib: gfxLibName.} proc polygonRGBA*(dst: PSurface, vx: ptr int16, vy: ptr int16, n: cint, r: byte, g: byte, b: byte, a: byte): cint{.cdecl, importc: "polygonRGBA", dynlib: gfxLibName.} # AA-Polygon proc aapolygonColor*(dst: PSurface, vx: ptr int16, vy: ptr int16, n: cint, - color: Uint32): cint{.cdecl, importc: "aapolygonColor", + color: uint32): cint{.cdecl, importc: "aapolygonColor", dynlib: gfxLibName.} proc aapolygonRGBA*(dst: PSurface, vx: ptr int16, vy: ptr int16, n: cint, r: byte, g: byte, b: byte, a: byte): cint{.cdecl, importc: "aapolygonRGBA", dynlib: gfxLibName.} # Filled Polygon proc filledPolygonColor*(dst: PSurface, vx: ptr int16, vy: ptr int16, n: cint, - color: Uint32): cint{.cdecl, + color: uint32): cint{.cdecl, importc: "filledPolygonColor", dynlib: gfxLibName.} proc filledPolygonRGBA*(dst: PSurface, vx: ptr int16, vy: ptr int16, n: cint, r: byte, g: byte, b: byte, a: byte): cint{.cdecl, @@ -227,23 +227,23 @@ proc filledPolygonRGBA*(dst: PSurface, vx: ptr int16, vy: ptr int16, n: cint, # Bezier # s = number of steps proc bezierColor*(dst: PSurface, vx: ptr int16, vy: ptr int16, n: cint, s: cint, - color: Uint32): cint{.cdecl, importc: "bezierColor", + color: uint32): cint{.cdecl, importc: "bezierColor", dynlib: gfxLibName.} proc bezierRGBA*(dst: PSurface, vx: ptr int16, vy: ptr int16, n: cint, s: cint, r: byte, g: byte, b: byte, a: byte): cint{.cdecl, importc: "bezierRGBA", dynlib: gfxLibName.} # Characters/Strings -proc characterColor*(dst: PSurface, x: int16, y: int16, c: char, color: Uint32): cint{. +proc characterColor*(dst: PSurface, x: int16, y: int16, c: char, color: uint32): cint{. cdecl, importc: "characterColor", dynlib: gfxLibName.} proc characterRGBA*(dst: PSurface, x: int16, y: int16, c: char, r: byte, g: byte, b: byte, a: byte): cint{.cdecl, importc: "characterRGBA", dynlib: gfxLibName.} -proc stringColor*(dst: PSurface, x: int16, y: int16, c: cstring, color: Uint32): cint{. +proc stringColor*(dst: PSurface, x: int16, y: int16, c: cstring, color: uint32): cint{. cdecl, importc: "stringColor", dynlib: gfxLibName.} proc stringRGBA*(dst: PSurface, x: int16, y: int16, c: cstring, r: byte, g: byte, b: byte, a: byte): cint{.cdecl, importc: "stringRGBA", dynlib: gfxLibName.} -proc gfxPrimitivesSetFont*(fontdata: Pointer, cw: cint, ch: cint){.cdecl, +proc gfxPrimitivesSetFont*(fontdata: pointer, cw: cint, ch: cint){.cdecl, importc: "gfxPrimitivesSetFont", dynlib: gfxLibName.} # # @@ -271,132 +271,132 @@ proc imageFilterMMXon*(){.cdecl, importc: "SDL_imageFilterMMXon", # -1 Error (internal error, parameter error) # # SDL_imageFilterAdd: D = saturation255(S1 + S2) -proc imageFilterAdd*(Src1: cstring, Src2: cstring, Dest: cstring, len: cint): cint{. +proc imageFilterAdd*(src1: cstring, src2: cstring, dest: cstring, len: cint): cint{. cdecl, importc: "SDL_imageFilterAdd", dynlib: gfxLibName.} # SDL_imageFilterMean: D = S1/2 + S2/2 -proc imageFilterMean*(Src1: cstring, Src2: cstring, Dest: cstring, len: cint): cint{. +proc imageFilterMean*(src1: cstring, src2: cstring, dest: cstring, len: cint): cint{. cdecl, importc: "SDL_imageFilterMean", dynlib: gfxLibName.} # SDL_imageFilterSub: D = saturation0(S1 - S2) -proc imageFilterSub*(Src1: cstring, Src2: cstring, Dest: cstring, len: cint): cint{. +proc imageFilterSub*(src1: cstring, src2: cstring, dest: cstring, len: cint): cint{. cdecl, importc: "SDL_imageFilterSub", dynlib: gfxLibName.} # SDL_imageFilterAbsDiff: D = | S1 - S2 | -proc imageFilterAbsDiff*(Src1: cstring, Src2: cstring, Dest: cstring, len: cint): cint{. +proc imageFilterAbsDiff*(src1: cstring, src2: cstring, dest: cstring, len: cint): cint{. cdecl, importc: "SDL_imageFilterAbsDiff", dynlib: gfxLibName.} # SDL_imageFilterMult: D = saturation(S1 * S2) -proc imageFilterMult*(Src1: cstring, Src2: cstring, Dest: cstring, len: cint): cint{. +proc imageFilterMult*(src1: cstring, src2: cstring, dest: cstring, len: cint): cint{. cdecl, importc: "SDL_imageFilterMult", dynlib: gfxLibName.} # SDL_imageFilterMultNor: D = S1 * S2 (non-MMX) -proc imageFilterMultNor*(Src1: cstring, Src2: cstring, Dest: cstring, len: cint): cint{. +proc imageFilterMultNor*(src1: cstring, src2: cstring, dest: cstring, len: cint): cint{. cdecl, importc: "SDL_imageFilterMultNor", dynlib: gfxLibName.} # SDL_imageFilterMultDivby2: D = saturation255(S1/2 * S2) -proc imageFilterMultDivby2*(Src1: cstring, Src2: cstring, Dest: cstring, +proc imageFilterMultDivby2*(src1: cstring, src2: cstring, dest: cstring, len: cint): cint{.cdecl, importc: "SDL_imageFilterMultDivby2", dynlib: gfxLibName.} # SDL_imageFilterMultDivby4: D = saturation255(S1/2 * S2/2) -proc imageFilterMultDivby4*(Src1: cstring, Src2: cstring, Dest: cstring, +proc imageFilterMultDivby4*(src1: cstring, src2: cstring, dest: cstring, len: cint): cint{.cdecl, importc: "SDL_imageFilterMultDivby4", dynlib: gfxLibName.} # SDL_imageFilterBitAnd: D = S1 & S2 -proc imageFilterBitAnd*(Src1: cstring, Src2: cstring, Dest: cstring, len: cint): cint{. +proc imageFilterBitAnd*(src1: cstring, src2: cstring, dest: cstring, len: cint): cint{. cdecl, importc: "SDL_imageFilterBitAnd", dynlib: gfxLibName.} # SDL_imageFilterBitOr: D = S1 | S2 -proc imageFilterBitOr*(Src1: cstring, Src2: cstring, Dest: cstring, len: cint): cint{. +proc imageFilterBitOr*(src1: cstring, src2: cstring, dest: cstring, len: cint): cint{. cdecl, importc: "SDL_imageFilterBitOr", dynlib: gfxLibName.} # SDL_imageFilterDiv: D = S1 / S2 (non-MMX) -proc imageFilterDiv*(Src1: cstring, Src2: cstring, Dest: cstring, len: cint): cint{. +proc imageFilterDiv*(src1: cstring, src2: cstring, dest: cstring, len: cint): cint{. cdecl, importc: "SDL_imageFilterDiv", dynlib: gfxLibName.} # SDL_imageFilterBitNegation: D = !S -proc imageFilterBitNegation*(Src1: cstring, Dest: cstring, len: cint): cint{. +proc imageFilterBitNegation*(src1: cstring, dest: cstring, len: cint): cint{. cdecl, importc: "SDL_imageFilterBitNegation", dynlib: gfxLibName.} # SDL_imageFilterAddByte: D = saturation255(S + C) -proc imageFilterAddByte*(Src1: cstring, Dest: cstring, len: cint, C: char): cint{. +proc imageFilterAddByte*(src1: cstring, dest: cstring, len: cint, c: char): cint{. cdecl, importc: "SDL_imageFilterAddByte", dynlib: gfxLibName.} # SDL_imageFilterAddUint: D = saturation255(S + (uint)C) -proc imageFilterAddUint*(Src1: cstring, Dest: cstring, len: cint, C: cint): cint{. +proc imageFilterAddUint*(src1: cstring, dest: cstring, len: cint, c: cint): cint{. cdecl, importc: "SDL_imageFilterAddUint", dynlib: gfxLibName.} # SDL_imageFilterAddByteToHalf: D = saturation255(S/2 + C) -proc imageFilterAddByteToHalf*(Src1: cstring, Dest: cstring, len: cint, C: char): cint{. +proc imageFilterAddByteToHalf*(src1: cstring, dest: cstring, len: cint, c: char): cint{. cdecl, importc: "SDL_imageFilterAddByteToHalf", dynlib: gfxLibName.} # SDL_imageFilterSubByte: D = saturation0(S - C) -proc imageFilterSubByte*(Src1: cstring, Dest: cstring, len: cint, C: char): cint{. +proc imageFilterSubByte*(src1: cstring, dest: cstring, len: cint, c: char): cint{. cdecl, importc: "SDL_imageFilterSubByte", dynlib: gfxLibName.} # SDL_imageFilterSubUint: D = saturation0(S - (uint)C) -proc imageFilterSubUint*(Src1: cstring, Dest: cstring, len: cint, C: cint): cint{. +proc imageFilterSubUint*(src1: cstring, dest: cstring, len: cint, c: cint): cint{. cdecl, importc: "SDL_imageFilterSubUint", dynlib: gfxLibName.} # SDL_imageFilterShiftRight: D = saturation0(S >> N) -proc imageFilterShiftRight*(Src1: cstring, Dest: cstring, len: cint, N: char): cint{. +proc imageFilterShiftRight*(src1: cstring, dest: cstring, len: cint, n: char): cint{. cdecl, importc: "SDL_imageFilterShiftRight", dynlib: gfxLibName.} # SDL_imageFilterShiftRightUint: D = saturation0((uint)S >> N) -proc imageFilterShiftRightUint*(Src1: cstring, Dest: cstring, len: cint, N: char): cint{. +proc imageFilterShiftRightUint*(src1: cstring, dest: cstring, len: cint, n: char): cint{. cdecl, importc: "SDL_imageFilterShiftRightUint", dynlib: gfxLibName.} # SDL_imageFilterMultByByte: D = saturation255(S * C) -proc imageFilterMultByByte*(Src1: cstring, Dest: cstring, len: cint, C: char): cint{. +proc imageFilterMultByByte*(src1: cstring, dest: cstring, len: cint, c: char): cint{. cdecl, importc: "SDL_imageFilterMultByByte", dynlib: gfxLibName.} # SDL_imageFilterShiftRightAndMultByByte: D = saturation255((S >> N) * C) -proc imageFilterShiftRightAndMultByByte*(Src1: cstring, Dest: cstring, len: cint, - N: char, C: char): cint{.cdecl, +proc imageFilterShiftRightAndMultByByte*(src1: cstring, dest: cstring, len: cint, + n: char, c: char): cint{.cdecl, importc: "SDL_imageFilterShiftRightAndMultByByte", dynlib: gfxLibName.} # SDL_imageFilterShiftLeftByte: D = (S << N) -proc imageFilterShiftLeftByte*(Src1: cstring, Dest: cstring, len: cint, N: char): cint{. +proc imageFilterShiftLeftByte*(src1: cstring, dest: cstring, len: cint, n: char): cint{. cdecl, importc: "SDL_imageFilterShiftLeftByte", dynlib: gfxLibName.} # SDL_imageFilterShiftLeftUint: D = ((uint)S << N) -proc imageFilterShiftLeftUint*(Src1: cstring, Dest: cstring, len: cint, N: char): cint{. +proc imageFilterShiftLeftUint*(src1: cstring, dest: cstring, len: cint, n: char): cint{. cdecl, importc: "SDL_imageFilterShiftLeftUint", dynlib: gfxLibName.} # SDL_imageFilterShiftLeft: D = saturation255(S << N) -proc imageFilterShiftLeft*(Src1: cstring, Dest: cstring, len: cint, N: char): cint{. +proc imageFilterShiftLeft*(src1: cstring, dest: cstring, len: cint, n: char): cint{. cdecl, importc: "SDL_imageFilterShiftLeft", dynlib: gfxLibName.} # SDL_imageFilterBinarizeUsingThreshold: D = S >= T ? 255:0 -proc imageFilterBinarizeUsingThreshold*(Src1: cstring, Dest: cstring, len: cint, - T: char): cint{.cdecl, +proc imageFilterBinarizeUsingThreshold*(src1: cstring, dest: cstring, len: cint, + t: char): cint{.cdecl, importc: "SDL_imageFilterBinarizeUsingThreshold", dynlib: gfxLibName.} # SDL_imageFilterClipToRange: D = (S >= Tmin) & (S <= Tmax) 255:0 -proc imageFilterClipToRange*(Src1: cstring, Dest: cstring, len: cint, Tmin: int8, - Tmax: int8): cint{.cdecl, +proc imageFilterClipToRange*(src1: cstring, dest: cstring, len: cint, tmin: int8, + tmax: int8): cint{.cdecl, importc: "SDL_imageFilterClipToRange", dynlib: gfxLibName.} # SDL_imageFilterNormalizeLinear: D = saturation255((Nmax - Nmin)/(Cmax - Cmin)*(S - Cmin) + Nmin) -proc imageFilterNormalizeLinear*(Src1: cstring, Dest: cstring, len: cint, - Cmin: cint, Cmax: cint, Nmin: cint, Nmax: cint): cint{. +proc imageFilterNormalizeLinear*(src1: cstring, dest: cstring, len: cint, + cmin: cint, cmax: cint, nmin: cint, nmax: cint): cint{. cdecl, importc: "SDL_imageFilterNormalizeLinear", dynlib: gfxLibName.} # !!! NO C-ROUTINE FOR THESE FUNCTIONS YET !!! # SDL_imageFilterConvolveKernel3x3Divide: Dij = saturation0and255( ... ) -proc imageFilterConvolveKernel3x3Divide*(Src: cstring, Dest: cstring, rows: cint, - columns: cint, Kernel: pointer, Divisor: int8): cint{.cdecl, +proc imageFilterConvolveKernel3x3Divide*(src: cstring, dest: cstring, rows: cint, + columns: cint, kernel: pointer, divisor: int8): cint{.cdecl, importc: "SDL_imageFilterConvolveKernel3x3Divide", dynlib: gfxLibName.} # SDL_imageFilterConvolveKernel5x5Divide: Dij = saturation0and255( ... ) -proc imageFilterConvolveKernel5x5Divide*(Src: cstring, Dest: cstring, rows: cint, - columns: cint, Kernel: pointer, Divisor: int8): cint{.cdecl, +proc imageFilterConvolveKernel5x5Divide*(src: cstring, dest: cstring, rows: cint, + columns: cint, kernel: pointer, divisor: int8): cint{.cdecl, importc: "SDL_imageFilterConvolveKernel5x5Divide", dynlib: gfxLibName.} # SDL_imageFilterConvolveKernel7x7Divide: Dij = saturation0and255( ... ) -proc imageFilterConvolveKernel7x7Divide*(Src: cstring, Dest: cstring, rows: cint, - columns: cint, Kernel: pointer, Divisor: int8): cint{.cdecl, +proc imageFilterConvolveKernel7x7Divide*(src: cstring, dest: cstring, rows: cint, + columns: cint, kernel: pointer, divisor: int8): cint{.cdecl, importc: "SDL_imageFilterConvolveKernel7x7Divide", dynlib: gfxLibName.} # SDL_imageFilterConvolveKernel9x9Divide: Dij = saturation0and255( ... ) -proc imageFilterConvolveKernel9x9Divide*(Src: cstring, Dest: cstring, rows: cint, - columns: cint, Kernel: pointer, Divisor: int8): cint{.cdecl, +proc imageFilterConvolveKernel9x9Divide*(src: cstring, dest: cstring, rows: cint, + columns: cint, kernel: pointer, divisor: int8): cint{.cdecl, importc: "SDL_imageFilterConvolveKernel9x9Divide", dynlib: gfxLibName.} # SDL_imageFilterConvolveKernel3x3ShiftRight: Dij = saturation0and255( ... ) -proc imageFilterConvolveKernel3x3ShiftRight*(Src: cstring, Dest: cstring, - rows: cint, columns: cint, Kernel: pointer, NRightShift: char): cint{.cdecl, +proc imageFilterConvolveKernel3x3ShiftRight*(src: cstring, dest: cstring, + rows: cint, columns: cint, kernel: pointer, nRightShift: char): cint{.cdecl, importc: "SDL_imageFilterConvolveKernel3x3ShiftRight", dynlib: gfxLibName.} # SDL_imageFilterConvolveKernel5x5ShiftRight: Dij = saturation0and255( ... ) -proc imageFilterConvolveKernel5x5ShiftRight*(Src: cstring, Dest: cstring, - rows: cint, columns: cint, Kernel: pointer, NRightShift: char): cint{.cdecl, +proc imageFilterConvolveKernel5x5ShiftRight*(src: cstring, dest: cstring, + rows: cint, columns: cint, kernel: pointer, nRightShift: char): cint{.cdecl, importc: "SDL_imageFilterConvolveKernel5x5ShiftRight", dynlib: gfxLibName.} # SDL_imageFilterConvolveKernel7x7ShiftRight: Dij = saturation0and255( ... ) -proc imageFilterConvolveKernel7x7ShiftRight*(Src: cstring, Dest: cstring, - rows: cint, columns: cint, Kernel: pointer, NRightShift: char): cint{.cdecl, +proc imageFilterConvolveKernel7x7ShiftRight*(src: cstring, dest: cstring, + rows: cint, columns: cint, kernel: pointer, nRightShift: char): cint{.cdecl, importc: "SDL_imageFilterConvolveKernel7x7ShiftRight", dynlib: gfxLibName.} # SDL_imageFilterConvolveKernel9x9ShiftRight: Dij = saturation0and255( ... ) -proc imageFilterConvolveKernel9x9ShiftRight*(Src: cstring, Dest: cstring, - rows: cint, columns: cint, Kernel: pointer, NRightShift: char): cint{.cdecl, +proc imageFilterConvolveKernel9x9ShiftRight*(src: cstring, dest: cstring, + rows: cint, columns: cint, kernel: pointer, nRightShift: char): cint{.cdecl, importc: "SDL_imageFilterConvolveKernel9x9ShiftRight", dynlib: gfxLibName.} # SDL_imageFilterSobelX: Dij = saturation255( ... ) -proc imageFilterSobelX*(Src: cstring, Dest: cstring, rows: cint, columns: cint): cint{. +proc imageFilterSobelX*(src: cstring, dest: cstring, rows: cint, columns: cint): cint{. cdecl, importc: "SDL_imageFilterSobelX", dynlib: gfxLibName.} # SDL_imageFilterSobelXShiftRight: Dij = saturation255( ... ) -proc imageFilterSobelXShiftRight*(Src: cstring, Dest: cstring, rows: cint, - columns: cint, NRightShift: char): cint{.cdecl, +proc imageFilterSobelXShiftRight*(src: cstring, dest: cstring, rows: cint, + columns: cint, nRightShift: char): cint{.cdecl, importc: "SDL_imageFilterSobelXShiftRight", dynlib: gfxLibName.} # Align/restore stack to 32 byte boundary -- Functionality untested! -- proc imageFilterAlignStack*(){.cdecl, importc: "SDL_imageFilterAlignStack", diff --git a/lib/wrappers/sdl/sdl_image.nim b/lib/wrappers/sdl/sdl_image.nim index 2bd731506..9c56e6a46 100644 --- a/lib/wrappers/sdl/sdl_image.nim +++ b/lib/wrappers/sdl/sdl_image.nim @@ -148,13 +148,13 @@ const # This macro can be used to fill a version structure with the compile-time # version of the SDL_image library. -proc IMAGE_VERSION*(X: var TVersion) +proc imageVersion*(x: var Tversion) # This function gets the version of the dynamically linked SDL_image library. # it should NOT be used to fill a version structure, instead you should # use the SDL_IMAGE_VERSION() macro. # -proc IMG_Linked_Version*(): Pversion{.importc: "IMG_Linked_Version", - dynlib: ImageLibName.} +proc imgLinkedVersion*(): Pversion{.importc: "IMG_Linked_Version", + dynlib: ImageLibName.} # Load an image from an SDL data source. # The 'type' may be one of: "BMP", "GIF", "PNG", etc. # @@ -170,74 +170,74 @@ const IMG_INIT_TIF* = 0x00000004 IMG_INIT_WEBP* = 0x00000008 -proc IMG_Init*(flags: cint): int {.cdecl, importc: "IMG_Init", +proc imgInit*(flags: cint): int {.cdecl, importc: "IMG_Init", dynlib: ImageLibName.} -proc IMG_Quit*() {.cdecl, importc: "IMG_Quit", +proc imgQuit*() {.cdecl, importc: "IMG_Quit", dynlib: ImageLibName.} -proc IMG_LoadTyped_RW*(src: PRWops, freesrc: cint, theType: cstring): PSurface{. +proc imgLoadTypedRW*(src: PRWops, freesrc: cint, theType: cstring): PSurface{. cdecl, importc: "IMG_LoadTyped_RW", dynlib: ImageLibName.} # Convenience functions -proc IMG_Load*(theFile: cstring): PSurface{.cdecl, importc: "IMG_Load", +proc imgLoad*(theFile: cstring): PSurface{.cdecl, importc: "IMG_Load", dynlib: ImageLibName.} -proc IMG_Load_RW*(src: PRWops, freesrc: cint): PSurface{.cdecl, +proc imgLoadRW*(src: PRWops, freesrc: cint): PSurface{.cdecl, importc: "IMG_Load_RW", dynlib: ImageLibName.} # Invert the alpha of a surface for use with OpenGL # This function is now a no-op, and only provided for backwards compatibility. -proc IMG_InvertAlpha*(theOn: cint): cint{.cdecl, importc: "IMG_InvertAlpha", +proc imgInvertAlpha*(theOn: cint): cint{.cdecl, importc: "IMG_InvertAlpha", dynlib: ImageLibName.} # Functions to detect a file type, given a seekable source -proc IMG_isBMP*(src: PRWops): cint{.cdecl, importc: "IMG_isBMP", +proc imgIsBMP*(src: PRWops): cint{.cdecl, importc: "IMG_isBMP", dynlib: ImageLibName.} -proc IMG_isGIF*(src: PRWops): cint{.cdecl, importc: "IMG_isGIF", +proc imgIsGIF*(src: PRWops): cint{.cdecl, importc: "IMG_isGIF", dynlib: ImageLibName.} -proc IMG_isJPG*(src: PRWops): cint{.cdecl, importc: "IMG_isJPG", +proc imgIsJPG*(src: PRWops): cint{.cdecl, importc: "IMG_isJPG", dynlib: ImageLibName.} -proc IMG_isLBM*(src: PRWops): cint{.cdecl, importc: "IMG_isLBM", +proc imgIsLBM*(src: PRWops): cint{.cdecl, importc: "IMG_isLBM", dynlib: ImageLibName.} -proc IMG_isPCX*(src: PRWops): cint{.cdecl, importc: "IMG_isPCX", +proc imgIsPCX*(src: PRWops): cint{.cdecl, importc: "IMG_isPCX", dynlib: ImageLibName.} -proc IMG_isPNG*(src: PRWops): cint{.cdecl, importc: "IMG_isPNG", +proc imgIsPNG*(src: PRWops): cint{.cdecl, importc: "IMG_isPNG", dynlib: ImageLibName.} -proc IMG_isPNM*(src: PRWops): cint{.cdecl, importc: "IMG_isPNM", +proc imgIsPNM*(src: PRWops): cint{.cdecl, importc: "IMG_isPNM", dynlib: ImageLibName.} -proc IMG_isTIF*(src: PRWops): cint{.cdecl, importc: "IMG_isTIF", +proc imgIsTIF*(src: PRWops): cint{.cdecl, importc: "IMG_isTIF", dynlib: ImageLibName.} -proc IMG_isXCF*(src: PRWops): cint{.cdecl, importc: "IMG_isXCF", +proc imgIsXCF*(src: PRWops): cint{.cdecl, importc: "IMG_isXCF", dynlib: ImageLibName.} -proc IMG_isXPM*(src: PRWops): cint{.cdecl, importc: "IMG_isXPM", +proc imgIsXPM*(src: PRWops): cint{.cdecl, importc: "IMG_isXPM", dynlib: ImageLibName.} -proc IMG_isXV*(src: PRWops): cint{.cdecl, importc: "IMG_isXV", +proc imgIsXV*(src: PRWops): cint{.cdecl, importc: "IMG_isXV", dynlib: ImageLibName.} # Individual loading functions -proc IMG_LoadBMP_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadBMP_RW", +proc imgLoadBMP_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadBMP_RW", dynlib: ImageLibName.} -proc IMG_LoadGIF_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadGIF_RW", +proc imgLoadGIF_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadGIF_RW", dynlib: ImageLibName.} -proc IMG_LoadJPG_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadJPG_RW", +proc imgLoadJPG_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadJPG_RW", dynlib: ImageLibName.} -proc IMG_LoadLBM_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadLBM_RW", +proc imgLoadLBM_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadLBM_RW", dynlib: ImageLibName.} -proc IMG_LoadPCX_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadPCX_RW", +proc imgLoadPCX_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadPCX_RW", dynlib: ImageLibName.} -proc IMG_LoadPNM_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadPNM_RW", +proc imgLoadPNM_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadPNM_RW", dynlib: ImageLibName.} -proc IMG_LoadPNG_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadPNG_RW", +proc imgLoadPNG_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadPNG_RW", dynlib: ImageLibName.} -proc IMG_LoadTGA_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadTGA_RW", +proc imgLoadTGA_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadTGA_RW", dynlib: ImageLibName.} -proc IMG_LoadTIF_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadTIF_RW", +proc imgLoadTIF_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadTIF_RW", dynlib: ImageLibName.} -proc IMG_LoadXCF_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadXCF_RW", +proc imgLoadXCF_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadXCF_RW", dynlib: ImageLibName.} -proc IMG_LoadXPM_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadXPM_RW", +proc imgLoadXPM_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadXPM_RW", dynlib: ImageLibName.} -proc IMG_LoadXV_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadXV_RW", +proc imgLoadXV_RW*(src: PRWops): PSurface{.cdecl, importc: "IMG_LoadXV_RW", dynlib: ImageLibName.} -proc IMG_ReadXPMFromArray*(xpm: cstringArray): PSurface{.cdecl, +proc imgReadXPMFromArray*(xpm: cstringArray): PSurface{.cdecl, importc: "IMG_ReadXPMFromArray", dynlib: ImageLibName.} -proc IMAGE_VERSION(X: var TVersion) = - X.major = IMAGE_MAJOR_VERSION - X.minor = IMAGE_MINOR_VERSION - X.patch = IMAGE_PATCHLEVEL +proc imageVersion(x: var Tversion) = + x.major = IMAGE_MAJOR_VERSION + x.minor = IMAGE_MINOR_VERSION + x.patch = IMAGE_PATCHLEVEL diff --git a/lib/wrappers/sdl/sdl_mixer.nim b/lib/wrappers/sdl/sdl_mixer.nim index 9199a9271..33a71508a 100644 --- a/lib/wrappers/sdl/sdl_mixer.nim +++ b/lib/wrappers/sdl/sdl_mixer.nim @@ -196,7 +196,7 @@ type #music_cmd.h types PWAVStream* = ptr TWAVStream TWAVStream*{.final.} = object #playmidi.h types - wavefp*: Pointer + wavefp*: pointer start*: int32 stop*: int32 cvt*: TAudioCVT @@ -223,8 +223,8 @@ type #music_cmd.h types volume*: int32 #vf: OggVorbis_File; section*: int32 cvt*: TAudioCVT - len_available*: int32 - snd_available*: pointer + lenAvailable*: int32 + sndAvailable*: pointer TErrorEnum* = enum MMERR_OPENING_FILE, MMERR_OUT_OF_MEMORY, MMERR_DYNAMIC_LINKING, @@ -255,7 +255,7 @@ type #music_cmd.h types TChunk*{.final.} = object allocated*: cint abuf*: pointer - alen*: Uint32 + alen*: uint32 volume*: byte # Per-sample volume, 0-128 TFading* = enum @@ -272,18 +272,18 @@ type #music_cmd.h types # fade_steps : integer; # error : integer; - TMixFunction* = proc (udata, stream: pointer, length: cint): Pointer{. + TMixFunction* = proc (udata, stream: pointer, length: cint): pointer{. cdecl.} # This macro can be used to fill a version structure with the compile-time # version of the SDL_mixer library. -proc VERSION*(X: var sdl.TVersion) +proc version*(x: var sdl.Tversion) # This function gets the version of the dynamically linked SDL_mixer library. # It should NOT be used to fill a version structure, instead you should use the # SDL_MIXER_VERSION() macro. -proc Linked_Version*(): sdl.Pversion{.cdecl, importc: "Mix_Linked_Version", +proc linkedVersion*(): sdl.Pversion{.cdecl, importc: "Mix_Linked_Version", dynlib: MixerLibName.} # Open the mixer with a certain audio format -proc OpenAudio*(frequency: cint, format: Uint16, channels: cint, +proc openAudio*(frequency: cint, format: uint16, channels: cint, chunksize: cint): cint{.cdecl, importc: "Mix_OpenAudio", dynlib: MixerLibName.} # Dynamically change the number of channels managed by the mixer. @@ -291,194 +291,194 @@ proc OpenAudio*(frequency: cint, format: Uint16, channels: cint, # stopped. # This function returns the new number of allocated channels. # -proc AllocateChannels*(numchannels: cint): cint{.cdecl, +proc allocateChannels*(numchannels: cint): cint{.cdecl, importc: "Mix_AllocateChannels", dynlib: MixerLibName.} # Find out what the actual audio device parameters are. # This function returns 1 if the audio has been opened, 0 otherwise. # -proc QuerySpec*(frequency: var cint, format: var Uint16, channels: var cint): cint{. +proc querySpec*(frequency: var cint, format: var uint16, channels: var cint): cint{. cdecl, importc: "Mix_QuerySpec", dynlib: MixerLibName.} # Load a wave file or a music (.mod .s3m .it .xm) file -proc LoadWAV_RW*(src: PRWops, freesrc: cint): PChunk{.cdecl, +proc loadWAV_RW*(src: PRWops, freesrc: cint): PChunk{.cdecl, importc: "Mix_LoadWAV_RW", dynlib: MixerLibName.} -proc LoadWAV*(filename: cstring): PChunk -proc LoadMUS*(filename: cstring): PMusic{.cdecl, importc: "Mix_LoadMUS", +proc loadWAV*(filename: cstring): PChunk +proc loadMUS*(filename: cstring): PMusic{.cdecl, importc: "Mix_LoadMUS", dynlib: MixerLibName.} # Load a wave file of the mixer format from a memory buffer -proc QuickLoad_WAV*(mem: pointer): PChunk{.cdecl, +proc quickLoadWAV*(mem: pointer): PChunk{.cdecl, importc: "Mix_QuickLoad_WAV", dynlib: MixerLibName.} # Free an audio chunk previously loaded -proc FreeChunk*(chunk: PChunk){.cdecl, importc: "Mix_FreeChunk", +proc freeChunk*(chunk: PChunk){.cdecl, importc: "Mix_FreeChunk", dynlib: MixerLibName.} -proc FreeMusic*(music: PMusic){.cdecl, importc: "Mix_FreeMusic", +proc freeMusic*(music: PMusic){.cdecl, importc: "Mix_FreeMusic", dynlib: MixerLibName.} # Find out the music format of a mixer music, or the currently playing # music, if 'music' is NULL. -proc GetMusicType*(music: PMusic): TMusicType{.cdecl, +proc getMusicType*(music: PMusic): TMusicType{.cdecl, importc: "Mix_GetMusicType", dynlib: MixerLibName.} # Set a function that is called after all mixing is performed. # This can be used to provide real-time visual display of the audio stream # or add a custom mixer filter for the stream data. # -proc SetPostMix*(mix_func: TMixFunction, arg: Pointer){.cdecl, +proc setPostMix*(mixFunc: TMixFunction, arg: pointer){.cdecl, importc: "Mix_SetPostMix", dynlib: MixerLibName.} # Add your own music player or additional mixer function. # If 'mix_func' is NULL, the default music player is re-enabled. # -proc HookMusic*(mix_func: TMixFunction, arg: Pointer){.cdecl, +proc hookMusic*(mixFunc: TMixFunction, arg: pointer){.cdecl, importc: "Mix_HookMusic", dynlib: MixerLibName.} # Add your own callback when the music has finished playing. # -proc HookMusicFinished*(music_finished: Pointer){.cdecl, +proc hookMusicFinished*(musicFinished: pointer){.cdecl, importc: "Mix_HookMusicFinished", dynlib: MixerLibName.} # Get a pointer to the user data for the current music hook -proc GetMusicHookData*(): Pointer{.cdecl, importc: "Mix_GetMusicHookData", +proc getMusicHookData*(): pointer{.cdecl, importc: "Mix_GetMusicHookData", dynlib: MixerLibName.} #* Add your own callback when a channel has finished playing. NULL # * to disable callback.* type - TChannel_finished* = proc (channel: cint){.cdecl.} + TChannelFinished* = proc (channel: cint){.cdecl.} -proc ChannelFinished*(channel_finished: TChannel_finished){.cdecl, +proc channelFinished*(channelFinished: TChannelFinished){.cdecl, importc: "Mix_ChannelFinished", dynlib: MixerLibName.} const CHANNEL_POST* = - 2 type - TEffectFunc* = proc (chan: cint, stream: Pointer, length: cint, - udata: Pointer): Pointer{.cdecl.} - TEffectDone* = proc (chan: cint, udata: Pointer): Pointer{.cdecl.} -proc RegisterEffect*(chan: cint, f: TEffectFunc, d: TEffectDone, - arg: Pointer): cint{.cdecl, + TEffectFunc* = proc (chan: cint, stream: pointer, length: cint, + udata: pointer): pointer{.cdecl.} + TEffectDone* = proc (chan: cint, udata: pointer): pointer{.cdecl.} +proc registerEffect*(chan: cint, f: TEffectFunc, d: TEffectDone, + arg: pointer): cint{.cdecl, importc: "Mix_RegisterEffect", dynlib: MixerLibName.} -proc UnregisterEffect*(channel: cint, f: TEffectFunc): cint{.cdecl, +proc unregisterEffect*(channel: cint, f: TEffectFunc): cint{.cdecl, importc: "Mix_UnregisterEffect", dynlib: MixerLibName.} -proc UnregisterAllEffects*(channel: cint): cint{.cdecl, +proc unregisterAllEffects*(channel: cint): cint{.cdecl, importc: "Mix_UnregisterAllEffects", dynlib: MixerLibName.} const EFFECTSMAXSPEED* = "MIX_EFFECTSMAXSPEED" -proc SetPanning*(channel: cint, left: byte, right: byte): cint{.cdecl, +proc setPanning*(channel: cint, left: byte, right: byte): cint{.cdecl, importc: "Mix_SetPanning", dynlib: MixerLibName.} -proc SetPosition*(channel: cint, angle: int16, distance: byte): cint{.cdecl, +proc setPosition*(channel: cint, angle: int16, distance: byte): cint{.cdecl, importc: "Mix_SetPosition", dynlib: MixerLibName.} -proc SetDistance*(channel: cint, distance: byte): cint{.cdecl, +proc setDistance*(channel: cint, distance: byte): cint{.cdecl, importc: "Mix_SetDistance", dynlib: MixerLibName.} -proc SetReverseStereo*(channel: cint, flip: cint): cint{.cdecl, +proc setReverseStereo*(channel: cint, flip: cint): cint{.cdecl, importc: "Mix_SetReverseStereo", dynlib: MixerLibName.} -proc ReserveChannels*(num: cint): cint{.cdecl, importc: "Mix_ReserveChannels", +proc reserveChannels*(num: cint): cint{.cdecl, importc: "Mix_ReserveChannels", dynlib: MixerLibName.} -proc GroupChannel*(which: cint, tag: cint): cint{.cdecl, +proc groupChannel*(which: cint, tag: cint): cint{.cdecl, importc: "Mix_GroupChannel", dynlib: MixerLibName.} -proc GroupChannels*(`from`: cint, `to`: cint, tag: cint): cint{.cdecl, +proc groupChannels*(`from`: cint, `to`: cint, tag: cint): cint{.cdecl, importc: "Mix_GroupChannels", dynlib: MixerLibName.} -proc GroupAvailable*(tag: cint): cint{.cdecl, importc: "Mix_GroupAvailable", +proc groupAvailable*(tag: cint): cint{.cdecl, importc: "Mix_GroupAvailable", dynlib: MixerLibName.} -proc GroupCount*(tag: cint): cint{.cdecl, importc: "Mix_GroupCount", +proc groupCount*(tag: cint): cint{.cdecl, importc: "Mix_GroupCount", dynlib: MixerLibName.} -proc GroupOldest*(tag: cint): cint{.cdecl, importc: "Mix_GroupOldest", +proc groupOldest*(tag: cint): cint{.cdecl, importc: "Mix_GroupOldest", dynlib: MixerLibName.} -proc GroupNewer*(tag: cint): cint{.cdecl, importc: "Mix_GroupNewer", +proc groupNewer*(tag: cint): cint{.cdecl, importc: "Mix_GroupNewer", dynlib: MixerLibName.} -proc PlayChannelTimed*(channel: cint, chunk: PChunk, loops: cint, +proc playChannelTimed*(channel: cint, chunk: PChunk, loops: cint, ticks: cint): cint{.cdecl, importc: "Mix_PlayChannelTimed", dynlib: MixerLibName.} -proc PlayChannel*(channel: cint, chunk: PChunk, loops: cint): cint -proc PlayMusic*(music: PMusic, loops: cint): cint{.cdecl, +proc playChannel*(channel: cint, chunk: PChunk, loops: cint): cint +proc playMusic*(music: PMusic, loops: cint): cint{.cdecl, importc: "Mix_PlayMusic", dynlib: MixerLibName.} -proc FadeInMusic*(music: PMusic, loops: cint, ms: cint): cint{.cdecl, +proc fadeInMusic*(music: PMusic, loops: cint, ms: cint): cint{.cdecl, importc: "Mix_FadeInMusic", dynlib: MixerLibName.} -proc FadeInChannelTimed*(channel: cint, chunk: PChunk, loops: cint, +proc fadeInChannelTimed*(channel: cint, chunk: PChunk, loops: cint, ms: cint, ticks: cint): cint{.cdecl, importc: "Mix_FadeInChannelTimed", dynlib: MixerLibName.} -proc FadeInChannel*(channel: cint, chunk: PChunk, loops: cint, ms: cint): cint +proc fadeInChannel*(channel: cint, chunk: PChunk, loops: cint, ms: cint): cint -proc Volume*(channel: cint, volume: cint): cint{.cdecl, importc: "Mix_Volume", +proc volume*(channel: cint, volume: cint): cint{.cdecl, importc: "Mix_Volume", dynlib: MixerLibName.} -proc VolumeChunk*(chunk: PChunk, volume: cint): cint{.cdecl, +proc volumeChunk*(chunk: PChunk, volume: cint): cint{.cdecl, importc: "Mix_VolumeChunk", dynlib: MixerLibName.} -proc VolumeMusic*(volume: cint): cint{.cdecl, importc: "Mix_VolumeMusic", +proc volumeMusic*(volume: cint): cint{.cdecl, importc: "Mix_VolumeMusic", dynlib: MixerLibName.} -proc HaltChannel*(channel: cint): cint{.cdecl, importc: "Mix_HaltChannel", +proc haltChannel*(channel: cint): cint{.cdecl, importc: "Mix_HaltChannel", dynlib: MixerLibName.} -proc HaltGroup*(tag: cint): cint{.cdecl, importc: "Mix_HaltGroup", +proc haltGroup*(tag: cint): cint{.cdecl, importc: "Mix_HaltGroup", dynlib: MixerLibName.} -proc HaltMusic*(): cint{.cdecl, importc: "Mix_HaltMusic", +proc haltMusic*(): cint{.cdecl, importc: "Mix_HaltMusic", dynlib: MixerLibName.} # Change the expiration delay for a particular channel. # The sample will stop playing after the 'ticks' milliseconds have elapsed, # or remove the expiration if 'ticks' is -1 # -proc ExpireChannel*(channel: cint, ticks: cint): cint{.cdecl, +proc expireChannel*(channel: cint, ticks: cint): cint{.cdecl, importc: "Mix_ExpireChannel", dynlib: MixerLibName.} # Halt a channel, fading it out progressively till it's silent # The ms parameter indicates the number of milliseconds the fading # will take. # -proc FadeOutChannel*(which: cint, ms: cint): cint{.cdecl, +proc fadeOutChannel*(which: cint, ms: cint): cint{.cdecl, importc: "Mix_FadeOutChannel", dynlib: MixerLibName.} -proc FadeOutGroup*(tag: cint, ms: cint): cint{.cdecl, +proc fadeOutGroup*(tag: cint, ms: cint): cint{.cdecl, importc: "Mix_FadeOutGroup", dynlib: MixerLibName.} -proc FadeOutMusic*(ms: cint): cint{.cdecl, importc: "Mix_FadeOutMusic", +proc fadeOutMusic*(ms: cint): cint{.cdecl, importc: "Mix_FadeOutMusic", dynlib: MixerLibName.} # Query the fading status of a channel -proc FadingMusic*(): TFading{.cdecl, importc: "Mix_FadingMusic", +proc fadingMusic*(): TFading{.cdecl, importc: "Mix_FadingMusic", dynlib: MixerLibName.} -proc FadingChannel*(which: cint): TFading{.cdecl, +proc fadingChannel*(which: cint): TFading{.cdecl, importc: "Mix_FadingChannel", dynlib: MixerLibName.} -proc Pause*(channel: cint){.cdecl, importc: "Mix_Pause", dynlib: MixerLibName.} -proc Resume*(channel: cint){.cdecl, importc: "Mix_Resume", +proc pause*(channel: cint){.cdecl, importc: "Mix_Pause", dynlib: MixerLibName.} +proc resume*(channel: cint){.cdecl, importc: "Mix_Resume", dynlib: MixerLibName.} -proc Paused*(channel: cint): cint{.cdecl, importc: "Mix_Paused", +proc paused*(channel: cint): cint{.cdecl, importc: "Mix_Paused", dynlib: MixerLibName.} -proc PauseMusic*(){.cdecl, importc: "Mix_PauseMusic", dynlib: MixerLibName.} -proc ResumeMusic*(){.cdecl, importc: "Mix_ResumeMusic", dynlib: MixerLibName.} -proc RewindMusic*(){.cdecl, importc: "Mix_RewindMusic", dynlib: MixerLibName.} -proc PausedMusic*(): cint{.cdecl, importc: "Mix_PausedMusic", +proc pauseMusic*(){.cdecl, importc: "Mix_PauseMusic", dynlib: MixerLibName.} +proc resumeMusic*(){.cdecl, importc: "Mix_ResumeMusic", dynlib: MixerLibName.} +proc rewindMusic*(){.cdecl, importc: "Mix_RewindMusic", dynlib: MixerLibName.} +proc pausedMusic*(): cint{.cdecl, importc: "Mix_PausedMusic", dynlib: MixerLibName.} -proc SetMusicPosition*(position: float64): cint{.cdecl, +proc setMusicPosition*(position: float64): cint{.cdecl, importc: "Mix_SetMusicPosition", dynlib: MixerLibName.} -proc Playing*(channel: cint): cint{.cdecl, importc: "Mix_Playing", +proc playing*(channel: cint): cint{.cdecl, importc: "Mix_Playing", dynlib: MixerLibName.} -proc PlayingMusic*(): cint{.cdecl, importc: "Mix_PlayingMusic", +proc playingMusic*(): cint{.cdecl, importc: "Mix_PlayingMusic", dynlib: MixerLibName.} -proc SetMusicCMD*(command: cstring): cint{.cdecl, importc: "Mix_SetMusicCMD", +proc setMusicCMD*(command: cstring): cint{.cdecl, importc: "Mix_SetMusicCMD", dynlib: MixerLibName.} -proc SetSynchroValue*(value: cint): cint{.cdecl, +proc setSynchroValue*(value: cint): cint{.cdecl, importc: "Mix_SetSynchroValue", dynlib: MixerLibName.} -proc GetSynchroValue*(): cint{.cdecl, importc: "Mix_GetSynchroValue", +proc getSynchroValue*(): cint{.cdecl, importc: "Mix_GetSynchroValue", dynlib: MixerLibName.} -proc GetChunk*(channel: cint): PChunk{.cdecl, importc: "Mix_GetChunk", +proc getChunk*(channel: cint): PChunk{.cdecl, importc: "Mix_GetChunk", dynlib: MixerLibName.} -proc CloseAudio*(){.cdecl, importc: "Mix_CloseAudio", dynlib: MixerLibName.} +proc closeAudio*(){.cdecl, importc: "Mix_CloseAudio", dynlib: MixerLibName.} -proc VERSION(X: var sdl.Tversion) = - X.major = MAJOR_VERSION - X.minor = MINOR_VERSION - X.patch = PATCHLEVEL +proc version(x: var sdl.Tversion) = + x.major = MAJOR_VERSION + x.minor = MINOR_VERSION + x.patch = PATCHLEVEL -proc LoadWAV(filename: cstring): PChunk = - result = LoadWAV_RW(RWFromFile(filename, "rb"), 1) +proc loadWAV(filename: cstring): PChunk = + result = loadWAV_RW(rWFromFile(filename, "rb"), 1) -proc PlayChannel(channel: cint, chunk: PChunk, loops: cint): cint = - result = PlayChannelTimed(channel, chunk, loops, - 1) +proc playChannel(channel: cint, chunk: PChunk, loops: cint): cint = + result = playChannelTimed(channel, chunk, loops, - 1) -proc FadeInChannel(channel: cint, chunk: PChunk, loops: cint, ms: cint): cint = - result = FadeInChannelTimed(channel, chunk, loops, ms, - 1) +proc fadeInChannel(channel: cint, chunk: PChunk, loops: cint, ms: cint): cint = + result = fadeInChannelTimed(channel, chunk, loops, ms, - 1) diff --git a/lib/wrappers/sdl/sdl_mixer_nosmpeg.nim b/lib/wrappers/sdl/sdl_mixer_nosmpeg.nim index 11f00e0a7..7a8c41af1 100644 --- a/lib/wrappers/sdl/sdl_mixer_nosmpeg.nim +++ b/lib/wrappers/sdl/sdl_mixer_nosmpeg.nim @@ -48,7 +48,7 @@ type #music_cmd.h types PWAVStream* = ptr TWAVStream TWAVStream*{.final.} = object #playmidi.h types - wavefp*: Pointer + wavefp*: pointer start*: int32 stop*: int32 cvt*: TAudioCVT @@ -75,8 +75,8 @@ type #music_cmd.h types volume*: cint #vf: OggVorbis_File; section*: cint cvt*: TAudioCVT - len_available*: cint - snd_available*: pointer + lenAvailable*: cint + sndAvailable*: pointer TErrorEnum* = enum MMERR_OPENING_FILE, MMERR_OUT_OF_MEMORY, MMERR_DYNAMIC_LINKING, @@ -107,7 +107,7 @@ type #music_cmd.h types TChunk*{.final.} = object allocated*: cint abuf*: pointer - alen*: Uint32 + alen*: uint32 volume*: byte # Per-sample volume, 0-128 TFading* = enum @@ -118,18 +118,18 @@ type #music_cmd.h types TMusic*{.final.} = object typ*: TMusicType - TMixFunction* = proc (udata, stream: pointer, length: cint): Pointer{. + TMixFunction* = proc (udata, stream: pointer, length: cint): pointer{. cdecl.} # This macro can be used to fill a version structure with the compile-time # version of the SDL_mixer library. -proc VERSION*(X: var sdl.TVersion) +proc version*(x: var sdl.Tversion) # This function gets the version of the dynamically linked SDL_mixer library. # It should NOT be used to fill a version structure, instead you should use the # SDL_MIXER_VERSION() macro. -proc Linked_Version*(): sdl.Pversion{.cdecl, importc: "Mix_Linked_Version", +proc linkedVersion*(): sdl.Pversion{.cdecl, importc: "Mix_Linked_Version", dynlib: MixerLibName.} # Open the mixer with a certain audio format -proc OpenAudio*(frequency: cint, format: Uint16, channels: cint, +proc openAudio*(frequency: cint, format: uint16, channels: cint, chunksize: cint): cint{.cdecl, importc: "Mix_OpenAudio", dynlib: MixerLibName.} # Dynamically change the number of channels managed by the mixer. @@ -137,170 +137,170 @@ proc OpenAudio*(frequency: cint, format: Uint16, channels: cint, # stopped. # This function returns the new number of allocated channels. # -proc AllocateChannels*(numchannels: cint): cint{.cdecl, +proc allocateChannels*(numchannels: cint): cint{.cdecl, importc: "Mix_AllocateChannels", dynlib: MixerLibName.} # Find out what the actual audio device parameters are. # This function returns 1 if the audio has been opened, 0 otherwise. # -proc QuerySpec*(frequency: var cint, format: var Uint16, channels: var cint): cint{. +proc querySpec*(frequency: var cint, format: var uint16, channels: var cint): cint{. cdecl, importc: "Mix_QuerySpec", dynlib: MixerLibName.} # Load a wave file or a music (.mod .s3m .it .xm) file proc LoadWAV_RW*(src: PRWops, freesrc: cint): PChunk{.cdecl, importc: "Mix_LoadWAV_RW", dynlib: MixerLibName.} -proc LoadWAV*(filename: cstring): PChunk -proc LoadMUS*(filename: cstring): PMusic{.cdecl, importc: "Mix_LoadMUS", +proc loadWAV*(filename: cstring): PChunk +proc loadMUS*(filename: cstring): PMusic{.cdecl, importc: "Mix_LoadMUS", dynlib: MixerLibName.} # Load a wave file of the mixer format from a memory buffer -proc QuickLoad_WAV*(mem: pointer): PChunk{.cdecl, +proc quickLoadWAV*(mem: pointer): PChunk{.cdecl, importc: "Mix_QuickLoad_WAV", dynlib: MixerLibName.} # Free an audio chunk previously loaded -proc FreeChunk*(chunk: PChunk){.cdecl, importc: "Mix_FreeChunk", +proc freeChunk*(chunk: PChunk){.cdecl, importc: "Mix_FreeChunk", dynlib: MixerLibName.} -proc FreeMusic*(music: PMusic){.cdecl, importc: "Mix_FreeMusic", +proc freeMusic*(music: PMusic){.cdecl, importc: "Mix_FreeMusic", dynlib: MixerLibName.} # Find out the music format of a mixer music, or the currently playing # music, if 'music' is NULL. -proc GetMusicType*(music: PMusic): TMusicType{.cdecl, +proc getMusicType*(music: PMusic): TMusicType{.cdecl, importc: "Mix_GetMusicType", dynlib: MixerLibName.} # Set a function that is called after all mixing is performed. # This can be used to provide real-time visual display of the audio stream # or add a custom mixer filter for the stream data. # -proc SetPostMix*(mixfunc: TMixFunction, arg: Pointer){.cdecl, +proc setPostMix*(mixfunc: TMixFunction, arg: pointer){.cdecl, importc: "Mix_SetPostMix", dynlib: MixerLibName.} # Add your own music player or additional mixer function. # If 'mix_func' is NULL, the default music player is re-enabled. # -proc HookMusic*(mix_func: TMixFunction, arg: Pointer){.cdecl, +proc hookMusic*(mixFunc: TMixFunction, arg: pointer){.cdecl, importc: "Mix_HookMusic", dynlib: MixerLibName.} # Add your own callback when the music has finished playing. # -proc HookMusicFinished*(music_finished: Pointer){.cdecl, +proc hookMusicFinished*(musicFinished: pointer){.cdecl, importc: "Mix_HookMusicFinished", dynlib: MixerLibName.} # Get a pointer to the user data for the current music hook -proc GetMusicHookData*(): Pointer{.cdecl, importc: "Mix_GetMusicHookData", +proc getMusicHookData*(): pointer{.cdecl, importc: "Mix_GetMusicHookData", dynlib: MixerLibName.} #* Add your own callback when a channel has finished playing. NULL # * to disable callback.* type - TChannel_finished* = proc (channel: cint){.cdecl.} + TChannelFinished* = proc (channel: cint){.cdecl.} -proc ChannelFinished*(channel_finished: TChannel_finished){.cdecl, +proc channelFinished*(channelFinished: TChannelFinished){.cdecl, importc: "Mix_ChannelFinished", dynlib: MixerLibName.} const CHANNEL_POST* = - 2 type - TEffectFunc* = proc (chan: cint, stream: Pointer, length: cint, - udata: Pointer): Pointer{.cdecl.} - TEffectDone* = proc (chan: cint, udata: Pointer): Pointer{.cdecl.} + TEffectFunc* = proc (chan: cint, stream: pointer, length: cint, + udata: pointer): pointer{.cdecl.} + TEffectDone* = proc (chan: cint, udata: pointer): pointer{.cdecl.} -proc RegisterEffect*(chan: cint, f: TEffectFunc, d: TEffectDone, - arg: Pointer): cint{.cdecl, +proc registerEffect*(chan: cint, f: TEffectFunc, d: TEffectDone, + arg: pointer): cint{.cdecl, importc: "Mix_RegisterEffect", dynlib: MixerLibName.} -proc UnregisterEffect*(channel: cint, f: TEffectFunc): cint{.cdecl, +proc unregisterEffect*(channel: cint, f: TEffectFunc): cint{.cdecl, importc: "Mix_UnregisterEffect", dynlib: MixerLibName.} -proc UnregisterAllEffects*(channel: cint): cint{.cdecl, +proc unregisterAllEffects*(channel: cint): cint{.cdecl, importc: "Mix_UnregisterAllEffects", dynlib: MixerLibName.} const EFFECTSMAXSPEED* = "MIX_EFFECTSMAXSPEED" -proc SetPanning*(channel: cint, left: byte, right: byte): cint{.cdecl, +proc setPanning*(channel: cint, left: byte, right: byte): cint{.cdecl, importc: "Mix_SetPanning", dynlib: MixerLibName.} -proc SetPosition*(channel: cint, angle: int16, distance: byte): cint{.cdecl, +proc setPosition*(channel: cint, angle: int16, distance: byte): cint{.cdecl, importc: "Mix_SetPosition", dynlib: MixerLibName.} -proc SetDistance*(channel: cint, distance: byte): cint{.cdecl, +proc setDistance*(channel: cint, distance: byte): cint{.cdecl, importc: "Mix_SetDistance", dynlib: MixerLibName.} -proc SetReverseStereo*(channel: cint, flip: cint): cint{.cdecl, +proc setReverseStereo*(channel: cint, flip: cint): cint{.cdecl, importc: "Mix_SetReverseStereo", dynlib: MixerLibName.} -proc ReserveChannels*(num: cint): cint{.cdecl, importc: "Mix_ReserveChannels", +proc reserveChannels*(num: cint): cint{.cdecl, importc: "Mix_ReserveChannels", dynlib: MixerLibName.} -proc GroupChannel*(which: cint, tag: cint): cint{.cdecl, +proc groupChannel*(which: cint, tag: cint): cint{.cdecl, importc: "Mix_GroupChannel", dynlib: MixerLibName.} # Assign several consecutive channels to a group -proc GroupChannels*(`from`: cint, `to`: cint, tag: cint): cint{.cdecl, +proc groupChannels*(`from`: cint, `to`: cint, tag: cint): cint{.cdecl, importc: "Mix_GroupChannels", dynlib: MixerLibName.} # Finds the first available channel in a group of channels -proc GroupAvailable*(tag: cint): cint{.cdecl, importc: "Mix_GroupAvailable", +proc groupAvailable*(tag: cint): cint{.cdecl, importc: "Mix_GroupAvailable", dynlib: MixerLibName.} # Returns the number of channels in a group. This is also a subtle # way to get the total number of channels when 'tag' is -1 # -proc GroupCount*(tag: cint): cint{.cdecl, importc: "Mix_GroupCount", +proc groupCount*(tag: cint): cint{.cdecl, importc: "Mix_GroupCount", dynlib: MixerLibName.} # Finds the "oldest" sample playing in a group of channels -proc GroupOldest*(tag: cint): cint{.cdecl, importc: "Mix_GroupOldest", +proc groupOldest*(tag: cint): cint{.cdecl, importc: "Mix_GroupOldest", dynlib: MixerLibName.} # Finds the "most recent" (i.e. last) sample playing in a group of channels -proc GroupNewer*(tag: cint): cint{.cdecl, importc: "Mix_GroupNewer", +proc groupNewer*(tag: cint): cint{.cdecl, importc: "Mix_GroupNewer", dynlib: MixerLibName.} # The same as above, but the sound is played at most 'ticks' milliseconds -proc PlayChannelTimed*(channel: cint, chunk: PChunk, loops: cint, +proc playChannelTimed*(channel: cint, chunk: PChunk, loops: cint, ticks: cint): cint{.cdecl, importc: "Mix_PlayChannelTimed", dynlib: MixerLibName.} -proc PlayChannel*(channel: cint, chunk: PChunk, loops: cint): cint -proc PlayMusic*(music: PMusic, loops: cint): cint{.cdecl, +proc playChannel*(channel: cint, chunk: PChunk, loops: cint): cint +proc playMusic*(music: PMusic, loops: cint): cint{.cdecl, importc: "Mix_PlayMusic", dynlib: MixerLibName.} # Fade in music or a channel over "ms" milliseconds, same semantics as the "Play" functions -proc FadeInMusic*(music: PMusic, loops: cint, ms: cint): cint{.cdecl, +proc fadeInMusic*(music: PMusic, loops: cint, ms: cint): cint{.cdecl, importc: "Mix_FadeInMusic", dynlib: MixerLibName.} -proc FadeInChannelTimed*(channel: cint, chunk: PChunk, loops: cint, +proc fadeInChannelTimed*(channel: cint, chunk: PChunk, loops: cint, ms: cint, ticks: cint): cint{.cdecl, importc: "Mix_FadeInChannelTimed", dynlib: MixerLibName.} -proc FadeInChannel*(channel: cint, chunk: PChunk, loops: cint, ms: cint): cint +proc fadeInChannel*(channel: cint, chunk: PChunk, loops: cint, ms: cint): cint # Set the volume in the range of 0-128 of a specific channel or chunk. # If the specified channel is -1, set volume for all channels. # Returns the original volume. # If the specified volume is -1, just return the current volume. # -proc Volume*(channel: cint, volume: cint): cint{.cdecl, importc: "Mix_Volume", +proc volume*(channel: cint, volume: cint): cint{.cdecl, importc: "Mix_Volume", dynlib: MixerLibName.} -proc VolumeChunk*(chunk: PChunk, volume: cint): cint{.cdecl, +proc volumeChunk*(chunk: PChunk, volume: cint): cint{.cdecl, importc: "Mix_VolumeChunk", dynlib: MixerLibName.} -proc VolumeMusic*(volume: cint): cint{.cdecl, importc: "Mix_VolumeMusic", +proc volumeMusic*(volume: cint): cint{.cdecl, importc: "Mix_VolumeMusic", dynlib: MixerLibName.} # Halt playing of a particular channel -proc HaltChannel*(channel: cint): cint{.cdecl, importc: "Mix_HaltChannel", +proc haltChannel*(channel: cint): cint{.cdecl, importc: "Mix_HaltChannel", dynlib: MixerLibName.} -proc HaltGroup*(tag: cint): cint{.cdecl, importc: "Mix_HaltGroup", +proc haltGroup*(tag: cint): cint{.cdecl, importc: "Mix_HaltGroup", dynlib: MixerLibName.} -proc HaltMusic*(): cint{.cdecl, importc: "Mix_HaltMusic", +proc haltMusic*(): cint{.cdecl, importc: "Mix_HaltMusic", dynlib: MixerLibName.} -proc ExpireChannel*(channel: cint, ticks: cint): cint{.cdecl, +proc expireChannel*(channel: cint, ticks: cint): cint{.cdecl, importc: "Mix_ExpireChannel", dynlib: MixerLibName.} -proc FadeOutChannel*(which: cint, ms: cint): cint{.cdecl, +proc fadeOutChannel*(which: cint, ms: cint): cint{.cdecl, importc: "Mix_FadeOutChannel", dynlib: MixerLibName.} -proc FadeOutGroup*(tag: cint, ms: cint): cint{.cdecl, +proc fadeOutGroup*(tag: cint, ms: cint): cint{.cdecl, importc: "Mix_FadeOutGroup", dynlib: MixerLibName.} -proc FadeOutMusic*(ms: cint): cint{.cdecl, importc: "Mix_FadeOutMusic", +proc fadeOutMusic*(ms: cint): cint{.cdecl, importc: "Mix_FadeOutMusic", dynlib: MixerLibName.} # Query the fading status of a channel -proc FadingMusic*(): TFading{.cdecl, importc: "Mix_FadingMusic", +proc fadingMusic*(): TFading{.cdecl, importc: "Mix_FadingMusic", dynlib: MixerLibName.} -proc FadingChannel*(which: cint): TFading{.cdecl, +proc fadingChannel*(which: cint): TFading{.cdecl, importc: "Mix_FadingChannel", dynlib: MixerLibName.} # Pause/Resume a particular channel -proc Pause*(channel: cint){.cdecl, importc: "Mix_Pause", dynlib: MixerLibName.} -proc Resume*(channel: cint){.cdecl, importc: "Mix_Resume", +proc pause*(channel: cint){.cdecl, importc: "Mix_Pause", dynlib: MixerLibName.} +proc resume*(channel: cint){.cdecl, importc: "Mix_Resume", dynlib: MixerLibName.} -proc Paused*(channel: cint): cint{.cdecl, importc: "Mix_Paused", +proc paused*(channel: cint): cint{.cdecl, importc: "Mix_Paused", dynlib: MixerLibName.} # Pause/Resume the music stream -proc PauseMusic*(){.cdecl, importc: "Mix_PauseMusic", dynlib: MixerLibName.} -proc ResumeMusic*(){.cdecl, importc: "Mix_ResumeMusic", dynlib: MixerLibName.} -proc RewindMusic*(){.cdecl, importc: "Mix_RewindMusic", dynlib: MixerLibName.} -proc PausedMusic*(): cint{.cdecl, importc: "Mix_PausedMusic", +proc pauseMusic*(){.cdecl, importc: "Mix_PauseMusic", dynlib: MixerLibName.} +proc resumeMusic*(){.cdecl, importc: "Mix_ResumeMusic", dynlib: MixerLibName.} +proc rewindMusic*(){.cdecl, importc: "Mix_RewindMusic", dynlib: MixerLibName.} +proc pausedMusic*(): cint{.cdecl, importc: "Mix_PausedMusic", dynlib: MixerLibName.} # Set the current position in the music stream. # This returns 0 if successful, or -1 if it failed or isn't implemented. @@ -308,44 +308,44 @@ proc PausedMusic*(): cint{.cdecl, importc: "Mix_PausedMusic", # order number) and for OGG music (set position in seconds), at the # moment. # -proc SetMusicPosition*(position: float64): cint{.cdecl, +proc setMusicPosition*(position: float64): cint{.cdecl, importc: "Mix_SetMusicPosition", dynlib: MixerLibName.} # Check the status of a specific channel. # If the specified channel is -1, check all channels. # -proc Playing*(channel: cint): cint{.cdecl, importc: "Mix_Playing", +proc playing*(channel: cint): cint{.cdecl, importc: "Mix_Playing", dynlib: MixerLibName.} -proc PlayingMusic*(): cint{.cdecl, importc: "Mix_PlayingMusic", +proc playingMusic*(): cint{.cdecl, importc: "Mix_PlayingMusic", dynlib: MixerLibName.} # Stop music and set external music playback command -proc SetMusicCMD*(command: cstring): cint{.cdecl, importc: "Mix_SetMusicCMD", +proc setMusicCMD*(command: cstring): cint{.cdecl, importc: "Mix_SetMusicCMD", dynlib: MixerLibName.} # Synchro value is set by MikMod from modules while playing -proc SetSynchroValue*(value: cint): cint{.cdecl, +proc setSynchroValue*(value: cint): cint{.cdecl, importc: "Mix_SetSynchroValue", dynlib: MixerLibName.} -proc GetSynchroValue*(): cint{.cdecl, importc: "Mix_GetSynchroValue", +proc getSynchroValue*(): cint{.cdecl, importc: "Mix_GetSynchroValue", dynlib: MixerLibName.} # # Get the Mix_Chunk currently associated with a mixer channel # Returns nil if it's an invalid channel, or there's no chunk associated. # -proc GetChunk*(channel: cint): PChunk{.cdecl, importc: "Mix_GetChunk", +proc getChunk*(channel: cint): PChunk{.cdecl, importc: "Mix_GetChunk", dynlib: MixerLibName.} # Close the mixer, halting all playing audio -proc CloseAudio*(){.cdecl, importc: "Mix_CloseAudio", dynlib: MixerLibName.} +proc closeAudio*(){.cdecl, importc: "Mix_CloseAudio", dynlib: MixerLibName.} # We'll use SDL for reporting errors -proc VERSION(X: var Tversion) = - X.major = MAJOR_VERSION - X.minor = MINOR_VERSION - X.patch = PATCHLEVEL +proc version(x: var Tversion) = + x.major = MAJOR_VERSION + x.minor = MINOR_VERSION + x.patch = PATCHLEVEL -proc LoadWAV(filename: cstring): PChunk = - result = LoadWAV_RW(RWFromFile(filename, "rb"), 1) +proc loadWAV(filename: cstring): PChunk = + result = LoadWAV_RW(rWFromFile(filename, "rb"), 1) -proc PlayChannel(channel: cint, chunk: PChunk, loops: cint): cint = - result = PlayChannelTimed(channel, chunk, loops, - 1) +proc playChannel(channel: cint, chunk: PChunk, loops: cint): cint = + result = playChannelTimed(channel, chunk, loops, - 1) -proc FadeInChannel(channel: cint, chunk: PChunk, loops: cint, ms: cint): cint = - result = FadeInChannelTimed(channel, chunk, loops, ms, - 1) +proc fadeInChannel(channel: cint, chunk: PChunk, loops: cint, ms: cint): cint = + result = fadeInChannelTimed(channel, chunk, loops, ms, - 1) diff --git a/lib/wrappers/sdl/sdl_net.nim b/lib/wrappers/sdl/sdl_net.nim index 742a59314..1ffdb5cca 100644 --- a/lib/wrappers/sdl/sdl_net.nim +++ b/lib/wrappers/sdl/sdl_net.nim @@ -145,15 +145,15 @@ type # SDL_net.h types #*********************************************************************** PIPAddress* = ptr TIPAddress TIPAddress*{.final.} = object #* TCP network API - host*: Uint32 # 32-bit IPv4 host address */ - port*: Uint16 # 16-bit protocol port */ + host*: uint32 # 32-bit IPv4 host address */ + port*: uint16 # 16-bit protocol port */ PTCPSocket* = ptr TTCPSocket TTCPSocket*{.final.} = object # UDP network API ready*: int channel*: int - remoteAddress*: TIPaddress - localAddress*: TIPaddress + remoteAddress*: TIPAddress + localAddress*: TIPAddress sflag*: int PUDP_Channel* = ptr TUDP_Channel @@ -196,27 +196,27 @@ type # SDL_net.h types ready*: int -proc VERSION*(X: var Tversion) +proc version*(x: var Tversion) #* Initialize/Cleanup the network API # SDL must be initialized before calls to functions in this library, # because this library uses utility functions from the SDL library. #* -proc Init*(): int{.cdecl, importc: "SDLNet_Init", dynlib: NetLibName.} -proc Quit*(){.cdecl, importc: "SDLNet_Quit", dynlib: NetLibName.} +proc init*(): int{.cdecl, importc: "SDLNet_Init", dynlib: NetLibName.} +proc quit*(){.cdecl, importc: "SDLNet_Quit", dynlib: NetLibName.} #* Resolve a host name and port to an IP address in network form. # If the function succeeds, it will return 0. # If the host couldn't be resolved, the host portion of the returned # address will be INADDR_NONE, and the function will return -1. # If 'host' is NULL, the resolved host will be set to INADDR_ANY. # * -proc ResolveHost*(address: var TIPaddress, host: cstring, port: Uint16): int{. +proc resolveHost*(address: var TIPAddress, host: cstring, port: uint16): int{. cdecl, importc: "SDLNet_ResolveHost", dynlib: NetLibName.} #* Resolve an ip address to a host name in canonical form. # If the ip couldn't be resolved, this function returns NULL, # otherwise a pointer to a static buffer containing the hostname # is returned. Note that this function is not thread-safe. #* -proc ResolveIP*(ip: var TIPaddress): cstring{.cdecl, +proc resolveIP*(ip: var TIPAddress): cstring{.cdecl, importc: "SDLNet_ResolveIP", dynlib: NetLibName.} #*********************************************************************** #* TCP network API * @@ -229,24 +229,24 @@ proc ResolveIP*(ip: var TIPaddress): cstring{.cdecl, # in the correct form). # The newly created socket is returned, or NULL if there was an error. #* -proc TCP_Open*(ip: var TIPaddress): PTCPSocket{.cdecl, +proc tcpOpen*(ip: var TIPAddress): PTCPSocket{.cdecl, importc: "SDLNet_TCP_Open", dynlib: NetLibName.} #* Accept an incoming connection on the given server socket. # The newly created socket is returned, or NULL if there was an error. #* -proc TCP_Accept*(server: PTCPsocket): PTCPSocket{.cdecl, +proc tcpAccept*(server: PTCPSocket): PTCPSocket{.cdecl, importc: "SDLNet_TCP_Accept", dynlib: NetLibName.} #* Get the IP address of the remote system associated with the socket. # If the socket is a server socket, this function returns NULL. #* -proc TCP_GetPeerAddress*(sock: PTCPsocket): PIPAddress{.cdecl, +proc tcpGetPeerAddress*(sock: PTCPSocket): PIPAddress{.cdecl, importc: "SDLNet_TCP_GetPeerAddress", dynlib: NetLibName.} #* Send 'len' bytes of 'data' over the non-server socket 'sock' # This function returns the actual amount of data sent. If the return value # is less than the amount of data sent, then either the remote connection was # closed, or an unknown socket error occurred. #* -proc TCP_Send*(sock: PTCPsocket, data: Pointer, length: int): int{.cdecl, +proc tcpSend*(sock: PTCPSocket, data: pointer, length: int): int{.cdecl, importc: "SDLNet_TCP_Send", dynlib: NetLibName.} #* Receive up to 'maxlen' bytes of data over the non-server socket 'sock', # and store them in the buffer pointed to by 'data'. @@ -254,10 +254,10 @@ proc TCP_Send*(sock: PTCPsocket, data: Pointer, length: int): int{.cdecl, # value is less than or equal to zero, then either the remote connection was # closed, or an unknown socket error occurred. #* -proc TCP_Recv*(sock: PTCPsocket, data: Pointer, maxlen: int): int{.cdecl, +proc tcpRecv*(sock: PTCPSocket, data: pointer, maxlen: int): int{.cdecl, importc: "SDLNet_TCP_Recv", dynlib: NetLibName.} #* Close a TCP network socket * -proc TCP_Close*(sock: PTCPsocket){.cdecl, importc: "SDLNet_TCP_Close", +proc tcpClose*(sock: PTCPSocket){.cdecl, importc: "SDLNet_TCP_Close", dynlib: NetLibName.} #*********************************************************************** #* UDP network API * @@ -265,26 +265,26 @@ proc TCP_Close*(sock: PTCPsocket){.cdecl, importc: "SDLNet_TCP_Close", #* Allocate/resize/free a single UDP packet 'size' bytes long. # The new packet is returned, or NULL if the function ran out of memory. # * -proc AllocPacket*(size: int): PUDPpacket{.cdecl, +proc allocPacket*(size: int): PUDPpacket{.cdecl, importc: "SDLNet_AllocPacket", dynlib: NetLibName.} -proc ResizePacket*(packet: PUDPpacket, newsize: int): int{.cdecl, +proc resizePacket*(packet: PUDPpacket, newsize: int): int{.cdecl, importc: "SDLNet_ResizePacket", dynlib: NetLibName.} -proc FreePacket*(packet: PUDPpacket){.cdecl, importc: "SDLNet_FreePacket", +proc freePacket*(packet: PUDPpacket){.cdecl, importc: "SDLNet_FreePacket", dynlib: NetLibName.} #* Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets, # each 'size' bytes long. # A pointer to the first packet in the array is returned, or NULL if the # function ran out of memory. # * -proc AllocPacketV*(howmany: int, size: int): PUDPpacket{.cdecl, +proc allocPacketV*(howmany: int, size: int): PUDPpacket{.cdecl, importc: "SDLNet_AllocPacketV", dynlib: NetLibName.} -proc FreePacketV*(packetV: PUDPpacket){.cdecl, +proc freePacketV*(packetV: PUDPpacket){.cdecl, importc: "SDLNet_FreePacketV", dynlib: NetLibName.} #* Open a UDP network socket # If 'port' is non-zero, the UDP socket is bound to a local port. # This allows other systems to send to this socket via a known port. #* -proc UDP_Open*(port: Uint16): PUDPsocket{.cdecl, importc: "SDLNet_UDP_Open", +proc udpOpen*(port: uint16): PUDPSocket{.cdecl, importc: "SDLNet_UDP_Open", dynlib: NetLibName.} #* Bind the address 'address' to the requested channel on the UDP socket. # If the channel is -1, then the first unbound channel will be bound with @@ -295,10 +295,10 @@ proc UDP_Open*(port: Uint16): PUDPsocket{.cdecl, importc: "SDLNet_UDP_Open", # address, to which all outbound packets on the channel are sent. # This function returns the channel which was bound, or -1 on error. #* -proc UDP_Bind*(sock: PUDPsocket, channel: int, address: var TIPaddress): int{. +proc udpBind*(sock: PUDPSocket, channel: int, address: var TIPAddress): int{. cdecl, importc: "SDLNet_UDP_Bind", dynlib: NetLibName.} #* Unbind all addresses from the given channel * -proc UDP_Unbind*(sock: PUDPsocket, channel: int){.cdecl, +proc udpUnbind*(sock: PUDPSocket, channel: int){.cdecl, importc: "SDLNet_UDP_Unbind", dynlib: NetLibName.} #* Get the primary IP address of the remote system associated with the # socket and channel. If the channel is -1, then the primary IP port @@ -306,7 +306,7 @@ proc UDP_Unbind*(sock: PUDPsocket, channel: int){.cdecl, # opened with a specific port. # If the channel is not bound and not -1, this function returns NULL. # * -proc UDP_GetPeerAddress*(sock: PUDPsocket, channel: int): PIPAddress{.cdecl, +proc udpGetPeerAddress*(sock: PUDPSocket, channel: int): PIPAddress{.cdecl, importc: "SDLNet_UDP_GetPeerAddress", dynlib: NetLibName.} #* Send a vector of packets to the the channels specified within the packet. # If the channel specified in the packet is -1, the packet will be sent to @@ -315,7 +315,7 @@ proc UDP_GetPeerAddress*(sock: PUDPsocket, channel: int): PIPAddress{.cdecl, # been sent, -1 if the packet send failed. # This function returns the number of packets sent. #* -proc UDP_SendV*(sock: PUDPsocket, packets: PPUDPpacket, npackets: int): int{. +proc udpSendV*(sock: PUDPSocket, packets: PPUDPpacket, npackets: int): int{. cdecl, importc: "SDLNet_UDP_SendV", dynlib: NetLibName.} #* Send a single packet to the specified channel. # If the channel specified in the packet is -1, the packet will be sent to @@ -324,7 +324,7 @@ proc UDP_SendV*(sock: PUDPsocket, packets: PPUDPpacket, npackets: int): int{. # been sent. # This function returns 1 if the packet was sent, or 0 on error. #* -proc UDP_Send*(sock: PUDPsocket, channel: int, packet: PUDPpacket): int{. +proc udpSend*(sock: PUDPSocket, channel: int, packet: PUDPpacket): int{. cdecl, importc: "SDLNet_UDP_Send", dynlib: NetLibName.} #* Receive a vector of pending packets from the UDP socket. # The returned packets contain the source address and the channel they arrived @@ -336,7 +336,7 @@ proc UDP_Send*(sock: PUDPsocket, channel: int, packet: PUDPpacket): int{. # This function returns the number of packets read from the network, or -1 # on error. This function does not block, so can return 0 packets pending. #* -proc UDP_RecvV*(sock: PUDPsocket, packets: PPUDPpacket): int{.cdecl, +proc udpRecvV*(sock: PUDPSocket, packets: PPUDPpacket): int{.cdecl, importc: "SDLNet_UDP_RecvV", dynlib: NetLibName.} #* Receive a single packet from the UDP socket. # The returned packet contains the source address and the channel it arrived @@ -348,10 +348,10 @@ proc UDP_RecvV*(sock: PUDPsocket, packets: PPUDPpacket): int{.cdecl, # This function returns the number of packets read from the network, or -1 # on error. This function does not block, so can return 0 packets pending. #* -proc UDP_Recv*(sock: PUDPsocket, packet: PUDPpacket): int{.cdecl, +proc udpRecv*(sock: PUDPSocket, packet: PUDPpacket): int{.cdecl, importc: "SDLNet_UDP_Recv", dynlib: NetLibName.} #* Close a UDP network socket * -proc UDP_Close*(sock: PUDPsocket){.cdecl, importc: "SDLNet_UDP_Close", +proc udpClose*(sock: PUDPSocket){.cdecl, importc: "SDLNet_UDP_Close", dynlib: NetLibName.} #*********************************************************************** #* Hooks for checking sockets for available data * @@ -360,19 +360,19 @@ proc UDP_Close*(sock: PUDPsocket){.cdecl, importc: "SDLNet_UDP_Close", # This returns a socket set for up to 'maxsockets' sockets, or NULL if # the function ran out of memory. # * -proc AllocSocketSet*(maxsockets: int): PSocketSet{.cdecl, +proc allocSocketSet*(maxsockets: int): PSocketSet{.cdecl, importc: "SDLNet_AllocSocketSet", dynlib: NetLibName.} #* Add a socket to a set of sockets to be checked for available data * -proc AddSocket*(theSet: PSocketSet, sock: PGenericSocket): int{. +proc addSocket*(theSet: PSocketSet, sock: PGenericSocket): int{. cdecl, importc: "SDLNet_AddSocket", dynlib: NetLibName.} -proc TCP_AddSocket*(theSet: PSocketSet, sock: PTCPSocket): int -proc UDP_AddSocket*(theSet: PSocketSet, sock: PUDPSocket): int +proc tcpAddSocket*(theSet: PSocketSet, sock: PTCPSocket): int +proc udpAddSocket*(theSet: PSocketSet, sock: PUDPSocket): int #* Remove a socket from a set of sockets to be checked for available data * -proc DelSocket*(theSet: PSocketSet, sock: PGenericSocket): int{. +proc delSocket*(theSet: PSocketSet, sock: PGenericSocket): int{. cdecl, importc: "SDLNet_DelSocket", dynlib: NetLibName.} -proc TCP_DelSocket*(theSet: PSocketSet, sock: PTCPSocket): int +proc tcpDelSocket*(theSet: PSocketSet, sock: PTCPSocket): int # SDLNet_DelSocket(set, (SDLNet_GenericSocket)sock) -proc UDP_DelSocket*(theSet: PSocketSet, sock: PUDPSocket): int +proc udpDelSocket*(theSet: PSocketSet, sock: PUDPSocket): int #SDLNet_DelSocket(set, (SDLNet_GenericSocket)sock) #* This function checks to see if data is available for reading on the # given set of sockets. If 'timeout' is 0, it performs a quick poll, @@ -381,47 +381,46 @@ proc UDP_DelSocket*(theSet: PSocketSet, sock: PUDPSocket): int # first. This function returns the number of sockets ready for reading, # or -1 if there was an error with the select() system call. #* -proc CheckSockets*(theSet: PSocketSet, timeout: int32): int{.cdecl, +proc checkSockets*(theSet: PSocketSet, timeout: int32): int{.cdecl, importc: "SDLNet_CheckSockets", dynlib: NetLibName.} #* After calling SDLNet_CheckSockets(), you can use this function on a # socket that was in the socket set, to find out if data is available # for reading. #* -proc SocketReady*(sock: PGenericSocket): bool +proc socketReady*(sock: PGenericSocket): bool #* Free a set of sockets allocated by SDL_NetAllocSocketSet() * -proc FreeSocketSet*(theSet: PSocketSet){.cdecl, +proc freeSocketSet*(theSet: PSocketSet){.cdecl, importc: "SDLNet_FreeSocketSet", dynlib: NetLibName.} #*********************************************************************** #* Platform-independent data conversion functions * #*********************************************************************** #* Write a 16/32 bit value to network packet buffer * -proc Write16*(value: Uint16, area: Pointer){.cdecl, +proc write16*(value: uint16, area: pointer){.cdecl, importc: "SDLNet_Write16", dynlib: NetLibName.} -proc Write32*(value: Uint32, area: Pointer){.cdecl, +proc write32*(value: uint32, area: pointer){.cdecl, importc: "SDLNet_Write32", dynlib: NetLibName.} #* Read a 16/32 bit value from network packet buffer * -proc Read16*(area: Pointer): Uint16{.cdecl, importc: "SDLNet_Read16", +proc read16*(area: pointer): uint16{.cdecl, importc: "SDLNet_Read16", dynlib: NetLibName.} -proc Read32*(area: Pointer): Uint32{.cdecl, importc: "SDLNet_Read32", +proc read32*(area: pointer): uint32{.cdecl, importc: "SDLNet_Read32", dynlib: NetLibName.} -proc VERSION(X: var Tversion) = - X.major = MAJOR_VERSION - X.minor = MINOR_VERSION - X.patch = PATCHLEVEL +proc version(x: var Tversion) = + x.major = MAJOR_VERSION + x.minor = MINOR_VERSION + x.patch = PATCHLEVEL -proc TCP_AddSocket(theSet: PSocketSet, sock: PTCPSocket): int = - result = AddSocket(theSet, cast[PGenericSocket](sock)) +proc tcpAddSocket(theSet: PSocketSet, sock: PTCPSocket): int = + result = addSocket(theSet, cast[PGenericSocket](sock)) -proc UDP_AddSocket(theSet: PSocketSet, sock: PUDPSocket): int = - result = AddSocket(theSet, cast[PGenericSocket](sock)) +proc udpAddSocket(theSet: PSocketSet, sock: PUDPSocket): int = + result = addSocket(theSet, cast[PGenericSocket](sock)) -proc TCP_DelSocket(theSet: PSocketSet, sock: PTCPSocket): int = - result = DelSocket(theSet, cast[PGenericSocket](sock)) +proc tcpDelSocket(theSet: PSocketSet, sock: PTCPSocket): int = + result = delSocket(theSet, cast[PGenericSocket](sock)) -proc UDP_DelSocket(theSet: PSocketSet, sock: PUDPSocket): int = - result = DelSocket(theSet, cast[PGenericSocket](sock)) - -proc SocketReady(sock: PGenericSocket): bool = - result = ((sock != nil) and (sock.ready == 1)) +proc udpDelSocket(theSet: PSocketSet, sock: PUDPSocket): int = + result = delSocket(theSet, cast[PGenericSocket](sock)) +proc socketReady(sock: PGenericSocket): bool = + result = sock != nil and sock.ready == 1 diff --git a/lib/wrappers/sdl/sdl_ttf.nim b/lib/wrappers/sdl/sdl_ttf.nim index 45247df4d..9ebe70b9d 100644 --- a/lib/wrappers/sdl/sdl_ttf.nim +++ b/lib/wrappers/sdl/sdl_ttf.nim @@ -163,7 +163,7 @@ elif defined(macosx): ttfLibName = "libSDL_ttf-2.0.0.dylib" else: const - ttfLibName = "libSDL_ttf.so(|.1|.0)" + ttfLibName = "libSDL_ttf(|-2.0).so(|.1|.0)" const MAJOR_VERSION* = 2 MINOR_VERSION* = 0 @@ -177,78 +177,78 @@ const UNICODE_BOM_SWAPPED* = 0x0000FFFE type - PFont* = ptr Tfont - TFont{.final.} = object + PFont* = ptr TFont + TFont = object # This macro can be used to fill a version structure with the compile-time # version of the SDL_ttf library. -proc Linked_Version*(): sdl.Pversion{.cdecl, importc: "TTF_Linked_Version", +proc linkedVersion*(): sdl.Pversion{.cdecl, importc: "TTF_Linked_Version", dynlib: ttfLibName.} # This function tells the library whether UNICODE text is generally # byteswapped. A UNICODE BOM character in a string will override # this setting for the remainder of that string. # -proc ByteSwappedUNICODE*(swapped: cint){.cdecl, +proc byteSwappedUNICODE*(swapped: cint){.cdecl, importc: "TTF_ByteSwappedUNICODE", dynlib: ttfLibName.} #returns 0 on succes, -1 if error occurs -proc Init*(): cint{.cdecl, importc: "TTF_Init", dynlib: ttfLibName.} +proc init*(): cint{.cdecl, importc: "TTF_Init", dynlib: ttfLibName.} # # Open a font file and create a font of the specified point size. # Some .fon fonts will have several sizes embedded in the file, so the # point size becomes the index of choosing which size. If the value # is too high, the last indexed size will be the default. # -proc OpenFont*(filename: cstring, ptsize: cint): PFont{.cdecl, +proc openFont*(filename: cstring, ptsize: cint): PFont{.cdecl, importc: "TTF_OpenFont", dynlib: ttfLibName.} -proc OpenFontIndex*(filename: cstring, ptsize: cint, index: int32): PFont{. +proc openFontIndex*(filename: cstring, ptsize: cint, index: int32): PFont{. cdecl, importc: "TTF_OpenFontIndex", dynlib: ttfLibName.} -proc OpenFontRW*(src: PRWops, freesrc: cint, ptsize: cint): PFont{.cdecl, +proc openFontRW*(src: PRWops, freesrc: cint, ptsize: cint): PFont{.cdecl, importc: "TTF_OpenFontRW", dynlib: ttfLibName.} -proc OpenFontIndexRW*(src: PRWops, freesrc: cint, ptsize: cint, index: int32): PFont{. +proc openFontIndexRW*(src: PRWops, freesrc: cint, ptsize: cint, index: int32): PFont{. cdecl, importc: "TTF_OpenFontIndexRW", dynlib: ttfLibName.} -proc GetFontStyle*(font: PFont): cint{.cdecl, +proc getFontStyle*(font: PFont): cint{.cdecl, importc: "TTF_GetFontStyle", dynlib: ttfLibName.} -proc SetFontStyle*(font: PFont, style: cint){.cdecl, +proc setFontStyle*(font: PFont, style: cint){.cdecl, importc: "TTF_SetFontStyle", dynlib: ttfLibName.} # Get the total height of the font - usually equal to point size -proc FontHeight*(font: PFont): cint{.cdecl, importc: "TTF_FontHeight", +proc fontHeight*(font: PFont): cint{.cdecl, importc: "TTF_FontHeight", dynlib: ttfLibName.} # Get the offset from the baseline to the top of the font # This is a positive value, relative to the baseline. # -proc FontAscent*(font: PFont): cint{.cdecl, importc: "TTF_FontAscent", +proc fontAscent*(font: PFont): cint{.cdecl, importc: "TTF_FontAscent", dynlib: ttfLibName.} # Get the offset from the baseline to the bottom of the font # This is a negative value, relative to the baseline. # -proc FontDescent*(font: PFont): cint{.cdecl, importc: "TTF_FontDescent", +proc fontDescent*(font: PFont): cint{.cdecl, importc: "TTF_FontDescent", dynlib: ttfLibName.} # Get the recommended spacing between lines of text for this font -proc FontLineSkip*(font: PFont): cint{.cdecl, +proc fontLineSkip*(font: PFont): cint{.cdecl, importc: "TTF_FontLineSkip", dynlib: ttfLibName.} # Get the number of faces of the font -proc FontFaces*(font: PFont): int32{.cdecl, importc: "TTF_FontFaces", +proc fontFaces*(font: PFont): int32{.cdecl, importc: "TTF_FontFaces", dynlib: ttfLibName.} # Get the font face attributes, if any -proc FontFaceIsFixedWidth*(font: PFont): cint{.cdecl, +proc fontFaceIsFixedWidth*(font: PFont): cint{.cdecl, importc: "TTF_FontFaceIsFixedWidth", dynlib: ttfLibName.} -proc FontFaceFamilyName*(font: PFont): cstring{.cdecl, +proc fontFaceFamilyName*(font: PFont): cstring{.cdecl, importc: "TTF_FontFaceFamilyName", dynlib: ttfLibName.} -proc FontFaceStyleName*(font: PFont): cstring{.cdecl, +proc fontFaceStyleName*(font: PFont): cstring{.cdecl, importc: "TTF_FontFaceStyleName", dynlib: ttfLibName.} # Get the metrics (dimensions) of a glyph -proc GlyphMetrics*(font: PFont, ch: Uint16, minx: var cint, +proc glyphMetrics*(font: PFont, ch: uint16, minx: var cint, maxx: var cint, miny: var cint, maxy: var cint, advance: var cint): cint{.cdecl, importc: "TTF_GlyphMetrics", dynlib: ttfLibName.} # Get the dimensions of a rendered string of text -proc SizeText*(font: PFont, text: cstring, w: var cint, y: var cint): cint{. +proc sizeText*(font: PFont, text: cstring, w: var cint, y: var cint): cint{. cdecl, importc: "TTF_SizeText", dynlib: ttfLibName.} -proc SizeUTF8*(font: PFont, text: cstring, w: var cint, y: var cint): cint{. +proc sizeUTF8*(font: PFont, text: cstring, w: var cint, y: var cint): cint{. cdecl, importc: "TTF_SizeUTF8", dynlib: ttfLibName.} -proc SizeUNICODE*(font: PFont, text: PUint16, w: var cint, y: var cint): cint{. +proc sizeUNICODE*(font: PFont, text: PUInt16, w: var cint, y: var cint): cint{. cdecl, importc: "TTF_SizeUNICODE", dynlib: ttfLibName.} # Create an 8-bit palettized surface and render the given text at # fast quality with the given font and color. The 0 pixel is the @@ -256,9 +256,9 @@ proc SizeUNICODE*(font: PFont, text: PUint16, w: var cint, y: var cint): cint{. # to the text color. # This function returns the new surface, or NULL if there was an error. # -proc RenderUTF8_Solid*(font: PFont, text: cstring, fg: TColor): PSurface{. +proc renderUTF8Solid*(font: PFont, text: cstring, fg: TColor): PSurface{. cdecl, importc: "TTF_RenderUTF8_Solid", dynlib: ttfLibName.} -proc RenderUNICODE_Solid*(font: PFont, text: PUint16, fg: TColor): PSurface{. +proc renderUNICODE_Solid*(font: PFont, text: PUInt16, fg: TColor): PSurface{. cdecl, importc: "TTF_RenderUNICODE_Solid", dynlib: ttfLibName.} # #Create an 8-bit palettized surface and render the given glyph at @@ -268,20 +268,20 @@ proc RenderUNICODE_Solid*(font: PFont, text: PUint16, fg: TColor): PSurface{. # centering in the X direction, and aligned normally in the Y direction. # This function returns the new surface, or NULL if there was an error. # -proc RenderGlyph_Solid*(font: PFont, ch: Uint16, fg: TColor): PSurface{. +proc renderGlyphSolid*(font: PFont, ch: uint16, fg: TColor): PSurface{. cdecl, importc: "TTF_RenderGlyph_Solid", dynlib: ttfLibName.} # Create an 8-bit palettized surface and render the given text at # high quality with the given font and colors. The 0 pixel is background, # while other pixels have varying degrees of the foreground color. # This function returns the new surface, or NULL if there was an error. # -proc RenderText_Shaded*(font: PFont, text: cstring, fg: TColor, +proc renderTextShaded*(font: PFont, text: cstring, fg: TColor, bg: TColor): PSurface{.cdecl, importc: "TTF_RenderText_Shaded", dynlib: ttfLibName.} -proc RenderUTF8_Shaded*(font: PFont, text: cstring, fg: TColor, +proc renderUTF8Shaded*(font: PFont, text: cstring, fg: TColor, bg: TColor): PSurface{.cdecl, importc: "TTF_RenderUTF8_Shaded", dynlib: ttfLibName.} -proc RenderUNICODE_Shaded*(font: PFont, text: PUint16, fg: TColor, +proc renderUNICODE_Shaded*(font: PFont, text: PUInt16, fg: TColor, bg: TColor): PSurface{.cdecl, importc: "TTF_RenderUNICODE_Shaded", dynlib: ttfLibName.} # Create an 8-bit palettized surface and render the given glyph at @@ -291,17 +291,17 @@ proc RenderUNICODE_Shaded*(font: PFont, text: PUint16, fg: TColor, # direction, and aligned normally in the Y direction. # This function returns the new surface, or NULL if there was an error. # -proc RenderGlyph_Shaded*(font: PFont, ch: Uint16, fg: TColor, bg: TColor): PSurface{. +proc renderGlyphShaded*(font: PFont, ch: uint16, fg: TColor, bg: TColor): PSurface{. cdecl, importc: "TTF_RenderGlyph_Shaded", dynlib: ttfLibName.} # Create a 32-bit ARGB surface and render the given text at high quality, # using alpha blending to dither the font with the given color. # This function returns the new surface, or NULL if there was an error. # -proc RenderText_Blended*(font: PFont, text: cstring, fg: TColor): PSurface{. +proc renderTextBlended*(font: PFont, text: cstring, fg: TColor): PSurface{. cdecl, importc: "TTF_RenderText_Blended", dynlib: ttfLibName.} -proc RenderUTF8_Blended*(font: PFont, text: cstring, fg: TColor): PSurface{. +proc renderUTF8Blended*(font: PFont, text: cstring, fg: TColor): PSurface{. cdecl, importc: "TTF_RenderUTF8_Blended", dynlib: ttfLibName.} -proc RenderUNICODE_Blended*(font: PFont, text: PUint16, fg: TColor): PSurface{. +proc RenderUNICODE_Blended*(font: PFont, text: PUInt16, fg: TColor): PSurface{. cdecl, importc: "TTF_RenderUNICODE_Blended", dynlib: ttfLibName.} # Create a 32-bit ARGB surface and render the given glyph at high quality, # using alpha blending to dither the font with the given color. @@ -309,29 +309,29 @@ proc RenderUNICODE_Blended*(font: PFont, text: PUint16, fg: TColor): PSurface{. # direction, and aligned normally in the Y direction. # This function returns the new surface, or NULL if there was an error. # -proc RenderGlyph_Blended*(font: PFont, ch: Uint16, fg: TColor): PSurface{. +proc renderGlyphBlended*(font: PFont, ch: uint16, fg: TColor): PSurface{. cdecl, importc: "TTF_RenderGlyph_Blended", dynlib: ttfLibName.} # For compatibility with previous versions, here are the old functions - ##define TTF_RenderText(font, text, fg, bg) + # #define TTF_RenderText(font, text, fg, bg) # TTF_RenderText_Shaded(font, text, fg, bg) - ##define TTF_RenderUTF8(font, text, fg, bg) + # #define TTF_RenderUTF8(font, text, fg, bg) # TTF_RenderUTF8_Shaded(font, text, fg, bg) - ##define TTF_RenderUNICODE(font, text, fg, bg) + # #define TTF_RenderUNICODE(font, text, fg, bg) # TTF_RenderUNICODE_Shaded(font, text, fg, bg) - # Close an opened font file -proc CloseFont*(font: PFont){.cdecl, importc: "TTF_CloseFont", + # Close an opened font file +proc closeFont*(font: PFont){.cdecl, importc: "TTF_CloseFont", dynlib: ttfLibName.} - #De-initialize TTF engine -proc Quit*(){.cdecl, importc: "TTF_Quit", dynlib: ttfLibName.} + # De-initialize TTF engine +proc quit*(){.cdecl, importc: "TTF_Quit", dynlib: ttfLibName.} # Check if the TTF engine is initialized -proc WasInit*(): cint{.cdecl, importc: "TTF_WasInit", dynlib: ttfLibName.} +proc wasInit*(): cint{.cdecl, importc: "TTF_WasInit", dynlib: ttfLibName.} -proc VERSION*(X: var sdl.Tversion) = - X.major = MAJOR_VERSION - X.minor = MINOR_VERSION - X.patch = PATCHLEVEL +proc version*(x: var sdl.Tversion) = + x.major = MAJOR_VERSION + x.minor = MINOR_VERSION + x.patch = PATCHLEVEL -proc RenderText_Solid*(font: PFont, text: cstring, fg: TColor): PSurface{. +proc renderTextSolid*(font: PFont, text: cstring, fg: TColor): PSurface{. cdecl, importc: "TTF_RenderText_Solid", dynlib: ttfLibName.} diff --git a/lib/wrappers/sdl/smpeg.nim b/lib/wrappers/sdl/smpeg.nim index 33f317631..318c0b3df 100644 --- a/lib/wrappers/sdl/smpeg.nim +++ b/lib/wrappers/sdl/smpeg.nim @@ -125,7 +125,7 @@ # #****************************************************************************** -import +import sdl when defined(windows): @@ -143,29 +143,29 @@ const type TFilterInfo*{.final.} = object - yuv_mb_square_error*: PUint16 - yuv_pixel_square_error*: PUint16 + yuvMbSquareError*: PUInt16 + yuvPixelSquareError*: PUInt16 PFilterInfo* = ptr TFilterInfo # MPEG filter definition PFilter* = ptr TFilter # Callback functions for the filter TFilterCallback* = proc (dest, source: POverlay, region: PRect, - filter_info: PFilterInfo, data: Pointer): Pointer{. + filterInfo: PFilterInfo, data: pointer): pointer{. cdecl.} - TFilterDestroy* = proc (Filter: PFilter): Pointer{.cdecl.} # The filter definition itself + TFilterDestroy* = proc (filter: PFilter): pointer{.cdecl.} # The filter definition itself TFilter*{.final.} = object # The null filter (default). It simply copies the source rectangle to the video overlay. - flags*: Uint32 - data*: Pointer + flags*: uint32 + data*: pointer callback*: TFilterCallback destroy*: TFilterDestroy -proc filter_null*(): PFilter{.cdecl, importc: "SMPEGfilter_null", +proc filterNull*(): PFilter{.cdecl, importc: "SMPEGfilter_null", dynlib: SmpegLibName.} # The bilinear filter. A basic low-pass filter that will produce a smoother image. -proc filter_bilinear*(): PFilter{.cdecl, +proc filterBilinear*(): PFilter{.cdecl, importc: "SMPEGfilter_bilinear", dynlib: SmpegLibName.} # The deblocking filter. It filters block borders and non-intra coded blocks to reduce blockiness -proc filter_deblocking*(): PFilter{.cdecl, +proc filterDeblocking*(): PFilter{.cdecl, importc: "SMPEGfilter_deblocking", dynlib: SmpegLibName.} #------------------------------------------------------------------------------ # SMPEG.h @@ -175,28 +175,28 @@ const MINOR_VERSION* = 4 PATCHLEVEL* = 2 -type - TVersion*{.final.} = object +type + TVersion* = object major*: byte minor*: byte patch*: byte - Pversion* = ptr Tversion # This is the actual SMPEG object - TSMPEG*{.final.} = object + Pversion* = ptr TVersion # This is the actual SMPEG object + TSMPEG* = object PSMPEG* = ptr TSMPEG # Used to get information about the SMPEG object - TInfo*{.final.} = object - has_audio*: int32 - has_video*: int32 + TInfo* = object + hasAudio*: int32 + hasVideo*: int32 width*: int32 height*: int32 - current_frame*: int32 - current_fps*: float64 - audio_string*: array[0..79, char] - audio_current_frame*: int32 - current_offset*: UInt32 - total_size*: UInt32 - current_time*: float64 - total_time*: float64 + currentFrame*: int32 + currentFps*: float64 + audioString*: array[0..79, char] + audioCurrentFrame*: int32 + currentOffset*: uint32 + totalSize*: uint32 + currentTime*: float64 + totalTime*: float64 PInfo* = ptr TInfo # Possible MPEG status codes @@ -208,7 +208,7 @@ const type Tstatus* = int32 Pstatus* = ptr int32 # Matches the declaration of SDL_UpdateRect() - TDisplayCallback* = proc (dst: PSurface, x, y: int, w, h: int): Pointer{. + TDisplayCallback* = proc (dst: PSurface, x, y: int, w, h: int): pointer{. cdecl.} # Create a new SMPEG object from an MPEG file. # On return, if 'info' is not NULL, it will be filled with information # about the MPEG object. @@ -218,15 +218,15 @@ type # subsystem. If not, you will have to use the playaudio() function below # to extract the decoded data. -proc SMPEG_new*(theFile: cstring, info: PInfo, audio: int): PSMPEG{.cdecl, +proc new*(theFile: cstring, info: PInfo, audio: int): PSMPEG{.cdecl, importc: "SMPEG_new", dynlib: SmpegLibName.} # The same as above for a file descriptor -proc new_descr*(theFile: int, info: PInfo, audio: int): PSMPEG{. +proc newDescr*(theFile: int, info: PInfo, audio: int): PSMPEG{. cdecl, importc: "SMPEG_new_descr", dynlib: SmpegLibName.} # The same as above but for a raw chunk of data. SMPEG makes a copy of the # data, so the application is free to delete after a successful call to this # function. -proc new_data*(data: Pointer, size: int, info: PInfo, audio: int): PSMPEG{. +proc newData*(data: pointer, size: int, info: PInfo, audio: int): PSMPEG{. cdecl, importc: "SMPEG_new_data", dynlib: SmpegLibName.} # Get current information about an SMPEG object proc getinfo*(mpeg: PSMPEG, info: PInfo){.cdecl, @@ -247,13 +247,13 @@ proc status*(mpeg: PSMPEG): Tstatus{.cdecl, importc: "SMPEG_status", dynlib: SmpegLibName.} # status # Set the audio volume of an MPEG stream, in the range 0-100 -proc setvolume*(mpeg: PSMPEG, volume: int){.cdecl, +proc setVolume*(mpeg: PSMPEG, volume: int){.cdecl, importc: "SMPEG_setvolume", dynlib: SmpegLibName.} # Set the destination surface for MPEG video playback # 'surfLock' is a mutex used to synchronize access to 'dst', and can be NULL. # 'callback' is a function called when an area of 'dst' needs to be updated. # If 'callback' is NULL, the default function (SDL_UpdateRect) will be used. -proc setdisplay*(mpeg: PSMPEG, dst: PSurface, surfLock: Pmutex, +proc setDisplay*(mpeg: PSMPEG, dst: PSurface, surfLock: PMutex, callback: TDisplayCallback){.cdecl, importc: "SMPEG_setdisplay", dynlib: SmpegLibName.} # Set or clear looping play on an SMPEG object @@ -264,12 +264,12 @@ proc scaleXY*(mpeg: PSMPEG, width, height: int){.cdecl, importc: "SMPEG_scaleXY", dynlib: SmpegLibName.} proc scale*(mpeg: PSMPEG, scale: int){.cdecl, importc: "SMPEG_scale", dynlib: SmpegLibName.} -proc Double*(mpeg: PSMPEG, doubleit: bool) +proc double*(mpeg: PSMPEG, doubleit: bool) # Move the video display area within the destination surface proc move*(mpeg: PSMPEG, x, y: int){.cdecl, importc: "SMPEG_move", dynlib: SmpegLibName.} # Set the region of the video to be shown -proc setdisplayregion*(mpeg: PSMPEG, x, y, w, h: int){.cdecl, +proc setDisplayRegion*(mpeg: PSMPEG, x, y, w, h: int){.cdecl, importc: "SMPEG_setdisplayregion", dynlib: SmpegLibName.} # Play an SMPEG object proc play*(mpeg: PSMPEG){.cdecl, importc: "SMPEG_play", @@ -312,7 +312,7 @@ proc error*(mpeg: PSMPEG): cstring{.cdecl, importc: "SMPEG_error", proc playAudio*(mpeg: PSMPEG, stream: pointer, length: int): int{.cdecl, importc: "SMPEG_playAudio", dynlib: SmpegLibName.} # Wrapper for playAudio() that can be passed to SDL and SDL_mixer -proc playAudioSDL*(mpeg: Pointer, stream: pointer, length: int){.cdecl, +proc playAudioSDL*(mpeg: pointer, stream: pointer, length: int){.cdecl, importc: "SMPEG_playAudioSDL", dynlib: SmpegLibName.} # Get the best SDL audio spec for the audio stream proc wantedSpec*(mpeg: PSMPEG, wanted: PAudioSpec): int{.cdecl, @@ -322,14 +322,11 @@ proc actualSpec*(mpeg: PSMPEG, spec: PAudioSpec){.cdecl, importc: "SMPEG_actualSpec", dynlib: SmpegLibName.} # This macro can be used to fill a version structure with the compile-time # version of the SDL library. -proc GETVERSION*(X: var Tversion) -# implementation +proc getversion*(x: var TVersion) = + x.major = MAJOR_VERSION + x.minor = MINOR_VERSION + x.patch = PATCHLEVEL proc double(mpeg: PSMPEG, doubleit: bool) = if doubleit: scale(mpeg, 2) else: scale(mpeg, 1) - -proc GETVERSION(X: var Tversion) = - X.major = MAJOR_VERSION - X.minor = MINOR_VERSION - X.patch = PATCHLEVEL diff --git a/lib/wrappers/sphinx.nim b/lib/wrappers/sphinx.nim index b4e127c65..e4a282968 100644 --- a/lib/wrappers/sphinx.nim +++ b/lib/wrappers/sphinx.nim @@ -12,7 +12,7 @@ # did not, you can find it at http://www.gnu.org/ # -## Nimrod wrapper for ``sphinx``. +## Nim wrapper for ``sphinx``. {.deadCodeElim: on.} when defined(windows): diff --git a/lib/wrappers/sqlite3.nim b/lib/wrappers/sqlite3.nim index 7b7f0874e..e3a3fa0b8 100644 --- a/lib/wrappers/sqlite3.nim +++ b/lib/wrappers/sqlite3.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -88,8 +88,8 @@ const SQLITE_REINDEX* = 27 SQLITE_DENY* = 1 SQLITE_IGNORE* = 2 # Original from sqlite3.h: - ##define SQLITE_STATIC ((void(*)(void *))0) - ##define SQLITE_TRANSIENT ((void(*)(void *))-1) + #define SQLITE_STATIC ((void(*)(void *))0) + #define SQLITE_TRANSIENT ((void(*)(void *))-1) SQLITE_DETERMINISTIC* = 0x800 const @@ -178,15 +178,15 @@ proc errcode*(db: PSqlite3): int32{.cdecl, dynlib: Lib, importc: "sqlite3_errcod proc errmsg*(para1: PSqlite3): cstring{.cdecl, dynlib: Lib, importc: "sqlite3_errmsg".} proc errmsg16*(para1: PSqlite3): pointer{.cdecl, dynlib: Lib, importc: "sqlite3_errmsg16".} -proc prepare*(db: PSqlite3, zSql: cstring, nBytes: int32, ppStmt: var PStmt, +proc prepare*(db: PSqlite3, zSql: cstring, nBytes: int32, ppStmt: var Pstmt, pzTail: ptr cstring): int32{.cdecl, dynlib: Lib, importc: "sqlite3_prepare".} -proc prepare_v2*(db: PSqlite3, zSql: cstring, nByte: cint, ppStmt: var PStmt, +proc prepare_v2*(db: PSqlite3, zSql: cstring, nByte: cint, ppStmt: var Pstmt, pzTail: ptr cstring): cint {. importc: "sqlite3_prepare_v2", cdecl, dynlib: Lib.} -proc prepare16*(db: PSqlite3, zSql: pointer, nBytes: int32, ppStmt: var PStmt, +proc prepare16*(db: PSqlite3, zSql: pointer, nBytes: int32, ppStmt: var Pstmt, pzTail: var pointer): int32{.cdecl, dynlib: Lib, importc: "sqlite3_prepare16".} proc bind_blob*(para1: Pstmt, para2: int32, para3: pointer, n: int32, diff --git a/lib/wrappers/tinyc.nim b/lib/wrappers/tinyc.nim index f685c714d..ac6cb70f1 100644 --- a/lib/wrappers/tinyc.nim +++ b/lib/wrappers/tinyc.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2010 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/lib/wrappers/zip/libzip.nim b/lib/wrappers/zip/libzip.nim index 0b8d2b3ec..86670b450 100644 --- a/lib/wrappers/zip/libzip.nim +++ b/lib/wrappers/zip/libzip.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this @@ -64,15 +64,15 @@ type TZipSourceCallback* = proc (state: pointer, data: pointer, length: int, cmd: TZipSourceCmd): int {.cdecl.} PZipStat* = ptr TZipStat - TZipStat* = object ## the 'zip_stat' struct + TZipStat* = object ## the 'zip_stat' struct name*: cstring ## name of the file index*: int32 ## index within archive crc*: int32 ## crc of file data - mtime*: TTime ## modification time + mtime*: Time ## modification time size*: int ## size of file (uncompressed) - compSize*: int ## size of file (compressed) - compMethod*: int16 ## compression method used - encryptionMethod*: int16 ## encryption method used + compSize*: int ## size of file (compressed) + compMethod*: int16 ## compression method used + encryptionMethod*: int16 ## encryption method used TZip = object TZipSource = object @@ -225,7 +225,7 @@ proc zip_source_buffer*(para1: PZip, para2: pointer, para3: int, para4: int32): cdecl, mydll, importc: "zip_source_buffer".} proc zip_source_file*(para1: PZip, para2: cstring, para3: int, para4: int): PZipSource {. cdecl, mydll, importc: "zip_source_file".} -proc zip_source_filep*(para1: PZip, para2: TFile, para3: int, para4: int): PZipSource {. +proc zip_source_filep*(para1: PZip, para2: File, para3: int, para4: int): PZipSource {. cdecl, mydll, importc: "zip_source_filep".} proc zip_source_free*(para1: PZipSource) {.cdecl, mydll, importc: "zip_source_free".} diff --git a/lib/wrappers/zip/zlib.nim b/lib/wrappers/zip/zlib.nim index cb61783d2..e3530d566 100644 --- a/lib/wrappers/zip/zlib.nim +++ b/lib/wrappers/zip/zlib.nim @@ -14,32 +14,32 @@ type Ulong* = int Ulongf* = int Pulongf* = ptr Ulongf - z_off_t* = int32 - pbyte* = cstring - pbytef* = cstring - TAllocfunc* = proc (p: pointer, items: uInt, size: uInt): pointer{.cdecl.} + ZOffT* = int32 + Pbyte* = cstring + Pbytef* = cstring + TAllocfunc* = proc (p: pointer, items: Uint, size: Uint): pointer{.cdecl.} TFreeFunc* = proc (p: pointer, address: pointer){.cdecl.} TInternalState*{.final, pure.} = object - PInternalState* = ptr TInternalstate + PInternalState* = ptr TInternalState TZStream*{.final, pure.} = object - next_in*: pbytef - avail_in*: uInt - total_in*: uLong - next_out*: pbytef - avail_out*: uInt - total_out*: uLong - msg*: pbytef + nextIn*: Pbytef + availIn*: Uint + totalIn*: Ulong + nextOut*: Pbytef + availOut*: Uint + totalOut*: Ulong + msg*: Pbytef state*: PInternalState - zalloc*: TAllocFunc + zalloc*: TAllocfunc zfree*: TFreeFunc opaque*: pointer - data_type*: int32 - adler*: uLong - reserved*: uLong + dataType*: int32 + adler*: Ulong + reserved*: Ulong TZStreamRec* = TZStream PZstream* = ptr TZStream - gzFile* = pointer + GzFile* = pointer const Z_NO_FLUSH* = 0 @@ -78,79 +78,79 @@ proc inflate*(strm: var TZStream, flush: int32): int32{.cdecl, dynlib: libz, importc: "inflate".} proc inflateEnd*(strm: var TZStream): int32{.cdecl, dynlib: libz, importc: "inflateEnd".} -proc deflateSetDictionary*(strm: var TZStream, dictionary: pbytef, - dictLength: uInt): int32{.cdecl, dynlib: libz, +proc deflateSetDictionary*(strm: var TZStream, dictionary: Pbytef, + dictLength: Uint): int32{.cdecl, dynlib: libz, importc: "deflateSetDictionary".} -proc deflateCopy*(dest, source: var TZstream): int32{.cdecl, dynlib: libz, +proc deflateCopy*(dest, source: var TZStream): int32{.cdecl, dynlib: libz, importc: "deflateCopy".} proc deflateReset*(strm: var TZStream): int32{.cdecl, dynlib: libz, importc: "deflateReset".} proc deflateParams*(strm: var TZStream, level: int32, strategy: int32): int32{. cdecl, dynlib: libz, importc: "deflateParams".} -proc inflateSetDictionary*(strm: var TZStream, dictionary: pbytef, - dictLength: uInt): int32{.cdecl, dynlib: libz, +proc inflateSetDictionary*(strm: var TZStream, dictionary: Pbytef, + dictLength: Uint): int32{.cdecl, dynlib: libz, importc: "inflateSetDictionary".} proc inflateSync*(strm: var TZStream): int32{.cdecl, dynlib: libz, importc: "inflateSync".} proc inflateReset*(strm: var TZStream): int32{.cdecl, dynlib: libz, importc: "inflateReset".} -proc compress*(dest: pbytef, destLen: puLongf, source: pbytef, sourceLen: uLong): cint{. +proc compress*(dest: Pbytef, destLen: Pulongf, source: Pbytef, sourceLen: Ulong): cint{. cdecl, dynlib: libz, importc: "compress".} -proc compress2*(dest: pbytef, destLen: puLongf, source: pbytef, - sourceLen: uLong, level: cint): cint{.cdecl, dynlib: libz, +proc compress2*(dest: Pbytef, destLen: Pulongf, source: Pbytef, + sourceLen: Ulong, level: cint): cint{.cdecl, dynlib: libz, importc: "compress2".} -proc uncompress*(dest: pbytef, destLen: puLongf, source: pbytef, - sourceLen: uLong): cint{.cdecl, dynlib: libz, +proc uncompress*(dest: Pbytef, destLen: Pulongf, source: Pbytef, + sourceLen: Ulong): cint{.cdecl, dynlib: libz, importc: "uncompress".} -proc compressBound*(sourceLen: uLong): uLong {.cdecl, dynlib: libz, importc.} -proc gzopen*(path: cstring, mode: cstring): gzFile{.cdecl, dynlib: libz, +proc compressBound*(sourceLen: Ulong): Ulong {.cdecl, dynlib: libz, importc.} +proc gzopen*(path: cstring, mode: cstring): GzFile{.cdecl, dynlib: libz, importc: "gzopen".} -proc gzdopen*(fd: int32, mode: cstring): gzFile{.cdecl, dynlib: libz, +proc gzdopen*(fd: int32, mode: cstring): GzFile{.cdecl, dynlib: libz, importc: "gzdopen".} -proc gzsetparams*(thefile: gzFile, level: int32, strategy: int32): int32{.cdecl, +proc gzsetparams*(thefile: GzFile, level: int32, strategy: int32): int32{.cdecl, dynlib: libz, importc: "gzsetparams".} -proc gzread*(thefile: gzFile, buf: pointer, length: int): int32{.cdecl, +proc gzread*(thefile: GzFile, buf: pointer, length: int): int32{.cdecl, dynlib: libz, importc: "gzread".} -proc gzwrite*(thefile: gzFile, buf: pointer, length: int): int32{.cdecl, +proc gzwrite*(thefile: GzFile, buf: pointer, length: int): int32{.cdecl, dynlib: libz, importc: "gzwrite".} -proc gzprintf*(thefile: gzFile, format: pbytef): int32{.varargs, cdecl, +proc gzprintf*(thefile: GzFile, format: Pbytef): int32{.varargs, cdecl, dynlib: libz, importc: "gzprintf".} -proc gzputs*(thefile: gzFile, s: pbytef): int32{.cdecl, dynlib: libz, +proc gzputs*(thefile: GzFile, s: Pbytef): int32{.cdecl, dynlib: libz, importc: "gzputs".} -proc gzgets*(thefile: gzFile, buf: pbytef, length: int32): pbytef{.cdecl, +proc gzgets*(thefile: GzFile, buf: Pbytef, length: int32): Pbytef{.cdecl, dynlib: libz, importc: "gzgets".} -proc gzputc*(thefile: gzFile, c: char): char{.cdecl, dynlib: libz, +proc gzputc*(thefile: GzFile, c: char): char{.cdecl, dynlib: libz, importc: "gzputc".} -proc gzgetc*(thefile: gzFile): char{.cdecl, dynlib: libz, importc: "gzgetc".} -proc gzflush*(thefile: gzFile, flush: int32): int32{.cdecl, dynlib: libz, +proc gzgetc*(thefile: GzFile): char{.cdecl, dynlib: libz, importc: "gzgetc".} +proc gzflush*(thefile: GzFile, flush: int32): int32{.cdecl, dynlib: libz, importc: "gzflush".} -proc gzseek*(thefile: gzFile, offset: z_off_t, whence: int32): z_off_t{.cdecl, +proc gzseek*(thefile: GzFile, offset: ZOffT, whence: int32): ZOffT{.cdecl, dynlib: libz, importc: "gzseek".} -proc gzrewind*(thefile: gzFile): int32{.cdecl, dynlib: libz, importc: "gzrewind".} -proc gztell*(thefile: gzFile): z_off_t{.cdecl, dynlib: libz, importc: "gztell".} -proc gzeof*(thefile: gzFile): int {.cdecl, dynlib: libz, importc: "gzeof".} -proc gzclose*(thefile: gzFile): int32{.cdecl, dynlib: libz, importc: "gzclose".} -proc gzerror*(thefile: gzFile, errnum: var int32): pbytef{.cdecl, dynlib: libz, +proc gzrewind*(thefile: GzFile): int32{.cdecl, dynlib: libz, importc: "gzrewind".} +proc gztell*(thefile: GzFile): ZOffT{.cdecl, dynlib: libz, importc: "gztell".} +proc gzeof*(thefile: GzFile): int {.cdecl, dynlib: libz, importc: "gzeof".} +proc gzclose*(thefile: GzFile): int32{.cdecl, dynlib: libz, importc: "gzclose".} +proc gzerror*(thefile: GzFile, errnum: var int32): Pbytef{.cdecl, dynlib: libz, importc: "gzerror".} -proc adler32*(adler: uLong, buf: pbytef, length: uInt): uLong{.cdecl, +proc adler32*(adler: Ulong, buf: Pbytef, length: Uint): Ulong{.cdecl, dynlib: libz, importc: "adler32".} ## **Warning**: Adler-32 requires at least a few hundred bytes to get rolling. -proc crc32*(crc: uLong, buf: pbytef, length: uInt): uLong{.cdecl, dynlib: libz, +proc crc32*(crc: Ulong, buf: Pbytef, length: Uint): Ulong{.cdecl, dynlib: libz, importc: "crc32".} proc deflateInitu*(strm: var TZStream, level: int32, version: cstring, - stream_size: int32): int32{.cdecl, dynlib: libz, + streamSize: int32): int32{.cdecl, dynlib: libz, importc: "deflateInit_".} proc inflateInitu*(strm: var TZStream, version: cstring, - stream_size: int32): int32 {. + streamSize: int32): int32 {. cdecl, dynlib: libz, importc: "inflateInit_".} proc deflateInit*(strm: var TZStream, level: int32): int32 proc inflateInit*(strm: var TZStream): int32 proc deflateInit2u*(strm: var TZStream, level: int32, `method`: int32, windowBits: int32, memLevel: int32, strategy: int32, - version: cstring, stream_size: int32): int32 {.cdecl, + version: cstring, streamSize: int32): int32 {.cdecl, dynlib: libz, importc: "deflateInit2_".} proc inflateInit2u*(strm: var TZStream, windowBits: int32, version: cstring, - stream_size: int32): int32{.cdecl, dynlib: libz, + streamSize: int32): int32{.cdecl, dynlib: libz, importc: "inflateInit2_".} proc deflateInit2*(strm: var TZStream, level, `method`, windowBits, memLevel, @@ -159,29 +159,29 @@ proc inflateInit2*(strm: var TZStream, windowBits: int32): int32 proc zError*(err: int32): cstring{.cdecl, dynlib: libz, importc: "zError".} proc inflateSyncPoint*(z: PZstream): int32{.cdecl, dynlib: libz, importc: "inflateSyncPoint".} -proc get_crc_table*(): pointer{.cdecl, dynlib: libz, importc: "get_crc_table".} +proc getCrcTable*(): pointer{.cdecl, dynlib: libz, importc: "get_crc_table".} proc deflateInit(strm: var TZStream, level: int32): int32 = - result = deflateInitu(strm, level, ZLIB_VERSION(), sizeof(TZStream).cint) + result = deflateInitu(strm, level, zlibVersion(), sizeof(TZStream).cint) proc inflateInit(strm: var TZStream): int32 = - result = inflateInitu(strm, ZLIB_VERSION(), sizeof(TZStream).cint) + result = inflateInitu(strm, zlibVersion(), sizeof(TZStream).cint) proc deflateInit2(strm: var TZStream, level, `method`, windowBits, memLevel, strategy: int32): int32 = result = deflateInit2u(strm, level, `method`, windowBits, memLevel, - strategy, ZLIB_VERSION(), sizeof(TZStream).cint) + strategy, zlibVersion(), sizeof(TZStream).cint) proc inflateInit2(strm: var TZStream, windowBits: int32): int32 = - result = inflateInit2u(strm, windowBits, ZLIB_VERSION(), + result = inflateInit2u(strm, windowBits, zlibVersion(), sizeof(TZStream).cint) -proc zlibAllocMem*(AppData: Pointer, Items, Size: int): Pointer {.cdecl.} = - result = Alloc(Items * Size) +proc zlibAllocMem*(appData: pointer, items, size: int): pointer {.cdecl.} = + result = alloc(items * size) -proc zlibFreeMem*(AppData, `Block`: Pointer) {.cdecl.} = - dealloc(`Block`) +proc zlibFreeMem*(appData, `block`: pointer) {.cdecl.} = + dealloc(`block`) proc uncompress*(sourceBuf: cstring, sourceLen: int): string = ## Given a deflated cstring returns its inflated version. @@ -202,7 +202,7 @@ proc uncompress*(sourceBuf: cstring, sourceLen: int): string = var z: TZStream # Initialize input. - z.next_in = sourceBuf + z.nextIn = sourceBuf # Input left to decompress. var left = zlib.Uint(sourceLen) @@ -220,12 +220,12 @@ proc uncompress*(sourceBuf: cstring, sourceLen: int): string = var decompressed = newStringOfCap(space) # Initialize output. - z.next_out = addr(decompressed[0]) + z.nextOut = addr(decompressed[0]) # Output generated so far. var have = 0 # Set up for gzip decoding. - z.avail_in = 0; + z.availIn = 0; var status = inflateInit2(z, (15+16)) if status != Z_OK: # Out of memory. @@ -241,10 +241,10 @@ proc uncompress*(sourceBuf: cstring, sourceLen: int): string = discard inflateReset(z) # Provide input for inflate. - if z.avail_in == 0: + if z.availIn == 0: # This only makes sense in the C version using unsigned values. - z.avail_in = left - left -= z.avail_in + z.availIn = left + left -= z.availIn # Decompress the available input. while true: @@ -260,15 +260,15 @@ proc uncompress*(sourceBuf: cstring, sourceLen: int): string = # Increase space. decompressed.setLen(space) # Update output pointer (might have moved). - z.next_out = addr(decompressed[have]) + z.nextOut = addr(decompressed[have]) # Provide output space for inflate. - z.avail_out = zlib.Uint(space - have) - have += z.avail_out; + z.availOut = zlib.Uint(space - have) + have += z.availOut; # Inflate and update the decompressed size. status = inflate(z, Z_SYNC_FLUSH); - have -= z.avail_out; + have -= z.availOut; # Bail out if any errors. if status != Z_OK and status != Z_BUF_ERROR and status != Z_STREAM_END: @@ -279,10 +279,10 @@ proc uncompress*(sourceBuf: cstring, sourceLen: int): string = # Repeat until all output is generated from provided input (note # that even if z.avail_in is zero, there may still be pending # output -- we're not done until the output buffer isn't filled) - if z.avail_out != 0: + if z.availOut != 0: break # Continue until all input consumed. - if left == 0 and z.avail_in == 0: + if left == 0 and z.availIn == 0: break # Verify that the input is a valid gzip stream. diff --git a/lib/wrappers/zip/zzip.nim b/lib/wrappers/zip/zzip.nim index a656322ee..73fd53c34 100644 --- a/lib/wrappers/zip/zzip.nim +++ b/lib/wrappers/zip/zzip.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2008 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/readme.md b/readme.md index 85bf480a0..d5968982b 100644 --- a/readme.md +++ b/readme.md @@ -1,15 +1,15 @@ -# Nimrod Compiler -This repo contains the Nimrod compiler, Nimrod's stdlib, tools and +# Nim Compiler +This repo contains the Nim compiler, Nim's stdlib, tools and documentation. ## Compiling -Compiling the Nimrod compiler is quite straightforward. Because -the Nimrod compiler itself is written in the Nimrod programming language +Compiling the Nim compiler is quite straightforward. Because +the Nim compiler itself is written in the Nim programming language the C source of an older version of the compiler are needed to bootstrap the -latest version. The C sources are available in a separate repo [here](http://github.com/nimrod-code/csources). +latest version. The C sources are available in a separate repo [here](http://github.com/nim-lang/csources). Pre-compiled snapshots of the compiler are also available on -[Nimbuild](http://build.nimrod-lang.org/). Your platform however may not +[Nimbuild](http://build.nim-lang.org/). Your platform however may not currently be built for. The compiler currently supports the following platform and architecture @@ -30,35 +30,44 @@ To build from source you will need: If you are on a fairly modern *nix system, the following steps should work: ``` -$ git clone git://github.com/Araq/Nimrod.git -$ cd Nimrod -$ git clone --depth 1 git://github.com/nimrod-code/csources +$ git clone git://github.com/Araq/Nim.git +$ cd Nim +$ git clone --depth 1 git://github.com/nim-lang/csources $ cd csources && sh build.sh $ cd .. -$ bin/nimrod c koch +$ bin/nim c koch $ ./koch boot -d:release ``` -Add Nimrod to your PATH afterwards. +``koch install [dir]`` may then be used to install Nim, or you can simply +add it to your PATH. More ``koch`` related options are documented in +[doc/koch.txt](doc/koch.txt). The above steps can be performed on Windows in a similar fashion, the ``build.bat`` and ``build64.bat`` (for x86_64 systems) are provided to be used instead of ``build.sh``. ## Getting help -A [forum](http://forum.nimrod-lang.org/) is available if you have any +A [forum](http://forum.nim-lang.org/) is available if you have any questions, and you can also get help in the IRC channel on -[Freenode](irc://irc.freenode.net/nimrod) in #nimrod. If you ask questions on -[StackOverflow use the nimrod -tag](http://stackoverflow.com/questions/tagged/nimrod). +[Freenode](irc://irc.freenode.net/nim) in #nim. If you ask questions on +[StackOverflow use the nim +tag](http://stackoverflow.com/questions/tagged/nim). ## License The compiler and the standard library are licensed under the MIT license, except for some modules where the documentation suggests otherwise. This means -that you can use any license for your own programs developed with Nimrod, +that you can use any license for your own programs developed with Nim, allowing you to create commercial applications. Read copying.txt for more details. Copyright (c) 2006-2014 Andreas Rumpf. All rights reserved. + +# Build Status +| |Linux|Windows|Mac| +|---|---|---|---| +| x86 | ![](http://178.62.143.63:8010/buildstatusimage?builder=linux-x32-builder) | ![](http://178.62.143.63:8010/buildstatusimage?builder=windows-x32-builder) | ![](http://178.62.143.63:8010/buildstatusimage?builder=mac-x32-builder) +| x86_64 | ![](http://178.62.143.63:8010/buildstatusimage?builder=linux-x64-builder) | ![](http://178.62.143.63:8010/buildstatusimage?builder=windows-x64-builder) | ![](http://178.62.143.63:8010/buildstatusimage?builder=mac-x64-builder) +| arm | ![](http://178.62.143.63:8010/buildstatusimage?builder=linux-arm5-builder) | diff --git a/readme.txt b/readme.txt index f9e5054fe..e3119f83d 100644 --- a/readme.txt +++ b/readme.txt @@ -1,15 +1,15 @@ -This package contains the Nimrod compiler, Nimrod's stdlib, tools and +This package contains the Nim compiler, Nim's stdlib, tools and documentation. -Nimrod is a compiled, garbage-collected systems programming language which has -an excellent productivity/performance ratio. Nimrod's design focuses on +Nim is a compiled, garbage-collected systems programming language which has +an excellent productivity/performance ratio. Nim's design focuses on efficiency, expressiveness, elegance (in the order of priority). Read install.txt for instructions of how to build and install it. The compiler and the standard library are licensed under the MIT license, except for some modules where the documentation suggests otherwise. This means -that you can use any license for your own programs developed with Nimrod, +that you can use any license for your own programs developed with Nim, allowing you to create commercial applications. Read copying.txt for more details. diff --git a/tests/actiontable/tactiontable2.nim b/tests/actiontable/tactiontable2.nim index bfeb1c169..99bb3dca0 100644 --- a/tests/actiontable/tactiontable2.nim +++ b/tests/actiontable/tactiontable2.nim @@ -1,6 +1,6 @@ discard """ line: 21 - errormsg: "invalid type: 'TTable[string, proc (string){.gcsafe.}]'" + errormsg: "invalid type: 'Table[string, proc (string){.gcsafe.}]'" """ import tables diff --git a/tests/array/tarray.nim b/tests/array/tarray.nim index 16ef558d1..01fbdf422 100644 --- a/tests/array/tarray.nim +++ b/tests/array/tarray.nim @@ -1,6 +1,6 @@ discard """ file: "tarray.nim" - output: "10012" + output: "100124" """ # simple check for one dimensional arrays @@ -30,4 +30,11 @@ write(stdout, sum([])) write(stdout, getPos( (x: 5, y: 7) )) #OUT 10012 +# bug #1669 +let filesToCreate = ["tempdir/fl1.a", "tempdir/fl2.b", + "tempdir/tempdir2/fl3.e", "tempdir/tempdir2/tempdir3/fl4.f"] + +var found: array[0..filesToCreate.high, bool] + +echo found.len diff --git a/tests/assert/tassert.nim b/tests/assert/tassert.nim index 0ea8d2034..b3df30bd1 100644 --- a/tests/assert/tassert.nim +++ b/tests/assert/tassert.nim @@ -5,7 +5,7 @@ discard """ """ # test assert and exception handling -proc callB() = assert(False) +proc callB() = assert(false) proc callA() = callB() proc callC() = callA() diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim index 2d65db4bd..13d531387 100644 --- a/tests/async/tasyncawait.nim +++ b/tests/async/tasyncawait.nim @@ -21,15 +21,8 @@ proc launchSwarm(port: TPort) {.async.} = var sock = newAsyncRawSocket() await connect(sock, "localhost", port) - when true: - await sendMessages(sock) - closeSocket(sock) - else: - # Issue #932: https://github.com/Araq/Nimrod/issues/932 - var msgFut = sendMessages(sock) - msgFut.callback = - proc () = - closeSocket(sock) + await sendMessages(sock) + closeSocket(sock) proc readMessages(client: TAsyncFD) {.async.} = while true: @@ -47,22 +40,20 @@ proc readMessages(client: TAsyncFD) {.async.} = proc createServer(port: TPort) {.async.} = var server = newAsyncRawSocket() block: - var name: TSockaddr_in + var name: Sockaddr_in when defined(windows): name.sin_family = toInt(AF_INET).int16 else: name.sin_family = toInt(AF_INET) name.sin_port = htons(int16(port)) name.sin_addr.s_addr = htonl(INADDR_ANY) - if bindAddr(server.TSocketHandle, cast[ptr TSockAddr](addr(name)), - sizeof(name).TSocklen) < 0'i32: - osError(osLastError()) + if bindAddr(server.SocketHandle, cast[ptr SockAddr](addr(name)), + sizeof(name).Socklen) < 0'i32: + raiseOSError(osLastError()) - discard server.TSocketHandle.listen() + discard server.SocketHandle.listen() while true: - var client = await accept(server) - asyncCheck readMessages(client) - # TODO: Test: readMessages(disp, await disp.accept(server)) + asyncCheck readMessages(await accept(server)) asyncCheck createServer(TPort(10335)) asyncCheck launchSwarm(TPort(10335)) diff --git a/tests/async/tasyncdiscard.nim b/tests/async/tasyncdiscard.nim index 966851acc..71aba29e2 100644 --- a/tests/async/tasyncdiscard.nim +++ b/tests/async/tasyncdiscard.nim @@ -13,7 +13,7 @@ discard """ import asyncio, asyncdispatch, asyncnet proc main {.async.} = - proc f: PFuture[int] {.async.} = + proc f: Future[int] {.async.} = discard echo 1 discard @@ -24,7 +24,7 @@ proc main {.async.} = echo x echo 3 - proc g: PFuture[int] {.async.} = + proc g: Future[int] {.async.} = discard echo 4 discard diff --git a/tests/async/tasyncexceptions.nim b/tests/async/tasyncexceptions.nim index ca73c6a3d..30ef41756 100644 --- a/tests/async/tasyncexceptions.nim +++ b/tests/async/tasyncexceptions.nim @@ -1,15 +1,15 @@ discard """ file: "tasyncexceptions.nim" exitcode: 1 - outputsub: "Error: unhandled exception: foobar [E_Base]" + outputsub: "Error: unhandled exception: foobar [Exception]" """ import asyncdispatch -proc accept(): PFuture[int] {.async.} = +proc accept(): Future[int] {.async.} = await sleepAsync(100) result = 4 -proc recvLine(fd: int): PFuture[string] {.async.} = +proc recvLine(fd: int): Future[string] {.async.} = await sleepAsync(100) return "get" diff --git a/tests/async/tasyncfile.nim b/tests/async/tasyncfile.nim new file mode 100644 index 000000000..c3cf33512 --- /dev/null +++ b/tests/async/tasyncfile.nim @@ -0,0 +1,36 @@ +discard """ + file: "tasyncfile.nim" + exitcode: 0 +""" +import asyncfile, asyncdispatch, os + +proc main() {.async.} = + let fn = getTempDir() / "foobar.txt" + removeFile(fn) + + # Simple write/read test. + block: + var file = openAsync(fn, fmReadWrite) + await file.write("test") + file.setFilePos(0) + await file.write("foo") + file.setFilePos(0) + let data = await file.readAll() + doAssert data == "foot" + file.close() + + # Append test + block: + var file = openAsync(fn, fmAppend) + await file.write("\ntest2") + let errorTest = file.readAll() + await errorTest + doAssert errorTest.failed + file.close() + file = openAsync(fn, fmRead) + let data = await file.readAll() + + doAssert data == "foot\ntest2" + file.close() + +waitFor main() diff --git a/tests/async/tasynciossl.nim b/tests/async/tasynciossl.nim index 6b38fcf7b..118b9e74d 100644 --- a/tests/async/tasynciossl.nim +++ b/tests/async/tasynciossl.nim @@ -1,6 +1,6 @@ discard """ file: "tasynciossl.nim" - cmd: "nimrod $target --hints:on --define:ssl $options $file" + cmd: "nim $target --hints:on --define:ssl $options $file" output: "20000" """ import sockets, asyncio, strutils, times @@ -47,7 +47,7 @@ proc serverAccept(s: PAsyncSocket) = proc launchSwarm(disp: var PDispatcher, port: TPort, count: int, buffered = true, useSSL = false) = for i in 1..count: - var client = AsyncSocket() + var client = asyncSocket() when defined(ssl): if useSSL: ctx1.wrapSocket(client) @@ -56,7 +56,7 @@ proc launchSwarm(disp: var PDispatcher, port: TPort, count: int, client.connect("localhost", port) proc createSwarm(port: TPort, buffered = true, useSSL = false) = - var server = AsyncSocket() + var server = asyncSocket() when defined(ssl): if useSSL: ctx.wrapSocket(server) diff --git a/tests/async/tasynctry.nim b/tests/async/tasynctry.nim new file mode 100644 index 000000000..66ea40d49 --- /dev/null +++ b/tests/async/tasynctry.nim @@ -0,0 +1,51 @@ +discard """ + file: "tasynctry.nim" + exitcode: 0 + output: ''' +Generic except: Test +Specific except +Multiple idents in except +Multiple except branches +Multiple except branches 2 +''' +""" +import asyncdispatch + +# Here we are testing the ability to catch exceptions. + +proc foobar() {.async.} = + if 5 == 5: + raise newException(EInvalidIndex, "Test") + +proc catch() {.async.} = + # TODO: Create a test for when exceptions are not caught. + try: + await foobar() + except: + echo("Generic except: ", getCurrentExceptionMsg()) + + try: + await foobar() + except EInvalidIndex: + echo("Specific except") + + try: + await foobar() + except OSError, EInvalidField, EInvalidIndex: + echo("Multiple idents in except") + + try: + await foobar() + except OSError, EInvalidField: + assert false + except EInvalidIndex: + echo("Multiple except branches") + + try: + await foobar() + except EInvalidIndex: + echo("Multiple except branches 2") + except OSError, EInvalidField: + assert false + +asyncCheck catch() diff --git a/tests/async/tasyncudp.nim b/tests/async/tasyncudp.nim index fd7f3d568..2a7ed40bf 100644 --- a/tests/async/tasyncudp.nim +++ b/tests/async/tasyncudp.nim @@ -42,14 +42,14 @@ proc swarmConnect(s: PAsyncSocket) = proc createClient(disp: var PDispatcher, port: TPort, buffered = true) = currentClient.inc() - var client = AsyncSocket(typ = SOCK_DGRAM, protocol = IPPROTO_UDP, + var client = asyncSocket(typ = SOCK_DGRAM, protocol = IPPROTO_UDP, buffered = buffered) client.handleConnect = swarmConnect disp.register(client) client.connect("localhost", port) proc createServer(port: TPort, buffered = true) = - var server = AsyncSocket(typ = SOCK_DGRAM, protocol = IPPROTO_UDP, + var server = asyncSocket(typ = SOCK_DGRAM, protocol = IPPROTO_UDP, buffered = buffered) server.handleRead = serverRead disp.register(server) @@ -75,4 +75,4 @@ while true: break assert msgCount == messagesToSend * serverCount * swarmSize -echo(msgCount) \ No newline at end of file +echo(msgCount) diff --git a/tests/async/tnestedpfuturetypeparam.nim b/tests/async/tnestedpfuturetypeparam.nim index 1db442170..bf346ff8e 100644 --- a/tests/async/tnestedpfuturetypeparam.nim +++ b/tests/async/tnestedpfuturetypeparam.nim @@ -1,8 +1,8 @@ import asyncdispatch, asyncnet proc main {.async.} = - proc f: PFuture[seq[int]] {.async.} = - await newAsyncSocket().connect("www.google.com", TPort(80)) + proc f: Future[seq[int]] {.async.} = + await newAsyncSocket().connect("www.google.com", Port(80)) let x = await f() asyncCheck main() diff --git a/tests/benchmark.nim b/tests/benchmark.nim index 8dd9cc2e2..0613d1bf9 100644 --- a/tests/benchmark.nim +++ b/tests/benchmark.nim @@ -1,6 +1,6 @@ # # -# Nimrod Benchmark tool +# Nim Benchmark tool # (c) Copyright 2012 Dominik Picheta # # See the file "copying.txt", included in this @@ -15,7 +15,7 @@ type proc compileBench(file: string) = ## Compiles ``file``. - doAssert(execCmdEx("nimrod c -d:release " & file).exitCode == QuitSuccess) + doAssert(execCmdEx("nim c -d:release " & file).exitCode == QuitSuccess) proc runBench(file: string): TBenchResult = ## Runs ``file`` and returns info on how long it took to run. diff --git a/tests/bind/tnicerrorforsymchoice.nim b/tests/bind/tnicerrorforsymchoice.nim index 4e7a271b3..e1ff090dd 100644 --- a/tests/bind/tnicerrorforsymchoice.nim +++ b/tests/bind/tnicerrorforsymchoice.nim @@ -1,17 +1,17 @@ discard """ line: 18 - errormsg: "type mismatch: got (proc (TScgi) | proc (PAsyncSocket, PStringTable, string){.gcsafe.})" + errormsg: "type mismatch: got (proc (TScgi) | proc (AsyncSocket, StringTableRef, string){.gcsafe.})" """ #bug #442 import scgi, sockets, asyncio, strtabs -proc handleSCGIRequest[TScgi: TScgiState | PAsyncScgiState](s: TScgi) = +proc handleSCGIRequest[TScgi: ScgiState | AsyncScgiState](s: TScgi) = discard -proc handleSCGIRequest(client: PAsyncSocket, headers: PStringTable, +proc handleSCGIRequest(client: AsyncSocket, headers: StringTableRef, input: string) = discard -proc test(handle: proc (client: PAsyncSocket, headers: PStringTable, +proc test(handle: proc (client: AsyncSocket, headers: StringTableRef, input: string), b: int) = discard diff --git a/tests/borrow/tborrow.nim b/tests/borrow/tborrow.nim index 8e6aeba74..d4df5f524 100644 --- a/tests/borrow/tborrow.nim +++ b/tests/borrow/tborrow.nim @@ -18,4 +18,4 @@ proc `++`(x, y: DF): DF {.borrow.} proc `$`(x: DI): string {.borrow.} proc `$`(x: DF): string {.borrow.} -echo 4544.DI ++ 343.di, " ", (4.5.df ++ 0.5.df).float == 5.0 +echo 4544.DI ++ 343.DI, " ", (4.5.DF ++ 0.5.DF).float == 5.0 diff --git a/tests/casestmt/tcasestm.nim b/tests/casestmt/tcasestm.nim index b033b98ec..a21b6be0d 100644 --- a/tests/casestmt/tcasestm.nim +++ b/tests/casestmt/tcasestm.nim @@ -5,7 +5,7 @@ discard """ # Test the case statement type - tenum = enum eA, eB, eC + Tenum = enum eA, eB, eC var x: string = "yyy" diff --git a/tests/ccgbugs/tccgen1.nim b/tests/ccgbugs/tccgen1.nim index 137dd545d..9234bbd6c 100644 --- a/tests/ccgbugs/tccgen1.nim +++ b/tests/ccgbugs/tccgen1.nim @@ -45,7 +45,7 @@ type PText* = ref Text Text = object of CharacterData - PComment* = ref comment + PComment* = ref Comment Comment = object of CharacterData PCDataSection* = ref CDataSection diff --git a/tests/ccgbugs/tcgbug.nim b/tests/ccgbugs/tcgbug.nim index 3e4755f2f..92e7b3a66 100644 --- a/tests/ccgbugs/tcgbug.nim +++ b/tests/ccgbugs/tcgbug.nim @@ -35,3 +35,4 @@ type cb: proc (future: PFuture[T]) {.closure.} var k = PFuture[void]() + diff --git a/tests/ccgbugs/tcodegenbug1.nim b/tests/ccgbugs/tcodegenbug1.nim index 7d0fc4ad5..9b9771485 100644 --- a/tests/ccgbugs/tcodegenbug1.nim +++ b/tests/ccgbugs/tcodegenbug1.nim @@ -51,7 +51,7 @@ proc `$`*(status: TStatusEnum): string = return "unknown" proc makeCommitPath*(platform, hash: string): string = - return platform / "nimrod_" & hash.substr(0, 11) # 11 Chars. + return platform / "nim_" & hash.substr(0, 11) # 11 Chars. type TFlag = enum diff --git a/tests/ccgbugs/tconstobj.nim b/tests/ccgbugs/tconstobj.nim new file mode 100644 index 000000000..98f441e83 --- /dev/null +++ b/tests/ccgbugs/tconstobj.nim @@ -0,0 +1,16 @@ +discard """ + output: '''(FirstName: James, LastName: Franco)''' +""" + +# bug #1547 +import tables + +type Person* = object + FirstName*: string + LastName*: string + +let people = { + "001": Person(FirstName: "James", LastName: "Franco") +}.toTable() + +echo people["001"] diff --git a/tests/ccgbugs/tcvarargs.nim b/tests/ccgbugs/tcvarargs.nim new file mode 100644 index 000000000..ebaf83a4a --- /dev/null +++ b/tests/ccgbugs/tcvarargs.nim @@ -0,0 +1,34 @@ +discard """ + output: '''17 +17 +17 +17 +17 +17 +''' +""" + +# bug #1593 + +{.emit: """ +#include + +void foo(int n, ...) { + NI64 k; + int i; + va_list argp; + va_start(argp, n); + for (i = 1; i <= n; i++) { + k = va_arg(argp, NI64); + printf("%lld\n", (long long)k); + } + va_end(argp); +} +""".} + +proc foo(x: cint) {.importc, varargs, nodecl.} + +proc main() = + const k = 17'i64 + foo(6, k, k, k, k, k, k) +main() diff --git a/tests/ccg/tmissingbracket.nim b/tests/ccgbugs/tmissingbracket.nim similarity index 96% rename from tests/ccg/tmissingbracket.nim rename to tests/ccgbugs/tmissingbracket.nim index ba681ebda..886884d0c 100644 --- a/tests/ccg/tmissingbracket.nim +++ b/tests/ccgbugs/tmissingbracket.nim @@ -34,7 +34,7 @@ proc newInstance(T: typedesc): T = var o = TObj.newInstance() type - TestObj* = object of TObject + TestObj* = object of RootObj t:int SubObject* = object of TestObj diff --git a/tests/ccgbugs/tmissingvolatile.nim b/tests/ccgbugs/tmissingvolatile.nim new file mode 100644 index 000000000..4d25e5c22 --- /dev/null +++ b/tests/ccgbugs/tmissingvolatile.nim @@ -0,0 +1,20 @@ +discard """ + output: "1" + cmd: r"nim c --hints:on $options -d:release $file" + ccodecheck: "'NI volatile state;'" +""" + +# bug #1539 + +proc err() = + raise newException(Exception, "test") + +proc main() = + var state: int + try: + state = 1 + err() + except: + echo state + +main() diff --git a/tests/clearmsg/mb.nim b/tests/clearmsg/mb.nim new file mode 100644 index 000000000..2d21e2396 --- /dev/null +++ b/tests/clearmsg/mb.nim @@ -0,0 +1,2 @@ +type + typ* = distinct string diff --git a/tests/clearmsg/mc.nim b/tests/clearmsg/mc.nim new file mode 100644 index 000000000..79d7431df --- /dev/null +++ b/tests/clearmsg/mc.nim @@ -0,0 +1,3 @@ + +type + typ* = distinct int diff --git a/tests/clearmsg/ta.nim b/tests/clearmsg/ta.nim new file mode 100644 index 000000000..b21522d12 --- /dev/null +++ b/tests/clearmsg/ta.nim @@ -0,0 +1,12 @@ +discard """ + errormsg: 'type mismatch: got (mc.typ)' + line: 12 +""" + +import mb, mc + +proc test(testing: mb.typ) = + discard + +var s: mc.typ +test(s) diff --git a/tests/closure/tclosure.nim b/tests/closure/tclosure.nim index 764aaa97d..445a99b6d 100644 --- a/tests/closure/tclosure.nim +++ b/tests/closure/tclosure.nim @@ -36,7 +36,7 @@ myData.each do (x: int): type ITest = tuple[ - setter: proc(v: Int), + setter: proc(v: int), getter: proc(): int] proc getInterf(): ITest = diff --git a/tests/closure/tclosurebug2.nim b/tests/closure/tclosurebug2.nim index ec4f0045b..12e4e3509 100644 --- a/tests/closure/tclosurebug2.nim +++ b/tests/closure/tclosurebug2.nim @@ -38,17 +38,17 @@ template rawInsertImpl() {.dirty.} = data[h].val = val data[h].slot = seFilled -template AddImpl() {.dirty.} = - if mustRehash(len(t.data), t.counter): Enlarge(t) - RawInsert(t, t.data, key, val) +template addImpl() {.dirty.} = + if mustRehash(len(t.data), t.counter): enlarge(t) + rawInsert(t, t.data, key, val) inc(t.counter) -template PutImpl() {.dirty.} = - var index = RawGet(t, key) +template putImpl() {.dirty.} = + var index = rawGet(t, key) if index >= 0: t.data[index].val = val else: - AddImpl() + addImpl() proc len*[A, B](t: TOrderedTable[A, B]): int {.inline.} = ## returns the number of keys in `t`. @@ -89,7 +89,7 @@ iterator mvalues*[A, B](t: var TOrderedTable[A, B]): var B = forAllOrderedPairs: yield t.data[h].val -proc RawGet[A, B](t: TOrderedTable[A, B], key: A): int = +proc rawGet[A, B](t: TOrderedTable[A, B], key: A): int = rawGetImpl() proc `[]`*[A, B](t: TOrderedTable[A, B], key: A): B = @@ -97,21 +97,21 @@ proc `[]`*[A, B](t: TOrderedTable[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 index = RawGet(t, key) + var index = rawGet(t, key) if index >= 0: result = t.data[index].val proc mget*[A, B](t: var TOrderedTable[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 index = RawGet(t, key) + var index = rawGet(t, key) if index >= 0: result = t.data[index].val - else: raise newException(EInvalidKey, "key not found: " & $key) + else: raise newException(KeyError, "key not found: " & $key) proc hasKey*[A, B](t: TOrderedTable[A, B], key: A): bool = ## returns true iff `key` is in the table `t`. result = rawGet(t, key) >= 0 -proc RawInsert[A, B](t: var TOrderedTable[A, B], +proc rawInsert[A, B](t: var TOrderedTable[A, B], data: var TOrderedKeyValuePairSeq[A, B], key: A, val: B) = rawInsertImpl() @@ -120,7 +120,7 @@ proc RawInsert[A, B](t: var TOrderedTable[A, B], if t.last >= 0: data[t.last].next = h t.last = h -proc Enlarge[A, B](t: var TOrderedTable[A, B]) = +proc enlarge[A, B](t: var TOrderedTable[A, B]) = var n: TOrderedKeyValuePairSeq[A, B] newSeq(n, len(t.data) * growthFactor) var h = t.first @@ -129,7 +129,7 @@ proc Enlarge[A, B](t: var TOrderedTable[A, B]) = while h >= 0: var nxt = t.data[h].next if t.data[h].slot == seFilled: - RawInsert(t, n, t.data[h].key, t.data[h].val) + rawInsert(t, n, t.data[h].key, t.data[h].val) h = nxt swap(t.data, n) @@ -139,7 +139,7 @@ proc `[]=`*[A, B](t: var TOrderedTable[A, B], key: A, val: B) = proc add*[A, B](t: var TOrderedTable[A, B], key: A, val: B) = ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. - AddImpl() + addImpl() proc initOrderedTable*[A, B](initialSize=64): TOrderedTable[A, B] = ## creates a new ordered hash table that is empty. `initialSize` needs to be diff --git a/tests/closure/tinvalidclosure.nim b/tests/closure/tinvalidclosure.nim index 06e19df3c..18968a6c6 100644 --- a/tests/closure/tinvalidclosure.nim +++ b/tests/closure/tinvalidclosure.nim @@ -1,12 +1,12 @@ discard """ line: 12 - errormsg: "type mismatch: got (proc (int){.closure, gcsafe.})" + errormsg: "type mismatch: got (proc (int){.closure, gcsafe, locks: 0.})" """ proc ugh[T](x: T) {.closure.} = echo "ugha" -proc takeCdecl(p: proc (x: int) {.cdecl.}) = nil +proc takeCdecl(p: proc (x: int) {.cdecl.}) = discard takeCDecl(ugh[int]) diff --git a/tests/compiles/tevilcompiles.nim b/tests/compiles/tevilcompiles.nim new file mode 100644 index 000000000..0930507d1 --- /dev/null +++ b/tests/compiles/tevilcompiles.nim @@ -0,0 +1,12 @@ +# bug #1055 +import unittest +type TMatrix*[N,M: static[int], T] = object + data*: array[0..N*M-1, T] +proc `==`*(a: distinct TMatrix; b: distinct TMatrix): bool = + result = a.data == b.data + +test "c": + var a = TMatrix[2,2,int](data: [1,2,3,4]) + var b = TMatrix[2,2,int](data: [1,2,3,4]) + check(a == b) + diff --git a/tests/concurrency/tnodeadlocks.nim b/tests/concurrency/tnodeadlocks.nim deleted file mode 100644 index d44196039..000000000 --- a/tests/concurrency/tnodeadlocks.nim +++ /dev/null @@ -1,70 +0,0 @@ -discard """ - outputsub: "101" - cmd: "nimrod $target --hints:on --threads:on $options $file" -""" - -import os, locks - -const - noDeadlocks = defined(preventDeadlocks) - -var - thr: array [0..5, TThread[tuple[a, b: int]]] - L, M, N: TLock - -proc doNothing() = discard - -proc threadFunc(interval: tuple[a, b: int]) {.thread.} = - doNothing() - for i in interval.a..interval.b: - when nodeadlocks: - case i mod 6 - of 0: - Acquire(L) # lock stdout - Acquire(M) - Acquire(N) - of 1: - Acquire(L) - Acquire(N) # lock stdout - Acquire(M) - of 2: - Acquire(M) - Acquire(L) - Acquire(N) - of 3: - Acquire(M) - Acquire(N) - Acquire(L) - of 4: - Acquire(N) - Acquire(M) - Acquire(L) - of 5: - Acquire(N) - Acquire(L) - Acquire(M) - else: assert false - else: - Acquire(L) # lock stdout - Acquire(M) - - echo i - os.sleep(10) - when nodeadlocks: - echo "deadlocks prevented: ", deadlocksPrevented - when nodeadlocks: - Release(N) - Release(M) - Release(L) - -InitLock(L) -InitLock(M) -InitLock(N) - -proc main = - for i in 0..high(thr): - createThread(thr[i], threadFunc, (i*100, i*100+50)) - joinThreads(thr) - -main() - diff --git a/tests/constr/tconstr1.nim b/tests/constr/tconstr1.nim index cb6594213..45e303554 100644 --- a/tests/constr/tconstr1.nim +++ b/tests/constr/tconstr1.nim @@ -10,7 +10,7 @@ type s: string, x, y: int, z: float, - chars: set[Char]] + chars: set[char]] proc testSem = var diff --git a/tests/converter/ttypeconverter1.nim b/tests/converter/ttypeconverter1.nim index b9a5e88ae..fd3a0318a 100644 --- a/tests/converter/ttypeconverter1.nim +++ b/tests/converter/ttypeconverter1.nim @@ -1,3 +1,7 @@ +discard """ + ouput: '''foo +true''' +""" converter p(i: int): bool = return i != 0 @@ -6,3 +10,6 @@ if 1: while 0: echo "bar" +var a: array[3, bool] +a[0] = 3 +echo a[0] diff --git a/tests/destructor/tdestructor.nim b/tests/destructor/tdestructor.nim index f0ea3c5c6..cbaba3154 100644 --- a/tests/destructor/tdestructor.nim +++ b/tests/destructor/tdestructor.nim @@ -22,6 +22,8 @@ myobj destroyed ''' """ +{.experimental.} + type TMyObj = object x, y: int diff --git a/tests/destructor/tdestructor2.nim b/tests/destructor/tdestructor2.nim index a5b62860c..1bdf4993b 100644 --- a/tests/destructor/tdestructor2.nim +++ b/tests/destructor/tdestructor2.nim @@ -3,6 +3,8 @@ discard """ errormsg: " usage of a type with a destructor in a non destructible context" """ +{.experimental.} + type TMyObj = object x, y: int diff --git a/tests/destructor/tdictdestruct.nim b/tests/destructor/tdictdestruct.nim index b7043f7b7..17ded4853 100644 --- a/tests/destructor/tdictdestruct.nim +++ b/tests/destructor/tdictdestruct.nim @@ -11,7 +11,7 @@ proc fakeNew[T](x: var ref T, destroy: proc (a: ref T) {.nimcall.}) = proc destroyDict[TK, TV](a: PDict[TK, TV]) = return proc newDict[TK, TV](a: TK, b: TV): PDict[TK, TV] = - Fakenew(result, destroyDict[TK, TV]) + fakeNew(result, destroyDict[TK, TV]) # Problem: destroyDict is not instantiated when newDict is instantiated! diff --git a/tests/dll/client.nim b/tests/dll/client.nim index 45fa8f639..d535e8750 100644 --- a/tests/dll/client.nim +++ b/tests/dll/client.nim @@ -1,6 +1,6 @@ discard """ output: "Done" - cmd: "nimrod $target --debuginfo --hints:on --define:useNimRtl $options $file" + cmd: "nim $target --debuginfo --hints:on --define:useNimRtl $options $file" """ type diff --git a/tests/dll/server.nim b/tests/dll/server.nim index b2fac9ecc..df3223444 100644 --- a/tests/dll/server.nim +++ b/tests/dll/server.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nimrod $target --debuginfo --hints:on --define:useNimRtl --app:lib $options $file" + cmd: "nim $target --debuginfo --hints:on --define:useNimRtl --app:lib $options $file" """ type diff --git a/tests/effects/teffects1.nim b/tests/effects/teffects1.nim index f73230ff9..e32096b57 100644 --- a/tests/effects/teffects1.nim +++ b/tests/effects/teffects1.nim @@ -1,7 +1,7 @@ discard """ - line: 2136 + line: 2170 file: "system.nim" - errormsg: "can raise an unlisted exception: ref EIO" + errormsg: "can raise an unlisted exception: ref IOError" """ type @@ -9,13 +9,13 @@ type TObjB = object of TObj a, b, c: string - EIO2 = ref object of EIO + IO2Error = ref object of IOError proc forw: int {. .} -proc lier(): int {.raises: [EIO2].} = +proc lier(): int {.raises: [IO2Error].} = writeln stdout, "arg" proc forw: int = - raise newException(EIO, "arg") + raise newException(IOError, "arg") diff --git a/tests/effects/teffects2.nim b/tests/effects/teffects2.nim index 7e6b17c36..89ad16edc 100644 --- a/tests/effects/teffects2.nim +++ b/tests/effects/teffects2.nim @@ -1,6 +1,6 @@ discard """ line: 19 - errormsg: "can raise an unlisted exception: ref EIO" + errormsg: "can raise an unlisted exception: ref IOError" """ type @@ -8,13 +8,13 @@ type TObjB = object of TObj a, b, c: string - EIO2 = ref object of EIO + EIO2 = ref object of IOError proc forw: int {.raises: [].} -proc lier(): int {.raises: [EIO].} = +proc lier(): int {.raises: [IOError].} = writeln stdout, "arg" proc forw: int = - raise newException(EIO, "arg") + raise newException(IOError, "arg") diff --git a/tests/effects/tgcsafe.nim b/tests/effects/tgcsafe.nim index 87388238a..0d5109439 100644 --- a/tests/effects/tgcsafe.nim +++ b/tests/effects/tgcsafe.nim @@ -1,6 +1,7 @@ discard """ - line: 15 + line: 16 errormsg: "'mainUnsafe' is not GC-safe" + cmd: "nim $target --hints:on --threads:on $options $file" """ proc mymap(x: proc ()) = diff --git a/tests/exception/texcpt1.nim b/tests/exception/texcpt1.nim index ec74c9470..50a95eeec 100644 --- a/tests/exception/texcpt1.nim +++ b/tests/exception/texcpt1.nim @@ -2,8 +2,8 @@ discard """ outputsub: "-6" """ type - ESomething = object of E_Base - ESomeOtherErr = object of E_Base + ESomething = object of Exception + ESomeOtherErr = object of Exception proc genErrors(s: string) = if s == "error!": diff --git a/tests/exception/tnestedreturn.nim b/tests/exception/tnestedreturn.nim index b9f7843f6..591638f0e 100644 --- a/tests/exception/tnestedreturn.nim +++ b/tests/exception/tnestedreturn.nim @@ -10,8 +10,8 @@ proc test1() = finally: echo "A" try: - raise newException(EOS, "Problem") - except EOS: + raise newException(OSError, "Problem") + except OSError: return test1() @@ -23,7 +23,7 @@ proc test2() = try: return - except EOS: + except OSError: discard test2() @@ -31,8 +31,8 @@ test2() proc test3() = try: try: - raise newException(EOS, "Problem") - except EOS: + raise newException(OSError, "Problem") + except OSError: return finally: echo "C" diff --git a/tests/exception/tnestedreturn2.nim b/tests/exception/tnestedreturn2.nim index 14a2dab92..4bd2d535d 100644 --- a/tests/exception/tnestedreturn2.nim +++ b/tests/exception/tnestedreturn2.nim @@ -1,14 +1,14 @@ discard """ - file: "tnestedreturn.nim" - outputsub: "Error: unhandled exception: Problem [EOS]" + file: "tnestedreturn2.nim" + outputsub: "Error: unhandled exception: Problem [OSError]" exitcode: "1" """ proc test4() = try: try: - raise newException(EOS, "Problem") - except EOS: + raise newException(OSError, "Problem") + except OSError: return finally: discard @@ -17,4 +17,4 @@ proc test4() = # but could cause segmentation fault if # exceptions are not handled properly. test4() -raise newException(EOS, "Problem") +raise newException(OSError, "Problem") diff --git a/tests/exception/tunhandledexc.nim b/tests/exception/tunhandledexc.nim index f24881aef..aa9d61236 100644 --- a/tests/exception/tunhandledexc.nim +++ b/tests/exception/tunhandledexc.nim @@ -4,8 +4,8 @@ discard """ exitcode: "1" """ type - ESomething = object of E_Base - ESomeOtherErr = object of E_Base + ESomething = object of Exception + ESomeOtherErr = object of Exception proc genErrors(s: string) = if s == "error!": @@ -13,7 +13,7 @@ proc genErrors(s: string) = else: raise newException(EsomeotherErr, "bla") -when True: +when true: try: genErrors("errssor!") except ESomething: diff --git a/tests/exception/twrongexc.nim b/tests/exception/twrongexc.nim index 755f7d979..4e921b8a3 100644 --- a/tests/exception/twrongexc.nim +++ b/tests/exception/twrongexc.nim @@ -1,11 +1,11 @@ discard """ file: "twrongexc.nim" - outputsub: "Error: unhandled exception: [EInvalidValue]" + outputsub: "Error: unhandled exception: [ValueError]" exitcode: "1" """ try: - raise newException(EInvalidValue, "") -except EOverflow: + raise newException(ValueError, "") +except OverflowError: echo("Error caught") diff --git a/tests/fields/tfielditerator2.nim b/tests/fields/tfielditerator2.nim index 76fa568f2..70ab9e2ab 100644 --- a/tests/fields/tfielditerator2.nim +++ b/tests/fields/tfielditerator2.nim @@ -5,16 +5,19 @@ a char: false an int: 5 an int: 6 a string: abc -false -true -true -false -true +a string: I'm root! +CMP false +CMP true +CMP true +CMP false +CMP true +CMP true a: a b: b x: 5 y: 6 z: abc +thaRootMan: I'm root! myDisc: enC c: Z enC @@ -23,7 +26,9 @@ Z """ type - TMyObj = object + SomeRootObj = object of RootObj + thaRootMan: string + TMyObj = object of SomeRootObj a, b: char x, y: int z: string @@ -41,6 +46,7 @@ proc p(x: string) = echo "a string: ", x proc myobj(a, b: char, x, y: int, z: string): TMyObj = result.a = a; result.b = b; result.x = x; result.y = y; result.z = z + result.thaRootMan = "I'm root!" var x = myobj('a', 'b', 5, 6, "abc") var y = myobj('A', 'b', 5, 9, "abc") @@ -49,7 +55,7 @@ for f in fields(x): p f for a, b in fields(x, y): - echo a == b + echo "CMP ", a == b for key, val in fieldPairs(x): echo key, ": ", val diff --git a/tests/float/tfloat1.nim b/tests/float/tfloat1.nim index f290fdb57..ed99260ea 100644 --- a/tests/float/tfloat1.nim +++ b/tests/float/tfloat1.nim @@ -1,6 +1,6 @@ discard """ file: "tfloat1.nim" - outputsub: "Error: unhandled exception: FPU operation caused an overflow [EFloatOverflow]" + outputsub: "Error: unhandled exception: FPU operation caused an overflow [FloatOverflowError]" exitcode: "1" """ # Test new floating point exceptions @@ -10,6 +10,6 @@ discard """ var x = 0.8 var y = 0.0 -echo x / y #OUT Error: unhandled exception: FPU operation caused an overflow [EFloatOverflow] +echo x / y #OUT Error: unhandled exception: FPU operation caused an overflow diff --git a/tests/float/tfloat2.nim b/tests/float/tfloat2.nim index 51883674f..b84120fba 100644 --- a/tests/float/tfloat2.nim +++ b/tests/float/tfloat2.nim @@ -1,6 +1,6 @@ discard """ file: "tfloat2.nim" - outputsub: "Error: unhandled exception: FPU operation caused a NaN result [EFloatInvalidOp]" + outputsub: "Error: unhandled exception: FPU operation caused a NaN result [FloatInvalidOpError]" exitcode: "1" """ # Test new floating point exceptions @@ -10,6 +10,6 @@ discard """ var x = 0.0 var y = 0.0 -echo x / y #OUT Error: unhandled exception: FPU operation caused a NaN result [EFloatInvalidOp] +echo x / y #OUT Error: unhandled exception: FPU operation caused a NaN result diff --git a/tests/float/tfloat3.nim b/tests/float/tfloat3.nim index 4382dd3ed..a3f5a2fc7 100644 --- a/tests/float/tfloat3.nim +++ b/tests/float/tfloat3.nim @@ -1,6 +1,6 @@ discard """ file: "tfloat3.nim" - output: "Nimrod 3.4368930843, 0.3299290698 C double: 3.4368930843, 0.3299290698" + output: "Nim 3.4368930843, 0.3299290698 C double: 3.4368930843, 0.3299290698" """ import math, strutils @@ -13,11 +13,11 @@ void printFloats(void) { } """.} -proc c_printf(frmt: CString) {.importc: "printf", header: "", varargs.} +proc c_printf(frmt: cstring) {.importc: "printf", header: "", varargs.} proc printFloats {.importc, nodecl.} var x: float = 1.234567890123456789 -c_printf("Nimrod %.10f, %.10f ", exp(x), cos(x)) +c_printf("Nim %.10f, %.10f ", exp(x), cos(x)) printFloats() diff --git a/tests/gc/bintrees.nim b/tests/gc/bintrees.nim index 3f8acee1e..5b65bb437 100644 --- a/tests/gc/bintrees.nim +++ b/tests/gc/bintrees.nim @@ -1,4 +1,4 @@ -# -*- nimrod -*- +# -*- nim -*- import os, strutils diff --git a/tests/gc/closureleak.nim b/tests/gc/closureleak.nim index 669e05227..38ee1250a 100644 --- a/tests/gc/closureleak.nim +++ b/tests/gc/closureleak.nim @@ -29,5 +29,5 @@ for i in 0 .. <10: f.func = proc = echo f.id -gc_fullcollect() +GC_fullcollect() echo alive_foos.len <= 3 diff --git a/tests/generics/mdotlookup.nim b/tests/generics/mdotlookup.nim index 0c4d0c87c..2984574c2 100644 --- a/tests/generics/mdotlookup.nim +++ b/tests/generics/mdotlookup.nim @@ -11,6 +11,6 @@ import sets var intset = initSet[int]() -proc func*[T](a: T) = +proc fn*[T](a: T) = if a in intset: echo("true") else: echo("false") diff --git a/tests/generics/tcan_alias_generic.nim b/tests/generics/tcan_alias_generic.nim index e90bdc6d2..780a47841 100644 --- a/tests/generics/tcan_alias_generic.nim +++ b/tests/generics/tcan_alias_generic.nim @@ -1,5 +1,5 @@ ## -## can_alias_generic Nimrod Module +## can_alias_generic Nim Module ## ## Created by Eric Doughty-Papassideris on 2011-02-16. ## Copyright (c) 2011 FWA. All rights reserved. diff --git a/tests/generics/tcan_alias_specialised_generic.nim b/tests/generics/tcan_alias_specialised_generic.nim index 8d4a29abd..a737d3580 100644 --- a/tests/generics/tcan_alias_specialised_generic.nim +++ b/tests/generics/tcan_alias_specialised_generic.nim @@ -3,7 +3,7 @@ discard """ """ ## -## can_alias_specialised_generic Nimrod Module +## can_alias_specialised_generic Nim Module ## ## Created by Eric Doughty-Papassideris on 2011-02-16. ## Copyright (c) 2011 FWA. All rights reserved. diff --git a/tests/generics/tcan_inherit_generic.nim b/tests/generics/tcan_inherit_generic.nim index a6f4d946b..ce2b6452f 100644 --- a/tests/generics/tcan_inherit_generic.nim +++ b/tests/generics/tcan_inherit_generic.nim @@ -1,5 +1,5 @@ ## -## can_inherit_generic Nimrod Module +## can_inherit_generic Nim Module ## ## Created by Eric Doughty-Papassideris on 2011-02-16. ## Copyright (c) 2011 FWA. All rights reserved. diff --git a/tests/generics/tcan_specialise_generic.nim b/tests/generics/tcan_specialise_generic.nim index 64d5f56e3..c510910e8 100644 --- a/tests/generics/tcan_specialise_generic.nim +++ b/tests/generics/tcan_specialise_generic.nim @@ -1,5 +1,5 @@ ## -## can_specialise_generic Nimrod Module +## can_specialise_generic Nim Module ## ## Created by Eric Doughty-Papassideris on 2011-02-16. ## Copyright (c) 2011 FWA. All rights reserved. diff --git a/tests/generics/tcannot_pass_empty_seq_to_generic.nim b/tests/generics/tcannot_pass_empty_seq_to_generic.nim new file mode 100644 index 000000000..5f0363af8 --- /dev/null +++ b/tests/generics/tcannot_pass_empty_seq_to_generic.nim @@ -0,0 +1,17 @@ +discard """ + errormsg: "type mismatch: got (seq[empty])" + line: 16 +""" + +# bug #836 + +type + TOption*[T] = object + case FIsSome: bool + of false: nil + of true: FData: T + +proc some*[T](value: T): TOption[T] = TOption[T](FIsSome: true, FData: value) + +echo some(@[]).FIsSome + diff --git a/tests/generics/tdotlookup.nim b/tests/generics/tdotlookup.nim index d3deca7fc..17c60ded2 100644 --- a/tests/generics/tdotlookup.nim +++ b/tests/generics/tdotlookup.nim @@ -7,4 +7,4 @@ import mdotlookup foo(7) # bug #1444 -func(4) +fn(4) diff --git a/tests/generics/texplicitgeneric1.nim b/tests/generics/texplicitgeneric1.nim index 6cca71ac0..d54044368 100644 --- a/tests/generics/texplicitgeneric1.nim +++ b/tests/generics/texplicitgeneric1.nim @@ -18,7 +18,7 @@ proc newDict*[TKey, TValue](): PDict[TKey, TValue] = proc add*[TKey, TValue](d: PDict[TKey, TValue], k: TKey, v: TValue) = d.data.add((k, v)) -iterator items*[Tkey, tValue](d: PDict[TKey, TValue]): tuple[k: TKey, +iterator items*[Tkey, TValue](d: PDict[TKey, TValue]): tuple[k: TKey, v: TValue] = for k, v in items(d.data): yield (k, v) diff --git a/tests/generics/tforwardgeneric.nim b/tests/generics/tforwardgeneric.nim index c5943b966..af0c7daf4 100644 --- a/tests/generics/tforwardgeneric.nim +++ b/tests/generics/tforwardgeneric.nim @@ -1,6 +1,7 @@ discard """ output: "1.1000000000000001e+00 11" ccodecheck: "!@'ClEnv'" + disabled: "true" """ proc p[T](a, b: T): T diff --git a/tests/generics/tgeneric0.nim b/tests/generics/tgeneric0.nim index 9292b729f..45450ccca 100644 --- a/tests/generics/tgeneric0.nim +++ b/tests/generics/tgeneric0.nim @@ -1,9 +1,9 @@ import tables type - TX = TTable[string, int] + TX = Table[string, int] -proc foo(models: seq[TTable[string, float]]): seq[float] = +proc foo(models: seq[Table[string, float]]): seq[float] = result = @[] for model in models.items: result.add model["foobar"] @@ -16,4 +16,12 @@ proc foo[T](p: TType[T, range[0..1]]) = proc foo[T](p: TType[T, range[0..2]]) = echo "bar" +#bug #1366 + +proc reversed(x) = + for i in countdown(x.low, x.high): + echo i + +reversed(@[-19, 7, -4, 6]) + diff --git a/tests/generics/tgeneric2.nim b/tests/generics/tgeneric2.nim index 56803017a..21eb4693e 100644 --- a/tests/generics/tgeneric2.nim +++ b/tests/generics/tgeneric2.nim @@ -1,7 +1,7 @@ import tables type - TX = TTable[string, int] + TX = Table[string, int] proc foo(models: seq[TX]): seq[int] = result = @[] @@ -9,7 +9,7 @@ proc foo(models: seq[TX]): seq[int] = result.add model["foobar"] type - obj = object - field: TTable[string, string] + Obj = object + field: Table[string, string] var t: Obj discard initTable[type(t.field), string]() diff --git a/tests/generics/tgeneric3.nim b/tests/generics/tgeneric3.nim index 963d0ccfb..6fb929efb 100644 --- a/tests/generics/tgeneric3.nim +++ b/tests/generics/tgeneric3.nim @@ -7,17 +7,17 @@ type value: D node: PNode[T,D] when not (D is string): - val_set: Bool + val_set: bool TItems[T,D] = seq[ref TItem[T,D]] TNode {.acyclic, pure, final, shallow.} [T,D] = object slots: TItems[T,D] left: PNode[T,D] - count: Int32 + count: int32 RPath[T,D] = tuple[ - Xi: Int, + Xi: int, Nd: PNode[T,D] ] const @@ -29,41 +29,41 @@ const cLenMax = 128 cCenter = cLenMax div 2 -proc len[T,D] (n:PNode[T,D]): Int {.inline.} = - return n.Count +proc len[T,D] (n:PNode[T,D]): int {.inline.} = + return n.count -proc clean[T: TOrdinal|TNumber](o: var T) {.inline.} = discard +proc clean[T: SomeOrdinal|SomeNumber](o: var T) {.inline.} = discard proc clean[T: string|seq](o: var T) {.inline.} = o = nil proc clean[T,D] (o: ref TItem[T,D]) {.inline.} = when (D is string) : - o.Value = nil + o.value = nil else : o.val_set = false -proc isClean[T,D] (it: ref TItem[T,D]): Bool {.inline.} = +proc isClean[T,D] (it: ref TItem[T,D]): bool {.inline.} = when (D is string) : - return it.Value == nil + return it.value == nil else : return not it.val_set -proc isClean[T,D] (n: PNode[T,D], x: Int): Bool {.inline.} = - when (D is string) : - return n.slots[x].Value == nil - else : +proc isClean[T,D](n: PNode[T,D], x: int): bool {.inline.} = + when (D is string): + return n.slots[x].value == nil + else: return not n.slots[x].val_set -proc setItem[T,D] (AKey: T, AValue: D, ANode: PNode[T,D]): ref TItem[T,D] {.inline.} = - new(Result) - Result.Key = AKey - Result.Value = AValue - Result.Node = ANode +proc setItem[T,D](Akey: T, Avalue: D, ANode: PNode[T,D]): ref TItem[T,D] {.inline.} = + new(result) + result.key = Akey + result.value = Avalue + result.node = ANode when not (D is string) : - Result.val_set = true + result.val_set = true -proc cmp[T:Int8|Int16|Int32|Int64|Int] (a,b: T): T {.inline.} = +proc cmp[T:int8|int16|int32|int64|int] (a,b: T): T {.inline.} = return a-b template binSearchImpl *(docmp: expr) {.immediate.} = @@ -76,41 +76,41 @@ template binSearchImpl *(docmp: expr) {.immediate.} = if SW < 0: result = I + 1 else: H = I - 1 - if SW == 0 : bFound = True + if SW == 0 : bFound = true if bFound: inc(result) else: result = - result -proc bSearch[T,D] (haystack: PNode[T,D], needle:T): Int {.inline.} = +proc bSearch[T,D] (haystack: PNode[T,D], needle:T): int {.inline.} = binSearchImpl(haystack.slots[I].key.cmp(needle)) -proc DeleteItem[T,D] (n: PNode[T,D], x: Int): PNode[T,D] {.inline.} = +proc DeleteItem[T,D] (n: PNode[T,D], x: int): PNode[T,D] {.inline.} = var w = n.slots[x] - if w.Node != nil : + if w.node != nil : clean(w) return n - dec(n.Count) - if n.Count > 0 : - for i in countup(x, n.Count -1) : n.slots[i] = n.slots[i + 1] - n.slots[n.Count] = nil - case n.Count + dec(n.count) + if n.count > 0 : + for i in countup(x, n.count -1) : n.slots[i] = n.slots[i + 1] + n.slots[n.count] = nil + case n.count of cLen1 : setLen(n.slots, cLen1) of cLen2 : setLen(n.slots, cLen2) of cLen3 : setLen(n.slots, cLen3) of cLenCenter : setLen(n.slots, cLenCenter) of cLen4 : setLen(n.slots, cLen4) else: discard - Result = n + result = n else : - Result = n.Left + result = n.left n.slots = nil - n.Left = nil + n.left = nil -proc InternalDelete[T,D] (ANode: PNode[T,D], key: T, AValue: var D): PNode[T,D] = +proc internalDelete[T,D] (ANode: PNode[T,D], key: T, Avalue: var D): PNode[T,D] = var Path: array[0..20, RPath[T,D]] var n = ANode - Result = n - clean(AValue) + result = n + clean(Avalue) var h = 0 while n != nil: var x = bSearch(n, key) @@ -119,17 +119,17 @@ proc InternalDelete[T,D] (ANode: PNode[T,D], key: T, AValue: var D): PNode[T,D] Path[h].Xi = - x inc(h) if x == 0 : - n = n.Left + n = n.left else : x = (-x) -1 - if x < n.Count : - n = n.slots[x].Node + if x < n.count : + n = n.slots[x].node else : n = nil else : dec(x) if isClean(n, x) : return - AValue = n.slots[x].Value + Avalue = n.slots[x].value var n2 = DeleteItem(n, x) dec(h) while (n2 != n) and (h >=0) : @@ -139,30 +139,30 @@ proc InternalDelete[T,D] (ANode: PNode[T,D], key: T, AValue: var D): PNode[T,D] if x >= 0 : if (n == nil) and isClean(w.Nd, x) : n = w.Nd - n.slots[x].Node = nil + n.slots[x].node = nil n2 = DeleteItem(n, x) else : - w.Nd.slots[x].Node = n + w.Nd.slots[x].node = n return else : - w.Nd.Left = n + w.Nd.left = n return dec(h) if h < 0: - Result = n2 + result = n2 return -proc InternalFind[T,D] (n: PNode[T,D], key: T): ref TItem[T,D] {.inline.} = +proc internalFind[T,D] (n: PNode[T,D], key: T): ref TItem[T,D] {.inline.} = var wn = n while wn != nil : var x = bSearch(wn, key) if x <= 0 : if x == 0 : - wn = wn.Left + wn = wn.left else : x = (-x) -1 - if x < wn.Count : - wn = wn.slots[x].Node + if x < wn.count : + wn = wn.slots[x].node else : return nil @@ -171,32 +171,32 @@ proc InternalFind[T,D] (n: PNode[T,D], key: T): ref TItem[T,D] {.inline.} = return nil proc traceTree[T,D](root: PNode[T,D]) = - proc traceX(x: Int) = + proc traceX(x: int) = write stdout, "(" write stdout, x write stdout, ") " proc traceEl(el: ref TItem[T,D]) = write stdout, " key: " - write stdout, el.Key + write stdout, el.key write stdout, " value: " - write stdout, el.Value + write stdout, el.value proc traceln(space: string) = writeln stdout, "" write stdout, space - proc doTrace(n: PNode[T,D], level: Int) = + proc doTrace(n: PNode[T,D], level: int) = var space = repeatChar(2 * level) traceln(space) write stdout, "node: " if n == nil: writeln stdout, "is empty" return - write stdout, n.Count + write stdout, n.count write stdout, " elements: " - if n.Left != nil: + if n.left != nil: traceln(space) write stdout, "left: " doTrace(n.left, level +1) @@ -204,188 +204,188 @@ proc traceTree[T,D](root: PNode[T,D]) = if el != nil and not isClean(el): traceln(space) traceX(i) - if i >= n.Count: + if i >= n.count: write stdout, "error " else: traceEl(el) - if el.Node != nil: doTrace(el.Node, level +1) + if el.node != nil: doTrace(el.node, level +1) else : write stdout, " empty " - elif i < n.Count : + elif i < n.count : traceln(space) traceX(i) write stdout, "clean: " when T is string : - if el.Key != nil: write stdout, el.Key - else : write stdout, el.Key - if el.Node != nil: doTrace(el.Node, level +1) + if el.key != nil: write stdout, el.key + else : write stdout, el.key + if el.node != nil: doTrace(el.node, level +1) else : write stdout, " empty " writeln stdout,"" doTrace(root, 0) -proc InsertItem[T,D](APath: RPath[T,D], ANode:PNode[T,D], AKey: T, AValue: D) = +proc InsertItem[T,D](APath: RPath[T,D], ANode:PNode[T,D], Akey: T, Avalue: D) = var x = - APath.Xi - inc(APath.Nd.Count) - case APath.Nd.Count + inc(APath.Nd.count) + case APath.Nd.count of cLen1: setLen(APath.Nd.slots, cLen2) of cLen2: setLen(APath.Nd.slots, cLen3) of cLen3: setLen(APath.Nd.slots, cLenCenter) of cLenCenter: setLen(APath.Nd.slots, cLen4) of cLen4: setLen(APath.Nd.slots, cLenMax) else: discard - for i in countdown(APath.Nd.Count.int - 1, x + 1): shallowCopy(APath.Nd.slots[i], APath.Nd.slots[i - 1]) - APath.Nd.slots[x] = setItem(AKey, AValue, ANode) + for i in countdown(APath.Nd.count.int - 1, x + 1): shallowCopy(APath.Nd.slots[i], APath.Nd.slots[i - 1]) + APath.Nd.slots[x] = setItem(Akey, Avalue, ANode) -proc SplitPage[T,D](n, left: PNode[T,D], xi: Int, AKey:var T, AValue:var D): PNode[T,D] = - var x = -Xi +proc SplitPage[T,D](n, left: PNode[T,D], xi: int, Akey:var T, Avalue:var D): PNode[T,D] = + var x = -xi var it1: TItems[T,D] it1.newSeq(cLenCenter) - new(Result) - Result.slots.newSeq(cLenCenter) - Result.Count = cCenter + new(result) + result.slots.newSeq(cLenCenter) + result.count = cCenter if x == cCenter: for i in 0..cCenter -1: shallowCopy(it1[i], left.slots[i]) - for i in 0..cCenter -1: shallowCopy(Result.slots[i], left.slots[cCenter + i]) - Result.Left = n + for i in 0..cCenter -1: shallowCopy(result.slots[i], left.slots[cCenter + i]) + result.left = n else : if x < cCenter : for i in 0..x-1: shallowCopy(it1[i], left.slots[i]) - it1[x] = setItem(AKey, AValue, n) + it1[x] = setItem(Akey, Avalue, n) for i in x+1 .. cCenter -1: shallowCopy(it1[i], left.slots[i-1]) var w = left.slots[cCenter -1] - AKey = w.Key - AValue = w.Value - Result.Left = w.Node - for i in 0..cCenter -1: shallowCopy(Result.slots[i], left.slots[cCenter + i]) + Akey = w.key + Avalue = w.value + result.left = w.node + for i in 0..cCenter -1: shallowCopy(result.slots[i], left.slots[cCenter + i]) else : for i in 0..cCenter -1: shallowCopy(it1[i], left.slots[i]) x = x - (cCenter + 1) - for i in 0..x-1: shallowCopy(Result.slots[i], left.slots[cCenter + i + 1]) - Result.slots[x] = setItem(AKey, AValue, n) - for i in x+1 .. cCenter -1: shallowCopy(Result.slots[i], left.slots[cCenter + i]) + for i in 0..x-1: shallowCopy(result.slots[i], left.slots[cCenter + i + 1]) + result.slots[x] = setItem(Akey, Avalue, n) + for i in x+1 .. cCenter -1: shallowCopy(result.slots[i], left.slots[cCenter + i]) var w = left.slots[cCenter] - AKey = w.Key - AValue = w.Value - Result.Left = w.Node - left.Count = cCenter + Akey = w.key + Avalue = w.value + result.left = w.node + left.count = cCenter shallowCopy(left.slots, it1) -proc InternalPut[T,D](ANode: ref TNode[T,D], AKey: T, AValue: D, OldValue: var D): ref TNode[T,D] = - var h: Int +proc internalPut[T,D](ANode: ref TNode[T,D], Akey: T, Avalue: D, Oldvalue: var D): ref TNode[T,D] = + var h: int var Path: array[0..30, RPath[T,D]] var left: PNode[T,D] var n = ANode - Result = ANode + result = ANode h = 0 while n != nil: - var x = bSearch[T,D](n, AKey) + var x = bSearch[T,D](n, Akey) if x <= 0 : Path[h].Nd = n Path[h].Xi = x inc(h) if x == 0 : - n = n.Left + n = n.left else : x = (-x) -1 - if x < n.Count : - n = n.slots[x].Node + if x < n.count : + n = n.slots[x].node else : n = nil else : var w = n.slots[x - 1] - OldValue = w.Value - w.Value = AValue + Oldvalue = w.value + w.value = Avalue return dec(h) left = nil - var lKey = AKey - var lValue = AValue + var lkey = Akey + var lvalue = Avalue while h >= 0 : - if Path[h].Nd.Count < cLenMax : - InsertItem(Path[h], n, lKey, lValue) + if Path[h].Nd.count < cLenMax : + InsertItem(Path[h], n, lkey, lvalue) return else : left = Path[h].Nd - n = SplitPage(n, left, Path[h].Xi, lKey, lValue) + n = SplitPage(n, left, Path[h].Xi, lkey, lvalue) dec(h) - new(Result) - Result.slots.newSeq(cLen1) - Result.Count = 1 - Result.Left = left - Result.slots[0] = setItem(lKey, lValue, n) + new(result) + result.slots.newSeq(cLen1) + result.count = 1 + result.left = left + result.slots[0] = setItem(lkey, lvalue, n) proc CleanTree[T,D](n: PNode[T,D]): PNode[T,D] = - if n.Left != nil : - n.Left = CleanTree(n.Left) - for i in 0 .. n.Count - 1 : + if n.left != nil : + n.left = CleanTree(n.left) + for i in 0 .. n.count - 1 : var w = n.slots[i] - if w.Node != nil : - w.Node = CleanTree(w.Node) - clean(w.Value) - clean(w.Key) + if w.node != nil : + w.node = CleanTree(w.node) + clean(w.value) + clean(w.key) n.slots = nil return nil proc VisitAllNodes[T,D](n: PNode[T,D], visit: proc(n: PNode[T,D]): PNode[T,D] {.closure.} ): PNode[T,D] = if n != nil : - if n.Left != nil : - n.Left = VisitAllNodes(n.Left, visit) - for i in 0 .. n.Count - 1 : + if n.left != nil : + n.left = VisitAllNodes(n.left, visit) + for i in 0 .. n.count - 1 : var w = n.slots[i] - if w.Node != nil : - w.Node = VisitAllNodes(w.Node, visit) + if w.node != nil : + w.node = VisitAllNodes(w.node, visit) return visit(n) return nil proc VisitAllNodes[T,D](n: PNode[T,D], visit: proc(n: PNode[T,D]) {.closure.} ) = if n != nil: - if n.Left != nil : - VisitAllNodes(n.Left, visit) - for i in 0 .. n.Count - 1 : + if n.left != nil : + VisitAllNodes(n.left, visit) + for i in 0 .. n.count - 1 : var w = n.slots[i] - if w.Node != nil : - VisitAllNodes(w.Node, visit) + if w.node != nil : + VisitAllNodes(w.node, visit) visit(n) -proc VisitAll[T,D](n: PNode[T,D], visit: proc(AKey: T, AValue: D) {.closure.} ) = +proc VisitAll[T,D](n: PNode[T,D], visit: proc(Akey: T, Avalue: D) {.closure.} ) = if n != nil: - if n.Left != nil : - VisitAll(n.Left, visit) - for i in 0 .. n.Count - 1 : + if n.left != nil : + VisitAll(n.left, visit) + for i in 0 .. n.count - 1 : var w = n.slots[i] if not w.isClean : - visit(w.Key, w.Value) - if w.Node != nil : - VisitAll(w.Node, visit) + visit(w.key, w.value) + if w.node != nil : + VisitAll(w.node, visit) -proc VisitAll[T,D](n: PNode[T,D], visit: proc(AKey: T, AValue: var D):Bool {.closure.} ): PNode[T,D] = +proc VisitAll[T,D](n: PNode[T,D], visit: proc(Akey: T, Avalue: var D):bool {.closure.} ): PNode[T,D] = if n != nil: - var n1 = n.Left + var n1 = n.left if n1 != nil : var n2 = VisitAll(n1, visit) if n1 != n2 : - n.Left = n2 + n.left = n2 var i = 0 - while i < n.Count : + while i < n.count : var w = n.slots[i] if not w.isClean : - if visit(w.Key, w.Value) : - Result = DeleteItem(n, i) - if Result == nil : return + if visit(w.key, w.value) : + result = DeleteItem(n, i) + if result == nil : return dec(i) - n1 = w.Node + n1 = w.node if n1 != nil : var n2 = VisitAll(n1, visit) if n1 != n2 : - w.Node = n2 + w.node = n2 inc(i) return n @@ -396,20 +396,20 @@ iterator keys* [T,D] (n: PNode[T,D]): T = var nd = n var i = -1 while true : - if i < nd.Count : + if i < nd.count : Path[level].Nd = nd Path[level].Xi = i if i < 0 : - if nd.Left != nil : - nd = nd.Left + if nd.left != nil : + nd = nd.left inc(level) else : inc(i) else : var w = nd.slots[i] if not w.isClean() : - yield w.Key - if w.Node != nil : - nd = w.Node + yield w.key + if w.node != nil : + nd = w.node i = -1 inc(level) else : inc(i) @@ -424,22 +424,22 @@ iterator keys* [T,D] (n: PNode[T,D]): T = when isMainModule: proc test() = - var oldValue: Int - var root = InternalPut[int, int](nil, 312, 312, oldValue) - var someOtherRoot = InternalPut[string, int](nil, "312", 312, oldValue) - var it1 = InternalFind(root, 312) - echo it1.Value + var oldvalue: int + var root = internalPut[int, int](nil, 312, 312, oldvalue) + var someOtherRoot = internalPut[string, int](nil, "312", 312, oldvalue) + var it1 = internalFind(root, 312) + echo it1.value for i in 1..1_000_000: - root = InternalPut(root, i, i, oldValue) + root = internalPut(root, i, i, oldvalue) var cnt = 0 - oldValue = -1 + oldvalue = -1 when true : # code compiles, when this or the other when is switched to false for k in root.keys : - if k <= oldValue : + if k <= oldvalue : echo k - oldValue = k + oldvalue = k inc(cnt) echo cnt when true : @@ -450,21 +450,21 @@ when isMainModule: root = VisitAll(root, proc(key: int, value: var int): bool = return key mod 2 == 0 ) cnt = 0 - oldValue = -1 + oldvalue = -1 VisitAll(root, proc(key: int, value: int) {.closure.} = - if key <= oldValue : + if key <= oldvalue : echo key - oldValue = key + oldvalue = key inc(cnt) ) echo cnt root = VisitAll(root, proc(key: int, value: var int): bool = return key mod 2 != 0 ) cnt = 0 - oldValue = -1 + oldvalue = -1 VisitAll(root, proc(key: int, value: int) {.closure.} = - if key <= oldValue : + if key <= oldvalue : echo "error ", key - oldValue = key + oldvalue = key inc(cnt) ) echo cnt #traceTree(root) diff --git a/tests/generics/tgenericprocvar.nim b/tests/generics/tgenericprocvar.nim index 1eba81fec..dca9c8538 100644 --- a/tests/generics/tgenericprocvar.nim +++ b/tests/generics/tgenericprocvar.nim @@ -25,7 +25,7 @@ proc filter[T,D](data: seq[T], env:D, pred: TFilterProc[T,D]): seq[T] = for e in data: if pred(e, env): result.add(e) -proc predTest(item: int, value: int): Bool = +proc predTest(item: int, value: int): bool = return item <= value proc test(data: seq[int], value: int): seq[int] = diff --git a/tests/generics/tgenericvariant.nim b/tests/generics/tgenericvariant.nim index 51d01355a..0150cda8d 100644 --- a/tests/generics/tgenericvariant.nim +++ b/tests/generics/tgenericvariant.nim @@ -1,15 +1,15 @@ type TMaybe[T] = object - case empty: Bool - of False: value: T + case empty: bool + of false: value: T else: nil proc Just*[T](val: T): TMaybe[T] = - result.empty = False + result.empty = false result.value = val proc Nothing[T](): TMaybe[T] = - result.empty = True + result.empty = true proc safeReadLine(): TMaybe[string] = var r = stdin.readLine() diff --git a/tests/generics/tinferredgenericprocs.nim b/tests/generics/tinferredgenericprocs.nim index ac445fd32..12adfe965 100644 --- a/tests/generics/tinferredgenericprocs.nim +++ b/tests/generics/tinferredgenericprocs.nim @@ -5,7 +5,7 @@ discard """ 3''' """ -# https://github.com/Araq/Nimrod/issues/797 +# https://github.com/Araq/Nim/issues/797 proc foo[T](s:T):string = $s type IntStringProc = proc(x: int): string diff --git a/tests/generics/tmetafield.nim b/tests/generics/tmetafield.nim index e1bc43ce3..bbfca7e3c 100644 --- a/tests/generics/tmetafield.nim +++ b/tests/generics/tmetafield.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nimrod check $options $file" + cmd: "nim check $options $file" errormsg: "'proc' is not a concrete type" errormsg: "'Foo' is not a concrete type." errormsg: "invalid type: 'TBaseMed'" diff --git a/tests/generics/tspecialised_is_equivalent.nim b/tests/generics/tspecialised_is_equivalent.nim index 60b976e90..ace562862 100644 --- a/tests/generics/tspecialised_is_equivalent.nim +++ b/tests/generics/tspecialised_is_equivalent.nim @@ -1,5 +1,5 @@ ## -## specialised_is_equivalent Nimrod Module +## specialised_is_equivalent Nim Module ## ## Created by Eric Doughty-Papassideris on 2011-02-16. ## Copyright (c) 2011 FWA. All rights reserved. diff --git a/tests/generics/tsubtypeconstraint.nim b/tests/generics/tsubtypeconstraint.nim new file mode 100644 index 000000000..2f0954522 --- /dev/null +++ b/tests/generics/tsubtypeconstraint.nim @@ -0,0 +1,13 @@ + +# bug #1684 +type + BaseType {.inheritable pure.} = object + idx: int + + DerivedType* {.final pure.} = object of BaseType + +proc index*[Toohoo: BaseType](h: Toohoo): int {.inline.} = h.idx +proc newDerived(idx: int): DerivedType {.inline.} = DerivedType(idx: idx) + +let d = newDerived(2) +assert(d.index == 2) diff --git a/tests/generics/tthread_generic.nim b/tests/generics/tthread_generic.nim index 7109bba18..fdd11d9d1 100644 --- a/tests/generics/tthread_generic.nim +++ b/tests/generics/tthread_generic.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nimrod $target --hints:on --threads:on $options $file" + cmd: "nim $target --hints:on --threads:on $options $file" """ type @@ -8,22 +8,22 @@ type b: proc(val: T) {.thread.} proc handleThreadFunc(arg: TThreadFuncArgs[int]){.thread.} = - var func = arg.a + var fn = arg.a var callback = arg.b - var output = func() + var output = fn() callback(output) -proc `@||->`*[T](func: proc(): T {.thread.}, +proc `@||->`*[T](fn: proc(): T {.thread.}, callback: proc(val: T){.thread.}): TThread[TThreadFuncArgs[T]] = var thr: TThread[TThreadFuncArgs[T]] var args: TThreadFuncArgs[T] - args.a = func + args.a = fn args.b = callback createThread(thr, handleThreadFunc, args) return thr -proc `||->`*[T](func: proc(): T{.thread.}, callback: proc(val: T){.thread.}) = - discard func @||-> callback +proc `||->`*[T](fn: proc(): T{.thread.}, callback: proc(val: T){.thread.}) = + discard fn @||-> callback when isMainModule: import os diff --git a/tests/iter/tchainediterators.nim b/tests/iter/tchainediterators.nim index 18d096761..8d9f44a54 100644 --- a/tests/iter/tchainediterators.nim +++ b/tests/iter/tchainediterators.nim @@ -28,7 +28,7 @@ iterator foo[T](x: iterator: T{.inline.}): T = var s = @[1, 2, 3] -# pass an interator several levels deep: +# pass an iterator several levels deep: for x in s.items.foo: echo x diff --git a/tests/iter/titer2.nim b/tests/iter/titer2.nim index 71c7327a7..4a7f76883 100644 --- a/tests/iter/titer2.nim +++ b/tests/iter/titer2.nim @@ -1,6 +1,9 @@ discard """ - output: '''true''' - cmd: "nimrod $target --gc:none --hints:on --warnings:off $options $file" + output: '''true +3 +4 +5''' + cmd: "nim $target --gc:none --hints:on --warnings:off $options $file" """ import hashes @@ -44,8 +47,11 @@ block Test1: # a non-generic iterator! var t = initTable[int, string]() - for k, v in t.pairs: nil - for k, v in t.pairs: nil + for k, v in t.pairs: discard + for k, v in t.pairs: discard echo "true" +# bug #1560 +for i in @[3, 4, 5]: + echo($i) diff --git a/tests/iter/titer6.nim b/tests/iter/titer6.nim index dceace0e0..2abfa0860 100644 --- a/tests/iter/titer6.nim +++ b/tests/iter/titer6.nim @@ -21,13 +21,13 @@ iterator tokenize2(s: string, seps: set[char] = Whitespace): tuple[ yield (substr(s, i, j-1), false) i = j -for word, isSep in tokenize2("ta da", whiteSpace): +for word, isSep in tokenize2("ta da", WhiteSpace): var titer2TestVar = 0 stdout.write(titer2TestVar) proc wordWrap2(s: string, maxLineWidth = 80, splitLongWords = true, - seps: set[char] = whitespace, + seps: set[char] = Whitespace, newLine = "\n"): string = result = "" for word, isSep in tokenize2(s, seps): diff --git a/tests/iter/titer8.nim b/tests/iter/titer8.nim index af0e643f1..3bc01122f 100644 --- a/tests/iter/titer8.nim +++ b/tests/iter/titer8.nim @@ -47,7 +47,7 @@ iterator count3(): int {.closure.} = yield 2 yield 3 -for word, isSep in tokenize2("ta da", whiteSpace): +for word, isSep in tokenize2("ta da", WhiteSpace): if not isSep: stdout.write(word) echo "" @@ -56,7 +56,7 @@ proc inProc() = for c in count3(): echo c - for word, isSep in tokenize2("ta da", whiteSpace): + for word, isSep in tokenize2("ta da", WhiteSpace): stdout.write(word) for c in count3(): diff --git a/tests/js.nim b/tests/js.nim index becc17834..e5e432366 100644 --- a/tests/js.nim +++ b/tests/js.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nimrod js --hints:on $options $file" + cmd: "nim js --hints:on $options $file" """ # This file tests the JavaScript generator diff --git a/tests/js/tbyvar.nim b/tests/js/tbyvar.nim new file mode 100644 index 000000000..1269e6f66 --- /dev/null +++ b/tests/js/tbyvar.nim @@ -0,0 +1,33 @@ +discard """ + output: '''foo 12 +bar 12 +2 +foo 12 +bar 12 +2 +''' +""" + +# bug #1489 +proc foo(x: int) = echo "foo ", x +proc bar(y: var int) = echo "bar ", y + +var x = 12 +foo(x) +bar(x) + +# bug #1490 +var y = 1 +y *= 2 +echo y + +proc main = + var x = 12 + foo(x) + bar(x) + + var y = 1 + y *= 2 + echo y + +main() diff --git a/tests/js/test1.nim b/tests/js/test1.nim index 09ba30676..7f1d346f0 100644 --- a/tests/js/test1.nim +++ b/tests/js/test1.nim @@ -10,9 +10,9 @@ import var inputElement = "1123" -proc OnButtonClick(inputElement: string) {.exportc.} = +proc onButtonClick(inputElement: string) {.exportc.} = let v = $inputElement - if v.allCharsInSet(whiteSpace): + if v.allCharsInSet(WhiteSpace): echo "only whitespace, hu?" else: var x = parseInt(v) diff --git a/tests/js/testobjs.nim b/tests/js/testobjs.nim new file mode 100644 index 000000000..4fb9a83dc --- /dev/null +++ b/tests/js/testobjs.nim @@ -0,0 +1,34 @@ +## Tests javascript object generation + +type + Kg = distinct float + Price = int + Item = object of RootObj + weight: Kg + price: Price + desc: cstring + Person = object of RootObj + name: cstring + age: int + item: Item + Test = object + name: cstring + Recurse[T] = object + data: T + next: ref Recurse[T] + +var + test = Test(name: "Jorden") + sword = Item(desc: "pointy", weight: Kg(10.0), + price: Price(50)) + knight = Person(name: "robert", age: 19, item: sword) + recurse4 = (ref Recurse[int])(data: 4, next: nil) + recurse3 = (ref Recurse[int])(data: 3, next: recurse4) + recurse2 = (ref Recurse[int])(data: 2, next: recurse3) + recurse1 = Recurse[int](data: 1, next: recurse2) + + +assert(test.name == "Jorden") +assert(knight.age == 19) +assert(knight.item.price == 50) +assert(recurse1.next.next.data == 3) diff --git a/tests/let/tlet.nim b/tests/let/tlet.nim index 3d36432fb..6703ba254 100644 --- a/tests/let/tlet.nim +++ b/tests/let/tlet.nim @@ -3,9 +3,9 @@ discard """ errormsg: "'name' cannot be assigned to" """ -Echo("What's your name? ") +echo("What's your name? ") let name = readLine(stdin) while name == "": - Echo("Please tell me your name: ") + echo("Please tell me your name: ") name = readLine(stdin) diff --git a/tests/lookups/tkoeniglookup.nim b/tests/lookups/tkoeniglookup.nim index e6f5c0112..6c42798ae 100644 --- a/tests/lookups/tkoeniglookup.nim +++ b/tests/lookups/tkoeniglookup.nim @@ -2,7 +2,7 @@ discard """ output: '''x: 0 y: 0''' """ -proc ToString*[T](x: T): string = return $x +proc toString*[T](x: T): string = return $x type diff --git a/tests/closure/tclosuremacro.nim b/tests/macros/tclosuremacro.nim similarity index 88% rename from tests/closure/tclosuremacro.nim rename to tests/macros/tclosuremacro.nim index 12e463316..d5d9b656c 100644 --- a/tests/closure/tclosuremacro.nim +++ b/tests/macros/tclosuremacro.nim @@ -35,9 +35,9 @@ echo noParams(() => 3) echo doWithOneAndTwo((x, y) => x + y) -noReturn(() -> void => echo("noReturn")) +noReturn((() -> void) => echo("noReturn")) proc pass2(f: (int, int) -> int): (int) -> int = - (x: int) -> int => f(2, x) + ((x: int) -> int) => f(2, x) echo pass2((x, y) => x + y)(4) diff --git a/tests/macros/tdebugstmt.nim b/tests/macros/tdebugstmt.nim index 865dc436a..00c55ccd8 100644 --- a/tests/macros/tdebugstmt.nim +++ b/tests/macros/tdebugstmt.nim @@ -7,7 +7,7 @@ x: some string''' import macros macro debug(n: varargs[expr]): stmt = - # `n` is a Nimrod AST that contains the whole macro invocation + # `n` is a Nim AST that contains the whole macro invocation # this macro returns a list of statements: result = newNimNode(nnkStmtList, n) # iterate over any argument that is passed to this macro: diff --git a/tests/macros/ttryparseexpr.nim b/tests/macros/ttryparseexpr.nim new file mode 100644 index 000000000..af932eb7d --- /dev/null +++ b/tests/macros/ttryparseexpr.nim @@ -0,0 +1,19 @@ +discard """ + outputsub: '''Error: invalid indentation 45''' +""" + +# feature request #1473 +import macros + +macro test(text: string): expr = + try: + result = parseExpr(text.strVal) + except ValueError: + result = newLit getCurrentExceptionMsg() + +const + valid = 45 + a = test("foo&&") + b = test("valid") + +echo a, " ", b diff --git a/tests/macros/tvtable.nim b/tests/macros/tvtable.nim index 51894618c..3e3b9c0e6 100644 --- a/tests/macros/tvtable.nim +++ b/tests/macros/tvtable.nim @@ -11,8 +11,8 @@ OBJ 2 bar type # these are the signatures of the virtual procs for each type - fooProc[T] = proc (o: var T): int - barProc[T] = proc (o: var T) + fooProc[T] = proc (o: var T): int {.nimcall.} + barProc[T] = proc (o: var T) {.nimcall.} # an untyped table to store the proc pointers # it's also possible to use a strongly typed tuple here diff --git a/tests/manyloc/argument_parser/argument_parser.nim b/tests/manyloc/argument_parser/argument_parser.nim index 6f4fb650e..fec00dbf8 100644 --- a/tests/manyloc/argument_parser/argument_parser.nim +++ b/tests/manyloc/argument_parser/argument_parser.nim @@ -1,7 +1,7 @@ ## Command line parsing module for Nimrod. ## -## `Nimrod `_ provides the `parseopt module -## `_ to parse options from the +## `Nim `_ provides the `parseopt module +## `_ to parse options from the ## commandline. This module tries to provide functionality to prevent you from ## writing commandline parsing and let you concentrate on providing the best ## possible experience for your users. @@ -76,14 +76,14 @@ type case kind*: Tparam_kind of PK_EMPTY: nil of PK_INT: int_val*: int - of PK_BIGGEST_INT: big_int_val*: biggestInt + of PK_BIGGEST_INT: big_int_val*: BiggestInt of PK_FLOAT: float_val*: float - of PK_BIGGEST_FLOAT: big_float_val*: biggestFloat + of PK_BIGGEST_FLOAT: big_float_val*: BiggestFloat of PK_STRING: str_val*: string of PK_BOOL: bool_val*: bool of PK_HELP: nil - Tcommandline_results* = object of TObject ## \ + Tcommandline_results* = object of RootObj ## \ ## Contains the results of the parsing. ## ## Usually this is the result of the parse() call, but you can inherit from @@ -97,7 +97,7 @@ type ## the first name variant for all options to avoid you repeating the test ## with different keys. positional_parameters*: seq[Tparsed_parameter] - options*: TOrderedTable[string, Tparsed_parameter] + options*: OrderedTable[string, Tparsed_parameter] # - Tparam_kind procs @@ -169,7 +169,7 @@ template new_parsed_parameter*(tkind: Tparam_kind, expr): Tparsed_parameter = ## assign the variable to, and thus you reduce code clutter and may use this ## to initialise single assignments variables in `let` blocks. Example: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## let ## parsed_param1 = new_parsed_parameter(PK_FLOAT, 3.41) ## parsed_param2 = new_parsed_parameter(PK_BIGGEST_INT, 2358123 * 23123) @@ -193,7 +193,7 @@ template new_parsed_parameter*(tkind: Tparam_kind, expr): Tparsed_parameter = proc init*(param: var Tcommandline_results; positional_parameters: seq[Tparsed_parameter] = @[]; - options: TOrderedTable[string, Tparsed_parameter] = + options: OrderedTable[string, Tparsed_parameter] = initOrderedTable[string, Tparsed_parameter](4)) = ## Initialization helper with default parameters. param.positional_parameters = positional_parameters @@ -231,12 +231,12 @@ template run_custom_proc(parsed_parameter: Tparsed_parameter, ## Pass in the string of the parameter triggering the call. If the if not custom_validator.isNil: except: - raise_or_quit(EInvalidValue, ("Couldn't run custom proc for " & + raise_or_quit(ValueError, ("Couldn't run custom proc for " & "parameter $1:\n$2" % [escape(parameter), getCurrentExceptionMsg()])) let message = custom_validator(parameter, parsed_parameter) if not message.isNil and message.len > 0: - raise_or_quit(EInvalidValue, ("Failed to validate value for " & + raise_or_quit(ValueError, ("Failed to validate value for " & "parameter $1:\n$2" % [escape(parameter), message])) @@ -246,50 +246,50 @@ proc parse_parameter(quit_on_failure: bool, param, value: string, ## ## Pass the parameter string which requires a value and the text the user ## passed in for it. It will be parsed according to the param_kind. This proc - ## will raise (EInvalidValue, EOverflow) if something can't be parsed. + ## will raise (ValueError, EOverflow) if something can't be parsed. result.kind = param_kind case param_kind: of PK_INT: try: result.int_val = value.parseInt - except EOverflow: - raise_or_quit(EOverflow, ("parameter $1 requires an " & + except OverflowError: + raise_or_quit(OverflowError, ("parameter $1 requires an " & "integer, but $2 is too large to fit into one") % [param, escape(value)]) - except EInvalidValue: - raise_or_quit(EInvalidValue, ("parameter $1 requires an " & + except ValueError: + raise_or_quit(ValueError, ("parameter $1 requires an " & "integer, but $2 can't be parsed into one") % [param, escape(value)]) of PK_STRING: result.str_val = value of PK_FLOAT: try: result.float_val = value.parseFloat - except EInvalidValue: - raise_or_quit(EInvalidValue, ("parameter $1 requires a " & + except ValueError: + raise_or_quit(ValueError, ("parameter $1 requires a " & "float, but $2 can't be parsed into one") % [param, escape(value)]) of PK_BOOL: try: result.bool_val = value.parseBool - except EInvalidValue: - raise_or_quit(EInvalidValue, ("parameter $1 requires a " & + except ValueError: + raise_or_quit(ValueError, ("parameter $1 requires a " & "boolean, but $2 can't be parsed into one. Valid values are: " & "y, yes, true, 1, on, n, no, false, 0, off") % [param, escape(value)]) of PK_BIGGEST_INT: try: let parsed_len = parseBiggestInt(value, result.big_int_val) if value.len != parsed_len or parsed_len < 1: - raise_or_quit(EInvalidValue, ("parameter $1 requires an " & + raise_or_quit(ValueError, ("parameter $1 requires an " & "integer, but $2 can't be parsed completely into one") % [ param, escape(value)]) - except EInvalidValue: - raise_or_quit(EInvalidValue, ("parameter $1 requires an " & + except ValueError: + raise_or_quit(ValueError, ("parameter $1 requires an " & "integer, but $2 can't be parsed into one") % [param, escape(value)]) of PK_BIGGEST_FLOAT: try: let parsed_len = parseBiggestFloat(value, result.big_float_val) if value.len != parsed_len or parsed_len < 1: - raise_or_quit(EInvalidValue, ("parameter $1 requires a " & + raise_or_quit(ValueError, ("parameter $1 requires a " & "float, but $2 can't be parsed completely into one") % [ param, escape(value)]) - except EInvalidValue: - raise_or_quit(EInvalidValue, ("parameter $1 requires a " & + except ValueError: + raise_or_quit(ValueError, ("parameter $1 requires a " & "float, but $2 can't be parsed into one") % [param, escape(value)]) of PK_EMPTY: discard @@ -298,15 +298,15 @@ proc parse_parameter(quit_on_failure: bool, param, value: string, template build_specification_lookup(): - TOrderedTable[string, ptr Tparameter_specification] = + OrderedTable[string, ptr Tparameter_specification] = ## Returns the table used to keep pointers to all of the specifications. - var result {.gensym.}: TOrderedTable[string, ptr Tparameter_specification] + var result {.gensym.}: OrderedTable[string, ptr Tparameter_specification] result = initOrderedTable[string, ptr Tparameter_specification]( nextPowerOfTwo(expected.len)) for i in 0..expected.len-1: for param_to_detect in expected[i].names: if result.hasKey(param_to_detect): - raise_or_quit(EInvalidKey, + raise_or_quit(KeyError, "Parameter $1 repeated in input specification" % param_to_detect) else: result[param_to_detect] = addr(expected[i]) @@ -344,7 +344,7 @@ proc parse*(expected: seq[Tparameter_specification] = @[], ## ## If there is any kind of error and quit_on_failure is true, the quit proc ## will be called with a user error message. If quit_on_failure is false - ## errors will raise exceptions (usually EInvalidValue or EOverflow) instead + ## errors will raise exceptions (usually ValueError or EOverflow) instead ## for you to catch and handle. assert type_of_positional_parameters != PK_EMPTY and @@ -359,7 +359,7 @@ proc parse*(expected: seq[Tparameter_specification] = @[], # Prepare the input parameter list, maybe get it from the OS if not available. var args = args if args == nil: - let total_params = ParamCount() + let total_params = paramCount() #echo "Got no explicit args, retrieving from OS. Count: ", total_params newSeq(args, total_params) for i in 0..total_params - 1: @@ -387,7 +387,7 @@ proc parse*(expected: seq[Tparameter_specification] = @[], if param.consumes == PK_HELP: echo_help(expected, type_of_positional_parameters, bad_prefixes, end_of_options) - raise_or_quit(EInvalidKey, "") + raise_or_quit(KeyError, "") if param.consumes != PK_EMPTY: if i + 1 < args.len: @@ -396,14 +396,14 @@ proc parse*(expected: seq[Tparameter_specification] = @[], run_custom_proc(parsed, param.custom_validator, arg) i += 1 else: - raise_or_quit(EInvalidValue, ("parameter $1 requires a " & + raise_or_quit(ValueError, ("parameter $1 requires a " & "value, but none was provided") % [arg]) result.options[param.names[0]] = parsed break adding_positional_parameter else: for bad_prefix in bad_prefixes: if arg.startsWith(bad_prefix): - raise_or_quit(EInvalidValue, ("Found ambiguos parameter '$1' " & + raise_or_quit(ValueError, ("Found ambiguos parameter '$1' " & "starting with '$2', put '$3' as the previous parameter " & "if you want to force it as positional parameter.") % [arg, bad_prefix, end_of_options]) @@ -415,7 +415,7 @@ proc parse*(expected: seq[Tparameter_specification] = @[], i += 1 -proc toString(runes: seq[TRune]): string = +proc toString(runes: seq[Rune]): string = result = "" for rune in runes: result.add(rune.toUTF8) @@ -424,7 +424,7 @@ proc ascii_cmp(a, b: string): int = ## Comparison ignoring non ascii characters, for better switch sorting. let a = filterIt(toSeq(runes(a)), it.isAlpha()) # Can't use filterIt twice, github bug #351. - let b = filter(toSeq(runes(b)), proc(x: TRune): bool = x.isAlpha()) + let b = filter(toSeq(runes(b)), proc(x: Rune): bool = x.isAlpha()) return system.cmp(toString(a), toString(b)) diff --git a/tests/manyloc/argument_parser/ex_wget.nimrod.cfg b/tests/manyloc/argument_parser/ex_wget.nim.cfg similarity index 100% rename from tests/manyloc/argument_parser/ex_wget.nimrod.cfg rename to tests/manyloc/argument_parser/ex_wget.nim.cfg diff --git a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim index d9c933939..d079a2e72 100644 --- a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim +++ b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim @@ -21,8 +21,8 @@ const Lib = "libchipmunk.so.6.1.1" -when defined(MoreNimrod): - {.hint: "MoreNimrod defined; some Chipmunk functions replaced in Nimrod".} +when defined(MoreNim): + {.hint: "MoreNim defined; some Chipmunk functions replaced in Nim".} {.deadCodeElim: on.} from math import sqrt, sin, cos, arctan2 when defined(CpUseFloat): @@ -729,7 +729,7 @@ proc isRogue*(body: PBody): Bool {.inline.} = defGetter(PBody, CpFloat, m, Mass) #/ Set the mass of a body. -when defined(MoreNimrod): +when defined(MoreNim): defSetter(PBody, CpFloat, m, Mass) else: proc setMass*(body: PBody; m: CpFloat){. @@ -738,7 +738,7 @@ else: #/ Get the moment of a body. defGetter(PBody, CpFloat, i, Moment) #/ Set the moment of a body. -when defined(MoreNimrod): +when defined(MoreNim): defSetter(PBody, CpFloat, i, Moment) else: proc SetMoment*(body: PBody; i: CpFloat) {. @@ -747,7 +747,7 @@ else: #/ Get the position of a body. defGetter(PBody, TVector, p, Pos) #/ Set the position of a body. -when defined(MoreNimrod): +when defined(MoreNim): defSetter(PBody, TVector, p, Pos) else: proc setPos*(body: PBody; pos: TVector) {. @@ -1088,7 +1088,7 @@ proc getSegmentRadius*(shape: PShape): CpFloat {. #var VersionString*{.importc: "cpVersionString", dynlib: Lib.}: cstring #/ Calculate the moment of inertia for a circle. #/ @c r1 and @c r2 are the inner and outer diameters. A solid circle has an inner diameter of 0. -when defined(MoreNimrod): +when defined(MoreNim): proc momentForCircle*(m, r1, r2: CpFloat; offset: TVector): CpFloat {.cdecl.} = result = m * (0.5 * (r1 * r1 + r2 * r2) + lenSq(offset)) else: diff --git a/tests/manyloc/keineschweine/dependencies/nake/nake.nim b/tests/manyloc/keineschweine/dependencies/nake/nake.nim index eade28c70..5828e400c 100644 --- a/tests/manyloc/keineschweine/dependencies/nake/nake.nim +++ b/tests/manyloc/keineschweine/dependencies/nake/nake.nim @@ -58,7 +58,7 @@ when isMainModule: for i in 1..paramCount(): args.add paramStr(i) args.add " " - quit(shell("nimrod", "c", "-r", "nakefile.nim", args)) + quit(shell("nim", "c", "-r", "nakefile.nim", args)) else: addQuitProc(proc() {.noconv.} = var diff --git a/tests/manyloc/keineschweine/dependencies/nake/nakefile.nim b/tests/manyloc/keineschweine/dependencies/nake/nakefile.nim index 24af63d10..bdf2139c9 100644 --- a/tests/manyloc/keineschweine/dependencies/nake/nakefile.nim +++ b/tests/manyloc/keineschweine/dependencies/nake/nakefile.nim @@ -2,7 +2,7 @@ import nake nakeImports task "install", "compile and install nake binary": - if shell("nimrod", "c", "nake") == 0: + if shell("nim", "c", "nake") == 0: let path = getEnv("PATH").split(PathSep) for index, dir in pairs(path): echo " ", index, ". ", dir diff --git a/tests/manyloc/keineschweine/enet_server/nakefile.nim b/tests/manyloc/keineschweine/enet_server/nakefile.nim index 1ca93a340..3764c6271 100644 --- a/tests/manyloc/keineschweine/enet_server/nakefile.nim +++ b/tests/manyloc/keineschweine/enet_server/nakefile.nim @@ -5,9 +5,9 @@ const ServerDefines = "-d:NoSFML --forceBuild" task "server", "build the server": - if shell("nimrod", ServerDefines, "-r", "compile", "enet_server") != 0: + if shell("nim", ServerDefines, "-r", "compile", "enet_server") != 0: quit "Failed to build" task "gui", "build the server GUI mode": - if shell("nimrod", "--app:gui", ServerDefines, "-r", "compile", "enet_server") != 0: + if shell("nim", "--app:gui", ServerDefines, "-r", "compile", "enet_server") != 0: quit "Failed to build" diff --git a/tests/manyloc/keineschweine/keineschweine.nimrod.cfg b/tests/manyloc/keineschweine/keineschweine.nim.cfg similarity index 100% rename from tests/manyloc/keineschweine/keineschweine.nimrod.cfg rename to tests/manyloc/keineschweine/keineschweine.nim.cfg diff --git a/tests/manyloc/nake/nake.nim b/tests/manyloc/nake/nake.nim index 4b1b35662..04b745003 100644 --- a/tests/manyloc/nake/nake.nim +++ b/tests/manyloc/nake/nake.nim @@ -58,7 +58,7 @@ when isMainModule: for i in 1..paramCount(): args.add paramStr(i) args.add " " - quit(shell("nimrod", "c", "-r", "nakefile.nim", args)) + quit(shell("nim", "c", "-r", "nakefile.nim", args)) else: addQuitProc(proc() {.noconv.} = var diff --git a/tests/manyloc/nake/nakefile.nim b/tests/manyloc/nake/nakefile.nim index 700f9ab49..d1d712964 100644 --- a/tests/manyloc/nake/nakefile.nim +++ b/tests/manyloc/nake/nakefile.nim @@ -9,16 +9,16 @@ const BinLibs = "http://dl.dropbox.com/u/37533467/libs-2012-09-12.zip" ExeName = "keineschweine" ServerDefines = "-d:NoSFML -d:NoChipmunk" - TestBuildDefines = "-d:escapeMenuTest -d:debugWeps -d:showFPS -d:moreNimrod -d:debugKeys -d:foo -d:recordMode --forceBuild" + TestBuildDefines = "-d:escapeMenuTest -d:debugWeps -d:showFPS -d:moreNim -d:debugKeys -d:foo -d:recordMode --forceBuild" ReleaseDefines = "-d:release --deadCodeElim:on" ReleaseTestDefines = "-d:debugWeps -d:debugKeys --forceBuild" task "testprofile", "..": - if shell("nimrod", TestBuildDefines, "--profiler:on", "--stacktrace:on", "compile", ExeName) == 0: + if shell("nim", TestBuildDefines, "--profiler:on", "--stacktrace:on", "compile", ExeName) == 0: shell "."/ExeName, "offline" task "test", "Build with test defines": - if shell("nimrod", TestBuildDefines, "compile", ExeName) != 0: + if shell("nim", TestBuildDefines, "compile", ExeName) != 0: quit "The build failed." task "testrun", "Build with test defines and run": @@ -26,22 +26,22 @@ task "testrun", "Build with test defines and run": shell "."/ExeName task "test2", "Build release test build test release build": - if shell("nimrod", ReleaseDefines, ReleaseTestDefines, "compile", ExeName) == 0: + if shell("nim", ReleaseDefines, ReleaseTestDefines, "compile", ExeName) == 0: shell "."/ExeName discard """task "dirserver", "build the directory server": withDir "server": - if shell("nimrod", ServerDefines, "compile", "dirserver") != 0: + if shell("nim", ServerDefines, "compile", "dirserver") != 0: echo "Failed to build the dirserver" quit 1""" task "zoneserver", "build the zone server": withDir "enet_server": - if shell("nimrod", ServerDefines, "compile", "enet_server") != 0: + if shell("nim", ServerDefines, "compile", "enet_server") != 0: quit "Failed to build the zoneserver" task "zoneserver-gui", "build the zone server, with gui!": withDir "enet_server": - if shell("nimrod", ServerDefines, "--app:gui", "compile", "enet_server") != 0: + if shell("nim", ServerDefines, "--app:gui", "compile", "enet_server") != 0: quit "Failed to build the zoneserver" task "servers", "build the server and directory server": @@ -54,7 +54,7 @@ task "all", "run SERVERS and TEST tasks": runTask "test" task "release", "release build": - let res = shell("nimrod", ReleaseDefines, "compile", ExeName) + let res = shell("nim", ReleaseDefines, "compile", ExeName) if res != 0: echo "The build failed." quit 1 @@ -84,7 +84,7 @@ task "download", "download game assets": skipAssets = false path = expandFilename("data") path.add DirSep - path.add(extractFilename(gameAssets)) + path.add(extractFilename(GameAssets)) if existsFile(path): echo "The file already exists\n", "[R]emove [M]ove [Q]uit [S]kip Source: ", GameAssets @@ -92,7 +92,7 @@ task "download", "download game assets": of "r": removeFile path of "m": - moveFile path, path/../(extractFilename(gameAssets)&"-old") + moveFile path, path/../(extractFilename(GameAssets)&"-old") of "s": skipAssets = true else: @@ -101,7 +101,7 @@ task "download", "download game assets": echo "Downloading from ", GameAssets if not skipAssets: echo "Downloading to ", path - downloadFile gameAssets, path + downloadFile GameAssets, path echo "Download finished" let targetDir = parentDir(parentDir(path)) diff --git a/tests/manyloc/nake/nakefile.nimrod.cfg b/tests/manyloc/nake/nakefile.nim.cfg similarity index 100% rename from tests/manyloc/nake/nakefile.nimrod.cfg rename to tests/manyloc/nake/nakefile.nim.cfg diff --git a/tests/manyloc/named_argument_bug/main.nimrod.cfg b/tests/manyloc/named_argument_bug/main.nim.cfg similarity index 100% rename from tests/manyloc/named_argument_bug/main.nimrod.cfg rename to tests/manyloc/named_argument_bug/main.nim.cfg diff --git a/tests/manyloc/named_argument_bug/tri_engine/math/vec.nim b/tests/manyloc/named_argument_bug/tri_engine/math/vec.nim index a6b7bac63..3b57acb8e 100644 --- a/tests/manyloc/named_argument_bug/tri_engine/math/vec.nim +++ b/tests/manyloc/named_argument_bug/tri_engine/math/vec.nim @@ -3,10 +3,10 @@ import tri_engine/config type - TV2*[T:TNumber=TR] = array[0..1, T] - TV3*[T:TNumber=TR] = array[0..2, T] - TV4*[T:TNumber=TR] = array[0..3, T] - TVT*[T:TNumber=TR] = TV2|TV3|TV4 + TV2*[T:SomeNumber=TR] = array[0..1, T] + TV3*[T:SomeNumber=TR] = array[0..2, T] + TV4*[T:SomeNumber=TR] = array[0..3, T] + TVT*[T:SomeNumber=TR] = TV2|TV3|TV4 #TV2* = array[0..1, TR] #TV3* = array[0..2, TR] #TV4* = array[0..3, TR] diff --git a/tests/manyloc/packages/noconflicts.nimrod.cfg b/tests/manyloc/packages/noconflicts.nim.cfg similarity index 100% rename from tests/manyloc/packages/noconflicts.nimrod.cfg rename to tests/manyloc/packages/noconflicts.nim.cfg diff --git a/tests/manyloc/standalone/barebone.nimrod.cfg b/tests/manyloc/standalone/barebone.nim.cfg similarity index 100% rename from tests/manyloc/standalone/barebone.nimrod.cfg rename to tests/manyloc/standalone/barebone.nim.cfg diff --git a/tests/metatype/tautonotgeneric.nim b/tests/metatype/tautonotgeneric.nim new file mode 100644 index 000000000..a55ae488e --- /dev/null +++ b/tests/metatype/tautonotgeneric.nim @@ -0,0 +1,15 @@ +discard """ + output: "wof!" +""" + +# bug #1659 +type Animal = ref object {.inheritable.} +type Dog = ref object of Animal + +method say(a: Animal): auto = "wat!" +method say(a: Dog): auto = "wof!" + +proc saySomething(a: Animal): auto = a.say() + +var a = Dog() +echo saySomething(a) diff --git a/tests/metatype/tcompositetypeclasses.nim b/tests/metatype/tcompositetypeclasses.nim index a2db73769..5ae93795f 100644 --- a/tests/metatype/tcompositetypeclasses.nim +++ b/tests/metatype/tcompositetypeclasses.nim @@ -33,7 +33,7 @@ accept baz(vbaz) reject baz(vnotbaz) reject bar(vfoo) -# https://github.com/Araq/Nimrod/issues/517 +# https://github.com/Araq/Nim/issues/517 type TVecT*[T] = array[0..1, T]|array[0..2, T]|array[0..3, T] TVec2* = array[0..1, float32] @@ -43,7 +43,7 @@ proc f[T](a: TVecT[T], b: TVecT[T]): T = discard var x: float = f([0.0'f32, 0.0'f32], [0.0'f32, 0.0'f32]) var y = f(TVec2([0.0'f32, 0.0'f32]), TVec2([0.0'f32, 0.0'f32])) -# https://github.com/Araq/Nimrod/issues/602 +# https://github.com/Araq/Nim/issues/602 type TTest = object TTest2* = object diff --git a/tests/metatype/tsemistatic.nim b/tests/metatype/tsemistatic.nim index d187f153c..0a003be03 100644 --- a/tests/metatype/tsemistatic.nim +++ b/tests/metatype/tsemistatic.nim @@ -1,6 +1,7 @@ discard """ msg: "static 10\ndynamic\nstatic 20\n" output: "s\nd\nd\ns" + disabled: "true" """ proc foo(x: semistatic[int]) = diff --git a/tests/metatype/ttypeclasses.nim b/tests/metatype/ttypeclasses.nim index 677229868..db9db7713 100644 --- a/tests/metatype/ttypeclasses.nim +++ b/tests/metatype/ttypeclasses.nim @@ -1,3 +1,18 @@ +discard """ + output: '''12 +1xxx +true0 +12 +testtest +1010 +11string +testtest1 +seq +seq +seq +foo seq +foo of numeric'''""" + type TFoo[T] = object val: T diff --git a/tests/metatype/tusertypeclasses.nim b/tests/metatype/tusertypeclasses.nim index a5d575dbf..6e9e4934b 100644 --- a/tests/metatype/tusertypeclasses.nim +++ b/tests/metatype/tusertypeclasses.nim @@ -12,7 +12,7 @@ type (x < y) is bool ObjectContainer = generic C - C.len is ordinal + C.len is Ordinal for v in items(C): v.type is tuple|object diff --git a/tests/metatype/typeclassinference.nim b/tests/metatype/typeclassinference.nim index 72b5aca96..2ac037ac5 100644 --- a/tests/metatype/typeclassinference.nim +++ b/tests/metatype/typeclassinference.nim @@ -1,3 +1,8 @@ +discard """ + errormsg: "type mismatch: got (string) but expected 'ptr'" + line: 20 +""" + import typetraits type @@ -8,3 +13,9 @@ var x = Vec([1, 2, 3]) static: assert x.type.name == "Vec[static[int](3), int]" +var str1: string = "hello, world!" +var ptr1: ptr = addr(str1) + +var str2: string = "hello, world!" +var ptr2: ptr = str2 + diff --git a/tests/method/tmultim2.nim b/tests/method/tmultim2.nim index 75f652137..c5fb568a0 100644 --- a/tests/method/tmultim2.nim +++ b/tests/method/tmultim2.nim @@ -1,6 +1,9 @@ discard """ file: "tmultim2.nim" - output: "collide: unit, thing collide: unit, thing collide: thing, unit" + output: '''collide: unit, thing +collide: unit, thing +collide: thing, unit +collide: thing, thing''' """ # Test multi methods @@ -12,25 +15,25 @@ type a, b: int method collide(a, b: TThing) {.inline.} = - quit "to override!" + echo "collide: thing, thing" method collide(a: TThing, b: TUnit) {.inline.} = - write stdout, "collide: thing, unit " + echo "collide: thing, unit" method collide(a: TUnit, b: TThing) {.inline.} = - write stdout, "collide: unit, thing " + echo "collide: unit, thing" proc test(a, b: TThing) {.inline.} = collide(a, b) +proc staticCollide(a, b: TThing) {.inline.} = + procCall collide(a, b) + + var a: TThing b, c: TUnit collide(b, c) # ambiguous (unit, thing) or (thing, unit)? -> prefer unit, thing! test(b, c) collide(a, b) -#OUT collide: unit, thing collide: unit, thing collide: thing, unit - - - - +staticCollide(a, b) diff --git a/tests/misc/t99bott.nim b/tests/misc/t99bott.nim index d18cb0d5c..b3b30d296 100644 --- a/tests/misc/t99bott.nim +++ b/tests/misc/t99bott.nim @@ -6,7 +6,7 @@ discard """ """ ## 99 Bottles of Beer ## http://www.99-bottles-of-beer.net/ -## Nimrod version +## Nim version ## Author: Philippe Lhoste http://Phi.Lho.free.fr # 2012-11-25 diff --git a/tests/misc/tcolonisproc.nim b/tests/misc/tcolonisproc.nim index e55587dfc..af4077284 100644 --- a/tests/misc/tcolonisproc.nim +++ b/tests/misc/tcolonisproc.nim @@ -2,10 +2,11 @@ proc p(a, b: int, c: proc ()) = c() - -p(1, 3): - echo 1 - echo 3 +when false: + # language spec changed: + p(1, 3): + echo 1 + echo 3 p(1, 1, proc() = echo 1 diff --git a/tests/misc/tdllvar.nim b/tests/misc/tdllvar.nim index ab767770c..5a31dfbbb 100644 --- a/tests/misc/tdllvar.nim +++ b/tests/misc/tdllvar.nim @@ -2,9 +2,9 @@ import os proc getDllName: string = result = "mylib.dll" - if ExistsFile(result): return + if fileExists(result): return result = "mylib2.dll" - if ExistsFile(result): return + if fileExists(result): return quit("could not load dynamic library") proc myImport(s: cstring) {.cdecl, importc, dynlib: getDllName().} diff --git a/tests/misc/temit.nim b/tests/misc/temit.nim index ff8df0585..e2a9eaff1 100644 --- a/tests/misc/temit.nim +++ b/tests/misc/temit.nim @@ -10,8 +10,8 @@ static int cvariable = 420; """.} proc embedsC() = - var nimrodVar = 89 - {.emit: """printf("%d\n", cvariable + (int)`nimrodVar`);""".} + var nimVar = 89 + {.emit: """printf("%d\n", cvariable + (int)`nimVar`);""".} embedsC() diff --git a/tests/misc/teventemitter.nim b/tests/misc/teventemitter.nim index 9ecf72ea2..c1cc3d3a9 100644 --- a/tests/misc/teventemitter.nim +++ b/tests/misc/teventemitter.nim @@ -5,29 +5,29 @@ discard """ import tables, lists type - TEventArgs = object of TObject - TEventEmitter = object of TObject - events*: TTable[string, TDoublyLinkedList[proc(e: TEventArgs) {.nimcall.}]] + EventArgs = object of RootObj + EventEmitter = object of RootObj + events*: Table[string, DoublyLinkedList[proc(e: EventArgs) {.nimcall.}]] -proc emit*(emitter: TEventEmitter, event: string, args: TEventArgs) = - for func in nodes(emitter.events[event]): - func.value(args) #call function with args. +proc emit*(emitter: EventEmitter, event: string, args: EventArgs) = + for fn in nodes(emitter.events[event]): + fn.value(args) #call function with args. -proc on*(emitter: var TEventEmitter, event: string, - func: proc(e: TEventArgs) {.nimcall.}) = +proc on*(emitter: var EventEmitter, event: string, + fn: proc(e: EventArgs) {.nimcall.}) = if not hasKey(emitter.events, event): - var list: TDoublyLinkedList[proc(e: TEventArgs) {.nimcall.}] + var list: DoublyLinkedList[proc(e: EventArgs) {.nimcall.}] add(emitter.events, event, list) #if not, add it. - append(emitter.events.mget(event), func) + append(emitter.events.mget(event), fn) -proc initEmitter(emitter: var TEventEmitter) = +proc initEmitter(emitter: var EventEmitter) = emitter.events = initTable[string, - TDoublyLinkedList[proc(e: TEventArgs) {.nimcall.}]]() + DoublyLinkedList[proc(e: EventArgs) {.nimcall.}]]() var - ee: TEventEmitter - args: TEventArgs + ee: EventEmitter + args: EventArgs initEmitter(ee) -ee.on("print", proc(e: TEventArgs) = echo("pie")) +ee.on("print", proc(e: EventArgs) = echo("pie")) ee.emit("print", args) diff --git a/tests/misc/thallo.nim b/tests/misc/thallo.nim index 7244c27a1..cbeb45b97 100644 --- a/tests/misc/thallo.nim +++ b/tests/misc/thallo.nim @@ -37,7 +37,7 @@ macrotest(stdout) #GC_disable() -echo("This was compiled by Nimrod version " & system.nimrodVersion) +echo("This was compiled by Nim version " & system.NimVersion) writeln(stdout, "Hello", " World", "!") echo(["a", "b", "c", "d"].len) diff --git a/tests/misc/tints.nim b/tests/misc/tints.nim index fb2852af9..ded24fb5c 100644 --- a/tests/misc/tints.nim +++ b/tests/misc/tints.nim @@ -1,6 +1,8 @@ discard """ - file: "tints.nim" - output: "Success" + output: ''' +0 0 +0 0 +Success''' """ # Test the different integer operations @@ -41,5 +43,12 @@ test(`shl`, 0xff'i8, 0x4'i8, 0xf0'i8) test(`shl`, 0xffffffff'i64, 0x4'i64, 0xffffffff0'i64) test(`shl`, 0xffffffff'i32, 0x4'i32, 0xfffffff0'i32) -Echo("Success") #OUT Success +# bug #916 +proc unc(a: float): float = + return a + +echo int(unc(0.5)), " ", int(unc(-0.5)) +echo int(0.5), " ", int(-0.5) + +echo("Success") #OUT Success diff --git a/tests/misc/tlastmod.nim b/tests/misc/tlastmod.nim index 75b047fc8..92ac922f7 100644 --- a/tests/misc/tlastmod.nim +++ b/tests/misc/tlastmod.nim @@ -6,13 +6,13 @@ import proc main() = var a, b: TTime - a = getLastModificationTime(ParamStr(1)) - b = getLastModificationTime(ParamStr(2)) + a = getLastModificationTime(paramStr(1)) + b = getLastModificationTime(paramStr(2)) writeln(stdout, $a) writeln(stdout, $b) if a < b: - Write(stdout, "$2 is newer than $1\n" % [ParamStr(1), ParamStr(2)]) + write(stdout, "$2 is newer than $1\n" % [paramStr(1), paramStr(2)]) else: - Write(stdout, "$1 is newer than $2\n" % [ParamStr(1), ParamStr(2)]) + write(stdout, "$1 is newer than $2\n" % [paramStr(1), paramStr(2)]) main() diff --git a/tests/misc/tloops.nim b/tests/misc/tloops.nim index f6f939769..1aada0298 100644 --- a/tests/misc/tloops.nim +++ b/tests/misc/tloops.nim @@ -31,7 +31,7 @@ proc TestLoops() = break break - while True: + while true: break diff --git a/tests/misc/tmandelbrot.nim b/tests/misc/tmandelbrot.nim index bb1b46d89..4228b0416 100644 --- a/tests/misc/tmandelbrot.nim +++ b/tests/misc/tmandelbrot.nim @@ -1,8 +1,8 @@ discard """ - cmd: "nimrod $target --hints:on -d:release $options $file" + cmd: "nim $target --hints:on -d:release $options $file" """ -# -*- nimrod -*- +# -*- nim -*- import math import os diff --git a/tests/misc/tnoinst.nim b/tests/misc/tnoinst.nim index db1058d09..4c8d9d1aa 100644 --- a/tests/misc/tnoinst.nim +++ b/tests/misc/tnoinst.nim @@ -1,6 +1,7 @@ discard """ line: 12 errormsg: "instantiate 'notConcrete' explicitly" + disabled: "true" """ proc wrap[T]() = diff --git a/tests/misc/tnot.nim b/tests/misc/tnot.nim index cd0f538e6..6193e21e1 100644 --- a/tests/misc/tnot.nim +++ b/tests/misc/tnot.nim @@ -5,16 +5,16 @@ discard """ """ # BUG: following compiles, but should not: -proc nodeOfDegree(x: Int): bool = +proc nodeOfDegree(x: int): bool = result = false proc main = for j in 0..2: for i in 0..10: if not nodeOfDegree(1) >= 0: #ERROR_MSG type mismatch - Echo "Yes" + echo "Yes" else: - Echo "No" + echo "No" main() diff --git a/tests/misc/tpos.nim b/tests/misc/tpos.nim index 3d72536dd..5560ef050 100644 --- a/tests/misc/tpos.nim +++ b/tests/misc/tpos.nim @@ -14,10 +14,10 @@ proc mypos(sub, s: string, start: int = 0): int = if i >= N: result = -1 else: - while True: + while true: if s[i] == sub[j]: - Inc(i) - Inc(j) + inc(i) + inc(j) else: i = i - j + 1 j = 0 diff --git a/tests/misc/tprep.nim b/tests/misc/tprep.nim index 4ef9e2543..8f40300d6 100644 --- a/tests/misc/tprep.nim +++ b/tests/misc/tprep.nim @@ -24,7 +24,7 @@ else: var s: string -write(stdout, "compiled at " & system.compileDate & - " " & compileTime & "\n") +write(stdout, "compiled at " & system.CompileDate & + " " & CompileTime & "\n") echo getDateStr() echo getClockStr() diff --git a/tests/misc/tradix.nim b/tests/misc/tradix.nim index e5998ee12..311aa9ccd 100644 --- a/tests/misc/tradix.nim +++ b/tests/misc/tradix.nim @@ -4,7 +4,7 @@ ## We use a radix tree with node compression. ## There are two node kinds: -const bitsPerUnit = 8*sizeof(int) +const BitsPerUnit = 8*sizeof(int) type TRadixNodeKind = enum rnLinear, rnFull, rnLeafBits, rnLeafLinear @@ -42,13 +42,13 @@ proc testBit(w, i: int): bool {.inline.} = result = (w and (1 shl (i %% BitsPerUnit))) != 0 proc setBit(w: var int, i: int) {.inline.} = - w = w or (1 shl (i %% bitsPerUnit)) + w = w or (1 shl (i %% BitsPerUnit)) proc resetBit(w: var int, i: int) {.inline.} = - w = w and not (1 shl (i %% bitsPerUnit)) + w = w and not (1 shl (i %% BitsPerUnit)) proc testOrSetBit(w: var int, i: int): bool {.inline.} = - var x = (1 shl (i %% bitsPerUnit)) + var x = (1 shl (i %% BitsPerUnit)) if (w and x) != 0: return true w = w or x @@ -78,7 +78,7 @@ proc exclLeaf(r: PRadixNode, a: int) = return else: assert(false) -proc contains*(r: PRadixNode, a: TAddress): bool = +proc contains*(r: PRadixNode, a: ByteAddress): bool = if r == nil: return false var x = searchInner(r, a shr 24 and 0xff) if x == nil: return false @@ -88,7 +88,7 @@ proc contains*(r: PRadixNode, a: TAddress): bool = if x == nil: return false return searchLeaf(x, a and 0xff) -proc excl*(r: PRadixNode, a: TAddress): bool = +proc excl*(r: PRadixNode, a: ByteAddress): bool = if r == nil: return false var x = searchInner(r, a shr 24 and 0xff) if x == nil: return false @@ -167,10 +167,10 @@ proc addInner(r: var PRadixNode, a: int, d: int): bool = return addInner(x.b[k], a, d-8) else: assert(false) -proc incl*(r: var PRadixNode, a: TAddress) {.inline.} = +proc incl*(r: var PRadixNode, a: ByteAddress) {.inline.} = discard addInner(r, a, 24) -proc testOrIncl*(r: var PRadixNode, a: TAddress): bool {.inline.} = +proc testOrIncl*(r: var PRadixNode, a: ByteAddress): bool {.inline.} = return addInner(r, a, 24) iterator innerElements(r: PRadixNode): tuple[prefix: int, n: PRadixNode] = @@ -204,7 +204,7 @@ iterator leafElements(r: PRadixNode): int = yield ze(r.keys[i]) else: assert(false) -iterator elements*(r: PRadixNode): TAddress {.inline.} = +iterator elements*(r: PRadixNode): ByteAddress {.inline.} = for p1, n1 in innerElements(r): for p2, n2 in innerElements(n1): for p3, n3 in innerElements(n2): @@ -297,7 +297,7 @@ when false: result = ze(r.keys[i.x]) inc(i.x) - iterator elements(r: PRadixNode): TAddress {.inline.} = + iterator elements(r: PRadixNode): ByteAddress {.inline.} = var a, b, c, d: TRadixIter init(a, r) diff --git a/tests/misc/treadln.nim b/tests/misc/treadln.nim index 1117ab5f9..1edbea992 100644 --- a/tests/misc/treadln.nim +++ b/tests/misc/treadln.nim @@ -2,11 +2,11 @@ # Macintosh, Unix or Windows text format. var - inp: TFile + inp: File line: string if open(inp, "readme.txt"): - while not EndOfFile(inp): + while not endOfFile(inp): line = readLine(inp) echo("#" & line & "#") close(inp) diff --git a/tests/misc/tsimplesort.nim b/tests/misc/tsimplesort.nim index 0167ca78a..c282b3445 100644 --- a/tests/misc/tsimplesort.nim +++ b/tests/misc/tsimplesort.nim @@ -4,11 +4,7 @@ discard """ import hashes, math - -when defined(shallowADT): - {.pragma: myShallow, shallow.} -else: - {.pragma: myShallow.} +{.pragma: myShallow.} type TSlotEnum = enum seEmpty, seFilled, seDeleted @@ -63,7 +59,7 @@ template rawInsertImpl() = data[h].val = val data[h].slot = seFilled -proc RawGet[A, B](t: TTable[A, B], key: A): int = +proc rawGet[A, B](t: TTable[A, B], key: A): int = rawGetImpl() proc `[]`*[A, B](t: TTable[A, B], key: A): B = @@ -71,31 +67,31 @@ proc `[]`*[A, B](t: TTable[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 index = RawGet(t, key) + var index = rawGet(t, key) if index >= 0: result = t.data[index].val proc hasKey*[A, B](t: TTable[A, B], key: A): bool = ## returns true iff `key` is in the table `t`. result = rawGet(t, key) >= 0 -proc RawInsert[A, B](t: var TTable[A, B], data: var TKeyValuePairSeq[A, B], +proc rawInsert[A, B](t: var TTable[A, B], data: var TKeyValuePairSeq[A, B], key: A, val: B) = rawInsertImpl() -proc Enlarge[A, B](t: var TTable[A, B]) = +proc enlarge[A, B](t: var TTable[A, B]) = var n: TKeyValuePairSeq[A, B] newSeq(n, len(t.data) * growthFactor) for i in countup(0, high(t.data)): - if t.data[i].slot == seFilled: RawInsert(t, n, t.data[i].key, t.data[i].val) + if t.data[i].slot == seFilled: rawInsert(t, n, t.data[i].key, t.data[i].val) swap(t.data, n) -template PutImpl() = - var index = RawGet(t, key) +template putImpl() = + var index = rawGet(t, key) if index >= 0: t.data[index].val = val else: - if mustRehash(len(t.data), t.counter): Enlarge(t) - RawInsert(t, t.data, key, val) + if mustRehash(len(t.data), t.counter): enlarge(t) + rawInsert(t, t.data, key, val) inc(t.counter) proc `[]=`*[A, B](t: var TTable[A, B], key: A, val: B) = @@ -104,7 +100,7 @@ proc `[]=`*[A, B](t: var TTable[A, B], key: A, val: B) = proc del*[A, B](t: var TTable[A, B], key: A) = ## deletes `key` from hash table `t`. - var index = RawGet(t, key) + var index = rawGet(t, key) if index >= 0: t.data[index].slot = seDeleted dec(t.counter) @@ -183,24 +179,24 @@ proc hasKey*[A](t: TCountTable[A], key: A): bool = ## returns true iff `key` is in the table `t`. result = rawGet(t, key) >= 0 -proc RawInsert[A](t: TCountTable[A], data: var seq[tuple[key: A, val: int]], +proc rawInsert[A](t: TCountTable[A], data: var seq[tuple[key: A, val: int]], key: A, val: int) = var h: THash = hash(key) and high(data) while data[h].val != 0: h = nextTry(h, high(data)) data[h].key = key data[h].val = val -proc Enlarge[A](t: var TCountTable[A]) = +proc enlarge[A](t: var TCountTable[A]) = var n: seq[tuple[key: A, val: int]] newSeq(n, len(t.data) * growthFactor) for i in countup(0, high(t.data)): - if t.data[i].val != 0: RawInsert(t, n, t.data[i].key, t.data[i].val) + if t.data[i].val != 0: rawInsert(t, n, t.data[i].key, t.data[i].val) swap(t.data, n) proc `[]=`*[A](t: var TCountTable[A], key: A, val: int) = ## puts a (key, value)-pair into `t`. `val` has to be positive. assert val > 0 - PutImpl() + putImpl() proc initCountTable*[A](initialSize=64): TCountTable[A] = ## creates a new count table that is empty. `initialSize` needs to be @@ -224,11 +220,11 @@ proc inc*[A](t: var TCountTable[A], key: A, val = 1) = if index >= 0: inc(t.data[index].val, val) else: - if mustRehash(len(t.data), t.counter): Enlarge(t) - RawInsert(t, t.data, key, val) + if mustRehash(len(t.data), t.counter): enlarge(t) + rawInsert(t, t.data, key, val) inc(t.counter) -proc Smallest*[A](t: TCountTable[A]): tuple[key: A, val: int] = +proc smallest*[A](t: TCountTable[A]): tuple[key: A, val: int] = ## returns the largest (key,val)-pair. Efficiency: O(n) assert t.len > 0 var minIdx = 0 @@ -237,7 +233,7 @@ proc Smallest*[A](t: TCountTable[A]): tuple[key: A, val: int] = result.key = t.data[minIdx].key result.val = t.data[minIdx].val -proc Largest*[A](t: TCountTable[A]): tuple[key: A, val: int] = +proc largest*[A](t: TCountTable[A]): tuple[key: A, val: int] = ## returns the (key,val)-pair with the largest `val`. Efficiency: O(n) assert t.len > 0 var maxIdx = 0 diff --git a/tests/misc/tunsigned64mod.nim b/tests/misc/tunsigned64mod.nim new file mode 100644 index 000000000..9ae0d535a --- /dev/null +++ b/tests/misc/tunsigned64mod.nim @@ -0,0 +1,12 @@ + +# bug #1638 + +import unsigned + +let v1 = 7 +let v2 = 7'u64 + +let t1 = v1 mod 2 # works +let t2 = 7'u64 mod 2'u64 # works +let t3 = v2 mod 2'u64 # Error: invalid type: 'range 0..1(uint64) +let t4 = (v2 mod 2'u64).uint64 # works diff --git a/tests/misc/tvarnums.nim b/tests/misc/tvarnums.nim index 4f99df8b9..b880cf006 100644 --- a/tests/misc/tvarnums.nim +++ b/tests/misc/tvarnums.nim @@ -74,7 +74,7 @@ proc toNum(b: TBuffer): int32 = while (ze(b[i]) and 128) != 0: inc(i) result = result or ((int32(ze(b[i])) and 127'i32) shl Shift) - Shift = shift + 7'i32 + Shift = Shift + 7'i32 if (ze(b[0]) and (1 shl 6)) != 0: # sign bit set? result = (not result) +% 1'i32 # this is the same as ``- result`` diff --git a/tests/modules/mexport2a.nim b/tests/modules/mexport2a.nim new file mode 100644 index 000000000..3cf84337e --- /dev/null +++ b/tests/modules/mexport2a.nim @@ -0,0 +1,7 @@ + +import mexport2b +export mexport2b +proc printAbc*() = echo "abc" + +proc foo*() = echo "A.foo" + diff --git a/tests/modules/mexport2b.nim b/tests/modules/mexport2b.nim new file mode 100644 index 000000000..1af034133 --- /dev/null +++ b/tests/modules/mexport2b.nim @@ -0,0 +1,3 @@ +proc printXyz*() = echo "xyz" + +proc foo*(x: int) = echo "B.foo" diff --git a/tests/modules/texport2.nim b/tests/modules/texport2.nim new file mode 100644 index 000000000..6e55873c5 --- /dev/null +++ b/tests/modules/texport2.nim @@ -0,0 +1,11 @@ +# bug #1595, #1612 + +import mexport2a + +proc main() = + echo "Import Test, two lines should follow. One with abc and one with xyz." + printAbc() + printXyz() + +main() +foo(3) diff --git a/tests/modules/tmismatchedvisibility.nim b/tests/modules/tmismatchedvisibility.nim new file mode 100644 index 000000000..6f2f79282 --- /dev/null +++ b/tests/modules/tmismatchedvisibility.nim @@ -0,0 +1,9 @@ +discard """ + line: 8 + errormsg: "public implementation 'tmismatchedvisibility.foo(a: int): int' has non-public forward declaration in tmismatchedvisibility.nim(6,5)" +""" + +proc foo(a: int): int + +proc foo*(a: int): int = + result = a + a \ No newline at end of file diff --git a/tests/objects/tinherentedvalues.nim b/tests/objects/tinherentedvalues.nim new file mode 100644 index 000000000..c96a0fd6d --- /dev/null +++ b/tests/objects/tinherentedvalues.nim @@ -0,0 +1,55 @@ +discard """ + output: '''tbObj of TC false +false +true +5 +false''' +""" + +# bug #1053 +type + TA = object of TObject + a: int + + TB = object of TA + b: int + + TC = object of TB + c: int + +proc test(p: TA) = + #echo "p of TB ", p of TB + if p of TB: + var tbObj = TB(p) + + # tbObj is actually no longer compatible with TC: + echo "tbObj of TC ", tbObj of TC + +var v = TC() +v.a = 1 +v.b = 2 +v.c = 3 +test(v) + + +# bug #924 +type + MyObject = object of TObject + x: int + +var + asd: MyObject + +proc isMyObject(obj: TObject) = + echo obj of MyObject + if obj of MyObject: + let a = MyObject(obj) + echo a.x + +asd.x = 5 + +var asdCopy = TObject(asd) +echo asdCopy of MyObject + +isMyObject(asd) +isMyObject(asdCopy) diff --git a/tests/objects/tobjconstr2.nim b/tests/objects/tobjconstr2.nim index 8ef7004e4..39683ddc5 100644 --- a/tests/objects/tobjconstr2.nim +++ b/tests/objects/tobjconstr2.nim @@ -32,3 +32,19 @@ type size: tuple[w, h: float] var d = Graphic(kind: 1, size: (12.9, 6.9)) + +# bug #1274 +type + K = enum Koo, Kar + Graphic2 = object of RootObj + case kind: K + of Koo: + radius: float + of Kar: + size: tuple[w, h: float] + +type NamedGraphic = object of Graphic2 + name: string + +var ngr = NamedGraphic(kind: Koo, radius: 6.9, name: "Foo") +echo ngr.name diff --git a/tests/objects/tobjcov.nim b/tests/objects/tobjcov.nim index fc44edf8e..8391727f2 100644 --- a/tests/objects/tobjcov.nim +++ b/tests/objects/tobjcov.nim @@ -9,7 +9,7 @@ type proc ap(x: var TA) = x.a = -1 proc bp(x: var TB) = x.b[high(x.b)] = -1 -# in Nimrod proc (x: TB) is compatible to proc (x: TA), +# in Nim proc (x: TB) is compatible to proc (x: TA), # but this is not type safe: var f = cast[proc (x: var TA) {.nimcall.}](bp) var a: TA diff --git a/tests/objects/tobject3.nim b/tests/objects/tobject3.nim index 935e6ca8c..85cf1cfe3 100644 --- a/tests/objects/tobject3.nim +++ b/tests/objects/tobject3.nim @@ -1,5 +1,9 @@ + +# It turned out that it's hard to generate correct for these two test cases at +# the same time. + type - TFoo = ref object of TObject + TFoo = ref object of RootObj Data: int TBar = ref object of TFoo nil @@ -26,3 +30,30 @@ var b: TBar2 new(b) Foo(b) + +# bug #837 +type + PView* = ref TView + TView* {.inheritable.} = object + data: int + + PWindow* = ref TWindow + TWindow* = object of TView + data3: int + + PDesktop* = ref TDesktop + TDesktop* = object of TView + data2: int + +proc makeDesktop(): PDesktop = new(TDesktop) + +proc makeWindow(): PWindow = new(TWindow) + +proc thisCausesError(a: var PView, b: PView) = + discard + +var dd = makeDesktop() +var aa = makeWindow() + +thisCausesError(dd, aa) + diff --git a/tests/objects/toop1.nim b/tests/objects/toop1.nim index 350799f51..0d8ba124b 100644 --- a/tests/objects/toop1.nim +++ b/tests/objects/toop1.nim @@ -6,7 +6,7 @@ discard """ import macros type - TFigure = object of TObject # abstract base class: + TFigure = object of RootObj # abstract base class: draw: proc (my: var TFigure) {.nimcall.} # concrete classes implement this proc init(f: var TFigure) = @@ -56,7 +56,7 @@ macro `!` (n: expr): stmt {.immediate.} = result.add(n[1]) # obj type - TSocket* = object of TObject + TSocket* = object of RootObj FHost: int # cannot be accessed from the outside of the module # the `F` prefix is a convention to avoid clashes since # the accessors are named `host` @@ -84,6 +84,3 @@ r!draw c!draw() #OUT 34[]o 5 - - - diff --git a/tests/objvariant/tyaoption.nim b/tests/objvariant/tyaoption.nim index 635e60bb8..7a29b8008 100644 --- a/tests/objvariant/tyaoption.nim +++ b/tests/objvariant/tyaoption.nim @@ -7,7 +7,7 @@ some(10)''' import strutils type Option[A] = object - case isDefined*: Bool + case isDefined*: bool of true: value*: A of false: @@ -19,7 +19,7 @@ proc some[A](value: A): Option[A] = proc none[A](): Option[A] = Option[A](isDefined: false) -proc `$`[A](o: Option[A]): String = +proc `$`[A](o: Option[A]): string = if o.isDefined: "some($1)" % [$o.value] else: @@ -27,14 +27,14 @@ proc `$`[A](o: Option[A]): String = let x = some("str") let y = some(5) -let z = none[Int]() +let z = none[int]() echo x, ", ", y, ", ", z -proc intOrString[A : Int | String](o: Option[A]): Option[A] = - when A is Int: +proc intOrString[A : int | string](o: Option[A]): Option[A] = + when A is int: some(o.value + 5) - elif A is String: + elif A is string: some(o.value & "!") else: o diff --git a/tests/overflw/toverflw.nim b/tests/overflw/toverflw.nim index cd7b65acf..fbe0d0a38 100644 --- a/tests/overflw/toverflw.nim +++ b/tests/overflw/toverflw.nim @@ -2,7 +2,7 @@ discard """ file: "toverflw.nim" output: "the computation overflowed" """ -# Tests nimrod's ability to detect overflows +# Tests nim's ability to detect overflows {.push overflowChecks: on.} @@ -12,7 +12,7 @@ a = high(int) b = -2 try: writeln(stdout, b - a) -except EOverflow: +except OverflowError: writeln(stdout, "the computation overflowed") {.pop.} # overflow check diff --git a/tests/overflw/toverflw2.nim b/tests/overflw/toverflw2.nim index f7fe3d574..75bd4cdf5 100644 --- a/tests/overflw/toverflw2.nim +++ b/tests/overflw/toverflw2.nim @@ -1,6 +1,6 @@ discard """ file: "toverflw2.nim" - outputsub: "Error: unhandled exception: over- or underflow [EOverflow]" + outputsub: "Error: unhandled exception: over- or underflow [OverflowError]" exitcode: "1" """ var a : int32 = 2147483647 diff --git a/tests/overload/tsystemcmp.nim b/tests/overload/tsystemcmp.nim new file mode 100644 index 000000000..54dff0c46 --- /dev/null +++ b/tests/overload/tsystemcmp.nim @@ -0,0 +1,9 @@ +discard """ + cmd: r"nim c --hints:on $options --threads:on $file" +""" + +import algorithm + +# bug #1657 +var modules = @["hi", "ho", "ha", "huu"] +sort(modules, system.cmp) diff --git a/tests/parallel/nimrod.cfg b/tests/parallel/nim.cfg similarity index 100% rename from tests/parallel/nimrod.cfg rename to tests/parallel/nim.cfg diff --git a/tests/parallel/t5000.nim b/tests/parallel/t5000.nim new file mode 100644 index 000000000..1dd47a61c --- /dev/null +++ b/tests/parallel/t5000.nim @@ -0,0 +1,25 @@ +discard """ + output: '''50005000''' + disabled: "true" +""" + +# XXX this seems to deadlock certain Linux machines + +import threadpool, strutils + +proc foo(x: int): string = $x + +proc main() = + var a = newSeq[int]() + for i in 1..10000: + add(a, i) + + var s = 0 + for i in a: + s += parseInt(^spawn(foo(i))) + echo s + +setMaxPoolSize 2 + +parallel: + spawn main() diff --git a/tests/parallel/tconvexhull.nim b/tests/parallel/tconvexhull.nim new file mode 100644 index 000000000..d7e4f7716 --- /dev/null +++ b/tests/parallel/tconvexhull.nim @@ -0,0 +1,64 @@ +discard """ + output: '''true +true +true +true +true +true''' + +ccodeCheck: "!'deepcopy('" +""" + +# parallel convex hull for Nim bigbreak +# nim c --threads:on -d:release pconvex_hull.nim +import algorithm, sequtils, threadpool + +type Point = tuple[x, y: float] + +proc cmpPoint(a, b: Point): int = + result = cmp(a.x, b.x) + if result == 0: + result = cmp(a.y, b.y) + +template cross[T](o, a, b: T): expr = + (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x) + +template pro(): expr = + while lr1 > 0 and cross(result[lr1 - 1], result[lr1], p[i]) <= 0: + discard result.pop + lr1 -= 1 + result.add(p[i]) + lr1 += 1 + +proc half[T](p: seq[T]; upper: bool): seq[T] = + var i, lr1: int + result = @[] + lr1 = -1 + if upper: + i = 0 + while i <= high(p): + pro() + i += 1 + else: + i = high(p) + while i >= low(p): + pro() + i -= 1 + discard result.pop + +proc convex_hull[T](points: var seq[T], cmp: proc(x, y: T): int {.closure.}) : seq[T] = + if len(points) < 2: return points + points.sort(cmp) + var ul: array[2, FlowVar[seq[T]]] + parallel: + for k in 0..ul.high: + ul[k] = spawn half[T](points, k == 0) + result = concat(^ul[0], ^ul[1]) + +var s = map(toSeq(0..999999), proc(x: int): Point = (float(x div 1000), float(x mod 1000))) +setMaxPoolSize 2 + +#echo convex_hull[Point](s, cmpPoint) +for i in 0..5: + echo convex_hull[Point](s, cmpPoint) == + @[(0.0, 0.0), (999.0, 0.0), (999.0, 999.0), (0.0, 999.0)] diff --git a/tests/parallel/tdeepcopy2.nim b/tests/parallel/tdeepcopy2.nim new file mode 100644 index 000000000..8ffdcc5f2 --- /dev/null +++ b/tests/parallel/tdeepcopy2.nim @@ -0,0 +1,35 @@ +discard """ + output: '''called deepCopy for int +called deepCopy for int +done999 999''' +""" + +import threadpool + + +type + Bar[T] = object + x: T + +proc deepCopy[T](b: ref Bar[T]): ref Bar[T] {.override.} = + result.new + result.x = b.x + when T is int: + echo "called deepCopy for int" + else: + echo "called deepCopy for something else" + +proc foo(b: ref Bar[int]): int = 999 + +# test that the disjoint checker deals with 'a = spawn f(); g = spawn f()': + +proc main = + var dummy: ref Bar[int] + new(dummy) + dummy.x = 44 + #parallel: + let f = spawn foo(dummy) + let b = spawn foo(dummy) + echo "done", ^f, " ", ^b + +main() diff --git a/tests/parallel/tflowvar.nim b/tests/parallel/tflowvar.nim index 77fab14b5..fd3aa326e 100644 --- a/tests/parallel/tflowvar.nim +++ b/tests/parallel/tflowvar.nim @@ -1,11 +1,14 @@ discard """ - output: '''foobarfoobarbazbearbazbear''' - cmd: "nimrod $target --threads:on $options $file" + output: '''foobarfoobar +bazbearbazbear + +1''' + cmd: "nim $target --threads:on $options $file" """ import threadpool -proc computeSomething(a, b: string): string = a & b & a & b +proc computeSomething(a, b: string): string = a & b & a & b & "\n" proc main = let fvA = spawn computeSomething("foo", "bar") @@ -15,3 +18,19 @@ proc main = main() sync() + + +type + TIntSeq = seq[int] + +proc t(): TIntSeq = + result = @[1] + +proc p(): int = + var a: FlowVar[TIntSeq] + parallel: + var aa = spawn t() + a = aa + result = (^a)[0] + +echo p() diff --git a/tests/parallel/tguard1.nim b/tests/parallel/tguard1.nim new file mode 100644 index 000000000..d96e17589 --- /dev/null +++ b/tests/parallel/tguard1.nim @@ -0,0 +1,37 @@ + +when false: + template lock(a, b: ptr TLock; body: stmt) = + if cast[ByteAddress](a) < cast[ByteAddress](b): + pthread_mutex_lock(a) + pthread_mutex_lock(b) + else: + pthread_mutex_lock(b) + pthread_mutex_lock(a) + {.locks: [a, b].}: + try: + body + finally: + pthread_mutex_unlock(a) + pthread_mutex_unlock(b) + +type + ProtectedCounter[T] = object + i {.guard: L.}: T + L: int + +var + c: ProtectedCounter[int] + +c.i = 89 + +template atomicRead(L, x): expr = + {.locks: [L].}: + x + +proc main = + {.locks: [c.L].}: + inc c.i + discard + echo(atomicRead(c.L, c.i)) + +main() diff --git a/tests/parallel/tguard2.nim b/tests/parallel/tguard2.nim new file mode 100644 index 000000000..b69ea3371 --- /dev/null +++ b/tests/parallel/tguard2.nim @@ -0,0 +1,27 @@ +discard """ + errormsg: "unguarded access: c.i" + line: 25 +""" + +type + ProtectedCounter[T] = object + i {.guard: L.}: T + L: int + +var + c: ProtectedCounter[int] + +c.i = 89 + +template atomicRead(L, x): expr = + {.locks: [L].}: + x + +proc main = + {.locks: [c.L].}: + inc c.i + discard + echo(atomicRead(c.L, c.i)) + echo c.i + +main() diff --git a/tests/parallel/tlet_spawn.nim b/tests/parallel/tlet_spawn.nim new file mode 100644 index 000000000..463ee1a47 --- /dev/null +++ b/tests/parallel/tlet_spawn.nim @@ -0,0 +1,14 @@ + +import threadpool + +proc foo(): int = 999 + +# test that the disjoint checker deals with 'a = spawn f(); g = spawn f()': + +proc main = + parallel: + let f = spawn foo() + let b = spawn foo() + echo "done", f, " ", b + +main() diff --git a/tests/parallel/treadafterwrite.nim b/tests/parallel/treadafterwrite.nim new file mode 100644 index 000000000..f59ad5ae0 --- /dev/null +++ b/tests/parallel/treadafterwrite.nim @@ -0,0 +1,31 @@ +discard """ + errormsg: "'foo' not disjoint from 'foo'" + line: 23 + disabled: "true" +""" + +# bug #1597 + +import strutils, math, threadpool + +type + BoxedFloat = object + value: float + +proc term(k: float): ptr BoxedFloat = + var temp = 4 * math.pow(-1, k) / (2*k + 1) + result = cast[ptr BoxedFloat](allocShared(sizeof(BoxedFloat))) + result.value = temp + +proc pi(n: int): float = + var ch = newSeq[ptr BoxedFloat](n+1) + parallel: + for k in 0..ch.high: + let foo = (spawn term(float(k))) + assert foo != nil + for k in 0..ch.high: + var temp = ch[k][].value + result += temp + deallocShared(ch[k]) + +echo formatFloat(pi(5000)) diff --git a/tests/parallel/tsysspawn.nim b/tests/parallel/tsysspawn.nim index fc7921b0e..7244a5ee6 100644 --- a/tests/parallel/tsysspawn.nim +++ b/tests/parallel/tsysspawn.nim @@ -1,7 +1,7 @@ discard """ output: '''4 8''' - cmd: "nimrod $target --threads:on $options $file" + cmd: "nim $target --threads:on $options $file" """ import threadpool diff --git a/tests/parallel/tsysspawnbadarg.nim b/tests/parallel/tsysspawnbadarg.nim index ad798a7d3..2d3ffd241 100644 --- a/tests/parallel/tsysspawnbadarg.nim +++ b/tests/parallel/tsysspawnbadarg.nim @@ -1,7 +1,7 @@ discard """ line: 9 errormsg: "'spawn' takes a call expression" - cmd: "nimrod $target --threads:on $options $file" + cmd: "nim $target --threads:on $options $file" """ import threadpool diff --git a/tests/parallel/tuseafterdef.nim b/tests/parallel/tuseafterdef.nim new file mode 100644 index 000000000..95123e886 --- /dev/null +++ b/tests/parallel/tuseafterdef.nim @@ -0,0 +1,31 @@ +discard """ + errormsg: "(k)..(k) not disjoint from (k)..(k)" + line: 23 +""" + +# bug #1597 + +import strutils, math, threadpool + +type + BoxedFloat = object + value: float + +proc term(k: float): ptr BoxedFloat = + var temp = 4 * math.pow(-1, k) / (2*k + 1) + result = cast[ptr BoxedFloat](allocShared(sizeof(BoxedFloat))) + result.value = temp + +proc pi(n: int): float = + var ch = newSeq[ptr BoxedFloat](n+1) + parallel: + for k in 0..ch.high: + ch[k] = (spawn term(float(k))) + assert ch[k] != nil + for k in 0..ch.high: + var temp = ch[k][].value + result += temp + deallocShared(ch[k]) + + +echo formatFloat(pi(5000)) diff --git a/tests/parser/tprecedence.nim b/tests/parser/tprecedence.nim index 6b1b250a2..d2c6d0b30 100644 --- a/tests/parser/tprecedence.nim +++ b/tests/parser/tprecedence.nim @@ -1,11 +1,15 @@ discard """ - output: "true" + output: '''holla +true''' """ +# Test top level semicolon works properly: +import os; echo "holla" + # Test the new predence rules proc `\+` (x, y: int): int = result = x + y proc `\*` (x, y: int): int = result = x * y -echo 5 \+ 1 \* 9 == 14 +echo 5 \+ 1 \* 9 == 6*9 diff --git a/tests/procvar/tprocvars.nim b/tests/procvar/tprocvars.nim index dc7592526..50d5d29f2 100644 --- a/tests/procvar/tprocvars.nim +++ b/tests/procvar/tprocvars.nim @@ -1,6 +1,6 @@ -proc doSomething(v: Int, x: proc(v:Int):Int): Int = return x(v) -proc doSomething(v: Int, x: proc(v:Int)) = x(v) +proc doSomething(v: int, x: proc(v:int):int): int = return x(v) +proc doSomething(v: int, x: proc(v:int)) = x(v) -echo doSomething(10, proc(v: Int): Int = return v div 2) +echo doSomething(10, proc(v: int): int = return v div 2) diff --git a/tests/range/tsubrange2.nim b/tests/range/tsubrange2.nim index d14111bb9..759d16b9c 100644 --- a/tests/range/tsubrange2.nim +++ b/tests/range/tsubrange2.nim @@ -1,6 +1,6 @@ discard """ file: "tsubrange2.nim" - outputsub: "value out of range: 50 [EOutOfRange]" + outputsub: "value out of range: 50 [RangeError]" exitcode: "1" """ diff --git a/tests/range/tsubrange3.nim b/tests/range/tsubrange3.nim index 9afb5018b..600161cfd 100644 --- a/tests/range/tsubrange3.nim +++ b/tests/range/tsubrange3.nim @@ -1,6 +1,6 @@ discard """ file: "tsubrange.nim" - outputsub: "value out of range: 50 [EOutOfRange]" + outputsub: "value out of range: 50 [RangeError]" exitcode: "1" """ @@ -16,4 +16,3 @@ var r = y #p y - diff --git a/tests/showoff/thtml2.nim b/tests/showoff/thtml2.nim index 8a451ebf1..faeb4e50d 100644 --- a/tests/showoff/thtml2.nim +++ b/tests/showoff/thtml2.nim @@ -1,5 +1,5 @@ discard """ - output: "now look at this
      • Nimrod is quite capable
      " + output: "now look at this
      • Nim is quite capable
      " """ import strutils @@ -32,6 +32,6 @@ html mainPage: title "now look at this" body: ul: - li "Nimrod is quite capable" + li "Nim is quite capable" echo mainPage() diff --git a/tests/stdlib/techo.nim b/tests/stdlib/techo.nim index 0fa4b5fe0..9cef9205f 100644 --- a/tests/stdlib/techo.nim +++ b/tests/stdlib/techo.nim @@ -1,3 +1,3 @@ -# Simplest Nimrod program +# Simplest Nim program echo "Hello, World!" diff --git a/tests/stdlib/tircbot.nim b/tests/stdlib/tircbot.nim index f0417c7ac..b91300762 100644 --- a/tests/stdlib/tircbot.nim +++ b/tests/stdlib/tircbot.nim @@ -191,7 +191,7 @@ type const ircServer = "irc.freenode.net" - joinChans = @["#nimrod"] + joinChans = @["#nim"] botNickname = "NimBot" proc setSeen(d: TDb, s: TSeen) = @@ -271,7 +271,7 @@ proc handleWebMessage(state: PState, line: string) = message.add("-" & $commit["removed"].len & "]: ") message.add(limitCommitMsg(commit["message"].str)) - # Send message to #nimrod. + # Send message to #nim. state.ircClient.privmsg(joinChans[0], message) elif json.hasKey("redisinfo"): assert json["redisinfo"].hasKey("port") @@ -420,7 +420,7 @@ proc handleIrc(irc: PAsyncIRC, event: TIRCEvent, state: PState) = seenNick.msg = msg state.database.setSeen(seenNick) of MNick: - createSeen(PSeenNick, event.nick, "#nimrod") + createSeen(PSeenNick, event.nick, "#nim") seenNick.newNick = event.params[0] state.database.setSeen(seenNick) else: diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim index 5471d347a..1b83aab53 100644 --- a/tests/stdlib/tmarshal.nim +++ b/tests/stdlib/tmarshal.nim @@ -1,10 +1,10 @@ discard """ - output: "" + output: '''{"age": 12, "name": "Cletus"}''' """ import marshal -template testit(x: expr) = echo($$to[type(x)]($$x)) +template testit(x: expr) = discard $$to[type(x)]($$x) var x: array[0..4, array[0..4, string]] = [ ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], @@ -63,3 +63,15 @@ testit(test7) var test6: set[char] = {'A'..'Z', '_'} testit(test6) + +# bug #1352 + +type + Entity = object of RootObj + name: string + + Person = object of Entity + age: int + +var instance1 = Person(name: "Cletus", age: 12) +echo($$instance1) diff --git a/tests/stdlib/tmath2.nim b/tests/stdlib/tmath2.nim index 935b08634..88d96c80a 100644 --- a/tests/stdlib/tmath2.nim +++ b/tests/stdlib/tmath2.nim @@ -58,7 +58,7 @@ proc TestLoops() = break break - while True: + while true: break @@ -73,7 +73,7 @@ proc main() = res: int s: string #write(stdout, mymax(23, 45)) - write(stdout, "Hallo! Wie heißt du? ") + write(stdout, "Hallo! Wie heisst du? ") s = readLine(stdin) # test the case statement case s diff --git a/tests/stdlib/tparsefloat.nim b/tests/stdlib/tparsefloat.nim deleted file mode 100644 index 38ed2db6d..000000000 --- a/tests/stdlib/tparsefloat.nim +++ /dev/null @@ -1,3 +0,0 @@ -import strutils - -echo ParseFloat("5000") / ParseFloat("10") diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim index 6e488bab4..1bc2669c3 100644 --- a/tests/stdlib/tpegs.nim +++ b/tests/stdlib/tpegs.nim @@ -397,7 +397,7 @@ proc esc(c: char, reserved = {'\0'..'\255'}): string = elif c in reserved: result = '\\' & c else: result = $c -proc singleQuoteEsc(c: Char): string = return "'" & esc(c, {'\''}) & "'" +proc singleQuoteEsc(c: char): string = return "'" & esc(c, {'\''}) & "'" proc singleQuoteEsc(str: string): string = result = "'" @@ -421,11 +421,11 @@ proc charSetEscAux(cc: set[char]): string = c1 = c2 inc(c1) -proc CharSetEsc(cc: set[char]): string = +proc charSetEsc(cc: set[char]): string = if card(cc) >= 128+64: - result = "[^" & CharSetEscAux({'\1'..'\xFF'} - cc) & ']' + result = "[^" & charSetEscAux({'\1'..'\xFF'} - cc) & ']' else: - result = '[' & CharSetEscAux(cc) & ']' + result = '[' & charSetEscAux(cc) & ']' proc toStrAux(r: TPeg, res: var string) = case r.kind @@ -522,12 +522,12 @@ proc `$` *(r: TPeg): string {.rtl, extern: "npegsToString".} = type TCaptures* {.final.} = object ## contains the captured substrings. - matches: array[0..maxSubpatterns-1, tuple[first, last: int]] + matches: array[0..MaxSubpatterns-1, tuple[first, last: int]] ml: int origStart: int proc bounds*(c: TCaptures, - i: range[0..maxSubpatterns-1]): tuple[first, last: int] = + i: range[0..MaxSubpatterns-1]): tuple[first, last: int] = ## returns the bounds ``[first..last]`` of the `i`'th capture. result = c.matches[i] @@ -695,7 +695,7 @@ proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {. while start+result < s.len: var x = rawMatch(s, p.sons[0], start+result, c) if x >= 0: - if idx < maxSubpatterns: + if idx < MaxSubpatterns: c.matches[idx] = (start, start+result-1) #else: silently ignore the capture inc(result, x) @@ -739,7 +739,7 @@ proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {. inc(c.ml) result = rawMatch(s, p.sons[0], start, c) if result >= 0: - if idx < maxSubpatterns: + if idx < MaxSubpatterns: c.matches[idx] = (start, start+result-1) #else: silently ignore the capture else: @@ -836,7 +836,7 @@ iterator findAll*(s: string, pattern: TPeg, start = 0): string = while i < s.len: var L = matchLen(s, pattern, matches, i) if L < 0: break - for k in 0..maxSubPatterns-1: + for k in 0..MaxSubPatterns-1: if isNil(matches[k]): break yield matches[k] inc(i, L) @@ -851,7 +851,7 @@ template `=~`*(s: string, pattern: TPeg): expr = ## This calls ``match`` with an implicit declared ``matches`` array that ## can be used in the scope of the ``=~`` call: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}": ## # matches a key=value pair: @@ -866,7 +866,7 @@ template `=~`*(s: string, pattern: TPeg): expr = ## echo("syntax error") ## when not definedInScope(matches): - var matches {.inject.}: array[0..maxSubpatterns-1, string] + var matches {.inject.}: array[0..MaxSubpatterns-1, string] match(s, pattern, matches) # ------------------------- more string handling ------------------------------ @@ -897,17 +897,17 @@ proc replacef*(s: string, sub: TPeg, by: string): string {. ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by` ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") ## ## Results in: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## ## "var1<-keykey; val2<-key2key2" result = "" var i = 0 - var caps: array[0..maxSubpatterns-1, string] + var caps: array[0..MaxSubpatterns-1, string] while i < s.len: var x = matchLen(s, sub, caps, i) if x <= 0: @@ -924,7 +924,7 @@ proc replace*(s: string, sub: TPeg, by = ""): string {. ## in `by`. result = "" var i = 0 - var caps: array[0..maxSubpatterns-1, string] + var caps: array[0..MaxSubpatterns-1, string] while i < s.len: var x = matchLen(s, sub, caps, i) if x <= 0: @@ -942,7 +942,7 @@ proc parallelReplace*(s: string, subs: varargs[ ## applied in parallel. result = "" var i = 0 - var caps: array[0..maxSubpatterns-1, string] + var caps: array[0..MaxSubpatterns-1, string] while i < s.len: block searchSubs: for j in 0..high(subs): @@ -979,13 +979,13 @@ iterator split*(s: string, sep: TPeg): string = ## Substrings are separated by the PEG `sep`. ## Examples: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## for word in split("00232this02939is39an22example111", peg"\d+"): ## writeln(stdout, word) ## ## Results in: ## - ## .. code-block:: nimrod + ## .. code-block:: nim ## "this" ## "is" ## "an" @@ -1055,7 +1055,7 @@ type TPegLexer {.inheritable.} = object ## the lexer object. bufpos: int ## the current position within the buffer buf: cstring ## the buffer itself - LineNumber: int ## the current line number + lineNumber: int ## the current line number lineStart: int ## index of last line start in buffer colOffset: int ## column to add filename: string @@ -1118,38 +1118,38 @@ proc getEscapedChar(c: var TPegLexer, tok: var TToken) = case c.buf[c.bufpos] of 'r', 'R', 'c', 'C': add(tok.literal, '\c') - Inc(c.bufpos) + inc(c.bufpos) of 'l', 'L': add(tok.literal, '\L') - Inc(c.bufpos) + inc(c.bufpos) of 'f', 'F': add(tok.literal, '\f') inc(c.bufpos) of 'e', 'E': add(tok.literal, '\e') - Inc(c.bufpos) + inc(c.bufpos) of 'a', 'A': add(tok.literal, '\a') - Inc(c.bufpos) + inc(c.bufpos) of 'b', 'B': add(tok.literal, '\b') - Inc(c.bufpos) + inc(c.bufpos) of 'v', 'V': add(tok.literal, '\v') - Inc(c.bufpos) + inc(c.bufpos) of 't', 'T': add(tok.literal, '\t') - Inc(c.bufpos) + inc(c.bufpos) of 'x', 'X': inc(c.bufpos) var xi = 0 handleHexChar(c, xi) handleHexChar(c, xi) if xi == 0: tok.kind = tkInvalid - else: add(tok.literal, Chr(xi)) + else: add(tok.literal, chr(xi)) of '0'..'9': var val = ord(c.buf[c.bufpos]) - ord('0') - Inc(c.bufpos) + inc(c.bufpos) var i = 1 while (i <= 3) and (c.buf[c.bufpos] in {'0'..'9'}): val = val * 10 + ord(c.buf[c.bufpos]) - ord('0') @@ -1159,11 +1159,11 @@ proc getEscapedChar(c: var TPegLexer, tok: var TToken) = else: tok.kind = tkInvalid of '\0'..'\31': tok.kind = tkInvalid - elif c.buf[c.bufpos] in strutils.letters: + elif c.buf[c.bufpos] in strutils.Letters: tok.kind = tkInvalid else: add(tok.literal, c.buf[c.bufpos]) - Inc(c.bufpos) + inc(c.bufpos) proc skip(c: var TPegLexer) = var pos = c.bufpos @@ -1171,7 +1171,7 @@ proc skip(c: var TPegLexer) = while true: case buf[pos] of ' ', '\t': - Inc(pos) + inc(pos) of '#': while not (buf[pos] in {'\c', '\L', '\0'}): inc(pos) of '\c': @@ -1203,7 +1203,7 @@ proc getString(c: var TPegLexer, tok: var TToken) = break else: add(tok.literal, buf[pos]) - Inc(pos) + inc(pos) c.bufpos = pos proc getDollar(c: var TPegLexer, tok: var TToken) = @@ -1244,7 +1244,7 @@ proc getCharSet(c: var TPegLexer, tok: var TToken) = break else: ch = buf[pos] - Inc(pos) + inc(pos) incl(tok.charset, ch) if buf[pos] == '-': if buf[pos+1] == ']': @@ -1264,7 +1264,7 @@ proc getCharSet(c: var TPegLexer, tok: var TToken) = break else: ch2 = buf[pos] - Inc(pos) + inc(pos) for i in ord(ch)+1 .. ord(ch2): incl(tok.charset, chr(i)) c.bufpos = pos @@ -1275,7 +1275,7 @@ proc getSymbol(c: var TPegLexer, tok: var TToken) = var buf = c.buf while true: add(tok.literal, buf[pos]) - Inc(pos) + inc(pos) if buf[pos] notin strutils.IdentChars: break c.bufpos = pos tok.kind = tkIdentifier @@ -1312,11 +1312,11 @@ proc getTok(c: var TPegLexer, tok: var TToken) = getCharset(c, tok) of '(': tok.kind = tkParLe - Inc(c.bufpos) + inc(c.bufpos) add(tok.literal, '(') of ')': tok.kind = tkParRi - Inc(c.bufpos) + inc(c.bufpos) add(tok.literal, ')') of '.': tok.kind = tkAny diff --git a/tests/stdlib/tsockets.nim b/tests/stdlib/tsockets.nim deleted file mode 100644 index ff566df74..000000000 --- a/tests/stdlib/tsockets.nim +++ /dev/null @@ -1,12 +0,0 @@ -import sockets, os -var s: TSocket -s = socket() -if s == InvalidSocket: osError(osLastError()) - -s.connect("www.google.com", TPort(80)) - -var data: string = "" -s.readLine(data) -echo(data) - - diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim index 80c2f3870..da65d1f89 100644 --- a/tests/stdlib/tstrutil.nim +++ b/tests/stdlib/tstrutil.nim @@ -38,6 +38,10 @@ assert(editDistance("prefix__hallo_suffix", "prefix__ha_suffix") == 3) assert(editDistance("prefix__hallo_suffix", "prefix") == 14) assert(editDistance("prefix__hallo_suffix", "suffix") == 14) assert(editDistance("prefix__hallo_suffix", "prefix__hao_suffix") == 2) + +assert "/1/2/3".rfind('/') == 4 +assert "/1/2/3".rfind('/', 1) == 0 +assert "/1/2/3".rfind('0') == -1 main() #OUT ha/home/a1xyz/usr/bin diff --git a/tests/stdlib/tunidecode.nim b/tests/stdlib/tunidecode.nim index 647858825..689453c76 100644 --- a/tests/stdlib/tunidecode.nim +++ b/tests/stdlib/tunidecode.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nimrod $target --hints:on -d:embedUnidecodeTable $options $file" + cmd: "nim $target --hints:on -d:embedUnidecodeTable $options $file" output: "Ausserst" """ diff --git a/tests/stdlib/txmlgen.nim b/tests/stdlib/txmlgen.nim index 917427abc..fa1dffe56 100644 --- a/tests/stdlib/txmlgen.nim +++ b/tests/stdlib/txmlgen.nim @@ -1,11 +1,11 @@ discard """ file: "txmlgen.nim" - output: "

      Nimrod

      " + output: "

      Nim

      " """ import htmlgen -var nim = "Nimrod" -echo h1(a(href="http://force7.de/nimrod", nim)) +var nim = "Nim" +echo h1(a(href="http://force7.de/nim", nim)) diff --git a/tests/stdlib/txmltree.nim b/tests/stdlib/txmltree.nim index 931871f15..bfe2dc94a 100644 --- a/tests/stdlib/txmltree.nim +++ b/tests/stdlib/txmltree.nim @@ -5,9 +5,9 @@ discard """ import xmltree, strtabs -var x = <>a(href="nimrod.de", newText("www.nimrod-test.de")) +var x = <>a(href="nim.de", newText("www.nim-test.de")) -echo($x == "www.nimrod-test.de") +echo($x == "www.nim-test.de") diff --git a/tests/table/ttables.nim b/tests/table/ttables.nim index 60446b5a3..de4aaed5e 100644 --- a/tests/table/ttables.nim +++ b/tests/table/ttables.nim @@ -84,7 +84,7 @@ block orderedTableTest1: block countTableTest1: var s = data.toTable var t = initCountTable[string]() - for k in s.Keys: t.inc(k) + for k in s.keys: t.inc(k) for k in t.keys: assert t[k] == 1 t.inc("90", 3) t.inc("12", 2) diff --git a/tests/template/t_otemplates.nim b/tests/template/t_otemplates.nim index 7de728ab2..1a9075d20 100644 --- a/tests/template/t_otemplates.nim +++ b/tests/template/t_otemplates.nim @@ -3,8 +3,8 @@ discard """ """ # Ref: -# http://nimrod-lang.org/macros.html -# http://nimrod-lang.org/parseutils.html +# http://nim-lang.org/macros.html +# http://nim-lang.org/parseutils.html # Imports @@ -313,7 +313,7 @@ proc parse_until_symbol(node: PNimrodNode, value: string, index: var int): bool proc parse_template(node: PNimrodNode, value: string) = ## Parses through entire template, outputing valid - ## Nimrod code into the input `node` AST. + ## Nim code into the input `node` AST. var index = 0 while index < value.len and parse_until_symbol(node, value, index): nil diff --git a/tests/template/tissue993.nim b/tests/template/tissue993.nim index d39f43942..dae9df683 100644 --- a/tests/template/tissue993.nim +++ b/tests/template/tissue993.nim @@ -1,5 +1,5 @@ -type pnode* = ref object of tobject +type PNode* = ref object of RootObj template litNode (name, ty): stmt = type name* = ref object of PNode @@ -8,7 +8,7 @@ litNode PIntNode, int import json -template withKey*(j: PJsonNode; key: string; varname: expr; +template withKey*(j: JsonNode; key: string; varname: expr; body:stmt): stmt {.immediate.} = if j.hasKey(key): let varname{.inject.}= j[key] diff --git a/tests/template/tit.nim b/tests/template/tit.nim new file mode 100644 index 000000000..9866711de --- /dev/null +++ b/tests/template/tit.nim @@ -0,0 +1,11 @@ + +# bug #1337 + +template someIt(a, pred: expr): expr = + var it {.inject.} = 0 + pred + +proc aProc(n) = + n.someIt(echo(it)) + +aProc(89) diff --git a/tests/template/twrongmapit.nim b/tests/template/twrongmapit.nim new file mode 100644 index 000000000..4b3e1553f --- /dev/null +++ b/tests/template/twrongmapit.nim @@ -0,0 +1,32 @@ +discard """ + errormsg: "'" + file: "sequtils.nim" + line: 416 +""" +# unfortunately our tester doesn't support multiple lines of compiler +# error messages yet... + +# bug #1562 +type Foo* {.pure, final.} = object + elt: float + +template defineOpAssign(T: expr, op: expr) {.immediate.} = + proc op*(v: var T, w: T) {.inline.} = + for i in 0..1: + op(v.elt, w.elt) + +const ATTEMPT = 0 + +when ATTEMPT == 0: + # FAILS: defining `/=` with template calling template + # ERROR about sem.nim line 144 + template defineOpAssigns(T: expr) {.immediate.} = + mixin `/=` + defineOpAssign(T, `/=`) + + defineOpAssigns(Foo) + +# bug #1543 +import sequtils + +(var i= @[""];i).mapIt(it) diff --git a/tests/template/twrongopensymchoice.nim b/tests/template/twrongopensymchoice.nim new file mode 100644 index 000000000..5a02a813c --- /dev/null +++ b/tests/template/twrongopensymchoice.nim @@ -0,0 +1,24 @@ +discard """ + output: '''10''' +""" + +# bug #940 + +type + Foo* = ref object + b*: int + +proc new*(this: var Foo) = + assert this != nil + this.b = 10 + +proc new*(T: typedesc[Foo]): Foo = + system.new(result) + twrongopensymchoice.new(result) + +proc main = + var f = Foo.new() + echo f.b + +when isMainModule: + main() diff --git a/tests/testament/backend.nim b/tests/testament/backend.nim index 5199bb9d6..c7122e1b2 100644 --- a/tests/testament/backend.nim +++ b/tests/testament/backend.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Tester +# The Nim Tester # (c) Copyright 2014 Andreas Rumpf # # Look at license.txt for more info. diff --git a/tests/testament/caasdriver.nim b/tests/testament/caasdriver.nim index ddfe88273..8f2eec33b 100644 --- a/tests/testament/caasdriver.nim +++ b/tests/testament/caasdriver.nim @@ -9,7 +9,7 @@ type TRunMode = enum ProcRun, CaasRun, SymbolProcRun - TNimrodSession* = object + NimSession* = object nim: PProcess # Holds the open process for CaasRun sessions, nil otherwise. mode: TRunMode # Stores the type of run mode the session was started with. lastOutput: string # Preserves the last output, needed for ProcRun mode. @@ -26,15 +26,15 @@ const var TesterDir = getAppDir() / ".." - NimrodBin = TesterDir / "../bin/nimrod" + NimBin = TesterDir / "../bin/nim" -proc replaceVars(session: var TNimrodSession, text: string): string = +proc replaceVars(session: var NimSession, text: string): string = result = text.replace(filenameReplaceVar, session.filename) result = result.replace(moduleReplaceVar, session.modname) result = result.replace(silentReplaceVar, silentReplaceText) -proc startNimrodSession(project, script: string, mode: TRunMode): - TNimrodSession = +proc startNimSession(project, script: string, mode: TRunMode): + NimSession = let (dir, name, ext) = project.splitFile result.mode = mode result.lastOutput = "" @@ -50,10 +50,10 @@ proc startNimrodSession(project, script: string, mode: TRunMode): removeDir(nimcacheDir / "nimcache") if mode == CaasRun: - result.nim = startProcess(NimrodBin, workingDir = dir, + result.nim = startProcess(NimBin, workingDir = dir, args = ["serve", "--server.type:stdin", name]) -proc doCaasCommand(session: var TNimrodSession, command: string): string = +proc doCaasCommand(session: var NimSession, command: string): string = assert session.mode == CaasRun session.nim.inputStream.write(session.replaceVars(command) & "\n") session.nim.inputStream.flush @@ -69,11 +69,11 @@ proc doCaasCommand(session: var TNimrodSession, command: string): string = result = "FAILED TO EXECUTE: " & command & "\n" & result break -proc doProcCommand(session: var TNimrodSession, command: string): string = +proc doProcCommand(session: var NimSession, command: string): string = assert session.mode == ProcRun or session.mode == SymbolProcRun except: result = "FAILED TO EXECUTE: " & command & "\n" & result var - process = startProcess(NimrodBin, args = session.replaceVars(command).split) + process = startProcess(NimBin, args = session.replaceVars(command).split) stream = outputStream(process) line = TaintedString("") @@ -84,7 +84,7 @@ proc doProcCommand(session: var TNimrodSession, command: string): string = process.close() -proc doCommand(session: var TNimrodSession, command: string) = +proc doCommand(session: var NimSession, command: string) = if session.mode == CaasRun: if not session.nim.running: session.lastOutput = "FAILED TO EXECUTE: " & command & "\n" & @@ -102,7 +102,7 @@ proc doCommand(session: var TNimrodSession, command: string) = session.lastOutput = doProcCommand(session, command & " " & session.filename) -proc close(session: var TNimrodSession) {.destructor.} = +proc close(session: var NimSession) {.destructor.} = if session.mode == CaasRun: session.nim.close @@ -114,7 +114,7 @@ proc doScenario(script: string, output: PStream, mode: TRunMode, verbose: bool): if f.readLine(project): var - s = startNimrodSession(script.parentDir / project.string, script, mode) + s = startNimSession(script.parentDir / project.string, script, mode) tline = TaintedString("") ln = 1 diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 02f7dc1d7..ae9905cde 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -1,6 +1,6 @@ # # -# Nimrod Tester +# Nim Tester # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -20,7 +20,7 @@ const proc delNimCache() = try: removeDir(nimcacheDir) - except EOS: + except OSError: echo "[Warning] could not delete: ", nimcacheDir proc runRodFiles(r: var TResults, cat: Category, options: string) = @@ -71,7 +71,7 @@ proc compileRodFiles(r: var TResults, cat: Category, options: string) = proc safeCopyFile(src, dest: string) = try: copyFile(src, dest) - except EOS: + except OSError: echo "[Warning] could not copy: ", src, " to ", dest proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = @@ -87,7 +87,7 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = else: # posix relies on crappy LD_LIBRARY_PATH (ugh!): var libpath = getenv"LD_LIBRARY_PATH".string - if peg"\i '/nimrod' (!'/')* '/lib'" notin libpath: + if peg"\i '/nim' (!'/')* '/lib'" notin libpath: echo "[Warning] insufficient LD_LIBRARY_PATH" var serverDll = DynlibFormat % "server" safeCopyFile("tests/dll" / serverDll, "lib" / serverDll) @@ -192,9 +192,9 @@ proc jsTests(r: var TResults, cat: Category, options: string) = # testSpec(r, t, options) proc findMainFile(dir: string): string = - # finds the file belonging to ".nimrod.cfg"; if there is no such file + # finds the file belonging to ".nim.cfg"; if there is no such file # it returns the some ".nim" file if there is only one: - const cfgExt = ".nimrod.cfg" + const cfgExt = ".nim.cfg" result = "" var nimFiles = 0 for kind, file in os.walkDir(dir): @@ -236,8 +236,8 @@ let packageDir = babelDir / "pkgs" packageIndex = babelDir / "packages.json" -proc waitForExitEx(p: PProcess): int = - var outp: PStream = outputStream(p) +proc waitForExitEx(p: Process): int = + var outp = outputStream(p) var line = newStringOfCap(120).TaintedString while true: if outp.readLine(line): @@ -250,7 +250,7 @@ proc waitForExitEx(p: PProcess): int = proc getPackageDir(package: string): string = ## TODO - Replace this with dom's version comparison magic. var commandOutput = execCmdEx("babel path $#" % package) - if commandOutput.exitCode != quitSuccess: + if commandOutput.exitCode != QuitSuccess: return "" else: result = commandOutput[0].string @@ -278,7 +278,7 @@ proc testBabelPackages(r: var TResults, cat: Category, filter: PackageFilter) = echo("[Warning] - Cannot run babel tests: Babel binary not found.") return - if execCmd("$# update" % babelExe) == quitFailure: + if execCmd("$# update" % babelExe) == QuitFailure: echo("[Warning] - Cannot run babel tests: Babel update failed.") return @@ -291,7 +291,7 @@ proc testBabelPackages(r: var TResults, cat: Category, filter: PackageFilter) = installProcess = startProcess(babelExe, "", ["install", "-y", name]) installStatus = waitForExitEx(installProcess) installProcess.close - if installStatus != quitSuccess: + if installStatus != QuitSuccess: r.addResult(test, "", "", reInstallFailed) continue @@ -301,18 +301,18 @@ proc testBabelPackages(r: var TResults, cat: Category, filter: PackageFilter) = buildProcess = startProcess(babelExe, buildPath, ["build"]) buildStatus = waitForExitEx(buildProcess) buildProcess.close - if buildStatus != quitSuccess: + if buildStatus != QuitSuccess: r.addResult(test, "", "", reBuildFailed) r.addResult(test, "", "", reSuccess) r.addResult(packageFileTest, "", "", reSuccess) - except EJsonParsingError: + except JsonParsingError: echo("[Warning] - Cannot run babel tests: Invalid package file.") r.addResult(packageFileTest, "", "", reBuildFailed) # ---------------------------------------------------------------------------- -const AdditionalCategories = ["debugger", "examples", "stdlib", "babel-core"] +const AdditionalCategories = ["debugger", "examples", "lib", "babel-core"] proc `&.?`(a, b: string): string = # candidate for the stdlib? @@ -342,7 +342,7 @@ proc processCategory(r: var TResults, cat: Category, options: string) = threadTests r, cat, options & " --threads:on" of "io": ioTests r, cat, options - of "stdlib": + of "lib": testStdlib(r, "lib/pure/*.nim", options, cat) testStdlib(r, "lib/packages/docutils/highlite", options, cat) of "examples": diff --git a/tests/testament/htmlgen.nim b/tests/testament/htmlgen.nim index b91475aee..b9eda5383 100644 --- a/tests/testament/htmlgen.nim +++ b/tests/testament/htmlgen.nim @@ -1,6 +1,6 @@ # # -# Nimrod Tester +# Nim Tester # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -107,7 +107,7 @@ div.tabContent.hide { display: none; } HtmlEnd = "" proc td(s: string): string = - result = "" & s.substr(0, 200).XMLEncode & "" + result = "" & s.substr(0, 200).xmlEncode & "" proc getCommit(db: TDbConn, c: int): string = var commit = c diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim index 184f07c51..37fe8cfee 100644 --- a/tests/testament/specs.nim +++ b/tests/testament/specs.nim @@ -1,6 +1,6 @@ # # -# Nimrod Tester +# Nim Tester # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this @@ -10,7 +10,7 @@ import parseutils, strutils, os, osproc, streams, parsecfg const - cmdTemplate* = r"nimrod $target --hints:on $options $file" + cmdTemplate* = r"nim $target --hints:on $options $file" type TTestAction* = enum @@ -18,7 +18,7 @@ type actionRun = "run" actionReject = "reject" TResultEnum* = enum - reNimrodcCrash, # nimrod compiler seems to have crashed + reNimcCrash, # nim compiler seems to have crashed reMsgsDiffer, # error messages differ reFilesDiffer, # expected and given filenames differ reLinesDiffer, # expected and given line numbers differ @@ -59,7 +59,7 @@ when not declared(parseCfgBool): case normalize(s) of "y", "yes", "true", "1", "on": result = true of "n", "no", "false", "0", "off": result = false - else: raise newException(EInvalidValue, "cannot interpret as a bool: " & s) + else: raise newException(ValueError, "cannot interpret as a bool: " & s) proc extractSpec(filename: string): string = const tripleQuote = "\"\"\"" @@ -78,7 +78,7 @@ when not defined(nimhygiene): template parseSpecAux(fillResult: stmt) {.immediate.} = var ss = newStringStream(extractSpec(filename)) - var p {.inject.}: TCfgParser + var p {.inject.}: CfgParser open(p, ss, filename, 1) while true: var e {.inject.} = next(p) diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index fc6b4ff95..b74fa99c8 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -1,13 +1,13 @@ # # -# Nimrod Tester +# Nim Tester # (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## This program verifies Nimrod against the testcases. +## This program verifies Nim against the testcases. import parseutils, strutils, pegs, os, osproc, streams, parsecfg, json, @@ -146,9 +146,9 @@ proc codegenCheck(test: TTest, check: string, given: var TSpec) = let contents = readFile(genFile).string if contents.find(check.peg) < 0: given.err = reCodegenFailure - except EInvalidValue: + except ValueError: given.err = reInvalidPeg - except EIO: + except IOError: given.err = reCodeNotFound proc makeDeterministic(s: string): string = @@ -193,7 +193,7 @@ proc testSpec(r: var TResults, test: TTest) = return var (buf, exitCode) = execCmdEx( (if test.target == targetJS: "nodejs " else: "") & exeFile) - if exitCode != expected.ExitCode: + if exitCode != expected.exitCode: r.addResult(test, "exitcode: " & $expected.exitCode, "exitcode: " & $exitCode, reExitCodesDiffer) else: @@ -246,15 +246,15 @@ proc main() = if p.kind == cmdLongoption: case p.key.string.normalize of "print", "verbose": optPrintResults = true - else: quit usage + else: quit Usage p.next() - if p.kind != cmdArgument: quit usage + if p.kind != cmdArgument: quit Usage var action = p.key.string.normalize p.next() var r = initResults() case action of "all": - let testsDir = "tests" & dirSep + let testsDir = "tests" & DirSep for kind, dir in walkDir(testsDir): assert testsDir.startsWith(testsDir) let cat = dir[testsDir.len .. -1] @@ -272,7 +272,7 @@ proc main() = generateHtml(resultsFile, commit) generateJson(jsonFile, commit) else: - quit usage + quit Usage if optPrintResults: if action == "html": openDefaultBrowser(resultsFile) @@ -280,6 +280,6 @@ proc main() = backend.close() if paramCount() == 0: - quit usage + quit Usage main() diff --git a/tests/threads/nimrod.cfg b/tests/threads/nim.cfg similarity index 100% rename from tests/threads/nimrod.cfg rename to tests/threads/nim.cfg diff --git a/tests/threads/tthreadanalysis.nim b/tests/threads/tthreadanalysis.nim index 37369b79c..b222f15a9 100644 --- a/tests/threads/tthreadanalysis.nim +++ b/tests/threads/tthreadanalysis.nim @@ -2,7 +2,7 @@ discard """ outputsub: "101" errormsg: "'threadFunc' is not GC-safe" line: 39 - cmd: "nimrod $target --hints:on --threads:on $options $file" + cmd: "nim $target --hints:on --threads:on $options $file" """ import os diff --git a/tests/threads/tthreadanalysis2.nim b/tests/threads/tthreadanalysis2.nim index bcc09db98..d5b2cd430 100644 --- a/tests/threads/tthreadanalysis2.nim +++ b/tests/threads/tthreadanalysis2.nim @@ -2,7 +2,7 @@ discard """ file: "tthreadanalysis2.nim" line: 37 errormsg: "'threadFunc' is not GC-safe" - cmd: "nimrod $target --hints:on --threads:on $options $file" + cmd: "nim $target --hints:on --threads:on $options $file" """ import os diff --git a/tests/threads/tthreadheapviolation1.nim b/tests/threads/tthreadheapviolation1.nim index e0629ed08..94e1b64db 100644 --- a/tests/threads/tthreadheapviolation1.nim +++ b/tests/threads/tthreadheapviolation1.nim @@ -1,7 +1,7 @@ discard """ line: 11 errormsg: "'horrible' is not GC-safe" - cmd: "nimrod $target --hints:on --threads:on $options $file" + cmd: "nim $target --hints:on --threads:on $options $file" """ var diff --git a/tests/trmacros/targlist.nim b/tests/trmacros/targlist.nim index e416edf0a..321b3d5d2 100644 --- a/tests/trmacros/targlist.nim +++ b/tests/trmacros/targlist.nim @@ -3,7 +3,7 @@ discard """ """ proc f(x: varargs[string, `$`]) = discard -template optF{f(X)}(x: varargs[expr]) = +template optF{f(x)}(x: varargs[expr]) = writeln(stdout, x) f 1, 2, false, 3, "ha" diff --git a/tests/trmacros/tnoalias2.nim b/tests/trmacros/tnoalias2.nim new file mode 100644 index 000000000..5a816acb9 --- /dev/null +++ b/tests/trmacros/tnoalias2.nim @@ -0,0 +1,19 @@ +discard """ + output: '''0''' +""" + +# bug #206 +template optimizeOut{testFunc(a, b)}(a: int, b: int{alias}) : expr = 0 + +proc testFunc(a, b: int): int = result = a + b +var testVar = 1 +echo testFunc(testVar, testVar) + + +template ex{a = b + c}(a : int{noalias}, b, c : int) = + a = b + inc a, b + echo "came here" + +var x = 5 +x = x + x diff --git a/tests/tuples/tanontuples.nim b/tests/tuples/tanontuples.nim index a2babf038..80bd32b7b 100644 --- a/tests/tuples/tanontuples.nim +++ b/tests/tuples/tanontuples.nim @@ -1,5 +1,6 @@ discard """ - output: "61, 125" + output: '''61, 125 +89''' """ proc `^` (a, b: int): int = @@ -13,3 +14,6 @@ m = (n[0] + m[1], m[1] ^ n[1]) echo m[0], ", ", m[1] +var x = (bar: 38) +x = (foo: 89) +echo x[0] diff --git a/tests/typerel/tnoargopenarray.nim b/tests/typerel/tnoargopenarray.nim index 872ec86d2..3e65194ff 100644 --- a/tests/typerel/tnoargopenarray.nim +++ b/tests/typerel/tnoargopenarray.nim @@ -2,6 +2,6 @@ import db_sqlite var db: TDbConn -Exec(db, sql"create table blabla()") +exec(db, sql"create table blabla()") diff --git a/tests/typerel/trectuple.nim b/tests/typerel/trectuple.nim index 7c43ec5ba..334c4a911 100644 --- a/tests/typerel/trectuple.nim +++ b/tests/typerel/trectuple.nim @@ -1,3 +1,8 @@ +discard """ + errormsg: "illegal recursion in type 'TNode'" + line: 8 + disabled: true +""" type PNode = ref TNode diff --git a/tests/typerel/trectuples.nim b/tests/typerel/trectuples.nim index c59cfe880..a74e4859c 100644 --- a/tests/typerel/trectuples.nim +++ b/tests/typerel/trectuples.nim @@ -1,3 +1,7 @@ +discard """ + errormsg: "illegal recursion in type 'Node'" + line: 6 +""" type Node = tuple[left: ref Node] diff --git a/tests/typerel/typalias.nim b/tests/typerel/typalias.nim index 40ff06765..55dcb9fa6 100644 --- a/tests/typerel/typalias.nim +++ b/tests/typerel/typalias.nim @@ -1,7 +1,7 @@ type TMyObj = TYourObj - TYourObj = object of TObject + TYourObj = object of RootObj x, y: int proc init: TYourObj = diff --git a/tests/types/temptyseqs.nim b/tests/types/temptyseqs.nim new file mode 100644 index 000000000..f8d22bdb8 --- /dev/null +++ b/tests/types/temptyseqs.nim @@ -0,0 +1,26 @@ +discard """ + output: "1" +""" + +# bug #1708 +let foo = { + "1" : (bar: @["1"]), + "2" : (baz: @[]) +} + +# bug #871 + +when true: + import os + + type + In_out = tuple[src, dest, options: string] + + let + nil_var: In_out = ("hey"/"there", "something", nil) + #nil_var2 = ("hey"/"there", "something", nil) + +# bug #1721 +const foo2: seq[string] = @[] + +echo foo[0][0][0] diff --git a/tests/types/tforwty2.nim b/tests/types/tforwty2.nim index 5d15e112a..d103314c5 100644 --- a/tests/types/tforwty2.nim +++ b/tests/types/tforwty2.nim @@ -6,7 +6,7 @@ type PSDL_semaphore = ptr TSDL_semaphore TSDL_semaphore {.final.} = object - sem: Pointer #PSem_t; + sem: pointer #PSem_t; when not defined(USE_NAMED_SEMAPHORES): sem_data: int when defined(BROKEN_SEMGETVALUE): @@ -18,5 +18,5 @@ type PSDL_Sem = ptr TSDL_Sem TSDL_Sem = TSDL_Semaphore -proc SDL_CreateSemaphore(initial_value: Int32): PSDL_Sem {. +proc SDL_CreateSemaphore(initial_value: int32): PSDL_Sem {. importc: "SDL_CreateSemaphore".} diff --git a/tests/types/tillegaltyperecursion.nim b/tests/types/tillegaltyperecursion.nim index 114e4d08e..ebdbc1d13 100644 --- a/tests/types/tillegaltyperecursion.nim +++ b/tests/types/tillegaltyperecursion.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nimrod $target --threads:on $options $file" + cmd: "nim $target --threads:on $options $file" errormsg: "illegal recursion in type 'TIRC'" line: 16 """ @@ -62,5 +62,5 @@ proc Connect*(irc: var TIRC, nick: string, host: string, port: int = 6667) = when isMainModule: var irc = initIRC() irc.Connect("AmryBot[Nim]","irc.freenode.net",6667) - irc.sendRaw("JOIN #nimrod") + irc.sendRaw("JOIN #nim") os.Sleep(4000) diff --git a/tests/types/tillrec.nim b/tests/types/tillrec.nim index 1d1ec0622..18757140a 100644 --- a/tests/types/tillrec.nim +++ b/tests/types/tillrec.nim @@ -11,6 +11,6 @@ type kids: seq[TLegal] TIllegal {.final.} = object #ERROR_MSG illegal recursion in type 'TIllegal' - y: Int + y: int x: array[0..3, TIllegal] diff --git a/tests/types/tisop.nim b/tests/types/tisop.nim index 509cc4e95..05c6a1a06 100644 --- a/tests/types/tisop.nim +++ b/tests/types/tisop.nim @@ -1,3 +1,7 @@ +discard """ + disabled: true +""" + import typetraits type @@ -35,7 +39,9 @@ proc p(a, b) = var f: TFoo[m(a.type), b.type] static: assert f.x.type.name == "int" + echo f.y.type.name assert f.y.type.name == "float" + echo f.z.type.name assert f.z.type.name == "float" p(A, f) diff --git a/tests/usingstmt/tusingstatement.nim b/tests/usingstmt/tusingstatement.nim index a33aced4c..b58478d74 100644 --- a/tests/usingstmt/tusingstatement.nim +++ b/tests/usingstmt/tusingstatement.nim @@ -9,7 +9,7 @@ import # This macro mimics the using statement from C# # # It's kept only as a test for the macro system -# Nimrod's destructors offer a mechanism for automatic +# Nim's destructors offer a mechanism for automatic # disposal of resources. # macro autoClose(e: expr): stmt {.immediate.} = diff --git a/tests/varstmt/tlet.nim b/tests/varstmt/tlet.nim index ba355c5d8..138f34433 100644 --- a/tests/varstmt/tlet.nim +++ b/tests/varstmt/tlet.nim @@ -10,7 +10,7 @@ proc main = elif name == "name": echo("Very funny, your name is name.") else: - Echo("Hi, ", name, "!") + echo("Hi, ", name, "!") let (x, y) = ("abc", name) echo y, x diff --git a/tests/vm/tasmparser.nim b/tests/vm/tasmparser.nim new file mode 100644 index 000000000..67313c858 --- /dev/null +++ b/tests/vm/tasmparser.nim @@ -0,0 +1,174 @@ + +# bug #1513 + +import os, parseutils, strutils, ropes, macros + +var + code {.compileTime.} = "" + start {.compileTime.} = 0 + line {.compileTime.} = 1 + cpp {.compileTime.} = "" + token {.compileTime.} = "" + +proc log (msg: string) {.compileTime.} = + echo msg + +proc asmx64 () {.compileTime} = + + #log "code = $1" % code + + const asmx64pre = "{.emit: \"\"\"{x64asm& x= *x64asm_ptr(`asm0`); try {" + const asmx64post = "} catch (Xbyak::Error e) { printf (\"asmx64 error: %s\\n\", e.what ()); }}\"\"\".} " + + const xp = "x." + + const symbolStart = { '_', 'a'..'z', 'A' .. 'Z' } + const symbol = { '0'..'9' } + symbolStart + const eolComment = { ';' } + const endOfLine = { '\l', '\r' } + const leadingWhiteSpace = { ' ' } + + const end_or_comment = endOfLine + eolComment + { '\0' } + + const passthrough_start = { '{', '`' } + const passthrough_end = { '}', '`', '\0' } + + const end_or_symbol_or_comment_or_passthrough = symbolStart + end_or_comment + passthrough_start + + + proc abortAsmParse (err:string) = + discard + + let codeLen = code.len + #let codeEnd = codeLen-1 + cpp.add asmx64pre + + #log "{$1}\n" % [code] + + type asmParseState = enum leading, mnemonic, betweenArguments, arguments, endCmd, skipToEndOfLine + + var state:asmParseState = leading + + proc checkEnd (err:string) = + let ch = code [start] + if int (ch) == 0: + abortAsmParse (err) + + proc get_passthrough () = + inc start + let prev_start = start + let prev_token = token + start += code.parseUntil (token, passthrough_end, start) + checkEnd ("Failed to find passthrough end delimiter from offset $1 for:$2\n$3" % [$prev_start, $(code [prev_start-prev_token.len..prev_start]), token[1..token.len-1]]) + inc start + cpp.add "`" + cpp.add token + cpp.add "`" + + var inparse = true + + proc checkCmdEnd () = + if codeLen == start: + state = endCmd + inparse = false + + while inparse: + checkCmdEnd () + + log ("state=$1 start=$2" % [$state, $start]) + + case state: + of leading: + + echo "b100 ", start + start += code.skipWhile (leadingWhiteSpace, start) + echo "b200 ", start + let ch = code [start] + if ch in endOfLine: + inc (line) + #echo "c100 ", start, ' ', code + start += code.skipWhile (endOfline, start) + #echo "c200 ", start, ' ', code + continue + elif ch in symbolStart: + state = mnemonic + elif ch in eolComment: + state = skipToEndOfLine + elif ch in passthrough_start: + get_passthrough () + echo "d100 ", start + start += code.parseUntil (token, end_or_symbol_or_comment_or_passthrough, start) + echo "d200 ", start + cpp.add token + state = mnemonic + elif int (ch) == 0: + break + else: + abortAsmParse ("after '$3' illegal character at offset $1: $2" % [$start, $(int (ch)), token]) + + of mnemonic: + echo "e100 ", start + start += code.parseWhile (token, symbol, start) + echo "e200 ", start + cpp.add xp + cpp.add token + cpp.add "(" + state = betweenArguments + + of betweenArguments: + let tmp = start + let rcode = code + start += rcode.parseUntil (token, end_or_symbol_or_comment_or_passthrough, tmp) + cpp.add token + + if codeLen <= start: + state = endCmd + continue + + let ch = code [start] + if ch in passthrough_start: + get_passthrough () + continue + if (ch in {'x', 'X'}) and ('0' == code [start-1]): + token = $(code [start]) + cpp.add token + inc start + continue + state = arguments + + of arguments: + if code [start] in end_or_comment: + state = endCmd + continue + start += code.parseWhile (token, symbol, start) + cpp.add xp + cpp.add token + state = betweenArguments + + of endCmd: + cpp.add ");\n" + state = skipToEndOfLine + + of skipToEndOfLine: + echo "a100 ", start + start += code.skipUntil (endOfLine, start) + echo "a200 ", start + start += code.skipWhile (endOfline, start) + echo "a300 ", start + inc line + state = leading + + cpp.add asmx64post + + echo ($cpp) + +macro asmx64x (code_in:expr) : stmt = + code = $code_in + echo ("code.len = $1, code = >>>$2<<<" % [$code.len, code]) + asmx64 () + discard result + +asmx64x """ + mov rax, {m} + ret +""" diff --git a/tests/vm/tcompiletimetable.nim b/tests/vm/tcompiletimetable.nim index f1d3ecd4e..df6ead56f 100644 --- a/tests/vm/tcompiletimetable.nim +++ b/tests/vm/tcompiletimetable.nim @@ -2,18 +2,19 @@ discard """ msg: '''2 3 4:2 - ''' +Got Hi +Got Hey''' """ # bug #404 -import macros, tables +import macros, tables, strtabs var ZOOT{.compileTime.} = initTable[int, int](2) var iii {.compiletime.} = 1 macro zoo:stmt= - zoot[iii] = iii*2 + ZOOT[iii] = iii*2 inc iii echo iii @@ -29,9 +30,7 @@ tupleUnpack # bug #903 -import strtabs - -var x {.compileTime.}: PStringTable +var x {.compileTime.}: StringTableRef macro addStuff(stuff, body: expr): stmt {.immediate.} = result = newNimNode(nnkStmtList) diff --git a/tests/vm/tconsteval.nim b/tests/vm/tconsteval.nim index 16fd8f4b8..96a1bafe8 100644 --- a/tests/vm/tconsteval.nim +++ b/tests/vm/tconsteval.nim @@ -6,7 +6,7 @@ import strutils const HelpText = """ +-----------------------------------------------------------------+ -| Maintenance program for Nimrod | +| Maintenance program for Nim | | Version $1| | (c) 2012 Andreas Rumpf | +-----------------------------------------------------------------+ @@ -19,13 +19,13 @@ Options: --help, -h shows this help and quits Possible Commands: boot [options] bootstraps with given command line options - clean cleans Nimrod project; removes generated files + clean cleans Nim project; removes generated files web generates the website csource [options] builds the C sources for installation zip builds the installation ZIP package inno builds the Inno Setup installer -""" % [NimrodVersion & repeatChar(44-len(NimrodVersion)), +""" % [NimVersion & repeatChar(44-len(NimVersion)), CompileDate, CompileTime] -echo helpText +echo HelpText diff --git a/tests/vm/trgba.nim b/tests/vm/trgba.nim index b1d94702f..22eec4d6e 100644 --- a/tests/vm/trgba.nim +++ b/tests/vm/trgba.nim @@ -6,20 +6,20 @@ discard """ #bug #1009 type - TAggRgba8* = array[4, Byte] + TAggRgba8* = array[4, byte] -template R*(self: TAggRgba8): Byte = self[0] -template G*(self: TAggRgba8): Byte = self[1] -template B*(self: TAggRgba8): Byte = self[2] -template A*(self: TAggRgba8): Byte = self[3] +template R*(self: TAggRgba8): byte = self[0] +template G*(self: TAggRgba8): byte = self[1] +template B*(self: TAggRgba8): byte = self[2] +template A*(self: TAggRgba8): byte = self[3] -template `R=`*(self: TAggRgba8, val: Byte) = +template `R=`*(self: TAggRgba8, val: byte) = self[0] = val -template `G=`*(self: TAggRgba8, val: Byte) = +template `G=`*(self: TAggRgba8, val: byte) = self[1] = val -template `B=`*(self: TAggRgba8, val: Byte) = +template `B=`*(self: TAggRgba8, val: byte) = self[2] = val -template `A=`*(self: TAggRgba8, val: Byte) = +template `A=`*(self: TAggRgba8, val: byte) = self[3] = val proc ABGR* (val: int| int64): TAggRgba8 = diff --git a/tests/vm/triangle_array.nim b/tests/vm/triangle_array.nim new file mode 100644 index 000000000..054c66f22 --- /dev/null +++ b/tests/vm/triangle_array.nim @@ -0,0 +1,17 @@ +discard """ + output: "56" +""" + +# bug #1781 + +proc initCombinations: array[11, array[11, int]] = + result[0] = [1,2,3,4,5,6,7,8,9,10,11] + result[1][1 .. 10] = [12,13,14,15,16,17,18,19,20,21] + result[2][2 .. 10] = [22,23,24,25,26,27,28,29,30] + result[3][3 .. 10] = [31,32,33,34,35,36,37,38] + result[4][4 .. 10] = [39,40,41,42,43,44,45] + result[5][5 .. 10] = [46,47,48,49,50,51] + result[6][6 .. 10] = [52,53,54,55,56] + +const combinations = initCombinations() +echo combinations[6][10] diff --git a/tests/vm/tstringnil.nim b/tests/vm/tstringnil.nim new file mode 100644 index 000000000..61ce60ee5 --- /dev/null +++ b/tests/vm/tstringnil.nim @@ -0,0 +1,50 @@ +# bug #1744 + +import macros + +type + SuiteTest = object + suiteName: string + suiteDesc: string + testName: string + testDesc: string + testBlock: PNimrodNode + +proc buildSuiteContents(suiteName, suiteDesc, suiteBloc: PNimrodNode): tuple[tests: seq[SuiteTest]] {.compileTime.} = + var + tests:seq[SuiteTest] = @[] + + for child in suiteBloc.children(): + case $child[0].ident: + of "test": + + var testObj = SuiteTest() + if suiteName.kind == nnkNilLit: + testObj.suiteName = nil + else: + testObj.suiteName = $suiteName + if suiteDesc.kind == nnkNilLit: + testObj.suiteDesc = nil + else: + testObj.suiteDesc = suiteDesc.strVal + testObj.testName = $child[1] # should not ever be nil + if child[2].kind == nnkNilLit: + testObj.testDesc = nil + else: + testObj.testDesc = child[2].strVal + testObj.testBlock = child[1] + + tests.add(testObj) + + else: + discard + + return (tests: tests) + +macro suite(suiteName, suiteDesc: expr, suiteBloc: stmt): stmt {.immediate.} = + let contents = buildSuiteContents(suiteName, suiteDesc, suiteBloc) + +# Test above +suite basics, "Description of such": + test(t5, nil): + assert false diff --git a/todo.txt b/todo.txt index d8085226d..b2d8b034e 100644 --- a/todo.txt +++ b/todo.txt @@ -1,39 +1,36 @@ -version 0.9.6 -============= +version 0.10 +============ + +- The bitwise 'not' operator will be renamed to 'bnot' to + prevent 'not 4 == 5' from compiling. -> requires 'mixin' annotation for procs! + +- c2nim depends on the compiler +- make nimble part of the distribution +- split idetools into separate tool +- split docgen into separate tool + -- scopes are still broken for generic instantiation! -- implicit deref for parameter matching Concurrency ----------- -- 'deepCopy' needs to be instantiated for - generics *when the type is constructed* -- test 'deepCopy' -- overloading of '='; general lift mechanism - -- the disjoint checker needs to deal with 'a = spawn f(); g = spawn f()' +- test 'deepCopy' for closures - implement 'foo[1..4] = spawn(f[4..7])' -- document the new 'spawn' and 'parallel' statements Low priority: - support for exception propagation? (hard to implement) - the copying of the 'ref Promise' into the thead local storage only happens to work due to the write barrier's implementation -- implement lock levels --> first without the more complex race avoidance +- clean up the C code generator. Full of cruft. Misc ---- -- fix the bug that keeps 'defer' template from working - make '--implicitStatic:on' the default -- fix the tuple unpacking in lambda bug - make tuple unpacking work in a non-var/let context -- special rule for ``[]=`` +- special rule for ``[]=``, items, pairs - built-in 'getImpl' -- type API for macros; make 'spawn' a macro -- markAndSweepGC should expose an API for fibers - prevent 'alloc(TypeWithGCedMemory)' - some table related tests are wrong (memory usage checks) @@ -41,102 +38,47 @@ Misc Bugs ==== -- bug: 'type T = ref T' not recognized as illegal recursion -- bug: type conversions concerning proc types are weird +- VM: Pegs do not work at compile-time +- VM: ptr/ref T cannot work in general +- scopes are still broken for generic instantiation! - compilation of niminst takes way too long. looks like a regression -- docgen: sometimes effects are listed twice -- 'result' is not properly cleaned for NRVO --> use uninit checking instead - blocks can "export" an identifier but the CCG generates {} for them ... version 0.9.x ============= +- implicit deref for parameter matching; but only for x.f(a, b) --> looks like + a nice compromise +- overloading of '=' +- allow simple read accesses to global variables --> difficult to ensure that + no data races happen - pragmas need 'bindSym' support - pragmas need re-work: 'push' is dangerous, 'hasPragma' does not work reliably with user-defined pragmas - memory manager: add a measure of fragmentation - implement 'bits' pragmas - we need a magic thisModule symbol -- provide --nilChecks:on|off - ensure (ref T)(a, b) works as a type conversion and type constructor - optimize 'genericReset'; 'newException' leads to code bloat -- stack-less GC +- The 'do' notation might be trimmed so that its only purpose is to pass + multiple multi line constructs to a macro. version 0.9.X ============= - macros as type pragmas +- implement type API for macros - lazy overloading resolution: * special case ``tyStmt`` -- FFI: - * test: times.format with the FFI - document NimMain and check whether it works for threading -- 'quote' without 'do' doesn't work: parser/grammar issue; could be supported - - -version 0.9.X -============= - -- implement the missing features wrt inheritance -- better support for macros that rewrite procs -- macros need access to types and symbols (partially implemented) -- enforce 'simpleExpr' more often --> doesn't work; tkProc is - part of primary! -- the typeDesc/expr unification is weird and only necessary because of - the ambiguous a[T] construct: It would be easy to support a[expr] for - generics but require a[.typeDesc] if that's required; this would also - allow [.ref T.](x) for a more general type conversion construct; for - templates that would work too: T([.ref int]) - - -Concurrency/Effect system -========================= - -- shared memory heap: ``shared ref`` etc. The only hard part in the GC is to - "stop the world". However, it may be worthwhile to generate explicit - (or implicit) syncGC() calls in loops. Automatic loop injection seems - troublesome, but maybe we can come up with a simple heuristic. (All procs - that `new` shared memory are syncGC() candidates... But then 'new' itself - calls syncGC() so that's pointless.) Hm instead of an heuristic simply - provide a ``syncgc`` pragma to trigger compiler injection --> more general: - an ``injectLoop`` pragma -- 'writes: []' effect; track reads/writes for shared types -- use the effect system for static deadlock prevention and race detection -- ``~`` operator for effects -- introduce 'noaddr' pragma to prevent taking the address of a location; this - is very handy to prevent aliasing of global data - - -version 0.9.XX -============== - -- make 'clamp' a magic for the range stuff -- better type syntax for functions and tuples: tuple(int, int); (int,int)->int - - -Memory safety -============= - -- object branch transitions from low(selector) are unsafe! ---> Needs a - deprecation path -- object branch transitions can't work with the current 'reset'; add a 'reset' - with an additional parameter --> simple: - provide a 'reset(x, TObj(k: nkValue))' instead? why bother? '=' does the - same. -- returning 'var T' is unsafe and needs some static analysis - GC == -- precise stack marking; embrace C++ code generation for that -- marker procs for Boehm GC - hybrid GC -- acyclic vs prunable; introduce GC hints - use big blocks in the allocator -- object pooling support for *hard* realtime systems - provide tool/API to track leaks/object counts - resizing of strings/sequences could take into account the memory that is allocated @@ -147,33 +89,3 @@ CGEN - codegen should use "NIM_CAST" macro and respect aliasing rules for GCC - ``restrict`` pragma + backend support - 'const' objects including case objects - - -Not essential for 1.0.0 -======================= - -- allow implicit forward declarations of procs via a pragma (so that the - wrappers can deactivate it): better solution: introduce the notion of a - 'proc section' that is similar to a type section. -- implement the "snoopResult" pragma; no, make a strutils with string append - semantics instead ... -- implement "closure tuple consists of a single 'ref'" optimization -- new feature: ``distinct T with operations`` -- arglist as a type (iterator chaining); variable length type lists for generics -- implement marker procs for message passing -- implement closures that support nesting of *procs* > 1 -- object constructors: static check for fields if discriminator is known at - compile time -- prove array accesses - - -Optimizations -============= - -- optimize 'if' with a constant condition --> necessary in frontend for better - dead code elimination; also necessary to prevent ``if c > 0: 1 div c`` -- escape analysis for string/seq seems to be easy to do too; - even further write barrier specialization -- inlining of first class functions -- proc specialization in the code gen for write barrier specialization -- VM/optimizer: implement on the fly CSE diff --git a/tools/cmerge.nim b/tools/cmerge.nim index 003b0e555..d2e540481 100644 --- a/tools/cmerge.nim +++ b/tools/cmerge.nim @@ -3,10 +3,10 @@ import os, sets, pegs type - TProcessResult = enum prSkipIncludeDir, prAddIncludeDir + ProcessResult = enum prSkipIncludeDir, prAddIncludeDir -proc process(dir, infile: string, outfile: TFile, - processed: var TSet[string]): TProcessResult = +proc process(dir, infile: string, outfile: File, + processed: var HashSet[string]): ProcessResult = if processed.containsOrIncl(infile): return prSkipIncludeDir let toProcess = dir / infile if not existsFile(toProcess): @@ -24,7 +24,7 @@ proc process(dir, infile: string, outfile: TFile, writeln(outfile, line) proc main(dir, outfile: string) = - var o: TFile + var o: File if open(o, outfile, fmWrite): var processed = initSet[string]() processed.incl(outfile) diff --git a/tools/detect/detect.nim b/tools/detect/detect.nim index 87b682ad5..77efdaacd 100644 --- a/tools/detect/detect.nim +++ b/tools/detect/detect.nim @@ -1,8 +1,8 @@ # Posix detect program # (c) 2010 Andreas Rumpf -# This program produces a C program that produces a Nimrod include file. -# The Nimrod include file lists the values of each POSIX constant. +# This program produces a C program that produces a Nim include file. +# The Nim include file lists the values of each POSIX constant. # This is needed because POSIX is brain-dead: It only cares for C, any other # language is ignored. It would have been easier had they specified the # concrete values of the constants. Sigh. diff --git a/tools/fakedeps.nim b/tools/fakedeps.nim new file mode 100644 index 000000000..80623fafb --- /dev/null +++ b/tools/fakedeps.nim @@ -0,0 +1,18 @@ +import strutils, os, pegs, strtabs, math, threadpool, times + +proc fakeCppDep(x: ptr float) {.importcpp: "fakeCppDep", header: "".} +proc fakeTimeDep() = echo(times.getDateStr()) +proc fakedeps() = + var x = 0.4 + {.emit: "#if 0\n".} + fakeCppDep(addr x) + {.emit: "#endif\n".} + + # this is not true: + if math.sin(x) > 0.6: + spawn(fakeTimeDep()) + +proc main = + fakedeps() +when isMainModule: + main() \ No newline at end of file diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim index 28d92402e..a38f2a88f 100644 --- a/tools/nimgrep.nim +++ b/tools/nimgrep.nim @@ -1,6 +1,6 @@ # # -# Nimrod Grep Utility +# Nim Grep Utility # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this @@ -12,7 +12,7 @@ import const Version = "0.9" - Usage = "nimgrep - Nimrod Grep Utility Version " & version & """ + Usage = "nimgrep - Nim Grep Utility Version " & Version & """ (c) 2012 Andreas Rumpf Usage: @@ -58,7 +58,7 @@ proc ask(msg: string): string = stdout.write(msg) result = stdin.readLine() -proc Confirm: TConfirmEnum = +proc confirm: TConfirmEnum = while true: case normalize(ask(" [a]bort; [y]es, a[l]l, [n]o, non[e]: ")) of "a", "abort": return ceAbort @@ -81,20 +81,20 @@ proc countLines(s: string, first, last: int): int = proc beforePattern(s: string, first: int): int = result = first-1 while result >= 0: - if s[result] in newlines: break + if s[result] in NewLines: break dec(result) inc(result) proc afterPattern(s: string, last: int): int = result = last+1 while result < s.len: - if s[result] in newlines: break + if s[result] in NewLines: break inc(result) dec(result) proc writeColored(s: string) = if useWriteStyled: - terminal.WriteStyled(s, {styleUnderscore, styleBright}) + terminal.writeStyled(s, {styleUnderscore, styleBright}) else: stdout.write(s) @@ -125,12 +125,12 @@ proc processFile(filename: string) = var buffer: string try: buffer = system.readFile(filename) - except EIO: + except IOError: echo "cannot open file: ", filename return if optVerbose in options: stdout.writeln(filename) var pegp: TPeg - var rep: TRegex + var rep: Regex var result: string if optRegex in options: @@ -171,7 +171,7 @@ proc processFile(filename: string) = r = replace(wholeMatch, rep, replacement % matches) if optConfirm in options: highlight(buffer, wholeMatch, r, t, line, showRepl=true) - case Confirm() + case confirm() of ceAbort: quit(0) of ceYes: reallyReplace = true of ceAll: @@ -194,7 +194,7 @@ proc processFile(filename: string) = i = t.last+1 if optReplace in options: result.add(substr(buffer, i)) - var f: TFile + var f: File if open(f, filename, fmWrite): f.write(result) f.close() @@ -234,8 +234,8 @@ proc styleInsensitive(s: string): string = while s[i] != '>' and s[i] != '\0': addx() of '\\': addx() - if s[i] in strutils.digits: - while s[i] in strutils.digits: addx() + if s[i] in strutils.Digits: + while s[i] in strutils.Digits: addx() else: addx() else: addx() @@ -267,7 +267,7 @@ proc checkOptions(subset: TOptions, a, b: string) = for kind, key, val in getopt(): case kind of cmdArgument: - if options.contains(optStdIn): + if options.contains(optStdin): filenames.add(key) elif pattern.len == 0: pattern = key @@ -275,7 +275,7 @@ for kind, key, val in getopt(): replacement = key else: filenames.add(key) - of cmdLongOption, cmdShortOption: + of cmdLongoption, cmdShortOption: case normalize(key) of "find", "f": incl(options, optFind) of "replace", "r": incl(options, optReplace) diff --git a/tools/nimgrep.nimrod.cfg b/tools/nimgrep.nim.cfg similarity index 100% rename from tools/nimgrep.nimrod.cfg rename to tools/nimgrep.nim.cfg diff --git a/tools/niminst/buildbat.tmpl b/tools/niminst/buildbat.tmpl index 9a19fc70b..415574273 100644 --- a/tools/niminst/buildbat.tmpl +++ b/tools/niminst/buildbat.tmpl @@ -20,6 +20,7 @@ REM call the compiler: ECHO %CC% %COMP_FLAGS% -Ic_code -c ?{f} -o ?{changeFileExt(f, "o")} %CC% %COMP_FLAGS% -Ic_code -c ?{f} -o ?{changeFileExt(f, "o")} # linkCmd.add(" " & changeFileExt(f, "o")) +IF ERRORLEVEL 1 (GOTO:END) # end for ECHO %LINKER% -o ?{"%BIN_DIR%"\toLower(c.name)}.exe ?linkCmd %LINK_FLAGS% @@ -27,4 +28,14 @@ ECHO %LINKER% -o ?{"%BIN_DIR%"\toLower(c.name)}.exe ?linkCmd %LINK_FLAGS% # end block -ECHO SUCCESS +:END +IF ERRORLEVEL 1 ( + ECHO FAILURE + ECHO. + ECHO CSource compilation failed. Please check that the gcc compiler is in + ECHO the PATH environment variable, and that you are calling the batch script + ECHO that matches the target architecture of the compiler. +) ELSE ( + ECHO SUCCESS +) +exit /b %ERRORLEVEL% diff --git a/tools/niminst/debcreation.nim b/tools/niminst/debcreation.nim index 1f4a8630e..574f7ea6b 100644 --- a/tools/niminst/debcreation.nim +++ b/tools/niminst/debcreation.nim @@ -1,6 +1,6 @@ # # -# The Nimrod Installation Generator +# The Nim Installation Generator # (c) Copyright 2012 Dominik Picheta # # See the file "copying.txt", included in this @@ -104,7 +104,7 @@ proc createChangelog(pkgName, version, maintainer: string): string = formatDateTime(getGMTime(getTime()), "+0000")) proc createRules(): string = - ## Creates a nimrod application-agnostic rules file for building deb packages. + ## Creates a nim application-agnostic rules file for building deb packages. ## Please note: this assumes the c sources have been built and the ## ``build.sh`` and ``install.sh`` files are available. result = "" @@ -131,9 +131,9 @@ proc createDotInstall(pkgName: string, binaries, config, docs, for c in config: addN(pkgName / c & " " & "etc/") for d in docs: - addN(pkgName / d & " " & "usr/share/doc/nimrod/") + addN(pkgName / d & " " & "usr/share/doc/nim/") for l1 in lib: - addN(pkgName / l1 & " " & "usr/lib/nimrod") + addN(pkgName / l1 & " " & "usr/lib/nim") proc makeMtn(name, email: string): string = return name & " <" & email & ">" @@ -145,7 +145,7 @@ proc prepDeb*(packName, version, mtnName, mtnEmail, shortDesc, desc: string, licenses: seq[tuple[files, license: string]], binaries, config, docs, lib: seq[string], buildDepends, pkgDepends = "") = - ## binaries/config/docs/lib: files relative to nimrod's root, that need to + ## binaries/config/docs/lib: files relative to nim's root, that need to ## be installed. let pkgName = packName.toLower() @@ -212,24 +212,24 @@ proc prepDeb*(packName, version, mtnName, mtnEmail, shortDesc, desc: string, echo("And execute `debuild -us -uc` to build the .deb") when isMainModule: - #var controlFile = createControl("nimrod", "Dominik Picheta ", - # "The Nimrod compiler", "Compiler for the Nimrod programming language", "gcc (>= 4:4.3.2)", "gcc (>= 4:4.3.2)") + #var controlFile = createControl("nim", "Dominik Picheta ", + # "The Nim compiler", "Compiler for the Nim programming language", "gcc (>= 4:4.3.2)", "gcc (>= 4:4.3.2)") #echo(controlFile) - #var copyrightFile = createCopyright("nimrod", "Dominik Picheta", "morfeusz8@a.b", "0.8.14", - # @[("bin/nimrod", "gpl2"), ("lib/*", "lgpl")]) + #var copyrightFile = createCopyright("nim", "Dominik Picheta", "morfeusz8@a.b", "0.8.14", + # @[("bin/nim", "gpl2"), ("lib/*", "lgpl")]) #echo copyrightFile - #var changelogFile = createChangelog("nimrod", "0.8.14", "Dom P ") + #var changelogFile = createChangelog("nim", "0.8.14", "Dom P ") #echo(changelogFile) #echo(createRules()) - prepDeb("nimrod", "0.9.2", "Dominik Picheta", "morfeusz8@gmail.com", - "The Nimrod compiler", "Compiler for the Nimrod programming language", - @[("bin/nimrod", "MIT"), ("lib/*", "MIT")], - @["bin/nimrod"], @["config/*"], @["doc/*"], @["lib/*"], + prepDeb("nim", "0.9.2", "Dominik Picheta", "morfeusz8@gmail.com", + "The Nim compiler", "Compiler for the Nim programming language", + @[("bin/nim", "MIT"), ("lib/*", "MIT")], + @["bin/nim"], @["config/*"], @["doc/*"], @["lib/*"], "gcc (>= 4:4.3.2)", "gcc (>= 4:4.3.2)") diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim index 53de52f0a..9ee3eb9b9 100644 --- a/tools/niminst/niminst.nim +++ b/tools/niminst/niminst.nim @@ -1,7 +1,7 @@ # # -# The Nimrod Installation Generator -# (c) Copyright 2013 Andreas Rumpf +# The Nim Installation Generator +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -61,7 +61,7 @@ type explicitPlatforms: bool vars: PStringTable app: TAppType - nimrodArgs: string + nimArgs: string debOpts: TDebOptions const @@ -88,7 +88,7 @@ proc initConfigData(c: var TConfigData) = c.license = "" c.infile = "" c.outdir = "" - c.nimrodArgs = "" + c.nimArgs = "" c.libpath = "" c.innoSetupFlag = false c.installScript = false @@ -130,7 +130,7 @@ include "deinstall.tmpl" const Version = "1.0" - Usage = "niminst - Nimrod Installation Generator Version " & Version & """ + Usage = "niminst - Nim Installation Generator Version " & Version & """ (c) 2014 Andreas Rumpf Usage: @@ -148,7 +148,7 @@ Options: -h, --help shows this help -v, --version shows the version Compile_options: - will be passed to the Nimrod compiler + will be passed to the Nim compiler """ proc parseCmdLine(c: var TConfigData) = @@ -172,7 +172,7 @@ proc parseCmdLine(c: var TConfigData) = else: quit(Usage) else: c.infile = addFileExt(key.string, "ini") - c.nimrodArgs = cmdLineRest(p).string + c.nimArgs = cmdLineRest(p).string break of cmdLongoption, cmdShortOption: case normalize(key.string) @@ -459,14 +459,14 @@ proc srcdist(c: var TConfigData) = var dir = getOutputDir(c) / buildDir(osA, cpuA) if existsDir(dir): removeDir(dir) createDir(dir) - var cmd = ("nimrod compile -f --symbolfiles:off --compileonly " & + var cmd = ("nim compile -f --symbolfiles:off --compileonly " & "--gen_mapping --cc:gcc --skipUserCfg" & " --os:$# --cpu:$# $# $#") % - [osname, cpuname, c.nimrodArgs, + [osname, cpuname, c.nimArgs, changeFileExt(c.infile, "nim")] echo(cmd) if execShellCmd(cmd) != 0: - quit("Error: call to nimrod compiler failed") + quit("Error: call to nim compiler failed") readCFiles(c, osA, cpuA) for i in 0 .. c.cfiles[osA][cpuA].len-1: let dest = dir / extractFilename(c.cfiles[osA][cpuA][i]) diff --git a/tools/nimrepl.nim b/tools/nimrepl.nim index 4b6379bd5..0c9f94616 100644 --- a/tools/nimrepl.nim +++ b/tools/nimrepl.nim @@ -1,6 +1,6 @@ # # -# Nimrod REPL +# Nim REPL # (c) Copyright 2012 Dominik Picheta # # See the file "copying.txt", included in this @@ -14,8 +14,8 @@ when defined(tinyc): else: const runCmd = "c -r" -var nimExe = findExe("nimrod") -if nimExe.len == 0: nimExe = "../bin" / addFileExt("nimrod", os.exeExt) +var nimExe = findExe("nim") +if nimExe.len == 0: nimExe = "../bin" / addFileExt("nim", os.exeExt) proc execCode(code: string): string = var f: TFile @@ -36,7 +36,7 @@ var OutputTextBuffer: PTextBuffer proc destroy(widget: PWidget, data: pgpointer){.cdecl.} = main_quit() -proc FileOpenClicked(menuitem: PMenuItem, userdata: pgpointer) {.cdecl.} = +proc fileOpenClicked(menuitem: PMenuItem, userdata: pgpointer) {.cdecl.} = var path = ChooseFileToOpen(w) if path != "": var file = readFile(path) @@ -45,7 +45,7 @@ proc FileOpenClicked(menuitem: PMenuItem, userdata: pgpointer) {.cdecl.} = else: error(w, "Unable to read from file") -proc FileSaveClicked(menuitem: PMenuItem, userdata: pgpointer) {.cdecl.} = +proc fileSaveClicked(menuitem: PMenuItem, userdata: pgpointer) {.cdecl.} = var path = ChooseFileToSave(w) if path == "": return @@ -115,12 +115,12 @@ proc initControls() = append(FileMenu, OpenMenuItem) show(OpenMenuItem) discard signal_connect(OpenMenuItem, "activate", - SIGNAL_FUNC(FileOpenClicked), nil) + SIGNAL_FUNC(fileOpenClicked), nil) var SaveMenuItem = menu_item_new("Save...") append(FileMenu, SaveMenuItem) show(SaveMenuItem) discard signal_connect(SaveMenuItem, "activate", - SIGNAL_FUNC(FileSaveClicked), nil) + SIGNAL_FUNC(fileSaveClicked), nil) var FileMenuItem = menu_item_new("File") diff --git a/tools/nimweb.nim b/tools/nimweb.nim index 5233bac45..bbd3776d7 100644 --- a/tools/nimweb.nim +++ b/tools/nimweb.nim @@ -1,7 +1,7 @@ # # -# Nimrod Website Generator -# (c) Copyright 2012 Andreas Rumpf +# Nim Website Generator +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -13,17 +13,22 @@ import type TKeyValPair = tuple[key, id, val: string] - TConfigData = object of TObject + TConfigData = object of RootObj tabs, links: seq[TKeyValPair] doc, srcdoc, srcdoc2, webdoc, pdf: seq[string] authors, projectName, projectTitle, logo, infile, outdir, ticker: string - vars: PStringTable - nimrodArgs: string + vars: StringTableRef + nimArgs: string + gitRepo: string gitCommit: string - quotations: TTable[string, tuple[quote, author: string]] + quotations: Table[string, tuple[quote, author: string]] numProcessors: int # Set by parallelBuild:n, only works for values > 0. TRssItem = object year, month, day, title: string + TAction = enum + actAll, actOnlyWebsite, actPdf + +var action: TAction proc initConfigData(c: var TConfigData) = c.tabs = @[] @@ -35,13 +40,14 @@ proc initConfigData(c: var TConfigData) = c.pdf = @[] c.infile = "" c.outdir = "" - c.nimrodArgs = "--hint[Conf]:off " + c.nimArgs = "--hint[Conf]:off --hint[Path]:off --hint[Processing]:off " c.authors = "" c.projectTitle = "" c.projectName = "" c.logo = "" c.ticker = "" c.vars = newStringTable(modeStyleInsensitive) + c.gitRepo = "https://github.com/Araq/Nimrod/tree" c.gitCommit = "master" c.numProcessors = countProcessors() # Attempts to obtain the git current commit. @@ -56,10 +62,10 @@ include "website.tmpl" # ------------------------- configuration file ------------------------------- const - Version = "0.7" - Usage = "nimweb - Nimrod Website Generator Version " & version & """ + version = "0.7" + usage = "nimweb - Nim Website Generator Version " & version & """ - (c) 2012 Andreas Rumpf + (c) 2014 Andreas Rumpf Usage: nimweb [options] ini-file[.ini] [compile_options] Options: @@ -67,13 +73,15 @@ Options: --var:name=value set the value of a variable -h, --help shows this help -v, --version shows the version + --website only build the website, not the full documentation + --pdf build the PDF version of the documentation Compile_options: - will be passed to the Nimrod compiler + will be passed to the Nim compiler """ rYearMonthDayTitle = r"(\d{4})-(\d{2})-(\d{2})\s+(.*)" - rssUrl = "http://nimrod-code.org/news.xml" - rssNewsUrl = "http://nimrod-code.org/news.html" + rssUrl = "http://nim-lang.org/news.xml" + rssNewsUrl = "http://nim-lang.org/news.html" validAnchorCharacters = Letters + Digits @@ -113,30 +121,32 @@ proc parseCmdLine(c: var TConfigData) = case kind of cmdArgument: c.infile = addFileExt(key, "ini") - c.nimrodArgs.add(cmdLineRest(p)) + c.nimArgs.add(cmdLineRest(p)) break of cmdLongOption, cmdShortOption: case normalize(key) of "help", "h": - stdout.write(Usage) + stdout.write(usage) quit(0) of "version", "v": - stdout.write(Version & "\n") + stdout.write(version & "\n") quit(0) of "o", "output": c.outdir = val of "parallelbuild": try: let num = parseInt(val) if num != 0: c.numProcessors = num - except EInvalidValue: + except ValueError: quit("invalid numeric value for --parallelBuild") of "var": var idx = val.find('=') if idx < 0: quit("invalid command line") c.vars[substr(val, 0, idx-1)] = substr(val, idx+1) - else: quit(Usage) + of "website": action = actOnlyWebsite + of "pdf": action = actPdf + else: quit(usage) of cmdEnd: break - if c.infile.len == 0: quit(Usage) + if c.infile.len == 0: quit(usage) proc walkDirRecursively(s: var seq[string], root, ext: string) = for k, f in walkDir(root): @@ -156,68 +166,65 @@ proc addFiles(s: var seq[string], dir, ext: string, patterns: seq[string]) = proc parseIniFile(c: var TConfigData) = var - p: TCfgParser + p: CfgParser section: string # current section var input = newFileStream(c.infile, fmRead) - if input != nil: - open(p, input, c.infile) - while true: - var k = next(p) - case k.kind - of cfgEof: break - of cfgSectionStart: - section = normalize(k.section) - case section - of "project", "links", "tabs", "ticker", "documentation", "var": discard - else: echo("[Warning] Skipping unknown section: " & section) + if input == nil: quit("cannot open: " & c.infile) + open(p, input, c.infile) + while true: + var k = next(p) + case k.kind + of cfgEof: break + of cfgSectionStart: + section = normalize(k.section) + case section + of "project", "links", "tabs", "ticker", "documentation", "var": discard + else: echo("[Warning] Skipping unknown section: " & section) - of cfgKeyValuePair: - var v = k.value % c.vars - c.vars[k.key] = v + of cfgKeyValuePair: + var v = k.value % c.vars + c.vars[k.key] = v - case section - of "project": - case normalize(k.key) - of "name": c.projectName = v - of "title": c.projectTitle = v - of "logo": c.logo = v - of "authors": c.authors = v - else: quit(errorStr(p, "unknown variable: " & k.key)) - of "var": discard - of "links": - let valID = v.split(';') - add(c.links, (k.key.replace('_', ' '), valID[1], valID[0])) - of "tabs": add(c.tabs, (k.key, "", v)) - of "ticker": c.ticker = v - of "documentation": - case normalize(k.key) - of "doc": addFiles(c.doc, "doc", ".txt", split(v, {';'})) - of "pdf": addFiles(c.pdf, "doc", ".txt", split(v, {';'})) - of "srcdoc": addFiles(c.srcdoc, "lib", ".nim", split(v, {';'})) - of "srcdoc2": addFiles(c.srcdoc2, "lib", ".nim", split(v, {';'})) - of "webdoc": addFiles(c.webdoc, "lib", ".nim", split(v, {';'})) - of "parallelbuild": - try: - let num = parseInt(v) - if num != 0: c.numProcessors = num - except EInvalidValue: - quit("invalid numeric value for --parallelBuild in config") - else: quit(errorStr(p, "unknown variable: " & k.key)) - of "quotations": - let vSplit = v.split('-') - doAssert vSplit.len == 2 - c.quotations[k.key.normalize] = (vSplit[0], vSplit[1]) - else: discard - - of cfgOption: quit(errorStr(p, "syntax error")) - of cfgError: quit(errorStr(p, k.msg)) - close(p) - if c.projectName.len == 0: - c.projectName = changeFileExt(extractFilename(c.infile), "") - if c.outdir.len == 0: - c.outdir = splitFile(c.infile).dir - else: - quit("cannot open: " & c.infile) + case section + of "project": + case normalize(k.key) + of "name": c.projectName = v + of "title": c.projectTitle = v + of "logo": c.logo = v + of "authors": c.authors = v + else: quit(errorStr(p, "unknown variable: " & k.key)) + of "var": discard + of "links": + let valID = v.split(';') + add(c.links, (k.key.replace('_', ' '), valID[1], valID[0])) + of "tabs": add(c.tabs, (k.key, "", v)) + of "ticker": c.ticker = v + of "documentation": + case normalize(k.key) + of "doc": addFiles(c.doc, "doc", ".txt", split(v, {';'})) + of "pdf": addFiles(c.pdf, "doc", ".txt", split(v, {';'})) + of "srcdoc": addFiles(c.srcdoc, "lib", ".nim", split(v, {';'})) + of "srcdoc2": addFiles(c.srcdoc2, "lib", ".nim", split(v, {';'})) + of "webdoc": addFiles(c.webdoc, "lib", ".nim", split(v, {';'})) + of "parallelbuild": + try: + let num = parseInt(v) + if num != 0: c.numProcessors = num + except ValueError: + quit("invalid numeric value for --parallelBuild in config") + else: quit(errorStr(p, "unknown variable: " & k.key)) + of "quotations": + let vSplit = v.split('-') + doAssert vSplit.len == 2 + c.quotations[k.key.normalize] = (vSplit[0], vSplit[1]) + else: discard + of cfgOption: quit(errorStr(p, "syntax error")) + of cfgError: quit(errorStr(p, k.msg)) + close(p) + if c.projectName.len == 0: + c.projectName = changeFileExt(extractFilename(c.infile), "") + if c.outdir.len == 0: + c.outdir = splitFile(c.infile).dir # Ugly hack to override git command output when building private repo. if c.vars.hasKey("githash"): let githash = c.vars["githash"].strip @@ -239,7 +246,6 @@ proc mexec(cmds: openarray[string], processors: int) = if processors < 2: sexec(cmds) return - if execProcesses(cmds, {poStdErrToStdOut, poParentStreams, poEchoCmd}) != 0: echo "external program failed, retrying serial work queue for logs!" sexec(cmds) @@ -251,10 +257,10 @@ proc buildDocSamples(c: var TConfigData, destPath: string) = ## it didn't make much sense to integrate into the existing generic ## documentation builders. const src = "doc"/"docgen_sample.nim" - exec("nimrod doc $# -o:$# $#" % - [c.nimrodArgs, destPath / "docgen_sample.html", src]) - exec("nimrod doc2 $# -o:$# $#" % - [c.nimrodArgs, destPath / "docgen_sample2.html", src]) + exec("nim doc $# -o:$# $#" % + [c.nimArgs, destPath / "docgen_sample.html", src]) + exec("nim doc2 $# -o:$# $#" % + [c.nimArgs, destPath / "docgen_sample2.html", src]) proc buildDoc(c: var TConfigData, destPath: string) = # call nim for the documentation: @@ -262,30 +268,30 @@ proc buildDoc(c: var TConfigData, destPath: string) = commands = newSeq[string](len(c.doc) + len(c.srcdoc) + len(c.srcdoc2)) i = 0 for d in items(c.doc): - commands[i] = "nimrod rst2html $# --docSeeSrcUrl:$# -o:$# --index:on $#" % - [c.nimrodArgs, c.gitCommit, + commands[i] = "nim rst2html $# --docSeeSrcUrl:$#/$# -o:$# --index:on $#" % + [c.nimArgs, c.gitRepo, c.gitCommit, destPath / changeFileExt(splitFile(d).name, "html"), d] i.inc for d in items(c.srcdoc): - commands[i] = "nimrod doc $# --docSeeSrcUrl:$# -o:$# --index:on $#" % - [c.nimrodArgs, c.gitCommit, + commands[i] = "nim doc $# --docSeeSrcUrl:$#/$# -o:$# --index:on $#" % + [c.nimArgs, c.gitRepo, c.gitCommit, destPath / changeFileExt(splitFile(d).name, "html"), d] i.inc for d in items(c.srcdoc2): - commands[i] = "nimrod doc2 $# --docSeeSrcUrl:$# -o:$# --index:on $#" % - [c.nimrodArgs, c.gitCommit, + commands[i] = "nim doc2 $# --docSeeSrcUrl:$#/$# -o:$# --index:on $#" % + [c.nimArgs, c.gitRepo, c.gitCommit, destPath / changeFileExt(splitFile(d).name, "html"), d] i.inc mexec(commands, c.numProcessors) - exec("nimrod buildIndex -o:$1/theindex.html $1" % [destPath]) + exec("nim buildIndex -o:$1/theindex.html $1" % [destPath]) proc buildPdfDoc(c: var TConfigData, destPath: string) = if os.execShellCmd("pdflatex -version") != 0: echo "pdflatex not found; no PDF documentation generated" else: for d in items(c.pdf): - exec("nimrod rst2tex $# $#" % [c.nimrodArgs, d]) + exec("nim rst2tex $# $#" % [c.nimArgs, d]) # call LaTeX twice to get cross references right: exec("pdflatex " & changeFileExt(d, "tex")) exec("pdflatex " & changeFileExt(d, "tex")) @@ -305,8 +311,8 @@ proc buildAddDoc(c: var TConfigData, destPath: string) = # build additional documentation (without the index): var commands = newSeq[string](c.webdoc.len) for i, doc in pairs(c.webdoc): - commands[i] = "nimrod doc $# --docSeeSrcUrl:$# -o:$# $#" % - [c.nimrodArgs, c.gitCommit, + commands[i] = "nim doc $# --docSeeSrcUrl:$#/$# -o:$# $#" % + [c.nimArgs, c.gitRepo, c.gitCommit, destPath / changeFileExt(splitFile(doc).name, "html"), doc] mexec(commands, c.numProcessors) @@ -314,7 +320,7 @@ proc parseNewsTitles(inputFilename: string): seq[TRssItem] = # parses the file for titles and returns them as TRssItem blocks. let reYearMonthDayTitle = re(rYearMonthDayTitle) var - input: TFile + input: File line = "" result = @[] @@ -350,7 +356,7 @@ proc genNewsLink(title: string): string = proc generateRss(outputFilename: string, news: seq[TRssItem]) = # Given a list of rss items generates an rss overwriting destination. var - output: TFile + output: File if not open(output, outputFilename, mode = fmWrite): quit("Could not write to $1 for rss generation" % [outputFilename]) @@ -359,7 +365,7 @@ proc generateRss(outputFilename: string, news: seq[TRssItem]) = output.write(""" """) - output.write(title("Nimrod website news")) + output.write(title("Nim website news")) output.write(link(href = rssUrl, rel = "self")) output.write(link(href = rssNewsUrl)) output.write(id(rssNewsUrl)) @@ -375,7 +381,7 @@ proc generateRss(outputFilename: string, news: seq[TRssItem]) = link(`type` = "text/html", rel = "alternate", href = genNewsLink(joinedTitle)), updatedDate(rss.year, rss.month, rss.day), - "Nimrod", + "Nim", content(joinedTitle, `type` = "text"), )) @@ -390,28 +396,29 @@ proc buildNewsRss(c: var TConfigData, destPath: string) = generateRss(destFilename, parseNewsTitles(srcFilename)) proc buildJS(destPath: string) = - exec("nimrod js -d:release --out:$1 web/babelpkglist.nim" % - [destPath / "babelpkglist.js"]) + exec("nim js -d:release --out:$1 web/nimblepkglist.nim" % + [destPath / "nimblepkglist.js"]) -proc main(c: var TConfigData) = +proc buildWebsite(c: var TConfigData) = const - cmd = "nimrod rst2html --compileonly $1 -o:web/$2.temp web/$2.txt" + cmd = "nim rst2html --compileonly $1 -o:web/$2.temp web/$2.txt" if c.ticker.len > 0: try: c.ticker = readFile("web" / c.ticker) - except EIO: + except IOError: quit("[Error] cannot open: " & c.ticker) for i in 0..c.tabs.len-1: var file = c.tabs[i].val let rss = if file in ["news", "index"]: extractFilename(rssUrl) else: "" - exec(cmd % [c.nimrodArgs, file]) + if '.' in file: continue + exec(cmd % [c.nimArgs, file]) var temp = "web" / changeFileExt(file, "temp") var content: string try: content = readFile(temp) - except EIO: + except IOError: quit("[Error] cannot open: " & temp) - var f: TFile + var f: File var outfile = "web/upload/$#.html" % file if not existsDir("web/upload"): createDir("web/upload") @@ -422,20 +429,22 @@ proc main(c: var TConfigData) = quit("[Error] cannot write file: " & outfile) removeFile(temp) copyDir("web/assets", "web/upload/assets") - buildJS("web/upload") buildNewsRss(c, "web/upload") + +proc main(c: var TConfigData) = + buildWebsite(c) + buildJS("web/upload") buildAddDoc(c, "web/upload") buildDocSamples(c, "web/upload") buildDoc(c, "web/upload") buildDocSamples(c, "doc") buildDoc(c, "doc") - buildPdfDoc(c, "doc") var c: TConfigData initConfigData(c) parseCmdLine(c) parseIniFile(c) -when false: - buildPdfDoc(c, "doc") -else: - main(c) +case action +of actOnlyWebsite: buildWebsite(c) +of actPdf: buildPdfDoc(c, "doc") +of actAll: main(c) diff --git a/tools/restorecc.nim b/tools/restorecc.nim new file mode 100644 index 000000000..f5d8f0bb3 --- /dev/null +++ b/tools/restorecc.nim @@ -0,0 +1,16 @@ + +import os, strutils + +proc main(dir: string, wanted: string) = + for kind, path in walkDir(dir): + case kind + of pcFile: + let name = extractFilename(path) + if name == wanted: + let newLoc = path.replace("mingw_backup", "mingw") + echo "creating ", newLoc + copyFile(path, newLoc) + of pcDir: main(path, wanted) + else: discard + +main("dist/mingw_backup", paramStr(1)) diff --git a/tools/trimcc.nim b/tools/trimcc.nim index 4d9686195..276ea1dbe 100644 --- a/tools/trimcc.nim +++ b/tools/trimcc.nim @@ -1,29 +1,174 @@ -# Trim C compiler installation to a minimum - -import strutils, os - -proc newName(f: string): string = - var (dir, name, ext) = splitFile(f) - return dir / "trim_" & name & ext - -proc walker(dir: string) = - for kind, path in walkDir(dir): - case kind - of pcFile: - moveFile(dest=newName(path), source=path) - # test if installation still works: - if execShellCmd(r"nimrod c --force_build koch") == 0: - echo "Optional: ", path - removeFile(newName(path)) - else: - echo "Required: ", path - # copy back: - moveFile(dest=path, source=newName(path)) - of pcDir: - walker(path) - else: discard - -if paramCount() == 1: - walker(paramStr(1)) -else: - quit "Usage: trimcc c_compiler_directory" +# Trim C compiler installation to a minimum + +import strutils, os, pegs, strtabs, math, threadpool, times + +const + Essential = """gcc.exe g++.exe gdb.exe ld.exe as.exe c++.exe cpp.exe cc1.exe +crtbegin.o crtend.o crt2.o dllcrt2.o libgcc_s_dw2-1.dll libgcc_s_sjlj-1.dll +libgcc_s_seh-1.dll libexpat-1.dll libwinpthread-1.dll aio.h dlfcn.h fcntl.h +fenv.h fmtmsg.h fnmatch.h ftw.h errno.h glob.h gtmath.h if.h in.h ipc.h +langinfo.h locale.h math.h mman.h netdb.h nl_types.h poll.h pthread.h pwd.h +sched.h select.h semaphore.h signal.h socket.h spawn.h stat.h statvfs.h stdio.h +stdlib.h string.h strings.h tcp.h time.h types.h ucontext.h uio.h utsname.h +unistd.h wait.h varargs.h windows.h zlib.h +""".split + +proc includes(headerpath, headerfile: string, whitelist: StringTableRef) = + whitelist[headerfile] = "processed" + for line in lines(headerpath): + if line =~ peg"""s <- ws '#include' ws ('"' / '<') {[^">]+} ('"' / '>') ws + comment <- '/*' @ '*/' / '//' .* + ws <- (comment / \s+)* """: + let m = matches[0].extractFilename + if whitelist[m] != "processed": + whitelist[m] = "found" + +proc processIncludes(dir: string, whitelist: StringTableRef) = + for kind, path in walkDir(dir): + case kind + of pcFile: + let name = extractFilename(path) + if ('.' notin name and "include" in path) or ("c++" in path): + let n = whitelist[name] + if n != "processed": whitelist[name] = "found" + if name.endswith(".h"): + let n = whitelist[name] + if n == "found": includes(path, name, whitelist) + of pcDir: processIncludes(path, whitelist) + else: discard + +proc gatherFiles(dir: string, whitelist: StringTableRef, result: var seq[string]) = + for kind, path in walkDir(dir): + case kind + of pcFile: + let name = extractFilename(path) + if not whitelist.hasKey(name): + result.add(path) + of pcDir: + gatherFiles(path, whitelist, result) + else: + discard + +proc gatherEmptyFolders(dir: string, whitelist: StringTableRef, result: var seq[string]) = + var empty = true + for kind, path in walkDir(dir): + case kind + of pcFile: + empty = false + of pcDir: + let (none, name) = splitPath(path) + if not whitelist.hasKey(name): + gatherEmptyFolders(path, whitelist, result) + empty = false + else: + discard + if empty: + result.add(dir) + +proc newName(f: string): string = + let (dir, name, ext) = splitFile(f) + return dir / "trim_" & name & ext + +proc ccStillWorks(): bool = + const + c1 = r"nim c --verbosity:0 --force_build koch" + c2 = r"nim c --verbosity:0 --force_build --threads:on --out:tempOne.exe trimcc" + c3 = r"nim c --verbosity:0 --force_build --threads:on --out:tempTwo.exe fakeDeps" + c4 = r".\koch.exe" + c5 = r".\tempOne.exe" + c6 = r".\tempTwo.exe" + result = execShellCmd(c1) == 0 and execShellCmd(c2) == 0 and + execShellCmd(c3) == 0 and execShellCmd(c4) == 0 and + execShellCmd(c5) == 0 and execShellCmd(c6) == 0 + +proc trialDeletion(files: seq[string], a, b: int, whitelist: StringTableRef): bool = + result = true + var single = (a == min(b, files.high)) + for path in files[a .. min(b, files.high)]: + try: + moveFile(dest=newName(path), source=path) + except OSError: + return false + + # Test if compilation still works, even with the moved files. + if ccStillWorks(): + for path in files[a .. min(b, files.high)]: + try: + removeFile(newName(path)) + echo "Optional: ", path + except OSError: + echo "Warning, couldn't move ", path + moveFile(dest=path, source=newName(path)) + return false + else: + for path in files[a .. min(b, files.high)]: + echo "Required: ", path + moveFile(dest=path, source=newName(path)) + if single: + whitelist[path] = "found" + result = false + +proc main(dir: string) = + # Construct a whitelist of files to not remove + var whitelist = newStringTable(modeCaseInsensitive) + for e in Essential: + whitelist[e] = "found" + while true: + let oldLen = whitelist.len + processIncludes(dir, whitelist) + if oldLen == whitelist.len: + break + + # Remove batches of files + var nearlyDone = false + while true: + # Gather files to test + var allFiles = newSeq[string]() + gatherFiles(dir, whitelist, allFiles) + + # Determine the initial size of groups to check + var + maxBucketSize = len(allFiles) + bucketSize = 1 + + # Loop through the list of files, deleting batches + var i = 0 + while i < allFiles.len: + var success = trialDeletion(allFiles, i, i+bucketSize-1, whitelist) + inc i, bucketSize + + # If we aren't on the last pass, adjust the batch size based on success + if not nearlyDone: + if success: + bucketSize = min(bucketSize * 2, maxBucketSize) + else: + bucketSize = max(bucketSize div 2, 1) + echo "Bucket size is now ", bucketSize + + # After looping through all the files, check if we need to break. + if nearlyDone: + break + if bucketSize == 1: + nearlyDone = true + + while true: + var + emptyFolders = newSeq[string]() + changed = false + + gatherEmptyFolders(dir, whitelist, emptyFolders) + for path in emptyFolders: + removeDir(path) + if not ccStillWorks(): + createDir(path) + whitelist[path] = "found" + else: + changed = true + if not changed: + break + +if paramCount() == 1: + doAssert ccStillWorks() + main(paramStr(1)) +else: + quit "Usage: trimcc c_compiler_directory", QuitSuccess diff --git a/tools/website.tmpl b/tools/website.tmpl index 1d6242736..6f7a216a5 100644 --- a/tools/website.tmpl +++ b/tools/website.tmpl @@ -1,88 +1,239 @@ #! stdtmpl | standard #proc generateHTMLPage(c: var TConfigData, currentTab, content, rss: string): string = # result = "" - + + + + $c.projectTitle + - - $c.projectTitle - - + #if len(rss) > 0: - - #end fi - - - -
      - - - - -
      + + #end if + + +
      + - -
      +# if currentTab == "index": +
      +# else: +
      +# end +
      +
      - - - -
      - #if c.quotations.hasKey(currentTab): -
      -

      - ${c.quotations[currentTab].quote} -
      - -- ${c.quotations[currentTab].author} -

      -
      - #end if - - $content -
      -
      - - -
      +

      Latest News

      + + +
      + + +
      +
      +
      +
      +
      + $content +
      +
      +
      + + + + diff --git a/web/assets/images/bg.jpg b/web/assets/images/bg.jpg new file mode 100644 index 000000000..4e33a79ce Binary files /dev/null and b/web/assets/images/bg.jpg differ diff --git a/web/assets/images/favicon.ico b/web/assets/images/favicon.ico new file mode 100644 index 000000000..aed71adb4 Binary files /dev/null and b/web/assets/images/favicon.ico differ diff --git a/web/assets/images/foot.png b/web/assets/images/foot.png new file mode 100644 index 000000000..dc2561cf3 Binary files /dev/null and b/web/assets/images/foot.png differ diff --git a/web/assets/images/glow-arrow.png b/web/assets/images/glow-arrow.png new file mode 100644 index 000000000..436d32f03 Binary files /dev/null and b/web/assets/images/glow-arrow.png differ diff --git a/web/assets/images/glow-line.png b/web/assets/images/glow-line.png new file mode 100644 index 000000000..6607bdee9 Binary files /dev/null and b/web/assets/images/glow-line.png differ diff --git a/web/assets/images/head-link.png b/web/assets/images/head-link.png new file mode 100644 index 000000000..d97cba5b8 Binary files /dev/null and b/web/assets/images/head-link.png differ diff --git a/web/assets/images/head-link_hover.png b/web/assets/images/head-link_hover.png new file mode 100644 index 000000000..27edf3b05 Binary files /dev/null and b/web/assets/images/head-link_hover.png differ diff --git a/web/assets/images/head.png b/web/assets/images/head.png new file mode 100644 index 000000000..009f86728 Binary files /dev/null and b/web/assets/images/head.png differ diff --git a/web/assets/images/logo.png b/web/assets/images/logo.png index 31ee0a6e1..85d3d2e51 100644 Binary files a/web/assets/images/logo.png and b/web/assets/images/logo.png differ diff --git a/web/assets/images/mascot.png b/web/assets/images/mascot.png new file mode 100644 index 000000000..9beb62c01 Binary files /dev/null and b/web/assets/images/mascot.png differ diff --git a/web/assets/images/more-links_editors.png b/web/assets/images/more-links_editors.png new file mode 100644 index 000000000..f5970ff1f Binary files /dev/null and b/web/assets/images/more-links_editors.png differ diff --git a/web/assets/images/more-links_forum.png b/web/assets/images/more-links_forum.png new file mode 100644 index 000000000..f33277777 Binary files /dev/null and b/web/assets/images/more-links_forum.png differ diff --git a/web/assets/images/more-links_github.png b/web/assets/images/more-links_github.png new file mode 100644 index 000000000..4a6a844f4 Binary files /dev/null and b/web/assets/images/more-links_github.png differ diff --git a/web/assets/images/more-links_nimbuild.png b/web/assets/images/more-links_nimbuild.png new file mode 100644 index 000000000..473fbe4cd Binary files /dev/null and b/web/assets/images/more-links_nimbuild.png differ diff --git a/web/assets/images/nim-logo.svg b/web/assets/images/nim-logo.svg new file mode 100644 index 000000000..feae6c3d7 --- /dev/null +++ b/web/assets/images/nim-logo.svg @@ -0,0 +1,500 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/web/assets/images/quotes.png b/web/assets/images/quotes.png new file mode 100644 index 000000000..0d75b4cc2 Binary files /dev/null and b/web/assets/images/quotes.png differ diff --git a/web/assets/images/sidebar.png b/web/assets/images/sidebar.png index 77624480e..142db93cf 100644 Binary files a/web/assets/images/sidebar.png and b/web/assets/images/sidebar.png differ diff --git a/web/assets/images/slideshow-nav.png b/web/assets/images/slideshow-nav.png new file mode 100644 index 000000000..fbfff3e5d Binary files /dev/null and b/web/assets/images/slideshow-nav.png differ diff --git a/web/assets/images/slideshow-nav_active.png b/web/assets/images/slideshow-nav_active.png new file mode 100644 index 000000000..525caf355 Binary files /dev/null and b/web/assets/images/slideshow-nav_active.png differ diff --git a/web/assets/images/tabEnd.png b/web/assets/images/tabEnd.png new file mode 100644 index 000000000..a94af90f1 Binary files /dev/null and b/web/assets/images/tabEnd.png differ diff --git a/web/assets/style.css b/web/assets/style.css index 91bb4bd8a..da4cff05c 100644 --- a/web/assets/style.css +++ b/web/assets/style.css @@ -1,210 +1,598 @@ -* { cursor:default; } -a, a * { cursor:pointer; text-decoration:none; } -html, body { - margin:0; - padding:0; - width:100%; - height:100%; - background:#121220; - font:14px arial; - letter-spacing:1px; - line-height:22px; +* { cursor:default; } +a, a * { cursor:pointer; } + +html { margin:0; overflow-x:auto; } +body { + overflow-x:hidden; + min-width:1030px; + margin:0; + font:13pt "arial"; + background:#152534 url("images/bg.jpg") no-repeat fixed center top; + color:rgba(0,0,0,.8); } + +pre { color:#fff;} +pre, pre * { cursor:text; } +pre .cmt { color:rgb(255,229,106); } +pre .kwd { color:#43A8CF; font-weight:bold; } +pre .typ { color:#128B7D; font-weight:bold; } +pre .atr { color:#128B7D; font-weight:bold; font-style:italic; } +pre .def { color:#CAD6E4; font-weight:bold; } +pre .prg { color:#854D6A; font-weight:bold; font-style:italic; } +pre .val { color:#8AB647; } +pre .tab { border-left:1px dotted rgba(67,168,207,0.4); } +pre .end { background:url("images/tabEnd.png") no-repeat left bottom; } + +.tall { height:100%; } +.pre { padding:0 5px; font:11pt monospace; background:rgba(255,255,255,.15); border-radius:3px; } + +.page-layout { margin:0 auto; width:1000px; } +.docs-layout { margin:0 40px; } +.talk-layout { margin:0 40px; } +.wide-layout { margin:0 auto; } + +#head { height:100px; background:url("images/head.png") repeat-x bottom; } +#head.docs { margin-left:280px; background:rgba(0,0,0,.25) url("images/head-fade.png") no-repeat right top; } +#head > div { position:relative } + + #head-logo { + position:absolute; + left:-390px; + top:0; + width:917px; + height:268px; + pointer-events:none; + background:url("images/logo.png") no-repeat; } + #head.docs #head-logo { left:-381px; position:fixed; } + #head.forum #head-logo { left:-370px; } + + #head-logo-link { + position:absolute; + display:block; + top:10px; + left:10px; + width:236px; + height:85px; } + #head.docs #head-logo-link { left:-260px; } + #head.forum #head-logo-link { left:30px; } + + #head-links { position:absolute; right:0; bottom:13px; } + #head.docs #head-links, + #head.forum #head-links { right:20px; } + #head-links > a { + display:block; + float:left; + padding:10px 25px 25px 25px; + color:rgba(255,255,255,.5); + font-size:14pt; + text-decoration:none; + letter-spacing:1px; + background:url("images/head-link.png") no-repeat center bottom; + transition: + color 0.3s ease-in-out, + text-shadow 0.4s ease-in-out; } + #head-links > a:hover, + #head-links > a.active { + color:#1cb3ec; + text-shadow:0 0 4px rgba(28,179,236,.8); + background-image:url("images/head-link_hover.png"); } + + #head-banner { width:200px; height:100px; background:#000; } + +#neck { z-index:0; height:40px; } +#neck.home { height:370px; } +#neck > div { position:relative } + + #glow-arrow { + position:absolute; + top:-9px; + left:0; + right:-16px; + height:48px; + background:url("images/glow-arrow.png") no-repeat right; } + glow-arrow.docs { left:280px; } + + #glow-line-vert { + position:fixed; + top:100px; + left:280px; + width:3px; + height:844px; + background:url("images/glow-line-vert.png") no-repeat; } + + #slideshow { position:absolute; top:10px; left:10px; width:700px; height: 1000px; } + #slideshow > div { + visibility:hidden; opacity:0; position:absolute; transition:visibility 0s linear 1s, opacity 1s ease-in-out; } + #slideshow > div.active { visibility:visible; opacity:1; transition-delay:0s; } + #slideshow > div.init { transition-delay:0s; } + #slideshow-nav { z-index:3; position:absolute; top:110px;; right:-12px; } + #slideshow-nav > div { margin:5px 0; width:23px; height:23px; background:url("images/slideshow-nav.png") no-repeat; } + #slideshow-nav > div:hover { background-image:url("images/slideshow-nav_active.png"); opacity:0.5; } + #slideshow-nav > div.active { background-image:url("images/slideshow-nav_active.png"); opacity:1; } + + #slide0 { margin:30px 0 0 10px; } + #slide0 > div { float:left; width:320px; font:10pt monospace; } + #slide0 > div:first-child { margin:0 40px 0 0; } + #slide0 h2 { margin:0 0 5px 0; color:rgba(162,198,223,.78); } + #slide0 > div > pre { + margin:0; + padding:15px 10px; + line-height:14pt; + background:rgba(0,0,0,.4); + border-left:8px solid rgba(0,0,0,.3); + box-shadow:1px 2px 16px rgba(28,180,236,.4); } + + #slide1 { margin:30px 0 0 10px; } + #slide1 { float:left; width:680px; font:10pt monospace; } + #slide1 > div:first-child { margin:0 40px 0 0; } + #slide1 h2 { margin:0 0 5px 0; color:rgba(162,198,223,.78); } + #slide1 .desc { margin:0 0 5px 0; color:rgba(162,198,223,.78); + font:13pt "arial"; } + #slide1 pre { + padding:7px 10px; + line-height:14pt; + background:rgba(0,0,0,.4); + border-left:8px solid rgba(0,0,0,.3); + box-shadow:1px 2px 16px rgba(28,180,236,.4); } + + + #slide2 { margin:30px 0 0 10px; } + #slide2 > div { float:left; width:320px; font:10pt monospace; } + #slide2 > div:first-child { margin:0 40px 0 0; } + #slide2 h2 { margin:0 0 5px 0; color:rgba(162,198,223,.78); } + #slide2 > div > pre { + margin:0; + padding:15px 10px; + line-height:14pt; + background:rgba(0,0,0,.4); + border-left:8px solid rgba(0,0,0,.3); + box-shadow:1px 2px 16px rgba(28,180,236,.4); } + + #slide2 .desc { margin:0 0 5px 0; color:rgba(162,198,223,.78); + font:13pt "arial"; } + + /* back when slide1 was the quote: + #slide1 { margin-top:50px; } + #slide1 > p { + padding:40px 20px 0 20px; + font-style:italic; + color:rgba(162,198,223,.78); + letter-spacing:1px; + line-height:25pt; + background:url("images/quotes.png") top left no-repeat; } + #slide1 > div { + float:right; + margin-right:40px; + font-style:italic; + font-weight:bold; + color:rgba(93,155,199,.44); } + */ + #sidebar { + z-index:2; + position:absolute; + top:5px; right:0; + width:275px; + height:726px; + padding:210px 0 0 0; + background:url("images/sidebar.png") no-repeat; } + #sidebar > h3 { margin:0 30px 0 30px; color:rgba(255,255,255,.5); } + #sidebar > h3.blue { color:rgba(28,180,236,.5); } + #sidebar-links, + #sidebar-news { + margin:10px 30px 50px 30px; + padding:10px 0; + background:rgba(0,0,0,.6); } + #sidebar-links { box-shadow:1px 2px 12px rgba(255,255,255,.4); } + #sidebar-news { box-shadow:1px 2px 12px rgba(28,180,236,.6); } + #sidebar-links > a { + display:block; + margin-left:15px; + padding:12px 20px 12px 45px; + font-weight:bold; + text-decoration:none; + letter-spacing:1px; + color:rgba(255,255,255,.4); + transition: + color 0.1s ease-in-out, + text-shadow 0.2s ease-in-out; } + #sidebar-news > a { transition: color 0.3s ease-in-out; } + #sidebar-news > a > h4 { transition: color 0.1s ease-in-out, text-shadow 0.2s ease-in-out; } + #sidebar-links > a:hover { color:#fff; text-shadow:0 0 6px #fff; } + #sidebar-news > a { display:block; padding:15px; color:rgba(255,255,255,.4); text-decoration:none; } + #sidebar-news > a > h4 { margin:0 0 5px 0; color:rgba(28,180,236,.5); } + #sidebar-news > a:hover > h4 { margin:0 0 5px 0; color:rgba(28,180,236,.8); text-shadow:0 0 6px rgba(28,180,236,.6); } + #sidebar-news > a:hover { color:rgba(255,255,255,1); } + #sidebar-news > a.blue { color:rgba(28,180,236,.5); font-weight:bold; } + #sidebar-news > a.blue:hover { color:#fff; } + + #link_forum { background:url("images/more-links_forum.png") no-repeat left center; } + #link_github { background:url("images/more-links_github.png") no-repeat left center; } + #link_editors { background:url("images/more-links_editors.png") no-repeat left center; } + #link_aporia { background:url("images/more-links_editors.png") no-repeat left center; } + #link_nimbuild { background:url("images/more-links_nimbuild.png") no-repeat left center; } + + #overview-bg { + position:fixed; + top:0; + bottom:0; + left:0; + width:280px; + background:rgba(0,0,0,0.25); } + #overview { + z-index:3; + position:fixed; + overflow:auto; + top:115px; + bottom:20px; + left:20px; + width:245px; } + #overview::-webkit-scrollbar { width:5px; } + #overview::-webkit-scrollbar-track { border-radius:2px; background:rgba(255,255,255,.03); } + #overview::-webkit-scrollbar-thumb { border-radius:2px; background:rgba(28,179,236,.5); } + #overview > div { overflow:auto; margin-bottom:40px; } + #overview a { + display:block; + padding:0 10px; + margin:2px 5px 2px 0; + color:rgba(255,255,255,.6); + background:rgba(255,255,255,0.03); + border-radius:2px; + letter-spacing:1px; + text-decoration:none; } + #overview a:hover { color:#fff; background:rgba(255,255,255,0.05); } + #overview > .types a { border-left:2px solid rgba(28,179,236,.4); } + #overview > .procs a { border-left:2px solid rgba(255,223,53,.4); } + #overview > .iters a { border-left:2px solid rgba(255,134,53,.4); } + #overview > div > h4 { + margin:0 5px 10px 0; + padding:5px 10px; + letter-spacing:1px; + color:#fff; + border-left:2px solid #fff; + border-radius:2px; + background:rgba(255,255,255,0.1); } + #overview > .types h4 { color:#1cb3ec; border-color:#1cb3ec; } + #overview > .procs h4 { color:#ffdf35; border-color:#ffdf35; } + #overview > .iters h4 { color:#ff8635; border-color:#ff8635; } + #overview h5 { + color:rgba(28,179,236,.6); + margin:10px 0 5px 0; + padding:5px 5px; + letter-spacing:1px; } + +#body { z-index:1; position:relative; background:rgba(220,231,248,0.8); } +#body.docs { margin:0 40px 20px 320px; } +#body.forum { margin:0 40px 20px 40px; } + + #body-border { + position:absolute; + top:-25px; + left:0; + right:0; + height:35px; + background:rgba(0,0,0,.25); } + + #body-border-left { + position:absolute; + left:-25px; + top:-25px; + bottom:-25px; + width:35px; + background:rgba(0,0,0,.25); } + + #body-border-right { + position:absolute; + right:-25px; + top:-25px; + bottom:-25px; + width:35px; + background:rgba(0,0,0,.25); } + + #body-border-bottom { + position:absolute; + left:10px; + right:10px; + bottom:-25px; + height:35px; + background:rgba(0,0,0,.25); } + + #body.docs #body-border, + #body.forum #body-border { left:10px; right:10px; } + + #glow-line { + position:absolute; + top:-27px; + left:100px; + right:-25px; + height:3px; + background:url("images/glow-line.png") no-repeat left; } + #glow-line-bottom { + position:absolute; + bottom:-27px; + left:-25px; + right:100px; + height:3px; + background:url("images/glow-line2.png") no-repeat right; } + + #content { padding:40px 0; line-height:150%; } + #content.page { width:680px; min-height:800px; padding-left:20px; } + #content h1 { font-size:20pt; letter-spacing:1px; color:rgba(0,0,0,.75); } + #content h2 { font-size:16pt; letter-spacing:1px; color:rgba(0,0,0,.7); margin-top:40px; } + #content p { text-align:justify; } + #content a { color:#0E65D1; text-decoration:none; } + #content a:hover { text-decoration:underline; } + #content ul { padding-left:20px; } + #content li { margin-bottom:10px; text-align:justify; } + + #body.docs #content > div { margin-top:40px; padding-top:40px; border-top:1px dashed rgba(0,0,0,.25); } + #body.docs #content > div:first-child { margin-top:0; padding-top:0; border:none; } + #body.docs #content > div > h3 { + color:#fff; + margin:0 0 10px 0; + padding:10px 20px; + letter-spacing:1px; + border-left:8px solid #fff; + border-radius:3px; + background:rgba(0,0,0,.7); + box-shadow:1px 3px 12px rgba(0,0,0,.4); } + #body.docs #content > #types-wrap > h3 { color:#1cb3ec; border-color:#1cb3ec; } + #body.docs #content > #procs-wrap > h3 { color:#ffdf35; border-color:#ffdf35; } + #body.docs #content > #iters-wrap > h3 { color:#ff8635; border-color:#ff8635; } + #body.docs #content > div > div > div { + overflow:auto; + margin:10px 0; + border-left:8px solid #fff; + border-radius:3px; + background:rgba(0,0,0,.1); } + #body.docs #content > #types-wrap > div > div { border-color:rgba(28,179,236,.5); } + #body.docs #content > #procs-wrap > div > div { border-color:rgba(255,223,53,.5); } + #body.docs #content > #iters-wrap > div > div { border-color:rgba(255,134,53,.5); } + #body.docs #content > #procs-wrap > div > div.overload-head { margin-bottom:0; } + #body.docs #content > #procs-wrap > div > div.overload-tail { margin-top:0; border-top:1px dashed rgba(255,223,53,.5); } + #body.docs #content > #procs-wrap > div > div.overload { margin-top:0; margin-bottom:0; border-top:1px dashed rgba(255,223,53,.5); } + #body.docs #content > #iters-wrap > div > div.overload-head { margin-bottom:0; } + #body.docs #content > #iters-wrap > div > div.overload-tail { margin-top:0; border-top:1px dashed rgba(255,134,53,.5); } + #body.docs #content > #iters-wrap > div > div.overload { margin-top:0; margin-bottom:0; border-top:1px dashed rgba(255,134,53,.5); } + #body.docs #content > div > div > p { margin:20px 10px 10px 10px; } + + #body.docs #content > div > div > div > div { float:left; } + #body.docs #content > div > div > div > div.head { width:60%; } + #body.docs #content > div > div > div > div.data { width:40%; } + + #body.docs #content > h1 > .symbol { + padding:0 8px; + border-radius:5px; + background:rgba(206,218,233,.4); } + + #body.docs #content > div > div > div > div.head > .sign { + margin:0 10px 5px 10px; + padding:10px 10px 0 10px; + font-weight:bold; + border-bottom:1px dashed rgba(0,0,0,.25); } + #body.docs #content > div > div > div > div.head > .desc { + padding:0 20px 10px 20px; + color:rgba(0,0,0,.75); } + #body.docs #content > div > #types > div > div.head > .sign > .symbol { + padding:0 5px; + border-radius:3px; + background:rgba(28,179,236,.4); } + #body.docs #content > div > #procs > div > div.head > .sign > .symbol { + padding:0 5px; + border-radius:3px; + background:rgba(255,223,53,.3); } + #body.docs #content > div > #iters > div > div.head > .sign > .symbol { + padding:0 5px; + border-radius:3px; + background:rgba(255,134,53,.3); } + + #body.docs #content > div > div > div > div.data > div { + margin:0 20px 5px 10px; + padding:10px 0 0 10px; + font-style:italic; + color:rgba(0,0,0,.6); + border-bottom:1px dashed rgba(0,0,0,.25); } + #body.docs #content > div > div > div > div.data > ul { margin:0; padding:0 10px; } + #body.docs #content > div > div > div > div.data > ul:last-child { margin-bottom:5px; padding-bottom:10px; } + #body.docs #content > div > div > div > div.data > ul .symbol { padding:0 5px; border-radius:3px; background:rgba(23,192,23,.25); } + #body.docs #content > div > div > div > div.data > ul.pragmas .symbol { background:rgba(106,50,145,.25); } + #body.docs #content > div > div > div > div.data > ul > li { margin:0; padding:0 10px; list-style:none; } + + #body.docs #content pre { + overflow:auto; + margin:10px 0; + padding:15px 10px; + font-size:10pt; + font-style:normal; + line-height:14pt; + background:rgba(0,0,0,.75); + border-left:8px solid rgba(0,0,0,.3); } + + #docs-sort { float:right; font-size:75%; } + #docs-sort > a { + cursor:default; + margin:0 0 0 10px; + padding:2px 10px; + border-radius:5px; + color:rgba(0,0,0,.25); + background:rgba(0,0,0,.1); + box-shadow:inset 0 1px 8px rgba(0,0,0,.4); } + #docs-sort > a:hover, + #docs-sort > a.active { color:#000; background:rgba(0,0,0,.2); } + + #talk-heads { overflow:auto; margin:0 8px 0 8px; } + #talk-heads > div { float:left; font-size:120%; font-weight:bold; } + #talk-heads > .topic { width:55%; } + #talk-heads > .detail { width:15%; } + #talk-heads > .author { width:15%; } + #talk-heads > .reply { width:15%; } + #talk-heads > div > div { margin:0 10px 10px 10px; padding:0 10px 10px 10px; border-bottom:1px dashed rgba(0,0,0,0.4); } + #talk-heads > .topic > div { margin-left:0; } + #talk-heads > .author > div { margin-right:0; } + + #talk-thread > div, + #talk-threads > div { + position:relative; + margin:5px 0; + overflow:auto; + border-radius:3px; + border:8px solid rgba(0,0,0,.8); + border-top:none; + border-bottom:none; + background:rgba(0,0,0,0.1); } + #talk-thread > div:nth-child(odd) { background:rgba(255,255,255,0.1); } + #talk-threads > div:nth-child(odd) { background:rgba(0,0,0,0.2); } + #talk-thread > div > div, + #talk-threads > div > div { float:left; } + #talk-thread > div > div > div, + #talk-threads > div > div > div { margin:10px 20px; } + #talk-threads > div > .topic { width:55%; } + #talk-threads > div > .reply { width:15%; overflow:hidden; } + #talk-threads > div > .detail { width:15%; overflow:hidden; } + #talk-thread > div > .author, + #talk-threads > div > .author { + position:absolute; + right:0; + top:0; + bottom:0; + width:15%; + overflow:hidden; + background:rgba(0,0,0,0.8); } + #talk-thread > div > .author a, + #talk-threads > div > .author a { color:#1cb3ec !important; } + #talk-thread > div > .author a:hover, + #talk-threads > div > .author a:hover { color:#fff !important; } + #talk-threads > div > .topic .pages { float:right; } + #talk-threads > div > .topic > div > a { font-weight:bold; } + #talk-threads > div > .detail > div { float:left; margin:0; } + #talk-threads > div > .detail > div > div { margin-left:20px; padding:10px 10px 10px 22px; } + #talk-threads > div > .detail > div { width:50%; } + #talk-threads > div > .detail > div:first-child > div { background:url("images/forum-views.png") no-repeat left; } + #talk-threads > div > .detail > div:last-child > div { background:url("images/forum-posts.png") no-repeat left; } + + #talk-thread > div { margin:20px 0; min-height:150px; box-shadow:1px 3px 12px rgba(0,0,0,.4) } + #talk-thread > div > .author > div > .avatar { margin-top:20px; } + #talk-thread > div > .author > div > .avatar > img { box-shadow:0 0 12px #1cb3ec; } + #talk-thread > div > .author > div > .name { } + #talk-thread > div > .topic { width:85%; padding-bottom:10px; } + #talk-thread > div > .topic pre { + overflow:auto; + margin:0; + padding:15px 10px; + font-size:10pt; + font-style:normal; + line-height:14pt; + background:rgba(0,0,0,.75); + border-left:8px solid rgba(0,0,0,.3); } + + #talk-head, + #talk-info { + overflow:auto; + border-radius:3px; + border:8px solid rgba(0,0,0,.2); + border-top:none; + border-bottom:none; + background:rgba(0,0,0,0.1); } + #talk-head { margin-bottom:20px; } + #talk-info { margin-top:20px; } + #talk-head > div, + #talk-info > div { float:left; } + #talk-head > .info, + #talk-info > .info { width:85%; } + #talk-head > .user, + #talk-info > .user { width:15%; background:rgba(0,0,0,.2); } + #talk-info > .user > div > .reply { font-weight:bold; padding-left:22px; background:url("images/forum-reply.png") no-repeat left; } + #talk-head > div > div, + #talk-info > div > div { padding:5px 20px; } + #talk-head > .detail > div { float:left; margin:0; } + #talk-head > .detail > div > div { padding-left:22px; } + #talk-head > .detail > div:first-child > div { background:url("images/forum-views.png") no-repeat left; } + #talk-head > .detail > div:last-child > div { background:url("images/forum-posts.png") no-repeat left; } + + #talk-nav { margin:20px 8px 0 8px; padding-top:10px; border-top:1px dashed rgba(0,0,0,0.4); text-align:center; } + #talk-nav > a.active { text-decoration:underline !important; } + + .standout { + padding:5px 30px; + margin-bottom:20px; + border:8px solid rgba(0,0,0,.8); + border-right-width:16px; + border-top-width:0; + border-bottom-width:0; + border-radius:3px; + background:rgba(0,0,0,0.1); + box-shadow:1px 3px 12px rgba(0,0,0,.4); } + .standout h2 { margin-bottom:10px; padding-bottom:10px; border-bottom:1px dashed rgba(0,0,0,.8); } + .standout li { margin:0 !important; padding-top:10px; border-top:1px dashed rgba(0,0,0,.2); } + .standout ul { padding-bottom:5px; } + .standout ul.tools { list-style:url("images/docs-tools.png"); } + .standout ul.library { list-style:url("images/docs-library.png"); } + .standout ul.internal { list-style:url("images/docs-internal.png"); } + .standout ul.tutorial { list-style:url("images/docs-tutorial.png"); } + .standout ul.example { list-style:url("images/docs-example.png"); } + .standout li:first-child { padding-top:0; border-top:none; } + .standout li p { margin:0 0 10px 0 !important; line-height:130%; } + .standout li > a { font-weight:bold; } + + .forum-user-info, + .forum-user-info * { cursor:help } + +#foot { height:150px; position:relative; top:-10px; letter-spacing:1px; } +#foot.home { background:url("images/foot.png") repeat-x top; height:200px; } +#foot.docs { margin-left:320px; margin-right:40px; } +#foot.forum { margin-left:40px; margin-right:40px; } +#foot > div { position:relative; } +#foot.home > div { width:960px; } +#foot h4 { font-size:11pt; color:rgba(255,255,255,.4); margin:40px 0 6px 0; } +#foot a:hover { color:#fff; } + + #foot-links { float:left; } + #foot-links > div { float:left; padding:0 40px 0 0; line-height:120%; } + #foot-links a { display:block; font-size:10pt; color:rgba(255,255,255,.3); text-decoration:none; } + #foot-legal { float:right; font-size:10pt; color:rgba(255,255,255,.3); line-height:150%; text-align:right; } + #foot-legal a { color:inherit; text-decoration:none; } + #foot-legal > h4 > a { color:inherit; } + + #mascot { + z-index:2; + position:absolute; + top:-340px; + right:25px; + width:202px; + height:319px; + background:url("images/mascot.png") no-repeat; } + + +#body pre { + padding:20px; + border-left:10px solid #8f9698; + background:#f3f6f8; + font-size:15px; + font-family:courier, monospace; + letter-spacing:0; + line-height:17px; + color: #343739; } -#site { z-index:0; position:relative; margin:0 auto; width:960px; } -#site > dive { position:relative; } +#body span.pre { + background-color: #96A9B7; + padding: 1pt 3pt; + border-radius: 2pt; + -moz-border-radius: 2pt; + -webkit-border-radius: 2pt; +} - /* site_head */ - #site_head { z-index:0; height:200px; } - - #logo { - z-index:-1; - position:absolute; - top:-35px; - left:-330px; - width:868px; - height:334px; - background:url("images/logo.png") no-repeat; - } - - /* site_neck */ - #site_neck { z-index:1; float:left; width:100%; height:40px; background:url("images/site_neck.png") bottom no-repeat; } - - #site_neck > a { - display:block; - float:left; - margin-top:3px; - margin-right:10px; - padding:4px 20px 0 20px; - height:28px; - color:#959a9b; - background:#394649; - font-size:18px; - } - - #site_neck > a:hover { - margin-top:0; - border-top:3px solid #1cb4ec; - } - - #site_neck > a.active { - margin-top:0; - padding-top:7px; - color:#fff; - background:#1cb4ec; - text-shadow:0 0 4px rgba(255,255,255,0.75); - } - - #site_neck > a.active:hover { - border-top:none; - } - - /* site_body */ - #site_body { z-index:2; float:left; clear:both; width:100%; background:#d1dbe3; } - - #page { position:relative; float:left; padding:20px 30px 50px 50px; width:620px; color:#343739; } - - #page h1 { margin-top:40px; line-height: 28px; } - #page h2 { margin-top:40px; } - - #page p { text-align:justify; } - - #page .quote-image { - z-index:0; - position:absolute; - top:15px; - left:20px; - width:59px; - height:42px; - background:url("images/quote.png") no-repeat; - } - - #page p.quote { - position:relative; - color:#6187a2; - font-style:italic; - letter-spacing:2px; - word-spacing:1px; - } - - #page pre { - padding:20px; - border-left:10px solid #8f9698; - background:#f3f6f8; - font-size:15px; - font-family:courier, monospace; - letter-spacing:0; - line-height:17px; - } - - #page span.pre { - background-color: #E6EDF2; - padding: 1pt 3pt; - border-radius: 2pt; - -moz-border-radius: 2pt; - -webkit-border-radius: 2pt; - } - - #page pre > .Comment { color:#858686; font-style:italic; } - #page pre > .Keyword { color:#1cb4ec; font-weight:bold; } - #page pre > .Operator { color:#777; } - #page pre > .StringLit, #page pre > .DecNumber { color:#ff7302; } - - #page li { margin-top:10px; } - - #page a:hover { text-decoration: underline; } - - #page table.docutils { - background: none repeat scroll 0 0 #F3F6F8; - border-collapse: collapse; - font-size: 8pt; - text-align: left; - width: 480px; - border-spacing: 0; - } - - #page .docutils th { - border-bottom: 2px solid #1a1a1a; - font-size: 14px; - font-weight: normal; - padding: 8px 8px; - } - - #page .docutils td { - padding: 3px 8px; - color: #4d4d4d; - } - - #sidebar_wrap { float:right; width:260px; } - #sidebar { - z-index:0; - position:relative; - left:20px; - padding:0 10px 60px 10px; - width:200px; - background:#394649 url("images/sidebar.png") bottom no-repeat; - } - - #sidebar_head { - z-index:-1; - position:absolute; - top:-220px; - left:-30px; - width:282px; - height:400px; - background:url("images/sidebar_head.png") top no-repeat; - } - - #sidebar > h2 { - position:relative; - left:-40px; - margin:20px 0 0 0; - padding:19px 0 0 48px; - width:204px; - height:47px; - color:#fff; - background:url("images/sidebar_h2.png") no-repeat; - text-shadow:0 0 4px rgba(255,255,255,0.75); - letter-spacing:2px; - } - - #sidebar > .news { display:block; margin-bottom:20px; padding:0 10px; } - #sidebar > .news > h3 { margin:0; color:#cdd1d1; font-size:18px; letter-spacing:2px; } - #sidebar > .news > p { margin:0; color:#99a0a1; } - #sidebar > .news:hover > h3 { color:#fff; text-shadow:0 0 4px rgba(255,255,255,0.75); } - #sidebar > .news:hover > p { color:#eee; text-shadow:0 0 4px rgba(255,255,255,0.5); } - - #sidebar > .link { - display:block; - margin:0; - padding:15px 20px 0 20px; - height:41px; - color:#cdd1d1; - font-size:18px; - font-weight:bold; - } - - #sidebar > .link:hover { - color:#fff; - text-shadow:0 0 4px rgba(255,255,255,0.75); - } - - #link_forum { background:url("images/link_forum.png") top right no-repeat; } - #link_aporia { background:url("images/link_aporia.png") top right no-repeat; } - #link_nimbuild { background:url("images/link_nimbuild.png") top right no-repeat; } - - /* site_foot */ - #site_foot { z-index:3; clear:both; padding-top:40px; height:150px; background:url("images/site_foot.png") top no-repeat; } - - #legal { - float:right; - margin-top:10px; - color:#88888f; - font-size:12px; - letter-spacing:1px; - } - - #legal > a { color:#88888f; } - #legal > a:visited { color:#88888f; } - #legal > a:hover { color:#fff; } +#body pre > .Comment { color:#858686; font-style:italic; } +#body pre > .Keyword { color:#1cb4ec; font-weight:bold; } +#body pre > .Operator { color:#777; } +#body pre > .StringLit, #page pre > .DecNumber { color:#ff7302; } + +#body .docutils th { + border-bottom: 2px solid #1A1A1A; + font-weight: normal; + padding: 8px; +} +#body table.docutils { + border-collapse: collapse; + text-align: left; + border-spacing: 0px; +} diff --git a/web/community.txt b/web/community.txt index d45e7df61..f5d6db0e7 100644 --- a/web/community.txt +++ b/web/community.txt @@ -1,97 +1,114 @@ -Forum -===== +Nim's Community +=============== -The `Nimrod forum `_ is the place where most -discussions related to the language happen. It not only includes discussions -relating to the design of Nimrod but also allows for beginners to ask questions -relating to Nimrod. +.. container:: standout -IRC -==== + Forum + ----- -Many Nimrod developers are a part of the -`#nimrod IRC channel `_ on -Freenode. That is the place where the rest of the discussion relating to Nimrod -occurs. Be sure to join us there if you wish to discuss Nimrod in real-time. -IRC is the perfect place for people just starting to learn Nimrod and we -welcome any questions that you may have! + The `Nim forum `_ is the place where most + discussions related to the language happen. It not only includes discussions + relating to the design of Nim but also allows for beginners to ask questions + relating to Nim. -You may also be interested in reading the -`IRC logs `_ which are an archive of all -of the previous discussions that took place in the IRC channel. -Github -====== +.. container:: standout -Nimrod's `source code `_ is hosted on Github. -Together with the `wiki `_ and -`issue tracker `_. + IRC + ---- -Github also hosts other projects relating to Nimrod. These projects are a part -of the `nimrod-code organisation `_. -This includes the `Babel package manager `_ -and its `package repository `_. + Many Nim developers are a part of the + `#nim IRC channel `_ on + Freenode. That is the place where the rest of the discussion relating to Nim + occurs. Be sure to join us there if you wish to discuss Nim in real-time. + IRC is the perfect place for people just starting to learn Nim and we + welcome any questions that you may have! -Twitter -======= + You may also be interested in reading the + `IRC logs `_ which are an archive of all + of the previous discussions that took place in the IRC channel. -Follow us `@nimrodlang `_ for latest news about -Nimrod. -Reddit -====== +.. container:: standout -Subscribe to `/r/nimrod `_ for latest news about -Nimrod. + Github + ------ -StackOverflow -============= + Nim's `source code `_ is hosted on Github. + Together with the `wiki `_ and + `issue tracker `_. -When asking a question relating to Nimrod, be sure to use the -`Nimrod `_ tag in your -question. + Github also hosts other projects relating to Nim. These projects are a part + of the `nim-lang organisation `_. + This includes the `Nimble package manager `_ + and its `package repository `_. -How to help -=========== -There are always many things to be done in the main -`Nimrod repository `_, check out the -`issues `_ for -things to do; pull requests are always welcome. You can -also contribute to the many other projects hosted by the -`nimrod-code `_ organisation on github. If you -can't find anything you fancy doing, you can always ask for inspiration on IRC -(irc.freenode.net #nimrod) or on the `Nimrod forums `_. +.. container:: standout -Donations ---------- + Twitter + ------- -If you love what we do and are feeling generous then you can always donate. -Contributions of any quantity are greatly appreciated and will contribute to -making Nimrod even better! + Follow us `@nimlang `_ for latest news about + Nim. -Gittip -`````` +.. container:: standout - .. raw:: html + Reddit + ------ - + Subscribe to `/r/nim `_ for latest news about + Nim. -Paypal -`````` +.. container:: standout - .. raw:: html + StackOverflow + ------------- -
      - - - - -
      + When asking a question relating to Nim, be sure to use the + `Nim `_ tag in your + question. -Bitcoin -``````` +.. container:: standout - Bitcoin address: 1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ + How to help + ----------- + + There are always many things to be done in the main + `Nim repository `_, check out the + `issues `_ for + things to do; pull requests are always welcome. You can + also contribute to the many other projects hosted by the + `nim-lang `_ organisation on github. If you + can't find anything you fancy doing, you can always ask for inspiration on IRC + (irc.freenode.net #nim) or on the `Nim forums `_. + + +.. container:: standout + + Donations + --------- + + If you love what we do and are feeling generous then you can always donate. + Contributions of any quantity are greatly appreciated and will contribute to + making Nim even better! + + Gittip + .. raw:: html + + + + Paypal + .. raw:: html + +
      + + + + +
      + + Bitcoin + Bitcoin address: 1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ diff --git a/web/documentation.txt b/web/documentation.txt index da0313c32..0bb2b8a0f 100644 --- a/web/documentation.txt +++ b/web/documentation.txt @@ -1 +1,51 @@ -.. include:: ../doc/docs.txt +Nim's Documentation +=================== + +.. container:: standout + + Standards & Guides + ------------------ + + + - | `Standard Library `_ + | This document describes Nim's standard library. + + - | `Language Manual `_ + | The Nim manual is a draft that will evolve into a proper specification. + + - | `Compiler user guide `_ + | The user guide lists command line arguments, special features of the + compiler, etc. + + +.. container:: standout + + Tools & Features + ---------------- + + - | `Source code filters `_ + | The Nim compiler supports source code filters as a simple yet powerful + builtin templating system. + + - | `Tools documentation `_ + | Description of some tools that come with the standard distribution. + + +.. container:: standout + + Internal Details + ---------------- + + - | `Garbage Collector `_ + | Additional documentation about Nim's GC and how to operate it in a + | realtime setting. + + - | `Internal documentation `_ + | The internal documentation describes how the compiler is implemented. Read + this if you want to hack the compiler. + + +Search Options +-------------- + +`Documentation Index `_ - The generated index. **Index + (Ctrl+F) == Joy** diff --git a/web/download.txt b/web/download.txt index dc53cbeb4..1ba1ad5f7 100644 --- a/web/download.txt +++ b/web/download.txt @@ -1,6 +1,6 @@ -You can download the latest version of the Nimrod compiler here. +You can download the latest version of the Nim compiler here. -**Note:** The Nimrod compiler requires a C compiler to compile software. On +**Note:** The Nim compiler requires a C compiler to compile software. On Windows we recommend that you use `Mingw-w64 `_. GCC is recommended on Linux and clang on Mac OS X. @@ -9,10 +9,12 @@ and clang on Mac OS X. Binaries ======== -Unfortunately for now we only provide builds for Windows. +Unfortunately for now we provide no binary builds. -* 32 bit: `nimrod_0.9.6.exe `_ -* 64 bit: `nimrod_0.9.6_x64.exe `_ +.. + Unfortunately for now we only provide builds for Windows. + * 32 bit: `nim_0.10.2.exe `_ + * 64 bit: `nim_0.10.2_x64.exe `_ Installation based on generated C code @@ -22,28 +24,28 @@ This installation method is the preferred way for Linux, Mac OS X, and other Uni like systems. Binary packages may be provided later. -Download `nimrod_0.9.6.zip `_, extract it and follow +Download `nim-0.10.2.zip `_, extract it and follow these instructions: * sh build.sh * Add ``$your_install_dir/bin`` to your PATH. -There are other ways to install Nimrod (like using the ``install.sh`` script), +There are other ways to install Nim (like using the ``install.sh`` script), but these tend to cause more problems. Installation from github ======================== -It is also possible to build directly from the -github `master `_ branch:: +Use the following commands to build the compiler from source. +Change the branch to suit your needs:: - git clone -b master git://github.com/Araq/Nimrod.git - cd Nimrod - git clone --depth 1 git://github.com/nimrod-code/csources + git clone -b master git://github.com/Araq/Nim.git + cd Nim + git clone -b master --depth 1 git://github.com/nim-lang/csources cd csources && sh build.sh cd .. - bin/nimrod c koch + bin/nim c koch ./koch boot -d:release The ``master`` branch always contains the latest stable version of the compiler. diff --git a/web/index.txt b/web/index.txt index 947ebb706..b6d4f8e8f 100644 --- a/web/index.txt +++ b/web/index.txt @@ -2,13 +2,13 @@ Home ==== -Welcome to Nimrod ------------------ +Welcome to Nim +-------------- -**Nimrod** is a statically typed, imperative programming language that tries to -give the programmer ultimate power without compromises on runtime efficiency. -This means it focuses on compile-time mechanisms in all their -various forms. +**Nim** (formerly known as "Nimrod") is a statically typed, imperative +programming language that tries to give the programmer ultimate power without +compromises on runtime efficiency. This means it focuses on compile-time +mechanisms in all their various forms. Beneath a nice infix/indentation based syntax with a powerful (AST based, hygienic) macro system lies a semantic model that supports @@ -18,27 +18,11 @@ shared memory heap is also provided for the increased efficiency that results from that model. -Nimrod looks like this: -======================= - -.. code-block:: nimrod - # compute average line length - var count = 0 - var sum = 0 - - for line in stdin.lines: - count += 1 - sum += line.len - - echo "Average line length: ", - if count > 0: sum / count else: 0 - - -Nimrod is efficient -=================== +Nim is efficient +================ * Native code generation (currently via compilation to C), not dependent on a - virtual machine: **Nimrod produces small executables without dependencies + virtual machine: **Nim produces small executables without dependencies for easy redistribution.** * A fast **non-tracing** garbage collector that supports soft real-time systems (like games). @@ -55,11 +39,11 @@ Nimrod is efficient the stack. -Nimrod is expressive -==================== +Nim is expressive +================= -* **The Nimrod compiler and all of the standard library are implemented in - Nimrod.** +* **The Nim compiler and all of the standard library are implemented in + Nim.** * Built-in high level datatypes: strings, sets, sequences, etc. * Modern type system with local type inference, tuples, variants, generics, etc. @@ -69,31 +53,31 @@ Nimrod is expressive * Macros can modify the abstract syntax tree at compile time. -Nimrod is elegant -================= +Nim is elegant +============== -* Macros can use the imperative paradigm to construct parse trees. Nimrod +* Macros can use the imperative paradigm to construct parse trees. Nim does not require a different coding style for meta programming. -* Macros cannot change Nimrod's syntax because there is no need for it. - Nimrod's syntax is flexible enough. +* Macros cannot change Nim's syntax because there is no need for it. + Nim's syntax is flexible enough. * Statements are grouped by indentation but can span multiple lines. Indentation must not contain tabulators so the compiler always sees the code the same way as you do. -Nimrod plays nice with others -============================= +Nim plays nice with others +========================== -* The Nimrod Compiler runs on Windows, Linux, BSD and Mac OS X. +* The Nim Compiler runs on Windows, Linux, BSD and Mac OS X. Porting to other platforms is easy. -* **The Nimrod Compiler can also generate C++ or Objective C for easier +* **The Nim Compiler can also generate C++ or Objective C for easier interfacing.** * There are lots of bindings: for example, bindings to GTK2, the Windows API, the POSIX API, OpenGL, SDL, Cairo, Python, Lua, TCL, X11, libzip, PCRE, libcurl, mySQL and SQLite are included in the standard distribution or can easily be obtained via the - `Babel package manager `_. -* A C to Nimrod conversion utility: New bindings to C libraries are easily + `Nimble package manager `_. +* A C to Nim conversion utility: New bindings to C libraries are easily generated by ``c2nim``. @@ -101,5 +85,5 @@ Roadmap to 1.0 ============== Please have a look at -this `wiki page `_ for +this `wiki page `_ for an up-to-date overview. diff --git a/web/learn.txt b/web/learn.txt new file mode 100644 index 000000000..854b31668 --- /dev/null +++ b/web/learn.txt @@ -0,0 +1,44 @@ +Learning Nim +============ + +.. container:: standout + + Tutorials + --------- + + - | `Tutorial (part I) `_ + | Learn the basics of Nim's types, variables, procedures, control flow, etc... + + - | `Tutorial (part II) `_ + | Learn Nim's more advanced features such as OOP, generics, macros, etc... + + +.. container:: standout + + Examples + -------- + + - | `Nim by Example `_ + | Nim by Example is an excellent starting place for beginners. + + - | `Nim on Rosetta Code `_ + | Many different Nim code examples comparable to other languages for reference. + + - | `Nim for C/C++ Programmers `_ + | A useful cheat-sheet for those most familiar with C/C++ languages. + + +.. container:: standout + + Articles + -------- + + - `Dr Dobbs Nimrod Publication `_ + - `Bootstrapping Nim `_ + + +Documentation +------------- + +More examples of Nim code can be found in the `Nim Language Documentation `_. + diff --git a/web/news.txt b/web/news.txt index 3d962172f..ddeab0f9b 100644 --- a/web/news.txt +++ b/web/news.txt @@ -2,8 +2,213 @@ News ==== -2014-10-19 Nimrod version 0.9.6 released -======================================== +2014-12-29 Version 0.10.2 released +================================== + +This release marks the completion of a very important change to the project: +the official renaming from Nimrod to Nim. Version 0.10.2 contains many language +changes, some of which may break your existing code. For your convenience, we +added a new tool called `nimfix `_ that will help you convert your +existing projects so that it works with the latest version of the compiler. + +Progress towards version 1.0 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Although Nim is still pre-1.0, we were able to keep the number of breaking +changes to a minimum so far. Starting with version 1.0, we will not introduce +any breaking changes between major release versions. +One of Nim's goals is to ensure that the compiler is as efficient as possible. +Take a look at the +`latest benchmarks `_, +which show that Nim is consistently near +the top and already nearly as fast as C and C++. Recent developments, such as +the new ``asyncdispatch`` module will allow you to write efficient web server +applications using non-blocking code. Nim now also has a built-in thread pool +for lightweight threading through the use of ``spawn``. + +The unpopular "T" and "P" prefixes on types have been deprecated. Nim also +became more expressive by weakening the distinction between statements and +epxressions. We also added a new and searchable forum, a new website, and our +documentation generator ``docgen`` has seen major improvements. + +What's left to be done +~~~~~~~~~~~~~~~~~~~~~~ + +The 1.0 release is actually very close. Apart from bug fixes, there are +two major features missing or incomplete: + +* ``static[T]`` needs to be defined precisely and the bugs in the + implementation need to be fixed. +* Overloading of the assignment operator is required for some generic + containers and needs to be implemented. + +This means that fancy matrix libraries will finally start to work, which used +to be a major point of pain in the language. + + +Nimble and other Nim tools +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Outside of the language and the compiler itself many Nim tools have seen +considerable improvements. + +Babel the Nim package manager has been renamed to Nimble. Nimble's purpose +is the installation of packages containing libraries and/or applications +written in Nim. +Even though Nimble is still very young it already is very +functional. It can install packages by name, it does so by accessing a +packages repository which is hosted on a Github repo. Packages can also be +installed via a Git repo URL or Mercurial repo URL. The package repository +is searchable through Nimble. Anyone is free to add their own packages to +the package repository by forking the +`nim-lang/packages `_ repo and creating +a pull request. Nimble is fully cross-platform and should be fully functional +on all major operating systems. +It is of course completely written in Nim. + +Changelog +~~~~~~~~~ + +Changes affecting backwards compatibility +----------------------------------------- + +- **The language has been renamed from Nimrod to Nim.** The name of the + compiler changed from ``nimrod`` to ``nim`` too. +- ``system.fileHandle`` has been renamed to ``system.getFileHandle`` to + prevent name conflicts with the new type ``FileHandle``. +- Comments are now not part of the AST anymore, as such you cannot use them + in place of ``discard``. +- Large parts of the stdlib got rid of the T/P type prefixes. Instead most + types now simply start with an uppercased letter. The + so called "partial case sensitivity" rule is now active allowing for code + like ``var foo: Foo`` in more contexts. +- String case (or any non-ordinal case) statements + without 'else' are deprecated. +- Recursive tuple types are not allowed anymore. Use ``object`` instead. +- The PEGS module returns ``nil`` instead of ``""`` when an optional capture + fails to match. +- The re module returns ``nil`` instead of ``""`` when an optional capture + fails to match. +- The "symmetric set difference" operator (``-+-``) never worked and has been + removed. +- ``defer`` is a keyword now. +- ``func`` is a keyword now. +- The ``using`` language feature now needs to be activated via the new + ``{.experimental.}`` pragma that enables experimental language features. +- Destructors are now officially *experimental*. +- Standalone ``except`` and ``finally`` statements are deprecated now. + The standalone ``finally`` can be replaced with ``defer``, + standalone ``except`` requires an explicit ``try``. +- Operators ending in ``>`` are considered as "arrow like" and have their + own priority level and are right associative. This means that + the ``=>`` and ``->`` operators from the `future `_ module + work better. +- Field names in tuples are now ignored for type comparisons. This allows + for greater interoperability between different modules. +- Statement lists are not converted to an implicit ``do`` block anymore. This + means the confusing ``nnkDo`` nodes when working with macros are gone for + good. + + +Language Additions +------------------ + +- The new concurrency model has been implemented including ``locks`` sections, + lock levels and object field ``guards``. +- The ``parallel`` statement has been implemented. +- ``deepCopy`` has been added to the language. +- The builtin ``procCall`` can be used to get ``super``-like functionality + for multi methods. +- There is a new pragma ``{.experimental.}`` that enables experimental + language features per module, or you can enable this features on a global + level with the ``--experimental`` command line option. + + +Compiler Additions +------------------ + +- The compiler now supports *mixed* Objective C / C++ / C code generation: + The modules that use ``importCpp`` or ``importObjc`` are compiled to C++ + or Objective C code, any other module is compiled to C code. This + improves interoperability. +- There is a new ``parallel`` statement for safe fork&join parallel computing. +- ``guard`` and ``lock`` pragmas have been implemented to support safer + concurrent programming. +- The following procs are now available at compile-time:: + + math.sqrt, math.ln, math.log10, math.log2, math.exp, math.round, + math.arccos, math.arcsin, math.arctan, math.arctan2, math.cos, math.cosh, + math.hypot, math.sinh, math.sin, math.tan, math.tanh, math.pow, + math.trunc, math.floor, math.ceil, math.fmod, + os.getEnv, os.existsEnv, os.dirExists, os.fileExists, + system.writeFile + +- Two backticks now produce a single backtick within an ``emit`` or ``asm`` + statement. +- There is a new tool, `nimfix `_ to help you in updating your + code from Nimrod to Nim. +- The compiler's output has been prettified. + +Library Additions +----------------- + +- Added module ``fenv`` to control the handling of floating-point rounding and + exceptions (overflow, division by zero, etc.). +- ``system.setupForeignThreadGc`` can be used for better interaction with + foreign libraries that create threads and run a Nim callback from these + foreign threads. +- List comprehensions have been implemented as a macro in the ``future`` + module. +- The new Async module (``asyncnet``) now supports SSL. +- The ``smtp`` module now has an async implementation. +- Added module ``asyncfile`` which implements asynchronous file reading + and writing. +- ``osproc.kill`` has been added. +- ``asyncnet`` and ``asynchttpserver`` now support ``SO_REUSEADDR``. + +Bugfixes +-------- + +- ``nil`` and ``NULL`` are now preserved between Nim and databases in the + ``db_*`` modules. +- Fixed issue with OS module in non-unicode mode on Windows. +- Fixed issue with ``x.low`` + (`#1366 `_). +- Fixed tuple unpacking issue inside closure iterators + (`#1067 `_). +- Fixed ENDB compilation issues. +- Many ``asynchttpserver`` fixes. +- Macros can now keep global state across macro calls + (`#903 `_). +- ``osproc`` fixes on Windows. +- ``osproc.terminate`` fixed. +- Improvements to exception handling in async procedures. + (`#1487 `_). +- ``try`` now works at compile-time. +- Fixes ``T = ref T`` to be an illegal recursive type. +- Self imports are now disallowed. +- Improved effect inference. +- Fixes for the ``math`` module on Windows. +- User defined pragmas will now work for generics that have + been instantiated in different modules. +- Fixed queue exhaustion bug. +- Many, many more. + + +2014-12-09 New website design! +============================== + +A brand new website including an improved forum is now live. +All thanks go to Philip Witte and +Dominik Picheta, Philip Witte for the design of the website (together with +the logo) as well as the HTML and CSS code for his template, and Dominik Picheta +for integrating Philip's design with Nim's forum. We're sure you will +agree that Philip's design is beautiful. + + + +2014-10-19 Version 0.9.6 released +================================= **Note: 0.9.6 is the last release of Nimrod. The language is being renamed to Nim. Nim slightly breaks compatibility.** @@ -103,7 +308,7 @@ the use of an ``await`` macro which behaves similar to C#'s await. The following is a very simple chat server demonstrating Nimrod's new async capabilities. -.. code-block::nimrod +.. code-block::nim import asyncnet, asyncdispatch var clients: seq[PAsyncSocket] = @[] @@ -136,7 +341,7 @@ Syntactic sugar for anonymous procedures has also been introduced. It too has been implemented as a macro. The following shows some simple usage of the new syntax: -.. code-block::nimrod +.. code-block::nim import future var s = @[1, 2, 3, 4, 5] @@ -275,8 +480,6 @@ We are pleased to announce that version 0.9.2 of the Nimrod compiler has been released. This release has attracted by far the most contributions in comparison to any other release. -This release can be downloaded from `here `_. - This release brings with it many new features and bug fixes, a list of which can be seen later. One of the major new features is the effect system together with exception tracking which allows for checked exceptions and more, diff --git a/web/nimrod.ini b/web/nim.ini similarity index 81% rename from web/nimrod.ini rename to web/nim.ini index fc8a5730f..aa7b6d933 100644 --- a/web/nimrod.ini +++ b/web/nim.ini @@ -1,24 +1,28 @@ [Project] -Name: "Nimrod" -Title: "Nimrod Programming Language" +Name: "Nim" +Title: "Nim Programming Language" Logo: "efficient, expressive, elegant" Authors: "Andreas Rumpf and contributors" [Links] # Underscores are replaced with a space. # Everything after ; is the ID -User_Forum: "http://forum.nimrod-code.org;link_forum" +Community: "community.html;link_forum" Aporia_IDE: "https://github.com/nimrod-code/Aporia;link_aporia" -Nimbuild: "http://build.nimrod-code.org;link_nimbuild" +Github_Repo: "http://github.com/Araq/Nimrod;link_github" + [Tabs] # Menu entry: filename home: index -news: news docs: documentation +learn: learn download: download +forum: "http://forum.nim-lang.org" +faq: question +# these two are not in the list of "tabs", but do exist: community: community -FAQ: question +news: news [Ticker] file: ticker.txt @@ -34,21 +38,23 @@ documentation: """Incorrect documentation is often worse than no documentation. - Bertrand Meyer""" download: """There are two major products that come out of Berkeley: LSD and UNIX. We don't believe this to be a coincidence. - Jeremy S. Anderson.""" - +learn: """Repetition renders the ridiculous reasonable. - Norman Wildberger""" [Documentation] -doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview;filters" +doc: "endb;intern;apis;lib;manual.txt;tut1;tut2;nimc;overview;filters" doc: "tools;niminst;nimgrep;gc;estp;idetools;docgen;koch;backends.txt" -pdf: "manual;lib;tut1;tut2;nimrodc;niminst;gc" -srcdoc2: "system.nim;impure/graphics;wrappers/sdl" +doc: "nimfix.txt" +pdf: "manual.txt;lib;tut1;tut2;nimc;niminst;gc" +srcdoc2: "system.nim" srcdoc2: "core/macros;pure/marshal;core/typeinfo;core/unsigned" srcdoc2: "impure/re;pure/sockets;pure/typetraits" +srcdoc2: "pure/concurrency/threadpool.nim;pure/concurrency/cpuinfo.nim" srcdoc: "system/threads.nim;system/channels.nim;js/dom" srcdoc2: "pure/os;pure/strutils;pure/math;pure/matchers;pure/algorithm" srcdoc2: "pure/complex;pure/times;pure/osproc;pure/pegs;pure/dynlib" srcdoc2: "pure/parseopt;pure/parseopt2;pure/hashes;pure/strtabs;pure/lexbase" srcdoc2: "pure/parsecfg;pure/parsexml;pure/parsecsv;pure/parsesql" -srcdoc2: "pure/streams;pure/terminal;pure/cgi;impure/web;pure/unicode" +srcdoc2: "pure/streams;pure/terminal;pure/cgi;pure/unicode" srcdoc2: "impure/zipfiles;pure/htmlgen;pure/parseutils;pure/browsers" srcdoc2: "impure/db_postgres;impure/db_mysql;impure/db_sqlite" srcdoc2: "pure/httpserver;pure/httpclient;pure/smtp;impure/ssl;pure/fsmonitor" @@ -68,20 +74,21 @@ srcdoc2: "pure/rawsockets;pure/asynchttpserver;pure/net;pure/selectors;pure/futu srcdoc2: "wrappers/expat;wrappers/readline/history" srcdoc2: "wrappers/libsvm.nim;wrappers/libuv" srcdoc2: "wrappers/zip/zlib;wrappers/zip/libzip" -srcdoc2: "wrappers/libcurl;pure/md5;wrappers/mysql;wrappers/iup" +srcdoc2: "pure/md5;wrappers/mysql;wrappers/iup" srcdoc2: "posix/posix;wrappers/odbcsql" srcdoc2: "wrappers/tre;wrappers/openssl;wrappers/pcre" srcdoc2: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc" srcdoc2: "wrappers/readline/readline;wrappers/readline/rltypedefs" srcdoc2: "wrappers/joyent_http_parser" +srcdoc2: "pure/fenv" -webdoc: "wrappers/libcurl;pure/md5;wrappers/mysql;wrappers/iup" +webdoc: "pure/md5;wrappers/mysql;wrappers/iup" webdoc: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc" webdoc: "wrappers/expat;wrappers/pcre" webdoc: "wrappers/tre;wrappers/openssl" webdoc: "wrappers/libuv;wrappers/joyent_http_parser" -webdoc: "posix/posix;wrappers/odbcsql;impure/dialogs" +webdoc: "posix/posix;wrappers/odbcsql" webdoc: "wrappers/zip/zlib;wrappers/zip/libzip" webdoc: "wrappers/libsvm.nim" webdoc: "windows" diff --git a/web/babelpkglist.nim b/web/nimblepkglist.nim similarity index 87% rename from web/babelpkglist.nim rename to web/nimblepkglist.nim index 5da7d60a9..7070f281b 100644 --- a/web/babelpkglist.nim +++ b/web/nimblepkglist.nim @@ -10,7 +10,7 @@ proc decodeContent(content: string): string = if line != "": result.add decode(line) -proc contains(x: seq[PJSonNode], s: string): bool = +proc contains(x: seq[JSonNode], s: string): bool = for i in x: assert i.kind == JString if i.str == s: return true @@ -20,7 +20,7 @@ proc processContent(content: string) = assert jsonDoc.kind == JArray var jsonArr = jsonDoc.elems - jsonArr.sort do (x, y: PJsonNode) -> int: + jsonArr.sort do (x, y: JsonNode) -> int: strutils.cmpIgnoreCase(x["name"].str, y["name"].str) var @@ -41,6 +41,7 @@ proc processContent(content: string) = dot = if desc.high > 0 and desc[desc.high] in endings: "" else: "." listItem = li(a(href=pkgWeb, pkg["name"].str), " ", desc & dot) if pkg["url"].str.startsWith("git://github.com/nimrod-code") or + pkg["url"].str.startsWith("git://github.com/nim-lang") or "official" in pkg["tags"].elems: officialCount.inc officialList.add listItem & "\n" @@ -52,14 +53,14 @@ proc processContent(content: string) = officialPkgListDiv.innerHTML = p("There are currently " & $officialCount & - " official packages in the Babel package repository.") & + " official packages in the Nimble package repository.") & ul(officialList) var unofficialPkgListDiv = document.getElementById("unofficialPkgList") unofficialPkgListDiv.innerHTML = p("There are currently " & $unofficialCount & - " unofficial packages in the Babel package repository.") & + " unofficial packages in the Nimble package repository.") & ul(unofficialList) proc gotPackageList(apiReply: TData) {.exportc.} = diff --git a/web/question.txt b/web/question.txt index be90cc67a..d3a2dd5c0 100644 --- a/web/question.txt +++ b/web/question.txt @@ -6,135 +6,161 @@ General ======= -What is Nimrod? ---------------- -Nimrod is a statically typed, imperative programming language that tries to -give the programmer ultimate power without compromises on runtime efficiency. -This means it focuses on compile-time mechanisms in all their -various forms. Beneath a nice infix/indentation based syntax with a -powerful (AST based, hygienic) macro system lies a semantic model that supports -a soft realtime GC on thread local heaps. Asynchronous message passing is used -between threads, so no "stop the world" mechanism is necessary. An unsafe -shared memory heap is also provided for the increased efficiency that results -from that model. +.. container:: standout + + What is Nim? + ------------ + + Nim (formerly known as "Nimrod") is a statically typed, imperative programming + language that tries to give the programmer ultimate power without compromises + on runtime efficiency. + This means it focuses on compile-time mechanisms in all their + various forms. Beneath a nice infix/indentation based syntax with a + powerful (AST based, hygienic) macro system lies a semantic model that supports + a soft realtime GC on thread local heaps. Asynchronous message passing is used + between threads, so no "stop the world" mechanism is necessary. An unsafe + shared memory heap is also provided for the increased efficiency that results + from that model. -Why yet another programming language? -------------------------------------- +.. container:: standout -Nimrod is one of the very few *programmable* statically typed languages, and -one of the even fewer that produces native binaries that require no -runtime or interpreter. + Why yet another programming language? + ------------------------------------- + + Nim is one of the very few *programmable* statically typed languages, and + one of the even fewer that produces native binaries that require no + runtime or interpreter. -What have been the major influences in the language's design? -------------------------------------------------------------- +.. container:: standout -The language borrows heavily from: Modula 3, Delphi, Ada, C++, Python, Lisp, -Oberon. As far as possible the list is sorted by the impact of influence. + What have been the major influences in the language's design? + ------------------------------------------------------------- + + The language borrows heavily from (in order of impact): Modula 3, Delphi, Ada, + C++, Python, Lisp, Oberon. +.. container:: standout -What is Nimrod's take on concurrency? -------------------------------------- + What is Nim's take on concurrency? + ---------------------------------- -Nimrod primarily focusses on thread local (and garbage collected) heaps and -asynchronous message passing between threads. Each thread has its own GC, so no -"stop the world" mechanism is necessary. An unsafe shared memory heap is also -provided. + Nim primarily focusses on thread local (and garbage collected) heaps and + message passing between threads. Each thread has its own GC, so no + "stop the world" mechanism is necessary. An unsafe shared memory heap is also + provided. -Future versions will additionally include a GC "per thread group" -and Nimrod's type system will be enhanced to accurately model this shared -memory heap. + Future versions will additionally include a GC "per thread group" + and Nim's type system will be enhanced to accurately model this shared + memory heap. -How is Nimrod licensed? ------------------------ +.. container:: standout -The Nimrod compiler and the library are MIT licensed. -This means that you can use any license for your own programs developed with -Nimrod. + How is Nim licensed? + -------------------- + + The Nim compiler and the library are MIT licensed. + This means that you can use any license for your own programs developed with + Nim. -How stable is Nimrod? ---------------------- +.. container:: standout -The compiler is in development and some important features are still missing. -However, the compiler is quite stable already: It is able to compile itself -and a substantial body of other code. Until version 1.0.0 is released, -minor incompatibilities with older versions of the compiler will be introduced. + How stable is Nim? + ------------------ + + The compiler is in development and some important features are still missing. + However, the compiler is quite stable already: It is able to compile itself + and a substantial body of other code. Until version 1.0.0 is released, + minor incompatibilities with older versions of the compiler will be introduced. -How fast is Nimrod? -------------------- -Benchmarks show it to be comparable to C. Some language features (methods, -closures, message passing) are not yet as optimized as they could and will be. -The only overhead Nimrod has over C is the GC which has been tuned -for years but still needs some work. +.. container:: standout + + How fast is Nim? + ---------------- + Benchmarks show it to be comparable to C. Some language features (methods, + closures, message passing) are not yet as optimized as they could and will be. + The only overhead Nim has over C is the GC which has been tuned + for years but still needs some work. -What about JVM/CLR backends? ----------------------------- +.. container:: standout -A JVM backend is almost impossible. The JVM is not expressive enough. It has -never been designed as a general purpose VM anyway. A CLR backend is possible -but would require much work. + What about JVM/CLR backends? + ---------------------------- -What about editor support? --------------------------- - -- Nimrod IDE: https://github.com/nimrod-code/Aporia -- Emacs: https://github.com/Tass/nimrod-mode -- Vim: https://github.com/zah/nimrod.vim/ -- Scite: Included -- Gedit: The `Aporia .lang file `_ -- jEdit: https://github.com/exhu/nimrod-misc/tree/master/jedit + A JVM backend is almost impossible. The JVM is not expressive enough. It has + never been designed as a general purpose VM anyway. A CLR backend is possible + but would require much work. -Why is it named ``proc``? -------------------------- +.. container:: standout -*Procedure* used to be the common term as opposed to a *function* which is a -mathematical entity that has no side effects. It was planned to have ``func`` -as syntactic sugar for ``proc {.noSideEffect.}`` but with the more fine-grained -effect system it is not yet clear what ``func`` should be a shortcut for. + What about editor support? + -------------------------- + + - Nim IDE: https://github.com/nimrod-code/Aporia + - Emacs: https://github.com/Tass/nimrod-mode + - Vim: https://github.com/zah/nimrod.vim/ + - Scite: Included + - Gedit: The `Aporia .lang file `_ + - jEdit: https://github.com/exhu/nimrod-misc/tree/master/jedit + + +.. container:: standout + + Why is it named ``proc``? + ------------------------- + + *Procedure* used to be the common term as opposed to a *function* which is a + mathematical entity that has no side effects. It is planned to have ``func`` + as syntactic sugar for ``proc {.noSideEffect.}`` and ``func`` is already a + keyword. Compilation =========== -Which option to use for the fastest executable? ------------------------------------------------ +.. container:: standout -For the standard configuration file, ``-d:release`` does the trick. + Which option to use for the fastest executable? + ----------------------------------------------- + For the standard configuration file, ``-d:release`` does the trick. -Which option to use for the smallest executable? ------------------------------------------------- +.. container:: standout -For the standard configuration file, ``-d:quick --opt:size`` does the trick. + Which option to use for the smallest executable? + ------------------------------------------------ + For the standard configuration file, ``-d:quick --opt:size`` does the trick. -How do I use a different C compiler than the default one? ---------------------------------------------------------- +.. container:: standout -Edit the ``config/nimrod.cfg`` file. -Change the value of the ``cc`` variable to one of the following: + How do I use a different C compiler than the default one? + --------------------------------------------------------- -============== ============================================ -Abbreviation C/C++ Compiler -============== ============================================ -``vcc`` Microsoft's Visual C++ -``gcc`` Gnu C -``llvm_gcc`` LLVM-GCC compiler -``icc`` Intel C++ compiler -``clang`` Clang compiler -``ucc`` Generic UNIX C compiler -============== ============================================ + Edit the ``config/nimrod.cfg`` file. + Change the value of the ``cc`` variable to one of the following: -Other C compilers are not officially supported, but might work too. + ============== ============================================ + Abbreviation C/C++ Compiler + ============== ============================================ + ``vcc`` Microsoft's Visual C++ + ``gcc`` Gnu C + ``llvm_gcc`` LLVM-GCC compiler + ``icc`` Intel C++ compiler + ``clang`` Clang compiler + ``ucc`` Generic UNIX C compiler + ============== ============================================ -If your C compiler is not in the above list, try using the -*generic UNIX C compiler* (``ucc``). If the C compiler needs -different command line arguments try the ``--passc`` and ``--passl`` switches. + Other C compilers are not officially supported, but might work too. + + If your C compiler is not in the above list, try using the + *generic UNIX C compiler* (``ucc``). If the C compiler needs + different command line arguments try the ``--passc`` and ``--passl`` switches. diff --git a/web/ticker.txt b/web/ticker.txt index 98ad25905..64218084e 100644 --- a/web/ticker.txt +++ b/web/ticker.txt @@ -1,54 +1,21 @@ - -

      Oct 19, 2014

      -

      Nimrod version 0.9.6 has been released!

      +
      +

      Dec 29, 2014

      +

      Nim version 0.10.2 has been released!

      - -

      Apr 21, 2014

      -

      Nimrod version 0.9.4 has been released!

      +
      +

      Dec 9, 2014

      +

      The new website design and forum are now online!

      -

      Feb 11, 2014

      +

      Feb 11, 2014

      Nimrod featured in Dr. Dobb's Journal

      -

      Jan 15, 2014

      +

      Jan 15, 2014

      Andreas Rumpf's talk on Nimrod at Strange Loop 2013 is now online.

      - -

      May 20, 2013

      -

      New website design!

      -
      - - -

      May 20, 2013

      -

      Nimrod version 0.9.2 has been released!

      -
      - - -

      Sep 23, 2012

      -

      Nimrod version 0.9.0 has been released!

      -
      - - -

      Feb 9, 2012

      -

      Nimrod version 0.8.14 has been released!

      -
      - - -

      Jul 10, 2011

      -

      Nimrod version 0.8.12 has been released!

      -
      - - -

      Oct 20, 2010

      -

      Nimrod version 0.8.10 has been released!

      -
      - - -

      Mar 14, 2010

      -

      Nimrod version 0.8.8 has been released!

      -
      +See All News...