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)/../npeg/src
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Package
|
||||
|
||||
version = "20231227"
|
||||
version = "20231230"
|
||||
author = "Emery Hemingway"
|
||||
description = "data model and serialization format"
|
||||
license = "Unlicense"
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
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
|
||||
: 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 *hooks.nim |> !nim_run |>
|
||||
|
|
|
@ -11,10 +11,10 @@ const
|
|||
fullTimeFormat = "HH:mm:sszzz"
|
||||
dateTimeFormat = "yyyy-MM-dd'T'HH:mm:sszzz"
|
||||
|
||||
proc toPreserveHook*(dt: DateTime; E: typedesc): Preserve[E] =
|
||||
initRecord[E](toSymbol("rfc3339", E), toPreserve($dt, E))
|
||||
proc toPreservesHook*(dt: DateTime): Value =
|
||||
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
|
||||
if result:
|
||||
try:
|
||||
|
@ -39,6 +39,6 @@ runnableExamples:
|
|||
import preserves
|
||||
var a, b: DateTime
|
||||
a = now()
|
||||
var pr = a.toPreserveHook(void)
|
||||
check fromPreserveHook(b, pr)
|
||||
var pr = a.toPreservesHook()
|
||||
check b.fromPreservesHook(pr)
|
||||
check $a == $b
|
||||
|
|
|
@ -4,51 +4,51 @@
|
|||
import std/[json, tables]
|
||||
import ../preserves
|
||||
|
||||
proc toPreserveHook*(js: JsonNode; E: typedesc): Preserve[E] =
|
||||
proc toPreservesHook*(js: JsonNode): Value =
|
||||
case js.kind
|
||||
of JString:
|
||||
result = Preserve[E](kind: pkString, string: js.str)
|
||||
result = js.str.toPreserves()
|
||||
of JInt:
|
||||
result = Preserve[E](kind: pkRegister, register: js.num)
|
||||
result = js.num.toPreserves()
|
||||
of JFloat:
|
||||
result = Preserve[E](kind: pkDouble, double: js.fnum)
|
||||
result = js.fnum.toPreserves()
|
||||
of JBool:
|
||||
result = case js.bval
|
||||
of false: toSymbol("false", E)
|
||||
of true: toSymbol("true", E)
|
||||
of false: toSymbol("false")
|
||||
of true: toSymbol("true")
|
||||
of JNull:
|
||||
result = toSymbol("null", E)
|
||||
result = toSymbol("null")
|
||||
of JObject:
|
||||
result = Preserve[E](kind: pkDictionary)
|
||||
result = Value(kind: pkDictionary)
|
||||
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:
|
||||
result = Preserve[E](kind: pkSequence,
|
||||
sequence: newSeq[Preserve[E]](js.elems.len))
|
||||
result = Value(kind: pkSequence,
|
||||
sequence: newSeq[Value](js.elems.len))
|
||||
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:
|
||||
import std/json
|
||||
var js = JsonNode()
|
||||
var pr = js.toPreserveHook(void)
|
||||
assert fromPreserveHook(js, pr)
|
||||
var pr = js.toPreservesHook()
|
||||
assert js.fromPreservesHook(pr)
|
||||
fromJsonHook(pr, js)
|
||||
js = toJsonHook(pr)
|
||||
case prs.kind:
|
||||
case pr.kind:
|
||||
of pkBoolean:
|
||||
js = newJBool(prs.bool)
|
||||
js = newJBool(pr.bool)
|
||||
of pkFloat:
|
||||
js = newJFloat(prs.float)
|
||||
js = newJFloat(pr.float)
|
||||
of pkDouble:
|
||||
js = newJFloat(prs.double)
|
||||
js = newJFloat(pr.double)
|
||||
of pkRegister:
|
||||
js = newJInt(prs.register)
|
||||
js = newJInt(pr.register)
|
||||
of pkString:
|
||||
js = newJString(prs.string)
|
||||
js = newJString(pr.string)
|
||||
of pkSymbol:
|
||||
case prs.symbol.string
|
||||
case pr.symbol.string
|
||||
of "false":
|
||||
js = newJBool(false)
|
||||
of "true":
|
||||
|
@ -59,38 +59,38 @@ proc fromPreserveHook*[E](js: var JsonNode; prs: Preserve[E]): bool =
|
|||
return false
|
||||
of pkSequence:
|
||||
js = newJArray()
|
||||
js.elems.setLen(prs.sequence.len)
|
||||
for i, val in prs.sequence:
|
||||
if not fromPreserveHook(js.elems[i], val):
|
||||
js.elems.setLen(pr.sequence.len)
|
||||
for i, val in pr.sequence:
|
||||
if not js.elems[i].fromPreservesHook(val):
|
||||
return false
|
||||
of pkSet:
|
||||
js = newJArray()
|
||||
js.elems.setLen(prs.set.len)
|
||||
js.elems.setLen(pr.set.len)
|
||||
var i: int
|
||||
for val in prs.set:
|
||||
if not fromPreserveHook(js.elems[i], val):
|
||||
for val in pr.set:
|
||||
if not js.elems[i].fromPreservesHook(val):
|
||||
return false
|
||||
inc i
|
||||
of pkDictionary:
|
||||
js = newJObject()
|
||||
for (key, val) in prs.dict.items:
|
||||
for (key, val) in pr.dict.items:
|
||||
case key.kind
|
||||
of pkSymbol:
|
||||
var jsVal: JsonNode
|
||||
if not fromPreserveHook(jsVal, val): return false
|
||||
if not jsVal.fromPreservesHook(val): return false
|
||||
js[string key.symbol] = jsVal
|
||||
of pkString:
|
||||
var jsVal: JsonNode
|
||||
if not fromPreserveHook(jsVal, val): return false
|
||||
if not jsVal.fromPreservesHook(val): return false
|
||||
js[key.string] = jsVal
|
||||
else:
|
||||
return false
|
||||
else: return false
|
||||
true
|
||||
|
||||
proc toJsonHook*[E](pr: Preserve[E]): JsonNode =
|
||||
if not fromPreserveHook(result, pr):
|
||||
proc toJsonHook*(pr: Value): JsonNode =
|
||||
if not result.fromPreservesHook(pr):
|
||||
raise newException(ValueError, "cannot convert Preserves value to JSON")
|
||||
|
||||
proc fromJsonHook*[E](pr: var Preserve[E]; js: JsonNode) =
|
||||
pr = toPreserveHook(js, E)
|
||||
proc fromJsonHook*(pr: var Value; js: JsonNode) =
|
||||
pr = toPreservesHook(js)
|
||||
|
|
|
@ -138,11 +138,7 @@ proc embeddedIdentString(scm: Schema): string =
|
|||
proc embeddedIdent(scm: Schema): PNode =
|
||||
ident(embeddedIdentString(scm))
|
||||
|
||||
proc preserveIdent(scm: Schema): Pnode =
|
||||
if scm.hasEmbeddedType:
|
||||
nkBracketExpr.newTree(ident"Preserve", embeddedIdent(scm))
|
||||
else:
|
||||
nkBracketExpr.newTree(ident"Preserve", ident"void")
|
||||
proc preserveIdent(scm: Schema): Pnode = ident"Value"
|
||||
|
||||
proc parameterize(scm: Schema; node: PNode; embeddable: bool): PNode =
|
||||
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 =
|
||||
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]
|
||||
|
||||
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.} =
|
||||
var sortedEntries =
|
||||
initOrderedTable[Preserve[void], NamedSimplePattern](entries.len)
|
||||
initOrderedTable[Value, NamedSimplePattern](entries.len)
|
||||
for key, val in entries.pairs:
|
||||
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])
|
||||
for key, val in sortedEntries.pairs:
|
||||
doAssert(key.isSymbol)
|
||||
|
@ -834,7 +830,7 @@ proc renderNimBundle*(bundle: Bundle): Table[string, string] =
|
|||
newEmpty(),
|
||||
nkStmtList.newTree(
|
||||
nkCall.newTree(ident"$",
|
||||
nkCall.newTree(ident"toPreserve", ident"x", embeddedIdent(scm)))))
|
||||
nkCall.newTree(ident"toPreserves", ident"x", embeddedIdent(scm)))))
|
||||
procs.add nkProcDef.newTree(
|
||||
"encode".ident.toExport,
|
||||
newEmpty(),
|
||||
|
@ -849,7 +845,7 @@ proc renderNimBundle*(bundle: Bundle): Table[string, string] =
|
|||
newEmpty(),
|
||||
nkStmtList.newTree(
|
||||
nkCall.newTree(ident"encode",
|
||||
nkCall.newTree(ident"toPreserve", ident"x", embeddedIdent(scm)))))
|
||||
nkCall.newTree(ident"toPreserves", ident"x", embeddedIdent(scm)))))
|
||||
if not unembeddableType.isNil:
|
||||
procs.add nkProcDef.newTree(
|
||||
"$".toFieldIdent,
|
||||
|
@ -865,7 +861,7 @@ proc renderNimBundle*(bundle: Bundle): Table[string, string] =
|
|||
newEmpty(),
|
||||
nkStmtList.newTree(
|
||||
nkCall.newTree(ident"$",
|
||||
nkCall.newTree(ident"toPreserve", ident"x"))))
|
||||
nkCall.newTree(ident"toPreserves", ident"x"))))
|
||||
procs.add nkProcDef.newTree(
|
||||
"encode".ident.toExport,
|
||||
newEmpty(),
|
||||
|
@ -880,7 +876,7 @@ proc renderNimBundle*(bundle: Bundle): Table[string, string] =
|
|||
newEmpty(),
|
||||
nkStmtList.newTree(
|
||||
nkCall.newTree(ident"encode", nkCall.newTree(
|
||||
ident"toPreserve", ident"x"))))
|
||||
ident"toPreserves", ident"x"))))
|
||||
var module = newNode(nkStmtList).add(
|
||||
imports,
|
||||
typeSection
|
||||
|
@ -902,7 +898,7 @@ when isMainModule:
|
|||
writeFile(path, txt)
|
||||
stdout.writeLine(path)
|
||||
|
||||
import std/[options, os, parseopt]
|
||||
import std/[os, parseopt]
|
||||
var inputs: seq[string]
|
||||
for kind, key, val in getopt():
|
||||
case kind
|
||||
|
@ -934,9 +930,9 @@ when isMainModule:
|
|||
let raw = readFile inputPath
|
||||
if raw[0] == 0xb4.char:
|
||||
var pr = decodePreserves raw
|
||||
if not fromPreserve(bundle, pr):
|
||||
if not fromPreserves(bundle, pr):
|
||||
var schema: Schema
|
||||
if fromPreserve(schema, pr):
|
||||
if fromPreserves(schema, pr):
|
||||
bundle.modules[@[Symbol fileName]] = schema
|
||||
else:
|
||||
new bundle
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
threads:off
|
|
@ -33,7 +33,7 @@ when isMainModule:
|
|||
if not fileExists inputPath:
|
||||
quit(inputPath & " does not exist or is not a file")
|
||||
var schema = parsePreservesSchema(readFile(inputPath))
|
||||
write(outStream, schema.toPreserve)
|
||||
write(outStream, schema.toPreserves)
|
||||
|
||||
else:
|
||||
let bundle = Bundle()
|
||||
|
@ -53,6 +53,6 @@ when isMainModule:
|
|||
if bundle.modules.len == 0:
|
||||
quit "no schemas parsed"
|
||||
else:
|
||||
write(outStream, bundle.toPreserve)
|
||||
write(outStream, bundle.toPreserves)
|
||||
|
||||
close(outStream)
|
||||
|
|
|
@ -15,31 +15,31 @@ proc readVarint(s: Stream): uint =
|
|||
c = uint s.readUint8
|
||||
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.
|
||||
if s.atEnd: raise newException(IOError, "End of Preserves stream")
|
||||
const endMarker = 0x84
|
||||
let tag = s.readUint8()
|
||||
case tag
|
||||
of 0x80: result = Preserve[E](kind: pkBoolean, bool: false)
|
||||
of 0x81: result = Preserve[E](kind: pkBoolean, bool: true)
|
||||
of 0x80: result = Value(kind: pkBoolean, bool: false)
|
||||
of 0x81: result = Value(kind: pkBoolean, bool: true)
|
||||
of 0x85:
|
||||
discard decodePreserves(s, E)
|
||||
result = decodePreserves(s, E)
|
||||
discard decodePreserves(s)
|
||||
result = decodePreserves(s)
|
||||
of 0x86:
|
||||
result = decodePreserves(s, E)
|
||||
result = decodePreserves(s)
|
||||
result.embedded = true
|
||||
of 0x87:
|
||||
var N: int
|
||||
let n = int s.readUint8()
|
||||
case n
|
||||
of 4:
|
||||
result = Preserve[E](kind: pkFloat)
|
||||
result = Value(kind: pkFloat)
|
||||
var buf: uint32
|
||||
N = s.readData(addr buf, sizeof(buf))
|
||||
bigEndian32(addr result.float, addr buf)
|
||||
of 8:
|
||||
result = Preserve[E](kind: pkDouble)
|
||||
result = Value(kind: pkDouble)
|
||||
var buf: uint64
|
||||
N = s.readData(addr buf, sizeof(buf))
|
||||
bigEndian64(addr result.double, addr buf)
|
||||
|
@ -49,7 +49,7 @@ proc decodePreserves*(s: Stream; E = void): Preserve[E] =
|
|||
of 0xb0:
|
||||
var n = int s.readVarint()
|
||||
if n <= sizeof(int):
|
||||
result = Preserve[E](kind: pkRegister)
|
||||
result = Value(kind: pkRegister)
|
||||
if n > 0:
|
||||
var
|
||||
buf: array[sizeof(int), byte]
|
||||
|
@ -65,7 +65,7 @@ proc decodePreserves*(s: Stream; E = void): Preserve[E] =
|
|||
bigEndian64(addr result.register, addr buf[0])
|
||||
else: {.error: "int size " & $buf.len & " not supported here".}
|
||||
else:
|
||||
result = Preserve[E](kind: pkBigInt)
|
||||
result = Value(kind: pkBigInt)
|
||||
var buf = newSeq[byte](n)
|
||||
if s.readData(addr buf[0], buf.len) != n:
|
||||
raise newException(IOError, "short read")
|
||||
|
@ -76,7 +76,7 @@ proc decodePreserves*(s: Stream; E = void): Preserve[E] =
|
|||
else:
|
||||
result.bigint.fromBytes(buf, bigEndian)
|
||||
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 s.readData(addr result.string[0], result.string.len) != result.string.len:
|
||||
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)
|
||||
if n != data.len:
|
||||
raise newException(IOError, "short read")
|
||||
result = Preserve[E](kind: pkByteString, bytes: data)
|
||||
result = Value(kind: pkByteString, bytes: data)
|
||||
of 0xb3:
|
||||
var data = newString(s.readVarint())
|
||||
if data.len > 0:
|
||||
let n = s.readData(addr data[0], data.len)
|
||||
if n != data.len:
|
||||
raise newException(IOError, "short read")
|
||||
result = Preserve[E](kind: pkSymbol, symbol: Symbol data)
|
||||
result = Value(kind: pkSymbol, symbol: Symbol data)
|
||||
of 0xb4:
|
||||
result = Preserve[E](kind: pkRecord)
|
||||
var label = decodePreserves(s, E)
|
||||
result = Value(kind: pkRecord)
|
||||
var label = decodePreserves(s)
|
||||
while s.peekUint8() != endMarker:
|
||||
result.record.add decodePreserves(s, E)
|
||||
result.record.add decodePreserves(s)
|
||||
result.record.add(move label)
|
||||
discard s.readUint8()
|
||||
of 0xb5:
|
||||
result = Preserve[E](kind: pkSequence)
|
||||
result = Value(kind: pkSequence)
|
||||
while s.peekUint8() != endMarker:
|
||||
result.sequence.add decodePreserves(s, E)
|
||||
result.sequence.add decodePreserves(s)
|
||||
discard s.readUint8()
|
||||
of 0xb6:
|
||||
result = Preserve[E](kind: pkSet)
|
||||
result = Value(kind: pkSet)
|
||||
while s.peekUint8() != endMarker:
|
||||
incl(result, decodePreserves(s, E))
|
||||
incl(result, decodePreserves(s))
|
||||
discard s.readUint8()
|
||||
of 0xb7:
|
||||
result = Preserve[E](kind: pkDictionary)
|
||||
result = Value(kind: pkDictionary)
|
||||
while s.peekUint8() != endMarker:
|
||||
result[decodePreserves(s, E)] = decodePreserves(s, E)
|
||||
result[decodePreserves(s)] = decodePreserves(s)
|
||||
discard s.readUint8()
|
||||
of endMarker:
|
||||
raise newException(ValueError, "invalid Preserves stream")
|
||||
else:
|
||||
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.
|
||||
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.
|
||||
decodePreserves(cast[string](s), E)
|
||||
decodePreserves(cast[string](s))
|
||||
|
||||
type BufferedDecoder* = object
|
||||
## 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:
|
||||
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
|
||||
## decoder does not advance.
|
||||
if dec.appendPosition > 0:
|
||||
assert(dec.decodePosition < dec.appendPosition)
|
||||
dec.stream.setPosition(dec.decodePosition)
|
||||
try:
|
||||
result[1] = decodePreserves(dec.stream, E)
|
||||
result[1] = decodePreserves(dec.stream)
|
||||
result[0] = true
|
||||
dec.decodePosition = dec.stream.getPosition()
|
||||
if dec.decodePosition == dec.appendPosition:
|
||||
|
|
|
@ -12,7 +12,7 @@ proc writeVarint(s: Stream; n: Natural) =
|
|||
n = n shr 7
|
||||
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.
|
||||
if pr.embedded: str.write(0x86'u8)
|
||||
case pr.kind:
|
||||
|
@ -110,10 +110,10 @@ proc write*[E](str: Stream; pr: Preserve[E]) =
|
|||
str.write(value)
|
||||
str.write(0x84'u8)
|
||||
of pkEmbedded:
|
||||
str.write(0x86'u8)
|
||||
str.write(pr.embed.toPreserve)
|
||||
# str.write(0x86'u8)
|
||||
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.
|
||||
let s = newStringStream()
|
||||
s.write pr
|
||||
|
|
|
@ -10,7 +10,6 @@ import ../pegs
|
|||
import ./decoding, ./values
|
||||
|
||||
type
|
||||
Value = Preserve[void]
|
||||
Frame = tuple[value: Value, pos: int]
|
||||
Stack = seq[Frame]
|
||||
|
||||
|
@ -226,11 +225,6 @@ proc parsePreserves*(text: string): Value =
|
|||
assert(stack.len == 1)
|
||||
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 =
|
||||
## Parse a text-encoded Preserves `string` to a Preserves `Atom`.
|
||||
let pegParser = peg("Atom", a: Atom):
|
||||
|
|
|
@ -19,17 +19,17 @@ when isMainModule:
|
|||
of "preserves_from_json":
|
||||
let
|
||||
js = stdin.newFileStream.parseJson
|
||||
pr = js.toPreserve
|
||||
pr = js.toPreserves
|
||||
stdout.newFileStream.write(pr)
|
||||
of "preserves_from_xml":
|
||||
let
|
||||
xn = stdin.newFileStream.parseXml
|
||||
pr = xn.toPreserveHook(void)
|
||||
pr = xn.toPreservesHook()
|
||||
stdout.newFileStream.write(pr)
|
||||
of "preserves_to_json":
|
||||
let
|
||||
pr = stdin.readAll.decodePreserves
|
||||
js = preserveTo(pr, JsonNode)
|
||||
js = preservesTo(pr, JsonNode)
|
||||
if js.isSome:
|
||||
stdout.writeLine(get js)
|
||||
else:
|
||||
|
@ -37,7 +37,7 @@ when isMainModule:
|
|||
of "preserves_to_xml":
|
||||
let pr = stdin.readAll.decodePreserves
|
||||
var xn: XmlNode
|
||||
if fromPreserve(xn, pr):
|
||||
if fromPreserves(xn, pr):
|
||||
stdout.writeLine(xn)
|
||||
else:
|
||||
quit("Preserves not convertable to XML")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[base64, endians, math, sequtils, streams, strutils, unicode]
|
||||
import std/[base64, endians, math, sequtils, streams, strutils]
|
||||
import bigints
|
||||
import ./values
|
||||
|
||||
|
@ -42,7 +42,7 @@ proc writeSymbol(stream: Stream; sym: string) =
|
|||
writeEscaped(stream, sym, '|')
|
||||
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.
|
||||
if pr.embedded: write(stream, "#!")
|
||||
case pr.kind:
|
||||
|
@ -153,17 +153,20 @@ proc writeText*[E](stream: Stream; pr: Preserve[E]; mode = textPreserves) =
|
|||
write(stream, '}')
|
||||
of pkEmbedded:
|
||||
write(stream, "#!")
|
||||
when compiles($pr.embed) and not E is void:
|
||||
write(stream, $pr.embed)
|
||||
if pr.embeddedRef.isNil:
|
||||
write(stream, "<null>")
|
||||
else:
|
||||
write(stream, "…")
|
||||
when compiles($pr.embed):
|
||||
write(stream, $pr.embed)
|
||||
else:
|
||||
write(stream, "…")
|
||||
|
||||
proc `$`*(sym: Symbol): string =
|
||||
var stream = newStringStream()
|
||||
writeSymbol(stream, sym.string)
|
||||
result = move stream.data
|
||||
|
||||
proc `$`*[E](pr: Preserve[E]): string =
|
||||
proc `$`*(pr: Value): string =
|
||||
## Generate the textual representation of ``pr``.
|
||||
var stream = newStringStream()
|
||||
writeText(stream, pr, textPreserves)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[hashes, math, options, sets, sequtils, tables]
|
||||
import std/[algorithm, hashes, math, options, sets, sequtils, tables]
|
||||
|
||||
import bigints
|
||||
|
||||
|
@ -44,9 +44,7 @@ type
|
|||
else:
|
||||
discard
|
||||
|
||||
Preserve*[E] = object
|
||||
embedded*: bool
|
||||
## Flag to mark embedded Preserves
|
||||
Value* = object
|
||||
case kind*: PreserveKind
|
||||
of pkBoolean:
|
||||
bool*: bool
|
||||
|
@ -65,26 +63,33 @@ type
|
|||
of pkSymbol:
|
||||
symbol*: Symbol
|
||||
of pkRecord:
|
||||
record*: seq[Preserve[E]] # label is last
|
||||
record*: seq[Value] # label is last
|
||||
of pkSequence:
|
||||
sequence*: seq[Preserve[E]]
|
||||
sequence*: seq[Value]
|
||||
of pkSet:
|
||||
set*: seq[Preserve[E]]
|
||||
set*: seq[Value]
|
||||
# TODO: HashSet
|
||||
of pkDictionary:
|
||||
dict*: seq[DictEntry[E]]
|
||||
dict*: seq[DictEntry]
|
||||
# TODO: Tables
|
||||
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 =
|
||||
## Compare where Nan == NaN.
|
||||
let class = a.classify
|
||||
(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.
|
||||
if x.kind == y.kind and x.embedded == y.embedded:
|
||||
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].val == y.dict[i].val)
|
||||
of pkEmbedded:
|
||||
when A is B:
|
||||
when A is void:
|
||||
result = true
|
||||
else:
|
||||
result = x.embed == y.embed
|
||||
result = x.embeddedRef == y.embeddedRef
|
||||
|
||||
proc `<`(x, y: string | seq[byte]): bool =
|
||||
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
|
||||
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`.
|
||||
if x.embedded != 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
|
||||
result = x.dict.len < y.dict.len
|
||||
of pkEmbedded:
|
||||
when (not A is void) and (A is B):
|
||||
result = x.embed < y.embed
|
||||
result = x.embeddedRef < y.embeddedRef
|
||||
|
||||
func cmp*[E](x, y: Preserve[E]): int =
|
||||
func cmp*(x, y: Value): int =
|
||||
## Compare by Preserves total ordering.
|
||||
if x == y: 0
|
||||
elif x < y: -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.
|
||||
|
||||
proc hash*[E](pr: Preserve[E]): Hash =
|
||||
proc hash*(pr: Value): Hash =
|
||||
## Produce a `Hash` of `pr` for use with a `HashSet` or `Table`.
|
||||
var h = hash(pr.kind.int) !& hash(pr.embedded)
|
||||
case pr.kind
|
||||
|
@ -231,16 +231,10 @@ proc hash*[E](pr: Preserve[E]): Hash =
|
|||
for (key, val) in pr.dict.items:
|
||||
h = h !& hash(key) !& hash(val)
|
||||
of pkEmbedded:
|
||||
when E is void:
|
||||
h = h !& hash(pr.embed)
|
||||
else:
|
||||
if pr.embed.isNil:
|
||||
h = h !& hash(false)
|
||||
else:
|
||||
h = h !& hash(pr.embed)
|
||||
h = h !& hash(cast[uint](addr pr.embeddedRef[]))
|
||||
!$h
|
||||
|
||||
proc `[]`*(pr: Preserve; i: int): Preserve =
|
||||
proc `[]`*(pr: Value; i: int): Value =
|
||||
## Select an indexed value from ``pr``.
|
||||
## Only valid for records and sequences.
|
||||
case pr.kind
|
||||
|
@ -249,16 +243,16 @@ proc `[]`*(pr: Preserve; i: int): Preserve =
|
|||
else:
|
||||
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``.
|
||||
## Only valid for records and sequences.
|
||||
case pr.kind
|
||||
of pkRecord: pr.record[i] = val
|
||||
of pkSequence: pr.sequence[i] = val
|
||||
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`.
|
||||
for i in 0..pr.dict.high:
|
||||
if key < pr.dict[i].key:
|
||||
|
@ -269,7 +263,7 @@ proc `[]=`*(pr: var Preserve; key, val: Preserve) =
|
|||
return
|
||||
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`.
|
||||
for i in 0..pr.set.high:
|
||||
if key < pr.set[i]:
|
||||
|
@ -277,7 +271,7 @@ proc incl*(pr: var Preserve; key: Preserve) =
|
|||
return
|
||||
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`.
|
||||
for i in 0..pr.set.high:
|
||||
if pr.set[i] == key:
|
||||
|
|
|
@ -54,11 +54,11 @@ type
|
|||
`ref`*: Ref
|
||||
|
||||
|
||||
Definitions* = Table[Symbol, Definition]
|
||||
`AtomKind`* {.preservesOr, pure.} = enum
|
||||
`Boolean`, `Float`, `Double`, `SignedInteger`, `String`, `ByteString`,
|
||||
`Symbol`
|
||||
Definitions* = Table[Symbol, Definition]
|
||||
DictionaryEntries* = Table[Preserve[void], NamedSimplePattern]
|
||||
DictionaryEntries* = Table[Value, NamedSimplePattern]
|
||||
NamedPatternKind* {.pure.} = enum
|
||||
`named`, `anonymous`
|
||||
`NamedPattern`* {.acyclic, preservesOr.} = ref object
|
||||
|
@ -79,7 +79,7 @@ type
|
|||
`interface`*: SimplePattern
|
||||
|
||||
SimplePatternLit* {.preservesRecord: "lit".} = object
|
||||
`value`*: Preserve[void]
|
||||
`value`*: Value
|
||||
|
||||
SimplePatternSeqof* {.acyclic, preservesRecord: "seqof".} = ref object
|
||||
`pattern`*: SimplePattern
|
||||
|
@ -198,7 +198,7 @@ proc `$`*(x: Ref | ModulePath | Bundle | CompoundPattern | Modules |
|
|||
Schema |
|
||||
Pattern |
|
||||
Binding): string =
|
||||
`$`(toPreserve(x))
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*(x: Ref | ModulePath | Bundle | CompoundPattern | Modules |
|
||||
EmbeddedTypeName |
|
||||
|
@ -212,4 +212,4 @@ proc encode*(x: Ref | ModulePath | Bundle | CompoundPattern | Modules |
|
|||
Schema |
|
||||
Pattern |
|
||||
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
|
||||
|
||||
import std/[strutils, tables]
|
||||
|
@ -9,7 +9,6 @@ import npeg
|
|||
import ../preserves, ./schema, ./pegs
|
||||
|
||||
type
|
||||
Value = Preserve[void]
|
||||
Stack = seq[tuple[node: Value, pos: int]]
|
||||
ParseState = object
|
||||
schema: SchemaField0
|
||||
|
@ -57,7 +56,7 @@ template pushStack(n: Value) =
|
|||
assert(p.stack.len > 0, capture[0].s)
|
||||
|
||||
proc toSymbolLit(s: string): Value =
|
||||
initRecord[void](toSymbol"lit", toSymbol s)
|
||||
initRecord(toSymbol"lit", toSymbol s)
|
||||
|
||||
proc match(text: string; p: var ParseState)
|
||||
|
||||
|
@ -75,7 +74,7 @@ const parser = peg("Schema", p: ParseState):
|
|||
var r = popStack()
|
||||
p.schema.embeddedType =
|
||||
EmbeddedTypeName(orKind: EmbeddedTypeNameKind.Ref)
|
||||
validate p.schema.embeddedType.`ref`.fromPreserve(r)
|
||||
validate p.schema.embeddedType.`ref`.fromPreserves(r)
|
||||
|
||||
Include <- "include" * S * '"' * >(+Preserves.char) * '"':
|
||||
var path: string
|
||||
|
@ -93,14 +92,14 @@ const parser = peg("Schema", p: ParseState):
|
|||
var
|
||||
node = popStack()
|
||||
def: Definition
|
||||
if not fromPreserve(def, node):
|
||||
if not def.fromPreserves(node):
|
||||
raise newException(ValueError, "failed to convert " & $1 & " to a Definition: " & $node)
|
||||
p.schema.definitions[Symbol $1] = def
|
||||
p.stack.setLen(0)
|
||||
|
||||
OrDelim <- *LineComment * '/' * S * *LineComment
|
||||
OrPattern <- ?OrDelim * AltPattern * +(S * OrDelim * AltPattern):
|
||||
var node = initRecord(toSymbol("or"), toPreserve takeStackAt())
|
||||
var node = initRecord(toSymbol("or"), takeStackAt().toPreserves)
|
||||
pushStack node
|
||||
|
||||
AltPattern <-
|
||||
|
@ -110,21 +109,21 @@ const parser = peg("Schema", p: ParseState):
|
|||
AltLiteralPattern
|
||||
|
||||
AltNamed <- atId * ?Annotation * Pattern:
|
||||
var n = toPreserve @[toPreserve $1] & takeStackAt()
|
||||
var n = toPreserves(@[toPreserves $1] & takeStackAt())
|
||||
pushStack n
|
||||
|
||||
AltRecord <- '<' * id * *NamedPattern * '>':
|
||||
var n = toPreserve @[
|
||||
toPreserve $1,
|
||||
var n = toPreserves @[
|
||||
toPreserves $1,
|
||||
initRecord(
|
||||
toSymbol"rec",
|
||||
toSymbolLit $1,
|
||||
initRecord(toSymbol"tuple", toPreserve takeStackAt()))]
|
||||
initRecord(toSymbol"tuple", toPreserves takeStackAt()))]
|
||||
pushStack n
|
||||
|
||||
AltRef <- Ref:
|
||||
var r = popStack()
|
||||
var n = toPreserve @[r[1].symbol.string.toPreserve, r]
|
||||
var n = toPreserves @[r[1].symbol.string.toPreserves, r]
|
||||
pushStack n
|
||||
|
||||
AltLiteralPattern <-
|
||||
|
@ -138,13 +137,13 @@ const parser = peg("Schema", p: ParseState):
|
|||
of "#f": "false"
|
||||
of "#t": "true"
|
||||
else: $1
|
||||
var n = toPreserve @[
|
||||
toPreserve id,
|
||||
var n = toPreserves @[
|
||||
toPreserves id,
|
||||
initRecord(toSymbol"lit", parsePreserves $1)]
|
||||
pushStack n
|
||||
|
||||
AndPattern <- ?'&' * S * NamedPattern * +('&' * S * NamedPattern):
|
||||
var node = initRecord(toSymbol("and"), toPreserve takeStackAt())
|
||||
var node = initRecord(toSymbol("and"), toPreserves takeStackAt())
|
||||
pushStack node
|
||||
|
||||
Pattern <- SimplePattern | CompoundPattern
|
||||
|
@ -230,7 +229,7 @@ const parser = peg("Schema", p: ParseState):
|
|||
if capture.len == 2:
|
||||
var n = initRecord(toSymbol"rec",
|
||||
toSymbolLit $1,
|
||||
initRecord(toSymbol"tuple", toPreserve takeStackAfter()))
|
||||
initRecord(toSymbol"tuple", toPreserves takeStackAfter()))
|
||||
pushStack n
|
||||
else:
|
||||
var n = initRecord(toSymbol"rec", takeStackAfter())
|
||||
|
@ -243,12 +242,12 @@ const parser = peg("Schema", p: ParseState):
|
|||
var n = initRecord(
|
||||
toSymbol"rec",
|
||||
toSymbolLit $1,
|
||||
initRecord(toSymbol"tuplePrefix", toPreserve fields, tail))
|
||||
initRecord(toSymbol"tuplePrefix", toPreserves fields, tail))
|
||||
pushStack n
|
||||
|
||||
TuplePattern <-
|
||||
'[' * S * *NamedPattern * ']':
|
||||
var n = initRecord(toSymbol"tuple", toPreserve takeStackAfter())
|
||||
var n = initRecord(toSymbol"tuple", toPreserves takeStackAfter())
|
||||
pushStack n
|
||||
|
||||
VariableTuplePattern <-
|
||||
|
@ -256,11 +255,11 @@ const parser = peg("Schema", p: ParseState):
|
|||
var fields = takeStackAfter()
|
||||
var tail = fields.pop
|
||||
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
|
||||
|
||||
DictionaryPattern <- '{' * *(S * >Value * S * ':' * S * NamedSimplePattern * ?',') * S * '}':
|
||||
var dict = initDictionary(void)
|
||||
var dict = initDictionary()
|
||||
for i in countDown(pred capture.len, 1):
|
||||
let key = toSymbol capture[i].s
|
||||
dict[key] = initRecord("named", key, popStack())
|
||||
|
@ -320,5 +319,5 @@ when isMainModule:
|
|||
if txt != "":
|
||||
let
|
||||
scm = parsePreservesSchema(txt)
|
||||
pr = toPreserve scm
|
||||
pr = toPreserves scm
|
||||
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
|
||||
|
||||
import std/[parseutils, strtabs, tables, xmltree]
|
||||
import std/[parseutils, strtabs, xmltree]
|
||||
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.
|
||||
case s
|
||||
of "false", "no", "off":
|
||||
result = toPreserve(false, E)
|
||||
result = toPreserves(false)
|
||||
of "true", "yes", "on":
|
||||
result = toPreserve(true, E)
|
||||
result = toPreserves(true)
|
||||
else:
|
||||
var
|
||||
n: BiggestInt
|
||||
f: BiggestFloat
|
||||
if parseBiggestInt(s, n) == s.len:
|
||||
result = toPreserve(n, E)
|
||||
result = toPreserves(n)
|
||||
elif parseHex(s, n) == s.len:
|
||||
result = toPreserve(n, E)
|
||||
result = toPreserves(n)
|
||||
elif parseFloat(s, f) == s.len:
|
||||
result = toPreserve(f, E)
|
||||
result = toPreserves(f)
|
||||
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:
|
||||
result = Preserve[E](kind: pkRecord)
|
||||
result = Value(kind: pkRecord)
|
||||
if not xn.attrs.isNil:
|
||||
var attrs = initDictionary(E)
|
||||
var attrs = initDictionary()
|
||||
for xk, xv in xn.attrs.pairs:
|
||||
attrs[toSymbol(xk, E)] = toPreserveFromString(xv, E)
|
||||
attrs[toSymbol(xk)] = toPreservesFromString(xv)
|
||||
result.record.add(attrs)
|
||||
var isText = xn.len > 0
|
||||
# escaped text is broken up and must be concatenated
|
||||
|
@ -39,20 +39,20 @@ proc toPreserveHook*(xn: XmlNode; E: typedesc): Preserve[E] =
|
|||
isText = false
|
||||
break
|
||||
if isText:
|
||||
result.record.add(toPreserve(xn.innerText, E))
|
||||
result.record.add(toPreserves(xn.innerText))
|
||||
else:
|
||||
for child in xn.items:
|
||||
case child.kind
|
||||
of xnElement:
|
||||
result.record.add(toPreserveHook(child, E))
|
||||
result.record.add(toPreservesHook(child))
|
||||
of xnText, xnVerbatimText, xnCData, xnEntity:
|
||||
result.record.add(toPreserve(text(child), E))
|
||||
result.record.add(toPreserves(text(child)))
|
||||
of xnComment:
|
||||
discard
|
||||
result.record.add(toSymbol(xn.tag, E))
|
||||
result.record.add(toSymbol(xn.tag))
|
||||
# record labels are stored after the fields
|
||||
|
||||
proc toUnquotedString[E](pr: Preserve[E]): string {.inline.} =
|
||||
proc toUnquotedString(pr: Value): string {.inline.} =
|
||||
case pr.kind
|
||||
of pkString:
|
||||
pr.string
|
||||
|
@ -60,7 +60,7 @@ proc toUnquotedString[E](pr: Preserve[E]): string {.inline.} =
|
|||
if pr.bool: "true" else: "false"
|
||||
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:
|
||||
xn = newElement($pr.label)
|
||||
var i: int
|
||||
|
@ -74,7 +74,7 @@ proc fromPreserveHook*[E](xn: var XmlNode; pr: Preserve[E]): bool =
|
|||
xn.add newText(e.string)
|
||||
else:
|
||||
var child: XmlNode
|
||||
result = fromPreserveHook(child, e)
|
||||
result = fromPreservesHook(child, e)
|
||||
if not result: return
|
||||
xn.add child
|
||||
inc i
|
||||
|
@ -82,5 +82,5 @@ proc fromPreserveHook*[E](xn: var XmlNode; pr: Preserve[E]): bool =
|
|||
|
||||
when isMainModule:
|
||||
var xn = newElement("foobar")
|
||||
var pr = xn.toPreserveHook(void)
|
||||
assert fromPreserveHook(xn, pr)
|
||||
var pr = xn.toPreservesHook()
|
||||
assert fromPreservesHook(xn, pr)
|
||||
|
|
|
@ -1 +1 @@
|
|||
switch("path", "$projectDir/../src")
|
||||
switch("path", "$projectDir/../src")
|
||||
|
|
|
@ -9,7 +9,7 @@ suite "BufferedDecoder":
|
|||
test "half-string":
|
||||
var
|
||||
buf = newBufferedDecoder()
|
||||
pr = Preserve[void](kind: pkByteString, bytes: newSeq[byte](23))
|
||||
pr = Value(kind: pkByteString, bytes: newSeq[byte](23))
|
||||
ok: bool
|
||||
for i, _ in pr.bytes:
|
||||
pr.bytes[i] = byte(i)
|
||||
|
|
|
@ -6,44 +6,47 @@ import preserves, preserves/xmlhooks
|
|||
|
||||
type
|
||||
Route {.preservesRecord: "route".} = object
|
||||
`transports`*: seq[Preserve[void]]
|
||||
`pathSteps`* {.preservesTupleTail.}: seq[Preserve[void]]
|
||||
`transports`*: seq[Value]
|
||||
`pathSteps`* {.preservesTupleTail.}: seq[Value]
|
||||
|
||||
suite "conversions":
|
||||
test "dictionary":
|
||||
type Bar = tuple
|
||||
s: string
|
||||
type Foobar {.preservesDictionary.} = object
|
||||
a, b: int
|
||||
a: int
|
||||
b: seq[int]
|
||||
c {.preservesEmbedded.}: Bar
|
||||
let
|
||||
c = Foobar(a: 1, b: 2, c: ("ku", ))
|
||||
b = toPreserve(c)
|
||||
a = preserveTo(b, Foobar)
|
||||
check($b == """{a: 1 b: 2 c: #!["ku"]}""")
|
||||
check(a.isSome and (get(a) == c))
|
||||
c = Foobar(a: 1, b: @[2], c: ("ku", ))
|
||||
b = toPreserves(c)
|
||||
a = preservesTo(b, Foobar)
|
||||
check($b == """{a: 1 b: [2] c: #!["ku"]}""")
|
||||
check(a.isSome)
|
||||
if a.isSome: check(get(a) == c)
|
||||
check(b.kind == pkDictionary)
|
||||
|
||||
test "records":
|
||||
type Bar {.preservesRecord: "bar".} = object
|
||||
s: string
|
||||
type Foobar {.preservesRecord: "foo".} = object
|
||||
a, b: int
|
||||
a: int
|
||||
b: seq[int]
|
||||
c: Bar
|
||||
let
|
||||
tup = Foobar(a: 1, b: 2, c: Bar(s: "ku", ))
|
||||
prs = toPreserve(tup)
|
||||
tup = Foobar(a: 1, b: @[2], c: Bar(s: "ku", ))
|
||||
prs = toPreserves(tup)
|
||||
check(prs.kind == pkRecord)
|
||||
check($prs == """<foo 1 2 <bar "ku">>""")
|
||||
check(preserveTo(prs, Foobar) == some(tup))
|
||||
check($prs == """<foo 1 [2] <bar "ku">>""")
|
||||
check(preservesTo(prs, Foobar) == some(tup))
|
||||
|
||||
test "tables":
|
||||
var a: Table[int, string]
|
||||
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"}""")
|
||||
var c: Table[int, string]
|
||||
check(fromPreserve(c, b))
|
||||
check(fromPreserves(c, b))
|
||||
check(a == c)
|
||||
|
||||
test "XML":
|
||||
|
@ -61,17 +64,35 @@ suite "conversions":
|
|||
<rect x="1" y="1" width="998" height="298" fill="none" stroke="blue" stroke-width="2"/>
|
||||
</svg>
|
||||
"""
|
||||
var pr = toPreserve(b, void)
|
||||
var pr = toPreserves(b)
|
||||
checkpoint $pr
|
||||
check fromPreserve(a, pr)
|
||||
check fromPreserves(a, pr)
|
||||
|
||||
test "preservesTupleTail":
|
||||
let pr = parsePreserves """<route [<tcp "localhost" 1024>] <ref {oid: "syndicate" sig: #x"69ca300c1dbfa08fba692102dd82311a"}>>"""
|
||||
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":
|
||||
template check(p: Preserve; s: string) =
|
||||
template check(p: Value; s: string) =
|
||||
test s: check($p == s)
|
||||
check false.toPreserve, "#f"
|
||||
check [0, 1, 2, 3].toPreserve, "[0 1 2 3]"
|
||||
check false.toPreserves, "#f"
|
||||
check [0, 1, 2, 3].toPreserves, "[0 1 2 3]"
|
||||
|
|
|
@ -27,12 +27,12 @@ suite "parse":
|
|||
for (txt, bin) in examples:
|
||||
test txt:
|
||||
checkpoint(txt)
|
||||
let test = parsePreserves(txt, int)
|
||||
let test = parsePreserves(txt)
|
||||
checkpoint($test)
|
||||
block:
|
||||
let
|
||||
a = test
|
||||
b = decodePreserves(bin, int)
|
||||
b = decodePreserves(bin)
|
||||
check(a == b)
|
||||
block:
|
||||
let
|
||||
|
|
|
@ -57,7 +57,7 @@ for i, jsText in testVectors:
|
|||
checkpoint(jsText)
|
||||
let
|
||||
control = parseJson jsText
|
||||
x = control.toPreserve
|
||||
x = control.toPreserves
|
||||
checkpoint($x)
|
||||
var stream = newStringStream()
|
||||
stream.write(x)
|
||||
|
|
|
@ -4,29 +4,27 @@
|
|||
import std/unittest
|
||||
import preserves
|
||||
|
||||
type Value = Preserve[void]
|
||||
|
||||
const upstreamTestfile {.strdefine.} = ""
|
||||
|
||||
proc strip(pr: Preserve): Preserve = pr
|
||||
proc strip(pr: Value): Value = pr
|
||||
|
||||
proc encodeBinary(pr: Value): Value =
|
||||
result = encode(pr).toPreserve
|
||||
result = encode(pr).toPreserves
|
||||
checkpoint("encoded binary: " & $result)
|
||||
|
||||
proc looseEncodeBinary(pr: Value): Value =
|
||||
result = encode(pr).toPreserve
|
||||
result = encode(pr).toPreserves
|
||||
checkpoint("loose encoded binary: " & $result)
|
||||
|
||||
proc annotatedBinary(pr: Value): Value =
|
||||
result = encode(pr).toPreserve
|
||||
result = encode(pr).toPreserves
|
||||
checkpoint("annotated binary: " & $result)
|
||||
|
||||
proc decodeBinary(pr: Value): Value =
|
||||
result = decodePreserves(pr.bytes)
|
||||
|
||||
proc encodeText(pr: Value): Value =
|
||||
result = ($pr).toPreserve
|
||||
result = ($pr).toPreserves
|
||||
checkpoint("encoded text: " & result.string)
|
||||
|
||||
proc decodeText(pr: Value): Value =
|
||||
|
@ -34,7 +32,7 @@ proc decodeText(pr: Value): Value =
|
|||
checkpoint("decoded text " & $pr)
|
||||
|
||||
if upstreamTestfile != "":
|
||||
let samples = readFile(upstreamTestfile).parsePreserves(void)
|
||||
let samples = readFile(upstreamTestfile).parsePreserves()
|
||||
assert samples.isRecord("TestCases")
|
||||
|
||||
var binary, annotatedValue, stripped, text, bytes: Value
|
||||
|
|
|
@ -10,7 +10,7 @@ suite "step":
|
|||
"""
|
||||
|
||||
var o = some data
|
||||
for i in [1.toPreserve, 1.toPreserve, "b".toPreserve]:
|
||||
for i in [1.toPreserves, 1.toPreserves, "b".toPreserves]:
|
||||
test $i:
|
||||
o = step(get o, i)
|
||||
check o.isSome
|
||||
|
|
Loading…
Reference in New Issue