Schema: a collection of breaking changes

- Process all modules in a bundle simultanously so that it can be
   determined if imported types are recursive or take a parameter
- Different rules for deciding when an object must be a ref
- Use Preserve[void] if there is no EmbededTypeName defined
- Put embeddeded types directly into other types without a
  Preserve[E] container
This commit is contained in:
Emery Hemingway 2022-12-08 01:53:11 -06:00
parent cc0703c343
commit d5909e4df0
8 changed files with 545 additions and 497 deletions

View File

@ -1,6 +1,6 @@
# Package
version = "20221205"
version = "20221208"
author = "Emery Hemingway"
description = "data model and serialization format"
license = "Unlicense"

View File

@ -1,2 +1,3 @@
include_rules
NIM_PATH += --path:$(TUP_CWD)/..
: foreach *.nim |> !nim_run |>

View File

@ -4,31 +4,30 @@
## This module implements Nim code generation from Preserves schemas.
# This module imports code that it generates! After making any changes here
# the schema module must be regenerated!
# nim c -r ./preserves_schema_nim ../../schema.bin
# nim c --path:.. -r ./preserves_schema_nim ../../../preserves/schema/schema.bin
import std/[hashes, sequtils, strutils, sets, tables]
import std/[hashes, strutils, sets, tables]
import compiler/[ast, idents, renderer, lineinfos]
import ../preserves, ./schema
type
Bundle = schema.Bundle[void]
Modules = schema.Modules[void]
Schema = schema.Schema[void]
Definitions = schema.Definitions[void]
Definition = schema.Definition[void]
Pattern = schema.Pattern[void]
SimplePattern = schema.SimplePattern[void]
CompoundPattern = schema.CompoundPattern[void]
DictionaryEntries = schema.DictionaryEntries[void]
NamedAlternative = schema.NamedAlternative[void]
NamedSimplePattern = schema.NamedSimplePattern[void]
NamedPattern = schema.NamedPattern[void]
Binding = schema.Binding[void]
Value = Preserve[void]
Attribute = enum
embedded
## type contains an embedded value and
## must take an parameter
recursive
## type is recursive and therefore must be a ref
Attributes = set[Attribute]
TypeSpec = object
node: PNode
attrs: Attributes
TypeTable = OrderedTable[schema.ModulePath, PNode]
Location = tuple[bundle: Bundle, schemaPath: ModulePath]
TypeSpec = tuple[node: PNode, embeddable: bool]
TypeTable = OrderedTable[string, PNode]
proc schema(loc: Location): Schema = loc.bundle.modules[loc.schemaPath]
proc add(parent, child: PNode): PNode {.discardable.} =
parent.sons.add child
@ -99,20 +98,18 @@ proc ident(np: NamedSimplePattern; fallback: string): PNode =
of NamedSimplePatternKind.`anonymous`:
ident(fallback)
proc parameterize(node: PNode; embeddable: bool): PNode =
if embeddable and node.kind notin {nkBracketExpr}:
nn(nkBracketExpr, node, ident"E")
else: node
proc parameterize(spec: TypeSpec): PNode =
parameterize(spec.node, spec.embeddable)
proc isPreserve(n: PNode): bool =
n.kind == nkBracketExpr and
n.renderTree == "Preserve[E]"
n.renderTree == "preserves.Value"
proc orEmbed(x: var TypeSpec; y: TypeSpec) =
x.embeddable = x.embeddable or y.embeddable
proc isEmbedded(ts: TypeSpec): bool =
embedded in ts.attrs
proc isRecursive(ts: TypeSpec): bool =
recursive in ts.attrs
proc addAttrs(x: var TypeSpec; y: TypeSpec) =
x.attrs = x.attrs + y.attrs
proc dotExtend(result: var PNode; label: string) =
var id = ident(label)
@ -123,119 +120,155 @@ proc ident(`ref`: Ref): PNode =
for m in`ref`.module: dotExtend(result, string m)
dotExtend(result, `ref`.name.string.capitalizeAscii)
proc deref(scm: Schema; r: Ref): Definition =
assert r.module == @[]
scm.data.definitions[r.name]
proc deref(loc: Location; r: Ref): (Location, Definition) =
result[0] = loc
if r.module == @[]:
result[1] = loc.bundle.modules[loc.schemaPath].data.definitions[r.name]
else:
result[0].schemaPath = r.module
result[1] = loc.bundle.modules[r.module].data.definitions[r.name]
proc preserveIdent(scm: Schema): Pnode =
nn(nkBracketExpr, ident"Preserve", ident("E"))
proc hasEmbeddedType(scm: Schema): bool =
case scm.data.embeddedType.orKind
of EmbeddedtypenameKind.`false`: false
of EmbeddedtypenameKind.`Ref`: true
proc embeddedIdentString(scm: Schema): string =
case scm.data.embeddedType.orKind
of EmbeddedtypenameKind.`false`:
raiseAssert "no embedded type for this module"
of EmbeddedtypenameKind.`Ref`:
doAssert $scm.data.embeddedType.ref.name != ""
$scm.data.embeddedType.ref.name
proc embeddedIdent(scm: Schema): PNode =
case scm.data.embeddedType.orKind
of EmbeddedtypenameKind.`false`: ident"void"
of EmbeddedtypenameKind.`Ref`: preserveIdent(scm)
ident(embeddedIdentString(scm))
proc preserveIdent(scm: Schema): Pnode =
if scm.hasEmbeddedType:
nn(nkBracketExpr, ident"Preserve", embeddedIdent(scm))
else:
nn(nkBracketExpr, ident"Preserve", ident"void")
proc parameterize(scm: Schema; node: PNode; embeddable: bool): PNode =
if embeddable and node.kind notin {nkBracketExpr}:
nn(nkBracketExpr, node, scm.embeddedIdent)
else: node
proc parameterize(scm: Schema; spec: TypeSpec): PNode =
parameterize(scm, spec.node, spec.isEmbedded)
proc hash(r: Ref): Hash = r.toPreserve.hash
type RefSet = HashSet[Ref]
proc isEmbeddable(scm: Schema; pat: Pattern; seen: RefSet): bool {.gcsafe.}
proc isEmbeddable(scm: Schema; def: Definition; seen: RefSet): bool {.gcsafe.}
proc attrs(loc: Location; pat: Pattern; seen: RefSet): Attributes {.gcsafe.}
proc attrs(loc: Location; def: Definition; seen: RefSet): Attributes {.gcsafe.}
proc isEmbeddable(scm: Schema; sp: SimplePattern; seen: RefSet): bool =
proc attrs(loc: Location; n: NamedAlternative|NamedPattern; seen: RefSet): Attributes =
attrs(loc, n.pattern, seen)
proc step(loc: Location; r: Ref): Location = (loc.bundle, r.module)
proc attrs(loc: Location; sp: SimplePattern; seen: RefSet): Attributes =
case sp.orKind
of SimplepatternKind.`atom`, SimplepatternKind.`lit`: false
of SimplepatternKind.`any`: true
of SimplepatternKind.`embedded`: true
of SimplepatternKind.`atom`, SimplepatternKind.`lit`: {}
of SimplepatternKind.`any`, SimplepatternKind.`embedded`:
if loc.schema.hasEmbeddedType: {embedded}
else: {}
of SimplepatternKind.`seqof`:
isEmbeddable(scm, sp.seqof.pattern, seen)
attrs(loc, sp.seqof.pattern, seen)
of SimplepatternKind.`setof`:
isEmbeddable(scm, sp.setof.pattern, seen)
attrs(loc, sp.setof.pattern, seen)
of SimplepatternKind.`dictof`:
isEmbeddable(scm, sp.dictof.key, seen) or
isEmbeddable(scm, sp.dictof.value, seen)
attrs(loc, sp.dictof.key, seen) + attrs(loc, sp.dictof.value, seen)
of SimplepatternKind.`Ref`:
if sp.ref.module != @[]: true
if sp.ref in seen: {recursive}
else:
if sp.ref in seen: false
else:
var seen = seen
seen.incl sp.ref
isEmbeddable(scm, deref(scm, sp.ref), seen)
var
(loc, def) = deref(loc, sp.ref)
seen = seen
incl(seen, sp.ref)
attrs(loc, def, seen)
proc isEmbeddable(scm: Schema; np: NamedSimplePattern; seen: RefSet): bool =
proc attrs(loc: Location; np: NamedSimplePattern; seen: RefSet): Attributes =
case np.orKind
of NamedSimplePatternKind.`named`:
isEmbeddable(scm, np.named.pattern, seen)
attrs(loc, np.named.pattern, seen)
of NamedSimplePatternKind.`anonymous`:
isEmbeddable(scm, np.anonymous, seen)
attrs(loc, np.anonymous, seen)
proc isEmbeddable(scm: Schema; cp: CompoundPattern; seen: RefSet): bool =
proc attrs(loc: Location; cp: CompoundPattern; seen: RefSet): Attributes =
case cp.orKind
of CompoundPatternKind.`rec`:
isEmbeddable(scm, cp.rec.label.pattern, seen) or
isEmbeddable(scm, cp.rec.fields.pattern, seen)
result =
attrs(loc, cp.rec.label.pattern, seen) +
attrs(loc, cp.rec.fields.pattern, seen)
of CompoundPatternKind.`tuple`:
any(cp.tuple.patterns) do (np: NamedPattern) -> bool:
isEmbeddable(scm, np.pattern, seen)
for np in cp.tuple.patterns:
result = result + attrs(loc, np.pattern, seen)
of CompoundPatternKind.`tupleprefix`:
proc pred(np: NamedPattern): bool =
isEmbeddable(scm, np.pattern, seen)
isEmbeddable(scm, cp.tupleprefix.variable, seen) or
any(cp.tupleprefix.fixed, pred)
result = attrs(loc, cp.tupleprefix.variable, seen)
for p in cp.tupleprefix.fixed:
result = result + attrs(loc, p, seen)
of CompoundPatternKind.`dict`:
true # the key type is `Preserve`
discard
proc isEmbeddable(scm: Schema; pat: Pattern; seen: RefSet): bool =
proc attrs(loc: Location; pat: Pattern; seen: RefSet): Attributes =
case pat.orKind
of PatternKind.`SimplePattern`:
isEmbeddable(scm, pat.simplePattern, seen)
attrs(loc, pat.simplePattern, seen)
of PatternKind.`CompoundPattern`:
isEmbeddable(scm, pat.compoundPattern, seen)
attrs(loc, pat.compoundPattern, seen)
proc isEmbeddable(scm: Schema; orDef: DefinitionOr; seen: RefSet): bool =
proc isEmbeddable(na: NamedAlternative): bool =
isEmbeddable(scm, na.pattern, seen)
isEmbeddable(orDef.data.pattern0) or
isEmbeddable(orDef.data.pattern1) or
sequtils.any(orDef.data.patternN, isEmbeddable)
proc attrs(loc: Location; orDef: DefinitionOr; seen: RefSet): Attributes =
result = attrs(loc, orDef.data.pattern0, seen) + attrs(loc, orDef.data.pattern1, seen)
for p in orDef.data.patternN:
result = result + attrs(loc, p, seen)
proc isEmbeddable(scm: Schema; def: Definition; seen: RefSet): bool =
proc attrs(loc: Location; def: Definition; seen: RefSet): Attributes =
case def.orKind
of DefinitionKind.`or`: isEmbeddable(scm, def.or, seen)
of DefinitionKind.`or`: result = attrs(loc, def.or, seen)
of DefinitionKind.`and`:
proc isEmbeddable(np: NamedPattern): bool =
isEmbeddable(scm, np.pattern, seen)
isEmbeddable(def.and.data.pattern0) or
isEmbeddable(def.and.data.pattern1) or
any(def.and.data.patternN, isEmbeddable)
result = attrs(loc, def.and.data.pattern0, seen) + attrs(loc, def.and.data.pattern1, seen)
for p in def.and.data.patternN:
result = result + attrs(loc, p, seen)
of DefinitionKind.`Pattern`:
isEmbeddable(scm, def.pattern, seen)
result = attrs(loc, def.pattern, seen)
proc isEmbeddable(scm: Schema; p: Definition|DefinitionOr|Pattern|CompoundPattern|SimplePattern): bool =
proc attrs(loc: Location; p: Definition|DefinitionOr|Pattern|CompoundPattern|SimplePattern): Attributes =
var seen: RefSet
isEmbeddable(scm, p, seen)
attrs(loc, p, seen)
proc isLiteral(scm: Schema; def: Definition): bool {.gcsafe.}
proc isEmbedded(loc: Location; p: Definition|DefinitionOr|Pattern|CompoundPattern): bool =
embedded in attrs(loc, p)
proc isLiteral(scm: Schema; sp: SimplePattern): bool =
proc isRecursive(loc: Location; p: Definition|DefinitionOr|Pattern|CompoundPattern): bool =
recursive in attrs(loc, p)
proc isLiteral(loc: Location; def: Definition): bool {.gcsafe.}
proc isLiteral(loc: Location; sp: SimplePattern): bool =
case sp.orKind
of SimplepatternKind.`Ref`:
of SimplepatternKind.Ref:
if sp.ref.module.len == 0:
result = isLiteral(scm, deref(scm, sp.ref))
var (loc, def) = deref(loc, sp.ref)
result = isLiteral(loc, def)
of SimplepatternKind.lit:
result = true
of SimplepatternKind.embedded:
result = isLiteral(loc, sp.embedded.interface)
else: discard
proc isLiteral(scm: Schema; pat: Pattern): bool =
proc isLiteral(loc: Location; pat: Pattern): bool =
case pat.orKind
of PatternKind.SimplePattern:
isLiteral(scm, pat.simplePattern)
isLiteral(loc, pat.simplePattern)
of PatternKind.CompoundPattern:
false # TODO it could be a compound of all literals
proc isLiteral(scm: Schema; def: Definition): bool =
proc isLiteral(loc: Location; def: Definition): bool =
if def.orKind == DefinitionKind.Pattern:
result = isLiteral(scm, def.pattern)
result = isLiteral(loc, def.pattern)
proc isRef(sp: SimplePattern): bool =
sp.orKind == SimplePatternKind.`Ref`
@ -247,37 +280,39 @@ proc isRef(pat: Pattern): bool =
proc isSimple(pat: Pattern): bool =
pat.orKind == PatternKind.SimplePattern
proc isSymbolEnum(scm: Schema; orDef: DefinitionOr): bool =
proc isLiteral(na: NamedAlternative): bool = isLiteral(scm, na.pattern)
result = isLiteral(orDef.data.pattern0) and isLiteral(orDef.data.pattern1)
proc isLiteral(loc: Location; na: NamedAlternative): bool = isLiteral(loc, na.pattern)
proc isSymbolEnum(loc: Location; orDef: DefinitionOr): bool =
result = isLiteral(loc, orDef.data.pattern0) and isLiteral(loc, orDef.data.pattern1)
for na in orDef.data.patternN:
if not result: break
result = isLiteral(na)
result = isLiteral(loc, na)
proc isSymbolEnum(scm: Schema; def: Definition): bool =
proc isSymbolEnum(loc: Location; def: Definition): bool =
case def.orKind
of DefinitionKind.Pattern:
if def.pattern.orKind == PatternKind.SimplePattern and
def.pattern.simplePattern.orKind == SimplepatternKind.`Ref` and
def.pattern.simplePattern.ref.module.len == 0:
result = isSymbolEnum(scm, deref(scm, def.pattern.simplePattern.ref))
# TODO: no need to de-ref this
def.pattern.simplePattern.orKind == SimplepatternKind.`Ref`:
var (loc, def) = deref(loc, def.pattern.simplePattern.ref)
result = isSymbolEnum(loc, def)
of DefinitionKind.or:
result = isSymbolEnum(scm, def.or)
result = isSymbolEnum(loc, def.or)
else: discard
proc isSymbolEnum(scm: Schema; sp: SimplePattern): bool =
proc isSymbolEnum(loc: Location; sp: SimplePattern): bool =
# HashSet
if sp.orKind == SimplepatternKind.`Ref` and sp.ref.module.len == 0:
result = isSymbolEnum(scm, deref(scm, sp.ref))
if sp.orKind == SimplepatternKind.`Ref`:
var (loc, def) = deref(loc, sp.ref)
result = isSymbolEnum(loc, def)
else: discard
proc isAny(scm: Schema; def: Definition): bool =
proc isAny(loc: Location; def: Definition): bool =
if def.orKind == DefinitionKind.Pattern:
if def.pattern.orKind == PatternKind.SimplePattern:
case def.pattern.simplePattern.orKind
of SimplePatternKind.Ref:
result = isAny(scm, deref(scm, def.pattern.simplePattern.`ref`))
var (loc, def) = deref(loc, def.pattern.simplePattern.`ref`)
result = isAny(loc, def)
of SimplePatternKind.any:
result = true
else: discard
@ -292,79 +327,97 @@ proc typeIdent(atom: AtomKind): PNode =
of AtomKind.`Bytestring`: nn(nkBracketExpr, ident"seq", ident"byte")
of AtomKind.`Symbol`: ident"Symbol"
proc typeIdent(scm: Schema; sp: SimplePattern): TypeSpec =
proc typeIdent(loc: Location; sp: SimplePattern): TypeSpec =
let scm = loc.schema
case sp.orKind
of SimplepatternKind.`atom`:
result = (typeIdent(sp.atom.atomKind), false)
result = TypeSpec(node: typeIdent(sp.atom.atomKind))
of SimplepatternKind.`seqof`:
result = typeIdent(scm, sp.seqof.pattern)
result = typeIdent(loc, sp.seqof.pattern)
result.node = nn(nkBracketExpr, ident"seq", result.node)
of SimplepatternKind.`setof`:
result = typeIdent(scm, sp.setof.pattern)
result = typeIdent(loc, sp.setof.pattern)
result.node =
if isSymbolEnum(scm, sp.setof.pattern):
if isSymbolEnum(loc, sp.setof.pattern):
nn(nkBracketExpr, ident"set", result.node)
else:
nn(nkBracketExpr, ident"HashSet", result.node)
of SimplepatternKind.`dictof`:
let
key = typeIdent(scm, sp.dictof.key)
val = typeIdent(scm, sp.dictof.value)
key = typeIdent(loc, sp.dictof.key)
val = typeIdent(loc, sp.dictof.value)
result.node = nn(nkBracketExpr, ident"Table", key.node, val.node)
result.embeddable = key.embeddable or val.embeddable
result.attrs = key.attrs + val.attrs
of SimplepatternKind.`Ref`:
result = (ident(sp.ref), isEmbeddable(scm, sp))
result.node = parameterize(result)
else:
result = (preserveIdent(scm), true)
result = TypeSpec(node: ident(sp.ref), attrs: attrs(loc, sp))
result.node = parameterize(scm, result)
of SimplepatternKind.`embedded`:
case scm.data.embeddedType.orKind
of EmbeddedtypenameKind.`false`:
result = typeIdent(loc, sp.embedded.interface)
of EmbeddedtypenameKind.`Ref`:
result = TypeSpec(node: scm.embeddedIdent())
incl(result.attrs, embedded)
of SimplepatternKind.`any`, SimplepatternKind.`lit`:
result = TypeSpec(node: preserveIdent(scm))
proc typeIdent(scm: Schema; pat: Pattern): TypeSpec =
proc typeIdent(loc: Location; pat: Pattern): TypeSpec =
case pat.orKind
of PatternKind.SimplePattern: typeIdent(scm, pat.simplePattern)
of PatternKind.SimplePattern: typeIdent(loc, pat.simplePattern)
else: raiseAssert "no typeIdent for " & $pat
proc toExport(n: sink PNode): PNode =
nkPostFix.newNode.add(ident"*", n)
proc toStrLit(scm: Schema; sp: SimplePattern): PNode =
proc toStrLit(loc: Location; sp: SimplePattern): PNode {.gcsafe.}
proc toStrLit(loc: Location; def: Definition): PNode =
if def.orKind == DefinitionKind.Pattern:
if def.pattern.orKind == PatternKind.SimplePattern:
return toStrLit(loc, def.pattern.simplepattern)
raiseAssert "not a string literal"
proc toStrLit(loc: Location; sp: SimplePattern): PNode =
case sp.orKind
of SimplePatternKind.`lit`:
of SimplePatternKind.lit:
result = PNode(kind: nkStrLit, strVal: $sp.lit.value)
of SimplePatternKind.`Ref`:
let def = deref(scm, sp.ref)
result = toStrLit(scm, def.pattern.simplePattern)
else: assert false
of SimplePatternKind.Ref:
var (loc, def) = deref(loc, sp.ref)
result = toStrLit(loc, def)
of SimplePatternKind.embedded:
result = PNode(kind: nkStrLit, strVal: "#!" & $sp.embedded.interface)
else: raiseAssert $sp
proc toFieldIdent(s: string): PNode =
nn(nkPostFix, ident("*"), nn(nkAccQuoted, ident(s)))
proc toFieldIdent(scm: Schema, label: string; pat: Pattern): PNode =
proc toFieldIdent(loc: Location, label: string; pat: Pattern): PNode =
result = label.toFieldIdent
if isLiteral(scm, pat):
if isLiteral(loc, pat):
result = nn(nkPragmaExpr,
result,
nn(nkPragma,
nn(nkExprColonExpr,
ident"preservesLiteral",
toStrLit(scm, pat.simplePattern))))
toStrLit(loc, pat.simplePattern))))
proc newEmpty(): PNode = newNode(nkEmpty)
proc embeddingParams(embeddable: bool): PNode =
proc embeddingParams(scm: Schema; embeddable: bool): PNode =
if embeddable:
nn(nkGenericParams, nn(nkIdentDefs, ident"E", newEmpty(), newEmpty()))
nn(nkGenericParams, nn(nkIdentDefs, embeddedIdent(scm), newEmpty(), newEmpty()))
else:
newEmpty()
proc identDef(a, b: PNode; embeddable: bool): PNode =
if embeddable and b.kind notin {nkBracketExpr, nkTupleTy}:
# TODO: probably not a sufficient check
nn(nkIdentDefs, a, nn(nkBracketExpr, b, ident"E"), newEmpty())
proc identDef(scm: Schema; a, b: PNode; embeddable: bool): PNode =
if embeddable and b.kind notin {nkBracketExpr, nkTupleTy} and
(b.kind != nkIdent or b.ident.s != scm.embeddedIdentString):
nn(nkIdentDefs, a, nn(nkBracketExpr, b, embeddedIdent(scm)), newEmpty())
else:
nn(nkIdentDefs, a, b, newEmpty())
proc identDef(l: PNode; ts: TypeSpec): PNode =
identDef(l, ts.node, ts.embeddable)
proc identDef(scm: Schema; l: PNode; ts: TypeSpec): PNode =
identDef(scm, l, ts.node, ts.isEmbedded)
proc label(pat: Pattern): string =
raiseAssert "need to derive record label for " & $pat
@ -397,11 +450,13 @@ proc idStr(np: NamedPattern): string =
of NamedPatternKind.`anonymous`:
np.anonymous.idStr
proc typeDef(scm: Schema; name: string; pat: Pattern; ty: PNode): PNode =
proc typeDef(loc: Location; name: string; pat: Pattern; ty: PNode): PNode =
let
embedParams = embeddingParams(isEmbeddable(scm, pat))
scm = loc.schema
embedParams = embeddingParams(scm, isEmbedded(loc, pat))
id = name.ident.toExport
if pat.orKind == PatternKind.`CompoundPattern`:
case pat.orKind
of PatternKind.`CompoundPattern`:
case pat.compoundPattern.orKind
of CompoundPatternKind.`rec`:
nn(nkTypeDef,
@ -425,121 +480,131 @@ proc typeDef(scm: Schema; name: string; pat: Pattern; ty: PNode): PNode =
else:
nn(nkTypeDef, name.ident.toExport, embedParams, ty)
proc typeDef(scm: Schema; name: string; def: Definition; ty: PNode): PNode =
proc typeDef(loc: Location; name: string; def: Definition; ty: PNode): PNode =
case def.orKind
of DefinitionKind.or:
let pragma = nn(nkPragma, ident"preservesOr")
if isSymbolEnum(scm, def):
if isSymbolEnum(loc, def):
pragma.add ident"pure"
nn(nkTypeDef,
nn(nkPragmaExpr,
name.ident.accQuote.toExport,
pragma),
embeddingParams(isEmbeddable(scm, def)),
embeddingParams(loc.schema, isEmbedded(loc, def)),
ty)
of DefinitionKind.and:
raiseAssert "And variants not suported"
of DefinitionKind.Pattern:
typeDef(scm, name, def.pattern, ty)
typeDef(loc, name, def.pattern, ty)
proc nimTypeOf(scm: Schema; known: var TypeTable; nsp: NamedSimplePattern; name = ""): TypeSpec
proc nimTypeOf(scm: Schema; known: var TypeTable; pat: Pattern; name = ""): TypeSpec
proc nimTypeOf(loc: Location; known: var TypeTable; nsp: NamedSimplePattern; name = ""): TypeSpec
proc nimTypeOf(loc: Location; known: var TypeTable; pat: Pattern; name = ""): TypeSpec
proc nimTypeOf(scm: Schema; known: var TypeTable; cp: CompoundPattern; name = ""): TypeSpec
proc nimTypeOf(scm: Schema; known: var TypeTable; sp: SimplePattern; name = ""): TypeSpec =
typeIdent(scm, sp)
proc nimTypeOf(loc: Location; known: var TypeTable; cp: CompoundPattern; name = ""): TypeSpec
proc nimTypeOf(loc: Location; known: var TypeTable; sp: SimplePattern; name = ""): TypeSpec =
typeIdent(loc, sp)
proc addField(recList: PNode; scm: Schema; known: var TypeTable; sp: SimplePattern; label: string): PNode {.discardable.} =
let id = label.toFieldIdent
if isLiteral(scm, sp):
proc addField(recList: PNode; loc: Location; known: var TypeTable; sp: SimplePattern; label: string): PNode {.discardable.} =
let
scm = loc.schema
id = label.toFieldIdent
if isLiteral(loc, sp):
let id = nn(nkPragmaExpr,
id,
nn(nkPragma,
nn(nkExprColonExpr,
ident"preservesLiteral",
toStrLit(scm, sp))))
recList.add identDef(id, (ident"bool", false))
toStrLit(loc, sp))))
recList.add identDef(scm, id, TypeSpec(node: ident"bool"))
elif sp.orKind == SimplePatternKind.embedded and not scm.hasEmbeddedType:
let id = nn(nkPragmaExpr,
id, nn(nkPragma, ident"preservesEmbedded"))
recList.add identDef(scm, id, nimTypeOf(loc, known, sp))
else:
recList.add identDef(id, nimTypeOf(scm, known, sp))
recList.add identDef(scm, id, nimTypeOf(loc, known, sp))
proc addFields(recList: PNode; scm: Schema; known: var TypeTable; cp: CompoundPattern; parentName: string): PNode {.discardable.} =
proc addFields(recList: PNode; loc: Location; known: var TypeTable; cp: CompoundPattern; parentName: string): PNode {.discardable.} =
let scm = loc.schema
template addField(np: NamedPattern) =
let
label = np.label
id = label.toFieldIdent
pattern = np.pattern
if pattern.isRef or pattern.isSimple:
addField(recList, scm, known, pattern.simplePattern, label)
addField(recList, loc, known, pattern.simplePattern, label)
else:
var
typeName = parentName & capitalizeAscii(label)
fieldSpec = nimTypeOf(scm, known, pattern, label)
known[typeName] = typeDef(scm, typeName, pattern, fieldSpec.node)
recList.add identDef(id, ident(typeName), isEmbeddable(scm, pattern))
typePath = loc.schemaPath & @[Symbol typeName]
fieldSpec = nimTypeOf(loc, known, pattern, label)
known[typePath] = typeDef(loc, typeName, pattern, fieldSpec.node)
recList.add identDef(scm, id, ident(typeName), isEmbedded(loc, pattern))
case cp.orKind
of CompoundPatternKind.rec:
# recList.add identDef(ident(label), nimTypeOf(scm, known, cp, ""))
# recList.add identDef(scm, ident(label), nimTypeOf(loc, known, cp, ""))
raiseassert "unexpected record of fields " #& $cp.rec
of CompoundPatternKind.tuple:
for np in cp.tuple.patterns: addField(np)
of CompoundPatternKind.tuplePrefix:
for np in cp.tuplePrefix.fixed: addField(np)
let variableType = nimTypeOf(scm, known, cp.tuplePrefix.variable)
let variableType = nimTypeOf(loc, known, cp.tuplePrefix.variable)
recList.add identDef(
scm,
nn(nkPragmaExpr,
ident(cp.tuplePrefix.variable, parentName).accQuote.toExport,
nn(nkPragma, ident"preservesTupleTail")),
variableType.parameterize,
variableType.embeddable)
parameterize(scm, variableType),
variableType.isEmbedded)
else: raiseAssert "not adding fields for " #& $cp
reclist
proc addFields(recList: PNode; scm: Schema; known: var TypeTable; pat: Pattern; parentName: string): PNode {.discardable.} =
proc addFields(recList: PNode; loc: Location; known: var TypeTable; pat: Pattern; parentName: string): PNode {.discardable.} =
case pat.orKind
of PatternKind.SimplePattern:
addField(recList, scm, known, pat.simplePattern, "data")
addField(recList, loc, known, pat.simplePattern, "data")
of PatternKind.CompoundPattern:
discard addFields(recList, scm, known, pat.compoundPattern, parentName)
discard addFields(recList, loc, known, pat.compoundPattern, parentName)
reclist
proc addFields(recList: PNode; scm: Schema; known: var TypeTable; entries: DictionaryEntries; parentName: string): PNode {.discardable.} =
proc addFields(recList: PNode; loc: Location; known: var TypeTable; entries: DictionaryEntries; parentName: string): PNode {.discardable.} =
for key, val in entries.pairs:
doAssert(key.isSymbol)
let label = string key.symbol
addField(recList, scm, known, val.pattern, label)
addField(recList, loc, known, val.pattern, label)
recList
proc nimTypeOf(scm: Schema; known: var TypeTable; nsp: NamedSimplePattern; name: string): TypeSpec =
proc nimTypeOf(loc: Location; known: var TypeTable; nsp: NamedSimplePattern; name: string): TypeSpec =
case nsp.orKind
of NamedsimplepatternKind.named:
nimTypeOf(scm, known, nsp.named.pattern, string nsp.named.name)
nimTypeOf(loc, known, nsp.named.pattern, string nsp.named.name)
of NamedsimplepatternKind.anonymous:
nimTypeOf(scm, known, nsp.anonymous, name)
nimTypeOf(loc, known, nsp.anonymous, name)
proc nimTypeOf(scm: Schema; known: var TypeTable; cp: CompoundPattern; name: string): TypeSpec =
proc nimTypeOf(loc: Location; known: var TypeTable; cp: CompoundPattern; name: string): TypeSpec =
case cp.orKind
of CompoundPatternKind.`rec`:
result.node = nn(nkObjectTy,
newEmpty(), newEmpty(),
nn(nkRecList).addFields(scm, known, cp.rec.fields.pattern, name))
nn(nkRecList).addFields(loc, known, cp.rec.fields.pattern, name))
of CompoundPatternKind.`tuple`, CompoundPatternKind.`tupleprefix`:
result.node = nn(nkObjectTy,
newEmpty(), newEmpty(),
nn(nkRecList).addFields(scm, known, cp, name))
nn(nkRecList).addFields(loc, known, cp, name))
of CompoundPatternKind.`dict`:
result.node = nn(nkObjectTy, newEmpty(), newEmpty(),
nn(nkRecList).addFields(scm, known, cp.dict.entries, name))
if result.node.kind == nkObjectTy and isEmbeddable(scm, cp):
nn(nkRecList).addFields(loc, known, cp.dict.entries, name))
if result.node.kind == nkObjectTy and isRecursive(loc, cp):
result.node = nn(nkRefTy, result.node)
proc nimTypeOf(scm: Schema; known: var TypeTable; pat: Pattern; name: string): TypeSpec =
proc nimTypeOf(loc: Location; known: var TypeTable; pat: Pattern; name: string): TypeSpec =
case pat.orKind
of PatternKind.SimplePattern:
nimTypeOf(scm, known, pat.simplePattern, name)
nimTypeOf(loc, known, pat.simplePattern, name)
of PatternKind.CompoundPattern:
nimTypeOf(scm, known, pat.compoundPattern, name)
nimTypeOf(loc, known, pat.compoundPattern, name)
proc nimTypeOf(scm: Schema; known: var TypeTable; orDef: DefinitionOr; name: string): TypeSpec =
proc nimTypeOf(loc: Location; known: var TypeTable; orDef: DefinitionOr; name: string): TypeSpec =
let scm = loc.schema
proc toEnumTy(): PNode =
let ty = nkEnumTy.newNode.add newEmpty()
proc add (na: NamedAlternative) =
@ -549,14 +614,15 @@ proc nimTypeOf(scm: Schema; known: var TypeTable; orDef: DefinitionOr; name: str
for na in orDef.data.patternN:
add(na)
ty
if isSymbolEnum(scm, orDef):
if isSymbolEnum(loc, orDef):
result.node = toEnumTy()
else:
let
enumName = name & "Kind"
enumPath = loc.schemaPath & @[Symbol enumName]
enumIdent = ident(enumName)
if enumName notin known:
known[enumName] = nn(nkTypeDef,
if enumPath notin known:
known[enumPath] = nn(nkTypeDef,
nn(nkPragmaExpr,
enumName.ident.toExport,
nn(nkPragma, ident"pure")),
@ -570,23 +636,25 @@ proc nimTypeOf(scm: Schema; known: var TypeTable; orDef: DefinitionOr; name: str
template addCase(na: NamedAlternative) =
let branchRecList = nn(nkRecList)
var memberType: TypeSpec
if isLiteral(scm, na.pattern):
if isLiteral(loc, na.pattern):
memberType.node = ident"bool"
elif na.pattern.isRef:
memberType = typeIdent(scm, na.pattern)
memberType = typeIdent(loc, na.pattern)
else:
let memberTypeName = name & na.variantLabel.capitalizeAscii
let
memberTypeName = name & na.variantLabel.capitalizeAscii
memberPath = loc.schemaPath & @[Symbol memberTypeName]
memberType.node = ident memberTypeName
let ty = nimTypeOf(scm, known, na.pattern, memberTypeName)
orEmbed memberType, ty
if memberTypeName notin known and not isLiteral(scm, na.pattern):
known[memberTypeName] =
typeDef(scm, memberTypeName, na.pattern, ty.node)
orEmbed result, memberType
let ty = nimTypeOf(loc, known, na.pattern, memberTypeName)
addAttrs(memberType, ty)
if memberPath notin known and not isLiteral(loc, na.pattern):
known[memberPath] =
typeDef(loc, memberTypeName, na.pattern, ty.node)
addAttrs(result, memberType)
memberType.node = parameterize(
memberType.node, isEmbeddable(scm, na.pattern))
scm, memberType.node, isEmbedded(loc, na.pattern))
branchRecList.add nn(nkIdentDefs,
toFieldIdent(scm, na.variantLabel.normalize, na.pattern),
toFieldIdent(loc, na.variantLabel.normalize, na.pattern),
memberType.node, newEmpty())
recCase.add nn(nkOfBranch,
nn(nkDotExpr,
@ -599,40 +667,41 @@ proc nimTypeOf(scm: Schema; known: var TypeTable; orDef: DefinitionOr; name: str
newEmpty(),
newEmpty(),
nn(nkRecList, recCase))
if result.node.kind == nkObjectTy and isEmbeddable(scm, orDef):
# result.attrs = attrs(loc, orDef)
if result.node.kind == nkObjectTy and (recursive in attrs(loc, orDef)):
result.node = nn(nkRefTy, result.node)
proc nimTypeOf(scm: Schema; known: var TypeTable; def: Definition; name: string): TypeSpec =
proc nimTypeOf(loc: Location; known: var TypeTable; def: Definition; name: string): TypeSpec =
case def.orKind
of DefinitionKind.or:
nimTypeOf(scm, known, def.or, name)
of DefinitionKind.and: raiseAssert "And definitions are unsupported"
nimTypeOf(loc, known, def.or, name)
of DefinitionKind.and: raiseAssert """"And" definitions are unsupported"""
of DefinitionKind.Pattern:
nimTypeOf(scm, known, def.pattern, name)
nimTypeOf(loc, known, def.pattern, name)
proc generateConstProcs(result: var seq[PNode]; scm: Schema, name: string; def: Definition) =
discard
proc literalToPreserveCall(scm: Schema; pr: Preserve): PNode =
proc literalToPreserveCall(scm: Schema; pr: Value): PNode =
var prConstr = nn(nkObjConstr, preserveIdent(scm))
proc constr(kind, field: string; lit: PNode) =
prConstr.add nn(nkExprColonExpr, ident"kind", ident(kind))
prConstr.add nn(nkExprColonExpr, ident(field), lit)
case pr.orKind
case pr.kind
of pkBoolean:
constr($pr.orKind, "bool", if pr.bool: ident"true" else: ident"false")
constr($pr.kind, "bool", if pr.bool: ident"true" else: ident"false")
of pkFloat:
constr($pr.orKind, "float", newFloatNode(nkFloat32Lit, pr.float))
constr($pr.kind, "float", newFloatNode(nkFloat32Lit, pr.float))
of pkDouble:
constr($pr.orKind, "double", newFloatNode(nkFloat64Lit, pr.double))
constr($pr.kind, "double", newFloatNode(nkFloat64Lit, pr.double))
of pkSignedInteger:
constr($pr.orKind, "BiggestInt", newIntNode(nkInt64Lit, pr.int))
constr($pr.kind, "BiggestInt", newIntNode(nkInt64Lit, pr.int))
of pkString:
constr($pr.orKind, "string", newStrNode(nkTripleStrLit, pr.string))
constr($pr.kind, "string", newStrNode(nkTripleStrLit, pr.string))
of pkByteString:
return nn(nkCall, ident"parsePreserves", newStrNode(nkTripleStrLit, $pr))
of pkSymbol:
constr($pr.orKind, "symbol", newStrNode(nkStrLit, string pr.symbol))
constr($pr.kind, "symbol", newStrNode(nkStrLit, string pr.symbol))
else:
raise newException(ValueError, "refusing to convert to a literal: " & $pr)
prConstr
@ -694,40 +763,52 @@ proc collectRefImports(imports: PNode; scm: Schema) =
for _, def in scm.data.definitions:
collectRefImports(imports, def)
proc renderNimModule*(scm: Schema): string =
## Construct and render a Nim module from a `Schema`.
proc mergeType(x: var PNode; y: PNode) =
proc mergeType(x: var PNode; y: PNode) =
if x.isNil: x = y
else: x = nn(nkInfix, ident"|", x, y)
proc hasPrefix(a, b: ModulePath): bool =
for i, e in b:
if i > a.high or a[i] != e: return false
true
proc renderNimBundle*(bundle: Bundle): Table[string, string] =
## Render Nim modules from a `Bundle`.
result = initTable[string, string](bundle.modules.len)
var typeDefs: TypeTable
for scmPath, scm in bundle.modules:
var
typeDefs: TypeTable
typeSection = newNode nkTypeSection
procs: seq[PNode]
unembeddableType, embeddableType: PNode
for name, def in scm.data.definitions.pairs:
if isLiteral(scm, def):
let loc = (bundle, scmPath)
if isLiteral(loc, def):
generateConstProcs(procs, scm, string name, def)
else:
var name = string name
name[0] = name[0].toUpperAscii
var defIdent = parameterize(ident(name), isEmbeddable(scm, def))
if not isSymbolEnum(scm, def) and not isAny(scm, def):
if isEmbeddable(scm, def):
var defIdent = parameterize(scm, ident(name), isEmbedded(loc, def))
if not isSymbolEnum(loc, def) and not isAny(loc, def):
if isEmbedded(loc, def):
mergeType(embeddableType, defIdent)
else:
mergeType(unembeddableType, defIdent)
let typeSpec = nimTypeOf(scm, typeDefs, def, name)
typeDefs[name] = typeDef(scm, name, def, typeSpec.node)
let typeSpec = nimTypeOf(loc, typeDefs, def, name)
typeDefs[scmPath & @[Symbol name]] = typeDef(loc, name, def, typeSpec.node)
generateProcs(procs, scm, name, def)
for td in typeDefs.values:
typeSection.add td
for typePath, typeDef in typeDefs.pairs:
if typepath.hasPrefix(scmPath):
add(typeSection, typeDef)
var imports = nkImportStmt.newNode.add(
ident"std/typetraits",
ident"preserves")
collectRefImports(imports, scm)
let genericParams =
nn(nkGenericParams, nn(nkIdentDefs, ident"E", newEmpty(), newEmpty()))
if not embeddableType.isNil:
let genericParams =
nn(nkGenericParams, nn(nkIdentDefs, embeddedIdent(scm), newEmpty(), newEmpty()))
procs.add nn(nkProcDef,
"$".toFieldIdent,
newEmpty(),
@ -742,7 +823,7 @@ proc renderNimModule*(scm: Schema): string =
newEmpty(),
nn(nkStmtList,
nn(nkCall, ident"$",
nn(nkCall, ident"toPreserve", ident"x", ident"E"))))
nn(nkCall, ident"toPreserve", ident"x", embeddedIdent(scm)))))
procs.add nn(nkProcDef,
"encode".ident.toExport,
newEmpty(),
@ -757,7 +838,7 @@ proc renderNimModule*(scm: Schema): string =
newEmpty(),
nn(nkStmtList,
nn(nkCall, ident"encode",
nn(nkCall, ident"toPreserve", ident"x", ident"E"))))
nn(nkCall, ident"toPreserve", ident"x", embeddedIdent(scm)))))
if not unembeddableType.isNil:
procs.add nn(nkProcDef,
"$".toFieldIdent,
@ -793,13 +874,19 @@ proc renderNimModule*(scm: Schema): string =
imports,
typeSection
).add(procs)
renderTree(module, {renderNone, renderIr})
var filePath = ""
for p in scmPath:
if filePath != "": add(filePath, '/')
add(filePath, string p)
add(filePath, ".nim")
result[filePath] = renderTree(module, {renderNone, renderIr})
when isMainModule:
import ./schemaparse
proc writeModule(scm: Schema; path: string) =
let txt = renderNimModule(scm)
proc writeModules(bundle: Bundle) =
let modules = renderNimBundle(bundle)
for path, txt in modules.pairs:
writeFile(path, txt)
stdout.writeLine(path)
@ -816,28 +903,32 @@ when isMainModule:
of cmdArgument:
inputs.add absolutePath(key)
of cmdEnd: discard
for filepath in inputs:
var useful: bool
let raw = readFile filepath
for inputPath in inputs:
var bundle: Bundle
if dirExists inputPath:
for filePath in walkDirRec(inputPath, relative = true):
var (dirPath, fileName, fileExt) = splitFile(filePath)
if fileExt == ".prs":
var
scm = parsePreservesSchema(readFile(inputPath / filePath), inputPath / dirPath)
path: ModulePath
for e in split(dirPath, '/'):
add(path, Symbol e)
add(path, Symbol fileName)
bundle.modules[path] = scm
elif fileExists inputPath:
var (dirPath, fileName, fileExt) = splitFile inputPath
let raw = readFile inputPath
if raw[0] == 0xb4.char:
let pr = decodePreserves raw
preserveTo(pr, Schema).map do (scm: Schema):
useful = true
let
(_, name, _) = splitFile(filepath)
outputPath = name & ".nim"
writeModule(scm, outputPath)
preserveTo(pr, Bundle).map do (bundle: Bundle):
useful = true
for modPath, scm in bundle.modules:
let path = joinPath(cast[seq[string]](modPath)) & ".nim"
writeModule(scm, path)
var pr = decodePreserves raw
if not fromPreserve(bundle, pr):
var schema: Schema
if fromPreserve(schema, pr):
bundle.modules[@[Symbol fileName]] = schema
else:
let
(dir, name, _) = splitFile filepath
scm = parsePreservesSchema(raw, dir)
modpath = name & ".nim"
writeModule(scm, modpath)
useful = true
if not useful:
quit "Failed to recognized " & filepath
var scm = parsePreservesSchema(readFile(inputPath), dirPath)
bundle.modules[@[Symbol fileName]] = scm
if bundle.modules.len == 0:
quit "Failed to recognize " & inputPath
else:
writeModules(bundle)

View File

@ -37,7 +37,7 @@ when isMainModule:
of "preserves_to_xml":
let pr = stdin.readAll.decodePreserves
var xn: XmlNode
if fromPreserveHook(xn, pr):
if fromPreserve(xn, pr):
stdout.writeLine(xn)
else:
quit("Preserves not convertable to XML")

View File

@ -1,5 +1,5 @@
import ../preserves, std/typetraits, std/tables
import ../preserves, std/tables
type
Ref* {.preservesRecord: "ref".} = object
@ -7,47 +7,48 @@ type
`name`*: Symbol
ModulePath* = seq[Symbol]
Bundle*[E] {.preservesRecord: "bundle".} = ref object
`modules`*: Modules[E]
Bundle* {.preservesRecord: "bundle".} = object
`modules`*: Modules
CompoundPatternKind* {.pure.} = enum
`rec`, `tuple`, `tuplePrefix`, `dict`
CompoundPatternRec*[E] {.preservesRecord: "rec".} = ref object
`label`*: NamedPattern[E]
`fields`*: NamedPattern[E]
CompoundPatternRec* {.preservesRecord: "rec".} = ref object
`label`*: NamedPattern
`fields`*: NamedPattern
CompoundPatternTuple*[E] {.preservesRecord: "tuple".} = ref object
`patterns`*: seq[NamedPattern[E]]
CompoundPatternTuple* {.preservesRecord: "tuple".} = ref object
`patterns`*: seq[NamedPattern]
CompoundPatternTuplePrefix*[E] {.preservesRecord: "tuplePrefix".} = ref object
`fixed`*: seq[NamedPattern[E]]
`variable`*: NamedSimplePattern[E]
CompoundPatternTuplePrefix* {.preservesRecord: "tuplePrefix".} = ref object
`fixed`*: seq[NamedPattern]
`variable`*: NamedSimplePattern
CompoundPatternDict*[E] {.preservesRecord: "dict".} = ref object
`entries`*: DictionaryEntries[E]
CompoundPatternDict* {.preservesRecord: "dict".} = ref object
`entries`*: DictionaryEntries
`CompoundPattern`*[E] {.preservesOr.} = ref object
`CompoundPattern`* {.preservesOr.} = ref object
case orKind*: CompoundPatternKind
of CompoundPatternKind.`rec`:
`rec`*: CompoundPatternRec[E]
`rec`*: CompoundPatternRec
of CompoundPatternKind.`tuple`:
`tuple`*: CompoundPatternTuple[E]
`tuple`*: CompoundPatternTuple
of CompoundPatternKind.`tuplePrefix`:
`tupleprefix`*: CompoundPatternTuplePrefix[E]
`tupleprefix`*: CompoundPatternTuplePrefix
of CompoundPatternKind.`dict`:
`dict`*: CompoundPatternDict[E]
`dict`*: CompoundPatternDict
Modules*[E] = Table[ModulePath, Schema[E]]
Modules* = Table[ModulePath, Schema]
EmbeddedTypeNameKind* {.pure.} = enum
`false`, `Ref`
`EmbeddedTypeName`* {.preservesOr.} = object
case orKind*: EmbeddedTypeNameKind
of EmbeddedTypeNameKind.`false`:
`false`* {.preservesLiteral: "#f".}: bool
of EmbeddedTypeNameKind.`Ref`:
`ref`*: Ref
@ -55,17 +56,17 @@ type
`AtomKind`* {.preservesOr, pure.} = enum
`Boolean`, `Float`, `Double`, `SignedInteger`, `String`, `ByteString`,
`Symbol`
Definitions*[E] = Table[Symbol, Definition[E]]
DictionaryEntries*[E] = Table[Preserve[E], NamedSimplePattern[E]]
Definitions* = Table[Symbol, Definition]
DictionaryEntries* = Table[Preserve[void], NamedSimplePattern]
NamedPatternKind* {.pure.} = enum
`named`, `anonymous`
`NamedPattern`*[E] {.preservesOr.} = ref object
`NamedPattern`* {.preservesOr.} = ref object
case orKind*: NamedPatternKind
of NamedPatternKind.`named`:
`named`*: Binding[E]
`named`*: Binding
of NamedPatternKind.`anonymous`:
`anonymous`*: Pattern[E]
`anonymous`*: Pattern
SimplePatternKind* {.pure.} = enum
@ -73,23 +74,23 @@ type
SimplePatternAtom* {.preservesRecord: "atom".} = object
`atomKind`*: AtomKind
SimplePatternEmbedded*[E] {.preservesRecord: "embedded".} = ref object
`interface`*: SimplePattern[E]
SimplePatternEmbedded* {.preservesRecord: "embedded".} = ref object
`interface`*: SimplePattern
SimplePatternLit*[E] {.preservesRecord: "lit".} = ref object
`value`*: Preserve[E]
SimplePatternLit* {.preservesRecord: "lit".} = object
`value`*: Preserve[void]
SimplePatternSeqof*[E] {.preservesRecord: "seqof".} = ref object
`pattern`*: SimplePattern[E]
SimplePatternSeqof* {.preservesRecord: "seqof".} = ref object
`pattern`*: SimplePattern
SimplePatternSetof*[E] {.preservesRecord: "setof".} = ref object
`pattern`*: SimplePattern[E]
SimplePatternSetof* {.preservesRecord: "setof".} = ref object
`pattern`*: SimplePattern
SimplePatternDictof*[E] {.preservesRecord: "dictof".} = ref object
`key`*: SimplePattern[E]
`value`*: SimplePattern[E]
SimplePatternDictof* {.preservesRecord: "dictof".} = ref object
`key`*: SimplePattern
`value`*: SimplePattern
`SimplePattern`*[E] {.preservesOr.} = ref object
`SimplePattern`* {.preservesOr.} = ref object
case orKind*: SimplePatternKind
of SimplePatternKind.`any`:
`any`* {.preservesLiteral: "any".}: bool
@ -98,19 +99,19 @@ type
`atom`*: SimplePatternAtom
of SimplePatternKind.`embedded`:
`embedded`*: SimplePatternEmbedded[E]
`embedded`*: SimplePatternEmbedded
of SimplePatternKind.`lit`:
`lit`*: SimplePatternLit[E]
`lit`*: SimplePatternLit
of SimplePatternKind.`seqof`:
`seqof`*: SimplePatternSeqof[E]
`seqof`*: SimplePatternSeqof
of SimplePatternKind.`setof`:
`setof`*: SimplePatternSetof[E]
`setof`*: SimplePatternSetof
of SimplePatternKind.`dictof`:
`dictof`*: SimplePatternDictof[E]
`dictof`*: SimplePatternDictof
of SimplePatternKind.`Ref`:
`ref`*: Ref
@ -118,99 +119,96 @@ type
NamedSimplePatternKind* {.pure.} = enum
`named`, `anonymous`
`NamedSimplePattern`*[E] {.preservesOr.} = ref object
`NamedSimplePattern`* {.preservesOr.} = ref object
case orKind*: NamedSimplePatternKind
of NamedSimplePatternKind.`named`:
`named`*: Binding[E]
`named`*: Binding
of NamedSimplePatternKind.`anonymous`:
`anonymous`*: SimplePattern[E]
`anonymous`*: SimplePattern
DefinitionKind* {.pure.} = enum
`or`, `and`, `Pattern`
DefinitionOrData*[E] {.preservesTuple.} = ref object
`pattern0`*: NamedAlternative[E]
`pattern1`*: NamedAlternative[E]
`patternN`* {.preservesTupleTail.}: seq[NamedAlternative[E]]
DefinitionOrData* {.preservesTuple.} = ref object
`pattern0`*: NamedAlternative
`pattern1`*: NamedAlternative
`patternN`* {.preservesTupleTail.}: seq[NamedAlternative]
DefinitionOr*[E] {.preservesRecord: "or".} = ref object
`data`*: DefinitionOrData[E]
DefinitionOr* {.preservesRecord: "or".} = ref object
`data`*: DefinitionOrData
DefinitionAndData*[E] {.preservesTuple.} = ref object
`pattern0`*: NamedPattern[E]
`pattern1`*: NamedPattern[E]
`patternN`* {.preservesTupleTail.}: seq[NamedPattern[E]]
DefinitionAndData* {.preservesTuple.} = ref object
`pattern0`*: NamedPattern
`pattern1`*: NamedPattern
`patternN`* {.preservesTupleTail.}: seq[NamedPattern]
DefinitionAnd*[E] {.preservesRecord: "and".} = ref object
`data`*: DefinitionAndData[E]
DefinitionAnd* {.preservesRecord: "and".} = ref object
`data`*: DefinitionAndData
`Definition`*[E] {.preservesOr.} = ref object
`Definition`* {.preservesOr.} = ref object
case orKind*: DefinitionKind
of DefinitionKind.`or`:
`or`*: DefinitionOr[E]
`or`*: DefinitionOr
of DefinitionKind.`and`:
`and`*: DefinitionAnd[E]
# doesn't build with C++
`and`*: DefinitionAnd
of DefinitionKind.`Pattern`:
`pattern`*: Pattern[E]
`pattern`*: Pattern
NamedAlternative*[E] {.preservesTuple.} = ref object
NamedAlternative* {.preservesTuple.} = ref object
`variantLabel`*: string
`pattern`*: Pattern[E]
`pattern`*: Pattern
SchemaData*[E] {.preservesDictionary.} = ref object
SchemaData* {.preservesDictionary.} = object
`version`* {.preservesLiteral: "1".}: bool
`embeddedType`*: EmbeddedTypeName
`definitions`*: Definitions[E]
`definitions`*: Definitions
Schema*[E] {.preservesRecord: "schema".} = ref object
`data`*: SchemaData[E]
Schema* {.preservesRecord: "schema".} = object
`data`*: SchemaData
PatternKind* {.pure.} = enum
`SimplePattern`, `CompoundPattern`
`Pattern`*[E] {.preservesOr.} = ref object
`Pattern`* {.preservesOr.} = ref object
case orKind*: PatternKind
of PatternKind.`SimplePattern`:
`simplepattern`*: SimplePattern[E]
`simplepattern`*: SimplePattern
of PatternKind.`CompoundPattern`:
`compoundpattern`*: CompoundPattern[E]
`compoundpattern`*: CompoundPattern
Binding*[E] {.preservesRecord: "named".} = ref object
Binding* {.preservesRecord: "named".} = ref object
`name`*: Symbol
`pattern`*: SimplePattern[E]
`pattern`*: SimplePattern
proc `$`*[E](x: Bundle[E] | CompoundPattern[E] | Modules[E] | Definitions[E] |
DictionaryEntries[E] |
NamedPattern[E] |
SimplePattern[E] |
NamedSimplePattern[E] |
Definition[E] |
NamedAlternative[E] |
Schema[E] |
Pattern[E] |
Binding[E]): string =
`$`(toPreserve(x, E))
proc encode*[E](x: Bundle[E] | CompoundPattern[E] | Modules[E] | Definitions[E] |
DictionaryEntries[E] |
NamedPattern[E] |
SimplePattern[E] |
NamedSimplePattern[E] |
Definition[E] |
NamedAlternative[E] |
Schema[E] |
Pattern[E] |
Binding[E]): seq[byte] =
encode(toPreserve(x, E))
proc `$`*(x: Ref | ModulePath | EmbeddedTypeName): string =
proc `$`*(x: Ref | ModulePath | Bundle | CompoundPattern | Modules |
EmbeddedTypeName |
Definitions |
DictionaryEntries |
NamedPattern |
SimplePattern |
NamedSimplePattern |
Definition |
NamedAlternative |
Schema |
Pattern |
Binding): string =
`$`(toPreserve(x))
proc encode*(x: Ref | ModulePath | EmbeddedTypeName): seq[byte] =
proc encode*(x: Ref | ModulePath | Bundle | CompoundPattern | Modules |
EmbeddedTypeName |
Definitions |
DictionaryEntries |
NamedPattern |
SimplePattern |
NamedSimplePattern |
Definition |
NamedAlternative |
Schema |
Pattern |
Binding): seq[byte] =
encode(toPreserve(x))

View File

@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: 2022 ☭ Emery Hemingway
# SPDX-License-Identifier: Unlicense
import std/[hashes, options, os, parseopt, streams, strutils, sets, tables]
import std/[hashes, options, os, parseopt, streams, strutils, tables]
import ../preserves, ./schema, ./schemaparse
@ -23,7 +23,7 @@ when isMainModule:
write(outStream, schema.toPreserve)
of "bundle":
var bundle: Bundle[void]
var bundle: Bundle
if not dirExists inputPath:
quit "not a directory of schemas: " & inputPath
else:

View File

@ -10,9 +10,6 @@ import ../preserves, ./schema, ./pegs
type
Value = Preserve[void]
Definition = schema.Definition[void]
SchemaData = schema.SchemaData[void]
Schema = schema.Schema[void]
Stack = seq[tuple[node: Value, pos: int]]
ParseState = object
schema: SchemaData
@ -60,7 +57,7 @@ template pushStack(n: Value) =
assert(p.stack.len > 0, capture[0].s)
proc toSymbolLit(s: string): Value =
initRecord(toSymbol"lit", toSymbol s)
initRecord[void](toSymbol"lit", toSymbol s)
proc match(text: string; p: var ParseState) {.gcsafe.}
@ -205,10 +202,10 @@ const parser = peg("Schema", p: ParseState):
pushStack n
Ref <- >(Alpha * *Alnum) * *('.' * >(*Alnum)):
var path: seq[string]
for i in 1..<capture.len: path.add capture[i].s
let name = pop path
var n = initRecord(toSymbol"ref", toPreserve path, toSymbol name)
var path = initSequence[void]()
for i in 1..<capture.len: path.sequence.add(toSymbol capture[i].s)
var name = pop(path.sequence)
var n = initRecord(toSymbol"ref", path, name)
pushStack n
CompoundPattern <-
@ -291,6 +288,7 @@ proc parsePreservesSchema*(text: string; directory = getCurrentDir()): Schema =
##
## Schemas in binary encoding should instead be parsed as Preserves
## and converted to `Schema` with `fromPreserve` or `preserveTo`.
assert directory != ""
var p = ParseState(schema: SchemaData(), directory: directory)
match(text, p)
Schema(data: p.schema)
@ -300,6 +298,6 @@ when isMainModule:
let txt = readAll stdin
if txt != "":
let
scm = parsePreservesSchema(readAll stdin)
scm = parsePreservesSchema(txt)
pr = toPreserve scm
stdout.newFileStream.write pr
stdout.newFileStream.writeText pr

View File

@ -1,40 +0,0 @@
# SPDX-FileCopyrightText: 2021 ☭ Emery Hemingway
# SPDX-License-Identifier: Unlicense
import std/[tables, options, os, unittest]
import preserves, preserves/schema, preserves/schemaparse
if dirExists "tests": setCurrentDir "tests"
# nimble runs tests in the directory above
suite "schema":
const
binPath = "../schema.bin"
textPath = "../schema.prs"
test "convertability":
if not fileExists(binPath): skip()
else:
var
b = decodePreserves readFile(binPath)
scm = preserveTo(b, Schema[void])
check scm.isSome
if scm.isSome:
var a = toPreserve(get scm)
check(a == b)
test "parser":
if not fileExists(binPath): skip()
else:
var
b = decodePreserves readFile(binPath)
scm = preserveTo(b, Schema[void])
check scm.isSome
if scm.isSome:
var a = toPreserve parsePreservesSchema(readFile textPath, textPath)
let aDefs = a[0][toSymbol "definitions"]
let bDefs = b[0][toSymbol "definitions"]
for (key, val) in aDefs.pairs:
check(bDefs[key] == val)
check(a == b)