changed TagId to Tag. removed JS stuff that wasn't working anyway.

This commit is contained in:
Felix Krause 2020-11-10 13:55:22 +01:00
parent 6238104622
commit aa65c066d5
9 changed files with 185 additions and 292 deletions

View File

@ -118,7 +118,7 @@ template yieldEvent() {.dirty.} =
yield curEvent yield curEvent
inEvent = false inEvent = false
template setTag(t: TagId) {.dirty.} = template setTag(t: Tag) {.dirty.} =
case curEvent.kind case curEvent.kind
of yamlStartSeq: curEvent.seqProperties.tag = t of yamlStartSeq: curEvent.seqProperties.tag = t
of yamlStartMap: curEvent.mapProperties.tag = t of yamlStartMap: curEvent.mapProperties.tag = t
@ -133,8 +133,8 @@ template setAnchor(a: Anchor) {.dirty.} =
of yamlAlias: curEvent.aliasTarget = a of yamlAlias: curEvent.aliasTarget = a
else: discard else: discard
template curTag(): TagId = template curTag(): Tag =
var foo: TagId var foo: Tag
case curEvent.kind case curEvent.kind
of yamlStartSeq: foo = curEvent.seqProperties.tag of yamlStartSeq: foo = curEvent.seqProperties.tag
of yamlStartMap: foo = curEvent.mapProperties.tag of yamlStartMap: foo = curEvent.mapProperties.tag
@ -143,7 +143,7 @@ template curTag(): TagId =
$curEvent.kind & " may not have a tag") $curEvent.kind & " may not have a tag")
foo foo
template setCurTag(val: TagId) = template setCurTag(val: Tag) =
case curEvent.kind case curEvent.kind
of yamlStartSeq: curEvent.seqProperties.tag = val of yamlStartSeq: curEvent.seqProperties.tag = val
of yamlStartMap: curEvent.mapProperties.tag = val of yamlStartMap: curEvent.mapProperties.tag = val

View File

@ -71,7 +71,7 @@ setTagUri(BetterInt, "!test:BetterInt")
const yamlDirs = "%YAML 1.2\n%TAG !n! tag:nimyaml.org,2016:\n--- " const yamlDirs = "%YAML 1.2\n%TAG !n! tag:nimyaml.org,2016:\n--- "
proc representObject*(value: BetterInt, ts: TagStyle = tsNone, proc representObject*(value: BetterInt, ts: TagStyle = tsNone,
c: SerializationContext, tag: TagId) {.raises: [].} = c: SerializationContext, tag: Tag) {.raises: [].} =
var var
val = $value val = $value
i = val.len - 3 i = val.len - 3

View File

@ -10,17 +10,8 @@ type
## Anchor provides the operator `$` for converting to string, `==` for ## Anchor provides the operator `$` for converting to string, `==` for
## comparison, and `hash` for usage in a hashmap. ## comparison, and `hash` for usage in a hashmap.
TagId* = distinct int ## \ Tag* = distinct string ## \
## A ``TagId`` identifies a tag URI, like for example ## A ``Tag`` contains an URI, like for example ``"tag:yaml.org,2002:str"``.
## ``"tag:yaml.org,2002:str"``. The URI corresponding to a ``TagId`` can
## be queried from the `TagLibrary <#TagLibrary>`_ which was
## used to create this ``TagId``; e.g. when you parse a YAML character
## stream, the ``TagLibrary`` of the parser is the one which generates
## the resulting ``TagId`` s.
##
## URI strings are mapped to ``TagId`` s for efficiency reasons (you
## do not need to compare strings every time) and to be able to
## discover unknown tag URIs early in the parsing process.
ScalarStyle* = enum ScalarStyle* = enum
## Original style of the scalar (for input), ## Original style of the scalar (for input),
@ -77,7 +68,21 @@ type
Mark* = tuple[line, column: Positive] Mark* = tuple[line, column: Positive]
Properties* = tuple[anchor: Anchor, tag: TagId] Properties* = tuple[anchor: Anchor, tag: Tag]
const
yamlTagRepositoryPrefix* = "tag:yaml.org,2002:"
nimyamlTagRepositoryPrefix* = "tag:nimyaml.org,2016:"
proc defineTag*(uri: string): Tag =
## defines a tag. Use this to optimize away copies of globally defined
## Tags.
result = uri.Tag
#shallow(result.string) # doesn't work at compile-time
proc defineCoreTag*(name: string): Tag =
## defines a tag in YAML's core namespace, ``tag:yaml.org,2002:``
result = defineTag(yamlTagRepositoryPrefix & name)
const const
yAnchorNone*: Anchor = "".Anchor ## \ yAnchorNone*: Anchor = "".Anchor ## \
@ -86,61 +91,36 @@ const
defaultMark: Mark = (1.Positive, 1.Positive) ## \ defaultMark: Mark = (1.Positive, 1.Positive) ## \
## used for events that are not generated from input. ## used for events that are not generated from input.
yTagExclamationMark*: TagId = 0.TagId ## ``!`` non-specific tag yTagExclamationMark*: Tag = defineTag("!")
yTagQuestionMark* : TagId = 1.TagId ## ``?`` non-specific tag yTagQuestionMark* : Tag = defineTag("?")
# failsafe schema # failsafe schema
yTagString* : TagId = 2.TagId ## \ yTagString* = defineCoreTag("str")
## `!!str <http://yaml.org/type/str.html >`_ tag yTagSequence* = defineCoreTag("seq")
yTagSequence* : TagId = 3.TagId ## \ yTagMapping* = defineCoreTag("map")
## `!!seq <http://yaml.org/type/seq.html>`_ tag
yTagMapping* : TagId = 4.TagId ## \
## `!!map <http://yaml.org/type/map.html>`_ tag
# json & core schema # json & core schema
yTagNull* : TagId = 5.TagId ## \ yTagNull* = defineCoreTag("null")
## `!!null <http://yaml.org/type/null.html>`_ tag yTagBoolean* = defineCoreTag("bool")
yTagBoolean* : TagId = 6.TagId ## \ yTagInteger* = defineCoreTag("int")
## `!!bool <http://yaml.org/type/bool.html>`_ tag yTagFloat* = defineCoreTag("float")
yTagInteger* : TagId = 7.TagId ## \
## `!!int <http://yaml.org/type/int.html>`_ tag
yTagFloat* : TagId = 8.TagId ## \
## `!!float <http://yaml.org/type/float.html>`_ tag
# other language-independent YAML types (from http://yaml.org/type/ ) # other language-independent YAML types (from http://yaml.org/type/ )
yTagOrderedMap* : TagId = 9.TagId ## \ yTagOrderedMap* = defineCoreTag("omap")
## `!!omap <http://yaml.org/type/omap.html>`_ tag yTagPairs* = defineCoreTag("pairs")
yTagPairs* : TagId = 10.TagId ## \ yTagSet* = defineCoreTag("set")
## `!!pairs <http://yaml.org/type/pairs.html>`_ tag yTagBinary* = defineCoreTag("binary")
yTagSet* : TagId = 11.TagId ## \ yTagMerge* = defineCoreTag("merge")
## `!!set <http://yaml.org/type/set.html>`_ tag yTagTimestamp* = defineCoreTag("timestamp")
yTagBinary* : TagId = 12.TagId ## \ yTagValue* = defineCoreTag("value")
## `!!binary <http://yaml.org/type/binary.html>`_ tag yTagYaml* = defineCoreTag("yaml")
yTagMerge* : TagId = 13.TagId ## \
## `!!merge <http://yaml.org/type/merge.html>`_ tag
yTagTimestamp* : TagId = 14.TagId ## \
## `!!timestamp <http://yaml.org/type/timestamp.html>`_ tag
yTagValue* : TagId = 15.TagId ## \
## `!!value <http://yaml.org/type/value.html>`_ tag
yTagYaml* : TagId = 16.TagId ## \
## `!!yaml <http://yaml.org/type/yaml.html>`_ tag
yTagNimField* : TagId = 100.TagId ## \ # NimYAML specific tags
## This tag is used in serialization for the name of a field of an
## object. It may contain any string scalar that is a valid Nim symbol.
yFirstStaticTagId* : TagId = 1000.TagId ## \ yTagNimField* = defineTag(nimyamlTagRepositoryPrefix & "field")
## The first ``TagId`` assigned by the ``setTagId`` templates.
yFirstCustomTagId* : TagId = 10000.TagId ## \
## The first ``TagId`` which should be assigned to an URI that does not
## exist in the ``YamlTagLibrary`` which is used for parsing.
yamlTagRepositoryPrefix* = "tag:yaml.org,2002:"
nimyamlTagRepositoryPrefix* = "tag:nimyaml.org,2016:"
proc properties*(event: Event): Properties = proc properties*(event: Event): Properties =
## returns the tag of the given event ## returns the tag of the given event
@ -186,7 +166,7 @@ proc startMapEvent*(style: CollectionStyle, props: Properties,
mapStyle: style) mapStyle: style)
proc startMapEvent*(style: CollectionStyle = csAny, proc startMapEvent*(style: CollectionStyle = csAny,
tag: TagId = yTagQuestionMark, tag: Tag = yTagQuestionMark,
anchor: Anchor = yAnchorNone, anchor: Anchor = yAnchorNone,
startPos, endPos: Mark = defaultMark): Event {.inline.} = startPos, endPos: Mark = defaultMark): Event {.inline.} =
return startMapEvent(style, (anchor, tag), startPos, endPos) return startMapEvent(style, (anchor, tag), startPos, endPos)
@ -204,7 +184,7 @@ proc startSeqEvent*(style: CollectionStyle,
seqStyle: style) seqStyle: style)
proc startSeqEvent*(style: CollectionStyle = csAny, proc startSeqEvent*(style: CollectionStyle = csAny,
tag: TagId = yTagQuestionMark, tag: Tag = yTagQuestionMark,
anchor: Anchor = yAnchorNone, anchor: Anchor = yAnchorNone,
startPos, endPos: Mark = defaultMark): Event {.inline.} = startPos, endPos: Mark = defaultMark): Event {.inline.} =
return startSeqEvent(style, (anchor, tag), startPos, endPos) return startSeqEvent(style, (anchor, tag), startPos, endPos)
@ -221,7 +201,7 @@ proc scalarEvent*(content: string, props: Properties,
kind: yamlScalar, scalarProperties: props, kind: yamlScalar, scalarProperties: props,
scalarContent: content, scalarStyle: style) scalarContent: content, scalarStyle: style)
proc scalarEvent*(content: string = "", tag: TagId = yTagQuestionMark, proc scalarEvent*(content: string = "", tag: Tag = yTagQuestionMark,
anchor: Anchor = yAnchorNone, anchor: Anchor = yAnchorNone,
style: ScalarStyle = ssAny, style: ScalarStyle = ssAny,
startPos, endPos: Mark = defaultMark): Event {.inline.} = startPos, endPos: Mark = defaultMark): Event {.inline.} =
@ -231,34 +211,13 @@ proc aliasEvent*(target: Anchor, startPos, endPos: Mark = defaultMark): Event {.
## creates a new event that represents a YAML alias ## creates a new event that represents a YAML alias
result = Event(startPos: startPos, endPos: endPos, kind: yamlAlias, aliasTarget: target) result = Event(startPos: startPos, endPos: endPos, kind: yamlAlias, aliasTarget: target)
proc `==`*(left, right: Anchor): bool {.borrow, locks: 0.} proc `==`*(left, right: Anchor): bool {.borrow.}
proc `$`*(id: Anchor): string {.borrow, locks: 0.} proc `$`*(id: Anchor): string {.borrow.}
proc hash*(id: Anchor): Hash {.borrow, locks: 0.} proc hash*(id: Anchor): Hash {.borrow.}
proc `==`*(left, right: TagId): bool {.borrow, locks: 0.} proc `==`*(left, right: Tag): bool {.borrow.}
proc hash*(id: TagId): Hash {.borrow, locks: 0.} proc `$`*(tag: Tag): string {.borrow.}
proc hash*(tag: Tag): Hash {.borrow.}
proc `$`*(id: TagId): string {.raises: [].} =
case id
of yTagQuestionMark: "?"
of yTagExclamationMark: "!"
of yTagString: "!!str"
of yTagSequence: "!!seq"
of yTagMapping: "!!map"
of yTagNull: "!!null"
of yTagBoolean: "!!bool"
of yTagInteger: "!!int"
of yTagFloat: "!!float"
of yTagOrderedMap: "!!omap"
of yTagPairs: "!!pairs"
of yTagSet: "!!set"
of yTagBinary: "!!binary"
of yTagMerge: "!!merge"
of yTagTimestamp: "!!timestamp"
of yTagValue: "!!value"
of yTagYaml: "!!yaml"
of yTagNimField: "!nim:field"
else: "<" & $int(id) & ">"
proc `==`*(left: Event, right: Event): bool {.raises: [].} = proc `==`*(left: Event, right: Event): bool {.raises: [].} =
## compares all existing fields of the given items ## compares all existing fields of the given items
@ -279,8 +238,8 @@ proc renderAttrs*(props: Properties, isPlain: bool = true): string =
result = "" result = ""
if props.anchor != yAnchorNone: result &= " &" & $props.anchor if props.anchor != yAnchorNone: result &= " &" & $props.anchor
case props.tag case props.tag
of yTagQuestionmark: discard of yTagQuestionMark: discard
of yTagExclamationmark: of yTagExclamationMark:
if isPlain: result &= " <!>" if isPlain: result &= " <!>"
else: else:
result &= " <" & $props.tag & ">" result &= " <" & $props.tag & ">"
@ -313,3 +272,14 @@ proc `$`*(event: Event): string {.raises: [].} =
of ssFolded: result &= " >" of ssFolded: result &= " >"
result &= yamlTestSuiteEscape(event.scalarContent) result &= yamlTestSuiteEscape(event.scalarContent)
of yamlAlias: result = "=ALI *" & $event.aliasTarget of yamlAlias: result = "=ALI *" & $event.aliasTarget
type
TypeID* = int ## unique ID of a type
var nextTypeID {.compileTime.}: TypeID
proc typeID*(T:typedesc): TypeID =
const id = nextTypeID
static:
inc nextTypeID
return id

View File

@ -137,11 +137,8 @@ proc composeNode(s: var YamlStream, tagLib: TagLibrary,
YamlNode {.raises: [YamlStreamError, YamlConstructionError].} = YamlNode {.raises: [YamlStreamError, YamlConstructionError].} =
template addAnchor(c: ConstructionContext, target: Anchor) = template addAnchor(c: ConstructionContext, target: Anchor) =
if target != yAnchorNone: if target != yAnchorNone:
when defined(JS):
{.emit: [c, """.refs.set(""", target, ", ", result, ");"].}
else:
yAssert(not c.refs.hasKey(target)) yAssert(not c.refs.hasKey(target))
c.refs[target] = cast[pointer](result) c.refs[target] = (id: typeID(YamlNode), p: cast[pointer](result))
var start: Event var start: Event
shallowCopy(start, s.next()) shallowCopy(start, s.next())
@ -149,7 +146,7 @@ proc composeNode(s: var YamlStream, tagLib: TagLibrary,
try: try:
case start.kind case start.kind
of yamlStartMap: of yamlStartMap:
result = YamlNode(tag: tagLib.uri(start.mapProperties.tag), result = YamlNode(tag: $start.mapProperties.tag,
kind: yMapping, kind: yMapping,
fields: newTable[YamlNode, YamlNode]()) fields: newTable[YamlNode, YamlNode]())
while s.peek().kind != yamlEndMap: while s.peek().kind != yamlEndMap:
@ -162,7 +159,7 @@ proc composeNode(s: var YamlStream, tagLib: TagLibrary,
discard s.next() discard s.next()
addAnchor(c, start.mapProperties.anchor) addAnchor(c, start.mapProperties.anchor)
of yamlStartSeq: of yamlStartSeq:
result = YamlNode(tag: tagLib.uri(start.seqProperties.tag), result = YamlNode(tag: $start.seqProperties.tag,
kind: ySequence, kind: ySequence,
elems: newSeq[YamlNode]()) elems: newSeq[YamlNode]())
while s.peek().kind != yamlEndSeq: while s.peek().kind != yamlEndSeq:
@ -170,15 +167,12 @@ proc composeNode(s: var YamlStream, tagLib: TagLibrary,
addAnchor(c, start.seqProperties.anchor) addAnchor(c, start.seqProperties.anchor)
discard s.next() discard s.next()
of yamlScalar: of yamlScalar:
result = YamlNode(tag: tagLib.uri(start.scalarProperties.tag), result = YamlNode(tag: $start.scalarProperties.tag,
kind: yScalar) kind: yScalar)
shallowCopy(result.content, start.scalarContent) shallowCopy(result.content, start.scalarContent)
addAnchor(c, start.scalarProperties.anchor) addAnchor(c, start.scalarProperties.anchor)
of yamlAlias: of yamlAlias:
when defined(JS): result = cast[YamlNode](c.refs[start.aliasTarget].p)
{.emit: [result, " = ", c, ".refs.get(", start.aliasTarget, ");"].}
else:
result = cast[YamlNode](c.refs[start.aliasTarget])
else: internalError("Malformed YamlStream") else: internalError("Malformed YamlStream")
except KeyError: except KeyError:
raise newException(YamlConstructionError, raise newException(YamlConstructionError,
@ -213,44 +207,22 @@ proc loadDom*(s: Stream | string): YamlDocument
proc serializeNode(n: YamlNode, c: SerializationContext, a: AnchorStyle, proc serializeNode(n: YamlNode, c: SerializationContext, a: AnchorStyle,
tagLib: TagLibrary) {.raises: [].}= tagLib: TagLibrary) {.raises: [].}=
var val = yAnchorNone var val = yAnchorNone
when defined(JS):
{.emit: ["""
if (""", a, " != ", asNone, " && ", c, ".refs.has(", n, """) {
""", val, " = ", c, ".refs.get(", n, """);
if (""", c, ".refs.get(", n, ") == ", yAnchorNone, ") {"].}
val = c.nextAnchorId
{.emit: [c, """.refs.set(""", n, """, """, val, """);"""].}
c.nextAnchorId = AnchorId(int(c.nextAnchorId) + 1)
{.emit: "}".}
c.put(aliasEvent(val))
return
{.emit: "}".}
else:
let p = cast[pointer](n) let p = cast[pointer](n)
if a != asNone and c.refs.hasKey(p): if a != asNone and c.refs.hasKey(p):
val = c.refs.getOrDefault(p) val = c.refs.getOrDefault(p).a
if val == yAnchorNone: if val == yAnchorNone:
val = c.nextAnchorId.Anchor val = c.nextAnchorId.Anchor
c.refs[p] = val c.refs[p] = (val, false)
nextAnchor(c.nextAnchorId, len(c.nextAnchorId) - 1) nextAnchor(c.nextAnchorId, len(c.nextAnchorId) - 1)
c.put(aliasEvent(val)) c.put(aliasEvent(val))
return return
var var
tagId: TagId
anchor: Anchor anchor: Anchor
if a == asAlways: if a != asNone:
val = c.nextAnchorId.Anchor val = c.nextAnchorId.Anchor
when defined(JS): c.refs[p] = (c.nextAnchorId.Anchor, false)
{.emit: [c, ".refs.set(", n, ", ", val, ");"].}
else:
c.refs[p] = c.nextAnchorId.Anchor
nextAnchor(c.nextAnchorId, len(c.nextAnchorId) - 1) nextAnchor(c.nextAnchorId, len(c.nextAnchorId) - 1)
else: let tag = if tagLib.tags.hasKey(n.tag): tagLib.tags.getOrDefault(n.tag) else:
when defined(JS):
{.emit: [c, ".refs.set(", n, ", ", yAnchorNone, ");"].}
else:
c.refs[p] = yAnchorNone
tagId = if tagLib.tags.hasKey(n.tag): tagLib.tags.getOrDefault(n.tag) else:
tagLib.registerUri(n.tag) tagLib.registerUri(n.tag)
case a case a
of asNone: anchor = yAnchorNone of asNone: anchor = yAnchorNone
@ -258,27 +230,25 @@ proc serializeNode(n: YamlNode, c: SerializationContext, a: AnchorStyle,
of asAlways: anchor = val of asAlways: anchor = val
case n.kind case n.kind
of yScalar: c.put(scalarEvent(n.content, tagId, anchor)) of yScalar: c.put(scalarEvent(n.content, tag, anchor))
of ySequence: of ySequence:
c.put(startSeqEvent(csBlock, (anchor, tagId))) c.put(startSeqEvent(csBlock, (anchor, tag)))
for item in n.elems: for item in n.elems:
serializeNode(item, c, a, tagLib) serializeNode(item, c, a, tagLib)
c.put(endSeqEvent()) c.put(endSeqEvent())
of yMapping: of yMapping:
c.put(startMapEvent(csBlock, (anchor, tagId))) c.put(startMapEvent(csBlock, (anchor, tag)))
for key, value in n.fields.pairs: for key, value in n.fields.pairs:
serializeNode(key, c, a, tagLib) serializeNode(key, c, a, tagLib)
serializeNode(value, c, a, tagLib) serializeNode(value, c, a, tagLib)
c.put(endMapEvent()) c.put(endMapEvent())
template processAnchoredEvent(target: untyped, c: SerializationContext) = proc processAnchoredEvent(target: var Properties, c: SerializationContext) =
var anchorId: Anchor for key, val in c.refs:
when defined(JS): if val.a == target.anchor:
{.emit: [anchorId, " = ", c, ".refs.get(", target, ");"].} if not val.referenced:
else: target.anchor = yAnchorNone
anchorId = c.refs.getOrDefault(cast[pointer](target.anchor)) break
if anchorId != yAnchorNone: target.anchor = anchorId
else: target.anchor = yAnchorNone
proc serialize*(doc: YamlDocument, tagLib: TagLibrary, a: AnchorStyle = asTidy): proc serialize*(doc: YamlDocument, tagLib: TagLibrary, a: AnchorStyle = asTidy):
YamlStream {.raises: [].} = YamlStream {.raises: [].} =

View File

@ -212,7 +212,7 @@ proc generateError(c: Context, message: string):
msg: message, parent: nil, mark: c.lex.curStartPos, msg: message, parent: nil, mark: c.lex.curStartPos,
lineContent: c.lex.currentLine()) lineContent: c.lex.currentLine())
proc parseTag(c: Context): TagId = proc parseTag(c: Context): Tag =
let handle = c.lex.fullLexeme() let handle = c.lex.fullLexeme()
var uri = c.resolveHandle(handle) var uri = c.resolveHandle(handle)
if uri == "": if uri == "":
@ -792,7 +792,7 @@ proc beforeBlockIndentation(c: Context, e: var Event): bool =
elif c.levels[^1].state == beforeBlockIndentation: elif c.levels[^1].state == beforeBlockIndentation:
raise c.generateError("Unexpected double beforeBlockIndentation") raise c.generateError("Unexpected double beforeBlockIndentation")
else: else:
raise c.generateError("Internal error (please report this bug)") raise c.generateError("Internal error (please report this bug): unexpected state at endBlockNode")
c.popLevel() c.popLevel()
c.popLevel() c.popLevel()
case c.lex.cur case c.lex.cur

View File

@ -428,7 +428,7 @@ proc writeTagAndAnchor(c: Context, props: Properties) {.raises: [YamlPresenterOu
let t = c.target let t = c.target
try: try:
if props.tag notin [yTagQuestionMark, yTagExclamationMark]: if props.tag notin [yTagQuestionMark, yTagExclamationMark]:
let tagUri = c.tagLib.uri(props.tag) let tagUri = $props.tag
let (handle, length) = c.searchHandle(tagUri) let (handle, length) = c.searchHandle(tagUri)
if length > 0: if length > 0:
t.append(handle) t.append(handle)

View File

@ -25,16 +25,14 @@ export data, stream, macros, annotations, options
type type
SerializationContext* = ref object SerializationContext* = ref object
## Context information for the process of serializing YAML from Nim values. ## Context information for the process of serializing YAML from Nim values.
when not defined(JS): refs*: Table[pointer, tuple[a: Anchor, referenced: bool]]
refs*: Table[pointer, Anchor] # `pointer` does not work with JS
style: AnchorStyle style: AnchorStyle
nextAnchorId*: string nextAnchorId*: string
put*: proc(e: Event) {.raises: [], closure.} put*: proc(e: Event) {.raises: [], closure.}
ConstructionContext* = ref object ConstructionContext* = ref object
## Context information for the process of constructing Nim values from YAML. ## Context information for the process of constructing Nim values from YAML.
when not defined(JS): refs*: Table[Anchor, tuple[id: TypeID, p: pointer]]
refs*: Table[Anchor, pointer]
YamlConstructionError* = object of YamlLoadingError YamlConstructionError* = object of YamlLoadingError
## Exception that may be raised when constructing data objects from a ## Exception that may be raised when constructing data objects from a
@ -88,42 +86,35 @@ proc representChild*[O](value: O, ts: TagStyle, c: SerializationContext)
proc newConstructionContext*(): ConstructionContext = proc newConstructionContext*(): ConstructionContext =
new(result) new(result)
when defined(JS): result.refs = initTable[Anchor, tuple[id: TypeID, p: pointer]]()
{.emit: [result, """.refs = new Map();"""].}
else:
result.refs = initTable[Anchor, pointer]()
proc newSerializationContext*(s: AnchorStyle, proc newSerializationContext*(s: AnchorStyle,
putImpl: proc(e: Event) {.raises: [], closure.}): putImpl: proc(e: Event) {.raises: [], closure.}):
SerializationContext = SerializationContext =
result = SerializationContext(style: s, nextAnchorId: "a", result = SerializationContext(style: s, nextAnchorId: "a",
put: putImpl) put: putImpl)
when defined(JS): result.refs = initTable[pointer, tuple[a: Anchor, referenced: bool]]()
{.emit: [result, """.refs = new Map();"""].}
else: result.refs = initTable[pointer, Anchor]()
template presentTag*(t: typedesc, ts: TagStyle): TagId = template presentTag*(t: typedesc, ts: TagStyle): Tag =
## Get the TagId that represents the given type in the given style ## Get the Tag that represents the given type in the given style
if ts == tsNone: yTagQuestionMark else: yamlTag(t) if ts == tsNone: yTagQuestionMark else: yamlTag(t)
proc lazyLoadTag(uri: string): TagId {.inline, raises: [].} = proc lazyLoadTag(uri: string): Tag {.inline, raises: [].} =
try: result = serializationTagLibrary.tags[uri] try: result = serializationTagLibrary.tags[uri]
except KeyError: result = serializationTagLibrary.registerUri(uri) except KeyError: result = serializationTagLibrary.registerUri(uri)
proc safeTagUri(id: TagId): string {.raises: [].} = proc safeTagUri(tag: Tag): string {.raises: [].} =
try: try:
var var uri = $tag
uri = serializationTagLibrary.uri(id)
i = 0
# '!' is not allowed inside a tag handle # '!' is not allowed inside a tag handle
if uri.len > 0 and uri[0] == '!': uri = uri[1..^1] if uri.len > 0 and uri[0] == '!': uri = uri[1..^1]
# ',' is not allowed after a tag handle in the suffix because it's a flow # ',' is not allowed after a tag handle in the suffix because it's a flow
# indicator # indicator
for c in uri.mitems(): for i in countup(0, uri.len - 1):
if c == ',': c = ';' if uri[i] == ',': uri[i] = ';'
inc(i)
return uri return uri
except KeyError: internalError("Unexpected KeyError for TagId " & $id) except KeyError:
internalError("Unexpected KeyError for Tag " & $tag)
proc constructionError(s: YamlStream, mark: Mark, msg: string): ref YamlConstructionError = proc constructionError(s: YamlStream, mark: Mark, msg: string): ref YamlConstructionError =
result = newException(YamlConstructionError, msg) result = newException(YamlConstructionError, msg)
@ -151,7 +142,7 @@ template constructScalarItem*(s: var YamlStream, i: untyped,
e.parent = getCurrentException() e.parent = getCurrentException()
raise e raise e
proc yamlTag*(T: typedesc[string]): TagId {.inline, noSideEffect, raises: [].} = proc yamlTag*(T: typedesc[string]): Tag {.inline, noSideEffect, raises: [].} =
yTagString yTagString
proc constructObject*(s: var YamlStream, c: ConstructionContext, proc constructObject*(s: var YamlStream, c: ConstructionContext,
@ -162,7 +153,7 @@ proc constructObject*(s: var YamlStream, c: ConstructionContext,
result = item.scalarContent result = item.scalarContent
proc representObject*(value: string, ts: TagStyle, proc representObject*(value: string, ts: TagStyle,
c: SerializationContext, tag: TagId) {.raises: [].} = c: SerializationContext, tag: Tag) {.raises: [].} =
## represents a string as YAML scalar ## represents a string as YAML scalar
c.put(scalarEvent(value, tag, yAnchorNone)) c.put(scalarEvent(value, tag, yAnchorNone))
@ -217,12 +208,12 @@ proc constructObject*(s: var YamlStream, c: ConstructionContext,
result = int(i32Result) result = int(i32Result)
proc representObject*[T: int8|int16|int32|int64](value: T, ts: TagStyle, proc representObject*[T: int8|int16|int32|int64](value: T, ts: TagStyle,
c: SerializationContext, tag: TagId) {.raises: [].} = c: SerializationContext, tag: Tag) {.raises: [].} =
## represents an integer value as YAML scalar ## represents an integer value as YAML scalar
c.put(scalarEvent($value, tag, yAnchorNone)) c.put(scalarEvent($value, tag, yAnchorNone))
proc representObject*(value: int, tagStyle: TagStyle, proc representObject*(value: int, tagStyle: TagStyle,
c: SerializationContext, tag: TagId) c: SerializationContext, tag: Tag)
{.raises: [YamlStreamError], inline.}= {.raises: [YamlStreamError], inline.}=
## represent an integer of architecture-defined length by casting it to int32. ## represent an integer of architecture-defined length by casting it to int32.
## on 64-bit systems, this may cause a RangeDefect. ## on 64-bit systems, this may cause a RangeDefect.
@ -265,12 +256,12 @@ when defined(JS):
result = $BiggestInt(x) result = $BiggestInt(x)
proc representObject*[T: uint8|uint16|uint32|uint64](value: T, ts: TagStyle, proc representObject*[T: uint8|uint16|uint32|uint64](value: T, ts: TagStyle,
c: SerializationContext, tag: TagId) {.raises: [].} = c: SerializationContext, tag: Tag) {.raises: [].} =
## represents an unsigned integer value as YAML scalar ## represents an unsigned integer value as YAML scalar
c.put(scalarEvent($value, tag, yAnchorNone)) c.put(scalarEvent($value, tag, yAnchorNone))
proc representObject*(value: uint, ts: TagStyle, c: SerializationContext, proc representObject*(value: uint, ts: TagStyle, c: SerializationContext,
tag: TagId) {.raises: [YamlStreamError], inline.} = tag: Tag) {.raises: [YamlStreamError], inline.} =
## represent an unsigned integer of architecture-defined length by casting it ## represent an unsigned integer of architecture-defined length by casting it
## to int32. on 64-bit systems, this may cause a RangeDefect. ## to int32. on 64-bit systems, this may cause a RangeDefect.
try: c.put(scalarEvent($uint32(value), tag, yAnchorNone)) try: c.put(scalarEvent($uint32(value), tag, yAnchorNone))
@ -299,7 +290,7 @@ proc constructObject*[T: float|float32|float64](
escape(item.scalarContent)) escape(item.scalarContent))
proc representObject*[T: float|float32|float64](value: T, ts: TagStyle, proc representObject*[T: float|float32|float64](value: T, ts: TagStyle,
c: SerializationContext, tag: TagId) {.raises: [].} = c: SerializationContext, tag: Tag) {.raises: [].} =
## represents a float value as YAML scalar ## represents a float value as YAML scalar
case value case value
of Inf: c.put(scalarEvent(".inf", tag)) of Inf: c.put(scalarEvent(".inf", tag))
@ -307,7 +298,7 @@ proc representObject*[T: float|float32|float64](value: T, ts: TagStyle,
of NaN: c.put(scalarEvent(".nan", tag)) of NaN: c.put(scalarEvent(".nan", tag))
else: c.put(scalarEvent($value, tag)) else: c.put(scalarEvent($value, tag))
proc yamlTag*(T: typedesc[bool]): TagId {.inline, raises: [].} = yTagBoolean proc yamlTag*(T: typedesc[bool]): Tag {.inline, raises: [].} = yTagBoolean
proc constructObject*(s: var YamlStream, c: ConstructionContext, proc constructObject*(s: var YamlStream, c: ConstructionContext,
result: var bool) result: var bool)
@ -322,7 +313,7 @@ proc constructObject*(s: var YamlStream, c: ConstructionContext,
escape(item.scalarContent)) escape(item.scalarContent))
proc representObject*(value: bool, ts: TagStyle, c: SerializationContext, proc representObject*(value: bool, ts: TagStyle, c: SerializationContext,
tag: TagId) {.raises: [].} = tag: Tag) {.raises: [].} =
## represents a bool value as a YAML scalar ## represents a bool value as a YAML scalar
c.put(scalarEvent(if value: "y" else: "n", tag, yAnchorNone)) c.put(scalarEvent(if value: "y" else: "n", tag, yAnchorNone))
@ -337,11 +328,11 @@ proc constructObject*(s: var YamlStream, c: ConstructionContext,
else: result = item.scalarContent[0] else: result = item.scalarContent[0]
proc representObject*(value: char, ts: TagStyle, c: SerializationContext, proc representObject*(value: char, ts: TagStyle, c: SerializationContext,
tag: TagId) {.raises: [].} = tag: Tag) {.raises: [].} =
## represents a char value as YAML scalar ## represents a char value as YAML scalar
c.put(scalarEvent("" & value, tag, yAnchorNone)) c.put(scalarEvent("" & value, tag, yAnchorNone))
proc yamlTag*(T: typedesc[Time]): TagId {.inline, raises: [].} = yTagTimestamp proc yamlTag*(T: typedesc[Time]): Tag {.inline, raises: [].} = yTagTimestamp
proc constructObject*(s: var YamlStream, c: ConstructionContext, proc constructObject*(s: var YamlStream, c: ConstructionContext,
result: var Time) result: var Time)
@ -403,15 +394,15 @@ proc constructObject*(s: var YamlStream, c: ConstructionContext,
escape(item.scalarContent)) escape(item.scalarContent))
proc representObject*(value: Time, ts: TagStyle, c: SerializationContext, proc representObject*(value: Time, ts: TagStyle, c: SerializationContext,
tag: TagId) {.raises: [ValueError].} = tag: Tag) {.raises: [ValueError].} =
let tmp = value.utc() let tmp = value.utc()
c.put(scalarEvent(tmp.format("yyyy-MM-dd'T'HH:mm:ss'Z'"))) c.put(scalarEvent(tmp.format("yyyy-MM-dd'T'HH:mm:ss'Z'")))
proc yamlTag*[I](T: typedesc[seq[I]]): TagId {.inline, raises: [].} = proc yamlTag*[I](T: typedesc[seq[I]]): Tag {.inline, raises: [].} =
let uri = nimTag("system:seq(" & safeTagUri(yamlTag(I)) & ')') let uri = nimTag("system:seq(" & safeTagUri(yamlTag(I)) & ')')
result = lazyLoadTag(uri) result = lazyLoadTag(uri)
proc yamlTag*[I](T: typedesc[set[I]]): TagId {.inline, raises: [].} = proc yamlTag*[I](T: typedesc[set[I]]): Tag {.inline, raises: [].} =
let uri = nimTag("system:set(" & safeTagUri(yamlTag(I)) & ')') let uri = nimTag("system:set(" & safeTagUri(yamlTag(I)) & ')')
result = lazyLoadTag(uri) result = lazyLoadTag(uri)
@ -444,7 +435,7 @@ proc constructObject*[T](s: var YamlStream, c: ConstructionContext,
discard s.next() discard s.next()
proc representObject*[T](value: seq[T]|set[T], ts: TagStyle, proc representObject*[T](value: seq[T]|set[T], ts: TagStyle,
c: SerializationContext, tag: TagId) = c: SerializationContext, tag: Tag) =
## represents a Nim seq as YAML sequence ## represents a Nim seq as YAML sequence
let childTagStyle = if ts == tsRootOnly: tsNone else: ts let childTagStyle = if ts == tsRootOnly: tsNone else: ts
c.put(startSeqEvent(tag = tag)) c.put(startSeqEvent(tag = tag))
@ -452,7 +443,7 @@ proc representObject*[T](value: seq[T]|set[T], ts: TagStyle,
representChild(item, childTagStyle, c) representChild(item, childTagStyle, c)
c.put(endSeqEvent()) c.put(endSeqEvent())
proc yamlTag*[I, V](T: typedesc[array[I, V]]): TagId {.inline, raises: [].} = proc yamlTag*[I, V](T: typedesc[array[I, V]]): Tag {.inline, raises: [].} =
const rangeName = name(I) const rangeName = name(I)
let uri = nimTag("system:array(" & rangeName[6..rangeName.high()] & ';' & let uri = nimTag("system:array(" & rangeName[6..rangeName.high()] & ';' &
safeTagUri(yamlTag(V)) & ')') safeTagUri(yamlTag(V)) & ')')
@ -475,7 +466,7 @@ proc constructObject*[I, T](s: var YamlStream, c: ConstructionContext,
raise s.constructionError(event.startPos, "Too many array values") raise s.constructionError(event.startPos, "Too many array values")
proc representObject*[I, T](value: array[I, T], ts: TagStyle, proc representObject*[I, T](value: array[I, T], ts: TagStyle,
c: SerializationContext, tag: TagId) = c: SerializationContext, tag: Tag) =
## represents a Nim array as YAML sequence ## represents a Nim array as YAML sequence
let childTagStyle = if ts == tsRootOnly: tsNone else: ts let childTagStyle = if ts == tsRootOnly: tsNone else: ts
c.put(startSeqEvent(tag = tag)) c.put(startSeqEvent(tag = tag))
@ -483,7 +474,7 @@ proc representObject*[I, T](value: array[I, T], ts: TagStyle,
representChild(item, childTagStyle, c) representChild(item, childTagStyle, c)
c.put(endSeqEvent()) c.put(endSeqEvent())
proc yamlTag*[K, V](T: typedesc[Table[K, V]]): TagId {.inline, raises: [].} = proc yamlTag*[K, V](T: typedesc[Table[K, V]]): Tag {.inline, raises: [].} =
try: try:
let uri = nimTag("tables:Table(" & safeTagUri(yamlTag(K)) & ';' & let uri = nimTag("tables:Table(" & safeTagUri(yamlTag(K)) & ';' &
safeTagUri(yamlTag(V)) & ")") safeTagUri(yamlTag(V)) & ")")
@ -512,7 +503,7 @@ proc constructObject*[K, V](s: var YamlStream, c: ConstructionContext,
discard s.next() discard s.next()
proc representObject*[K, V](value: Table[K, V], ts: TagStyle, proc representObject*[K, V](value: Table[K, V], ts: TagStyle,
c: SerializationContext, tag: TagId) = c: SerializationContext, tag: Tag) =
## represents a Nim Table as YAML mapping ## represents a Nim Table as YAML mapping
let childTagStyle = if ts == tsRootOnly: tsNone else: ts let childTagStyle = if ts == tsRootOnly: tsNone else: ts
c.put(startMapEvent(tag = tag)) c.put(startMapEvent(tag = tag))
@ -521,7 +512,7 @@ proc representObject*[K, V](value: Table[K, V], ts: TagStyle,
representChild(value, childTagStyle, c) representChild(value, childTagStyle, c)
c.put(endMapEvent()) c.put(endMapEvent())
proc yamlTag*[K, V](T: typedesc[OrderedTable[K, V]]): TagId proc yamlTag*[K, V](T: typedesc[OrderedTable[K, V]]): Tag
{.inline, raises: [].} = {.inline, raises: [].} =
try: try:
let uri = nimTag("tables:OrderedTable(" & safeTagUri(yamlTag(K)) & ';' & let uri = nimTag("tables:OrderedTable(" & safeTagUri(yamlTag(K)) & ';' &
@ -557,7 +548,7 @@ proc constructObject*[K, V](s: var YamlStream, c: ConstructionContext,
discard s.next() discard s.next()
proc representObject*[K, V](value: OrderedTable[K, V], ts: TagStyle, proc representObject*[K, V](value: OrderedTable[K, V], ts: TagStyle,
c: SerializationContext, tag: TagId) = c: SerializationContext, tag: Tag) =
let childTagStyle = if ts == tsRootOnly: tsNone else: ts let childTagStyle = if ts == tsRootOnly: tsNone else: ts
c.put(startSeqEvent(tag = tag)) c.put(startSeqEvent(tag = tag))
for key, value in value.pairs: for key, value in value.pairs:
@ -568,13 +559,13 @@ proc representObject*[K, V](value: OrderedTable[K, V], ts: TagStyle,
c.put(endSeqEvent()) c.put(endSeqEvent())
proc yamlTag*(T: typedesc[object|enum]): proc yamlTag*(T: typedesc[object|enum]):
TagId {.inline, raises: [].} = Tag {.inline, raises: [].} =
var uri = nimTag("custom:" & (typetraits.name(type(T)))) var uri = nimTag("custom:" & (typetraits.name(type(T))))
try: serializationTagLibrary.tags[uri] try: serializationTagLibrary.tags[uri]
except KeyError: serializationTagLibrary.registerUri(uri) except KeyError: serializationTagLibrary.registerUri(uri)
proc yamlTag*(T: typedesc[tuple]): proc yamlTag*(T: typedesc[tuple]):
TagId {.inline, raises: [].} = Tag {.inline, raises: [].} =
var var
i: T i: T
uri = nimTag("tuple(") uri = nimTag("tuple(")
@ -999,7 +990,7 @@ macro genRepresentObject(t: typedesc, value, childTagStyle: typed) =
inc(fieldIndex) inc(fieldIndex)
proc representObject*[O: object](value: O, ts: TagStyle, proc representObject*[O: object](value: O, ts: TagStyle,
c: SerializationContext, tag: TagId) = c: SerializationContext, tag: Tag) =
## represents a Nim object or tuple as YAML mapping ## represents a Nim object or tuple as YAML mapping
let childTagStyle = if ts == tsRootOnly: tsNone else: ts let childTagStyle = if ts == tsRootOnly: tsNone else: ts
when isVariantObject(getType(O)): c.put(startSeqEvent(tag = tag)) when isVariantObject(getType(O)): c.put(startSeqEvent(tag = tag))
@ -1009,7 +1000,7 @@ proc representObject*[O: object](value: O, ts: TagStyle,
else: c.put(endMapEvent()) else: c.put(endMapEvent())
proc representObject*[O: tuple](value: O, ts: TagStyle, proc representObject*[O: tuple](value: O, ts: TagStyle,
c: SerializationContext, tag: TagId) = c: SerializationContext, tag: Tag) =
let childTagStyle = if ts == tsRootOnly: tsNone else: ts let childTagStyle = if ts == tsRootOnly: tsNone else: ts
var fieldIndex = 0'i16 var fieldIndex = 0'i16
c.put(startMapEvent(tag = tag)) c.put(startMapEvent(tag = tag))
@ -1035,13 +1026,13 @@ proc constructObject*[O: enum](s: var YamlStream, c: ConstructionContext,
raise ex raise ex
proc representObject*[O: enum](value: O, ts: TagStyle, proc representObject*[O: enum](value: O, ts: TagStyle,
c: SerializationContext, tag: TagId) {.raises: [].} = c: SerializationContext, tag: Tag) {.raises: [].} =
## represents a Nim enum as YAML scalar ## represents a Nim enum as YAML scalar
c.put(scalarEvent($value, tag, yAnchorNone)) c.put(scalarEvent($value, tag, yAnchorNone))
proc yamlTag*[O](T: typedesc[ref O]): TagId {.inline, raises: [].} = yamlTag(O) proc yamlTag*[O](T: typedesc[ref O]): Tag {.inline, raises: [].} = yamlTag(O)
macro constructImplicitVariantObject(s, m, c, r, possibleTagIds: untyped, macro constructImplicitVariantObject(s, m, c, r, possibleTags: untyped,
t: typedesc) = t: typedesc) =
let tDesc = getType(getType(t)[1]) let tDesc = getType(getType(t)[1])
yAssert tDesc.kind == nnkObjectTy yAssert tDesc.kind == nnkObjectTy
@ -1058,12 +1049,12 @@ macro constructImplicitVariantObject(s, m, c, r, possibleTagIds: untyped,
))) )))
case recCase[i][1].recListLen case recCase[i][1].recListLen
of 0: of 0:
branch.add(infix(newIdentNode("yTagNull"), "in", possibleTagIds)) branch.add(infix(newIdentNode("yTagNull"), "in", possibleTags))
branchContent.add(newNimNode(nnkDiscardStmt).add(newCall("next", s))) branchContent.add(newNimNode(nnkDiscardStmt).add(newCall("next", s)))
of 1: of 1:
let field = newDotExpr(r, newIdentNode($recCase[i][1].recListNode)) let field = newDotExpr(r, newIdentNode($recCase[i][1].recListNode))
branch.add(infix( branch.add(infix(
newCall("yamlTag", newCall("type", field)), "in", possibleTagIds)) newCall("yamlTag", newCall("type", field)), "in", possibleTags))
branchContent.add(newCall("constructChild", s, c, field)) branchContent.add(newCall("constructChild", s, c, field))
else: else:
block: block:
@ -1074,8 +1065,7 @@ macro constructImplicitVariantObject(s, m, c, r, possibleTagIds: untyped,
newCall(bindSym("constructionError"), s, m, newCall(bindSym("constructionError"), s, m,
infix(newStrLitNode("This value type does not map to any field in " & infix(newStrLitNode("This value type does not map to any field in " &
getTypeImpl(t)[1].repr & ": "), "&", getTypeImpl(t)[1].repr & ": "), "&",
newCall("uri", newIdentNode("serializationTagLibrary"), newCall("$", newNimNode(nnkBracketExpr).add(possibleTags, newIntLitNode(0)))
newNimNode(nnkBracketExpr).add(possibleTagIds, newIntLitNode(0)))
) )
)) ))
result.add(newNimNode(nnkElse).add(newNimNode(nnkTryStmt).add( result.add(newNimNode(nnkElse).add(newNimNode(nnkTryStmt).add(
@ -1111,45 +1101,45 @@ proc constructChild*[T](s: var YamlStream, c: ConstructionContext,
when isImplicitVariantObject(T): when isImplicitVariantObject(T):
when not canBeImplicit(T): when not canBeImplicit(T):
{. fatal: "This type cannot be marked as implicit" .} {. fatal: "This type cannot be marked as implicit" .}
var possibleTagIds = newSeq[TagId]() var possibleTags = newSeq[Tag]()
case item.kind case item.kind
of yamlScalar: of yamlScalar:
case item.scalarProperties.tag case item.scalarProperties.tag
of yTagQuestionMark: of yTagQuestionMark:
case guessType(item.scalarContent) case guessType(item.scalarContent)
of yTypeInteger: of yTypeInteger:
possibleTagIds.add([yamlTag(int), yamlTag(int8), yamlTag(int16), possibleTags.add([yamlTag(int), yamlTag(int8), yamlTag(int16),
yamlTag(int32), yamlTag(int64)]) yamlTag(int32), yamlTag(int64)])
if item.scalarContent[0] != '-': if item.scalarContent[0] != '-':
possibleTagIds.add([yamlTag(uint), yamlTag(uint8), yamlTag(uint16), possibleTags.add([yamlTag(uint), yamlTag(uint8), yamlTag(uint16),
yamlTag(uint32), yamlTag(uint64)]) yamlTag(uint32), yamlTag(uint64)])
of yTypeFloat, yTypeFloatInf, yTypeFloatNaN: of yTypeFloat, yTypeFloatInf, yTypeFloatNaN:
possibleTagIds.add([yamlTag(float), yamlTag(float32), possibleTags.add([yamlTag(float), yamlTag(float32),
yamlTag(float64)]) yamlTag(float64)])
of yTypeBoolTrue, yTypeBoolFalse: of yTypeBoolTrue, yTypeBoolFalse:
possibleTagIds.add(yamlTag(bool)) possibleTags.add(yamlTag(bool))
of yTypeNull: of yTypeNull:
raise s.constructionError(item.startPos, "not implemented!") raise s.constructionError(item.startPos, "not implemented!")
of yTypeUnknown: of yTypeUnknown:
possibleTagIds.add(yamlTag(string)) possibleTags.add(yamlTag(string))
of yTypeTimestamp: of yTypeTimestamp:
possibleTagIds.add(yamlTag(Time)) possibleTags.add(yamlTag(Time))
of yTagExclamationMark: of yTagExclamationMark:
possibleTagIds.add(yamlTag(string)) possibleTags.add(yamlTag(string))
else: else:
possibleTagIds.add(item.scalarProperties.tag) possibleTags.add(item.scalarProperties.tag)
of yamlStartMap: of yamlStartMap:
if item.mapProperties.tag in [yTagQuestionMark, yTagExclamationMark]: if item.mapProperties.tag in [yTagQuestionMark, yTagExclamationMark]:
raise s.constructionError(item.startPos, raise s.constructionError(item.startPos,
"Complex value of implicit variant object type must have a tag.") "Complex value of implicit variant object type must have a tag.")
possibleTagIds.add(item.mapProperties.tag) possibleTags.add(item.mapProperties.tag)
of yamlStartSeq: of yamlStartSeq:
if item.seqProperties.tag in [yTagQuestionMark, yTagExclamationMark]: if item.seqProperties.tag in [yTagQuestionMark, yTagExclamationMark]:
raise s.constructionError(item.startPos, raise s.constructionError(item.startPos,
"Complex value of implicit variant object type must have a tag.") "Complex value of implicit variant object type must have a tag.")
possibleTagIds.add(item.seqProperties.tag) possibleTags.add(item.seqProperties.tag)
else: internalError("Unexpected item kind: " & $item.kind) else: internalError("Unexpected item kind: " & $item.kind)
constructImplicitVariantObject(s, item.startPos, c, result, possibleTagIds, T) constructImplicitVariantObject(s, item.startPos, c, result, possibleTags, T)
else: else:
case item.kind case item.kind
of yamlScalar: of yamlScalar:
@ -1233,20 +1223,14 @@ proc constructChild*[O](s: var YamlStream, c: ConstructionContext,
discard s.next() discard s.next()
return return
elif e.kind == yamlAlias: elif e.kind == yamlAlias:
when defined(JS): result = cast[ref O](c.refs.getOrDefault(e.aliasTarget).p)
{.emit: [result, """ = """, c, """.refs.get(""", e.aliasTarget, """);"""].}
else:
result = cast[ref O](c.refs.getOrDefault(e.aliasTarget))
discard s.next() discard s.next()
return return
new(result) new(result)
template removeAnchor(anchor: var Anchor) {.dirty.} = template removeAnchor(anchor: var Anchor) {.dirty.} =
if anchor != yAnchorNone: if anchor != yAnchorNone:
when defined(JS):
{.emit: [c, """.refs.set(""", anchor, """, """, result, """);"""].}
else:
yAssert(not c.refs.hasKey(anchor)) yAssert(not c.refs.hasKey(anchor))
c.refs[anchor] = cast[pointer](result) c.refs[anchor] = (0, cast[pointer](result))
anchor = yAnchorNone anchor = yAnchorNone
case e.kind case e.kind
@ -1280,50 +1264,33 @@ proc representChild*[O](value: ref O, ts: TagStyle, c: SerializationContext) =
if isNil(value): c.put(scalarEvent("~", yTagNull)) if isNil(value): c.put(scalarEvent("~", yTagNull))
elif c.style == asNone: representChild(value[], ts, c) elif c.style == asNone: representChild(value[], ts, c)
else: else:
var val: Anchor var val: tuple[a: Anchor, referenced: bool]
when defined(JS):
{.emit: ["""
if (""", c, """.refs.has(""", value, """) {
""", val, """ = """, c, """.refs.get(""", value, """);
if (val == """, yAnchorNone, ") {"].}
val = c.nextAnchorId
{.emit: [c, """.refs.set(""", value, """, """, val, """);"""].}
nextAnchor(c, len(c.nextAnchorId) - 1)
{.emit: "}".}
c.put(aliasEvent(val))
return
else:
let p = cast[pointer](value) let p = cast[pointer](value)
if c.refs.hasKey(p): if c.refs.hasKey(p):
val = c.refs.getOrDefault(p) val = c.refs.getOrDefault(p)
if val == yAnchorNone: yAssert(val.a != yAnchorNone)
val = c.nextAnchorId.Anchor if not val.referenced:
c.refs[p] = (val.a, true)
c.put(aliasEvent(val.a))
return
if c.style != asNone:
val = (c.nextAnchorId.Anchor, false)
c.refs[p] = val c.refs[p] = val
nextAnchor(c.nextAnchorId, len(c.nextAnchorId) - 1) nextAnchor(c.nextAnchorId, len(c.nextAnchorId) - 1)
c.put(aliasEvent(val))
return
if c.style == asAlways:
val = c.nextAnchorId.Anchor
when defined(JS):
{.emit: [c, ".refs.set(", p, ", ", val, ");"].}
else: c.refs[p] = val
nextAnchor(c.nextAnchorId, len(c.nextAnchorId) - 1)
else: c.refs[p] = yAnchorNone
let let
a = if c.style == asAlways: val else: cast[Anchor](p)
childTagStyle = if ts == tsAll: tsAll else: tsRootOnly childTagStyle = if ts == tsAll: tsAll else: tsRootOnly
origPut = c.put origPut = c.put
c.put = proc(e: Event) = c.put = proc(e: Event) =
var ex = e var ex = e
case ex.kind case ex.kind
of yamlStartMap: of yamlStartMap:
ex.mapProperties.anchor = a ex.mapProperties.anchor = val.a
if ts == tsNone: ex.mapProperties.tag = yTagQuestionMark if ts == tsNone: ex.mapProperties.tag = yTagQuestionMark
of yamlStartSeq: of yamlStartSeq:
ex.seqProperties.anchor = a ex.seqProperties.anchor = val.a
if ts == tsNone: ex.seqProperties.tag = yTagQuestionMark if ts == tsNone: ex.seqProperties.tag = yTagQuestionMark
of yamlScalar: of yamlScalar:
ex.scalarProperties.anchor = a ex.scalarProperties.anchor = val.a
if ts == tsNone and guessType(ex.scalarContent) != yTypeNull: if ts == tsNone and guessType(ex.scalarContent) != yTypeNull:
ex.scalarProperties.tag = yTagQuestionMark ex.scalarProperties.tag = yTagQuestionMark
else: discard else: discard
@ -1401,6 +1368,10 @@ proc load*[K](input: Stream | string, target: var K)
elif e.parent of YamlParserError: raise (ref YamlParserError)(e.parent) elif e.parent of YamlParserError: raise (ref YamlParserError)(e.parent)
else: internalError("Unexpected exception: " & $e.parent.name) else: internalError("Unexpected exception: " & $e.parent.name)
proc loadAs*[K](input: string): K {.raises: [YamlConstructionError, IOError, YamlParserError].} =
## Loads the given YAML input to a value of the type K and returns it
load(input, result)
proc loadMultiDoc*[K](input: Stream | string, target: var seq[K]) = proc loadMultiDoc*[K](input: Stream | string, target: var seq[K]) =
var var
parser = initYamlParser(serializationTagLibrary) parser = initYamlParser(serializationTagLibrary)
@ -1427,10 +1398,10 @@ proc loadMultiDoc*[K](input: Stream | string, target: var seq[K]) =
proc setAnchor(a: var Anchor, c: var SerializationContext) proc setAnchor(a: var Anchor, c: var SerializationContext)
{.inline.} = {.inline.} =
if a != yAnchorNone: if a != yAnchorNone:
when defined(JS): for key, val in c.refs:
{.emit: [a, """ = """, c, """.refs.get(""", a, """);"""].} if val.a == a:
else: if not val.referenced: a = yAnchorNone
a = c.refs.getOrDefault(cast[pointer](a)) return
proc represent*[T](value: T, ts: TagStyle = tsRootOnly, proc represent*[T](value: T, ts: TagStyle = tsRootOnly,
a: AnchorStyle = asTidy): YamlStream = a: AnchorStyle = asTidy): YamlStream =

View File

@ -28,29 +28,19 @@ type
## them with `initFailsafeTagLibrary <#initFailsafeTagLibrary>`_, ## them with `initFailsafeTagLibrary <#initFailsafeTagLibrary>`_,
## `initCoreTagLibrary <#initCoreTagLibrary>`_ or ## `initCoreTagLibrary <#initCoreTagLibrary>`_ or
## `initExtendedTagLibrary <#initExtendedTagLibrary>`_. ## `initExtendedTagLibrary <#initExtendedTagLibrary>`_.
tags*: Table[string, TagId] tags*: Table[string, Tag]
nextCustomTagId*: TagId
proc initTagLibrary*(): TagLibrary {.raises: [].} = proc initTagLibrary*(): TagLibrary {.raises: [].} =
## initializes the ``tags`` table and sets ``nextCustomTagId`` to ## initializes the ``tags`` table and sets ``nextCustomTagId`` to
## ``yFirstCustomTagId``. ## ``yFirstCustomTagId``.
new(result) new(result)
result.tags = initTable[string, TagId]() result.tags = initTable[string, Tag]()
result.nextCustomTagId = yFirstCustomTagId proc registerUri*(tagLib: TagLibrary, uri: string): Tag {.raises: [].} =
proc registerUri*(tagLib: TagLibrary, uri: string): TagId {.raises: [].} =
## registers a custom tag URI with a ``TagLibrary``. The URI will get ## registers a custom tag URI with a ``TagLibrary``. The URI will get
## the ``TagId`` ``nextCustomTagId``, which will be incremented. ## the ``TagId`` ``nextCustomTagId``, which will be incremented.
tagLib.tags[uri] = tagLib.nextCustomTagId result = uri.Tag
result = tagLib.nextCustomTagId tagLib.tags[uri] = result
tagLib.nextCustomTagId = cast[TagId](cast[int](tagLib.nextCustomTagId) + 1)
proc uri*(tagLib: TagLibrary, id: TagId): string {.raises: [KeyError].} =
## retrieve the URI a ``TagId`` maps to.
for iUri, iId in tagLib.tags.pairs:
if iId == id: return iUri
raise newException(KeyError, "Unknown tag id: " & $id)
template y(suffix: string): string = yamlTagRepositoryPrefix & suffix template y(suffix: string): string = yamlTagRepositoryPrefix & suffix
template n(suffix: string): string = nimyamlTagRepositoryPrefix & suffix template n(suffix: string): string = nimyamlTagRepositoryPrefix & suffix
@ -114,7 +104,7 @@ proc initSerializationTagLibrary*(): TagLibrary =
result.tags[n"field"] = yTagNimField result.tags[n"field"] = yTagNimField
var var
serializationTagLibrary* = initSerializationTagLibrary() ## \ serializationTagLibrary* {.compileTime.} = initSerializationTagLibrary() ## \
## contains all local tags that are used for type serialization. Does ## contains all local tags that are used for type serialization. Does
## not contain any of the specific default tags for sequences or maps, ## not contain any of the specific default tags for sequences or maps,
## as those are not suited for Nim's static type system. ## as those are not suited for Nim's static type system.
@ -123,8 +113,6 @@ var
## `serializable <#serializable,stmt,stmt>`_. ## `serializable <#serializable,stmt,stmt>`_.
var var
nextStaticTagId {.compileTime.} = yFirstStaticTagId ## \
## used for generating unique TagIds with ``setTagUri``.
registeredUris {.compileTime.} = newSeq[string]() ## \ registeredUris {.compileTime.} = newSeq[string]() ## \
## Since Table doesn't really work at compile time, we also store ## Since Table doesn't really work at compile time, we also store
## registered URIs here to be able to generate a static compiler error ## registered URIs here to be able to generate a static compiler error
@ -135,14 +123,11 @@ template setTagUri*(t: typedesc, uri: string) =
## when loading and dumping values of this type. ## when loading and dumping values of this type.
when uri in registeredUris: when uri in registeredUris:
{. fatal: "[NimYAML] URI \"" & uri & "\" registered twice!" .} {. fatal: "[NimYAML] URI \"" & uri & "\" registered twice!" .}
const id {.genSym.} = nextStaticTagId const tag {.genSym.} = uri.Tag
static: static:
registeredUris.add(uri) registeredUris.add(uri)
nextStaticTagId = TagId(int(nextStaticTagId) + 1) serializationTagLibrary.tags[uri] = tag
when nextStaticTagId == yFirstCustomTagId: proc yamlTag*(T: typedesc[t]): Tag {.inline, raises: [].} = tag
{.fatal: "Too many tags!".}
serializationTagLibrary.tags[uri] = id
proc yamlTag*(T: typedesc[t]): TagId {.inline, raises: [].} = id
## autogenerated ## autogenerated
template setTagUri*(t: typedesc, uri: string, idName: untyped) = template setTagUri*(t: typedesc, uri: string, idName: untyped) =
@ -151,14 +136,11 @@ template setTagUri*(t: typedesc, uri: string, idName: untyped) =
## necessary if you want to implement serialization / construction yourself. ## necessary if you want to implement serialization / construction yourself.
when uri in registeredUris: when uri in registeredUris:
{. fatal: "[NimYAML] URI \"" & uri & "\" registered twice!" .} {. fatal: "[NimYAML] URI \"" & uri & "\" registered twice!" .}
const idName* = nextStaticTagId const idName* = uri.Tag
static: static:
registeredUris.add(uri) registeredUris.add(uri)
nextStaticTagId = TagId(int(nextStaticTagId) + 1)
when nextStaticTagId == yFirstCustomTagId:
{.fatal: "Too many tags!".}
serializationTagLibrary.tags[uri] = idName serializationTagLibrary.tags[uri] = idName
proc yamlTag*(T: typedesc[t]): TagId {.inline, raises: [].} = idName proc yamlTag*(T: typedesc[t]): Tag {.inline, raises: [].} = idName
## autogenerated ## autogenerated
static: static:

View File

@ -21,7 +21,7 @@ type Level = tuple[node: JsonNode, key: string, expKey: bool]
proc initLevel(node: JsonNode): Level {.raises: [].} = proc initLevel(node: JsonNode): Level {.raises: [].} =
(node: node, key: "", expKey: true) (node: node, key: "", expKey: true)
proc jsonFromScalar(content: string, tag: TagId): JsonNode proc jsonFromScalar(content: string, tag: Tag): JsonNode
{.raises: [YamlConstructionError].}= {.raises: [YamlConstructionError].}=
new(result) new(result)
var mappedType: TypeHint var mappedType: TypeHint