diff options
Diffstat (limited to 'lib')
-rwxr-xr-x | lib/core/marshal.nim | 275 | ||||
-rw-r--r-- | lib/core/typeinfo.nim | 243 | ||||
-rwxr-xr-x | lib/pure/json.nim | 11 | ||||
-rwxr-xr-x | lib/pure/marshal.nim | 288 | ||||
-rwxr-xr-x | lib/pure/streams.nim | 5 | ||||
-rwxr-xr-x | lib/pure/strutils.nim | 80 | ||||
-rwxr-xr-x | lib/pure/xmltree.nim | 3 | ||||
-rwxr-xr-x | lib/system.nim | 3 | ||||
-rwxr-xr-x | lib/system/gc.nim | 8 | ||||
-rwxr-xr-x | lib/system/repr.nim | 3 | ||||
-rwxr-xr-x | lib/system/sysio.nim | 10 | ||||
-rwxr-xr-x | lib/system/sysstr.nim | 38 | ||||
-rwxr-xr-x | lib/system/threads.nim | 18 |
13 files changed, 598 insertions, 387 deletions
diff --git a/lib/core/marshal.nim b/lib/core/marshal.nim deleted file mode 100755 index 26264b1a8..000000000 --- a/lib/core/marshal.nim +++ /dev/null @@ -1,275 +0,0 @@ -# -# -# Nimrod's Runtime Library -# (c) Copyright 2011 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. XXX This is not implemented yet! - -import streams - -proc load*[T](s: PStream, data: var T) = - ## loads `data` from the stream `s`. Raises `EIO` in case of an error. - -proc store*[T](s: PStream, data: T) = - ## stores `data` into the stream `s`. Raises `EIO` in case of an error. - - -type - TTypeInfo = distinct whatever - - TValue = object - t: TTypeInfo - x: pointer - - -proc rtti[T](x: T): TTypeInfo {.magic: "rtti".} - -proc `[]` (a: TValue, i: int): TValue = - ## works for arrays, objects, etc. - -proc `[]=` (a: TValue, i: int, x: TValue) = - ## - - - - -proc reprPointer(x: pointer): string {.compilerproc.} = - var buf: array [0..59, char] - c_sprintf(buf, "%p", x) - return $buf - -proc reprStrAux(result: var string, s: string) = - if cast[pointer](s) == nil: - add result, "nil" - return - add result, reprPointer(cast[pointer](s)) & "\"" - for c in items(s): - case c - of '"': add result, "\\\"" - of '\\': add result, "\\\\" # BUGFIX: forgotten - of '\10': add result, "\\10\"\n\"" # " \n " # better readability - of '\128' .. '\255', '\0'..'\9', '\11'..'\31': - add result, "\\" & reprInt(ord(c)) - else: result.add(c) - add result, "\"" - -proc reprStr(s: string): string {.compilerRtl.} = - result = "" - reprStrAux(result, s) - -proc reprBool(x: bool): string {.compilerRtl.} = - if x: result = "true" - else: result = "false" - -proc reprChar(x: char): string {.compilerRtl.} = - result = "\'" - case x - of '"': add result, "\\\"" - of '\\': add result, "\\\\" - of '\128' .. '\255', '\0'..'\31': add result, "\\" & reprInt(ord(x)) - else: add result, x - add result, "\'" - -proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = - if e <% typ.node.len: # BUGFIX - result = $typ.node.sons[e].name - else: - result = $e & " (invalid data!)" - -type - pbyteArray = ptr array[0.. 0xffff, byte] - -proc addSetElem(result: var string, elem: int, typ: PNimType) = - case typ.kind - of tyEnum: add result, reprEnum(elem, typ) - of tyBool: add result, reprBool(bool(elem)) - of tyChar: add result, reprChar(chr(elem)) - of tyRange: addSetElem(result, elem, typ.base) - of tyInt..tyInt64: add result, reprInt(elem) - else: # data corrupt --> inform the user - add result, " (invalid data!)" - -proc reprSetAux(result: var string, p: pointer, typ: PNimType) = - # "typ.slots.len" field is for sets the "first" field - var elemCounter = 0 # we need this flag for adding the comma at - # the right places - add result, "{" - var u: int64 - case typ.size - of 1: u = ze64(cast[ptr int8](p)[]) - of 2: u = ze64(cast[ptr int16](p)[]) - of 4: u = ze64(cast[ptr int32](p)[]) - of 8: u = cast[ptr int64](p)[] - else: - var a = cast[pbyteArray](p) - for i in 0 .. typ.size*8-1: - if (ze(a[i div 8]) and (1 shl (i mod 8))) != 0: - if elemCounter > 0: add result, ", " - addSetElem(result, i+typ.node.len, typ.base) - inc(elemCounter) - if typ.size <= 8: - for i in 0..sizeof(int64)*8-1: - if (u and (1 shl i)) != 0: - if elemCounter > 0: add result, ", " - addSetElem(result, i+typ.node.len, typ.base) - inc(elemCounter) - add result, "}" - -proc reprSet(p: pointer, typ: PNimType): string {.compilerRtl.} = - result = "" - reprSetAux(result, p, typ) - -type - TReprClosure {.final.} = object # we cannot use a global variable here - # as this wouldn't be thread-safe - marked: TCellSet - recdepth: int # do not recurse endless - indent: int # indentation - -when not defined(useNimRtl): - proc initReprClosure(cl: var TReprClosure) = - Init(cl.marked) - cl.recdepth = -1 # default is to display everything! - cl.indent = 0 - - proc deinitReprClosure(cl: var TReprClosure) = - Deinit(cl.marked) - - proc reprBreak(result: var string, cl: TReprClosure) = - add result, "\n" - for i in 0..cl.indent-1: add result, ' ' - - proc reprAux(result: var string, p: pointer, typ: PNimType, - cl: var TReprClosure) - - proc reprArray(result: var string, p: pointer, typ: PNimType, - cl: var TReprClosure) = - add result, "[" - 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) - add result, "]" - - proc reprSequence(result: var string, p: pointer, typ: PNimType, - cl: var TReprClosure) = - if p == nil: - add result, "nil" - return - result.add(reprPointer(p) & "[") - 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), - typ.Base, cl) - add result, "]" - - proc reprRecordAux(result: var string, p: pointer, n: ptr TNimNode, - cl: var TReprClosure) = - case n.kind - of nkNone: assert(false) - of nkSlot: - add result, $n.name - add result, " = " - reprAux(result, cast[pointer](cast[TAddress](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) - if m != nil: reprRecordAux(result, p, m, cl) - - proc reprRecord(result: var string, p: pointer, typ: PNimType, - cl: var TReprClosure) = - add result, "[" - reprRecordAux(result, p, typ.node, cl) - add result, "]" - - proc reprRef(result: var string, p: pointer, typ: PNimType, - cl: var TReprClosure) = - # we know that p is not nil here: - when defined(boehmGC) or defined(nogc): - var cell = cast[PCell](p) - else: - var cell = usrToCell(p) - add result, "ref " & reprPointer(p) - if cell notin cl.marked: - # only the address is shown: - incl(cl.marked, cell) - add result, " --> " - reprAux(result, p, typ.base, cl) - - proc reprAux(result: var string, p: pointer, typ: PNimType, - cl: var TReprClosure) = - if cl.recdepth == 0: - add result, "..." - return - dec(cl.recdepth) - case typ.kind - of tySet: reprSetAux(result, p, typ) - of tyArray: reprArray(result, p, typ, cl) - of tyTuple, tyPureObject: reprRecord(result, p, typ, cl) - of tyObject: - var t = cast[ptr PNimType](p)[] - reprRecord(result, p, t, cl) - of tyRef, tyPtr: - assert(p != nil) - if cast[ppointer](p)[] == nil: add result, "nil" - else: reprRef(result, cast[ppointer](p)[], typ, cl) - of tySequence: - reprSequence(result, cast[ppointer](p)[], typ, cl) - of tyInt: add result, $(cast[ptr int](p)[]) - of tyInt8: add result, $int(cast[ptr Int8](p)[]) - of tyInt16: add result, $int(cast[ptr Int16](p)[]) - of tyInt32: add result, $int(cast[ptr Int32](p)[]) - of tyInt64: add result, $(cast[ptr Int64](p)[]) - of tyFloat: add result, $(cast[ptr float](p)[]) - of tyFloat32: add result, $(cast[ptr float32](p)[]) - of tyFloat64: add result, $(cast[ptr float64](p)[]) - of tyEnum: add result, reprEnum(cast[ptr int](p)[], typ) - of tyBool: add result, reprBool(cast[ptr bool](p)[]) - of tyChar: add result, reprChar(cast[ptr char](p)[]) - of tyString: reprStrAux(result, cast[ptr string](p)[]) - of tyCString: reprStrAux(result, $(cast[ptr cstring](p)[])) - of tyRange: reprAux(result, p, typ.base, cl) - of tyProc, tyPointer: - if cast[ppointer](p)[] == nil: add result, "nil" - else: add result, reprPointer(cast[ppointer](p)[]) - else: - add result, "(invalid data!)" - inc(cl.recdepth) - -proc reprOpenArray(p: pointer, length: int, elemtyp: PNimType): string {. - compilerRtl.} = - var - cl: TReprClosure - initReprClosure(cl) - result = "[" - 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) - add result, "]" - deinitReprClosure(cl) - -when not defined(useNimRtl): - proc reprAny(p: pointer, typ: PNimType): string = - var - cl: TReprClosure - initReprClosure(cl) - result = "" - if typ.kind in {tyObject, tyPureObject, tyTuple, tyArray, tySet}: - reprAux(result, p, typ, cl) - else: - var p = p - reprAux(result, addr(p), typ, cl) - add result, "\n" - deinitReprClosure(cl) - diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 9fc34a7bd..9de3fde16 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -11,8 +11,6 @@ ## Note that even though ``TAny`` and its operations hide the nasty low level ## details from its clients, it remains inherently unsafe! -# XXX raw pointer needs to be exposed somehow? - include "system/hti.nim" type @@ -42,7 +40,7 @@ type akFloat32 = 37, ## any represents a float32 akFloat64 = 38, ## any represents a float64 akFloat128 = 39, ## any represents a float128 - akPureObject = 40 ## any represents an object has no `type` field + akPureObject = 40 ## any represents an object that has no `type` field TAny* = object {.pure.} ## can represent any nimrod value; NOTE: the wrapped ## value can be modified with its wrapper! This means @@ -63,6 +61,13 @@ const GenericSeqSize = (2 * sizeof(int)) proc genericAssign(dest, src: Pointer, mt: PNimType) {.importc.} +proc genericShallowAssign(dest, src: Pointer, mt: PNimType) {.importc.} +proc incrSeq(seq: PGenSeq, elemSize: int): PGenSeq {.importc, nodecl.} +proc newObj(typ: PNimType, size: int): pointer {.importc, nodecl.} +proc newSeq(typ: PNimType, len: int): pointer {.importc.} +proc objectInit(dest: Pointer, typ: PNimType) {.importc.} + +template `+!!`(a, b: expr): expr = cast[pointer](cast[TAddress](a) + b) proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int = assert(n.kind == nkCase) @@ -95,28 +100,61 @@ proc toAny*[T](x: var T): TAny {.inline.} = result.value = addr(x) result.rawType = cast[PNimType](getTypeInfo(x)) -proc getKind*(x: TAny): TAnyKind {.inline.} = +proc kind*(x: TAny): TAnyKind {.inline.} = ## get the type kind result = TAnyKind(ord(x.rawType.kind)) + +proc baseTypeKind*(x: TAny): TAnyKind {.inline.} = + ## get the base type's kind; akNone is returned if `x` has no base type. + if x.rawType.base != nil: + result = TAnyKind(ord(x.rawType.base.kind)) + +proc baseTypeSize*(x: TAny): int = + ## returns the size of `x`'s basetype. + if x.rawType.base != nil: + result = x.rawType.base.size + +proc invokeNew*(x: TAny) = + ## performs ``new(x)``. `x` needs to represent a ``ref``. + assert x.rawType.kind == tyRef + var z = newObj(x.rawType, x.rawType.base.size) + genericAssign(x.value, addr(z), x.rawType) + +proc invokeNewSeq*(x: TAny, len: int) = + ## performs ``newSeq(x, len)``. `x` needs to represent a ``seq``. + assert x.rawType.kind == tySequence + var z = newSeq(x.rawType, len) + genericShallowAssign(x.value, addr(z), x.rawType) + +proc extendSeq*(x: TAny, elems = 1) = + ## performs ``setLen(x, x.len+elems)``. `x` needs to represent a ``seq``. + assert x.rawType.kind == tySequence + var y = cast[ptr PGenSeq](x.value)[] + var z = incrSeq(y, x.rawType.base.size * elems) + genericShallowAssign(x.value, addr(z), x.rawType) + +proc setObjectRuntimeType*(x: TAny) = + ## this needs to be called to set `x`'s runtime object type field. + assert x.rawType.kind == tyObject + objectInit(x.value, x.rawType) proc skipRange(x: PNimType): PNimType {.inline.} = result = x if result.kind == tyRange: result = result.base -template `+!!`(a, b: expr): expr = cast[pointer](cast[TAddress](a) + b) - proc `[]`*(x: TAny, i: int): TAny = ## accessor for an any `x` that represents an array or a sequence. case x.rawType.kind of tyArray: var bs = x.rawType.base.size - if i >% (x.rawType.size div bs - 1): + if i >=% x.rawType.size div bs: raise newException(EInvalidIndex, "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") var bs = x.rawType.base.size - if i >% (cast[PGenSeq](s).len-1): + if i >=% cast[PGenSeq](s).len: raise newException(EInvalidIndex, "index out of bounds") return newAny(s +!! (GenericSeqSize+i*bs), x.rawType.base) else: assert false @@ -126,14 +164,15 @@ proc `[]=`*(x: TAny, i: int, y: TAny) = case x.rawType.kind of tyArray: var bs = x.rawType.base.size - if i >% (x.rawType.size div bs - 1): + if i >=% x.rawType.size div bs: raise newException(EInvalidIndex, "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") var bs = x.rawType.base.size - if i >% (cast[PGenSeq](s).len-1): + if i >=% cast[PGenSeq](s).len: raise newException(EInvalidIndex, "index out of bounds") assert y.rawType == x.rawType.base genericAssign(s +!! (GenericSeqSize+i*bs), y.value, y.rawType) @@ -146,6 +185,29 @@ proc len*(x: TAny): int = of tySequence: result = cast[PGenSeq](cast[ppointer](x.value)[]).len else: assert false +proc isNil*(x: TAny): bool = + ## `isNil` for an any `x` that represents a sequence, string, cstring, + ## proc or some pointer type. + assert x.rawType.kind in {tyString, tyCString, tyRef, tyPtr, tyPointer, + tySequence, tyProc} + result = isNil(cast[ppointer](x.value)[]) + +proc getPointer*(x: TAny): pointer = + ## retrieve the pointer value out of `x`. ``x`` needs to be of kind + ## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``, + ## ``akPointer``, ``akSequence``. + assert x.rawType.kind in {tyString, tyCString, tyRef, tyPtr, tyPointer, + tySequence, tyProc} + result = cast[ppointer](x.value)[] + +proc setPointer*(x: TAny, y: pointer) = + ## sets the pointer value of `x`. ``x`` needs to be of kind + ## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``, + ## ``akPointer``, ``akSequence``. + assert x.rawType.kind in {tyString, tyCString, tyRef, tyPtr, tyPointer, + tySequence, tyProc} + cast[ppointer](x.value)[] = y + proc fieldsAux(p: pointer, n: ptr TNimNode, ret: var seq[tuple[name: cstring, any: TAny]]) = case n.kind @@ -173,6 +235,64 @@ iterator fields*(x: TAny): tuple[name: string, any: TAny] = for name, any in items(ret): yield ($name, any) +proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} = + proc toLower(c: char): char {.inline.} = + if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A'))) + else: result = c + var i = 0 + var j = 0 + while True: + while a[i] == '_': inc(i) + while b[j] == '_': inc(j) # BUGFIX: typo + var aa = toLower(a[i]) + var bb = toLower(b[j]) + result = ord(aa) - ord(bb) + if result != 0 or aa == '\0': break + inc(i) + inc(j) + +proc getFieldNode(p: pointer, n: ptr TNimNode, + name: cstring): ptr TNimNode = + case n.kind + of nkNone: assert(false) + of nkSlot: + if cmpIgnoreStyle(n.name, name) == 0: + result = n + of nkList: + for i in 0..n.len-1: + result = getFieldNode(p, n.sons[i], name) + if result != nil: break + of nkCase: + if cmpIgnoreStyle(n.name, name) == 0: + result = n + else: + var m = selectBranch(p, n) + if m != nil: result = getFieldNode(p, m, name) + +proc `[]=`*(x: TAny, fieldName: string, value: TAny) = + ## sets a field of `x`; `x` represents an object or a tuple. + var t = x.rawType + if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[] + assert x.rawType.kind in {tyTuple, tyPureObject, tyObject} + 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) + +proc `[]`*(x: TAny, fieldName: string): TAny = + ## gets a field of `x`; `x` represents an object or a tuple. + var t = x.rawType + if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[] + assert x.rawType.kind in {tyTuple, tyPureObject, tyObject} + 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) + proc `[]`*(x: TAny): TAny = ## dereference operation for the any `x` that represents a ptr or a ref. assert x.rawtype.kind in {tyRef, tyPtr} @@ -232,6 +352,27 @@ proc getBiggestInt*(x: TAny): biggestInt = else: assert false else: assert false +proc setBiggestInt*(x: TAny, y: biggestInt) = + ## sets the integer value of `x`. `x` needs to represent + ## some integer, a bool, a char or an enum. + 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) + of tyInt16: cast[ptr int16](x.value)[] = int16(y) + of tyInt32: cast[ptr int32](x.value)[] = int32(y) + of tyInt64: cast[ptr int64](x.value)[] = int64(y) + of tyBool: cast[ptr bool](x.value)[] = y != 0 + of tyChar: cast[ptr char](x.value)[] = chr(y.int) + of tyEnum: + case t.size + of 1: cast[ptr int8](x.value)[] = toU8(y.int) + of 2: cast[ptr int16](x.value)[] = toU16(y.int) + of 4: cast[ptr int32](x.value)[] = int32(y) + of 8: cast[ptr int64](x.value)[] = y + else: assert false + else: assert false + proc getChar*(x: TAny): char = ## retrieve the char value out of `x`. `x` needs to represent a char. var t = skipRange(x.rawtype) @@ -244,11 +385,35 @@ proc getBool*(x: TAny): bool = assert t.kind == tyBool result = cast[ptr bool](x.value)[] -proc getEnumField*(x: TAny): string = - ## gets the enum field name as a string. `x` needs to represent an enum. +proc skipRange*(x: TAny): TAny = + ## skips the range information of `x`. + assert x.rawType.kind == tyRange + result.rawType = x.rawType.base + result.value = x.value + +proc getEnumOrdinal*(x: TAny, name: string): int = + ## 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) assert typ.kind == tyEnum - var e = int(getBiggestInt(x)) + var n = typ.node + var s = n.sons + for i in 0 .. n.len-1: + if cmpIgnoreStyle($s[i].name, name) == 0: + if ntfEnumHole notin typ.flags: + return i + else: + return s[i].offset + result = low(int) + +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) + assert typ.kind == tyEnum + var e = ordinalValue if ntfEnumHole notin typ.flags: if e <% typ.node.len: return $typ.node.sons[e].name @@ -258,7 +423,11 @@ proc getEnumField*(x: TAny): string = var s = n.sons for i in 0 .. n.len-1: if s[i].offset == e: return $s[i].name - result = $e & " (invalid data!)" + result = $e + +proc getEnumField*(x: TAny): string = + ## gets the enum field name as a string. `x` needs to represent an enum. + result = getEnumField(x, getBiggestInt(x).int) proc getFloat*(x: TAny): float = ## retrieve the float value out of `x`. `x` needs to represent an float. @@ -284,11 +453,30 @@ proc getBiggestFloat*(x: TAny): biggestFloat = of tyFloat64: result = biggestFloat(cast[ptr Float64](x.value)[]) else: assert false +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 + 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 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 + 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 + result = cast[ptr cstring](x.value)[] + proc assign*(x, y: TAny) = ## copies the value of `y` to `x`. The assignment operator for ``TAny`` ## does NOT do this; it performs a shallow copy instead! @@ -339,7 +527,7 @@ proc inclSetElement*(x: TAny, elem: int) = a[] = a[] or (1'i64 shl e) else: var a = cast[pbyteArray](p) - a[e div 8] = toU8(a[e div 8] or (1 shl (e mod 8))) + a[e shr 3] = toU8(a[e shr 3] or (1 shl (e and 7))) when isMainModule: type @@ -365,8 +553,8 @@ when isMainModule: var i = 0 for n, a in fields(x2): case i - of 0: assert n == "name" and $a.getKind == "akString" - of 1: assert n == "s" and $a.getKind == "akInt" + of 0: assert n == "name" and $a.kind == "akString" + of 1: assert n == "s" and $a.kind == "akInt" else: assert false inc i @@ -377,9 +565,9 @@ when isMainModule: i = 0 for n, a in fields(x3): case i - of 0: assert n == "test" and $a.getKind == "akInt" - of 1: assert n == "asd" and $a.getKind == "akInt" - of 2: assert n == "test2" and $a.getKind == "akEnum" + of 0: assert n == "test" and $a.kind == "akInt" + of 1: assert n == "asd" and $a.kind == "akInt" + of 2: assert n == "test2" and $a.kind == "akEnum" else: assert false inc i @@ -387,4 +575,17 @@ when isMainModule: new(test4) test4[] = "test" var x4 = toAny(test4) - assert($x4[].getKind() == "akString") + assert($x4[].kind() == "akString") + + block: + # gimme a new scope dammit + var myarr: 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"]] + var m = toAny(myArr) + for i in 0 .. m.len-1: + for j in 0 .. m[i].len-1: + echo getString(m[i][j]) + + diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 7b2707784..7c4961b61 100755 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -22,7 +22,7 @@ type jsonError, ## an error ocurred during parsing jsonEof, ## end of file reached jsonString, ## a string literal - jsonInt, ## a integer literal + jsonInt, ## an integer literal jsonFloat, ## a float literal jsonTrue, ## the value ``true`` jsonFalse, ## the value ``false`` @@ -121,7 +121,7 @@ proc str*(my: TJsonParser): string {.inline.} = proc getInt*(my: TJsonParser): biggestInt {.inline.} = ## returns the number for the event: ``jsonInt`` assert(my.kind == jsonInt) - return parseInt(my.a) + return parseBiggestInt(my.a) proc getFloat*(my: TJsonParser): float {.inline.} = ## returns the number for the event: ``jsonFloat`` @@ -512,9 +512,10 @@ type of JArray: elems*: seq[PJsonNode] - EJsonParsingError* = object of EInvalidValue + EJsonParsingError* = object of EInvalidValue ## is raised for a JSON error -proc raiseParseErr(p: TJsonParser, msg: string) = +proc raiseParseErr*(p: TJsonParser, msg: string) {.noinline, noreturn.} = + ## raises an `EJsonParsingError` exception. raise newException(EJsonParsingError, errorMsgExpected(p, msg)) proc newJString*(s: String): PJsonNode = @@ -607,6 +608,7 @@ proc `[]=`*(obj: PJsonNode, key: String, val: PJsonNode) = obj.fields.add((key, val)) proc delete*(obj: PJsonNode, key: string) = + ## Deletes ``obj[key]`` preserving the order of the other (key, value)-pairs. assert(obj.kind == JObject) for i in 0..obj.fields.len-1: if obj.fields[i].key == key: @@ -615,6 +617,7 @@ proc delete*(obj: PJsonNode, key: string) = raise newException(EInvalidIndex, "key not in object") proc copy*(p: PJsonNode): PJsonNode = + ## Performs a deep copy of `a`. case p.kind of JString: result = newJString(p.str) diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim new file mode 100755 index 000000000..f96d177ae --- /dev/null +++ b/lib/pure/marshal.nim @@ -0,0 +1,288 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2011 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. + +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, akPureObject, 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: 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, akPureObject, 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: + 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: int] = ("tuple test", 56) + 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) + diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index d0e6ecec7..242e40d83 100755 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -33,8 +33,9 @@ proc write*[T](s: PStream, x: T) = ## .. code-block:: Nimrod ## ## s.writeData(s, addr(x), sizeof(x)) - var x = x - s.writeData(s, addr(x), sizeof(x)) + var y: T + shallowCopy(y, x) + s.writeData(s, addr(y), sizeof(y)) proc write*(s: PStream, x: string) = ## writes the string `x` to the the stream `s`. No length field or diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 8659c7d29..58e1e5fed 100755 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -89,15 +89,15 @@ proc normalize*(s: string): string {.noSideEffect, procvar, rtl, extern: "nsuNormalize".} = ## Normalizes the string `s`. That means to convert it to lower case and ## remove any '_'. This is needed for Nimrod identifiers for example. - result = newString(s.len) + result = newString(s.len) var j = 0 for i in 0..len(s) - 1: if s[i] in {'A'..'Z'}: - result[j] = Chr(Ord(s[i]) + (Ord('a') - Ord('A'))) + result[j] = Chr(Ord(s[i]) + (Ord('a') - Ord('A'))) inc j elif s[i] != '_': - result[j] = s[i] - inc j + result[j] = s[i] + inc j if j != s.len: setLen(result, j) proc cmpIgnoreCase*(a, b: string): int {.noSideEffect, @@ -461,7 +461,7 @@ proc repeatChar*(count: int, c: Char = ' '): string {.noSideEffect, ## the character `c`. result = newString(count) for i in 0..count-1: result[i] = c - + proc repeatStr*(count: int, s: string): string {.noSideEffect, rtl, extern: "nsuRepeatStr".} = ## Returns `s` concatenated `count` times. @@ -480,40 +480,40 @@ proc align*(s: string, count: int): string {. for i in spaces..count-1: result[i] = s[i-spaces] else: result = s - -iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[ - token: string, isSep: bool] = - ## Tokenizes the string `s` into substrings. - ## - ## Substrings are separated by a substring containing only `seps`. - ## Examples: - ## - ## .. code-block:: nimrod - ## for word in tokenize(" this is an example "): - ## writeln(stdout, word) - ## - ## Results in: - ## - ## .. code-block:: nimrod - ## (" ", true) - ## ("this", false) - ## (" ", true) - ## ("is", false) - ## (" ", true) - ## ("an", false) - ## (" ", true) - ## ("example", false) - ## (" ", true) - var i = 0 - while true: - var j = i - var isSep = s[j] in seps - while j < s.len and (s[j] in seps) == isSep: inc(j) - if j > i: - yield (substr(s, i, j-1), isSep) - else: - break - i = j + +iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[ + token: string, isSep: bool] = + ## Tokenizes the string `s` into substrings. + ## + ## Substrings are separated by a substring containing only `seps`. + ## Examples: + ## + ## .. code-block:: nimrod + ## for word in tokenize(" this is an example "): + ## writeln(stdout, word) + ## + ## Results in: + ## + ## .. code-block:: nimrod + ## (" ", true) + ## ("this", false) + ## (" ", true) + ## ("is", false) + ## (" ", true) + ## ("an", false) + ## (" ", true) + ## ("example", false) + ## (" ", true) + var i = 0 + while true: + var j = i + var isSep = s[j] in seps + while j < s.len and (s[j] in seps) == isSep: inc(j) + if j > i: + yield (substr(s, i, j-1), isSep) + else: + break + i = j proc wordWrap*(s: string, maxLineWidth = 80, splitLongWords = true, @@ -815,7 +815,7 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect, ## The procedure has been designed so that its output is usable for many ## different common syntaxes. The resulting string is prefixed with ## `prefix` and suffixed with `suffix`. Both may be empty strings. - result = newStringOfCap(s.len + s.len shr 2) + result = newStringOfCap(s.len + s.len shr 2) result.add(prefix) for c in items(s): case c diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index 41765b87a..7f6d5cff8 100755 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -145,8 +145,7 @@ proc escape*(s: string): string = ## ``&`` ``&`` ## ``"`` ``"`` ## ------------ ------------------- - result = newString(s.len) - setLen(result, 0) + result = newStringOfCap(s.len) addEscaped(result, s) proc addIndent(result: var string, indent: int) = diff --git a/lib/system.nim b/lib/system.nim index af948265f..e19a9749d 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -1612,8 +1612,7 @@ when not defined(EcmaScript) and not defined(NimrodVM): proc readLine*(f: TFile): string ## reads a line of text from the file `f`. May throw an IO exception. - ## Reading from an empty file buffer, does not throw an exception, but - ## returns nil. A line of text may be delimited by ``CR``, ``LF`` or + ## A line of text may be delimited by ``CR``, ``LF`` or ## ``CRLF``. The newline character(s) are not part of the returned string. proc writeln*[Ty](f: TFile, x: Ty) {.inline.} diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 033a7bdbe..619b1c9b1 100755 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -198,18 +198,18 @@ proc prepareDealloc(cell: PCell) = proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} = # we MUST access gch as a global here, because this crosses DLL boundaries! - when hasThreadSupport: + when hasThreadSupport and hasSharedHeap: AcquireSys(HeapLock) incl(gch.cycleRoots, c) - when hasThreadSupport: + when hasThreadSupport and hasSharedHeap: ReleaseSys(HeapLock) proc rtlAddZCT(c: PCell) {.rtl, inl.} = # we MUST access gch as a global here, because this crosses DLL boundaries! - when hasThreadSupport: + when hasThreadSupport and hasSharedHeap: AcquireSys(HeapLock) addZCT(gch.zct, c) - when hasThreadSupport: + when hasThreadSupport and hasSharedHeap: ReleaseSys(HeapLock) proc decRef(c: PCell) {.inline.} = diff --git a/lib/system/repr.nim b/lib/system/repr.nim index add0af10c..256313ebd 100755 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -9,9 +9,6 @@ # The generic ``repr`` procedure. It is an invaluable debugging tool. -#proc cstrToNimStrDummy(s: cstring): string {.inline.} = -# result = cast[string](cstrToNimStr(s)) - proc reprInt(x: int64): string {.compilerproc.} = return $x proc reprFloat(x: float): string {.compilerproc.} = return $x diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index af6ba49b2..cb4aaae54 100755 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -36,13 +36,16 @@ var IOFBF {.importc: "_IOFBF", nodecl.}: cint IONBF {.importc: "_IONBF", nodecl.}: cint +proc raiseEIO(msg: string) {.noinline, noreturn.} = + raise newException(EIO, msg) + proc rawReadLine(f: TFile, result: var string) = # 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(result, 0) # reuse the buffer! while True: var c = fgetc(f) - if c < 0'i32: break # EOF + if c < 0'i32: raiseEIO("EOF reached") if c == 10'i32: break # LF if c == 13'i32: # CR c = fgetc(f) # is the next char LF? @@ -76,11 +79,6 @@ proc write(f: TFile, c: Char) = putc(c, f) proc write(f: TFile, a: openArray[string]) = for x in items(a): write(f, x) -#{.error: "for debugging.".} - -proc raiseEIO(msg: string) {.noinline, noreturn.} = - raise newException(EIO, msg) - proc readFile(filename: string): string = var f = open(filename) try: diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 2afb91d7b..e8d351899 100755 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -15,12 +15,10 @@ # we don't use refcounts because that's a behaviour # the programmer may not want -# implementation: - proc resize(old: int): int {.inline.} = - if old <= 0: return 4 - elif old < 65536: return old * 2 - else: return old * 3 div 2 # for large arrays * 3/2 is better + if old <= 0: result = 4 + elif old < 65536: result = old * 2 + else: result = old * 3 div 2 # for large arrays * 3/2 is better proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} = if a == b: return 0 @@ -53,13 +51,13 @@ proc toNimStr(str: CString, len: int): NimString {.compilerProc.} = result.data[len] = '\0' # readline relies on this! proc cstrToNimstr(str: CString): NimString {.compilerProc.} = - return toNimstr(str, c_strlen(str)) + result = toNimstr(str, c_strlen(str)) proc copyString(src: NimString): NimString {.compilerProc.} = - if src == nil: return nil - result = rawNewString(src.space) - result.len = src.len - c_memcpy(result.data, src.data, (src.len + 1) * sizeof(Char)) + if src != nil: + result = rawNewString(src.space) + result.len = src.len + c_memcpy(result.data, src.data, (src.len + 1) * sizeof(Char)) proc hashString(s: string): int {.compilerproc.} = # the compiler needs exactly the same hash function! @@ -86,7 +84,7 @@ proc copyStrLast(s: NimString, start, last: int): NimString {.exportc.} = result = mnewString(0) proc copyStr(s: NimString, start: int): NimString {.exportc.} = - return copyStrLast(s, start, s.len-1) + result = copyStrLast(s, start, s.len-1) proc addChar(s: NimString, c: char): NimString = # is compilerproc! @@ -135,7 +133,7 @@ proc addChar(s: NimString, c: char): NimString = # s = rawNewString(0); proc resizeString(dest: NimString, addlen: int): NimString {.compilerproc.} = - if dest.len + addLen + 1 <= dest.space: # BUGFIX: this is horrible! + if dest.len + addLen + 1 <= dest.space: result = dest else: # slow path: var sp = max(resize(dest.space), dest.len + addLen + 1) @@ -279,12 +277,12 @@ proc binaryStrSearch(x: openarray[string], y: string): int {.compilerproc.} = a = 0 b = len(x) while a < b: - var mid = (a + b) div 2 - if x[mid] < y: - a = mid + 1 - else: - b = mid - if (a < len(x)) and (x[a] == y): - return a + var mid = (a + b) div 2 + if x[mid] < y: + a = mid + 1 + else: + b = mid + if a < len(x) and x[a] == y: + result = a else: - return -1 + result = -1 diff --git a/lib/system/threads.nim b/lib/system/threads.nim index db16502ff..7478b89d0 100755 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -339,14 +339,16 @@ proc joinThreads*[TParam](t: openArray[TThread[TParam]]) = else: for i in 0..t.high: joinThread(t[i]) -proc destroyThread*[TParam](t: var TThread[TParam]) {.inline.} = - ## forces the thread `t` to terminate. This is potentially dangerous if - ## you don't have full control over `t` and its acquired resources. - when hostOS == "windows": - discard TerminateThread(t.sys, 1'i32) - else: - discard pthread_cancel(t.sys) - unregisterThread(addr(t.gcInfo)) +when false: + # XXX a thread should really release its heap here somehow: + proc destroyThread*[TParam](t: var TThread[TParam]) {.inline.} = + ## forces the thread `t` to terminate. This is potentially dangerous if + ## you don't have full control over `t` and its acquired resources. + when hostOS == "windows": + discard TerminateThread(t.sys, 1'i32) + else: + discard pthread_cancel(t.sys) + unregisterThread(addr(t)) proc createThread*[TParam](t: var TThread[TParam], tp: proc (param: TParam), |