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
|
# Package
|
||||||
|
|
||||||
version = "20231225"
|
version = "20231227"
|
||||||
author = "Emery Hemingway"
|
author = "Emery Hemingway"
|
||||||
description = "data model and serialization format"
|
description = "data model and serialization format"
|
||||||
license = "Unlicense"
|
license = "Unlicense"
|
||||||
|
|
|
@ -209,6 +209,8 @@ iterator pairs*[E](pr: Preserve[E]): DictEntry[E] =
|
||||||
assert(pr.kind == pkDictionary, "not a dictionary")
|
assert(pr.kind == pkDictionary, "not a dictionary")
|
||||||
for i in 0..pr.dict.high: yield pr.dict[i]
|
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
|
func isBoolean*(pr: Preserve): bool {.inline.} = pr.kind == pkBoolean
|
||||||
## Check if ``pr`` is a Preserve boolean.
|
## Check if ``pr`` is a Preserve boolean.
|
||||||
|
|
||||||
|
@ -414,8 +416,8 @@ proc toPreserve*[T](x: T; E = void): Preserve[E] {.gcsafe.} =
|
||||||
v.embedded = true
|
v.embedded = true
|
||||||
template fieldToPreserve(key: string; val: typed): Preserve {.used.} =
|
template fieldToPreserve(key: string; val: typed): Preserve {.used.} =
|
||||||
when x.dot(key).hasCustomPragma(preservesLiteral):
|
when x.dot(key).hasCustomPragma(preservesLiteral):
|
||||||
const lit = parsePreserves(x.dot(key).getCustomPragmaVal(preservesLiteral))
|
const atom = x.dot(key).getCustomPragmaVal(preservesLiteral).parsePreservesAtom
|
||||||
cast[Preserve[E]](lit)
|
atom.toPreserveHook(E)
|
||||||
else:
|
else:
|
||||||
toPreserve(val, E)
|
toPreserve(val, E)
|
||||||
when T.hasCustomPragma(unpreservable):
|
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
|
# the hook doesn't compile but produces a useful error
|
||||||
trace T, " -> ", result
|
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] =
|
proc toPreserveHook*[T](set: HashSet[T]; E: typedesc): Preserve[E] =
|
||||||
## Hook for preserving ``HashSet``.
|
## Hook for preserving ``HashSet``.
|
||||||
result = Preserve[E](kind: pkSet, set: newSeqOfCap[Preserve[E]](set.len))
|
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:
|
elif pr.kind == pkEmbedded:
|
||||||
result = true
|
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.} =
|
proc fromPreserve*[T, E](v: var T; pr: Preserve[E]): bool {.gcsafe.} =
|
||||||
## Inplace version of `preserveTo`. Returns ``true`` on
|
## Inplace version of `preserveTo`. Returns ``true`` on
|
||||||
## a complete match, otherwise returns ``false``.
|
## 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:
|
elif T is object:
|
||||||
template fieldFromPreserve(key: string; val: typed; pr: Preserve[E]): bool {.used.} =
|
template fieldFromPreserve(key: string; val: typed; pr: Preserve[E]): bool {.used.} =
|
||||||
when v.dot(key).hasCustomPragma(preservesLiteral):
|
when v.dot(key).hasCustomPragma(preservesLiteral):
|
||||||
const lit = parsePreserves(v.dot(key).getCustomPragmaVal(preservesLiteral))
|
const atom = v.dot(key).getCustomPragmaVal(preservesLiteral).parsePreservesAtom
|
||||||
pr == lit
|
pr == atom.toPreserveHook(E)
|
||||||
else:
|
else:
|
||||||
fromPreserve(val, pr)
|
fromPreserve(val, pr)
|
||||||
when T.hasCustomPragma(unpreservable):
|
when T.hasCustomPragma(unpreservable):
|
||||||
|
|
|
@ -2,6 +2,6 @@ include_rules
|
||||||
NIM_FLAGS += --path:$(TUP_CWD)/..
|
NIM_FLAGS += --path:$(TUP_CWD)/..
|
||||||
: foreach preserves_schema_nim.nim schemaparse.nim |> !nim_bin |> $(BIN_DIR)/%B | $(BIN_DIR)/<%B>
|
: 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>
|
: 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
|
||||||
|
|
|
@ -102,11 +102,9 @@ proc pushHexNibble[T](result: var T; c: char) =
|
||||||
else: return
|
else: return
|
||||||
result = (result shl 4) or n
|
result = (result shl 4) or n
|
||||||
|
|
||||||
proc parsePreserves*(text: string): Preserve[void] =
|
proc parsePreserves*(text: string): Value =
|
||||||
## Parse a text-encoded Preserves `string` to a `Preserve` value.
|
## Parse a text-encoded Preserves `string` to a Preserves `Value`.
|
||||||
runnableExamples:
|
let pegParser = peg("Document", stack: Stack):
|
||||||
assert parsePreserves"[ 1 2 3 ]" == [ 1, 2, 3 ].toPreserve
|
|
||||||
const pegParser = peg("Document", stack: Stack):
|
|
||||||
# Override rules from pegs.nim
|
# Override rules from pegs.nim
|
||||||
|
|
||||||
Document <- Preserves.Document
|
Document <- Preserves.Document
|
||||||
|
@ -219,7 +217,7 @@ proc parsePreserves*(text: string): Preserve[void] =
|
||||||
pushStack val
|
pushStack val
|
||||||
|
|
||||||
Preserves.Compact <- Preserves.Compact:
|
Preserves.Compact <- Preserves.Compact:
|
||||||
pushStack decodePreserves(stack.pop.value.bytes, void)
|
pushStack decodePreserves(stack.pop.value.bytes)
|
||||||
|
|
||||||
var stack: Stack
|
var stack: Stack
|
||||||
let match = pegParser.match(text, stack)
|
let match = pegParser.match(text, stack)
|
||||||
|
@ -229,6 +227,69 @@ proc parsePreserves*(text: string): Preserve[void] =
|
||||||
stack.pop.value
|
stack.pop.value
|
||||||
|
|
||||||
proc parsePreserves*(text: string; E: typedesc): Preserve[E] =
|
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)
|
when E is void: parsePreserves(text)
|
||||||
else: mapEmbeds(parsePreserves(text), E)
|
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.}
|
proc len*(s: Symbol): int {.borrow.}
|
||||||
|
|
||||||
type
|
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
|
Preserve*[E] = object
|
||||||
embedded*: bool
|
embedded*: bool
|
||||||
## Flag to mark embedded Preserves
|
## Flag to mark embedded Preserves
|
||||||
|
|
|
@ -39,3 +39,5 @@ suite "parse":
|
||||||
a = encode test
|
a = encode test
|
||||||
b = bin
|
b = bin
|
||||||
check(cast[string](a).toHex == b.toHex)
|
check(cast[string](a).toHex == b.toHex)
|
||||||
|
if test.isAtomic:
|
||||||
|
discard parsePreservesAtom(txt)
|
||||||
|
|
Loading…
Reference in New Issue