marshalling can be done at compile-time
This commit is contained in:
parent
6725aa3634
commit
d3fc6e1f28
8 changed files with 404 additions and 51 deletions
|
|
@ -16,7 +16,8 @@ import ast except getstr
|
||||||
|
|
||||||
import
|
import
|
||||||
strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned,
|
strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned,
|
||||||
parser, vmdeps, idents, trees, renderer, options, transf, parseutils
|
parser, vmdeps, idents, trees, renderer, options, transf, parseutils,
|
||||||
|
vmmarshal
|
||||||
|
|
||||||
from semfold import leValueConv, ordinalValToString
|
from semfold import leValueConv, ordinalValToString
|
||||||
from evaltempl import evalTemplate
|
from evaltempl import evalTemplate
|
||||||
|
|
@ -371,11 +372,6 @@ template handleJmpBack() {.dirty.} =
|
||||||
globalError(c.debug[pc], errTooManyIterations)
|
globalError(c.debug[pc], errTooManyIterations)
|
||||||
dec(c.loopIterations)
|
dec(c.loopIterations)
|
||||||
|
|
||||||
proc skipColon(n: PNode): PNode =
|
|
||||||
result = n
|
|
||||||
if n.kind == nkExprColonExpr:
|
|
||||||
result = n.sons[1]
|
|
||||||
|
|
||||||
proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
var pc = start
|
var pc = start
|
||||||
var tos = tos
|
var tos = tos
|
||||||
|
|
@ -1369,6 +1365,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0]
|
while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0]
|
||||||
createStr regs[ra]
|
createStr regs[ra]
|
||||||
regs[ra].node.strVal = typ.typeToString(preferExported)
|
regs[ra].node.strVal = typ.typeToString(preferExported)
|
||||||
|
of opcMarshalLoad:
|
||||||
|
let ra = instr.regA
|
||||||
|
let rb = instr.regB
|
||||||
|
inc pc
|
||||||
|
let typ = c.types[c.code[pc].regBx - wordExcess]
|
||||||
|
putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ))
|
||||||
|
of opcMarshalStore:
|
||||||
|
decodeB(rkNode)
|
||||||
|
inc pc
|
||||||
|
let typ = c.types[c.code[pc].regBx - wordExcess]
|
||||||
|
createStrKeepNode(regs[ra])
|
||||||
|
if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000)
|
||||||
|
storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode)
|
||||||
inc pc
|
inc pc
|
||||||
|
|
||||||
proc execute(c: PCtx, start: int): PNode =
|
proc execute(c: PCtx, start: int): PNode =
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,8 @@ type
|
||||||
opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr,
|
opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr,
|
||||||
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
|
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
|
||||||
opcSwap, opcIsNil, opcOf, opcIs,
|
opcSwap, opcIsNil, opcOf, opcIs,
|
||||||
opcSubStr, opcParseFloat, opcConv, opcCast, opcQuit, opcReset,
|
opcSubStr, opcParseFloat, opcConv, opcCast,
|
||||||
|
opcQuit, opcReset,
|
||||||
opcNarrowS, opcNarrowU,
|
opcNarrowS, opcNarrowU,
|
||||||
|
|
||||||
opcAddStrCh,
|
opcAddStrCh,
|
||||||
|
|
@ -132,7 +133,8 @@ type
|
||||||
opcLdImmInt, # dest = immediate value
|
opcLdImmInt, # dest = immediate value
|
||||||
opcNBindSym,
|
opcNBindSym,
|
||||||
opcSetType, # dest.typ = types[Bx]
|
opcSetType, # dest.typ = types[Bx]
|
||||||
opcTypeTrait
|
opcTypeTrait,
|
||||||
|
opcMarshalLoad, opcMarshalStore
|
||||||
|
|
||||||
TBlock* = object
|
TBlock* = object
|
||||||
label*: PSym
|
label*: PSym
|
||||||
|
|
@ -221,7 +223,8 @@ proc registerCallback*(c: PCtx; name: string; callback: VmCallback) =
|
||||||
const
|
const
|
||||||
firstABxInstr* = opcTJmp
|
firstABxInstr* = opcTJmp
|
||||||
largeInstrs* = { # instructions which use 2 int32s instead of 1:
|
largeInstrs* = { # instructions which use 2 int32s instead of 1:
|
||||||
opcSubStr, opcConv, opcCast, opcNewSeq, opcOf}
|
opcSubStr, opcConv, opcCast, opcNewSeq, opcOf,
|
||||||
|
opcMarshalLoad, opcMarshalStore}
|
||||||
slotSomeTemp* = slotTempUnknown
|
slotSomeTemp* = slotTempUnknown
|
||||||
relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack}
|
relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,11 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
|
||||||
elif opc in {opcLdConst, opcAsgnConst}:
|
elif opc in {opcLdConst, opcAsgnConst}:
|
||||||
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA,
|
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA,
|
||||||
c.constants[x.regBx-wordExcess].renderTree)
|
c.constants[x.regBx-wordExcess].renderTree)
|
||||||
|
elif opc in {opcMarshalLoad, opcMarshalStore}:
|
||||||
|
let y = c.code[i+1]
|
||||||
|
result.addf("\t$#\tr$#, r$#, $#", ($opc).substr(3), x.regA, x.regB,
|
||||||
|
c.types[y.regBx-wordExcess].typeToString)
|
||||||
|
inc i
|
||||||
else:
|
else:
|
||||||
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess)
|
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess)
|
||||||
result.add("\t#")
|
result.add("\t#")
|
||||||
|
|
@ -696,8 +701,7 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
c.gABC(n, opcCard, dest, tmp)
|
c.gABC(n, opcCard, dest, tmp)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
|
|
||||||
proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||||
let m = n.sons[0].sym.magic
|
|
||||||
case m
|
case m
|
||||||
of mAnd: c.genAndOr(n, opcFJmp, dest)
|
of mAnd: c.genAndOr(n, opcFJmp, dest)
|
||||||
of mOr: c.genAndOr(n, opcTJmp, dest)
|
of mOr: c.genAndOr(n, opcTJmp, dest)
|
||||||
|
|
@ -1028,6 +1032,22 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
# mGCref, mGCunref,
|
# mGCref, mGCunref,
|
||||||
internalError(n.info, "cannot generate code for: " & $m)
|
internalError(n.info, "cannot generate code for: " & $m)
|
||||||
|
|
||||||
|
proc genMarshalLoad(c: PCtx, n: PNode, dest: var TDest) =
|
||||||
|
## Signature: proc to*[T](data: string): T
|
||||||
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
|
var tmp = c.genx(n.sons[1])
|
||||||
|
c.gABC(n, opcMarshalLoad, dest, tmp)
|
||||||
|
c.gABx(n, opcMarshalLoad, 0, c.genType(n.typ))
|
||||||
|
c.freeTemp(tmp)
|
||||||
|
|
||||||
|
proc genMarshalStore(c: PCtx, n: PNode, dest: var TDest) =
|
||||||
|
## Signature: proc `$$`*[T](x: T): string
|
||||||
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
|
var tmp = c.genx(n.sons[1])
|
||||||
|
c.gABC(n, opcMarshalStore, dest, tmp)
|
||||||
|
c.gABx(n, opcMarshalStore, 0, c.genType(n.sons[1].typ))
|
||||||
|
c.freeTemp(tmp)
|
||||||
|
|
||||||
const
|
const
|
||||||
atomicTypes = {tyBool, tyChar,
|
atomicTypes = {tyBool, tyChar,
|
||||||
tyExpr, tyStmt, tyTypeDesc, tyStatic,
|
tyExpr, tyStmt, tyTypeDesc, tyStatic,
|
||||||
|
|
@ -1533,6 +1553,15 @@ proc matches(s: PSym; x: string): bool =
|
||||||
dec L
|
dec L
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
|
proc matches(s: PSym; y: varargs[string]): bool =
|
||||||
|
var s = s
|
||||||
|
var L = y.len-1
|
||||||
|
while L >= 0:
|
||||||
|
if s == nil or y[L].cmpIgnoreStyle(s.name.s) != 0: return false
|
||||||
|
s = if sfFromGeneric in s.flags: s.owner.owner else: s.owner
|
||||||
|
dec L
|
||||||
|
result = true
|
||||||
|
|
||||||
proc procIsCallback(c: PCtx; s: PSym): bool =
|
proc procIsCallback(c: PCtx; s: PSym): bool =
|
||||||
if s.offset < -1: return true
|
if s.offset < -1: return true
|
||||||
var i = -2
|
var i = -2
|
||||||
|
|
@ -1570,8 +1599,17 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||||
else:
|
else:
|
||||||
internalError(n.info, "cannot generate code for: " & s.name.s)
|
internalError(n.info, "cannot generate code for: " & s.name.s)
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
if n.sons[0].kind == nkSym and n.sons[0].sym.magic != mNone:
|
if n.sons[0].kind == nkSym:
|
||||||
genMagic(c, n, dest)
|
let s = n.sons[0].sym
|
||||||
|
if s.magic != mNone:
|
||||||
|
genMagic(c, n, dest, s.magic)
|
||||||
|
elif matches(s, "stdlib", "marshal", "to"):
|
||||||
|
genMarshalLoad(c, n, dest)
|
||||||
|
elif matches(s, "stdlib", "marshal", "$$"):
|
||||||
|
genMarshalStore(c, n, dest)
|
||||||
|
else:
|
||||||
|
genCall(c, n, dest)
|
||||||
|
clearDest(c, n, dest)
|
||||||
else:
|
else:
|
||||||
genCall(c, n, dest)
|
genCall(c, n, dest)
|
||||||
clearDest(c, n, dest)
|
clearDest(c, n, dest)
|
||||||
|
|
|
||||||
283
compiler/vmmarshal.nim
Normal file
283
compiler/vmmarshal.nim
Normal file
|
|
@ -0,0 +1,283 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nim Compiler
|
||||||
|
# (c) Copyright 2015 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## Implements marshaling for the VM.
|
||||||
|
|
||||||
|
import streams, json, intsets, tables, ast, astalgo, idents, types, msgs
|
||||||
|
|
||||||
|
proc ptrToInt(x: PNode): int {.inline.} =
|
||||||
|
result = cast[int](x) # don't skip alignment
|
||||||
|
|
||||||
|
proc getField(n: PNode; position: int): PSym =
|
||||||
|
case n.kind
|
||||||
|
of nkRecList:
|
||||||
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
|
result = getField(n.sons[i], position)
|
||||||
|
if result != nil: return
|
||||||
|
of nkRecCase:
|
||||||
|
result = getField(n.sons[0], position)
|
||||||
|
if result != nil: return
|
||||||
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
|
case n.sons[i].kind
|
||||||
|
of nkOfBranch, nkElse:
|
||||||
|
result = getField(lastSon(n.sons[i]), position)
|
||||||
|
if result != nil: return
|
||||||
|
else: internalError(n.info, "getField(record case branch)")
|
||||||
|
of nkSym:
|
||||||
|
if n.sym.position == position: result = n.sym
|
||||||
|
else: discard
|
||||||
|
|
||||||
|
proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet)
|
||||||
|
|
||||||
|
proc storeObj(s: var string; typ: PType; x: PNode; stored: var IntSet) =
|
||||||
|
internalAssert x.kind in {nkObjConstr, nkPar}
|
||||||
|
let start = ord(x.kind == nkObjConstr)
|
||||||
|
for i in countup(start, sonsLen(x) - 1):
|
||||||
|
if i > start: s.add(", ")
|
||||||
|
var it = x.sons[i]
|
||||||
|
if it.kind == nkExprColonExpr:
|
||||||
|
internalAssert it.sons[0].kind == nkSym
|
||||||
|
let field = it.sons[0].sym
|
||||||
|
s.add(escapeJson(field.name.s))
|
||||||
|
s.add(": ")
|
||||||
|
storeAny(s, field.typ, it.sons[1], stored)
|
||||||
|
elif typ.n != nil:
|
||||||
|
let field = getField(typ.n, i)
|
||||||
|
s.add(escapeJson(field.name.s))
|
||||||
|
s.add(": ")
|
||||||
|
storeAny(s, field.typ, it, stored)
|
||||||
|
|
||||||
|
proc skipColon*(n: PNode): PNode =
|
||||||
|
result = n
|
||||||
|
if n.kind == nkExprColonExpr:
|
||||||
|
result = n.sons[1]
|
||||||
|
|
||||||
|
proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) =
|
||||||
|
case t.kind
|
||||||
|
of tyNone: assert false
|
||||||
|
of tyBool: s.add($(a.intVal != 0))
|
||||||
|
of tyChar:
|
||||||
|
let ch = char(a.intVal)
|
||||||
|
if ch < '\128':
|
||||||
|
s.add(escapeJson($ch))
|
||||||
|
else:
|
||||||
|
s.add($int(ch))
|
||||||
|
of tyArray, tySequence:
|
||||||
|
if t.kind == tySequence and a.kind == nkNilLit: s.add("null")
|
||||||
|
else:
|
||||||
|
s.add("[")
|
||||||
|
for i in 0 .. a.len-1:
|
||||||
|
if i > 0: s.add(", ")
|
||||||
|
storeAny(s, t.elemType, a[i], stored)
|
||||||
|
s.add("]")
|
||||||
|
of tyTuple:
|
||||||
|
s.add("{")
|
||||||
|
for i in 0.. <t.len:
|
||||||
|
if i > 0: s.add(", ")
|
||||||
|
s.add("\"Field" & $i)
|
||||||
|
s.add("\": ")
|
||||||
|
storeAny(s, t.sons[i], a[i].skipColon, stored)
|
||||||
|
s.add("}")
|
||||||
|
of tyObject:
|
||||||
|
s.add("{")
|
||||||
|
storeObj(s, t, a, stored)
|
||||||
|
s.add("}")
|
||||||
|
of tySet:
|
||||||
|
s.add("[")
|
||||||
|
for i in 0.. <a.len:
|
||||||
|
if i > 0: s.add(", ")
|
||||||
|
if a[i].kind == nkRange:
|
||||||
|
var x = copyNode(a[i][0])
|
||||||
|
storeAny(s, t.lastSon, x, stored)
|
||||||
|
while x.intVal+1 <= a[i][1].intVal:
|
||||||
|
s.add(", ")
|
||||||
|
storeAny(s, t.lastSon, x, stored)
|
||||||
|
inc x.intVal
|
||||||
|
else:
|
||||||
|
storeAny(s, t.lastSon, a[i], stored)
|
||||||
|
s.add("]")
|
||||||
|
of tyRange, tyGenericInst: storeAny(s, t.lastSon, a, stored)
|
||||||
|
of tyEnum:
|
||||||
|
# we need a slow linear search because of enums with holes:
|
||||||
|
for e in items(t.n):
|
||||||
|
if e.sym.position == a.intVal:
|
||||||
|
s.add e.sym.name.s.escapeJson
|
||||||
|
break
|
||||||
|
of tyPtr, tyRef:
|
||||||
|
var x = a
|
||||||
|
if isNil(x) or x.kind == nkNilLit: s.add("null")
|
||||||
|
elif stored.containsOrIncl(x.ptrToInt):
|
||||||
|
# already stored, so we simply write out the pointer as an int:
|
||||||
|
s.add($x.ptrToInt)
|
||||||
|
else:
|
||||||
|
# else as a [value, key] pair:
|
||||||
|
# (reversed order for convenient x[0] access!)
|
||||||
|
s.add("[")
|
||||||
|
s.add($x.ptrToInt)
|
||||||
|
s.add(", ")
|
||||||
|
storeAny(s, t.lastSon, a, stored)
|
||||||
|
s.add("]")
|
||||||
|
of tyString, tyCString:
|
||||||
|
if a.kind == nkNilLit or a.strVal.isNil: s.add("null")
|
||||||
|
else: s.add(escapeJson(a.strVal))
|
||||||
|
of tyInt..tyInt64, tyUInt..tyUInt64: s.add($a.intVal)
|
||||||
|
of tyFloat..tyFloat128: s.add($a.floatVal)
|
||||||
|
else:
|
||||||
|
internalError a.info, "cannot marshal at compile-time " & t.typeToString
|
||||||
|
|
||||||
|
proc storeAny*(s: var string; t: PType; a: PNode) =
|
||||||
|
var stored = initIntSet()
|
||||||
|
storeAny(s, t, a, stored)
|
||||||
|
|
||||||
|
proc loadAny(p: var JsonParser, t: PType,
|
||||||
|
tab: var Table[BiggestInt, PNode]): PNode =
|
||||||
|
case t.kind
|
||||||
|
of tyNone: assert false
|
||||||
|
of tyBool:
|
||||||
|
case p.kind
|
||||||
|
of jsonFalse: result = newIntNode(nkIntLit, 0)
|
||||||
|
of jsonTrue: result = newIntNode(nkIntLit, 1)
|
||||||
|
else: raiseParseErr(p, "'true' or 'false' expected for a bool")
|
||||||
|
next(p)
|
||||||
|
of tyChar:
|
||||||
|
if p.kind == jsonString:
|
||||||
|
var x = p.str
|
||||||
|
if x.len == 1:
|
||||||
|
result = newIntNode(nkIntLit, ord(x[0]))
|
||||||
|
next(p)
|
||||||
|
return
|
||||||
|
elif p.kind == jsonInt:
|
||||||
|
result = newIntNode(nkIntLit, getInt(p))
|
||||||
|
next(p)
|
||||||
|
return
|
||||||
|
raiseParseErr(p, "string of length 1 expected for a char")
|
||||||
|
of tyEnum:
|
||||||
|
if p.kind == jsonString:
|
||||||
|
for e in items(t.n):
|
||||||
|
if e.sym.name.s == p.str:
|
||||||
|
result = newIntNode(nkIntLit, e.sym.position)
|
||||||
|
next(p)
|
||||||
|
return
|
||||||
|
raiseParseErr(p, "string expected for an enum")
|
||||||
|
of tyArray:
|
||||||
|
if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for an array")
|
||||||
|
next(p)
|
||||||
|
result = newNode(nkBracket)
|
||||||
|
while p.kind != jsonArrayEnd and p.kind != jsonEof:
|
||||||
|
result.add loadAny(p, t.elemType, tab)
|
||||||
|
if p.kind == jsonArrayEnd: next(p)
|
||||||
|
else: raiseParseErr(p, "']' end of array expected")
|
||||||
|
of tySequence:
|
||||||
|
case p.kind
|
||||||
|
of jsonNull:
|
||||||
|
result = newNode(nkNilLit)
|
||||||
|
next(p)
|
||||||
|
of jsonArrayStart:
|
||||||
|
next(p)
|
||||||
|
result = newNode(nkBracket)
|
||||||
|
while p.kind != jsonArrayEnd and p.kind != jsonEof:
|
||||||
|
result.add loadAny(p, t.elemType, tab)
|
||||||
|
if p.kind == jsonArrayEnd: next(p)
|
||||||
|
else: raiseParseErr(p, "")
|
||||||
|
else:
|
||||||
|
raiseParseErr(p, "'[' expected for a seq")
|
||||||
|
of tyTuple:
|
||||||
|
if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
|
||||||
|
next(p)
|
||||||
|
result = newNode(nkPar)
|
||||||
|
var i = 0
|
||||||
|
while p.kind != jsonObjectEnd and p.kind != jsonEof:
|
||||||
|
if p.kind != jsonString:
|
||||||
|
raiseParseErr(p, "string expected for a field name")
|
||||||
|
next(p)
|
||||||
|
if i >= t.len:
|
||||||
|
raiseParseErr(p, "too many fields to tuple type " & typeToString(t))
|
||||||
|
result.add loadAny(p, t.sons[i], tab)
|
||||||
|
inc i
|
||||||
|
if p.kind == jsonObjectEnd: next(p)
|
||||||
|
else: raiseParseErr(p, "'}' end of object expected")
|
||||||
|
of tyObject:
|
||||||
|
if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
|
||||||
|
next(p)
|
||||||
|
result = newNode(nkPar)
|
||||||
|
result.sons = @[]
|
||||||
|
while p.kind != jsonObjectEnd and p.kind != jsonEof:
|
||||||
|
if p.kind != jsonString:
|
||||||
|
raiseParseErr(p, "string expected for a field name")
|
||||||
|
let field = lookupInRecord(t.n, getIdent(p.str))
|
||||||
|
if field.isNil:
|
||||||
|
raiseParseErr(p, "unknown field for object of type " & typeToString(t))
|
||||||
|
next(p)
|
||||||
|
if field.position >= result.sons.len:
|
||||||
|
setLen(result.sons, field.position+1)
|
||||||
|
result.sons[field.position] = loadAny(p, field.typ, tab)
|
||||||
|
if p.kind == jsonObjectEnd: next(p)
|
||||||
|
else: raiseParseErr(p, "'}' end of object expected")
|
||||||
|
of tySet:
|
||||||
|
if p.kind != jsonArrayStart: raiseParseErr(p, "'[' expected for a set")
|
||||||
|
next(p)
|
||||||
|
result = newNode(nkCurly)
|
||||||
|
while p.kind != jsonArrayEnd and p.kind != jsonEof:
|
||||||
|
result.add loadAny(p, t.lastSon, tab)
|
||||||
|
next(p)
|
||||||
|
if p.kind == jsonArrayEnd: next(p)
|
||||||
|
else: raiseParseErr(p, "']' end of array expected")
|
||||||
|
of tyPtr, tyRef:
|
||||||
|
case p.kind
|
||||||
|
of jsonNull:
|
||||||
|
result = newNode(nkNilLit)
|
||||||
|
next(p)
|
||||||
|
of jsonInt:
|
||||||
|
result = tab[p.getInt]
|
||||||
|
if result.isNil:
|
||||||
|
raiseParseErr(p, "cannot load object with address " & $p.getInt)
|
||||||
|
next(p)
|
||||||
|
of jsonArrayStart:
|
||||||
|
next(p)
|
||||||
|
if p.kind == jsonInt:
|
||||||
|
let idx = p.getInt
|
||||||
|
next(p)
|
||||||
|
result = loadAny(p, t.lastSon, tab)
|
||||||
|
tab[idx] = result
|
||||||
|
else: raiseParseErr(p, "index for ref type expected")
|
||||||
|
if p.kind == jsonArrayEnd: next(p)
|
||||||
|
else: raiseParseErr(p, "']' end of ref-address pair expected")
|
||||||
|
else: raiseParseErr(p, "int for pointer type expected")
|
||||||
|
of tyString, tyCString:
|
||||||
|
case p.kind
|
||||||
|
of jsonNull:
|
||||||
|
result = newNode(nkNilLit)
|
||||||
|
next(p)
|
||||||
|
of jsonString:
|
||||||
|
result = newStrNode(nkStrLit, p.str)
|
||||||
|
next(p)
|
||||||
|
else: raiseParseErr(p, "string expected")
|
||||||
|
of tyInt..tyInt64, tyUInt..tyUInt64:
|
||||||
|
if p.kind == jsonInt:
|
||||||
|
result = newIntNode(nkIntLit, getInt(p))
|
||||||
|
next(p)
|
||||||
|
return
|
||||||
|
raiseParseErr(p, "int expected")
|
||||||
|
of tyFloat..tyFloat128:
|
||||||
|
if p.kind == jsonFloat:
|
||||||
|
result = newFloatNode(nkFloatLit, getFloat(p))
|
||||||
|
next(p)
|
||||||
|
return
|
||||||
|
raiseParseErr(p, "float expected")
|
||||||
|
of tyRange, tyGenericInst: result = loadAny(p, t.lastSon, tab)
|
||||||
|
else:
|
||||||
|
internalError "cannot marshal at compile-time " & t.typeToString
|
||||||
|
|
||||||
|
proc loadAny*(s: string; t: PType): PNode =
|
||||||
|
var tab = initTable[BiggestInt, PNode]()
|
||||||
|
var p: JsonParser
|
||||||
|
open(p, newStringStream(s), "unknown file")
|
||||||
|
next(p)
|
||||||
|
result = loadAny(p, t, tab)
|
||||||
|
close(p)
|
||||||
|
|
@ -15,8 +15,8 @@
|
||||||
## type than its compiletime type:
|
## type than its compiletime type:
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## type
|
## type
|
||||||
## TA = object
|
## TA = object
|
||||||
## TB = object of TA
|
## TB = object of TA
|
||||||
## f: int
|
## f: int
|
||||||
|
|
@ -28,6 +28,8 @@
|
||||||
## new(b)
|
## new(b)
|
||||||
## a = b
|
## a = b
|
||||||
## echo($$a[]) # produces "{}", not "{f: 0}"
|
## echo($$a[]) # produces "{}", not "{f: 0}"
|
||||||
|
##
|
||||||
|
## **Note**: The ``to`` and ``$$`` operations are available at compile-time!
|
||||||
|
|
||||||
import streams, typeinfo, json, intsets, tables
|
import streams, typeinfo, json, intsets, tables
|
||||||
|
|
||||||
|
|
@ -38,7 +40,12 @@ proc storeAny(s: Stream, a: TAny, stored: var IntSet) =
|
||||||
case a.kind
|
case a.kind
|
||||||
of akNone: assert false
|
of akNone: assert false
|
||||||
of akBool: s.write($getBool(a))
|
of akBool: s.write($getBool(a))
|
||||||
of akChar: s.write(escapeJson($getChar(a)))
|
of akChar:
|
||||||
|
let ch = getChar(a)
|
||||||
|
if ch < '\128':
|
||||||
|
s.write(escapeJson($ch))
|
||||||
|
else:
|
||||||
|
s.write($int(ch))
|
||||||
of akArray, akSequence:
|
of akArray, akSequence:
|
||||||
if a.kind == akSequence and isNil(a): s.write("null")
|
if a.kind == akSequence and isNil(a): s.write("null")
|
||||||
else:
|
else:
|
||||||
|
|
@ -92,7 +99,7 @@ proc storeAny(s: Stream, a: TAny, stored: var IntSet) =
|
||||||
proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
case a.kind
|
case a.kind
|
||||||
of akNone: assert false
|
of akNone: assert false
|
||||||
of akBool:
|
of akBool:
|
||||||
case p.kind
|
case p.kind
|
||||||
of jsonFalse: setBiggestInt(a, 0)
|
of jsonFalse: setBiggestInt(a, 0)
|
||||||
of jsonTrue: setBiggestInt(a, 1)
|
of jsonTrue: setBiggestInt(a, 1)
|
||||||
|
|
@ -105,8 +112,12 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
setBiggestInt(a, ord(x[0]))
|
setBiggestInt(a, ord(x[0]))
|
||||||
next(p)
|
next(p)
|
||||||
return
|
return
|
||||||
|
elif p.kind == jsonInt:
|
||||||
|
setBiggestInt(a, getInt(p))
|
||||||
|
next(p)
|
||||||
|
return
|
||||||
raiseParseErr(p, "string of length 1 expected for a char")
|
raiseParseErr(p, "string of length 1 expected for a char")
|
||||||
of akEnum:
|
of akEnum:
|
||||||
if p.kind == jsonString:
|
if p.kind == jsonString:
|
||||||
setBiggestInt(a, getEnumOrdinal(a, p.str))
|
setBiggestInt(a, getEnumOrdinal(a, p.str))
|
||||||
next(p)
|
next(p)
|
||||||
|
|
@ -122,7 +133,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
if p.kind == jsonArrayEnd: next(p)
|
if p.kind == jsonArrayEnd: next(p)
|
||||||
else: raiseParseErr(p, "']' end of array expected")
|
else: raiseParseErr(p, "']' end of array expected")
|
||||||
of akSequence:
|
of akSequence:
|
||||||
case p.kind
|
case p.kind
|
||||||
of jsonNull:
|
of jsonNull:
|
||||||
setPointer(a, nil)
|
setPointer(a, nil)
|
||||||
next(p)
|
next(p)
|
||||||
|
|
@ -143,7 +154,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
|
if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
|
||||||
next(p)
|
next(p)
|
||||||
while p.kind != jsonObjectEnd and p.kind != jsonEof:
|
while p.kind != jsonObjectEnd and p.kind != jsonEof:
|
||||||
if p.kind != jsonString:
|
if p.kind != jsonString:
|
||||||
raiseParseErr(p, "string expected for a field name")
|
raiseParseErr(p, "string expected for a field name")
|
||||||
var fieldName = p.str
|
var fieldName = p.str
|
||||||
next(p)
|
next(p)
|
||||||
|
|
@ -160,7 +171,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
if p.kind == jsonArrayEnd: next(p)
|
if p.kind == jsonArrayEnd: next(p)
|
||||||
else: raiseParseErr(p, "']' end of array expected")
|
else: raiseParseErr(p, "']' end of array expected")
|
||||||
of akPtr, akRef:
|
of akPtr, akRef:
|
||||||
case p.kind
|
case p.kind
|
||||||
of jsonNull:
|
of jsonNull:
|
||||||
setPointer(a, nil)
|
setPointer(a, nil)
|
||||||
next(p)
|
next(p)
|
||||||
|
|
@ -170,7 +181,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
of jsonArrayStart:
|
of jsonArrayStart:
|
||||||
next(p)
|
next(p)
|
||||||
if a.kind == akRef: invokeNew(a)
|
if a.kind == akRef: invokeNew(a)
|
||||||
else: setPointer(a, alloc0(a.baseTypeSize))
|
else: setPointer(a, alloc0(a.baseTypeSize))
|
||||||
if p.kind == jsonInt:
|
if p.kind == jsonInt:
|
||||||
t[p.getInt] = getPointer(a)
|
t[p.getInt] = getPointer(a)
|
||||||
next(p)
|
next(p)
|
||||||
|
|
@ -179,8 +190,8 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
if p.kind == jsonArrayEnd: next(p)
|
if p.kind == jsonArrayEnd: next(p)
|
||||||
else: raiseParseErr(p, "']' end of ref-address pair expected")
|
else: raiseParseErr(p, "']' end of ref-address pair expected")
|
||||||
else: raiseParseErr(p, "int for pointer type expected")
|
else: raiseParseErr(p, "int for pointer type expected")
|
||||||
of akProc, akPointer, akCString:
|
of akProc, akPointer, akCString:
|
||||||
case p.kind
|
case p.kind
|
||||||
of jsonNull:
|
of jsonNull:
|
||||||
setPointer(a, nil)
|
setPointer(a, nil)
|
||||||
next(p)
|
next(p)
|
||||||
|
|
@ -189,7 +200,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
next(p)
|
next(p)
|
||||||
else: raiseParseErr(p, "int for pointer type expected")
|
else: raiseParseErr(p, "int for pointer type expected")
|
||||||
of akString:
|
of akString:
|
||||||
case p.kind
|
case p.kind
|
||||||
of jsonNull:
|
of jsonNull:
|
||||||
setPointer(a, nil)
|
setPointer(a, nil)
|
||||||
next(p)
|
next(p)
|
||||||
|
|
@ -197,7 +208,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||||
setString(a, p.str)
|
setString(a, p.str)
|
||||||
next(p)
|
next(p)
|
||||||
else: raiseParseErr(p, "string expected")
|
else: raiseParseErr(p, "string expected")
|
||||||
of akInt..akInt64, akUInt..akUInt64:
|
of akInt..akInt64, akUInt..akUInt64:
|
||||||
if p.kind == jsonInt:
|
if p.kind == jsonInt:
|
||||||
setBiggestInt(a, getInt(p))
|
setBiggestInt(a, getInt(p))
|
||||||
next(p)
|
next(p)
|
||||||
|
|
@ -243,22 +254,22 @@ proc to*[T](data: string): T =
|
||||||
## reads data and transforms it to a ``T``.
|
## reads data and transforms it to a ``T``.
|
||||||
var tab = initTable[BiggestInt, pointer]()
|
var tab = initTable[BiggestInt, pointer]()
|
||||||
loadAny(newStringStream(data), toAny(result), tab)
|
loadAny(newStringStream(data), toAny(result), tab)
|
||||||
|
|
||||||
when not defined(testing) and isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
template testit(x: expr) = echo($$to[type(x)]($$x))
|
template testit(x: expr) = echo($$to[type(x)]($$x))
|
||||||
|
|
||||||
var x: array[0..4, array[0..4, string]] = [
|
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"], ["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)
|
testit(x)
|
||||||
var test2: tuple[name: string, s: uint] = ("tuple test", 56u)
|
var test2: tuple[name: string, s: uint] = ("tuple test", 56u)
|
||||||
testit(test2)
|
testit(test2)
|
||||||
|
|
||||||
type
|
type
|
||||||
TE = enum
|
TE = enum
|
||||||
blah, blah2
|
blah, blah2
|
||||||
|
|
||||||
TestObj = object
|
TestObj = object
|
||||||
test, asd: int
|
test, asd: int
|
||||||
case test2: TE
|
case test2: TE
|
||||||
|
|
@ -266,7 +277,7 @@ when not defined(testing) and isMainModule:
|
||||||
help: string
|
help: string
|
||||||
else:
|
else:
|
||||||
nil
|
nil
|
||||||
|
|
||||||
PNode = ref TNode
|
PNode = ref TNode
|
||||||
TNode = object
|
TNode = object
|
||||||
next, prev: PNode
|
next, prev: PNode
|
||||||
|
|
@ -294,7 +305,7 @@ when not defined(testing) and isMainModule:
|
||||||
test4.a = "ref string test: A"
|
test4.a = "ref string test: A"
|
||||||
test4.b = "ref string test: B"
|
test4.b = "ref string test: B"
|
||||||
testit(test4)
|
testit(test4)
|
||||||
|
|
||||||
var test5 = @[(0,1),(2,3),(4,5)]
|
var test5 = @[(0,1),(2,3),(4,5)]
|
||||||
testit(test5)
|
testit(test5)
|
||||||
|
|
||||||
|
|
@ -305,7 +316,7 @@ when not defined(testing) and isMainModule:
|
||||||
echo($$test7)
|
echo($$test7)
|
||||||
testit(test7)
|
testit(test7)
|
||||||
|
|
||||||
type
|
type
|
||||||
TA {.inheritable.} = object
|
TA {.inheritable.} = object
|
||||||
TB = object of TA
|
TB = object of TA
|
||||||
f: int
|
f: int
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,31 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) =
|
||||||
result = Rune(ord(s[i]))
|
result = Rune(ord(s[i]))
|
||||||
when doInc: inc(i)
|
when doInc: inc(i)
|
||||||
|
|
||||||
|
proc validateUtf8*(s: string): int =
|
||||||
|
## returns the position of the invalid byte in ``s`` if the string ``s`` does
|
||||||
|
## not hold valid UTF-8 data. Otherwise -1 is returned.
|
||||||
|
var i = 0
|
||||||
|
let L = s.len
|
||||||
|
while i < L:
|
||||||
|
if ord(s[i]) <=% 127:
|
||||||
|
inc(i)
|
||||||
|
elif ord(s[i]) shr 5 == 0b110:
|
||||||
|
if i+1 < L and ord(s[i+1]) shr 6 == 0b10: inc(i, 2)
|
||||||
|
else: return i
|
||||||
|
elif ord(s[i]) shr 4 == 0b1110:
|
||||||
|
if i+2 < L and ord(s[i+1]) shr 6 == 0b10 and ord(s[i+2]) shr 6 == 0b10:
|
||||||
|
inc i, 3
|
||||||
|
else: return i
|
||||||
|
elif ord(s[i]) shr 3 == 0b11110:
|
||||||
|
if i+3 < L and ord(s[i+1]) shr 6 == 0b10 and
|
||||||
|
ord(s[i+2]) shr 6 == 0b10 and
|
||||||
|
ord(s[i+3]) shr 6 == 0b10:
|
||||||
|
inc i, 4
|
||||||
|
else: return i
|
||||||
|
else:
|
||||||
|
return i
|
||||||
|
return -1
|
||||||
|
|
||||||
proc runeAt*(s: string, i: Natural): Rune =
|
proc runeAt*(s: string, i: Natural): Rune =
|
||||||
## returns the unicode character in `s` at byte index `i`
|
## returns the unicode character in `s` at byte index `i`
|
||||||
fastRuneAt(s, i, result, false)
|
fastRuneAt(s, i, result, false)
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
# Test the dialogs module
|
|
||||||
|
|
||||||
import dialogs, gtk2
|
|
||||||
|
|
||||||
gtk2.nimrod_init()
|
|
||||||
|
|
||||||
var x = chooseFilesToOpen(nil)
|
|
||||||
for a in items(x):
|
|
||||||
writeln(stdout, a)
|
|
||||||
|
|
||||||
info(nil, "start with an info box")
|
|
||||||
warning(nil, "now a warning ...")
|
|
||||||
error(nil, "... and an error!")
|
|
||||||
|
|
||||||
writeln(stdout, chooseFileToOpen(nil))
|
|
||||||
writeln(stdout, chooseFileToSave(nil))
|
|
||||||
writeln(stdout, chooseDir(nil))
|
|
||||||
|
|
@ -130,6 +130,7 @@ News
|
||||||
- ``system.len`` for strings and sequences now returns 0 for nil.
|
- ``system.len`` for strings and sequences now returns 0 for nil.
|
||||||
|
|
||||||
- A single underscore can now be used to discard values when unpacking tuples.
|
- A single underscore can now be used to discard values when unpacking tuples.
|
||||||
|
- ``marshal.$$`` and ``marshal.to`` can be executed at compile-time.
|
||||||
|
|
||||||
|
|
||||||
Library additions
|
Library additions
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue