mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-01 19:02:18 +00:00
code gen bugfixes; marshal.nim implemented
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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) =
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
## 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])
|
||||
|
||||
|
||||
|
||||
@@ -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
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
|
||||
##
|
||||
## 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -145,8 +145,7 @@ proc escape*(s: string): string =
|
||||
## ``&`` ``&``
|
||||
## ``"`` ``"``
|
||||
## ------------ -------------------
|
||||
result = newString(s.len)
|
||||
setLen(result, 0)
|
||||
result = newStringOfCap(s.len)
|
||||
addEscaped(result, s)
|
||||
|
||||
proc addIndent(result: var string, indent: int) =
|
||||
|
||||
@@ -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.}
|
||||
|
||||
@@ -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.} =
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
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)
|
||||
==============================
|
||||
* 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
|
||||
|
||||
|
||||
10
web/news.txt
10
web/news.txt
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user