preserves_schema_nim: better recursive type detection
This commit is contained in:
parent
c01e587e5b
commit
97ab7ce070
|
@ -20,10 +20,7 @@ import ../preserves, ./schema
|
|||
type
|
||||
Attribute = enum
|
||||
embedded
|
||||
## type contains an embedded value and
|
||||
## must take an parameter
|
||||
recursive
|
||||
## type is recursive and therefore must be a ref
|
||||
## type contains an embedded value
|
||||
Attributes = set[Attribute]
|
||||
TypeSpec = object
|
||||
node: PNode
|
||||
|
@ -119,12 +116,15 @@ proc ident(`ref`: Ref): PNode =
|
|||
dotExtend(result, `ref`.name.string.capitalizeAscii)
|
||||
|
||||
proc deref(loc: Location; r: Ref): (Location, Definition) =
|
||||
result[0] = loc
|
||||
if r.module == @[]:
|
||||
result[1] = loc.bundle.modules[loc.schemaPath].field0.definitions[r.name]
|
||||
else:
|
||||
result[0].schemaPath = r.module
|
||||
result[1] = loc.bundle.modules[r.module].field0.definitions[r.name]
|
||||
try:
|
||||
result[0] = loc
|
||||
if r.module == @[]:
|
||||
result[1] = loc.bundle.modules[loc.schemaPath].field0.definitions[r.name]
|
||||
else:
|
||||
result[0].schemaPath = r.module
|
||||
result[1] = loc.bundle.modules[r.module].field0.definitions[r.name]
|
||||
except KeyError:
|
||||
raise newException(KeyError, "reference not found in bundle: " & $r)
|
||||
|
||||
proc hasEmbeddedType(scm: Schema): bool =
|
||||
case scm.field0.embeddedType.orKind
|
||||
|
@ -152,8 +152,7 @@ proc attrs(loc: Location; sp: SimplePattern; seen: RefSet): Attributes =
|
|||
of SimplepatternKind.dictof:
|
||||
attrs(loc, sp.dictof.key, seen) + attrs(loc, sp.dictof.value, seen)
|
||||
of SimplepatternKind.Ref:
|
||||
if sp.ref in seen: {recursive}
|
||||
elif sp.ref.isAtomic: {}
|
||||
if (sp.ref in seen) or sp.ref.isAtomic: {}
|
||||
else:
|
||||
var
|
||||
(loc, def) = deref(loc, sp.ref)
|
||||
|
@ -211,8 +210,85 @@ proc attrs(loc: Location; p: Definition|DefinitionOr|DefinitionAnd|Pattern|Compo
|
|||
proc isEmbedded(loc: Location; p: Definition|DefinitionOr|DefinitionAnd|Pattern|CompoundPattern|SimplePattern): bool =
|
||||
embedded in attrs(loc, p)
|
||||
|
||||
proc isRecursive(loc: Location; p: Definition|DefinitionOr|DefinitionAnd|Pattern|CompoundPattern): bool =
|
||||
recursive in attrs(loc, p)
|
||||
proc isRecursive(loc: Location; name: string; pat: Pattern; seen: RefSet): bool {.gcsafe.}
|
||||
|
||||
proc isRecursive(loc: Location; name: string; def: Definition; seen: RefSet): bool {.gcsafe.}
|
||||
|
||||
proc isRecursive(loc: Location; name: string; n: NamedAlternative|NamedPattern; seen: RefSet): bool =
|
||||
isRecursive(loc, name, n.pattern, seen)
|
||||
|
||||
proc isRecursive(loc: Location; name: string; sp: SimplePattern; seen: RefSet): bool =
|
||||
case sp.orKind
|
||||
of SimplepatternKind.embedded:
|
||||
isRecursive(loc, name, sp.embedded.interface, seen)
|
||||
of SimplepatternKind.Ref:
|
||||
if sp.ref.name.string == name: true
|
||||
elif sp.ref in seen: false
|
||||
else:
|
||||
var
|
||||
(loc, def) = deref(loc, sp.ref)
|
||||
seen = seen
|
||||
incl(seen, sp.ref)
|
||||
isRecursive(loc, name, def, seen)
|
||||
else:
|
||||
false
|
||||
# seqof, setof, and dictof are not processed
|
||||
# because they imply pointer indirection
|
||||
|
||||
proc isRecursive(loc: Location; name: string; np: NamedSimplePattern; seen: RefSet): bool =
|
||||
case np.orKind
|
||||
of NamedSimplePatternKind.named:
|
||||
isRecursive(loc, name, np.named.pattern, seen)
|
||||
of NamedSimplePatternKind.anonymous:
|
||||
isRecursive(loc, name, np.anonymous, seen)
|
||||
|
||||
proc isRecursive(loc: Location; name: string; cp: CompoundPattern; seen: RefSet): bool =
|
||||
case cp.orKind
|
||||
of CompoundPatternKind.rec:
|
||||
result =
|
||||
isRecursive(loc, name, cp.rec.label.pattern, seen) or
|
||||
isRecursive(loc, name, cp.rec.fields.pattern, seen)
|
||||
of CompoundPatternKind.tuple:
|
||||
for np in cp.tuple.patterns:
|
||||
if result: return
|
||||
result = isRecursive(loc, name, np.pattern, seen)
|
||||
of CompoundPatternKind.tupleprefix:
|
||||
result = isRecursive(loc, name, cp.tupleprefix.variable, seen)
|
||||
for p in cp.tupleprefix.fixed:
|
||||
if result: return
|
||||
result = isRecursive(loc, name, p, seen)
|
||||
of CompoundPatternKind.dict:
|
||||
for nsp in cp.dict.entries.values:
|
||||
if result: return
|
||||
result = isRecursive(loc, name, nsp, seen)
|
||||
|
||||
proc isRecursive(loc: Location; name: string; pat: Pattern; seen: RefSet): bool =
|
||||
case pat.orKind
|
||||
of PatternKind.SimplePattern:
|
||||
isRecursive(loc, name, pat.simplePattern, seen)
|
||||
of PatternKind.CompoundPattern:
|
||||
isRecursive(loc, name, pat.compoundPattern, seen)
|
||||
|
||||
proc isRecursive(loc: Location; name: string; def: DefinitionOr|DefinitionAnd; seen: RefSet): bool =
|
||||
result =
|
||||
isRecursive(loc, name, def.field0.pattern0, seen) or
|
||||
isRecursive(loc, name, def.field0.pattern1, seen)
|
||||
for p in def.field0.patternN:
|
||||
if result: return
|
||||
result = isRecursive(loc, name, p, seen)
|
||||
|
||||
proc isRecursive(loc: Location; name: string; def: Definition; seen: RefSet): bool =
|
||||
case def.orKind
|
||||
of DefinitionKind.or:
|
||||
isRecursive(loc, name, def.or, seen)
|
||||
of DefinitionKind.and:
|
||||
isRecursive(loc, name, def.and, seen)
|
||||
of DefinitionKind.Pattern:
|
||||
isRecursive(loc, name, def.pattern, seen)
|
||||
|
||||
proc isRecursive(loc: Location; name: string; def: Definition): bool =
|
||||
var seen: RefSet
|
||||
isRecursive(loc, name, def, seen)
|
||||
|
||||
proc isLiteral(loc: Location; def: Definition): bool {.gcsafe.}
|
||||
proc isLiteral(loc: Location; pat: Pattern): bool {.gcsafe.}
|
||||
|
@ -466,7 +542,6 @@ proc typeDef(loc: Location; name: string; pat: Pattern; ty: PNode): PNode =
|
|||
case pat.orKind
|
||||
of PatternKind.CompoundPattern:
|
||||
let pragma = newNode(nkPragma)
|
||||
if isRecursive(loc, pat): pragma.add(ident"acyclic")
|
||||
case pat.compoundPattern.orKind
|
||||
of CompoundPatternKind.rec:
|
||||
if isLiteral(loc, pat.compoundPattern.rec.label):
|
||||
|
@ -491,8 +566,12 @@ proc typeDef(loc: Location; name: string; pat: Pattern; ty: PNode): PNode =
|
|||
proc typeDef(loc: Location; name: string; def: Definition; ty: PNode): PNode =
|
||||
case def.orKind
|
||||
of DefinitionKind.or:
|
||||
var ty = ty
|
||||
let pragma = newNode(nkPragma)
|
||||
if isRecursive(loc, def): pragma.add(ident"acyclic")
|
||||
if isRecursive(loc, name, def):
|
||||
doAssert ty.kind == nkObjectTy
|
||||
pragma.add(ident"acyclic")
|
||||
ty = nkRefTy.newTree(ty)
|
||||
pragma.add(ident"preservesOr")
|
||||
if isSymbolEnum(loc, def):
|
||||
pragma.add ident"pure"
|
||||
|
@ -656,8 +735,6 @@ proc nimTypeOf(loc: Location; known: var TypeTable; name: string; cp: CompoundPa
|
|||
of CompoundPatternKind.`dict`:
|
||||
result.node = nkObjectTy.newTree(newEmpty(), newEmpty(),
|
||||
newNode(nkRecList).addFields(loc, known, name, cp.dict.entries))
|
||||
if result.node.kind == nkObjectTy and isRecursive(loc, cp):
|
||||
result.node = nkRefTy.newTree(result.node)
|
||||
|
||||
proc nimTypeOf(loc: Location; known: var TypeTable; name: string; pat: Pattern): TypeSpec =
|
||||
case pat.orKind
|
||||
|
@ -727,9 +804,6 @@ proc nimTypeOf(loc: Location; known: var TypeTable; name: string; orDef: Definit
|
|||
newEmpty(),
|
||||
newEmpty(),
|
||||
nkRecList.newTree(recCase))
|
||||
# result.attrs = attrs(loc, orDef)
|
||||
if result.node.kind == nkObjectTy and (recursive in attrs(loc, orDef)):
|
||||
result.node = nkRefTy.newTree(result.node)
|
||||
|
||||
proc nimTypeOf(loc: Location; known: var TypeTable; name: string; def: DefinitionAnd): TypeSpec =
|
||||
if isDictionary(loc, def):
|
||||
|
@ -953,7 +1027,6 @@ when isMainModule:
|
|||
for inputPath in inputs:
|
||||
var bundle: Bundle
|
||||
if dirExists inputPath:
|
||||
new bundle
|
||||
for filePath in walkDirRec(inputPath, relative = true):
|
||||
var (dirPath, fileName, fileExt) = splitFile(filePath)
|
||||
if fileExt == ".prs":
|
||||
|
@ -974,10 +1047,9 @@ when isMainModule:
|
|||
if fromPreserves(schema, pr):
|
||||
bundle.modules[@[Symbol fileName]] = schema
|
||||
else:
|
||||
new bundle
|
||||
var scm = parsePreservesSchema(readFile(inputPath), dirPath)
|
||||
bundle.modules[@[Symbol fileName]] = scm
|
||||
if bundle.isNil or bundle.modules.len == 0:
|
||||
if bundle.modules.len == 0:
|
||||
quit "Failed to recognize " & inputPath
|
||||
else:
|
||||
writeModules(bundle)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[hashes, options, os, parseopt, streams, strutils, tables]
|
||||
import std/[hashes, os, parseopt, streams, strutils, tables]
|
||||
|
||||
import ../preserves, ./schema, ./schemaparse
|
||||
|
||||
|
@ -36,7 +36,7 @@ when isMainModule:
|
|||
write(outStream, schema.toPreserves)
|
||||
|
||||
else:
|
||||
let bundle = Bundle()
|
||||
var bundle: Bundle
|
||||
if not dirExists inputPath:
|
||||
quit "not a directory of schemas: " & inputPath
|
||||
else:
|
||||
|
|
|
@ -8,23 +8,23 @@ type
|
|||
`name`*: Symbol
|
||||
|
||||
ModulePath* = seq[Symbol]
|
||||
Bundle* {.acyclic, preservesRecord: "bundle".} = ref object
|
||||
Bundle* {.preservesRecord: "bundle".} = object
|
||||
`modules`*: Modules
|
||||
|
||||
CompoundPatternKind* {.pure.} = enum
|
||||
`rec`, `tuple`, `tuplePrefix`, `dict`
|
||||
CompoundPatternRec* {.acyclic, preservesRecord: "rec".} = ref object
|
||||
CompoundPatternRec* {.preservesRecord: "rec".} = object
|
||||
`label`*: NamedPattern
|
||||
`fields`*: NamedPattern
|
||||
|
||||
CompoundPatternTuple* {.acyclic, preservesRecord: "tuple".} = ref object
|
||||
CompoundPatternTuple* {.preservesRecord: "tuple".} = object
|
||||
`patterns`*: seq[NamedPattern]
|
||||
|
||||
CompoundPatternTuplePrefix* {.acyclic, preservesRecord: "tuplePrefix".} = ref object
|
||||
CompoundPatternTuplePrefix* {.preservesRecord: "tuplePrefix".} = object
|
||||
`fixed`*: seq[NamedPattern]
|
||||
`variable`*: NamedSimplePattern
|
||||
|
||||
CompoundPatternDict* {.acyclic, preservesRecord: "dict".} = ref object
|
||||
CompoundPatternDict* {.preservesRecord: "dict".} = object
|
||||
`entries`*: DictionaryEntries
|
||||
|
||||
`CompoundPattern`* {.acyclic, preservesOr.} = ref object
|
||||
|
@ -75,19 +75,19 @@ type
|
|||
SimplePatternAtom* {.preservesRecord: "atom".} = object
|
||||
`atomKind`*: AtomKind
|
||||
|
||||
SimplePatternEmbedded* {.acyclic, preservesRecord: "embedded".} = ref object
|
||||
SimplePatternEmbedded* {.preservesRecord: "embedded".} = object
|
||||
`interface`*: SimplePattern
|
||||
|
||||
SimplePatternLit* {.preservesRecord: "lit".} = object
|
||||
`value`*: Value
|
||||
|
||||
SimplePatternSeqof* {.acyclic, preservesRecord: "seqof".} = ref object
|
||||
SimplePatternSeqof* {.preservesRecord: "seqof".} = object
|
||||
`pattern`*: SimplePattern
|
||||
|
||||
SimplePatternSetof* {.acyclic, preservesRecord: "setof".} = ref object
|
||||
SimplePatternSetof* {.preservesRecord: "setof".} = object
|
||||
`pattern`*: SimplePattern
|
||||
|
||||
SimplePatternDictof* {.acyclic, preservesRecord: "dictof".} = ref object
|
||||
SimplePatternDictof* {.preservesRecord: "dictof".} = object
|
||||
`key`*: SimplePattern
|
||||
`value`*: SimplePattern
|
||||
|
||||
|
@ -120,7 +120,7 @@ type
|
|||
|
||||
NamedSimplePatternKind* {.pure.} = enum
|
||||
`named`, `anonymous`
|
||||
`NamedSimplePattern`* {.acyclic, preservesOr.} = ref object
|
||||
`NamedSimplePattern`* {.preservesOr.} = object
|
||||
case orKind*: NamedSimplePatternKind
|
||||
of NamedSimplePatternKind.`named`:
|
||||
`named`*: Binding
|
||||
|
@ -131,23 +131,23 @@ type
|
|||
|
||||
DefinitionKind* {.pure.} = enum
|
||||
`or`, `and`, `Pattern`
|
||||
DefinitionOrField0* {.acyclic, preservesTuple.} = ref object
|
||||
DefinitionOrField0* {.preservesTuple.} = object
|
||||
`pattern0`*: NamedAlternative
|
||||
`pattern1`*: NamedAlternative
|
||||
`patternN`* {.preservesTupleTail.}: seq[NamedAlternative]
|
||||
|
||||
DefinitionOr* {.acyclic, preservesRecord: "or".} = ref object
|
||||
DefinitionOr* {.preservesRecord: "or".} = object
|
||||
`field0`*: DefinitionOrField0
|
||||
|
||||
DefinitionAndField0* {.acyclic, preservesTuple.} = ref object
|
||||
DefinitionAndField0* {.preservesTuple.} = object
|
||||
`pattern0`*: NamedPattern
|
||||
`pattern1`*: NamedPattern
|
||||
`patternN`* {.preservesTupleTail.}: seq[NamedPattern]
|
||||
|
||||
DefinitionAnd* {.acyclic, preservesRecord: "and".} = ref object
|
||||
DefinitionAnd* {.preservesRecord: "and".} = object
|
||||
`field0`*: DefinitionAndField0
|
||||
|
||||
`Definition`* {.acyclic, preservesOr.} = ref object
|
||||
`Definition`* {.preservesOr.} = object
|
||||
case orKind*: DefinitionKind
|
||||
of DefinitionKind.`or`:
|
||||
`or`*: DefinitionOr
|
||||
|
@ -159,16 +159,16 @@ type
|
|||
`pattern`*: Pattern
|
||||
|
||||
|
||||
NamedAlternative* {.acyclic, preservesTuple.} = ref object
|
||||
NamedAlternative* {.preservesTuple.} = object
|
||||
`variantLabel`*: string
|
||||
`pattern`*: Pattern
|
||||
|
||||
SchemaField0* {.acyclic, preservesDictionary.} = ref object
|
||||
SchemaField0* {.preservesDictionary.} = object
|
||||
`definitions`*: Definitions
|
||||
`embeddedType`*: EmbeddedTypeName
|
||||
`version`* {.preservesLiteral: "1".}: tuple[]
|
||||
|
||||
Schema* {.acyclic, preservesRecord: "schema".} = ref object
|
||||
Schema* {.preservesRecord: "schema".} = object
|
||||
`field0`*: SchemaField0
|
||||
|
||||
PatternKind* {.pure.} = enum
|
||||
|
@ -182,7 +182,7 @@ type
|
|||
`compoundpattern`*: CompoundPattern
|
||||
|
||||
|
||||
Binding* {.acyclic, preservesRecord: "named".} = ref object
|
||||
Binding* {.preservesRecord: "named".} = object
|
||||
`name`*: Symbol
|
||||
`pattern`*: SimplePattern
|
||||
|
||||
|
|
Loading…
Reference in New Issue