code gen bugfixes; marshal.nim implemented

This commit is contained in:
Araq
2011-06-26 17:21:52 +02:00
parent db0a4a9f86
commit 990dc2d715
21 changed files with 725 additions and 422 deletions

View File

@@ -61,18 +61,20 @@ proc getTypeName(typ: PType): PRope =
result = typ.loc.r
if result == nil: InternalError("getTypeName: " & $typ.kind)
proc mapSetType(typ: PType): TCTypeKind =
case int(getSize(typ))
of 1: result = ctInt8
of 2: result = ctInt16
of 4: result = ctInt32
of 8: result = ctInt64
else: result = ctArray
proc mapType(typ: PType): TCTypeKind =
case typ.kind
of tyNone: result = ctVoid
of tyBool: result = ctBool
of tyChar: result = ctChar
of tySet:
case int(getSize(typ))
of 1: result = ctInt8
of 2: result = ctInt16
of 4: result = ctInt32
of 8: result = ctInt64
else: result = ctArray
of tySet: result = mapSetType(typ)
of tyOpenArray, tyArrayConstr, tyArray: result = ctArray
of tyObject, tyTuple: result = ctStruct
of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal:
@@ -89,8 +91,12 @@ proc mapType(typ: PType): TCTypeKind =
else: internalError("mapType")
of tyRange: result = mapType(typ.sons[0])
of tyPtr, tyVar, tyRef:
case typ.sons[0].kind
var base = skipTypes(typ.sons[0], abstractInst)
case base.kind
of tyOpenArray, tyArrayConstr, tyArray: result = ctArray
of tySet:
result = mapSetType(base)
if result != ctArray: result = ctPtr
else: result = ctPtr
of tyPointer: result = ctPtr
of tySequence: result = ctNimSeq
@@ -172,6 +178,14 @@ proc fillResult(param: PSym) =
incl(param.loc.flags, lfIndirect)
param.loc.s = OnUnknown
proc getParamTypeDesc(m: BModule, t: PType, check: var TIntSet): PRope =
if t.Kind in {tyRef, tyPtr, tyVar}:
var b = skipTypes(t.sons[0], abstractInst)
if b.kind == tySet and mapSetType(b) == ctArray:
return toRope("NU8*")
# getTypeDescAux(m, b, check)
result = getTypeDescAux(m, t, check)
proc genProcParams(m: BModule, t: PType, rettype, params: var PRope,
check: var TIntSet) =
params = nil
@@ -183,19 +197,20 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var PRope,
if t.n.sons[i].kind != nkSym: InternalError(t.n.info, "genProcParams")
var param = t.n.sons[i].sym
fillLoc(param.loc, locParam, param.typ, mangleName(param), OnStack)
app(params, getTypeDescAux(m, param.typ, check))
app(params, getParamTypeDesc(m, param.typ, check))
if ccgIntroducedPtr(param):
app(params, "*")
incl(param.loc.flags, lfIndirect)
param.loc.s = OnUnknown
app(params, " ")
app(params, param.loc.r) # declare the len field for open arrays:
app(params, param.loc.r)
# declare the len field for open arrays:
var arr = param.typ
if arr.kind == tyVar: arr = arr.sons[0]
var j = 0
while arr.Kind == tyOpenArray:
# need to pass hidden parameter:
appff(params, ", NI $1Len$2", ", @NI $1Len$2", [param.loc.r, toRope(j)])
appff(params, ", NI $1Len$2", ", @NI $1Len$2", [param.loc.r, j.toRope])
inc(j)
arr = arr.sons[0]
if i < sonsLen(t.n) - 1: app(params, ", ")
@@ -396,6 +411,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope =
of tyRef, tyPtr, tyVar:
et = getUniqueType(t.sons[0])
if et.kind in {tyArrayConstr, tyArray, tyOpenArray}:
# this is correct! sets have no proper base type, so we treat
# ``var set[char]`` in `getParamTypeDesc`
et = getUniqueType(elemType(et))
case et.Kind
of tyObject, tyTuple:

View File

@@ -253,7 +253,8 @@ proc rdLoc(a: TLoc): PRope =
proc addrLoc(a: TLoc): PRope =
result = a.r
if lfIndirect notin a.flags: result = con("&", result)
if lfIndirect notin a.flags and mapType(a.t) != ctArray:
result = con("&", result)
proc rdCharLoc(a: TLoc): PRope =
# read a location that may need a char-cast:
@@ -333,7 +334,7 @@ proc initVariable(p: BProc, v: PSym) =
zeroVar(p, v.loc, b)
proc initTemp(p: BProc, tmp: var TLoc) =
if containsGarbageCollectedRef(tmp.t):
if containsGarbageCollectedRef(tmp.t) or isInvalidReturnType(tmp.t):
zeroTemp(p, tmp)
proc getTemp(p: BProc, t: PType, result: var TLoc) =

View File

@@ -119,7 +119,7 @@ proc processMagic(c: PContext, n: PNode, s: PSym) =
s.magic = m
break
if s.magic == mNone: Message(n.info, warnUnknownMagic, v)
elif s.magic != mCreateThread:
if s.magic != mCreateThread:
# magics don't need an implementation, so we
# treat them as imported, instead of modifing a lot of working code:
incl(s.flags, sfImportc)

View File

@@ -38,10 +38,6 @@ Core
* `macros <macros.html>`_
Contains the AST API and documentation of Nimrod for writing macros.
* `marshal <marshal.html>`_
Contains procs for serialization and deseralization of arbitrary Nimrod
data structures.
* `typeinfo <typeinfo.html>`_
Provides (unsafe) access to Nimrod's run time type information.
@@ -121,6 +117,10 @@ Generic Operating System Services
interface for Nimrod file objects (`TFile`) and strings. Other modules
may provide other implementations for this standard stream interface.
* `marshal <marshal.html>`_
Contains procs for serialization and deseralization of arbitrary Nimrod
data structures.
* `terminal <terminal.html>`_
This module contains a few procedures to control the *terminal*
(also called *console*). The implementation simply uses ANSI escape

View File

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

View File

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

View File

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

288
lib/pure/marshal.nim Executable file
View File

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

View File

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

View File

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

View File

@@ -145,8 +145,7 @@ proc escape*(s: string): string =
## ``&`` ``&amp;``
## ``"`` ``&quot;``
## ------------ -------------------
result = newString(s.len)
setLen(result, 0)
result = newStringOfCap(s.len)
addEscaped(result, s)
proc addIndent(result: var string, indent: int) =

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,65 @@
discard """
output: ""
"""
import marshal
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 test7 = buildList()
testit(test7)
var test6: set[char] = {'A'..'Z', '_'}
testit(test6)

View File

@@ -1,13 +1,15 @@
High priority (version 0.8.12)
==============================
* implement message passing built-ins
* add --deadlock_prevention:on|off switch? timeout for locks?
* built-in serialization
* test threads on windows; thread analysis needs to be even more restrictive!
* implement message passing built-ins: channels/queues
* outOfMem hook; 0 over-head for hello world program :-)
* bug: {:}.toTable[int, string]()
version 0.9.0
=============
- add --deadlock_prevention:on|off switch? timeout for locks?
- bug: tfFinal not passed to generic
- bug: forward proc for generic seems broken
- test the sort implementation again
@@ -36,6 +38,7 @@ Bugs
- bug: generic assign still buggy
- Optimization: If we use a temporary for the result anyway the code gen
should make use of this fact to generate better code...
- special case the generic assign that needs to care about case objects
version 0.9.XX
@@ -80,6 +83,17 @@ Library
--> ensure @[] calls the array version!
Low priority
------------
- ``when T is int`` for generic code
- ``when validCode( proc () )`` for generic code
- macros: ``typecheck`` pragma; this allows transformations based on types!
- find a way for easy constructors and destructors; (destructors are much more
important than constructors)
- code generated for type information is wasteful
Version 2
=========
@@ -114,15 +128,6 @@ Version 2
is that often the scope of ``try`` is wrong and apart from that ``try`` is
a full blown statement; a ``try`` expression might be a good idea to make
error handling more light-weight
people also want ``inc a; inc b``
Low priority
------------
- ``when T is int`` for generic code
- ``when validCode( proc () )`` for generic code
- macros: ``typecheck`` pragma; this allows transformations based on types!
- find a way for easy constructors and destructors; (destructors are much more
important than constructors)
- code generated for type information is wasteful

View File

@@ -22,6 +22,8 @@ Bugfixes
- Bugfix: The compiler does not emit very inaccurate floating point literals
anymore.
- Bugfix: Subclasses are taken into account for ``try except`` matching.
- Bugfix: Generated type information for tuples was sometimes wrong, causing
random crashes.
- Lots of other bugfixes: Too many to list them all.
@@ -61,7 +63,7 @@ Additions
- Added ``re.findAll``, ``pegs.findAll``.
- Added ``os.findExe``.
- Added ``parseutils.parseUntil`` and ``parseutils.parseWhile``.
- Added ``strutils.align``, ``strutils.tokenize``, ``strutils.wordWrap``.
- Added ``strutils.align``, ``strutils.tokenize``, ``strutils.wordWrap``.
- Pegs support a *captured search loop operator* ``{@}``.
- Pegs support new built-ins: ``\letter``, ``\upper``, ``\lower``,
``\title``, ``\white``.
@@ -93,12 +95,14 @@ Additions
- Added a wrapper for ``sphinx``.
- The compiler now supports array, sequence and string slicing.
- Added ``system.newStringOfCap``.
- Added ``system.raiseHook``.
- Added ``system.raiseHook`` and ``system.outOfMemoryHook``.
- Added ``system.writeFile``.
- Added ``system.shallowCopy``.
- ``system.echo`` is guaranteed to be thread-safe.
- Case statement branches support constant sets for programming convenience.
- Added ``prelude`` include file for scripting convenience.
- Added ``typeinfo`` core module for access to runtime type information.
- Added ``marshal`` module for JSON serialization.
2010-10-20 Version 0.8.10 released

View File

@@ -24,7 +24,7 @@ file: ticker
doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview"
doc: "tools;c2nim;niminst"
pdf: "manual;lib;tut1;tut2;nimrodc;c2nim;niminst"
srcdoc: "core/macros;core/marshal;core/typeinfo"
srcdoc: "core/macros;pure/marshal;core/typeinfo"
srcdoc: "impure/graphics;pure/sockets"
srcdoc: "system.nim;system/threads.nim;pure/os;pure/strutils;pure/math"
srcdoc: "pure/complex;pure/times;pure/osproc;pure/pegs;pure/dynlib"