Add Atom type
Type for holding constant Preserves values create at compile-time. This is a prerequisite for making embedded values "ref RootObj". It is also requesite for making Value a ref object.
This commit is contained in:
parent
441bd253b0
commit
c2bce1404a
|
@ -1,6 +1,6 @@
|
|||
# Package
|
||||
|
||||
version = "20231225"
|
||||
version = "20231227"
|
||||
author = "Emery Hemingway"
|
||||
description = "data model and serialization format"
|
||||
license = "Unlicense"
|
||||
|
|
|
@ -209,6 +209,8 @@ iterator pairs*[E](pr: Preserve[E]): DictEntry[E] =
|
|||
assert(pr.kind == pkDictionary, "not a dictionary")
|
||||
for i in 0..pr.dict.high: yield pr.dict[i]
|
||||
|
||||
func isAtomic*(pr: Preserve): bool = pr.kind in atomKinds
|
||||
|
||||
func isBoolean*(pr: Preserve): bool {.inline.} = pr.kind == pkBoolean
|
||||
## Check if ``pr`` is a Preserve boolean.
|
||||
|
||||
|
@ -414,8 +416,8 @@ proc toPreserve*[T](x: T; E = void): Preserve[E] {.gcsafe.} =
|
|||
v.embedded = true
|
||||
template fieldToPreserve(key: string; val: typed): Preserve {.used.} =
|
||||
when x.dot(key).hasCustomPragma(preservesLiteral):
|
||||
const lit = parsePreserves(x.dot(key).getCustomPragmaVal(preservesLiteral))
|
||||
cast[Preserve[E]](lit)
|
||||
const atom = x.dot(key).getCustomPragmaVal(preservesLiteral).parsePreservesAtom
|
||||
atom.toPreserveHook(E)
|
||||
else:
|
||||
toPreserve(val, E)
|
||||
when T.hasCustomPragma(unpreservable):
|
||||
|
@ -465,6 +467,28 @@ proc toPreserve*[T](x: T; E = void): Preserve[E] {.gcsafe.} =
|
|||
# the hook doesn't compile but produces a useful error
|
||||
trace T, " -> ", result
|
||||
|
||||
proc toPreserveHook*(a: Atom; E: typedesc): Preserve[E] =
|
||||
result = Preserve[E](kind: a.kind)
|
||||
case a.kind
|
||||
of pkBoolean:
|
||||
result.bool = a.bool
|
||||
of pkFloat:
|
||||
result.float = a.float
|
||||
of pkDouble:
|
||||
result.double = a.double
|
||||
of pkRegister:
|
||||
result.register = a.register
|
||||
of pkBigInt:
|
||||
result.bigint = a.bigint
|
||||
of pkString:
|
||||
result.string = a.string
|
||||
of pkByteString:
|
||||
result.bytes = a.bytes
|
||||
of pkSymbol:
|
||||
result.symbol = a.symbol
|
||||
else:
|
||||
discard
|
||||
|
||||
proc toPreserveHook*[T](set: HashSet[T]; E: typedesc): Preserve[E] =
|
||||
## Hook for preserving ``HashSet``.
|
||||
result = Preserve[E](kind: pkSet, set: newSeqOfCap[Preserve[E]](set.len))
|
||||
|
@ -489,6 +513,69 @@ func containsNativeEmbeds[E](pr: Preserve[E]): bool =
|
|||
elif pr.kind == pkEmbedded:
|
||||
result = true
|
||||
|
||||
proc fromAtom*[T](v: var T; a: ATom): bool =
|
||||
if T is Atom:
|
||||
v = a
|
||||
result = true
|
||||
if T is Preserve:
|
||||
v = a.toPreservesHook
|
||||
result = true
|
||||
elif T is enum:
|
||||
if a.kind == pkSymbol:
|
||||
try:
|
||||
v = parseEnum[T](string a.symbol)
|
||||
result = true
|
||||
except ValueError: discard
|
||||
elif T is bool:
|
||||
if a.kind == pkBoolean:
|
||||
v = a.bool
|
||||
result = true
|
||||
elif T is SomeInteger:
|
||||
if a.kind == pkRegister:
|
||||
result = a.register.T < high(T)
|
||||
if result:
|
||||
v = T a.register
|
||||
elif a.kind == pkBigInt:
|
||||
var o = toInt[T](a.bigint)
|
||||
result = o.isSome
|
||||
if result: v = o.get
|
||||
elif T is seq[byte]:
|
||||
if a.kind == pkByteString:
|
||||
v = a.bytes
|
||||
result = true
|
||||
elif T is float32:
|
||||
if a.kind == pkFloat:
|
||||
v = a.float
|
||||
result = true
|
||||
elif T is float64:
|
||||
case a.kind
|
||||
of pkFloat:
|
||||
v = a.float
|
||||
result = true
|
||||
of pkDouble:
|
||||
v = a.double
|
||||
result = true
|
||||
else: discard
|
||||
elif T is Ordinal | SomeInteger:
|
||||
if a.kind == pkRegister:
|
||||
v = T(a.register)
|
||||
result = int(v) == a.register
|
||||
elif a.kind == pkBigInt:
|
||||
var o = toInt[T](a.bigint)
|
||||
if o.isSome:
|
||||
v = get o
|
||||
result = true
|
||||
elif T is string:
|
||||
if a.kind == pkString:
|
||||
v = a.string
|
||||
result = true
|
||||
elif T is Symbol:
|
||||
if a.kind == pkSymbol:
|
||||
v = a.symbol
|
||||
result = true
|
||||
elif T is distinct:
|
||||
result = fromAtom(v.distinctBase, a)
|
||||
|
||||
proc fromPreserve*[T, E](v: var T; pr: Preserve[E]): bool {.gcsafe.} =
|
||||
## Inplace version of `preserveTo`. Returns ``true`` on
|
||||
## a complete match, otherwise returns ``false``.
|
||||
|
@ -609,8 +696,8 @@ proc fromPreserve*[T, E](v: var T; pr: Preserve[E]): bool {.gcsafe.} =
|
|||
elif T is object:
|
||||
template fieldFromPreserve(key: string; val: typed; pr: Preserve[E]): bool {.used.} =
|
||||
when v.dot(key).hasCustomPragma(preservesLiteral):
|
||||
const lit = parsePreserves(v.dot(key).getCustomPragmaVal(preservesLiteral))
|
||||
pr == lit
|
||||
const atom = v.dot(key).getCustomPragmaVal(preservesLiteral).parsePreservesAtom
|
||||
pr == atom.toPreserveHook(E)
|
||||
else:
|
||||
fromPreserve(val, pr)
|
||||
when T.hasCustomPragma(unpreservable):
|
||||
|
|
|
@ -2,6 +2,6 @@ include_rules
|
|||
NIM_FLAGS += --path:$(TUP_CWD)/..
|
||||
: foreach preserves_schema_nim.nim schemaparse.nim |> !nim_bin |> $(BIN_DIR)/%B | $(BIN_DIR)/<%B>
|
||||
|
||||
DOT_FILES = ../../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>
|
||||
: foreach $(DOT_FILES) |> dot -Tsvg -LO %f > %o |> ../../%B-Grammer-Graph.svg
|
||||
|
|
|
@ -102,11 +102,9 @@ proc pushHexNibble[T](result: var T; c: char) =
|
|||
else: return
|
||||
result = (result shl 4) or n
|
||||
|
||||
proc parsePreserves*(text: string): Preserve[void] =
|
||||
## Parse a text-encoded Preserves `string` to a `Preserve` value.
|
||||
runnableExamples:
|
||||
assert parsePreserves"[ 1 2 3 ]" == [ 1, 2, 3 ].toPreserve
|
||||
const pegParser = peg("Document", stack: Stack):
|
||||
proc parsePreserves*(text: string): Value =
|
||||
## Parse a text-encoded Preserves `string` to a Preserves `Value`.
|
||||
let pegParser = peg("Document", stack: Stack):
|
||||
# Override rules from pegs.nim
|
||||
|
||||
Document <- Preserves.Document
|
||||
|
@ -219,7 +217,7 @@ proc parsePreserves*(text: string): Preserve[void] =
|
|||
pushStack val
|
||||
|
||||
Preserves.Compact <- Preserves.Compact:
|
||||
pushStack decodePreserves(stack.pop.value.bytes, void)
|
||||
pushStack decodePreserves(stack.pop.value.bytes)
|
||||
|
||||
var stack: Stack
|
||||
let match = pegParser.match(text, stack)
|
||||
|
@ -229,6 +227,69 @@ proc parsePreserves*(text: string): Preserve[void] =
|
|||
stack.pop.value
|
||||
|
||||
proc parsePreserves*(text: string; E: typedesc): Preserve[E] =
|
||||
## Parse a text-encoded Preserves `string` to a `Preserve[E]` value for embedded type `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):
|
||||
# Override rules from pegs.nim
|
||||
|
||||
Atom <- ?"#!" * Preserves.Atom
|
||||
|
||||
Preserves.Boolean <- Preserves.Boolean:
|
||||
case $0
|
||||
of "#f": a = Atom(kind: pkBoolean)
|
||||
of "#t": a = Atom(kind: pkBoolean, bool: true)
|
||||
else: discard
|
||||
|
||||
Preserves.Float <- Preserves.Float:
|
||||
a = Atom(kind: pkFloat, float: parseFloat($1))
|
||||
|
||||
Preserves.Double <- Preserves.Double:
|
||||
a = Atom(kind: pkDouble)
|
||||
discard parseBiggestFloat($0, a.double)
|
||||
|
||||
Preserves.FloatRaw <- Preserves.FloatRaw:
|
||||
var reg: uint32
|
||||
for c in $1: pushHexNibble(reg, c)
|
||||
a = Atom(kind: pkFloat, float: cast[float32](reg))
|
||||
|
||||
Preserves.DoubleRaw <- Preserves.DoubleRaw:
|
||||
var reg: uint64
|
||||
for c in $1: pushHexNibble(reg, c)
|
||||
a = Atom(kind: pkDouble, double: cast[float64](reg))
|
||||
|
||||
Preserves.SignedInteger <- Preserves.SignedInteger:
|
||||
var
|
||||
big = initBigInt($0)
|
||||
small = toInt[int](big)
|
||||
if small.isSome:
|
||||
a = Atom(kind: pkRegister, register: small.get)
|
||||
else:
|
||||
a = Atom(kind: pkBigInt, bigint: big)
|
||||
|
||||
Preserves.String <- Preserves.String:
|
||||
a = Atom(kind: pkString, string: newStringOfCap(len($1)))
|
||||
unescape(a.string, $1)
|
||||
if validateUtf8(a.string) != -1:
|
||||
raise newException(ValueError, "Preserves text contains an invalid UTF-8 sequence")
|
||||
|
||||
Preserves.charByteString <- Preserves.charByteString:
|
||||
a = Atom(kind: pkByteString, bytes: newSeqOfCap[byte](len($1)))
|
||||
unescape(a.bytes, $1)
|
||||
|
||||
Preserves.hexByteString <- Preserves.hexByteString:
|
||||
a = Atom(kind: pkByteString, bytes: cast[seq[byte]](parseHexStr(joinWhitespace($1))))
|
||||
|
||||
Preserves.b64ByteString <- Preserves.b64ByteString:
|
||||
a = Atom(kind: pkByteString, bytes: cast[seq[byte]](base64.decode(joinWhitespace($1))))
|
||||
|
||||
Preserves.Symbol <- Preserves.Symbol:
|
||||
var buf = newStringOfCap(len($1))
|
||||
unescape(buf, $1)
|
||||
a = Atom(kind: pkSymbol, symbol: Symbol buf)
|
||||
|
||||
if not pegParser.match(text, result).ok:
|
||||
raise newException(ValueError, "failed to parse Preserves atom: " & text)
|
||||
|
|
|
@ -21,6 +21,29 @@ proc hash*(s: Symbol): Hash {.borrow.}
|
|||
proc len*(s: Symbol): int {.borrow.}
|
||||
|
||||
type
|
||||
Atom* = object
|
||||
## Atomic Preserves value.
|
||||
## Useful when a `const Value` is required.
|
||||
case kind*: PreserveKind
|
||||
of pkBoolean:
|
||||
bool*: bool
|
||||
of pkFloat:
|
||||
float*: float32
|
||||
of pkDouble:
|
||||
double*: float64
|
||||
of pkRegister:
|
||||
register*: int
|
||||
of pkBigInt:
|
||||
bigint*: BigInt
|
||||
of pkString:
|
||||
string*: string
|
||||
of pkByteString:
|
||||
bytes*: seq[byte]
|
||||
of pkSymbol:
|
||||
symbol*: Symbol
|
||||
else:
|
||||
discard
|
||||
|
||||
Preserve*[E] = object
|
||||
embedded*: bool
|
||||
## Flag to mark embedded Preserves
|
||||
|
|
|
@ -39,3 +39,5 @@ suite "parse":
|
|||
a = encode test
|
||||
b = bin
|
||||
check(cast[string](a).toHex == b.toHex)
|
||||
if test.isAtomic:
|
||||
discard parsePreservesAtom(txt)
|
||||
|
|
Loading…
Reference in New Issue