Compare commits
10 Commits
15f7a6bc3d
...
d4f027ffbe
Author | SHA1 | Date |
---|---|---|
Emery Hemingway | d4f027ffbe | |
Emery Hemingway | a104866bc5 | |
Emery Hemingway | 407b7542bb | |
Emery Hemingway | e4d6c4f04a | |
Emery Hemingway | 78b63ac282 | |
Emery Hemingway | 5106f148ed | |
Emery Hemingway | 9c7839d05a | |
Emery Hemingway | 3ad3127a40 | |
Emery Hemingway | c0054ede85 | |
Emery Hemingway | 765dbfe640 |
|
@ -1,6 +1,6 @@
|
|||
# Package
|
||||
|
||||
version = "20231230"
|
||||
version = "20240106"
|
||||
author = "Emery Hemingway"
|
||||
description = "data model and serialization format"
|
||||
license = "Unlicense"
|
||||
|
|
|
@ -142,15 +142,6 @@ proc `&`*(x: Value; y: seq[Value]): Value =
|
|||
raise newException(ValueError, "cannot concatenate to non-sequence value")
|
||||
result = Value(kind: pkSequence, sequence: x.sequence & y)
|
||||
|
||||
proc getOrDefault(pr, key: Value): Value =
|
||||
## Retrieves the value of `pr[key]` if `pr` is a dictionary containing `key`
|
||||
## or returns the `#f` Preserves value.
|
||||
if pr.kind == pkDictionary:
|
||||
for (k, v) in pr.dict:
|
||||
if key == k:
|
||||
result = v
|
||||
break
|
||||
|
||||
proc pop*(pr: var Value; key: Value; val: var Value): bool =
|
||||
## Deletes the `key` from a Preserves dictionary.
|
||||
## Returns true, if the key existed, and sets `val` to the mapping
|
||||
|
@ -207,6 +198,11 @@ proc initRecord*(label: string; args: varargs[Value]): Value {.inline.} =
|
|||
## Create a Preserves record value.
|
||||
initRecord(toSymbol(label), args)
|
||||
|
||||
proc toRecord*(items: varargs[Value, toPreserves]): Value =
|
||||
assert items.len > 0
|
||||
result = initRecord(items[0], items.len.pred)
|
||||
for i in 0..<items.high: result.record[i] = items[succ i]
|
||||
|
||||
proc initSequence*(len: Natural = 0): Value =
|
||||
## Create a Preserves sequence value.
|
||||
Value(kind: pkSequence, sequence: newSeq[Value](len))
|
||||
|
@ -435,9 +431,15 @@ proc toPreserves*[T](x: T): Value {.gcsafe.} =
|
|||
elif T.hasCustomPragma(preservesDictionary):
|
||||
result = initDictionary()
|
||||
for key, val in x.fieldPairs:
|
||||
var pr = fieldToPreserve(key, val)
|
||||
applyEmbed(key, pr)
|
||||
result[toSymbol(key)] = pr
|
||||
when val is Option:
|
||||
if val.isSome:
|
||||
var pr = fieldToPreserve(key, val.get)
|
||||
applyEmbed(key, pr)
|
||||
result[key.toSymbol] = pr
|
||||
else:
|
||||
var pr = fieldToPreserve(key, val)
|
||||
applyEmbed(key, pr)
|
||||
result[key.toSymbol] = pr
|
||||
sortDict(result)
|
||||
else:
|
||||
{.warning: "failed to preserve object " & $T .}
|
||||
|
@ -486,6 +488,9 @@ proc toPreservesHook*[A, B](table: Table[A, B]|TableRef[A, B]): Value =
|
|||
result[toPreserves(k)] = toPreserves(v)
|
||||
sortDict(result)
|
||||
|
||||
proc toPreservesHook*(o: Option): Value =
|
||||
o.get.toPreserves
|
||||
|
||||
proc fromAtom*[T](v: var T; a: ATom): bool =
|
||||
if T is Atom:
|
||||
v = a
|
||||
|
@ -710,12 +715,17 @@ proc fromPreserves*[T](v: var T; pr: Value): bool {.gcsafe.} =
|
|||
if pr.isDictionary:
|
||||
result = true
|
||||
var i: int
|
||||
for key, _ in fieldPairs(v):
|
||||
let val = pr.getOrDefault(toSymbol(key))
|
||||
result = result and fieldFromPreserve(
|
||||
key, v.dot(key), val)
|
||||
for key, field in fieldPairs(v):
|
||||
if not result: break
|
||||
inc i
|
||||
let val = step(pr, key.toSymbol)
|
||||
when field is Option:
|
||||
if val.isSome:
|
||||
discard fieldFromPreserve(key, v.dot(key), val.get)
|
||||
else:
|
||||
inc i
|
||||
result = result and val.isSome
|
||||
if result:
|
||||
result = result and fieldFromPreserve(key, v.dot(key), val.get)
|
||||
result = result and (i <= pr.len)
|
||||
elif T.hasCustomPragma(preservesOr):
|
||||
for kind in typeof(T.orKind):
|
||||
|
@ -735,9 +745,11 @@ proc fromPreserves*[T](v: var T; pr: Value): bool {.gcsafe.} =
|
|||
result = true
|
||||
var i: int
|
||||
for key, _ in fieldPairs(v):
|
||||
let val = pr.getOrDefault(toSymbol(key))
|
||||
result = result and fieldFromPreserve(key, v.dot(key), val)
|
||||
if not result: break
|
||||
let val = step(pr, key.toSymbol)
|
||||
result = result and val.isSome
|
||||
if result:
|
||||
result = result and fieldFromPreserve(key, v.dot(key), val.get)
|
||||
inc i
|
||||
result = result and (i <= pr.len)
|
||||
else:
|
||||
|
@ -806,19 +818,18 @@ proc fromPreservesHook*[A,B](t: var (Table[A,B]|TableRef[A,B]); pr: Value): bool
|
|||
break
|
||||
t[move a] = move b
|
||||
|
||||
proc fromPreservesHook*[T](opt: var Option[T]; pr: Value): bool =
|
||||
opt = some(default T)
|
||||
result = opt.get.fromPreserves(pr)
|
||||
if not result:
|
||||
opt = none(T)
|
||||
|
||||
when isMainModule:
|
||||
var t: Table[int, string]
|
||||
var pr = t.toPreservesHook()
|
||||
assert fromPreservesHook(t, pr)
|
||||
|
||||
func step*(pr, idx: Value): Option[Value] =
|
||||
## Step into `pr` by index `idx`.
|
||||
## Works for sequences, records, and dictionaries.
|
||||
runnableExamples:
|
||||
import std/options
|
||||
assert step(parsePreserves("""<foo 1 2>"""), 1.toPreserve) == some(2.toPreserve)
|
||||
assert step(parsePreserves("""{ foo: 1 bar: 2}"""), "foo".toSymbol) == some(1.toPreserve)
|
||||
assert step(parsePreserves("""[ ]"""), 1.toPreserve) == none(Value)
|
||||
func step(pr, idx: Value): Option[Value] =
|
||||
if pr.isDictionary:
|
||||
for (k, v) in pr.dict.items:
|
||||
if k == idx:
|
||||
|
@ -831,8 +842,14 @@ func step*(pr, idx: Value): Option[Value] =
|
|||
if i < pr.len:
|
||||
result = some(pr[i])
|
||||
|
||||
func step*(pr: Value; path: varargs[Value]): Option[Value] =
|
||||
## Step into `pr` by indexes at `path`.
|
||||
func step*(pr: Value; path: varargs[Value, toPreserves]): Option[Value] =
|
||||
## Step into `pr` by index `idx`.
|
||||
## Works for sequences, records, and dictionaries.
|
||||
runnableExamples:
|
||||
import std/options
|
||||
assert step(parsePreserves("""<foo 1 2>"""), 1.toPreserve) == some(2.toPreserve)
|
||||
assert step(parsePreserves("""{ foo: 1 bar: 2}"""), "foo".toSymbol) == some(1.toPreserve)
|
||||
assert step(parsePreserves("""[ ]"""), 1.toPreserve) == none(Value)
|
||||
result = some(pr)
|
||||
for index in path:
|
||||
if result.isSome:
|
||||
|
@ -865,105 +882,40 @@ proc apply*(result: var Value; op: proc(_: var Value) {.gcsafe.}) {.gcsafe.} =
|
|||
recurse(e.val)
|
||||
cannonicalize(result)
|
||||
|
||||
proc mapEmbeds*[A, B](pr: sink Value; op: proc (v: A): B): Value =
|
||||
## Convert `Value` to `Value` using an `A → B` procedure.
|
||||
runnableExamples:
|
||||
import std/tables
|
||||
type MacGuffin = ref object
|
||||
stuff: void
|
||||
var registry = {20: new MacGuffin}.toTable
|
||||
let
|
||||
a = [ 20.embed ].toPreserves(int)
|
||||
b = mapEmbeds(a) do (i: int) -> MacGuffin:
|
||||
registry[i]
|
||||
assert typeof(b[0].unembed) is MacGuffin
|
||||
if pr.embedded:
|
||||
var
|
||||
e: A
|
||||
pr = pr # TODO: avoid copy
|
||||
pr.embedded = false
|
||||
if not fromPreserves(e, pr):
|
||||
raise newException(ValueError, "failed to map across embedded types")
|
||||
result = embed op(e)
|
||||
else:
|
||||
case pr.kind
|
||||
of pkBoolean, pkFloat, pkDouble, pkRegister, pkBigInt,
|
||||
pkString, pkByteString, pkSymbol:
|
||||
result = cast[Value](pr)
|
||||
of pkRecord:
|
||||
result = Value(kind: pr.kind)
|
||||
result.record = map(pr.record) do (x: Value) -> Value:
|
||||
mapEmbeds(x, op)
|
||||
of pkSequence:
|
||||
result = Value(kind: pr.kind)
|
||||
result.sequence = map(pr.sequence) do (x: Value) -> Value:
|
||||
mapEmbeds(x, op)
|
||||
of pkSet:
|
||||
result = Value(kind: pr.kind)
|
||||
result.set = map(pr.set) do (x: Value) -> Value:
|
||||
mapEmbeds(x, op)
|
||||
of pkDictionary:
|
||||
result = Value(kind: pr.kind)
|
||||
result.dict = map(pr.dict) do (e: DictEntry[A]) -> DictEntry[B]:
|
||||
(mapEmbeds(e.key, op), mapEmbeds(e.val, op))
|
||||
of pkEmbedded:
|
||||
result = embed op(pr.embed)
|
||||
cannonicalize(result)
|
||||
|
||||
proc contract*[T](pr: sink Value; op: proc (v: T): Value {.gcsafe.}): Value {.gcsafe.} =
|
||||
## Convert `Value` to `Value` using an `T → Value` procedure.
|
||||
if not pr.embedded:
|
||||
case pr.kind
|
||||
of pkBoolean, pkFloat, pkDouble, pkRegister, pkBigInt,
|
||||
pkString, pkByteString, pkSymbol:
|
||||
result = cast[Value](pr)
|
||||
of pkRecord:
|
||||
result = Value(kind: pr.kind)
|
||||
result.record = map(pr.record) do (x: Value) -> Value:
|
||||
contract(x, op)
|
||||
of pkSequence:
|
||||
result = Value(kind: pr.kind)
|
||||
result.sequence = map(pr.sequence) do (x: Value) -> Value:
|
||||
contract(x, op)
|
||||
of pkSet:
|
||||
result = Value(kind: pr.kind)
|
||||
result.set = map(pr.set) do (x: Value) -> Value:
|
||||
contract(x, op)
|
||||
of pkDictionary:
|
||||
result = Value(kind: pr.kind)
|
||||
result.dict = map(pr.dict) do (e: DictEntry) -> DictEntry:
|
||||
(contract(e.key, op), contract(e.val, op))
|
||||
of pkEmbedded:
|
||||
proc mapEmbeds*[T](pr: sink Value; op: proc (x: T): Value {.gcsafe.}): Value {.gcsafe.} =
|
||||
## Process all embeds in a `Value` that are of type `T`.
|
||||
case pr.kind
|
||||
of pkBoolean, pkFloat, pkDouble, pkRegister, pkBigInt,
|
||||
pkString, pkByteString, pkSymbol:
|
||||
result = cast[Value](pr)
|
||||
of pkRecord:
|
||||
result = Value(kind: pr.kind)
|
||||
result.record = map(pr.record) do (x: Value) -> Value:
|
||||
mapEmbeds(x, op)
|
||||
of pkSequence:
|
||||
result = Value(kind: pr.kind)
|
||||
result.sequence = map(pr.sequence) do (x: Value) -> Value:
|
||||
mapEmbeds(x, op)
|
||||
of pkSet:
|
||||
result = Value(kind: pr.kind)
|
||||
result.set = map(pr.set) do (x: Value) -> Value:
|
||||
mapEmbeds(x, op)
|
||||
of pkDictionary:
|
||||
result = Value(kind: pr.kind)
|
||||
result.dict = map(pr.dict) do (e: DictEntry) -> DictEntry:
|
||||
(mapEmbeds(e.key, op), mapEmbeds(e.val, op))
|
||||
of pkEmbedded:
|
||||
when T is Value: result = pr
|
||||
else:
|
||||
if pr.embeddedRef of T:
|
||||
result = embed op(T pr.embeddedRef)
|
||||
cannonicalize(result)
|
||||
|
||||
proc expand*(pr: sink Value; op: proc (v: Value): Value {.gcsafe.}): Value {.gcsafe.} =
|
||||
## Convert `Value` to `Value` using an `Value → Value` procedure.
|
||||
if pr.embedded:
|
||||
result = op(pr)
|
||||
else:
|
||||
case pr.kind
|
||||
of pkBoolean, pkFloat, pkDouble, pkRegister, pkBigInt,
|
||||
pkString, pkByteString, pkSymbol, pkEmbedded:
|
||||
result = pr
|
||||
of pkRecord:
|
||||
result = Value(kind: pr.kind)
|
||||
result.record = map(pr.record) do (x: Value) -> Value:
|
||||
expand(x, op)
|
||||
of pkSequence:
|
||||
result = Value(kind: pr.kind)
|
||||
result.sequence = map(pr.sequence) do (x: Value) -> Value:
|
||||
expand(x, op)
|
||||
of pkSet:
|
||||
result = Value(kind: pr.kind)
|
||||
result.set = map(pr.set) do (x: Value) -> Value:
|
||||
expand(x, op)
|
||||
of pkDictionary:
|
||||
result = Value(kind: pr.kind)
|
||||
result.dict = map(pr.dict) do (e: DictEntry) -> DictEntry:
|
||||
(expand(e.key, op), expand(e.val, op))
|
||||
result = op(T pr.embeddedRef)
|
||||
result.embedded = true
|
||||
else: result = pr
|
||||
when T is Value:
|
||||
if result.embedded:
|
||||
result = op(pr)
|
||||
cannonicalize(result)
|
||||
# TODO: is this necessary?
|
||||
|
||||
proc getOrDefault*[T, V](pr: Value; key: string; default: V): V =
|
||||
## Retrieves the value of `pr[key]` if `pr` is a dictionary containing `key`
|
||||
|
|
|
@ -6,10 +6,7 @@
|
|||
# the schema module must be regenerated!
|
||||
# nim c --path:../../../nim --path:.. -r ./preserves_schema_nim ../../../preserves/schema/schema.bin
|
||||
|
||||
# This module formally parameterized types wherever embedded values were found.
|
||||
# This is no longer the case but some of that code is lingering around.
|
||||
|
||||
import std/[hashes, strutils, sets, tables]
|
||||
import std/[hashes, sets, strutils, tables]
|
||||
|
||||
# Cannot use std/macros, must use compiler modules because
|
||||
# we are generating code at run-time.
|
||||
|
@ -97,7 +94,7 @@ proc isEmbedded(ts: TypeSpec): bool =
|
|||
|
||||
func isAtomic(r: Ref): bool =
|
||||
case r.name.string
|
||||
of "bool", "float", "double", "int", "string", "bytes", "symbol": true
|
||||
of "bool", "float", "double", "int", "string", "bytes", "symbol": true
|
||||
else: false
|
||||
|
||||
proc addAttrs(x: var TypeSpec; y: TypeSpec) =
|
||||
|
@ -131,6 +128,24 @@ proc hasEmbeddedType(scm: Schema): bool =
|
|||
of EmbeddedtypenameKind.false: false
|
||||
of EmbeddedtypenameKind.Ref: true
|
||||
|
||||
proc embeddedIdentString(scm: Schema): string =
|
||||
doAssert $scm.field0.embeddedType.ref.name != ""
|
||||
$scm.field0.embeddedType.ref.name
|
||||
|
||||
proc embeddedIdent(scm: Schema): PNode =
|
||||
ident(embeddedIdentString(scm))
|
||||
|
||||
proc parameterize(loc: Location; node: PNode; embeddable: bool): PNode =
|
||||
if embeddable and
|
||||
loc.schema.hasEmbeddedType and
|
||||
node.kind notin {nkBracketExpr} and
|
||||
(node.kind != nkIdent or node.ident.s != loc.schema.embeddedIdentString):
|
||||
nkBracketExpr.newTree(node, loc.schema.embeddedIdent)
|
||||
else: node
|
||||
|
||||
proc parameterize(loc: Location; spec: TypeSpec): PNode =
|
||||
parameterize(loc, spec.node, spec.isEmbedded)
|
||||
|
||||
proc hash(r: Ref): Hash = r.toPreserves.hash
|
||||
type RefSet = HashSet[Ref]
|
||||
|
||||
|
@ -143,8 +158,7 @@ proc attrs(loc: Location; n: NamedAlternative|NamedPattern; seen: RefSet): Attri
|
|||
proc attrs(loc: Location; sp: SimplePattern; seen: RefSet): Attributes =
|
||||
case sp.orKind
|
||||
of SimplepatternKind.atom, SimplepatternKind.lit, SimplepatternKind.any: {}
|
||||
of SimplepatternKind.embedded:
|
||||
attrs(loc, sp.embedded.interface, seen)
|
||||
of SimplepatternKind.embedded: {embedded}
|
||||
of SimplepatternKind.seqof:
|
||||
attrs(loc, sp.seqof.pattern, seen)
|
||||
of SimplepatternKind.setof:
|
||||
|
@ -302,7 +316,8 @@ proc isLiteral(loc: Location; sp: SimplePattern): bool =
|
|||
of SimplepatternKind.lit:
|
||||
result = true
|
||||
of SimplepatternKind.embedded:
|
||||
result = isLiteral(loc, sp.embedded.interface)
|
||||
if not loc.schema.hasEmbeddedType:
|
||||
result = isLiteral(loc, sp.embedded.interface)
|
||||
else: discard
|
||||
|
||||
proc isLiteral(loc: Location; np: NamedPattern): bool =
|
||||
|
@ -326,10 +341,6 @@ proc isLiteral(loc: Location; def: Definition): bool =
|
|||
proc isRef(sp: SimplePattern): bool =
|
||||
sp.orKind == SimplePatternKind.Ref
|
||||
|
||||
proc isRef(pat: Pattern): bool =
|
||||
pat.orKind == PatternKind.SimplePattern and
|
||||
pat.simplePattern.isRef
|
||||
|
||||
proc isSimple(pat: Pattern): bool =
|
||||
pat.orKind == PatternKind.SimplePattern
|
||||
|
||||
|
@ -377,16 +388,22 @@ proc isDictionary(loc: Location; pat: Pattern): bool =
|
|||
result = true
|
||||
else: discard
|
||||
|
||||
proc isDictionary(loc: Location; defAnd: DefinitionAnd): bool =
|
||||
result = isDictionary(loc, defAnd.field0.pattern0.pattern)
|
||||
|
||||
proc isDictionary(loc: Location; def: Definition): bool =
|
||||
case def.orKind
|
||||
of DefinitionKind.Pattern:
|
||||
result = isDictionary(loc, def.pattern)
|
||||
of DefinitionKind.or:
|
||||
result =
|
||||
isDictionary(loc, def.or.field0.pattern0.pattern) and
|
||||
isDictionary(loc, def.or.field0.pattern1.pattern)
|
||||
for np in def.or.field0.patternN:
|
||||
if result: result = isDictionary(loc, np.pattern)
|
||||
of DefinitionKind.and:
|
||||
result = isDictionary(loc, def.and)
|
||||
else: discard
|
||||
result =
|
||||
isDictionary(loc, def.and.field0.pattern0.pattern) and
|
||||
isDictionary(loc, def.and.field0.pattern1.pattern)
|
||||
for np in def.and.field0.patternN:
|
||||
if result: result = isDictionary(loc, np.pattern)
|
||||
|
||||
proc isAny(loc: Location; def: Definition): bool =
|
||||
case def.orKind
|
||||
|
@ -439,8 +456,12 @@ proc typeIdent(loc: Location; sp: SimplePattern): TypeSpec =
|
|||
result.attrs = key.attrs + val.attrs
|
||||
of SimplepatternKind.Ref:
|
||||
result = TypeSpec(node: ident(sp.ref), attrs: attrs(loc, sp))
|
||||
result.node = parameterize(loc, result)
|
||||
of SimplepatternKind.embedded:
|
||||
result = typeIdent(loc, sp.embedded.interface)
|
||||
if loc.schema.hasEmbeddedType:
|
||||
result = TypeSpec(node: loc.schema.embeddedIdent())
|
||||
else:
|
||||
result = TypeSpec(node: ident"Value")
|
||||
incl(result.attrs, embedded)
|
||||
of SimplepatternKind.any, SimplepatternKind.lit:
|
||||
result = TypeSpec(node: ident"Value")
|
||||
|
@ -469,6 +490,7 @@ proc toStrLit(loc: Location; sp: SimplePattern): PNode =
|
|||
var (loc, def) = deref(loc, sp.ref)
|
||||
result = toStrLit(loc, def)
|
||||
of SimplePatternKind.embedded:
|
||||
doAssert not loc.schema.hasEmbeddedType
|
||||
result = PNode(kind: nkStrLit, strVal: "#!" & toStrLit(loc, sp.embedded.interface).strVal)
|
||||
else: raiseAssert $sp
|
||||
|
||||
|
@ -487,8 +509,21 @@ proc toFieldIdent(loc: Location, label: string; pat: Pattern): PNode =
|
|||
|
||||
proc newEmpty(): PNode = newNode(nkEmpty)
|
||||
|
||||
proc embeddingParams(loc: Location; embeddable: bool): PNode =
|
||||
if embeddable and loc.schema.hasEmbeddedType:
|
||||
nkGenericParams.newTree(
|
||||
nkIdentDefs.newTree(embeddedIdent(loc.schema),
|
||||
newEmpty(),
|
||||
newEmpty()))
|
||||
else:
|
||||
newEmpty()
|
||||
|
||||
proc identDef(scm: Schema; a, b: PNode; embeddable: bool): PNode =
|
||||
nkIdentDefs.newTree(a, b, newEmpty())
|
||||
if embeddable and scm.hasEmbeddedType and b.kind notin {nkBracketExpr, nkTupleTy} and
|
||||
(b.kind != nkIdent or b.ident.s != scm.embeddedIdentString):
|
||||
nkIdentDefs.newTree(a, nkBracketExpr.newTree(b, embeddedIdent(scm)), newEmpty())
|
||||
else:
|
||||
nkIdentDefs.newTree(a, b, newEmpty())
|
||||
|
||||
proc identDef(scm: Schema; l: PNode; ts: TypeSpec): PNode =
|
||||
identDef(scm, l, ts.node, ts.isEmbedded)
|
||||
|
@ -537,7 +572,7 @@ proc typeDef(loc: Location; name: string; pat: SimplePattern; ty: PNode): PNode
|
|||
|
||||
proc typeDef(loc: Location; name: string; pat: Pattern; ty: PNode): PNode =
|
||||
let
|
||||
embedParams = newEmpty()
|
||||
embedParams = embeddingParams(loc, isEmbedded(loc, pat))
|
||||
id = name.ident.toExport
|
||||
case pat.orKind
|
||||
of PatternKind.CompoundPattern:
|
||||
|
@ -579,15 +614,17 @@ proc typeDef(loc: Location; name: string; def: Definition; ty: PNode): PNode =
|
|||
nkPragmaExpr.newTree(
|
||||
name.ident.accQuote.toExport,
|
||||
pragma),
|
||||
newEmpty(),
|
||||
embeddingParams(loc, isEmbedded(loc, def)),
|
||||
ty)
|
||||
of DefinitionKind.and:
|
||||
let pragma = newEmpty()
|
||||
var pragma = nkPragma.newNode
|
||||
if isDictionary(loc, def):
|
||||
pragma.add(ident"preservesDictionary")
|
||||
nkTypeDef.newTree(
|
||||
nkPragmaExpr.newTree(
|
||||
name.ident.accQuote.toExport,
|
||||
pragma),
|
||||
newEmpty(),
|
||||
embeddingParams(loc, isEmbedded(loc, def)),
|
||||
ty)
|
||||
of DefinitionKind.Pattern:
|
||||
typeDef(loc, name, def.pattern, ty)
|
||||
|
@ -611,7 +648,7 @@ proc addField(recList: PNode; loc: Location; known: var TypeTable; sp: SimplePat
|
|||
ident"preservesLiteral",
|
||||
toStrLit(loc, sp))))
|
||||
recList.add identDef(scm, id, TypeSpec(node: ident"tuple[]"))
|
||||
elif sp.orKind == SimplePatternKind.embedded and not scm.hasEmbeddedType:
|
||||
elif sp.orKind == SimplePatternKind.embedded:
|
||||
let id = nkPragmaExpr.newTree(
|
||||
id, nkPragma.newTree(ident"preservesEmbedded"))
|
||||
recList.add identDef(scm, id, nimTypeOf(loc, known, "", sp))
|
||||
|
@ -623,7 +660,7 @@ proc addField(recList: PNode; loc: Location; known: var TypeTable; parentName: s
|
|||
label = label(np, parentName, index)
|
||||
id = label.toFieldIdent
|
||||
pattern = np.pattern
|
||||
if pattern.isRef or pattern.isSimple:
|
||||
if pattern.isSimple:
|
||||
addField(recList, loc, known, pattern.simplePattern, label)
|
||||
else:
|
||||
var
|
||||
|
@ -633,20 +670,25 @@ proc addField(recList: PNode; loc: Location; known: var TypeTable; parentName: s
|
|||
known[typePath] = typeDef(loc, typeName, pattern, fieldSpec.node)
|
||||
recList.add identDef(loc.schema, id, ident(typeName), isEmbedded(loc, pattern))
|
||||
|
||||
proc addField(recList: PNode; loc: Location; known: var TypeTable; parentName: string; nsp: NamedSimplePattern; index = 0) =
|
||||
proc addField(recList: PNode; loc: Location; known: var TypeTable; parentName: string; nsp: NamedSimplePattern; index: int; optional: bool) =
|
||||
let
|
||||
label = label(nsp, parentName, index)
|
||||
id = label.toFieldIdent
|
||||
pattern = nsp.pattern
|
||||
if pattern.isRef:
|
||||
addField(recList, loc, known, pattern, label)
|
||||
var node = typeIdent(loc, pattern).node
|
||||
if optional:
|
||||
node = nkBracketExpr.newTree(ident"Option", node)
|
||||
recList.add identDef(loc.schema, id, node, false)
|
||||
else:
|
||||
var
|
||||
typeName = parentName & capitalizeAscii(label)
|
||||
typePath = loc.schemaPath & @[Symbol typeName]
|
||||
fieldSpec = nimTypeOf(loc, known, label, pattern)
|
||||
if optional:
|
||||
fieldSpec.node = nkBracketExpr.newTree(ident"Option", fieldSpec.node)
|
||||
known[typePath] = typeDef(loc, typeName, pattern, fieldSpec.node)
|
||||
recList.add identDef(loc.schema, id, ident(typeName), false)
|
||||
recList.add identDef(loc.schema, id, fieldSpec.node, false)
|
||||
|
||||
proc addFields(recList: PNode; loc: Location; known: var TypeTable; parentName: string; cp: CompoundPattern): PNode {.discardable.} =
|
||||
let scm = loc.schema
|
||||
|
@ -655,7 +697,7 @@ proc addFields(recList: PNode; loc: Location; known: var TypeTable; parentName:
|
|||
label = label(np, parentName, index)
|
||||
id = label.toFieldIdent
|
||||
pattern = np.pattern
|
||||
if pattern.isRef or pattern.isSimple:
|
||||
if pattern.isSimple:
|
||||
addField(recList, loc, known, pattern.simplePattern, label)
|
||||
else:
|
||||
var
|
||||
|
@ -678,11 +720,11 @@ proc addFields(recList: PNode; loc: Location; known: var TypeTable; parentName:
|
|||
nkPragmaExpr.newTree(
|
||||
ident(cp.tuplePrefix.variable, parentName).accQuote.toExport,
|
||||
nkPragma.newTree(ident"preservesTupleTail")),
|
||||
variableType.node,
|
||||
parameterize(loc, variableType),
|
||||
variableType.isEmbedded)
|
||||
of CompoundPatternKind.dict:
|
||||
for nameVal, nsp in cp.dict.entries:
|
||||
recList.addField(loc, known, $nameVal, nsp)
|
||||
recList.addField(loc, known, $nameVal, nsp, 0, false)
|
||||
reclist
|
||||
|
||||
proc addFields(recList: PNode; loc: Location; known: var TypeTable; name: string; pat: SimplePattern): PNode {.discardable.} =
|
||||
|
@ -777,8 +819,8 @@ proc nimTypeOf(loc: Location; known: var TypeTable; name: string; orDef: Definit
|
|||
var memberType: TypeSpec
|
||||
if isLiteral(loc, na.pattern):
|
||||
memberType.node = ident"bool"
|
||||
elif na.pattern.isRef:
|
||||
memberType = typeIdent(loc, na.pattern)
|
||||
elif na.pattern.isSimple:
|
||||
memberType = typeIdent(loc, na.pattern)
|
||||
else:
|
||||
let
|
||||
memberTypeName = name & na.variantLabel.capitalizeAscii
|
||||
|
@ -790,6 +832,8 @@ proc nimTypeOf(loc: Location; known: var TypeTable; name: string; orDef: Definit
|
|||
known[memberPath] =
|
||||
typeDef(loc, memberTypeName, na.pattern, ty.node)
|
||||
addAttrs(result, memberType)
|
||||
memberType.node = parameterize(
|
||||
loc, memberType.node, isEmbedded(loc, na.pattern))
|
||||
branchRecList.add nkIdentDefs.newTree(
|
||||
toFieldIdent(loc, na.variantLabel.normalize, na.pattern),
|
||||
memberType.node, newEmpty())
|
||||
|
@ -805,9 +849,88 @@ proc nimTypeOf(loc: Location; known: var TypeTable; name: string; orDef: Definit
|
|||
newEmpty(),
|
||||
nkRecList.newTree(recCase))
|
||||
|
||||
proc nimTypeOf(loc: Location; known: var TypeTable; name: string; def: DefinitionAnd): TypeSpec =
|
||||
proc isAny(sp: SimplePattern): bool =
|
||||
sp.orKind == SimplePatternKind.any
|
||||
|
||||
proc initSimpleAny: SimplePattern =
|
||||
SimplePattern(orKind: SimplePatternKind.any)
|
||||
|
||||
proc asAny(nsp: NamedSimplePattern): NamedSimplePattern =
|
||||
result = nsp
|
||||
case result.orKind
|
||||
of NamedSimplePatternKind.named:
|
||||
if not result.named.pattern.isAny:
|
||||
result.named.pattern = initSimpleAny()
|
||||
of NamedSimplePatternKind.anonymous:
|
||||
if not result.anonymous.isAny:
|
||||
result.anonymous = initSimpleAny()
|
||||
|
||||
type
|
||||
AndEntry = tuple[pattern: NamedSimplePattern, optional: bool]
|
||||
AndEntries = OrderedTable[Value, AndEntry]
|
||||
|
||||
proc collect(entries: var AndEntries; loc: Location; def: Definition; optional: bool) {.gcsafe.}
|
||||
|
||||
proc collect(entries: var AndEntries; loc: Location; pat: SimplePattern; optional: bool) =
|
||||
case pat.orKind
|
||||
of SimplePatternKind.Ref:
|
||||
let (loc, def) = deref(loc, pat.ref)
|
||||
collect(entries, loc, def, optional)
|
||||
else:
|
||||
raiseAssert "cannot collect dictionary entries from " & $pat
|
||||
|
||||
proc collect(entries: var AndEntries; loc: Location; comp: CompoundPattern; optional: bool) =
|
||||
case comp.orKind
|
||||
of CompoundPatternKind.dict:
|
||||
for key, nsp in comp.dict.entries.pairs:
|
||||
if entries.hasKey(key):
|
||||
entries[key] = (asAny nsp, optional)
|
||||
else:
|
||||
entries[key] = (nsp, optional)
|
||||
else:
|
||||
raiseAssert "cannot collect dictionary entries from " & $comp
|
||||
|
||||
proc collect(entries: var AndEntries; loc: Location; pat: Pattern; optional: bool) =
|
||||
case pat.orKind
|
||||
of PatternKind.SimplePattern:
|
||||
collect(entries, loc, pat.simplepattern, optional)
|
||||
of PatternKind.CompoundPattern:
|
||||
collect(entries, loc, pat.compoundpattern, optional)
|
||||
|
||||
proc collect(entries: var AndEntries; loc: Location; def: Definition; optional: bool) =
|
||||
case def.orKind
|
||||
of DefinitionKind.or:
|
||||
collect(entries, loc, def.or.field0.pattern0.pattern, true)
|
||||
collect(entries, loc, def.or.field0.pattern1.pattern, true)
|
||||
for np in def.or.field0.patternN:
|
||||
collect(entries, loc, np.pattern, true)
|
||||
of DefinitionKind.and:
|
||||
collect(entries, loc, def.and.field0.pattern0.pattern, optional)
|
||||
collect(entries, loc, def.and.field0.pattern1.pattern, optional)
|
||||
for np in def.and.field0.patternN:
|
||||
collect(entries, loc, np.pattern, optional)
|
||||
of DefinitionKind.Pattern:
|
||||
collect(entries, loc, def.pattern, optional)
|
||||
|
||||
proc toDef(a: DefinitionAnd): Definition =
|
||||
Definition(orKind: DefinitionKind.and, `and`: a)
|
||||
|
||||
proc nimTypeOf(loc: Location; known: var TypeTable; name: string; andDef: DefinitionAnd): TypeSpec =
|
||||
var def = andDef.toDef
|
||||
if isDictionary(loc, def):
|
||||
result.node = nkBracketExpr.newTree(ident"Table", ident"Symbol", ident"Value")
|
||||
var
|
||||
recList = nkRecList.newNode
|
||||
entries: AndEntries
|
||||
collect(entries, loc, def, false)
|
||||
sort(entries) do (x, y: (Value, AndEntry)) -> int:
|
||||
preserves.cmp(x[0], y[0])
|
||||
# TODO: sort the entries
|
||||
var i = 0
|
||||
for key, (nsp, opt) in entries.pairs:
|
||||
recList.addField(loc, known, name, nsp, i, opt)
|
||||
inc(i)
|
||||
result.node = nkObjectTy.newTree(
|
||||
newEmpty(), newEmpty(), recList)
|
||||
else:
|
||||
result.node = ident"Value"
|
||||
|
||||
|
@ -869,7 +992,7 @@ proc collectRefImports(imports: var StringSet; loc: Location; def: Definition) =
|
|||
collectRefImports(imports, loc, na.pattern)
|
||||
of DefinitionKind.and:
|
||||
if isDictionary(loc, def):
|
||||
incl(imports, "std/tables")
|
||||
incl(imports, "std/options")
|
||||
collectRefImports(imports, loc, def.and.field0.pattern0.pattern)
|
||||
collectRefImports(imports, loc, def.and.field0.pattern1.pattern)
|
||||
for np in def.and.field0.patternN:
|
||||
|
@ -906,7 +1029,7 @@ proc renderNimBundle*(bundle: Bundle): Table[string, string] =
|
|||
else:
|
||||
var name = string name
|
||||
name[0] = name[0].toUpperAscii
|
||||
var defIdent = ident(name)
|
||||
var defIdent = parameterize(loc, ident(name), isEmbedded(loc, def))
|
||||
if not isSymbolEnum(loc, def) and not isAny(loc, def):
|
||||
if isEmbedded(loc, def):
|
||||
mergeType(embeddableType, defIdent)
|
||||
|
@ -928,7 +1051,12 @@ proc renderNimBundle*(bundle: Bundle): Table[string, string] =
|
|||
for module in importSet:
|
||||
add(imports, ident(module))
|
||||
if not embeddableType.isNil:
|
||||
let genericParams = newEmpty()
|
||||
let genericParams =
|
||||
if scm.hasEmbeddedType:
|
||||
nkGenericParams.newTree(
|
||||
nkIdentDefs.newTree(
|
||||
embeddedIdent(scm), newEmpty(), newEmpty()))
|
||||
else: newEmpty()
|
||||
procs.add nkProcDef.newTree(
|
||||
"$".toFieldIdent,
|
||||
newEmpty(),
|
||||
|
|
|
@ -152,7 +152,7 @@ proc writeText*(stream: Stream; pr: Value; mode = textPreserves) =
|
|||
writeText(stream, value, mode)
|
||||
write(stream, '}')
|
||||
of pkEmbedded:
|
||||
write(stream, "#!")
|
||||
if not pr.embedded: write(stream, "#!")
|
||||
if pr.embeddedRef.isNil:
|
||||
write(stream, "<null>")
|
||||
else:
|
||||
|
|
|
@ -17,11 +17,13 @@ suite "conversions":
|
|||
a: int
|
||||
b: seq[int]
|
||||
c {.preservesEmbedded.}: Bar
|
||||
d: Option[bool]
|
||||
e: Option[bool]
|
||||
let
|
||||
c = Foobar(a: 1, b: @[2], c: ("ku", ))
|
||||
c = Foobar(a: 1, b: @[2], c: ("ku", ), e: some(true))
|
||||
b = toPreserves(c)
|
||||
a = preservesTo(b, Foobar)
|
||||
check($b == """{a: 1 b: [2] c: #!["ku"]}""")
|
||||
check($b == """{a: 1 b: [2] c: #!["ku"] e: #t}""")
|
||||
check(a.isSome)
|
||||
if a.isSome: check(get(a) == c)
|
||||
check(b.kind == pkDictionary)
|
||||
|
@ -96,3 +98,7 @@ suite "toPreserve":
|
|||
test s: check($p == s)
|
||||
check false.toPreserves, "#f"
|
||||
check [0, 1, 2, 3].toPreserves, "[0 1 2 3]"
|
||||
|
||||
test "toRecord":
|
||||
let r = toRecord(Symbol"foo", "üks", "kaks", "kolm", {4..7})
|
||||
check $r == """<foo "üks" "kaks" "kolm" #{4 5 6 7}>"""
|
||||
|
|
Loading…
Reference in New Issue