Remodel Preserves[E] into Value
Using a parameterized Preserve[E] type is too much hassle. Replace with a Value type with an embedded field of "ref RootObj".
This commit is contained in:
parent
85cef2e1d2
commit
867d25afee
|
@ -1 +1,2 @@
|
||||||
NIM_FLAGS += --path:$(TUP_CWD)/../nim
|
NIM_FLAGS += --path:$(TUP_CWD)/../nim
|
||||||
|
NIM_FLAGS += --path:$(TUP_CWD)/../npeg/src
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Package
|
# Package
|
||||||
|
|
||||||
version = "20231227"
|
version = "20231230"
|
||||||
author = "Emery Hemingway"
|
author = "Emery Hemingway"
|
||||||
description = "data model and serialization format"
|
description = "data model and serialization format"
|
||||||
license = "Unlicense"
|
license = "Unlicense"
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
include_rules
|
include_rules
|
||||||
: preserves.nim |> !nim_run |>
|
: preserves.nim |> !nim_check |>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -7,3 +7,4 @@ NIM_FLAGS_preserves_schemac += -d:npegDotDir="../.."
|
||||||
DOT_FILES = ../../Atom.dot ../../Document.dot ../../Schema.dot
|
DOT_FILES = ../../Atom.dot ../../Document.dot ../../Schema.dot
|
||||||
: preserves_schemac.nim |> !nim_bin |> $(BIN_DIR)/preserves-schemac | $(DOT_FILES) $(BIN_DIR)/<preserves-schemac>
|
: preserves_schemac.nim |> !nim_bin |> $(BIN_DIR)/preserves-schemac | $(DOT_FILES) $(BIN_DIR)/<preserves-schemac>
|
||||||
: foreach $(DOT_FILES) |> dot -Tsvg -LO %f > %o |> ../../%B-Grammer-Graph.svg
|
: foreach $(DOT_FILES) |> dot -Tsvg -LO %f > %o |> ../../%B-Grammer-Graph.svg
|
||||||
|
: foreach *hooks.nim |> !nim_run |>
|
||||||
|
|
|
@ -11,10 +11,10 @@ const
|
||||||
fullTimeFormat = "HH:mm:sszzz"
|
fullTimeFormat = "HH:mm:sszzz"
|
||||||
dateTimeFormat = "yyyy-MM-dd'T'HH:mm:sszzz"
|
dateTimeFormat = "yyyy-MM-dd'T'HH:mm:sszzz"
|
||||||
|
|
||||||
proc toPreserveHook*(dt: DateTime; E: typedesc): Preserve[E] =
|
proc toPreservesHook*(dt: DateTime): Value =
|
||||||
initRecord[E](toSymbol("rfc3339", E), toPreserve($dt, E))
|
initRecord("rfc3339", toPreserves($dt))
|
||||||
|
|
||||||
proc fromPreserveHook*[E](dt: var DateTime; pr: Preserve[E]): bool =
|
proc fromPreservesHook*(dt: var DateTime; pr: Value): bool =
|
||||||
result = pr.isRecord(label, 1) and pr.record[0].isString
|
result = pr.isRecord(label, 1) and pr.record[0].isString
|
||||||
if result:
|
if result:
|
||||||
try:
|
try:
|
||||||
|
@ -39,6 +39,6 @@ runnableExamples:
|
||||||
import preserves
|
import preserves
|
||||||
var a, b: DateTime
|
var a, b: DateTime
|
||||||
a = now()
|
a = now()
|
||||||
var pr = a.toPreserveHook(void)
|
var pr = a.toPreservesHook()
|
||||||
check fromPreserveHook(b, pr)
|
check b.fromPreservesHook(pr)
|
||||||
check $a == $b
|
check $a == $b
|
||||||
|
|
|
@ -4,51 +4,51 @@
|
||||||
import std/[json, tables]
|
import std/[json, tables]
|
||||||
import ../preserves
|
import ../preserves
|
||||||
|
|
||||||
proc toPreserveHook*(js: JsonNode; E: typedesc): Preserve[E] =
|
proc toPreservesHook*(js: JsonNode): Value =
|
||||||
case js.kind
|
case js.kind
|
||||||
of JString:
|
of JString:
|
||||||
result = Preserve[E](kind: pkString, string: js.str)
|
result = js.str.toPreserves()
|
||||||
of JInt:
|
of JInt:
|
||||||
result = Preserve[E](kind: pkRegister, register: js.num)
|
result = js.num.toPreserves()
|
||||||
of JFloat:
|
of JFloat:
|
||||||
result = Preserve[E](kind: pkDouble, double: js.fnum)
|
result = js.fnum.toPreserves()
|
||||||
of JBool:
|
of JBool:
|
||||||
result = case js.bval
|
result = case js.bval
|
||||||
of false: toSymbol("false", E)
|
of false: toSymbol("false")
|
||||||
of true: toSymbol("true", E)
|
of true: toSymbol("true")
|
||||||
of JNull:
|
of JNull:
|
||||||
result = toSymbol("null", E)
|
result = toSymbol("null")
|
||||||
of JObject:
|
of JObject:
|
||||||
result = Preserve[E](kind: pkDictionary)
|
result = Value(kind: pkDictionary)
|
||||||
for key, val in js.fields.pairs:
|
for key, val in js.fields.pairs:
|
||||||
result[Preserve[E](kind: pkSymbol, symbol: Symbol key)] = toPreserveHook(val, E)
|
result[Value(kind: pkSymbol, symbol: Symbol key)] = toPreservesHook(val)
|
||||||
of JArray:
|
of JArray:
|
||||||
result = Preserve[E](kind: pkSequence,
|
result = Value(kind: pkSequence,
|
||||||
sequence: newSeq[Preserve[E]](js.elems.len))
|
sequence: newSeq[Value](js.elems.len))
|
||||||
for i, e in js.elems:
|
for i, e in js.elems:
|
||||||
result.sequence[i] = toPreserveHook(e, E)
|
result.sequence[i] = toPreservesHook(e)
|
||||||
|
|
||||||
proc fromPreserveHook*[E](js: var JsonNode; prs: Preserve[E]): bool =
|
proc fromPreservesHook*(js: var JsonNode; pr: Value): bool =
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
import std/json
|
import std/json
|
||||||
var js = JsonNode()
|
var js = JsonNode()
|
||||||
var pr = js.toPreserveHook(void)
|
var pr = js.toPreservesHook()
|
||||||
assert fromPreserveHook(js, pr)
|
assert js.fromPreservesHook(pr)
|
||||||
fromJsonHook(pr, js)
|
fromJsonHook(pr, js)
|
||||||
js = toJsonHook(pr)
|
js = toJsonHook(pr)
|
||||||
case prs.kind:
|
case pr.kind:
|
||||||
of pkBoolean:
|
of pkBoolean:
|
||||||
js = newJBool(prs.bool)
|
js = newJBool(pr.bool)
|
||||||
of pkFloat:
|
of pkFloat:
|
||||||
js = newJFloat(prs.float)
|
js = newJFloat(pr.float)
|
||||||
of pkDouble:
|
of pkDouble:
|
||||||
js = newJFloat(prs.double)
|
js = newJFloat(pr.double)
|
||||||
of pkRegister:
|
of pkRegister:
|
||||||
js = newJInt(prs.register)
|
js = newJInt(pr.register)
|
||||||
of pkString:
|
of pkString:
|
||||||
js = newJString(prs.string)
|
js = newJString(pr.string)
|
||||||
of pkSymbol:
|
of pkSymbol:
|
||||||
case prs.symbol.string
|
case pr.symbol.string
|
||||||
of "false":
|
of "false":
|
||||||
js = newJBool(false)
|
js = newJBool(false)
|
||||||
of "true":
|
of "true":
|
||||||
|
@ -59,38 +59,38 @@ proc fromPreserveHook*[E](js: var JsonNode; prs: Preserve[E]): bool =
|
||||||
return false
|
return false
|
||||||
of pkSequence:
|
of pkSequence:
|
||||||
js = newJArray()
|
js = newJArray()
|
||||||
js.elems.setLen(prs.sequence.len)
|
js.elems.setLen(pr.sequence.len)
|
||||||
for i, val in prs.sequence:
|
for i, val in pr.sequence:
|
||||||
if not fromPreserveHook(js.elems[i], val):
|
if not js.elems[i].fromPreservesHook(val):
|
||||||
return false
|
return false
|
||||||
of pkSet:
|
of pkSet:
|
||||||
js = newJArray()
|
js = newJArray()
|
||||||
js.elems.setLen(prs.set.len)
|
js.elems.setLen(pr.set.len)
|
||||||
var i: int
|
var i: int
|
||||||
for val in prs.set:
|
for val in pr.set:
|
||||||
if not fromPreserveHook(js.elems[i], val):
|
if not js.elems[i].fromPreservesHook(val):
|
||||||
return false
|
return false
|
||||||
inc i
|
inc i
|
||||||
of pkDictionary:
|
of pkDictionary:
|
||||||
js = newJObject()
|
js = newJObject()
|
||||||
for (key, val) in prs.dict.items:
|
for (key, val) in pr.dict.items:
|
||||||
case key.kind
|
case key.kind
|
||||||
of pkSymbol:
|
of pkSymbol:
|
||||||
var jsVal: JsonNode
|
var jsVal: JsonNode
|
||||||
if not fromPreserveHook(jsVal, val): return false
|
if not jsVal.fromPreservesHook(val): return false
|
||||||
js[string key.symbol] = jsVal
|
js[string key.symbol] = jsVal
|
||||||
of pkString:
|
of pkString:
|
||||||
var jsVal: JsonNode
|
var jsVal: JsonNode
|
||||||
if not fromPreserveHook(jsVal, val): return false
|
if not jsVal.fromPreservesHook(val): return false
|
||||||
js[key.string] = jsVal
|
js[key.string] = jsVal
|
||||||
else:
|
else:
|
||||||
return false
|
return false
|
||||||
else: return false
|
else: return false
|
||||||
true
|
true
|
||||||
|
|
||||||
proc toJsonHook*[E](pr: Preserve[E]): JsonNode =
|
proc toJsonHook*(pr: Value): JsonNode =
|
||||||
if not fromPreserveHook(result, pr):
|
if not result.fromPreservesHook(pr):
|
||||||
raise newException(ValueError, "cannot convert Preserves value to JSON")
|
raise newException(ValueError, "cannot convert Preserves value to JSON")
|
||||||
|
|
||||||
proc fromJsonHook*[E](pr: var Preserve[E]; js: JsonNode) =
|
proc fromJsonHook*(pr: var Value; js: JsonNode) =
|
||||||
pr = toPreserveHook(js, E)
|
pr = toPreservesHook(js)
|
||||||
|
|
|
@ -138,11 +138,7 @@ proc embeddedIdentString(scm: Schema): string =
|
||||||
proc embeddedIdent(scm: Schema): PNode =
|
proc embeddedIdent(scm: Schema): PNode =
|
||||||
ident(embeddedIdentString(scm))
|
ident(embeddedIdentString(scm))
|
||||||
|
|
||||||
proc preserveIdent(scm: Schema): Pnode =
|
proc preserveIdent(scm: Schema): Pnode = ident"Value"
|
||||||
if scm.hasEmbeddedType:
|
|
||||||
nkBracketExpr.newTree(ident"Preserve", embeddedIdent(scm))
|
|
||||||
else:
|
|
||||||
nkBracketExpr.newTree(ident"Preserve", ident"void")
|
|
||||||
|
|
||||||
proc parameterize(scm: Schema; node: PNode; embeddable: bool): PNode =
|
proc parameterize(scm: Schema; node: PNode; embeddable: bool): PNode =
|
||||||
if embeddable and node.kind notin {nkBracketExpr}:
|
if embeddable and node.kind notin {nkBracketExpr}:
|
||||||
|
@ -152,7 +148,7 @@ proc parameterize(scm: Schema; node: PNode; embeddable: bool): PNode =
|
||||||
proc parameterize(scm: Schema; spec: TypeSpec): PNode =
|
proc parameterize(scm: Schema; spec: TypeSpec): PNode =
|
||||||
parameterize(scm, spec.node, spec.isEmbedded)
|
parameterize(scm, spec.node, spec.isEmbedded)
|
||||||
|
|
||||||
proc hash(r: Ref): Hash = r.toPreserve.hash
|
proc hash(r: Ref): Hash = r.toPreserves.hash
|
||||||
type RefSet = HashSet[Ref]
|
type RefSet = HashSet[Ref]
|
||||||
|
|
||||||
proc attrs(loc: Location; pat: Pattern; seen: RefSet): Attributes {.gcsafe.}
|
proc attrs(loc: Location; pat: Pattern; seen: RefSet): Attributes {.gcsafe.}
|
||||||
|
@ -588,10 +584,10 @@ proc addFields(recList: PNode; loc: Location; known: var TypeTable; pat: Pattern
|
||||||
|
|
||||||
proc addFields(recList: PNode; loc: Location; known: var TypeTable; entries: DictionaryEntries; parentName: string): PNode {.discardable.} =
|
proc addFields(recList: PNode; loc: Location; known: var TypeTable; entries: DictionaryEntries; parentName: string): PNode {.discardable.} =
|
||||||
var sortedEntries =
|
var sortedEntries =
|
||||||
initOrderedTable[Preserve[void], NamedSimplePattern](entries.len)
|
initOrderedTable[Value, NamedSimplePattern](entries.len)
|
||||||
for key, val in entries.pairs:
|
for key, val in entries.pairs:
|
||||||
sortedEntries[key] = val
|
sortedEntries[key] = val
|
||||||
sort(sortedEntries) do (x, y: (Preserve[void], NamedSimplePattern)) -> int:
|
sort(sortedEntries) do (x, y: (Value, NamedSimplePattern)) -> int:
|
||||||
cmp(x[0], y[0])
|
cmp(x[0], y[0])
|
||||||
for key, val in sortedEntries.pairs:
|
for key, val in sortedEntries.pairs:
|
||||||
doAssert(key.isSymbol)
|
doAssert(key.isSymbol)
|
||||||
|
@ -834,7 +830,7 @@ proc renderNimBundle*(bundle: Bundle): Table[string, string] =
|
||||||
newEmpty(),
|
newEmpty(),
|
||||||
nkStmtList.newTree(
|
nkStmtList.newTree(
|
||||||
nkCall.newTree(ident"$",
|
nkCall.newTree(ident"$",
|
||||||
nkCall.newTree(ident"toPreserve", ident"x", embeddedIdent(scm)))))
|
nkCall.newTree(ident"toPreserves", ident"x", embeddedIdent(scm)))))
|
||||||
procs.add nkProcDef.newTree(
|
procs.add nkProcDef.newTree(
|
||||||
"encode".ident.toExport,
|
"encode".ident.toExport,
|
||||||
newEmpty(),
|
newEmpty(),
|
||||||
|
@ -849,7 +845,7 @@ proc renderNimBundle*(bundle: Bundle): Table[string, string] =
|
||||||
newEmpty(),
|
newEmpty(),
|
||||||
nkStmtList.newTree(
|
nkStmtList.newTree(
|
||||||
nkCall.newTree(ident"encode",
|
nkCall.newTree(ident"encode",
|
||||||
nkCall.newTree(ident"toPreserve", ident"x", embeddedIdent(scm)))))
|
nkCall.newTree(ident"toPreserves", ident"x", embeddedIdent(scm)))))
|
||||||
if not unembeddableType.isNil:
|
if not unembeddableType.isNil:
|
||||||
procs.add nkProcDef.newTree(
|
procs.add nkProcDef.newTree(
|
||||||
"$".toFieldIdent,
|
"$".toFieldIdent,
|
||||||
|
@ -865,7 +861,7 @@ proc renderNimBundle*(bundle: Bundle): Table[string, string] =
|
||||||
newEmpty(),
|
newEmpty(),
|
||||||
nkStmtList.newTree(
|
nkStmtList.newTree(
|
||||||
nkCall.newTree(ident"$",
|
nkCall.newTree(ident"$",
|
||||||
nkCall.newTree(ident"toPreserve", ident"x"))))
|
nkCall.newTree(ident"toPreserves", ident"x"))))
|
||||||
procs.add nkProcDef.newTree(
|
procs.add nkProcDef.newTree(
|
||||||
"encode".ident.toExport,
|
"encode".ident.toExport,
|
||||||
newEmpty(),
|
newEmpty(),
|
||||||
|
@ -880,7 +876,7 @@ proc renderNimBundle*(bundle: Bundle): Table[string, string] =
|
||||||
newEmpty(),
|
newEmpty(),
|
||||||
nkStmtList.newTree(
|
nkStmtList.newTree(
|
||||||
nkCall.newTree(ident"encode", nkCall.newTree(
|
nkCall.newTree(ident"encode", nkCall.newTree(
|
||||||
ident"toPreserve", ident"x"))))
|
ident"toPreserves", ident"x"))))
|
||||||
var module = newNode(nkStmtList).add(
|
var module = newNode(nkStmtList).add(
|
||||||
imports,
|
imports,
|
||||||
typeSection
|
typeSection
|
||||||
|
@ -902,7 +898,7 @@ when isMainModule:
|
||||||
writeFile(path, txt)
|
writeFile(path, txt)
|
||||||
stdout.writeLine(path)
|
stdout.writeLine(path)
|
||||||
|
|
||||||
import std/[options, os, parseopt]
|
import std/[os, parseopt]
|
||||||
var inputs: seq[string]
|
var inputs: seq[string]
|
||||||
for kind, key, val in getopt():
|
for kind, key, val in getopt():
|
||||||
case kind
|
case kind
|
||||||
|
@ -934,9 +930,9 @@ when isMainModule:
|
||||||
let raw = readFile inputPath
|
let raw = readFile inputPath
|
||||||
if raw[0] == 0xb4.char:
|
if raw[0] == 0xb4.char:
|
||||||
var pr = decodePreserves raw
|
var pr = decodePreserves raw
|
||||||
if not fromPreserve(bundle, pr):
|
if not fromPreserves(bundle, pr):
|
||||||
var schema: Schema
|
var schema: Schema
|
||||||
if fromPreserve(schema, pr):
|
if fromPreserves(schema, pr):
|
||||||
bundle.modules[@[Symbol fileName]] = schema
|
bundle.modules[@[Symbol fileName]] = schema
|
||||||
else:
|
else:
|
||||||
new bundle
|
new bundle
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
threads:off
|
|
|
@ -33,7 +33,7 @@ when isMainModule:
|
||||||
if not fileExists inputPath:
|
if not fileExists inputPath:
|
||||||
quit(inputPath & " does not exist or is not a file")
|
quit(inputPath & " does not exist or is not a file")
|
||||||
var schema = parsePreservesSchema(readFile(inputPath))
|
var schema = parsePreservesSchema(readFile(inputPath))
|
||||||
write(outStream, schema.toPreserve)
|
write(outStream, schema.toPreserves)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
let bundle = Bundle()
|
let bundle = Bundle()
|
||||||
|
@ -53,6 +53,6 @@ when isMainModule:
|
||||||
if bundle.modules.len == 0:
|
if bundle.modules.len == 0:
|
||||||
quit "no schemas parsed"
|
quit "no schemas parsed"
|
||||||
else:
|
else:
|
||||||
write(outStream, bundle.toPreserve)
|
write(outStream, bundle.toPreserves)
|
||||||
|
|
||||||
close(outStream)
|
close(outStream)
|
||||||
|
|
|
@ -15,31 +15,31 @@ proc readVarint(s: Stream): uint =
|
||||||
c = uint s.readUint8
|
c = uint s.readUint8
|
||||||
result = result or (c shl shift)
|
result = result or (c shl shift)
|
||||||
|
|
||||||
proc decodePreserves*(s: Stream; E = void): Preserve[E] =
|
proc decodePreserves*(s: Stream): Value =
|
||||||
## Decode a Preserves value from a binary-encoded stream.
|
## Decode a Preserves value from a binary-encoded stream.
|
||||||
if s.atEnd: raise newException(IOError, "End of Preserves stream")
|
if s.atEnd: raise newException(IOError, "End of Preserves stream")
|
||||||
const endMarker = 0x84
|
const endMarker = 0x84
|
||||||
let tag = s.readUint8()
|
let tag = s.readUint8()
|
||||||
case tag
|
case tag
|
||||||
of 0x80: result = Preserve[E](kind: pkBoolean, bool: false)
|
of 0x80: result = Value(kind: pkBoolean, bool: false)
|
||||||
of 0x81: result = Preserve[E](kind: pkBoolean, bool: true)
|
of 0x81: result = Value(kind: pkBoolean, bool: true)
|
||||||
of 0x85:
|
of 0x85:
|
||||||
discard decodePreserves(s, E)
|
discard decodePreserves(s)
|
||||||
result = decodePreserves(s, E)
|
result = decodePreserves(s)
|
||||||
of 0x86:
|
of 0x86:
|
||||||
result = decodePreserves(s, E)
|
result = decodePreserves(s)
|
||||||
result.embedded = true
|
result.embedded = true
|
||||||
of 0x87:
|
of 0x87:
|
||||||
var N: int
|
var N: int
|
||||||
let n = int s.readUint8()
|
let n = int s.readUint8()
|
||||||
case n
|
case n
|
||||||
of 4:
|
of 4:
|
||||||
result = Preserve[E](kind: pkFloat)
|
result = Value(kind: pkFloat)
|
||||||
var buf: uint32
|
var buf: uint32
|
||||||
N = s.readData(addr buf, sizeof(buf))
|
N = s.readData(addr buf, sizeof(buf))
|
||||||
bigEndian32(addr result.float, addr buf)
|
bigEndian32(addr result.float, addr buf)
|
||||||
of 8:
|
of 8:
|
||||||
result = Preserve[E](kind: pkDouble)
|
result = Value(kind: pkDouble)
|
||||||
var buf: uint64
|
var buf: uint64
|
||||||
N = s.readData(addr buf, sizeof(buf))
|
N = s.readData(addr buf, sizeof(buf))
|
||||||
bigEndian64(addr result.double, addr buf)
|
bigEndian64(addr result.double, addr buf)
|
||||||
|
@ -49,7 +49,7 @@ proc decodePreserves*(s: Stream; E = void): Preserve[E] =
|
||||||
of 0xb0:
|
of 0xb0:
|
||||||
var n = int s.readVarint()
|
var n = int s.readVarint()
|
||||||
if n <= sizeof(int):
|
if n <= sizeof(int):
|
||||||
result = Preserve[E](kind: pkRegister)
|
result = Value(kind: pkRegister)
|
||||||
if n > 0:
|
if n > 0:
|
||||||
var
|
var
|
||||||
buf: array[sizeof(int), byte]
|
buf: array[sizeof(int), byte]
|
||||||
|
@ -65,7 +65,7 @@ proc decodePreserves*(s: Stream; E = void): Preserve[E] =
|
||||||
bigEndian64(addr result.register, addr buf[0])
|
bigEndian64(addr result.register, addr buf[0])
|
||||||
else: {.error: "int size " & $buf.len & " not supported here".}
|
else: {.error: "int size " & $buf.len & " not supported here".}
|
||||||
else:
|
else:
|
||||||
result = Preserve[E](kind: pkBigInt)
|
result = Value(kind: pkBigInt)
|
||||||
var buf = newSeq[byte](n)
|
var buf = newSeq[byte](n)
|
||||||
if s.readData(addr buf[0], buf.len) != n:
|
if s.readData(addr buf[0], buf.len) != n:
|
||||||
raise newException(IOError, "short read")
|
raise newException(IOError, "short read")
|
||||||
|
@ -76,7 +76,7 @@ proc decodePreserves*(s: Stream; E = void): Preserve[E] =
|
||||||
else:
|
else:
|
||||||
result.bigint.fromBytes(buf, bigEndian)
|
result.bigint.fromBytes(buf, bigEndian)
|
||||||
of 0xb1:
|
of 0xb1:
|
||||||
result = Preserve[E](kind: pkString, string: newString(s.readVarint()))
|
result = Value(kind: pkString, string: newString(s.readVarint()))
|
||||||
if result.string.len > 0:
|
if result.string.len > 0:
|
||||||
if s.readData(addr result.string[0], result.string.len) != result.string.len:
|
if s.readData(addr result.string[0], result.string.len) != result.string.len:
|
||||||
raise newException(IOError, "short read")
|
raise newException(IOError, "short read")
|
||||||
|
@ -86,48 +86,48 @@ proc decodePreserves*(s: Stream; E = void): Preserve[E] =
|
||||||
let n = s.readData(addr data[0], data.len)
|
let n = s.readData(addr data[0], data.len)
|
||||||
if n != data.len:
|
if n != data.len:
|
||||||
raise newException(IOError, "short read")
|
raise newException(IOError, "short read")
|
||||||
result = Preserve[E](kind: pkByteString, bytes: data)
|
result = Value(kind: pkByteString, bytes: data)
|
||||||
of 0xb3:
|
of 0xb3:
|
||||||
var data = newString(s.readVarint())
|
var data = newString(s.readVarint())
|
||||||
if data.len > 0:
|
if data.len > 0:
|
||||||
let n = s.readData(addr data[0], data.len)
|
let n = s.readData(addr data[0], data.len)
|
||||||
if n != data.len:
|
if n != data.len:
|
||||||
raise newException(IOError, "short read")
|
raise newException(IOError, "short read")
|
||||||
result = Preserve[E](kind: pkSymbol, symbol: Symbol data)
|
result = Value(kind: pkSymbol, symbol: Symbol data)
|
||||||
of 0xb4:
|
of 0xb4:
|
||||||
result = Preserve[E](kind: pkRecord)
|
result = Value(kind: pkRecord)
|
||||||
var label = decodePreserves(s, E)
|
var label = decodePreserves(s)
|
||||||
while s.peekUint8() != endMarker:
|
while s.peekUint8() != endMarker:
|
||||||
result.record.add decodePreserves(s, E)
|
result.record.add decodePreserves(s)
|
||||||
result.record.add(move label)
|
result.record.add(move label)
|
||||||
discard s.readUint8()
|
discard s.readUint8()
|
||||||
of 0xb5:
|
of 0xb5:
|
||||||
result = Preserve[E](kind: pkSequence)
|
result = Value(kind: pkSequence)
|
||||||
while s.peekUint8() != endMarker:
|
while s.peekUint8() != endMarker:
|
||||||
result.sequence.add decodePreserves(s, E)
|
result.sequence.add decodePreserves(s)
|
||||||
discard s.readUint8()
|
discard s.readUint8()
|
||||||
of 0xb6:
|
of 0xb6:
|
||||||
result = Preserve[E](kind: pkSet)
|
result = Value(kind: pkSet)
|
||||||
while s.peekUint8() != endMarker:
|
while s.peekUint8() != endMarker:
|
||||||
incl(result, decodePreserves(s, E))
|
incl(result, decodePreserves(s))
|
||||||
discard s.readUint8()
|
discard s.readUint8()
|
||||||
of 0xb7:
|
of 0xb7:
|
||||||
result = Preserve[E](kind: pkDictionary)
|
result = Value(kind: pkDictionary)
|
||||||
while s.peekUint8() != endMarker:
|
while s.peekUint8() != endMarker:
|
||||||
result[decodePreserves(s, E)] = decodePreserves(s, E)
|
result[decodePreserves(s)] = decodePreserves(s)
|
||||||
discard s.readUint8()
|
discard s.readUint8()
|
||||||
of endMarker:
|
of endMarker:
|
||||||
raise newException(ValueError, "invalid Preserves stream")
|
raise newException(ValueError, "invalid Preserves stream")
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "invalid Preserves tag byte 0x" & tag.toHex(2))
|
raise newException(ValueError, "invalid Preserves tag byte 0x" & tag.toHex(2))
|
||||||
|
|
||||||
proc decodePreserves*(s: string; E = void): Preserve[E] =
|
proc decodePreserves*(s: string): Value =
|
||||||
## Decode a string of binary-encoded Preserves.
|
## Decode a string of binary-encoded Preserves.
|
||||||
decodePreserves(s.newStringStream, E)
|
decodePreserves(s.newStringStream)
|
||||||
|
|
||||||
proc decodePreserves*(s: seq[byte]; E = void): Preserve[E] =
|
proc decodePreserves*(s: seq[byte]): Value =
|
||||||
## Decode a byte-string of binary-encoded Preserves.
|
## Decode a byte-string of binary-encoded Preserves.
|
||||||
decodePreserves(cast[string](s), E)
|
decodePreserves(cast[string](s))
|
||||||
|
|
||||||
type BufferedDecoder* = object
|
type BufferedDecoder* = object
|
||||||
## Type for buffering binary Preserves before decoding.
|
## Type for buffering binary Preserves before decoding.
|
||||||
|
@ -163,14 +163,14 @@ proc feed*[T: byte|char](dec: var BufferedDecoder; data: openarray[T]) =
|
||||||
if data.len > 0:
|
if data.len > 0:
|
||||||
dec.feed(unsafeAddr data[0], data.len)
|
dec.feed(unsafeAddr data[0], data.len)
|
||||||
|
|
||||||
proc decode*(dec: var BufferedDecoder; E = void): (bool, Preserve[E]) =
|
proc decode*(dec: var BufferedDecoder): (bool, Value) =
|
||||||
## Decode from `dec`. If decoding fails the internal position of the
|
## Decode from `dec`. If decoding fails the internal position of the
|
||||||
## decoder does not advance.
|
## decoder does not advance.
|
||||||
if dec.appendPosition > 0:
|
if dec.appendPosition > 0:
|
||||||
assert(dec.decodePosition < dec.appendPosition)
|
assert(dec.decodePosition < dec.appendPosition)
|
||||||
dec.stream.setPosition(dec.decodePosition)
|
dec.stream.setPosition(dec.decodePosition)
|
||||||
try:
|
try:
|
||||||
result[1] = decodePreserves(dec.stream, E)
|
result[1] = decodePreserves(dec.stream)
|
||||||
result[0] = true
|
result[0] = true
|
||||||
dec.decodePosition = dec.stream.getPosition()
|
dec.decodePosition = dec.stream.getPosition()
|
||||||
if dec.decodePosition == dec.appendPosition:
|
if dec.decodePosition == dec.appendPosition:
|
||||||
|
|
|
@ -12,7 +12,7 @@ proc writeVarint(s: Stream; n: Natural) =
|
||||||
n = n shr 7
|
n = n shr 7
|
||||||
s.write(uint8 n and 0x7f)
|
s.write(uint8 n and 0x7f)
|
||||||
|
|
||||||
proc write*[E](str: Stream; pr: Preserve[E]) =
|
proc write*(str: Stream; pr: Value) =
|
||||||
## Write the binary-encoding of a Preserves value to a stream.
|
## Write the binary-encoding of a Preserves value to a stream.
|
||||||
if pr.embedded: str.write(0x86'u8)
|
if pr.embedded: str.write(0x86'u8)
|
||||||
case pr.kind:
|
case pr.kind:
|
||||||
|
@ -110,10 +110,10 @@ proc write*[E](str: Stream; pr: Preserve[E]) =
|
||||||
str.write(value)
|
str.write(value)
|
||||||
str.write(0x84'u8)
|
str.write(0x84'u8)
|
||||||
of pkEmbedded:
|
of pkEmbedded:
|
||||||
str.write(0x86'u8)
|
# str.write(0x86'u8)
|
||||||
str.write(pr.embed.toPreserve)
|
raise newException(ValueError, "cannot encode an embedded object")
|
||||||
|
|
||||||
proc encode*[E](pr: Preserve[E]): seq[byte] =
|
proc encode*(pr: Value): seq[byte] =
|
||||||
## Return the binary-encoding of a Preserves value.
|
## Return the binary-encoding of a Preserves value.
|
||||||
let s = newStringStream()
|
let s = newStringStream()
|
||||||
s.write pr
|
s.write pr
|
||||||
|
|
|
@ -10,7 +10,6 @@ import ../pegs
|
||||||
import ./decoding, ./values
|
import ./decoding, ./values
|
||||||
|
|
||||||
type
|
type
|
||||||
Value = Preserve[void]
|
|
||||||
Frame = tuple[value: Value, pos: int]
|
Frame = tuple[value: Value, pos: int]
|
||||||
Stack = seq[Frame]
|
Stack = seq[Frame]
|
||||||
|
|
||||||
|
@ -226,11 +225,6 @@ proc parsePreserves*(text: string): Value =
|
||||||
assert(stack.len == 1)
|
assert(stack.len == 1)
|
||||||
stack.pop.value
|
stack.pop.value
|
||||||
|
|
||||||
proc parsePreserves*(text: string; E: typedesc): Preserve[E] =
|
|
||||||
## Parse a textencoded Preserves `string` to a `Preserve[E]` value for embedded type `E`.
|
|
||||||
when E is void: parsePreserves(text)
|
|
||||||
else: mapEmbeds(parsePreserves(text), E)
|
|
||||||
|
|
||||||
proc parsePreservesAtom*(text: string): Atom =
|
proc parsePreservesAtom*(text: string): Atom =
|
||||||
## Parse a text-encoded Preserves `string` to a Preserves `Atom`.
|
## Parse a text-encoded Preserves `string` to a Preserves `Atom`.
|
||||||
let pegParser = peg("Atom", a: Atom):
|
let pegParser = peg("Atom", a: Atom):
|
||||||
|
|
|
@ -19,17 +19,17 @@ when isMainModule:
|
||||||
of "preserves_from_json":
|
of "preserves_from_json":
|
||||||
let
|
let
|
||||||
js = stdin.newFileStream.parseJson
|
js = stdin.newFileStream.parseJson
|
||||||
pr = js.toPreserve
|
pr = js.toPreserves
|
||||||
stdout.newFileStream.write(pr)
|
stdout.newFileStream.write(pr)
|
||||||
of "preserves_from_xml":
|
of "preserves_from_xml":
|
||||||
let
|
let
|
||||||
xn = stdin.newFileStream.parseXml
|
xn = stdin.newFileStream.parseXml
|
||||||
pr = xn.toPreserveHook(void)
|
pr = xn.toPreservesHook()
|
||||||
stdout.newFileStream.write(pr)
|
stdout.newFileStream.write(pr)
|
||||||
of "preserves_to_json":
|
of "preserves_to_json":
|
||||||
let
|
let
|
||||||
pr = stdin.readAll.decodePreserves
|
pr = stdin.readAll.decodePreserves
|
||||||
js = preserveTo(pr, JsonNode)
|
js = preservesTo(pr, JsonNode)
|
||||||
if js.isSome:
|
if js.isSome:
|
||||||
stdout.writeLine(get js)
|
stdout.writeLine(get js)
|
||||||
else:
|
else:
|
||||||
|
@ -37,7 +37,7 @@ when isMainModule:
|
||||||
of "preserves_to_xml":
|
of "preserves_to_xml":
|
||||||
let pr = stdin.readAll.decodePreserves
|
let pr = stdin.readAll.decodePreserves
|
||||||
var xn: XmlNode
|
var xn: XmlNode
|
||||||
if fromPreserve(xn, pr):
|
if fromPreserves(xn, pr):
|
||||||
stdout.writeLine(xn)
|
stdout.writeLine(xn)
|
||||||
else:
|
else:
|
||||||
quit("Preserves not convertable to XML")
|
quit("Preserves not convertable to XML")
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[base64, endians, math, sequtils, streams, strutils, unicode]
|
import std/[base64, endians, math, sequtils, streams, strutils]
|
||||||
import bigints
|
import bigints
|
||||||
import ./values
|
import ./values
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ proc writeSymbol(stream: Stream; sym: string) =
|
||||||
writeEscaped(stream, sym, '|')
|
writeEscaped(stream, sym, '|')
|
||||||
write(stream, '|')
|
write(stream, '|')
|
||||||
|
|
||||||
proc writeText*[E](stream: Stream; pr: Preserve[E]; mode = textPreserves) =
|
proc writeText*(stream: Stream; pr: Value; mode = textPreserves) =
|
||||||
## Encode Preserves to a `Stream` as text.
|
## Encode Preserves to a `Stream` as text.
|
||||||
if pr.embedded: write(stream, "#!")
|
if pr.embedded: write(stream, "#!")
|
||||||
case pr.kind:
|
case pr.kind:
|
||||||
|
@ -153,7 +153,10 @@ proc writeText*[E](stream: Stream; pr: Preserve[E]; mode = textPreserves) =
|
||||||
write(stream, '}')
|
write(stream, '}')
|
||||||
of pkEmbedded:
|
of pkEmbedded:
|
||||||
write(stream, "#!")
|
write(stream, "#!")
|
||||||
when compiles($pr.embed) and not E is void:
|
if pr.embeddedRef.isNil:
|
||||||
|
write(stream, "<null>")
|
||||||
|
else:
|
||||||
|
when compiles($pr.embed):
|
||||||
write(stream, $pr.embed)
|
write(stream, $pr.embed)
|
||||||
else:
|
else:
|
||||||
write(stream, "…")
|
write(stream, "…")
|
||||||
|
@ -163,7 +166,7 @@ proc `$`*(sym: Symbol): string =
|
||||||
writeSymbol(stream, sym.string)
|
writeSymbol(stream, sym.string)
|
||||||
result = move stream.data
|
result = move stream.data
|
||||||
|
|
||||||
proc `$`*[E](pr: Preserve[E]): string =
|
proc `$`*(pr: Value): string =
|
||||||
## Generate the textual representation of ``pr``.
|
## Generate the textual representation of ``pr``.
|
||||||
var stream = newStringStream()
|
var stream = newStringStream()
|
||||||
writeText(stream, pr, textPreserves)
|
writeText(stream, pr, textPreserves)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[hashes, math, options, sets, sequtils, tables]
|
import std/[algorithm, hashes, math, options, sets, sequtils, tables]
|
||||||
|
|
||||||
import bigints
|
import bigints
|
||||||
|
|
||||||
|
@ -44,9 +44,7 @@ type
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
Preserve*[E] = object
|
Value* = object
|
||||||
embedded*: bool
|
|
||||||
## Flag to mark embedded Preserves
|
|
||||||
case kind*: PreserveKind
|
case kind*: PreserveKind
|
||||||
of pkBoolean:
|
of pkBoolean:
|
||||||
bool*: bool
|
bool*: bool
|
||||||
|
@ -65,26 +63,33 @@ type
|
||||||
of pkSymbol:
|
of pkSymbol:
|
||||||
symbol*: Symbol
|
symbol*: Symbol
|
||||||
of pkRecord:
|
of pkRecord:
|
||||||
record*: seq[Preserve[E]] # label is last
|
record*: seq[Value] # label is last
|
||||||
of pkSequence:
|
of pkSequence:
|
||||||
sequence*: seq[Preserve[E]]
|
sequence*: seq[Value]
|
||||||
of pkSet:
|
of pkSet:
|
||||||
set*: seq[Preserve[E]]
|
set*: seq[Value]
|
||||||
# TODO: HashSet
|
# TODO: HashSet
|
||||||
of pkDictionary:
|
of pkDictionary:
|
||||||
dict*: seq[DictEntry[E]]
|
dict*: seq[DictEntry]
|
||||||
# TODO: Tables
|
# TODO: Tables
|
||||||
of pkEmbedded:
|
of pkEmbedded:
|
||||||
embed*: E
|
embeddedRef*: EmbeddedRef
|
||||||
|
embedded*: bool
|
||||||
|
## Flag to mark embedded Preserves value
|
||||||
|
|
||||||
DictEntry*[E] = tuple[key: Preserve[E], val: Preserve[E]]
|
DictEntry* = tuple[key: Value, val: Value]
|
||||||
|
|
||||||
|
EmbeddedRef* = ref RootObj
|
||||||
|
EmbeddedObj* = RootObj
|
||||||
|
## Object refs embedded in Preserves `Value`s must inherit from `EmbeddedObj`.
|
||||||
|
## At the moment this is just an alias to `RootObj` but this may change in the future.
|
||||||
|
|
||||||
func `===`[T: SomeFloat](a, b: T): bool =
|
func `===`[T: SomeFloat](a, b: T): bool =
|
||||||
## Compare where Nan == NaN.
|
## Compare where Nan == NaN.
|
||||||
let class = a.classify
|
let class = a.classify
|
||||||
(class == b.classify) and ((class notin {fcNormal,fcSubnormal}) or (a == b))
|
(class == b.classify) and ((class notin {fcNormal,fcSubnormal}) or (a == b))
|
||||||
|
|
||||||
func `==`*[A, B](x: Preserve[A]; y: Preserve[B]): bool =
|
func `==`*(x, y: Value): bool =
|
||||||
## Check `x` and `y` for equivalence.
|
## Check `x` and `y` for equivalence.
|
||||||
if x.kind == y.kind and x.embedded == y.embedded:
|
if x.kind == y.kind and x.embedded == y.embedded:
|
||||||
case x.kind
|
case x.kind
|
||||||
|
@ -126,11 +131,7 @@ func `==`*[A, B](x: Preserve[A]; y: Preserve[B]): bool =
|
||||||
(x.dict[i].key == y.dict[i].key) and
|
(x.dict[i].key == y.dict[i].key) and
|
||||||
(x.dict[i].val == y.dict[i].val)
|
(x.dict[i].val == y.dict[i].val)
|
||||||
of pkEmbedded:
|
of pkEmbedded:
|
||||||
when A is B:
|
result = x.embeddedRef == y.embeddedRef
|
||||||
when A is void:
|
|
||||||
result = true
|
|
||||||
else:
|
|
||||||
result = x.embed == y.embed
|
|
||||||
|
|
||||||
proc `<`(x, y: string | seq[byte]): bool =
|
proc `<`(x, y: string | seq[byte]): bool =
|
||||||
for i in 0 .. min(x.high, y.high):
|
for i in 0 .. min(x.high, y.high):
|
||||||
|
@ -138,7 +139,7 @@ proc `<`(x, y: string | seq[byte]): bool =
|
||||||
if x[i] != y[i]: return false
|
if x[i] != y[i]: return false
|
||||||
x.len < y.len
|
x.len < y.len
|
||||||
|
|
||||||
proc `<`*[A, B](x: Preserve[A]; y: Preserve[B]): bool =
|
proc `<`*(x, y: Value): bool =
|
||||||
## Preserves have a total order over values. Check if `x` is ordered before `y`.
|
## Preserves have a total order over values. Check if `x` is ordered before `y`.
|
||||||
if x.embedded != y.embedded:
|
if x.embedded != y.embedded:
|
||||||
result = y.embedded
|
result = y.embedded
|
||||||
|
@ -186,19 +187,18 @@ proc `<`*[A, B](x: Preserve[A]; y: Preserve[B]): bool =
|
||||||
if x.dict[i].val != y.dict[i].val: return false
|
if x.dict[i].val != y.dict[i].val: return false
|
||||||
result = x.dict.len < y.dict.len
|
result = x.dict.len < y.dict.len
|
||||||
of pkEmbedded:
|
of pkEmbedded:
|
||||||
when (not A is void) and (A is B):
|
result = x.embeddedRef < y.embeddedRef
|
||||||
result = x.embed < y.embed
|
|
||||||
|
|
||||||
func cmp*[E](x, y: Preserve[E]): int =
|
func cmp*(x, y: Value): int =
|
||||||
## Compare by Preserves total ordering.
|
## Compare by Preserves total ordering.
|
||||||
if x == y: 0
|
if x == y: 0
|
||||||
elif x < y: -1
|
elif x < y: -1
|
||||||
else: 1
|
else: 1
|
||||||
|
|
||||||
proc sort*[E](pr: var Preserve[E]) = sort(pr.sequence, cmp)
|
proc sort*(pr: var Value) = sort(pr.sequence, cmp)
|
||||||
## Sort a Preserves array by total ordering.
|
## Sort a Preserves array by total ordering.
|
||||||
|
|
||||||
proc hash*[E](pr: Preserve[E]): Hash =
|
proc hash*(pr: Value): Hash =
|
||||||
## Produce a `Hash` of `pr` for use with a `HashSet` or `Table`.
|
## Produce a `Hash` of `pr` for use with a `HashSet` or `Table`.
|
||||||
var h = hash(pr.kind.int) !& hash(pr.embedded)
|
var h = hash(pr.kind.int) !& hash(pr.embedded)
|
||||||
case pr.kind
|
case pr.kind
|
||||||
|
@ -231,16 +231,10 @@ proc hash*[E](pr: Preserve[E]): Hash =
|
||||||
for (key, val) in pr.dict.items:
|
for (key, val) in pr.dict.items:
|
||||||
h = h !& hash(key) !& hash(val)
|
h = h !& hash(key) !& hash(val)
|
||||||
of pkEmbedded:
|
of pkEmbedded:
|
||||||
when E is void:
|
h = h !& hash(cast[uint](addr pr.embeddedRef[]))
|
||||||
h = h !& hash(pr.embed)
|
|
||||||
else:
|
|
||||||
if pr.embed.isNil:
|
|
||||||
h = h !& hash(false)
|
|
||||||
else:
|
|
||||||
h = h !& hash(pr.embed)
|
|
||||||
!$h
|
!$h
|
||||||
|
|
||||||
proc `[]`*(pr: Preserve; i: int): Preserve =
|
proc `[]`*(pr: Value; i: int): Value =
|
||||||
## Select an indexed value from ``pr``.
|
## Select an indexed value from ``pr``.
|
||||||
## Only valid for records and sequences.
|
## Only valid for records and sequences.
|
||||||
case pr.kind
|
case pr.kind
|
||||||
|
@ -249,16 +243,16 @@ proc `[]`*(pr: Preserve; i: int): Preserve =
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "Preserves value is not indexable")
|
raise newException(ValueError, "Preserves value is not indexable")
|
||||||
|
|
||||||
proc `[]=`*(pr: var Preserve; i: Natural; val: Preserve) =
|
proc `[]=`*(pr: var Value; i: Natural; val: Value) =
|
||||||
## Assign an indexed value into ``pr``.
|
## Assign an indexed value into ``pr``.
|
||||||
## Only valid for records and sequences.
|
## Only valid for records and sequences.
|
||||||
case pr.kind
|
case pr.kind
|
||||||
of pkRecord: pr.record[i] = val
|
of pkRecord: pr.record[i] = val
|
||||||
of pkSequence: pr.sequence[i] = val
|
of pkSequence: pr.sequence[i] = val
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "`Preserves value is not indexable")
|
raise newException(ValueError, "Preserves value is not indexable")
|
||||||
|
|
||||||
proc `[]=`*(pr: var Preserve; key, val: Preserve) =
|
proc `[]=`*(pr: var Value; key, val: Value) =
|
||||||
## Insert `val` by `key` in the Preserves dictionary `pr`.
|
## Insert `val` by `key` in the Preserves dictionary `pr`.
|
||||||
for i in 0..pr.dict.high:
|
for i in 0..pr.dict.high:
|
||||||
if key < pr.dict[i].key:
|
if key < pr.dict[i].key:
|
||||||
|
@ -269,7 +263,7 @@ proc `[]=`*(pr: var Preserve; key, val: Preserve) =
|
||||||
return
|
return
|
||||||
pr.dict.add((key, val, ))
|
pr.dict.add((key, val, ))
|
||||||
|
|
||||||
proc incl*(pr: var Preserve; key: Preserve) =
|
proc incl*(pr: var Value; key: Value) =
|
||||||
## Include `key` in the Preserves set `pr`.
|
## Include `key` in the Preserves set `pr`.
|
||||||
for i in 0..pr.set.high:
|
for i in 0..pr.set.high:
|
||||||
if key < pr.set[i]:
|
if key < pr.set[i]:
|
||||||
|
@ -277,7 +271,7 @@ proc incl*(pr: var Preserve; key: Preserve) =
|
||||||
return
|
return
|
||||||
pr.set.add(key)
|
pr.set.add(key)
|
||||||
|
|
||||||
proc excl*(pr: var Preserve; key: Preserve) =
|
proc excl*(pr: var Value; key: Value) =
|
||||||
## Exclude `key` from the Preserves set `pr`.
|
## Exclude `key` from the Preserves set `pr`.
|
||||||
for i in 0..pr.set.high:
|
for i in 0..pr.set.high:
|
||||||
if pr.set[i] == key:
|
if pr.set[i] == key:
|
||||||
|
|
|
@ -54,11 +54,11 @@ type
|
||||||
`ref`*: Ref
|
`ref`*: Ref
|
||||||
|
|
||||||
|
|
||||||
|
Definitions* = Table[Symbol, Definition]
|
||||||
`AtomKind`* {.preservesOr, pure.} = enum
|
`AtomKind`* {.preservesOr, pure.} = enum
|
||||||
`Boolean`, `Float`, `Double`, `SignedInteger`, `String`, `ByteString`,
|
`Boolean`, `Float`, `Double`, `SignedInteger`, `String`, `ByteString`,
|
||||||
`Symbol`
|
`Symbol`
|
||||||
Definitions* = Table[Symbol, Definition]
|
DictionaryEntries* = Table[Value, NamedSimplePattern]
|
||||||
DictionaryEntries* = Table[Preserve[void], NamedSimplePattern]
|
|
||||||
NamedPatternKind* {.pure.} = enum
|
NamedPatternKind* {.pure.} = enum
|
||||||
`named`, `anonymous`
|
`named`, `anonymous`
|
||||||
`NamedPattern`* {.acyclic, preservesOr.} = ref object
|
`NamedPattern`* {.acyclic, preservesOr.} = ref object
|
||||||
|
@ -79,7 +79,7 @@ type
|
||||||
`interface`*: SimplePattern
|
`interface`*: SimplePattern
|
||||||
|
|
||||||
SimplePatternLit* {.preservesRecord: "lit".} = object
|
SimplePatternLit* {.preservesRecord: "lit".} = object
|
||||||
`value`*: Preserve[void]
|
`value`*: Value
|
||||||
|
|
||||||
SimplePatternSeqof* {.acyclic, preservesRecord: "seqof".} = ref object
|
SimplePatternSeqof* {.acyclic, preservesRecord: "seqof".} = ref object
|
||||||
`pattern`*: SimplePattern
|
`pattern`*: SimplePattern
|
||||||
|
@ -198,7 +198,7 @@ proc `$`*(x: Ref | ModulePath | Bundle | CompoundPattern | Modules |
|
||||||
Schema |
|
Schema |
|
||||||
Pattern |
|
Pattern |
|
||||||
Binding): string =
|
Binding): string =
|
||||||
`$`(toPreserve(x))
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
proc encode*(x: Ref | ModulePath | Bundle | CompoundPattern | Modules |
|
proc encode*(x: Ref | ModulePath | Bundle | CompoundPattern | Modules |
|
||||||
EmbeddedTypeName |
|
EmbeddedTypeName |
|
||||||
|
@ -212,4 +212,4 @@ proc encode*(x: Ref | ModulePath | Bundle | CompoundPattern | Modules |
|
||||||
Schema |
|
Schema |
|
||||||
Pattern |
|
Pattern |
|
||||||
Binding): seq[byte] =
|
Binding): seq[byte] =
|
||||||
encode(toPreserve(x))
|
encode(toPreserves(x))
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# SPDX-FileCopyrightText: 2021 ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[strutils, tables]
|
import std/[strutils, tables]
|
||||||
|
@ -9,7 +9,6 @@ import npeg
|
||||||
import ../preserves, ./schema, ./pegs
|
import ../preserves, ./schema, ./pegs
|
||||||
|
|
||||||
type
|
type
|
||||||
Value = Preserve[void]
|
|
||||||
Stack = seq[tuple[node: Value, pos: int]]
|
Stack = seq[tuple[node: Value, pos: int]]
|
||||||
ParseState = object
|
ParseState = object
|
||||||
schema: SchemaField0
|
schema: SchemaField0
|
||||||
|
@ -57,7 +56,7 @@ template pushStack(n: Value) =
|
||||||
assert(p.stack.len > 0, capture[0].s)
|
assert(p.stack.len > 0, capture[0].s)
|
||||||
|
|
||||||
proc toSymbolLit(s: string): Value =
|
proc toSymbolLit(s: string): Value =
|
||||||
initRecord[void](toSymbol"lit", toSymbol s)
|
initRecord(toSymbol"lit", toSymbol s)
|
||||||
|
|
||||||
proc match(text: string; p: var ParseState)
|
proc match(text: string; p: var ParseState)
|
||||||
|
|
||||||
|
@ -75,7 +74,7 @@ const parser = peg("Schema", p: ParseState):
|
||||||
var r = popStack()
|
var r = popStack()
|
||||||
p.schema.embeddedType =
|
p.schema.embeddedType =
|
||||||
EmbeddedTypeName(orKind: EmbeddedTypeNameKind.Ref)
|
EmbeddedTypeName(orKind: EmbeddedTypeNameKind.Ref)
|
||||||
validate p.schema.embeddedType.`ref`.fromPreserve(r)
|
validate p.schema.embeddedType.`ref`.fromPreserves(r)
|
||||||
|
|
||||||
Include <- "include" * S * '"' * >(+Preserves.char) * '"':
|
Include <- "include" * S * '"' * >(+Preserves.char) * '"':
|
||||||
var path: string
|
var path: string
|
||||||
|
@ -93,14 +92,14 @@ const parser = peg("Schema", p: ParseState):
|
||||||
var
|
var
|
||||||
node = popStack()
|
node = popStack()
|
||||||
def: Definition
|
def: Definition
|
||||||
if not fromPreserve(def, node):
|
if not def.fromPreserves(node):
|
||||||
raise newException(ValueError, "failed to convert " & $1 & " to a Definition: " & $node)
|
raise newException(ValueError, "failed to convert " & $1 & " to a Definition: " & $node)
|
||||||
p.schema.definitions[Symbol $1] = def
|
p.schema.definitions[Symbol $1] = def
|
||||||
p.stack.setLen(0)
|
p.stack.setLen(0)
|
||||||
|
|
||||||
OrDelim <- *LineComment * '/' * S * *LineComment
|
OrDelim <- *LineComment * '/' * S * *LineComment
|
||||||
OrPattern <- ?OrDelim * AltPattern * +(S * OrDelim * AltPattern):
|
OrPattern <- ?OrDelim * AltPattern * +(S * OrDelim * AltPattern):
|
||||||
var node = initRecord(toSymbol("or"), toPreserve takeStackAt())
|
var node = initRecord(toSymbol("or"), takeStackAt().toPreserves)
|
||||||
pushStack node
|
pushStack node
|
||||||
|
|
||||||
AltPattern <-
|
AltPattern <-
|
||||||
|
@ -110,21 +109,21 @@ const parser = peg("Schema", p: ParseState):
|
||||||
AltLiteralPattern
|
AltLiteralPattern
|
||||||
|
|
||||||
AltNamed <- atId * ?Annotation * Pattern:
|
AltNamed <- atId * ?Annotation * Pattern:
|
||||||
var n = toPreserve @[toPreserve $1] & takeStackAt()
|
var n = toPreserves(@[toPreserves $1] & takeStackAt())
|
||||||
pushStack n
|
pushStack n
|
||||||
|
|
||||||
AltRecord <- '<' * id * *NamedPattern * '>':
|
AltRecord <- '<' * id * *NamedPattern * '>':
|
||||||
var n = toPreserve @[
|
var n = toPreserves @[
|
||||||
toPreserve $1,
|
toPreserves $1,
|
||||||
initRecord(
|
initRecord(
|
||||||
toSymbol"rec",
|
toSymbol"rec",
|
||||||
toSymbolLit $1,
|
toSymbolLit $1,
|
||||||
initRecord(toSymbol"tuple", toPreserve takeStackAt()))]
|
initRecord(toSymbol"tuple", toPreserves takeStackAt()))]
|
||||||
pushStack n
|
pushStack n
|
||||||
|
|
||||||
AltRef <- Ref:
|
AltRef <- Ref:
|
||||||
var r = popStack()
|
var r = popStack()
|
||||||
var n = toPreserve @[r[1].symbol.string.toPreserve, r]
|
var n = toPreserves @[r[1].symbol.string.toPreserves, r]
|
||||||
pushStack n
|
pushStack n
|
||||||
|
|
||||||
AltLiteralPattern <-
|
AltLiteralPattern <-
|
||||||
|
@ -138,13 +137,13 @@ const parser = peg("Schema", p: ParseState):
|
||||||
of "#f": "false"
|
of "#f": "false"
|
||||||
of "#t": "true"
|
of "#t": "true"
|
||||||
else: $1
|
else: $1
|
||||||
var n = toPreserve @[
|
var n = toPreserves @[
|
||||||
toPreserve id,
|
toPreserves id,
|
||||||
initRecord(toSymbol"lit", parsePreserves $1)]
|
initRecord(toSymbol"lit", parsePreserves $1)]
|
||||||
pushStack n
|
pushStack n
|
||||||
|
|
||||||
AndPattern <- ?'&' * S * NamedPattern * +('&' * S * NamedPattern):
|
AndPattern <- ?'&' * S * NamedPattern * +('&' * S * NamedPattern):
|
||||||
var node = initRecord(toSymbol("and"), toPreserve takeStackAt())
|
var node = initRecord(toSymbol("and"), toPreserves takeStackAt())
|
||||||
pushStack node
|
pushStack node
|
||||||
|
|
||||||
Pattern <- SimplePattern | CompoundPattern
|
Pattern <- SimplePattern | CompoundPattern
|
||||||
|
@ -230,7 +229,7 @@ const parser = peg("Schema", p: ParseState):
|
||||||
if capture.len == 2:
|
if capture.len == 2:
|
||||||
var n = initRecord(toSymbol"rec",
|
var n = initRecord(toSymbol"rec",
|
||||||
toSymbolLit $1,
|
toSymbolLit $1,
|
||||||
initRecord(toSymbol"tuple", toPreserve takeStackAfter()))
|
initRecord(toSymbol"tuple", toPreserves takeStackAfter()))
|
||||||
pushStack n
|
pushStack n
|
||||||
else:
|
else:
|
||||||
var n = initRecord(toSymbol"rec", takeStackAfter())
|
var n = initRecord(toSymbol"rec", takeStackAfter())
|
||||||
|
@ -243,12 +242,12 @@ const parser = peg("Schema", p: ParseState):
|
||||||
var n = initRecord(
|
var n = initRecord(
|
||||||
toSymbol"rec",
|
toSymbol"rec",
|
||||||
toSymbolLit $1,
|
toSymbolLit $1,
|
||||||
initRecord(toSymbol"tuplePrefix", toPreserve fields, tail))
|
initRecord(toSymbol"tuplePrefix", toPreserves fields, tail))
|
||||||
pushStack n
|
pushStack n
|
||||||
|
|
||||||
TuplePattern <-
|
TuplePattern <-
|
||||||
'[' * S * *NamedPattern * ']':
|
'[' * S * *NamedPattern * ']':
|
||||||
var n = initRecord(toSymbol"tuple", toPreserve takeStackAfter())
|
var n = initRecord(toSymbol"tuple", toPreserves takeStackAfter())
|
||||||
pushStack n
|
pushStack n
|
||||||
|
|
||||||
VariableTuplePattern <-
|
VariableTuplePattern <-
|
||||||
|
@ -256,11 +255,11 @@ const parser = peg("Schema", p: ParseState):
|
||||||
var fields = takeStackAfter()
|
var fields = takeStackAfter()
|
||||||
var tail = fields.pop
|
var tail = fields.pop
|
||||||
tail[1] = initRecord(toSymbol"seqof", tail[1])
|
tail[1] = initRecord(toSymbol"seqof", tail[1])
|
||||||
var node = initRecord(toSymbol"tuplePrefix", toPreserve fields, tail)
|
var node = initRecord(toSymbol"tuplePrefix", toPreserves fields, tail)
|
||||||
pushStack node
|
pushStack node
|
||||||
|
|
||||||
DictionaryPattern <- '{' * *(S * >Value * S * ':' * S * NamedSimplePattern * ?',') * S * '}':
|
DictionaryPattern <- '{' * *(S * >Value * S * ':' * S * NamedSimplePattern * ?',') * S * '}':
|
||||||
var dict = initDictionary(void)
|
var dict = initDictionary()
|
||||||
for i in countDown(pred capture.len, 1):
|
for i in countDown(pred capture.len, 1):
|
||||||
let key = toSymbol capture[i].s
|
let key = toSymbol capture[i].s
|
||||||
dict[key] = initRecord("named", key, popStack())
|
dict[key] = initRecord("named", key, popStack())
|
||||||
|
@ -320,5 +319,5 @@ when isMainModule:
|
||||||
if txt != "":
|
if txt != "":
|
||||||
let
|
let
|
||||||
scm = parsePreservesSchema(txt)
|
scm = parsePreservesSchema(txt)
|
||||||
pr = toPreserve scm
|
pr = toPreserves scm
|
||||||
stdout.newFileStream.writeText(pr, textPreserves)
|
stdout.newFileStream.writeText(pr, textPreserves)
|
||||||
|
|
|
@ -1,156 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
|
|
||||||
# SPDX-License-Identifier: Unlicense
|
|
||||||
|
|
||||||
import std/[sequtils, tables]
|
|
||||||
|
|
||||||
import spryvm/spryvm
|
|
||||||
|
|
||||||
import ../preserves
|
|
||||||
|
|
||||||
type
|
|
||||||
PreservesNode* = ref object of Value
|
|
||||||
preserve: Preserve[void]
|
|
||||||
EmbeddedNode* = ref object of PreservesNode
|
|
||||||
ByteStringNode* = ref object of StringVal
|
|
||||||
RecordNode* = ref object of Blok
|
|
||||||
SetNode = ref object of PreservesNode
|
|
||||||
|
|
||||||
method eval*(self: PreservesNode; spry: Interpreter): Node =
|
|
||||||
self
|
|
||||||
|
|
||||||
method `$`*(self: PreservesNode): string =
|
|
||||||
$self.preserve
|
|
||||||
|
|
||||||
method typeName*(self: PreservesNode): string =
|
|
||||||
"preserves-value"
|
|
||||||
|
|
||||||
method typeName*(self: EmbeddedNode): string =
|
|
||||||
"preserves-embedded-value"
|
|
||||||
|
|
||||||
method typeName*(self: ByteStringNode): string =
|
|
||||||
"preserves-bytestring"
|
|
||||||
|
|
||||||
method typeName*(self: RecordNode): string =
|
|
||||||
"preserves-record"
|
|
||||||
|
|
||||||
method typeName*(self: SetNode): string =
|
|
||||||
"preserves-set"
|
|
||||||
|
|
||||||
proc toSpry(pr: Preserve[void], spry: Interpreter): Node =
|
|
||||||
if pr.embedded:
|
|
||||||
result = EmbeddedNode(preserve: pr)
|
|
||||||
# TODO: need to be able to manipulate these
|
|
||||||
else:
|
|
||||||
case pr.kind
|
|
||||||
of pkBoolean:
|
|
||||||
result = boolVal(pr.bool, spry)
|
|
||||||
of pkFloat:
|
|
||||||
result = newValue(pr.float)
|
|
||||||
of pkDouble:
|
|
||||||
result = newValue(pr.double)
|
|
||||||
of pkRegister:
|
|
||||||
result = newValue(pr.register)
|
|
||||||
of pkBigInt:
|
|
||||||
raiseAssert "Arbitrary sized integers not supported by Spry implementation"
|
|
||||||
of pkString:
|
|
||||||
result = newValue(pr.string)
|
|
||||||
of pkByteString:
|
|
||||||
result = ByteStringNode(value: cast[string](pr.bytes))
|
|
||||||
of pkSymbol:
|
|
||||||
result =
|
|
||||||
if pr.symbol == Symbol"null": newNilVal()
|
|
||||||
else: newLitWord(spry, string pr.symbol)
|
|
||||||
of pkRecord:
|
|
||||||
var comp = RecordNode()
|
|
||||||
proc f(pr: Preserve[void]): Node = toSpry(pr, spry)
|
|
||||||
comp.nodes = map(pr.record, f)
|
|
||||||
result = comp
|
|
||||||
of pkSequence:
|
|
||||||
var blk = newBlok()
|
|
||||||
for e in pr.sequence: blk.add toSpry(e, spry)
|
|
||||||
result = blk
|
|
||||||
of pkSet:
|
|
||||||
result = SetNode(preserve: pr)
|
|
||||||
of pkDictionary:
|
|
||||||
var map = newMap()
|
|
||||||
for (key, val) in pr.dict.items:
|
|
||||||
map[toSpry(key, spry)] = toSpry(val, spry)
|
|
||||||
result = map
|
|
||||||
of pkEmbedded:
|
|
||||||
result = EmbeddedNode(preserve: pr)
|
|
||||||
|
|
||||||
proc toPreserveHook*(node: Node; E: typedesc): Preserve[E] =
|
|
||||||
if node of PreservesNode:
|
|
||||||
result = PreservesNode(node).preserve
|
|
||||||
elif node of RecordNode:
|
|
||||||
result = Preserve[E](kind: pkRecord)
|
|
||||||
var comp = RecordNode(node)
|
|
||||||
proc f(child: Node): Preserve[void] = toPreserve(child, void)
|
|
||||||
result.record = map(comp.nodes, f)
|
|
||||||
elif node of ByteStringNode:
|
|
||||||
result = toPreserve(cast[seq[byte]](ByteStringNode(node).value), E)
|
|
||||||
elif node of Blok:
|
|
||||||
var blk = Blok(node)
|
|
||||||
result = initSequence(blk.nodes.len, E)
|
|
||||||
for i, child in blk.nodes: result.sequence[i] = toPreserve(child, E)
|
|
||||||
elif node of Map:
|
|
||||||
result = initDictionary(E)
|
|
||||||
for key, val in Map(node).bindings:
|
|
||||||
result[toPreserve(key, E)] = toPreserve(val, E)
|
|
||||||
elif node of StringVal:
|
|
||||||
result = toPreserve(StringVal(node).value, E)
|
|
||||||
elif node of LitWord:
|
|
||||||
result = toSymbol(LitWord(node).word, E)
|
|
||||||
elif node of IntVal:
|
|
||||||
result = toPreserve(IntVal(node).value, E)
|
|
||||||
elif node of FloatVal:
|
|
||||||
result = toPreserve(FloatVal(node).value, E)
|
|
||||||
elif node of BoolVal:
|
|
||||||
result = toPreserve(BoolVal(node).value, E)
|
|
||||||
else: # node of NilVal:
|
|
||||||
result = toSymbol("null", E)
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
var
|
|
||||||
node: Node
|
|
||||||
pr = toPreserveHook(node, void)
|
|
||||||
|
|
||||||
proc addPreserves*(spry: Interpreter) =
|
|
||||||
nimFunc("parsePreserves"):
|
|
||||||
let node = evalArg(spry)
|
|
||||||
if node of StringVal:
|
|
||||||
let str = StringVal(node).value
|
|
||||||
result = PreservesNode(preserve: parsePreserves(str))
|
|
||||||
|
|
||||||
nimFunc("decodePreserves"):
|
|
||||||
let node = evalArg(spry)
|
|
||||||
if node of StringVal:
|
|
||||||
let str = StringVal(node).value
|
|
||||||
result = PreservesNode(preserve: decodePreserves(cast[seq[byte]](str)))
|
|
||||||
|
|
||||||
nimMeth("encodePreserves"):
|
|
||||||
let node = evalArgInfix(spry)
|
|
||||||
if node of PreservesNode:
|
|
||||||
var bin = encode PreservesNode(node).preserve
|
|
||||||
result = newValue(cast[string](bin))
|
|
||||||
|
|
||||||
nimFunc("fromPreserves"):
|
|
||||||
let node = evalArg(spry)
|
|
||||||
if node of PreservesNode:
|
|
||||||
let pr = PreservesNode(node).preserve
|
|
||||||
return toSpry(pr, spry)
|
|
||||||
|
|
||||||
nimMeth("toPreserves"):
|
|
||||||
let node = evalArgInfix(spry)
|
|
||||||
PreservesNode(preserve: node.toPreserve)
|
|
||||||
|
|
||||||
nimMeth("arity"):
|
|
||||||
let node = evalArgInfix(spry)
|
|
||||||
if node of RecordNode:
|
|
||||||
return newValue(pred SeqComposite(node).nodes.len)
|
|
||||||
|
|
||||||
nimMeth("label"):
|
|
||||||
let node = evalArgInfix(spry)
|
|
||||||
if node of RecordNode:
|
|
||||||
let rec = RecordNode(node)
|
|
||||||
return rec.nodes[rec.nodes.high]
|
|
|
@ -1,2 +0,0 @@
|
||||||
multimethods:on
|
|
||||||
nilseqs:on
|
|
|
@ -1,36 +1,36 @@
|
||||||
# SPDX-FileCopyrightText: 2021 ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[parseutils, strtabs, tables, xmltree]
|
import std/[parseutils, strtabs, xmltree]
|
||||||
import ../preserves
|
import ../preserves
|
||||||
|
|
||||||
proc toPreserveFromString*(s: string; E: typedesc): Preserve[E] =
|
proc toPreservesFromString*(s: string): Value =
|
||||||
# This is a bad and slow thing to do, but that is XML.
|
# This is a bad and slow thing to do, but that is XML.
|
||||||
case s
|
case s
|
||||||
of "false", "no", "off":
|
of "false", "no", "off":
|
||||||
result = toPreserve(false, E)
|
result = toPreserves(false)
|
||||||
of "true", "yes", "on":
|
of "true", "yes", "on":
|
||||||
result = toPreserve(true, E)
|
result = toPreserves(true)
|
||||||
else:
|
else:
|
||||||
var
|
var
|
||||||
n: BiggestInt
|
n: BiggestInt
|
||||||
f: BiggestFloat
|
f: BiggestFloat
|
||||||
if parseBiggestInt(s, n) == s.len:
|
if parseBiggestInt(s, n) == s.len:
|
||||||
result = toPreserve(n, E)
|
result = toPreserves(n)
|
||||||
elif parseHex(s, n) == s.len:
|
elif parseHex(s, n) == s.len:
|
||||||
result = toPreserve(n, E)
|
result = toPreserves(n)
|
||||||
elif parseFloat(s, f) == s.len:
|
elif parseFloat(s, f) == s.len:
|
||||||
result = toPreserve(f, E)
|
result = toPreserves(f)
|
||||||
else:
|
else:
|
||||||
result = toPreserve(s, E)
|
result = toPreserves(s)
|
||||||
|
|
||||||
proc toPreserveHook*(xn: XmlNode; E: typedesc): Preserve[E] =
|
proc toPreservesHook*(xn: XmlNode): Value =
|
||||||
if xn.kind == xnElement:
|
if xn.kind == xnElement:
|
||||||
result = Preserve[E](kind: pkRecord)
|
result = Value(kind: pkRecord)
|
||||||
if not xn.attrs.isNil:
|
if not xn.attrs.isNil:
|
||||||
var attrs = initDictionary(E)
|
var attrs = initDictionary()
|
||||||
for xk, xv in xn.attrs.pairs:
|
for xk, xv in xn.attrs.pairs:
|
||||||
attrs[toSymbol(xk, E)] = toPreserveFromString(xv, E)
|
attrs[toSymbol(xk)] = toPreservesFromString(xv)
|
||||||
result.record.add(attrs)
|
result.record.add(attrs)
|
||||||
var isText = xn.len > 0
|
var isText = xn.len > 0
|
||||||
# escaped text is broken up and must be concatenated
|
# escaped text is broken up and must be concatenated
|
||||||
|
@ -39,20 +39,20 @@ proc toPreserveHook*(xn: XmlNode; E: typedesc): Preserve[E] =
|
||||||
isText = false
|
isText = false
|
||||||
break
|
break
|
||||||
if isText:
|
if isText:
|
||||||
result.record.add(toPreserve(xn.innerText, E))
|
result.record.add(toPreserves(xn.innerText))
|
||||||
else:
|
else:
|
||||||
for child in xn.items:
|
for child in xn.items:
|
||||||
case child.kind
|
case child.kind
|
||||||
of xnElement:
|
of xnElement:
|
||||||
result.record.add(toPreserveHook(child, E))
|
result.record.add(toPreservesHook(child))
|
||||||
of xnText, xnVerbatimText, xnCData, xnEntity:
|
of xnText, xnVerbatimText, xnCData, xnEntity:
|
||||||
result.record.add(toPreserve(text(child), E))
|
result.record.add(toPreserves(text(child)))
|
||||||
of xnComment:
|
of xnComment:
|
||||||
discard
|
discard
|
||||||
result.record.add(toSymbol(xn.tag, E))
|
result.record.add(toSymbol(xn.tag))
|
||||||
# record labels are stored after the fields
|
# record labels are stored after the fields
|
||||||
|
|
||||||
proc toUnquotedString[E](pr: Preserve[E]): string {.inline.} =
|
proc toUnquotedString(pr: Value): string {.inline.} =
|
||||||
case pr.kind
|
case pr.kind
|
||||||
of pkString:
|
of pkString:
|
||||||
pr.string
|
pr.string
|
||||||
|
@ -60,7 +60,7 @@ proc toUnquotedString[E](pr: Preserve[E]): string {.inline.} =
|
||||||
if pr.bool: "true" else: "false"
|
if pr.bool: "true" else: "false"
|
||||||
else: $pr
|
else: $pr
|
||||||
|
|
||||||
proc fromPreserveHook*[E](xn: var XmlNode; pr: Preserve[E]): bool =
|
proc fromPreservesHook*(xn: var XmlNode; pr: Value): bool =
|
||||||
if pr.kind == pkRecord and pr.label.kind == pkSymbol:
|
if pr.kind == pkRecord and pr.label.kind == pkSymbol:
|
||||||
xn = newElement($pr.label)
|
xn = newElement($pr.label)
|
||||||
var i: int
|
var i: int
|
||||||
|
@ -74,7 +74,7 @@ proc fromPreserveHook*[E](xn: var XmlNode; pr: Preserve[E]): bool =
|
||||||
xn.add newText(e.string)
|
xn.add newText(e.string)
|
||||||
else:
|
else:
|
||||||
var child: XmlNode
|
var child: XmlNode
|
||||||
result = fromPreserveHook(child, e)
|
result = fromPreservesHook(child, e)
|
||||||
if not result: return
|
if not result: return
|
||||||
xn.add child
|
xn.add child
|
||||||
inc i
|
inc i
|
||||||
|
@ -82,5 +82,5 @@ proc fromPreserveHook*[E](xn: var XmlNode; pr: Preserve[E]): bool =
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
var xn = newElement("foobar")
|
var xn = newElement("foobar")
|
||||||
var pr = xn.toPreserveHook(void)
|
var pr = xn.toPreservesHook()
|
||||||
assert fromPreserveHook(xn, pr)
|
assert fromPreservesHook(xn, pr)
|
||||||
|
|
|
@ -9,7 +9,7 @@ suite "BufferedDecoder":
|
||||||
test "half-string":
|
test "half-string":
|
||||||
var
|
var
|
||||||
buf = newBufferedDecoder()
|
buf = newBufferedDecoder()
|
||||||
pr = Preserve[void](kind: pkByteString, bytes: newSeq[byte](23))
|
pr = Value(kind: pkByteString, bytes: newSeq[byte](23))
|
||||||
ok: bool
|
ok: bool
|
||||||
for i, _ in pr.bytes:
|
for i, _ in pr.bytes:
|
||||||
pr.bytes[i] = byte(i)
|
pr.bytes[i] = byte(i)
|
||||||
|
|
|
@ -6,44 +6,47 @@ import preserves, preserves/xmlhooks
|
||||||
|
|
||||||
type
|
type
|
||||||
Route {.preservesRecord: "route".} = object
|
Route {.preservesRecord: "route".} = object
|
||||||
`transports`*: seq[Preserve[void]]
|
`transports`*: seq[Value]
|
||||||
`pathSteps`* {.preservesTupleTail.}: seq[Preserve[void]]
|
`pathSteps`* {.preservesTupleTail.}: seq[Value]
|
||||||
|
|
||||||
suite "conversions":
|
suite "conversions":
|
||||||
test "dictionary":
|
test "dictionary":
|
||||||
type Bar = tuple
|
type Bar = tuple
|
||||||
s: string
|
s: string
|
||||||
type Foobar {.preservesDictionary.} = object
|
type Foobar {.preservesDictionary.} = object
|
||||||
a, b: int
|
a: int
|
||||||
|
b: seq[int]
|
||||||
c {.preservesEmbedded.}: Bar
|
c {.preservesEmbedded.}: Bar
|
||||||
let
|
let
|
||||||
c = Foobar(a: 1, b: 2, c: ("ku", ))
|
c = Foobar(a: 1, b: @[2], c: ("ku", ))
|
||||||
b = toPreserve(c)
|
b = toPreserves(c)
|
||||||
a = preserveTo(b, Foobar)
|
a = preservesTo(b, Foobar)
|
||||||
check($b == """{a: 1 b: 2 c: #!["ku"]}""")
|
check($b == """{a: 1 b: [2] c: #!["ku"]}""")
|
||||||
check(a.isSome and (get(a) == c))
|
check(a.isSome)
|
||||||
|
if a.isSome: check(get(a) == c)
|
||||||
check(b.kind == pkDictionary)
|
check(b.kind == pkDictionary)
|
||||||
|
|
||||||
test "records":
|
test "records":
|
||||||
type Bar {.preservesRecord: "bar".} = object
|
type Bar {.preservesRecord: "bar".} = object
|
||||||
s: string
|
s: string
|
||||||
type Foobar {.preservesRecord: "foo".} = object
|
type Foobar {.preservesRecord: "foo".} = object
|
||||||
a, b: int
|
a: int
|
||||||
|
b: seq[int]
|
||||||
c: Bar
|
c: Bar
|
||||||
let
|
let
|
||||||
tup = Foobar(a: 1, b: 2, c: Bar(s: "ku", ))
|
tup = Foobar(a: 1, b: @[2], c: Bar(s: "ku", ))
|
||||||
prs = toPreserve(tup)
|
prs = toPreserves(tup)
|
||||||
check(prs.kind == pkRecord)
|
check(prs.kind == pkRecord)
|
||||||
check($prs == """<foo 1 2 <bar "ku">>""")
|
check($prs == """<foo 1 [2] <bar "ku">>""")
|
||||||
check(preserveTo(prs, Foobar) == some(tup))
|
check(preservesTo(prs, Foobar) == some(tup))
|
||||||
|
|
||||||
test "tables":
|
test "tables":
|
||||||
var a: Table[int, string]
|
var a: Table[int, string]
|
||||||
for i, s in ["a", "b", "c"]: a[i] = s
|
for i, s in ["a", "b", "c"]: a[i] = s
|
||||||
let b = toPreserve(a)
|
let b = toPreserves(a)
|
||||||
check($b == """{0: "a" 1: "b" 2: "c"}""")
|
check($b == """{0: "a" 1: "b" 2: "c"}""")
|
||||||
var c: Table[int, string]
|
var c: Table[int, string]
|
||||||
check(fromPreserve(c, b))
|
check(fromPreserves(c, b))
|
||||||
check(a == c)
|
check(a == c)
|
||||||
|
|
||||||
test "XML":
|
test "XML":
|
||||||
|
@ -61,17 +64,35 @@ suite "conversions":
|
||||||
<rect x="1" y="1" width="998" height="298" fill="none" stroke="blue" stroke-width="2"/>
|
<rect x="1" y="1" width="998" height="298" fill="none" stroke="blue" stroke-width="2"/>
|
||||||
</svg>
|
</svg>
|
||||||
"""
|
"""
|
||||||
var pr = toPreserve(b, void)
|
var pr = toPreserves(b)
|
||||||
checkpoint $pr
|
checkpoint $pr
|
||||||
check fromPreserve(a, pr)
|
check fromPreserves(a, pr)
|
||||||
|
|
||||||
test "preservesTupleTail":
|
test "preservesTupleTail":
|
||||||
let pr = parsePreserves """<route [<tcp "localhost" 1024>] <ref {oid: "syndicate" sig: #x"69ca300c1dbfa08fba692102dd82311a"}>>"""
|
let pr = parsePreserves """<route [<tcp "localhost" 1024>] <ref {oid: "syndicate" sig: #x"69ca300c1dbfa08fba692102dd82311a"}>>"""
|
||||||
var route: Route
|
var route: Route
|
||||||
check route.fromPreserve(pr)
|
check route.fromPreserves(pr)
|
||||||
|
|
||||||
|
test "ebedded":
|
||||||
|
type
|
||||||
|
Foo {.preservesRecord: "foo".} = object
|
||||||
|
n: int
|
||||||
|
bar: Bar
|
||||||
|
Bar = ref object of RootObj
|
||||||
|
x: int
|
||||||
|
Baz = ref object of RootObj
|
||||||
|
x: int
|
||||||
|
|
||||||
|
let a = initRecord("foo", 9.toPreserves, embed Bar(x: 768))
|
||||||
|
checkpoint $a
|
||||||
|
check a.preservesTo(Foo).isSome
|
||||||
|
|
||||||
|
let b = initRecord("foo", 2.toPreserves, embed Baz(x: 999))
|
||||||
|
checkpoint $b
|
||||||
|
check not b.preservesTo(Foo).isSome
|
||||||
|
|
||||||
suite "toPreserve":
|
suite "toPreserve":
|
||||||
template check(p: Preserve; s: string) =
|
template check(p: Value; s: string) =
|
||||||
test s: check($p == s)
|
test s: check($p == s)
|
||||||
check false.toPreserve, "#f"
|
check false.toPreserves, "#f"
|
||||||
check [0, 1, 2, 3].toPreserve, "[0 1 2 3]"
|
check [0, 1, 2, 3].toPreserves, "[0 1 2 3]"
|
||||||
|
|
|
@ -27,12 +27,12 @@ suite "parse":
|
||||||
for (txt, bin) in examples:
|
for (txt, bin) in examples:
|
||||||
test txt:
|
test txt:
|
||||||
checkpoint(txt)
|
checkpoint(txt)
|
||||||
let test = parsePreserves(txt, int)
|
let test = parsePreserves(txt)
|
||||||
checkpoint($test)
|
checkpoint($test)
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
a = test
|
a = test
|
||||||
b = decodePreserves(bin, int)
|
b = decodePreserves(bin)
|
||||||
check(a == b)
|
check(a == b)
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
|
|
|
@ -57,7 +57,7 @@ for i, jsText in testVectors:
|
||||||
checkpoint(jsText)
|
checkpoint(jsText)
|
||||||
let
|
let
|
||||||
control = parseJson jsText
|
control = parseJson jsText
|
||||||
x = control.toPreserve
|
x = control.toPreserves
|
||||||
checkpoint($x)
|
checkpoint($x)
|
||||||
var stream = newStringStream()
|
var stream = newStringStream()
|
||||||
stream.write(x)
|
stream.write(x)
|
||||||
|
|
|
@ -4,29 +4,27 @@
|
||||||
import std/unittest
|
import std/unittest
|
||||||
import preserves
|
import preserves
|
||||||
|
|
||||||
type Value = Preserve[void]
|
|
||||||
|
|
||||||
const upstreamTestfile {.strdefine.} = ""
|
const upstreamTestfile {.strdefine.} = ""
|
||||||
|
|
||||||
proc strip(pr: Preserve): Preserve = pr
|
proc strip(pr: Value): Value = pr
|
||||||
|
|
||||||
proc encodeBinary(pr: Value): Value =
|
proc encodeBinary(pr: Value): Value =
|
||||||
result = encode(pr).toPreserve
|
result = encode(pr).toPreserves
|
||||||
checkpoint("encoded binary: " & $result)
|
checkpoint("encoded binary: " & $result)
|
||||||
|
|
||||||
proc looseEncodeBinary(pr: Value): Value =
|
proc looseEncodeBinary(pr: Value): Value =
|
||||||
result = encode(pr).toPreserve
|
result = encode(pr).toPreserves
|
||||||
checkpoint("loose encoded binary: " & $result)
|
checkpoint("loose encoded binary: " & $result)
|
||||||
|
|
||||||
proc annotatedBinary(pr: Value): Value =
|
proc annotatedBinary(pr: Value): Value =
|
||||||
result = encode(pr).toPreserve
|
result = encode(pr).toPreserves
|
||||||
checkpoint("annotated binary: " & $result)
|
checkpoint("annotated binary: " & $result)
|
||||||
|
|
||||||
proc decodeBinary(pr: Value): Value =
|
proc decodeBinary(pr: Value): Value =
|
||||||
result = decodePreserves(pr.bytes)
|
result = decodePreserves(pr.bytes)
|
||||||
|
|
||||||
proc encodeText(pr: Value): Value =
|
proc encodeText(pr: Value): Value =
|
||||||
result = ($pr).toPreserve
|
result = ($pr).toPreserves
|
||||||
checkpoint("encoded text: " & result.string)
|
checkpoint("encoded text: " & result.string)
|
||||||
|
|
||||||
proc decodeText(pr: Value): Value =
|
proc decodeText(pr: Value): Value =
|
||||||
|
@ -34,7 +32,7 @@ proc decodeText(pr: Value): Value =
|
||||||
checkpoint("decoded text " & $pr)
|
checkpoint("decoded text " & $pr)
|
||||||
|
|
||||||
if upstreamTestfile != "":
|
if upstreamTestfile != "":
|
||||||
let samples = readFile(upstreamTestfile).parsePreserves(void)
|
let samples = readFile(upstreamTestfile).parsePreserves()
|
||||||
assert samples.isRecord("TestCases")
|
assert samples.isRecord("TestCases")
|
||||||
|
|
||||||
var binary, annotatedValue, stripped, text, bytes: Value
|
var binary, annotatedValue, stripped, text, bytes: Value
|
||||||
|
|
|
@ -10,7 +10,7 @@ suite "step":
|
||||||
"""
|
"""
|
||||||
|
|
||||||
var o = some data
|
var o = some data
|
||||||
for i in [1.toPreserve, 1.toPreserve, "b".toPreserve]:
|
for i in [1.toPreserves, 1.toPreserves, "b".toPreserves]:
|
||||||
test $i:
|
test $i:
|
||||||
o = step(get o, i)
|
o = step(get o, i)
|
||||||
check o.isSome
|
check o.isSome
|
||||||
|
|
Loading…
Reference in New Issue