code gen bugfixes; marshal.nim implemented
This commit is contained in:
parent
db0a4a9f86
commit
990dc2d715
21 changed files with 725 additions and 422 deletions
|
|
@ -61,18 +61,20 @@ proc getTypeName(typ: PType): PRope =
|
||||||
result = typ.loc.r
|
result = typ.loc.r
|
||||||
if result == nil: InternalError("getTypeName: " & $typ.kind)
|
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 =
|
proc mapType(typ: PType): TCTypeKind =
|
||||||
case typ.kind
|
case typ.kind
|
||||||
of tyNone: result = ctVoid
|
of tyNone: result = ctVoid
|
||||||
of tyBool: result = ctBool
|
of tyBool: result = ctBool
|
||||||
of tyChar: result = ctChar
|
of tyChar: result = ctChar
|
||||||
of tySet:
|
of tySet: result = mapSetType(typ)
|
||||||
case int(getSize(typ))
|
|
||||||
of 1: result = ctInt8
|
|
||||||
of 2: result = ctInt16
|
|
||||||
of 4: result = ctInt32
|
|
||||||
of 8: result = ctInt64
|
|
||||||
else: result = ctArray
|
|
||||||
of tyOpenArray, tyArrayConstr, tyArray: result = ctArray
|
of tyOpenArray, tyArrayConstr, tyArray: result = ctArray
|
||||||
of tyObject, tyTuple: result = ctStruct
|
of tyObject, tyTuple: result = ctStruct
|
||||||
of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal:
|
of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal:
|
||||||
|
|
@ -89,8 +91,12 @@ proc mapType(typ: PType): TCTypeKind =
|
||||||
else: internalError("mapType")
|
else: internalError("mapType")
|
||||||
of tyRange: result = mapType(typ.sons[0])
|
of tyRange: result = mapType(typ.sons[0])
|
||||||
of tyPtr, tyVar, tyRef:
|
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 tyOpenArray, tyArrayConstr, tyArray: result = ctArray
|
||||||
|
of tySet:
|
||||||
|
result = mapSetType(base)
|
||||||
|
if result != ctArray: result = ctPtr
|
||||||
else: result = ctPtr
|
else: result = ctPtr
|
||||||
of tyPointer: result = ctPtr
|
of tyPointer: result = ctPtr
|
||||||
of tySequence: result = ctNimSeq
|
of tySequence: result = ctNimSeq
|
||||||
|
|
@ -172,6 +178,14 @@ proc fillResult(param: PSym) =
|
||||||
incl(param.loc.flags, lfIndirect)
|
incl(param.loc.flags, lfIndirect)
|
||||||
param.loc.s = OnUnknown
|
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,
|
proc genProcParams(m: BModule, t: PType, rettype, params: var PRope,
|
||||||
check: var TIntSet) =
|
check: var TIntSet) =
|
||||||
params = nil
|
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")
|
if t.n.sons[i].kind != nkSym: InternalError(t.n.info, "genProcParams")
|
||||||
var param = t.n.sons[i].sym
|
var param = t.n.sons[i].sym
|
||||||
fillLoc(param.loc, locParam, param.typ, mangleName(param), OnStack)
|
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):
|
if ccgIntroducedPtr(param):
|
||||||
app(params, "*")
|
app(params, "*")
|
||||||
incl(param.loc.flags, lfIndirect)
|
incl(param.loc.flags, lfIndirect)
|
||||||
param.loc.s = OnUnknown
|
param.loc.s = OnUnknown
|
||||||
app(params, " ")
|
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
|
var arr = param.typ
|
||||||
if arr.kind == tyVar: arr = arr.sons[0]
|
if arr.kind == tyVar: arr = arr.sons[0]
|
||||||
var j = 0
|
var j = 0
|
||||||
while arr.Kind == tyOpenArray:
|
while arr.Kind == tyOpenArray:
|
||||||
# need to pass hidden parameter:
|
# 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)
|
inc(j)
|
||||||
arr = arr.sons[0]
|
arr = arr.sons[0]
|
||||||
if i < sonsLen(t.n) - 1: app(params, ", ")
|
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:
|
of tyRef, tyPtr, tyVar:
|
||||||
et = getUniqueType(t.sons[0])
|
et = getUniqueType(t.sons[0])
|
||||||
if et.kind in {tyArrayConstr, tyArray, tyOpenArray}:
|
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))
|
et = getUniqueType(elemType(et))
|
||||||
case et.Kind
|
case et.Kind
|
||||||
of tyObject, tyTuple:
|
of tyObject, tyTuple:
|
||||||
|
|
|
||||||
|
|
@ -253,7 +253,8 @@ proc rdLoc(a: TLoc): PRope =
|
||||||
|
|
||||||
proc addrLoc(a: TLoc): PRope =
|
proc addrLoc(a: TLoc): PRope =
|
||||||
result = a.r
|
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 =
|
proc rdCharLoc(a: TLoc): PRope =
|
||||||
# read a location that may need a char-cast:
|
# read a location that may need a char-cast:
|
||||||
|
|
@ -333,7 +334,7 @@ proc initVariable(p: BProc, v: PSym) =
|
||||||
zeroVar(p, v.loc, b)
|
zeroVar(p, v.loc, b)
|
||||||
|
|
||||||
proc initTemp(p: BProc, tmp: var TLoc) =
|
proc initTemp(p: BProc, tmp: var TLoc) =
|
||||||
if containsGarbageCollectedRef(tmp.t):
|
if containsGarbageCollectedRef(tmp.t) or isInvalidReturnType(tmp.t):
|
||||||
zeroTemp(p, tmp)
|
zeroTemp(p, tmp)
|
||||||
|
|
||||||
proc getTemp(p: BProc, t: PType, result: var TLoc) =
|
proc getTemp(p: BProc, t: PType, result: var TLoc) =
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ proc processMagic(c: PContext, n: PNode, s: PSym) =
|
||||||
s.magic = m
|
s.magic = m
|
||||||
break
|
break
|
||||||
if s.magic == mNone: Message(n.info, warnUnknownMagic, v)
|
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
|
# magics don't need an implementation, so we
|
||||||
# treat them as imported, instead of modifing a lot of working code:
|
# treat them as imported, instead of modifing a lot of working code:
|
||||||
incl(s.flags, sfImportc)
|
incl(s.flags, sfImportc)
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,6 @@ Core
|
||||||
* `macros <macros.html>`_
|
* `macros <macros.html>`_
|
||||||
Contains the AST API and documentation of Nimrod for writing macros.
|
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>`_
|
* `typeinfo <typeinfo.html>`_
|
||||||
Provides (unsafe) access to Nimrod's run time type information.
|
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
|
interface for Nimrod file objects (`TFile`) and strings. Other modules
|
||||||
may provide other implementations for this standard stream interface.
|
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>`_
|
* `terminal <terminal.html>`_
|
||||||
This module contains a few procedures to control the *terminal*
|
This module contains a few procedures to control the *terminal*
|
||||||
(also called *console*). The implementation simply uses ANSI escape
|
(also called *console*). The implementation simply uses ANSI escape
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
||||||
|
|
@ -11,8 +11,6 @@
|
||||||
## Note that even though ``TAny`` and its operations hide the nasty low level
|
## Note that even though ``TAny`` and its operations hide the nasty low level
|
||||||
## details from its clients, it remains inherently unsafe!
|
## details from its clients, it remains inherently unsafe!
|
||||||
|
|
||||||
# XXX raw pointer needs to be exposed somehow?
|
|
||||||
|
|
||||||
include "system/hti.nim"
|
include "system/hti.nim"
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -42,7 +40,7 @@ type
|
||||||
akFloat32 = 37, ## any represents a float32
|
akFloat32 = 37, ## any represents a float32
|
||||||
akFloat64 = 38, ## any represents a float64
|
akFloat64 = 38, ## any represents a float64
|
||||||
akFloat128 = 39, ## any represents a float128
|
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
|
TAny* = object {.pure.} ## can represent any nimrod value; NOTE: the wrapped
|
||||||
## value can be modified with its wrapper! This means
|
## value can be modified with its wrapper! This means
|
||||||
|
|
@ -63,6 +61,13 @@ const
|
||||||
GenericSeqSize = (2 * sizeof(int))
|
GenericSeqSize = (2 * sizeof(int))
|
||||||
|
|
||||||
proc genericAssign(dest, src: Pointer, mt: PNimType) {.importc.}
|
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 =
|
proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int =
|
||||||
assert(n.kind == nkCase)
|
assert(n.kind == nkCase)
|
||||||
|
|
@ -95,28 +100,61 @@ proc toAny*[T](x: var T): TAny {.inline.} =
|
||||||
result.value = addr(x)
|
result.value = addr(x)
|
||||||
result.rawType = cast[PNimType](getTypeInfo(x))
|
result.rawType = cast[PNimType](getTypeInfo(x))
|
||||||
|
|
||||||
proc getKind*(x: TAny): TAnyKind {.inline.} =
|
proc kind*(x: TAny): TAnyKind {.inline.} =
|
||||||
## get the type kind
|
## get the type kind
|
||||||
result = TAnyKind(ord(x.rawType.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.} =
|
proc skipRange(x: PNimType): PNimType {.inline.} =
|
||||||
result = x
|
result = x
|
||||||
if result.kind == tyRange: result = result.base
|
if result.kind == tyRange: result = result.base
|
||||||
|
|
||||||
template `+!!`(a, b: expr): expr = cast[pointer](cast[TAddress](a) + b)
|
|
||||||
|
|
||||||
proc `[]`*(x: TAny, i: int): TAny =
|
proc `[]`*(x: TAny, i: int): TAny =
|
||||||
## accessor for an any `x` that represents an array or a sequence.
|
## accessor for an any `x` that represents an array or a sequence.
|
||||||
case x.rawType.kind
|
case x.rawType.kind
|
||||||
of tyArray:
|
of tyArray:
|
||||||
var bs = x.rawType.base.size
|
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")
|
raise newException(EInvalidIndex, "index out of bounds")
|
||||||
return newAny(x.value +!! i*bs, x.rawType.base)
|
return newAny(x.value +!! i*bs, x.rawType.base)
|
||||||
of tySequence:
|
of tySequence:
|
||||||
var s = cast[ppointer](x.value)[]
|
var s = cast[ppointer](x.value)[]
|
||||||
|
if s == nil: raise newException(EInvalidValue, "sequence is nil")
|
||||||
var bs = x.rawType.base.size
|
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")
|
raise newException(EInvalidIndex, "index out of bounds")
|
||||||
return newAny(s +!! (GenericSeqSize+i*bs), x.rawType.base)
|
return newAny(s +!! (GenericSeqSize+i*bs), x.rawType.base)
|
||||||
else: assert false
|
else: assert false
|
||||||
|
|
@ -126,14 +164,15 @@ proc `[]=`*(x: TAny, i: int, y: TAny) =
|
||||||
case x.rawType.kind
|
case x.rawType.kind
|
||||||
of tyArray:
|
of tyArray:
|
||||||
var bs = x.rawType.base.size
|
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")
|
raise newException(EInvalidIndex, "index out of bounds")
|
||||||
assert y.rawType == x.rawType.base
|
assert y.rawType == x.rawType.base
|
||||||
genericAssign(x.value +!! i*bs, y.value, y.rawType)
|
genericAssign(x.value +!! i*bs, y.value, y.rawType)
|
||||||
of tySequence:
|
of tySequence:
|
||||||
var s = cast[ppointer](x.value)[]
|
var s = cast[ppointer](x.value)[]
|
||||||
|
if s == nil: raise newException(EInvalidValue, "sequence is nil")
|
||||||
var bs = x.rawType.base.size
|
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")
|
raise newException(EInvalidIndex, "index out of bounds")
|
||||||
assert y.rawType == x.rawType.base
|
assert y.rawType == x.rawType.base
|
||||||
genericAssign(s +!! (GenericSeqSize+i*bs), y.value, y.rawType)
|
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
|
of tySequence: result = cast[PGenSeq](cast[ppointer](x.value)[]).len
|
||||||
else: assert false
|
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,
|
proc fieldsAux(p: pointer, n: ptr TNimNode,
|
||||||
ret: var seq[tuple[name: cstring, any: TAny]]) =
|
ret: var seq[tuple[name: cstring, any: TAny]]) =
|
||||||
case n.kind
|
case n.kind
|
||||||
|
|
@ -173,6 +235,64 @@ iterator fields*(x: TAny): tuple[name: string, any: TAny] =
|
||||||
for name, any in items(ret):
|
for name, any in items(ret):
|
||||||
yield ($name, any)
|
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 =
|
proc `[]`*(x: TAny): TAny =
|
||||||
## dereference operation for the any `x` that represents a ptr or a ref.
|
## 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}
|
||||||
|
|
@ -232,6 +352,27 @@ proc getBiggestInt*(x: TAny): biggestInt =
|
||||||
else: assert false
|
else: assert false
|
||||||
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 =
|
proc getChar*(x: TAny): char =
|
||||||
## retrieve the char value out of `x`. `x` needs to represent a char.
|
## retrieve the char value out of `x`. `x` needs to represent a char.
|
||||||
var t = skipRange(x.rawtype)
|
var t = skipRange(x.rawtype)
|
||||||
|
|
@ -244,11 +385,35 @@ proc getBool*(x: TAny): bool =
|
||||||
assert t.kind == tyBool
|
assert t.kind == tyBool
|
||||||
result = cast[ptr bool](x.value)[]
|
result = cast[ptr bool](x.value)[]
|
||||||
|
|
||||||
proc getEnumField*(x: TAny): string =
|
proc skipRange*(x: TAny): TAny =
|
||||||
## gets the enum field name as a string. `x` needs to represent an enum.
|
## 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)
|
var typ = skipRange(x.rawtype)
|
||||||
assert typ.kind == tyEnum
|
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 ntfEnumHole notin typ.flags:
|
||||||
if e <% typ.node.len:
|
if e <% typ.node.len:
|
||||||
return $typ.node.sons[e].name
|
return $typ.node.sons[e].name
|
||||||
|
|
@ -258,7 +423,11 @@ proc getEnumField*(x: TAny): string =
|
||||||
var s = n.sons
|
var s = n.sons
|
||||||
for i in 0 .. n.len-1:
|
for i in 0 .. n.len-1:
|
||||||
if s[i].offset == e: return $s[i].name
|
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 =
|
proc getFloat*(x: TAny): float =
|
||||||
## retrieve the float value out of `x`. `x` needs to represent an 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)[])
|
of tyFloat64: result = biggestFloat(cast[ptr Float64](x.value)[])
|
||||||
else: assert false
|
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 =
|
proc getString*(x: TAny): string =
|
||||||
## retrieve the string value out of `x`. `x` needs to represent a string.
|
## retrieve the string value out of `x`. `x` needs to represent a string.
|
||||||
assert x.rawtype.kind == tyString
|
assert x.rawtype.kind == tyString
|
||||||
result = cast[ptr string](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
|
||||||
|
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) =
|
proc assign*(x, y: TAny) =
|
||||||
## copies the value of `y` to `x`. The assignment operator for ``TAny``
|
## copies the value of `y` to `x`. The assignment operator for ``TAny``
|
||||||
## does NOT do this; it performs a shallow copy instead!
|
## 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)
|
a[] = a[] or (1'i64 shl e)
|
||||||
else:
|
else:
|
||||||
var a = cast[pbyteArray](p)
|
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:
|
when isMainModule:
|
||||||
type
|
type
|
||||||
|
|
@ -365,8 +553,8 @@ when isMainModule:
|
||||||
var i = 0
|
var i = 0
|
||||||
for n, a in fields(x2):
|
for n, a in fields(x2):
|
||||||
case i
|
case i
|
||||||
of 0: assert n == "name" and $a.getKind == "akString"
|
of 0: assert n == "name" and $a.kind == "akString"
|
||||||
of 1: assert n == "s" and $a.getKind == "akInt"
|
of 1: assert n == "s" and $a.kind == "akInt"
|
||||||
else: assert false
|
else: assert false
|
||||||
inc i
|
inc i
|
||||||
|
|
||||||
|
|
@ -377,9 +565,9 @@ when isMainModule:
|
||||||
i = 0
|
i = 0
|
||||||
for n, a in fields(x3):
|
for n, a in fields(x3):
|
||||||
case i
|
case i
|
||||||
of 0: assert n == "test" and $a.getKind == "akInt"
|
of 0: assert n == "test" and $a.kind == "akInt"
|
||||||
of 1: assert n == "asd" and $a.getKind == "akInt"
|
of 1: assert n == "asd" and $a.kind == "akInt"
|
||||||
of 2: assert n == "test2" and $a.getKind == "akEnum"
|
of 2: assert n == "test2" and $a.kind == "akEnum"
|
||||||
else: assert false
|
else: assert false
|
||||||
inc i
|
inc i
|
||||||
|
|
||||||
|
|
@ -387,4 +575,17 @@ when isMainModule:
|
||||||
new(test4)
|
new(test4)
|
||||||
test4[] = "test"
|
test4[] = "test"
|
||||||
var x4 = toAny(test4)
|
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])
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ type
|
||||||
jsonError, ## an error ocurred during parsing
|
jsonError, ## an error ocurred during parsing
|
||||||
jsonEof, ## end of file reached
|
jsonEof, ## end of file reached
|
||||||
jsonString, ## a string literal
|
jsonString, ## a string literal
|
||||||
jsonInt, ## a integer literal
|
jsonInt, ## an integer literal
|
||||||
jsonFloat, ## a float literal
|
jsonFloat, ## a float literal
|
||||||
jsonTrue, ## the value ``true``
|
jsonTrue, ## the value ``true``
|
||||||
jsonFalse, ## the value ``false``
|
jsonFalse, ## the value ``false``
|
||||||
|
|
@ -121,7 +121,7 @@ proc str*(my: TJsonParser): string {.inline.} =
|
||||||
proc getInt*(my: TJsonParser): biggestInt {.inline.} =
|
proc getInt*(my: TJsonParser): biggestInt {.inline.} =
|
||||||
## returns the number for the event: ``jsonInt``
|
## returns the number for the event: ``jsonInt``
|
||||||
assert(my.kind == jsonInt)
|
assert(my.kind == jsonInt)
|
||||||
return parseInt(my.a)
|
return parseBiggestInt(my.a)
|
||||||
|
|
||||||
proc getFloat*(my: TJsonParser): float {.inline.} =
|
proc getFloat*(my: TJsonParser): float {.inline.} =
|
||||||
## returns the number for the event: ``jsonFloat``
|
## returns the number for the event: ``jsonFloat``
|
||||||
|
|
@ -512,9 +512,10 @@ type
|
||||||
of JArray:
|
of JArray:
|
||||||
elems*: seq[PJsonNode]
|
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))
|
raise newException(EJsonParsingError, errorMsgExpected(p, msg))
|
||||||
|
|
||||||
proc newJString*(s: String): PJsonNode =
|
proc newJString*(s: String): PJsonNode =
|
||||||
|
|
@ -607,6 +608,7 @@ proc `[]=`*(obj: PJsonNode, key: String, val: PJsonNode) =
|
||||||
obj.fields.add((key, val))
|
obj.fields.add((key, val))
|
||||||
|
|
||||||
proc delete*(obj: PJsonNode, key: string) =
|
proc delete*(obj: PJsonNode, key: string) =
|
||||||
|
## Deletes ``obj[key]`` preserving the order of the other (key, value)-pairs.
|
||||||
assert(obj.kind == JObject)
|
assert(obj.kind == JObject)
|
||||||
for i in 0..obj.fields.len-1:
|
for i in 0..obj.fields.len-1:
|
||||||
if obj.fields[i].key == key:
|
if obj.fields[i].key == key:
|
||||||
|
|
@ -615,6 +617,7 @@ proc delete*(obj: PJsonNode, key: string) =
|
||||||
raise newException(EInvalidIndex, "key not in object")
|
raise newException(EInvalidIndex, "key not in object")
|
||||||
|
|
||||||
proc copy*(p: PJsonNode): PJsonNode =
|
proc copy*(p: PJsonNode): PJsonNode =
|
||||||
|
## Performs a deep copy of `a`.
|
||||||
case p.kind
|
case p.kind
|
||||||
of JString:
|
of JString:
|
||||||
result = newJString(p.str)
|
result = newJString(p.str)
|
||||||
|
|
|
||||||
288
lib/pure/marshal.nim
Executable file
288
lib/pure/marshal.nim
Executable 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)
|
||||||
|
|
||||||
|
|
@ -33,8 +33,9 @@ proc write*[T](s: PStream, x: T) =
|
||||||
## .. code-block:: Nimrod
|
## .. code-block:: Nimrod
|
||||||
##
|
##
|
||||||
## s.writeData(s, addr(x), sizeof(x))
|
## s.writeData(s, addr(x), sizeof(x))
|
||||||
var x = x
|
var y: T
|
||||||
s.writeData(s, addr(x), sizeof(x))
|
shallowCopy(y, x)
|
||||||
|
s.writeData(s, addr(y), sizeof(y))
|
||||||
|
|
||||||
proc write*(s: PStream, x: string) =
|
proc write*(s: PStream, x: string) =
|
||||||
## writes the string `x` to the the stream `s`. No length field or
|
## writes the string `x` to the the stream `s`. No length field or
|
||||||
|
|
|
||||||
|
|
@ -145,8 +145,7 @@ proc escape*(s: string): string =
|
||||||
## ``&`` ``&``
|
## ``&`` ``&``
|
||||||
## ``"`` ``"``
|
## ``"`` ``"``
|
||||||
## ------------ -------------------
|
## ------------ -------------------
|
||||||
result = newString(s.len)
|
result = newStringOfCap(s.len)
|
||||||
setLen(result, 0)
|
|
||||||
addEscaped(result, s)
|
addEscaped(result, s)
|
||||||
|
|
||||||
proc addIndent(result: var string, indent: int) =
|
proc addIndent(result: var string, indent: int) =
|
||||||
|
|
|
||||||
|
|
@ -1612,8 +1612,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
|
|
||||||
proc readLine*(f: TFile): string
|
proc readLine*(f: TFile): string
|
||||||
## reads a line of text from the file `f`. May throw an IO exception.
|
## 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
|
## A line of text may be delimited by ``CR``, ``LF`` or
|
||||||
## returns nil. A line of text may be delimited by ``CR``, ``LF`` or
|
|
||||||
## ``CRLF``. The newline character(s) are not part of the returned string.
|
## ``CRLF``. The newline character(s) are not part of the returned string.
|
||||||
|
|
||||||
proc writeln*[Ty](f: TFile, x: Ty) {.inline.}
|
proc writeln*[Ty](f: TFile, x: Ty) {.inline.}
|
||||||
|
|
|
||||||
|
|
@ -198,18 +198,18 @@ proc prepareDealloc(cell: PCell) =
|
||||||
|
|
||||||
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
|
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
|
||||||
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
||||||
when hasThreadSupport:
|
when hasThreadSupport and hasSharedHeap:
|
||||||
AcquireSys(HeapLock)
|
AcquireSys(HeapLock)
|
||||||
incl(gch.cycleRoots, c)
|
incl(gch.cycleRoots, c)
|
||||||
when hasThreadSupport:
|
when hasThreadSupport and hasSharedHeap:
|
||||||
ReleaseSys(HeapLock)
|
ReleaseSys(HeapLock)
|
||||||
|
|
||||||
proc rtlAddZCT(c: PCell) {.rtl, inl.} =
|
proc rtlAddZCT(c: PCell) {.rtl, inl.} =
|
||||||
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
||||||
when hasThreadSupport:
|
when hasThreadSupport and hasSharedHeap:
|
||||||
AcquireSys(HeapLock)
|
AcquireSys(HeapLock)
|
||||||
addZCT(gch.zct, c)
|
addZCT(gch.zct, c)
|
||||||
when hasThreadSupport:
|
when hasThreadSupport and hasSharedHeap:
|
||||||
ReleaseSys(HeapLock)
|
ReleaseSys(HeapLock)
|
||||||
|
|
||||||
proc decRef(c: PCell) {.inline.} =
|
proc decRef(c: PCell) {.inline.} =
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,6 @@
|
||||||
|
|
||||||
# The generic ``repr`` procedure. It is an invaluable debugging tool.
|
# 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 reprInt(x: int64): string {.compilerproc.} = return $x
|
||||||
proc reprFloat(x: float): string {.compilerproc.} = return $x
|
proc reprFloat(x: float): string {.compilerproc.} = return $x
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,13 +36,16 @@ var
|
||||||
IOFBF {.importc: "_IOFBF", nodecl.}: cint
|
IOFBF {.importc: "_IOFBF", nodecl.}: cint
|
||||||
IONBF {.importc: "_IONBF", nodecl.}: cint
|
IONBF {.importc: "_IONBF", nodecl.}: cint
|
||||||
|
|
||||||
|
proc raiseEIO(msg: string) {.noinline, noreturn.} =
|
||||||
|
raise newException(EIO, msg)
|
||||||
|
|
||||||
proc rawReadLine(f: TFile, result: var string) =
|
proc rawReadLine(f: TFile, result: var string) =
|
||||||
# of course this could be optimized a bit; but IO is slow anyway...
|
# 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
|
# and it was difficult to get this CORRECT with Ansi C's methods
|
||||||
setLen(result, 0) # reuse the buffer!
|
setLen(result, 0) # reuse the buffer!
|
||||||
while True:
|
while True:
|
||||||
var c = fgetc(f)
|
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 == 10'i32: break # LF
|
||||||
if c == 13'i32: # CR
|
if c == 13'i32: # CR
|
||||||
c = fgetc(f) # is the next char LF?
|
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]) =
|
proc write(f: TFile, a: openArray[string]) =
|
||||||
for x in items(a): write(f, x)
|
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 =
|
proc readFile(filename: string): string =
|
||||||
var f = open(filename)
|
var f = open(filename)
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,10 @@
|
||||||
# we don't use refcounts because that's a behaviour
|
# we don't use refcounts because that's a behaviour
|
||||||
# the programmer may not want
|
# the programmer may not want
|
||||||
|
|
||||||
# implementation:
|
|
||||||
|
|
||||||
proc resize(old: int): int {.inline.} =
|
proc resize(old: int): int {.inline.} =
|
||||||
if old <= 0: return 4
|
if old <= 0: result = 4
|
||||||
elif old < 65536: return old * 2
|
elif old < 65536: result = old * 2
|
||||||
else: return old * 3 div 2 # for large arrays * 3/2 is better
|
else: result = old * 3 div 2 # for large arrays * 3/2 is better
|
||||||
|
|
||||||
proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} =
|
proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} =
|
||||||
if a == b: return 0
|
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!
|
result.data[len] = '\0' # readline relies on this!
|
||||||
|
|
||||||
proc cstrToNimstr(str: CString): NimString {.compilerProc.} =
|
proc cstrToNimstr(str: CString): NimString {.compilerProc.} =
|
||||||
return toNimstr(str, c_strlen(str))
|
result = toNimstr(str, c_strlen(str))
|
||||||
|
|
||||||
proc copyString(src: NimString): NimString {.compilerProc.} =
|
proc copyString(src: NimString): NimString {.compilerProc.} =
|
||||||
if src == nil: return nil
|
if src != nil:
|
||||||
result = rawNewString(src.space)
|
result = rawNewString(src.space)
|
||||||
result.len = src.len
|
result.len = src.len
|
||||||
c_memcpy(result.data, src.data, (src.len + 1) * sizeof(Char))
|
c_memcpy(result.data, src.data, (src.len + 1) * sizeof(Char))
|
||||||
|
|
||||||
proc hashString(s: string): int {.compilerproc.} =
|
proc hashString(s: string): int {.compilerproc.} =
|
||||||
# the compiler needs exactly the same hash function!
|
# the compiler needs exactly the same hash function!
|
||||||
|
|
@ -86,7 +84,7 @@ proc copyStrLast(s: NimString, start, last: int): NimString {.exportc.} =
|
||||||
result = mnewString(0)
|
result = mnewString(0)
|
||||||
|
|
||||||
proc copyStr(s: NimString, start: int): NimString {.exportc.} =
|
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 =
|
proc addChar(s: NimString, c: char): NimString =
|
||||||
# is compilerproc!
|
# is compilerproc!
|
||||||
|
|
@ -135,7 +133,7 @@ proc addChar(s: NimString, c: char): NimString =
|
||||||
# s = rawNewString(0);
|
# s = rawNewString(0);
|
||||||
|
|
||||||
proc resizeString(dest: NimString, addlen: int): NimString {.compilerproc.} =
|
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
|
result = dest
|
||||||
else: # slow path:
|
else: # slow path:
|
||||||
var sp = max(resize(dest.space), dest.len + addLen + 1)
|
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
|
a = 0
|
||||||
b = len(x)
|
b = len(x)
|
||||||
while a < b:
|
while a < b:
|
||||||
var mid = (a + b) div 2
|
var mid = (a + b) div 2
|
||||||
if x[mid] < y:
|
if x[mid] < y:
|
||||||
a = mid + 1
|
a = mid + 1
|
||||||
else:
|
else:
|
||||||
b = mid
|
b = mid
|
||||||
if (a < len(x)) and (x[a] == y):
|
if a < len(x) and x[a] == y:
|
||||||
return a
|
result = a
|
||||||
else:
|
else:
|
||||||
return -1
|
result = -1
|
||||||
|
|
|
||||||
|
|
@ -339,14 +339,16 @@ proc joinThreads*[TParam](t: openArray[TThread[TParam]]) =
|
||||||
else:
|
else:
|
||||||
for i in 0..t.high: joinThread(t[i])
|
for i in 0..t.high: joinThread(t[i])
|
||||||
|
|
||||||
proc destroyThread*[TParam](t: var TThread[TParam]) {.inline.} =
|
when false:
|
||||||
## forces the thread `t` to terminate. This is potentially dangerous if
|
# XXX a thread should really release its heap here somehow:
|
||||||
## you don't have full control over `t` and its acquired resources.
|
proc destroyThread*[TParam](t: var TThread[TParam]) {.inline.} =
|
||||||
when hostOS == "windows":
|
## forces the thread `t` to terminate. This is potentially dangerous if
|
||||||
discard TerminateThread(t.sys, 1'i32)
|
## you don't have full control over `t` and its acquired resources.
|
||||||
else:
|
when hostOS == "windows":
|
||||||
discard pthread_cancel(t.sys)
|
discard TerminateThread(t.sys, 1'i32)
|
||||||
unregisterThread(addr(t.gcInfo))
|
else:
|
||||||
|
discard pthread_cancel(t.sys)
|
||||||
|
unregisterThread(addr(t))
|
||||||
|
|
||||||
proc createThread*[TParam](t: var TThread[TParam],
|
proc createThread*[TParam](t: var TThread[TParam],
|
||||||
tp: proc (param: TParam),
|
tp: proc (param: TParam),
|
||||||
|
|
|
||||||
65
tests/accept/compile/tmarshal.nim
Normal file
65
tests/accept/compile/tmarshal.nim
Normal 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)
|
||||||
|
|
||||||
31
todo.txt
31
todo.txt
|
|
@ -1,13 +1,15 @@
|
||||||
High priority (version 0.8.12)
|
High priority (version 0.8.12)
|
||||||
==============================
|
==============================
|
||||||
* implement message passing built-ins
|
* test threads on windows; thread analysis needs to be even more restrictive!
|
||||||
* add --deadlock_prevention:on|off switch? timeout for locks?
|
* implement message passing built-ins: channels/queues
|
||||||
* built-in serialization
|
* outOfMem hook; 0 over-head for hello world program :-)
|
||||||
|
* bug: {:}.toTable[int, string]()
|
||||||
|
|
||||||
|
|
||||||
version 0.9.0
|
version 0.9.0
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
- add --deadlock_prevention:on|off switch? timeout for locks?
|
||||||
- bug: tfFinal not passed to generic
|
- bug: tfFinal not passed to generic
|
||||||
- bug: forward proc for generic seems broken
|
- bug: forward proc for generic seems broken
|
||||||
- test the sort implementation again
|
- test the sort implementation again
|
||||||
|
|
@ -36,6 +38,7 @@ Bugs
|
||||||
- bug: generic assign still buggy
|
- bug: generic assign still buggy
|
||||||
- Optimization: If we use a temporary for the result anyway the code gen
|
- Optimization: If we use a temporary for the result anyway the code gen
|
||||||
should make use of this fact to generate better code...
|
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
|
version 0.9.XX
|
||||||
|
|
@ -80,6 +83,17 @@ Library
|
||||||
--> ensure @[] calls the array version!
|
--> 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
|
Version 2
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
|
@ -114,15 +128,6 @@ Version 2
|
||||||
is that often the scope of ``try`` is wrong and apart from that ``try`` is
|
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
|
a full blown statement; a ``try`` expression might be a good idea to make
|
||||||
error handling more light-weight
|
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
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ Bugfixes
|
||||||
- Bugfix: The compiler does not emit very inaccurate floating point literals
|
- Bugfix: The compiler does not emit very inaccurate floating point literals
|
||||||
anymore.
|
anymore.
|
||||||
- Bugfix: Subclasses are taken into account for ``try except`` matching.
|
- 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.
|
- Lots of other bugfixes: Too many to list them all.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -93,12 +95,14 @@ Additions
|
||||||
- Added a wrapper for ``sphinx``.
|
- Added a wrapper for ``sphinx``.
|
||||||
- The compiler now supports array, sequence and string slicing.
|
- The compiler now supports array, sequence and string slicing.
|
||||||
- Added ``system.newStringOfCap``.
|
- Added ``system.newStringOfCap``.
|
||||||
- Added ``system.raiseHook``.
|
- Added ``system.raiseHook`` and ``system.outOfMemoryHook``.
|
||||||
- Added ``system.writeFile``.
|
- Added ``system.writeFile``.
|
||||||
- Added ``system.shallowCopy``.
|
- Added ``system.shallowCopy``.
|
||||||
- ``system.echo`` is guaranteed to be thread-safe.
|
- ``system.echo`` is guaranteed to be thread-safe.
|
||||||
- Case statement branches support constant sets for programming convenience.
|
- 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
|
2010-10-20 Version 0.8.10 released
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ file: ticker
|
||||||
doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview"
|
doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview"
|
||||||
doc: "tools;c2nim;niminst"
|
doc: "tools;c2nim;niminst"
|
||||||
pdf: "manual;lib;tut1;tut2;nimrodc;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: "impure/graphics;pure/sockets"
|
||||||
srcdoc: "system.nim;system/threads.nim;pure/os;pure/strutils;pure/math"
|
srcdoc: "system.nim;system/threads.nim;pure/os;pure/strutils;pure/math"
|
||||||
srcdoc: "pure/complex;pure/times;pure/osproc;pure/pegs;pure/dynlib"
|
srcdoc: "pure/complex;pure/times;pure/osproc;pure/pegs;pure/dynlib"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue