2016-02-27 12:09:50 +00:00
|
|
|
# NimYAML - YAML implementation in Nim
|
2020-06-26 21:21:22 +00:00
|
|
|
# (c) Copyright 2016 - 2020 Felix Krause
|
2016-02-27 12:09:50 +00:00
|
|
|
#
|
|
|
|
# See the file "copying.txt", included in this
|
|
|
|
# distribution, for details about the copyright.
|
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
## ==================
|
|
|
|
## Module yaml/native
|
|
|
|
## ==================
|
2016-09-21 19:20:57 +00:00
|
|
|
##
|
2023-08-29 21:23:15 +00:00
|
|
|
## This module transforms native Nim values into a stream of YAML events,
|
|
|
|
## and vice versa. The procs of this module must be available for name binding
|
|
|
|
## when using the loading and dumping APIs. A NimYAML consumer would rarely
|
|
|
|
## call this module's procs directly. The main entry points to this API are
|
|
|
|
## ``construct`` and ``represent``; all other procs are usually called via
|
|
|
|
## instantiations of those two procs.
|
2016-09-21 19:20:57 +00:00
|
|
|
##
|
2023-08-29 21:23:15 +00:00
|
|
|
## You can extend the procs defined here with own procs to define custom
|
|
|
|
## handling for native types. See the documentation on the NimYAML
|
|
|
|
## website for more information.
|
2016-09-21 19:20:57 +00:00
|
|
|
|
2022-09-07 14:23:50 +00:00
|
|
|
import std / [tables, typetraits, strutils, macros, streams, times, parseutils, options]
|
2023-08-29 21:23:15 +00:00
|
|
|
import data, taglib, stream, private/internal, hints, annotations
|
2020-11-05 19:23:42 +00:00
|
|
|
export data, stream, macros, annotations, options
|
2016-10-08 21:35:33 +00:00
|
|
|
# *something* in here needs externally visible `==`(x,y: AnchorId),
|
|
|
|
# but I cannot figure out what. binding it would be the better option.
|
2016-09-20 19:53:38 +00:00
|
|
|
|
|
|
|
type
|
2023-08-29 18:46:26 +00:00
|
|
|
TagStyle* = enum
|
|
|
|
## Whether object should be serialized with explicit tags.
|
|
|
|
##
|
|
|
|
## - ``tsNone``: No tags will be outputted unless necessary.
|
|
|
|
## - ``tsRootOnly``: A tag will only be outputted for the root tag and
|
|
|
|
## where necessary.
|
|
|
|
## - ``tsAll``: Tags will be outputted for every object.
|
|
|
|
tsNone, tsRootOnly, tsAll
|
|
|
|
|
|
|
|
AnchorStyle* = enum
|
|
|
|
## How ref object should be serialized.
|
|
|
|
##
|
|
|
|
## - ``asNone``: No anchors will be written. Values present at
|
|
|
|
## multiple places in the content that is serialized will be
|
|
|
|
## duplicated at every occurrence. If the content is cyclic, this
|
|
|
|
## will raise a YamlSerializationError.
|
|
|
|
## - ``asTidy``: Anchors will only be generated for objects that
|
|
|
|
## actually occur more than once in the content to be serialized.
|
|
|
|
## This is a bit slower and needs more memory than ``asAlways``.
|
|
|
|
## - ``asAlways``: Achors will be generated for every ref object in the
|
|
|
|
## content that is serialized, regardless of whether the object is
|
|
|
|
## referenced again afterwards.
|
|
|
|
asNone, asTidy, asAlways
|
|
|
|
|
|
|
|
SerializationOptions* = object
|
|
|
|
tagStyle* : TagStyle = tsNone
|
|
|
|
anchorStyle*: AnchorStyle = asTidy
|
|
|
|
handles* : seq[tuple[handle, uriPrefix: string]]
|
|
|
|
|
|
|
|
SerializationContext* = object
|
2016-09-20 19:53:38 +00:00
|
|
|
## Context information for the process of serializing YAML from Nim values.
|
2023-08-29 18:46:26 +00:00
|
|
|
refs: Table[pointer, tuple[a: Anchor, referenced: bool]]
|
|
|
|
emitTag: bool
|
|
|
|
nextAnchorId: string
|
|
|
|
options*: SerializationOptions
|
|
|
|
putImpl*: proc(ctx: var SerializationContext, e: Event) {.raises: [], closure.}
|
2016-09-20 19:53:38 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
ConstructionContext* = object
|
2016-09-20 19:53:38 +00:00
|
|
|
## Context information for the process of constructing Nim values from YAML.
|
2023-08-29 21:23:15 +00:00
|
|
|
input*: YamlStream
|
|
|
|
refs* : Table[Anchor, tuple[tag: Tag, p: pointer]]
|
2016-09-20 19:53:38 +00:00
|
|
|
|
|
|
|
YamlConstructionError* = object of YamlLoadingError
|
|
|
|
## Exception that may be raised when constructing data objects from a
|
|
|
|
## `YamlStream <#YamlStream>`_. The fields ``line``, ``column`` and
|
|
|
|
## ``lineContent`` are only available if the costructing proc also does
|
|
|
|
## parsing, because otherwise this information is not available to the
|
|
|
|
## costruction proc.
|
|
|
|
|
2021-09-06 10:37:19 +00:00
|
|
|
YamlSerializationError* = object of ValueError
|
|
|
|
## Exception that may be raised when serializing Nim values into YAML
|
|
|
|
## stream events.
|
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc put*(ctx: var SerializationContext, e: Event) {.raises: [].} =
|
|
|
|
ctx.putImpl(ctx, e)
|
|
|
|
|
2016-09-20 19:53:38 +00:00
|
|
|
# forward declares
|
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructChild*[T](
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var T,
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].}
|
2016-09-20 19:53:38 +00:00
|
|
|
## Constructs an arbitrary Nim value from a part of a YAML stream.
|
|
|
|
## The stream will advance until after the finishing token that was used
|
|
|
|
## for constructing the value. The ``ConstructionContext`` is needed for
|
|
|
|
## potential child objects which may be refs.
|
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructChild*(
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var string,
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].}
|
2016-09-20 19:53:38 +00:00
|
|
|
## Constructs a Nim value that is a string from a part of a YAML stream.
|
|
|
|
## This specialization takes care of possible nil strings.
|
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructChild*[T](
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var seq[T],
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].}
|
2016-09-20 19:53:38 +00:00
|
|
|
## Constructs a Nim value that is a string from a part of a YAML stream.
|
|
|
|
## This specialization takes care of possible nil seqs.
|
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructChild*[O](
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var ref O,
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].}
|
2016-09-20 19:53:38 +00:00
|
|
|
## Constructs an arbitrary Nim value from a part of a YAML stream.
|
|
|
|
## The stream will advance until after the finishing token that was used
|
|
|
|
## for constructing the value. The object may be constructed from an alias
|
|
|
|
## node which will be resolved using the ``ConstructionContext``.
|
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc representChild*[O](
|
|
|
|
ctx : var SerializationContext,
|
|
|
|
value: ref O,
|
|
|
|
) {.raises: [YamlSerializationError].}
|
2016-09-20 19:53:38 +00:00
|
|
|
## Represents an arbitrary Nim reference value as YAML object. The object
|
|
|
|
## may be represented as alias node if it is already present in the
|
|
|
|
## ``SerializationContext``.
|
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc representChild*(
|
|
|
|
ctx : var SerializationContext,
|
|
|
|
value: string,
|
|
|
|
) {.inline, raises: [].}
|
2016-09-20 19:53:38 +00:00
|
|
|
## Represents a Nim string. Supports nil strings.
|
|
|
|
|
2023-08-30 17:56:04 +00:00
|
|
|
proc representChild*[O](
|
|
|
|
ctx: var SerializationContext,
|
|
|
|
value: O,
|
|
|
|
) {.raises: [YamlSerializationError].}
|
2016-09-20 19:53:38 +00:00
|
|
|
## Represents an arbitrary Nim object as YAML object.
|
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc initConstructionContext*(input: YamlStream): ConstructionContext =
|
|
|
|
result = ConstructionContext(
|
|
|
|
input: input,
|
|
|
|
refs : initTable[Anchor, tuple[tag: Tag, p: pointer]](),
|
|
|
|
)
|
2016-01-28 21:29:26 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc initSerializationContext*(
|
|
|
|
options: SerializationOptions,
|
|
|
|
putImpl: proc(ctx: var SerializationContext, e: Event) {.raises: [], closure.}
|
|
|
|
): SerializationContext =
|
|
|
|
result = SerializationContext(
|
|
|
|
refs: initTable[pointer, tuple[a: Anchor, referenced: bool]](),
|
|
|
|
emitTag: options.tagStyle != tsNone,
|
|
|
|
nextAnchorId: "a",
|
|
|
|
options: options,
|
|
|
|
putImpl: putImpl
|
|
|
|
)
|
|
|
|
|
|
|
|
proc presentTag*(ctx: var SerializationContext, t: typedesc): Tag {.inline.} =
|
2020-11-10 12:55:22 +00:00
|
|
|
## Get the Tag that represents the given type in the given style
|
2023-08-29 18:46:26 +00:00
|
|
|
if ctx.emitTag:
|
|
|
|
result = yamlTag(t)
|
|
|
|
if ctx.options.tagStyle == tsRootOnly: ctx.emitTag = false
|
|
|
|
else:
|
|
|
|
result = yTagQuestionMark
|
2016-01-04 20:46:33 +00:00
|
|
|
|
2020-11-10 12:55:22 +00:00
|
|
|
proc safeTagUri(tag: Tag): string {.raises: [].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
try:
|
2020-11-10 12:55:22 +00:00
|
|
|
var uri = $tag
|
2016-10-10 18:16:54 +00:00
|
|
|
# '!' is not allowed inside a tag handle
|
|
|
|
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
|
|
|
|
# indicator
|
2020-11-10 12:55:22 +00:00
|
|
|
for i in countup(0, uri.len - 1):
|
|
|
|
if uri[i] == ',': uri[i] = ';'
|
2016-10-10 18:16:54 +00:00
|
|
|
return uri
|
2020-11-10 12:55:22 +00:00
|
|
|
except KeyError:
|
|
|
|
internalError("Unexpected KeyError for Tag " & $tag)
|
2016-01-05 15:54:14 +00:00
|
|
|
|
2020-11-10 18:07:46 +00:00
|
|
|
proc newYamlConstructionError*(s: YamlStream, mark: Mark, msg: string): ref YamlConstructionError =
|
2016-09-23 13:42:24 +00:00
|
|
|
result = newException(YamlConstructionError, msg)
|
2020-11-05 19:23:42 +00:00
|
|
|
result.mark = mark
|
|
|
|
if not s.getLastTokenContext(result.lineContent):
|
2016-09-23 13:42:24 +00:00
|
|
|
result.lineContent = ""
|
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructionError*(s: YamlStream, mark: Mark, msg: string):
|
|
|
|
ref YamlConstructionError =
|
2020-11-10 18:07:46 +00:00
|
|
|
return newYamlConstructionError(s, mark, msg)
|
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
template constructScalarItem*(
|
|
|
|
s: var YamlStream,
|
|
|
|
i: untyped,
|
|
|
|
t: typedesc,
|
|
|
|
content: untyped,
|
|
|
|
) =
|
2016-04-02 15:48:22 +00:00
|
|
|
## Helper template for implementing ``constructObject`` for types that
|
|
|
|
## are constructed from a scalar. ``i`` is the identifier that holds
|
2020-11-03 21:08:21 +00:00
|
|
|
## the scalar as ``Event`` in the content. Exceptions raised in
|
2020-11-04 21:47:52 +00:00
|
|
|
## the content will be automatically caught and wrapped in
|
2016-04-02 15:48:22 +00:00
|
|
|
## ``YamlConstructionError``, which will then be raised.
|
2016-09-23 13:42:24 +00:00
|
|
|
bind constructionError
|
2016-07-08 09:35:31 +00:00
|
|
|
let i = s.next()
|
2016-04-02 15:48:22 +00:00
|
|
|
if i.kind != yamlScalar:
|
2020-11-06 15:21:58 +00:00
|
|
|
raise constructionError(s, i.startPos, "Expected scalar")
|
2016-04-02 15:48:22 +00:00
|
|
|
try: content
|
2019-06-11 19:30:14 +00:00
|
|
|
except YamlConstructionError as e: raise e
|
2023-03-18 12:54:45 +00:00
|
|
|
except CatchableError as e:
|
|
|
|
var ce = constructionError(s, i.startPos,
|
2016-11-10 09:12:32 +00:00
|
|
|
"Cannot construct to " & name(t) & ": " & item.scalarContent &
|
2023-03-18 12:54:45 +00:00
|
|
|
"; error: " & e.msg)
|
|
|
|
ce.parent = e
|
|
|
|
raise ce
|
2016-01-24 19:38:30 +00:00
|
|
|
|
2020-11-10 12:55:22 +00:00
|
|
|
proc yamlTag*(T: typedesc[string]): Tag {.inline, noSideEffect, raises: [].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
yTagString
|
2016-01-24 19:38:30 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructObject*(
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var string,
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].} =
|
|
|
|
## constructs a string from a YAML scalar
|
|
|
|
ctx.input.constructScalarItem(item, string):
|
2016-04-02 15:48:22 +00:00
|
|
|
result = item.scalarContent
|
2015-12-29 14:09:37 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc representObject*(
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var SerializationContext,
|
2023-08-29 18:46:26 +00:00
|
|
|
value: string,
|
2023-08-29 21:23:15 +00:00
|
|
|
tag : Tag,
|
2023-08-29 18:46:26 +00:00
|
|
|
) {.raises: [].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## represents a string as YAML scalar
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(scalarEvent(value, tag, yAnchorNone))
|
2016-01-04 20:46:33 +00:00
|
|
|
|
2016-09-23 13:42:24 +00:00
|
|
|
proc parseHex[T: int8|int16|int32|int64|uint8|uint16|uint32|uint64](
|
2023-08-29 21:23:15 +00:00
|
|
|
s: YamlStream, mark: Mark, val: string
|
|
|
|
): T =
|
2016-09-19 18:51:50 +00:00
|
|
|
result = 0
|
2016-09-23 13:42:24 +00:00
|
|
|
for i in 2..<val.len:
|
|
|
|
case val[i]
|
2016-09-19 18:51:50 +00:00
|
|
|
of '_': discard
|
2016-09-23 13:42:24 +00:00
|
|
|
of '0'..'9': result = result shl 4 or T(ord(val[i]) - ord('0'))
|
|
|
|
of 'a'..'f': result = result shl 4 or T(ord(val[i]) - ord('a') + 10)
|
|
|
|
of 'A'..'F': result = result shl 4 or T(ord(val[i]) - ord('A') + 10)
|
|
|
|
else:
|
2020-11-05 19:23:42 +00:00
|
|
|
raise s.constructionError(mark, "Invalid character in hex: " &
|
2016-09-23 13:42:24 +00:00
|
|
|
escape("" & val[i]))
|
|
|
|
|
|
|
|
proc parseOctal[T: int8|int16|int32|int64|uint8|uint16|uint32|uint64](
|
2023-08-29 21:23:15 +00:00
|
|
|
s: YamlStream, mark: Mark, val: string
|
|
|
|
): T =
|
2016-09-23 13:42:24 +00:00
|
|
|
for i in 2..<val.len:
|
|
|
|
case val[i]
|
2016-09-19 18:51:50 +00:00
|
|
|
of '_': discard
|
2016-09-23 13:42:24 +00:00
|
|
|
of '0'..'7': result = result shl 3 + T((ord(val[i]) - ord('0')))
|
|
|
|
else:
|
2020-11-05 19:23:42 +00:00
|
|
|
raise s.constructionError(mark, "Invalid character in hex: " &
|
2016-09-23 13:42:24 +00:00
|
|
|
escape("" & val[i]))
|
2016-09-19 18:51:50 +00:00
|
|
|
|
2022-09-07 14:50:45 +00:00
|
|
|
type NumberStyle = enum
|
|
|
|
nsHex
|
|
|
|
nsOctal
|
|
|
|
nsDecimal
|
|
|
|
|
|
|
|
proc numberStyle(item: Event): NumberStyle =
|
|
|
|
if item.scalarContent[0] == '0' and item.scalarContent.len > 1:
|
|
|
|
if item.scalarContent[1] in {'x', 'X' }: return nsHex
|
|
|
|
if item.scalarContent[1] in {'o', 'O'}: return nsOctal
|
|
|
|
return nsDecimal
|
|
|
|
|
2016-01-28 21:29:26 +00:00
|
|
|
proc constructObject*[T: int8|int16|int32|int64](
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var T,
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## constructs an integer value from a YAML scalar
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx.input.constructScalarItem(item, T):
|
2022-09-07 14:50:45 +00:00
|
|
|
case item.numberStyle
|
2023-08-29 21:23:15 +00:00
|
|
|
of nsHex: result = parseHex[T](ctx.input, item.startPos, item.scalarContent)
|
|
|
|
of nsOctal: result = parseOctal[T](ctx.input, item.startPos, item.scalarContent)
|
2022-09-07 14:50:45 +00:00
|
|
|
of nsDecimal:
|
2018-10-03 18:00:12 +00:00
|
|
|
let nInt = parseBiggestInt(item.scalarContent)
|
|
|
|
if nInt <= T.high:
|
|
|
|
# make sure we don't produce a range error
|
|
|
|
result = T(nInt)
|
|
|
|
else:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(
|
|
|
|
item.startPos,
|
|
|
|
"Cannot construct int; out of range: " &
|
|
|
|
$nInt & " for type " & T.name & " with max of: " & $T.high
|
|
|
|
)
|
|
|
|
|
|
|
|
proc constructObject*(
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var int,
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError], inline.} =
|
2016-04-04 18:44:16 +00:00
|
|
|
## constructs an integer of architecture-defined length by loading it into
|
|
|
|
## int32 and then converting it.
|
|
|
|
var i32Result: int32
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx.constructObject(i32Result)
|
2016-04-04 18:44:16 +00:00
|
|
|
result = int(i32Result)
|
2016-01-04 20:46:33 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc representObject*[T: int8|int16|int32|int64](
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var SerializationContext,
|
2023-08-29 18:46:26 +00:00
|
|
|
value: T,
|
2023-08-29 21:23:15 +00:00
|
|
|
tag : Tag,
|
2023-08-29 18:46:26 +00:00
|
|
|
) {.raises: [].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## represents an integer value as YAML scalar
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(scalarEvent($value, tag, yAnchorNone))
|
2016-01-26 19:00:28 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc representObject*(
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var SerializationContext,
|
2023-08-29 18:46:26 +00:00
|
|
|
value: int,
|
2023-08-29 21:23:15 +00:00
|
|
|
tag : Tag,
|
2023-08-29 18:46:26 +00:00
|
|
|
) {.raises: [YamlSerializationError], inline.}=
|
2016-04-04 18:44:16 +00:00
|
|
|
## represent an integer of architecture-defined length by casting it to int32.
|
2020-11-03 21:08:21 +00:00
|
|
|
## on 64-bit systems, this may cause a RangeDefect.
|
2016-07-08 09:35:31 +00:00
|
|
|
|
2016-04-04 18:44:16 +00:00
|
|
|
# currently, sizeof(int) is at least sizeof(int32).
|
2023-08-29 18:46:26 +00:00
|
|
|
try: ctx.put(scalarEvent($int32(value), tag, yAnchorNone))
|
2023-03-18 12:54:45 +00:00
|
|
|
except RangeDefect as rd:
|
|
|
|
var e = newException(YamlSerializationError, rd.msg)
|
|
|
|
e.parent = rd
|
2016-09-01 18:56:34 +00:00
|
|
|
raise e
|
2016-01-26 19:00:28 +00:00
|
|
|
|
2017-02-13 16:10:56 +00:00
|
|
|
when defined(JS):
|
|
|
|
type DefiniteUIntTypes = uint8 | uint16 | uint32
|
|
|
|
else:
|
|
|
|
type DefiniteUIntTypes = uint8 | uint16 | uint32 | uint64
|
|
|
|
|
|
|
|
proc constructObject*[T: DefiniteUIntTypes](
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var T,
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## construct an unsigned integer value from a YAML scalar
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx.input.constructScalarItem(item, T):
|
2022-09-07 14:50:45 +00:00
|
|
|
case item.numberStyle
|
2023-08-29 21:23:15 +00:00
|
|
|
of nsHex: result = parseHex[T](ctx.input, item.startPos, item.scalarContent)
|
|
|
|
of nsOctal: result = parseOctal[T](ctx.input, item.startPos, item.scalarContent)
|
2022-09-07 14:50:45 +00:00
|
|
|
else:
|
|
|
|
let nUInt = parseBiggestUInt(item.scalarContent)
|
|
|
|
if nUInt <= T.high:
|
|
|
|
# make sure we don't produce a range error
|
|
|
|
result = T(nUInt)
|
|
|
|
else:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(
|
|
|
|
item.startPos,
|
|
|
|
"Cannot construct uint; out of range: " &
|
|
|
|
$nUInt & " for type " & T.name & " with max of: " & $T.high
|
|
|
|
)
|
|
|
|
|
|
|
|
proc constructObject*(
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var uint,
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError], inline.} =
|
2016-04-04 18:44:16 +00:00
|
|
|
## represent an unsigned integer of architecture-defined length by loading it
|
|
|
|
## into uint32 and then converting it.
|
|
|
|
var u32Result: uint32
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx.constructObject(u32Result)
|
|
|
|
result = uint(u32Result)
|
2016-01-24 19:38:30 +00:00
|
|
|
|
2016-09-01 19:08:27 +00:00
|
|
|
when defined(JS):
|
|
|
|
# TODO: this is a dirty hack and may lead to overflows!
|
|
|
|
proc `$`(x: uint8|uint16|uint32|uint64|uint): string =
|
|
|
|
result = $BiggestInt(x)
|
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc representObject*[T: uint8|uint16|uint32|uint64](
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var SerializationContext,
|
2023-08-29 18:46:26 +00:00
|
|
|
value: T,
|
2023-08-29 21:23:15 +00:00
|
|
|
tag : Tag,
|
2023-08-29 18:46:26 +00:00
|
|
|
) {.raises: [].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## represents an unsigned integer value as YAML scalar
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(scalarEvent($value, tag, yAnchorNone))
|
2016-01-04 20:46:33 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc representObject*(
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var SerializationContext,
|
2023-08-29 18:46:26 +00:00
|
|
|
value: uint,
|
2023-08-29 21:23:15 +00:00
|
|
|
tag : Tag,
|
2023-08-29 18:46:26 +00:00
|
|
|
) {.raises: [YamlSerializationError], inline.} =
|
2016-04-04 18:44:16 +00:00
|
|
|
## represent an unsigned integer of architecture-defined length by casting it
|
2020-11-03 21:08:21 +00:00
|
|
|
## to int32. on 64-bit systems, this may cause a RangeDefect.
|
2023-08-29 18:46:26 +00:00
|
|
|
try: ctx.put(scalarEvent($uint32(value), tag, yAnchorNone))
|
2023-03-18 12:54:45 +00:00
|
|
|
except RangeDefect as rd:
|
|
|
|
var e = newException(YamlSerializationError, rd.msg)
|
|
|
|
e.parent = rd
|
2016-09-01 18:56:34 +00:00
|
|
|
raise e
|
2015-12-29 15:10:47 +00:00
|
|
|
|
2016-04-04 18:44:16 +00:00
|
|
|
proc constructObject*[T: float|float32|float64](
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var T,
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## construct a float value from a YAML scalar
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx.input.constructScalarItem(item, T):
|
2016-04-02 15:48:22 +00:00
|
|
|
let hint = guessType(item.scalarContent)
|
|
|
|
case hint
|
2016-06-26 10:41:06 +00:00
|
|
|
of yTypeFloat:
|
2022-09-07 14:54:11 +00:00
|
|
|
var res: BiggestFloat
|
|
|
|
discard parseBiggestFloat(item.scalarContent, res)
|
|
|
|
result = res
|
2016-07-08 09:35:31 +00:00
|
|
|
of yTypeInteger:
|
2022-09-07 14:54:11 +00:00
|
|
|
var res: BiggestFloat
|
|
|
|
discard parseBiggestFloat(item.scalarContent, res)
|
|
|
|
result = res
|
2016-04-02 15:48:22 +00:00
|
|
|
of yTypeFloatInf:
|
|
|
|
if item.scalarContent[0] == '-': result = NegInf
|
|
|
|
else: result = Inf
|
|
|
|
of yTypeFloatNaN: result = NaN
|
|
|
|
else:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(
|
|
|
|
item.startPos,
|
|
|
|
"Cannot construct to float: " & escape(item.scalarContent)
|
|
|
|
)
|
2015-12-29 14:09:37 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc representObject*[T: float|float32|float64](
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var SerializationContext,
|
2023-08-29 18:46:26 +00:00
|
|
|
value: T,
|
2023-08-29 21:23:15 +00:00
|
|
|
tag : Tag,
|
2023-08-29 18:46:26 +00:00
|
|
|
) {.raises: [].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## represents a float value as YAML scalar
|
2016-09-01 18:56:34 +00:00
|
|
|
case value
|
2023-08-29 18:46:26 +00:00
|
|
|
of Inf: ctx.put(scalarEvent(".inf", tag))
|
|
|
|
of NegInf: ctx.put(scalarEvent("-.inf", tag))
|
|
|
|
of NaN: ctx.put(scalarEvent(".nan", tag))
|
|
|
|
else: ctx.put(scalarEvent($value, tag))
|
2016-01-26 19:00:28 +00:00
|
|
|
|
2020-11-10 12:55:22 +00:00
|
|
|
proc yamlTag*(T: typedesc[bool]): Tag {.inline, raises: [].} = yTagBoolean
|
2016-01-04 20:46:33 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructObject*(
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var bool,
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## constructs a bool value from a YAML scalar
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx.input.constructScalarItem(item, bool):
|
2016-04-02 15:48:22 +00:00
|
|
|
case guessType(item.scalarContent)
|
|
|
|
of yTypeBoolTrue: result = true
|
|
|
|
of yTypeBoolFalse: result = false
|
|
|
|
else:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(
|
|
|
|
item.startPos,
|
|
|
|
"Cannot construct to bool: " & escape(item.scalarContent)
|
|
|
|
)
|
2016-07-08 09:35:31 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc representObject*(
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var SerializationContext,
|
2023-08-29 18:46:26 +00:00
|
|
|
value: bool,
|
2023-08-29 21:23:15 +00:00
|
|
|
tag : Tag,
|
2023-08-29 18:46:26 +00:00
|
|
|
) {.raises: [].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## represents a bool value as a YAML scalar
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(scalarEvent(if value: "true" else: "false", tag, yAnchorNone))
|
2016-01-04 20:46:33 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructObject*(
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var char,
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## constructs a char value from a YAML scalar
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx.input.constructScalarItem(item, char):
|
2016-04-02 15:48:22 +00:00
|
|
|
if item.scalarContent.len != 1:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(
|
|
|
|
item.startPos,
|
|
|
|
"Cannot construct to char (length != 1): " & escape(item.scalarContent)
|
|
|
|
)
|
2016-04-02 15:48:22 +00:00
|
|
|
else: result = item.scalarContent[0]
|
2016-01-26 19:00:28 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc representObject*(
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var SerializationContext,
|
2023-08-29 18:46:26 +00:00
|
|
|
value: char,
|
2023-08-29 21:23:15 +00:00
|
|
|
tag : Tag
|
2023-08-29 18:46:26 +00:00
|
|
|
) {.raises: [].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## represents a char value as YAML scalar
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(scalarEvent("" & value, tag, yAnchorNone))
|
2016-01-26 19:00:28 +00:00
|
|
|
|
2020-11-10 12:55:22 +00:00
|
|
|
proc yamlTag*(T: typedesc[Time]): Tag {.inline, raises: [].} = yTagTimestamp
|
2016-11-08 20:13:01 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructObject*(
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var Time,
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].} =
|
|
|
|
ctx.input.constructScalarItem(item, Time):
|
2016-11-08 20:13:01 +00:00
|
|
|
if guessType(item.scalarContent) == yTypeTimestamp:
|
|
|
|
var
|
|
|
|
tmp = newStringOfCap(60)
|
|
|
|
pos = 8
|
|
|
|
c: char
|
|
|
|
while pos < item.scalarContent.len():
|
|
|
|
c = item.scalarContent[pos]
|
|
|
|
if c in {' ', '\t', 'T', 't'}: break
|
|
|
|
inc(pos)
|
|
|
|
if pos == item.scalarContent.len():
|
|
|
|
tmp.add(item.scalarContent)
|
|
|
|
tmp.add("T00:00:00+00:00")
|
|
|
|
else:
|
|
|
|
tmp.add(item.scalarContent[0 .. pos - 1])
|
|
|
|
if c in {' ', '\t'}:
|
|
|
|
while true:
|
|
|
|
inc(pos)
|
|
|
|
c = item.scalarContent[pos]
|
|
|
|
if c notin {' ', '\t'}: break
|
|
|
|
else: inc(pos)
|
|
|
|
tmp.add("T")
|
|
|
|
let timeStart = pos
|
|
|
|
inc(pos, 7)
|
|
|
|
var fractionStart = -1
|
|
|
|
while pos < item.scalarContent.len():
|
|
|
|
c = item.scalarContent[pos]
|
|
|
|
if c in {'+', '-', 'Z', ' ', '\t'}: break
|
|
|
|
elif c == '.': fractionStart = pos
|
|
|
|
inc(pos)
|
|
|
|
if fractionStart == -1:
|
|
|
|
tmp.add(item.scalarContent[timeStart .. pos - 1])
|
|
|
|
else:
|
|
|
|
tmp.add(item.scalarContent[timeStart .. fractionStart - 1])
|
|
|
|
if c in {'Z', ' ', '\t'}: tmp.add("+00:00")
|
|
|
|
else:
|
|
|
|
tmp.add(c)
|
|
|
|
inc(pos)
|
|
|
|
let tzStart = pos
|
|
|
|
inc(pos)
|
|
|
|
if pos < item.scalarContent.len() and item.scalarContent[pos] != ':':
|
|
|
|
inc(pos)
|
|
|
|
if pos - tzStart == 1: tmp.add('0')
|
|
|
|
tmp.add(item.scalarContent[tzStart .. pos - 1])
|
|
|
|
if pos == item.scalarContent.len(): tmp.add(":00")
|
|
|
|
elif pos + 2 == item.scalarContent.len():
|
|
|
|
tmp.add(":0")
|
|
|
|
tmp.add(item.scalarContent[pos + 1])
|
|
|
|
else:
|
|
|
|
tmp.add(item.scalarContent[pos .. pos + 2])
|
2018-10-03 17:43:27 +00:00
|
|
|
let info = tmp.parse("yyyy-M-d'T'H:mm:sszzz")
|
2016-11-08 20:13:01 +00:00
|
|
|
result = info.toTime()
|
|
|
|
else:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(
|
|
|
|
item.startPos,
|
|
|
|
"Not a parsable timestamp: " & escape(item.scalarContent)
|
|
|
|
)
|
2016-11-08 20:13:01 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc representObject*(
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var SerializationContext,
|
2023-08-29 18:46:26 +00:00
|
|
|
value: Time,
|
2023-08-29 21:23:15 +00:00
|
|
|
tag : Tag,
|
2023-08-29 18:46:26 +00:00
|
|
|
) {.raises: [].} =
|
2020-07-22 09:30:19 +00:00
|
|
|
let tmp = value.utc()
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(scalarEvent(tmp.format("yyyy-MM-dd'T'HH:mm:ss'Z'")))
|
2016-11-08 20:13:01 +00:00
|
|
|
|
2020-11-10 12:55:22 +00:00
|
|
|
proc yamlTag*[I](T: typedesc[seq[I]]): Tag {.inline, raises: [].} =
|
2021-03-23 17:51:05 +00:00
|
|
|
return nimTag("system:seq(" & safeTagUri(yamlTag(I)) & ')')
|
2016-04-02 16:29:26 +00:00
|
|
|
|
2020-11-10 12:55:22 +00:00
|
|
|
proc yamlTag*[I](T: typedesc[set[I]]): Tag {.inline, raises: [].} =
|
2021-03-23 17:51:05 +00:00
|
|
|
return nimTag("system:set(" & safeTagUri(yamlTag(I)) & ')')
|
2015-12-29 15:10:47 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructObject*[T](
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var seq[T],
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## constructs a Nim seq from a YAML sequence
|
2023-08-29 21:23:15 +00:00
|
|
|
let event = ctx.input.next()
|
2016-04-02 15:48:22 +00:00
|
|
|
if event.kind != yamlStartSeq:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(event.startPos, "Expected sequence start")
|
2016-04-02 15:48:22 +00:00
|
|
|
result = newSeq[T]()
|
2023-08-29 21:23:15 +00:00
|
|
|
while ctx.input.peek().kind != yamlEndSeq:
|
2016-04-02 15:48:22 +00:00
|
|
|
var item: T
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx.constructChild(item)
|
2021-05-17 21:51:35 +00:00
|
|
|
result.add(move(item))
|
2023-08-29 21:23:15 +00:00
|
|
|
discard ctx.input.next()
|
2015-12-29 14:09:37 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructObject*[T](
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var set[T],
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].} =
|
2016-04-02 16:29:26 +00:00
|
|
|
## constructs a Nim seq from a YAML sequence
|
2023-08-29 21:23:15 +00:00
|
|
|
let event = ctx.input.next()
|
2016-04-02 16:29:26 +00:00
|
|
|
if event.kind != yamlStartSeq:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(event.startPos, "Expected sequence start")
|
2016-04-02 16:29:26 +00:00
|
|
|
result = {}
|
2023-08-29 21:23:15 +00:00
|
|
|
while ctx.input.peek().kind != yamlEndSeq:
|
2016-04-02 16:29:26 +00:00
|
|
|
var item: T
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx.constructChild(item)
|
2016-04-02 16:29:26 +00:00
|
|
|
result.incl(item)
|
2023-08-29 21:23:15 +00:00
|
|
|
discard ctx.input.next()
|
2016-04-02 16:29:26 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc representObject*[T](
|
|
|
|
ctx : var SerializationContext,
|
|
|
|
value: seq[T]|set[T],
|
|
|
|
tag : Tag,
|
2023-08-30 17:56:04 +00:00
|
|
|
) {.raises: [YamlSerializationError].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## represents a Nim seq as YAML sequence
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(startSeqEvent(tag = tag))
|
|
|
|
for item in value: ctx.representChild(item)
|
|
|
|
ctx.put(endSeqEvent())
|
2015-12-29 15:10:47 +00:00
|
|
|
|
2020-11-10 12:55:22 +00:00
|
|
|
proc yamlTag*[I, V](T: typedesc[array[I, V]]): Tag {.inline, raises: [].} =
|
2016-04-03 09:29:49 +00:00
|
|
|
const rangeName = name(I)
|
2021-03-23 17:51:05 +00:00
|
|
|
return nimTag("system:array(" & rangeName[6..rangeName.high()] & ';' &
|
2016-10-10 18:16:54 +00:00
|
|
|
safeTagUri(yamlTag(V)) & ')')
|
2016-04-03 09:29:49 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructObject*[I, T](
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var array[I, T],
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].} =
|
2016-04-03 09:29:49 +00:00
|
|
|
## constructs a Nim array from a YAML sequence
|
2023-08-29 21:23:15 +00:00
|
|
|
var event = ctx.input.next()
|
2016-04-03 09:29:49 +00:00
|
|
|
if event.kind != yamlStartSeq:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(event.startPos, "Expected sequence start")
|
2016-04-03 09:29:49 +00:00
|
|
|
for index in low(I)..high(I):
|
2023-08-29 21:23:15 +00:00
|
|
|
event = ctx.input.peek()
|
2016-04-03 09:29:49 +00:00
|
|
|
if event.kind == yamlEndSeq:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(event.startPos, "Too few array values")
|
|
|
|
ctx.constructChild(result[index])
|
|
|
|
event = ctx.input.next()
|
2016-04-03 09:29:49 +00:00
|
|
|
if event.kind != yamlEndSeq:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(event.startPos, "Too many array values")
|
2016-04-03 09:29:49 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc representObject*[I, T](
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var SerializationContext,
|
2023-08-29 18:46:26 +00:00
|
|
|
value: array[I, T],
|
2023-08-29 21:23:15 +00:00
|
|
|
tag : Tag,
|
2023-08-30 17:56:04 +00:00
|
|
|
) {.raises: [YamlSerializationError].} =
|
2016-04-03 09:29:49 +00:00
|
|
|
## represents a Nim array as YAML sequence
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(startSeqEvent(tag = tag))
|
|
|
|
for item in value: ctx.representChild(item)
|
|
|
|
ctx.put(endSeqEvent())
|
2016-07-08 09:35:31 +00:00
|
|
|
|
2020-11-10 12:55:22 +00:00
|
|
|
proc yamlTag*[K, V](T: typedesc[Table[K, V]]): Tag {.inline, raises: [].} =
|
2021-03-23 17:51:05 +00:00
|
|
|
return nimTag("tables:Table(" & safeTagUri(yamlTag(K)) & ';' &
|
|
|
|
safeTagUri(yamlTag(V)) & ")")
|
2016-01-04 20:46:33 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructObject*[K, V](
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var Table[K, V],
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## constructs a Nim Table from a YAML mapping
|
2023-08-29 21:23:15 +00:00
|
|
|
let event = ctx.input.next()
|
2016-04-02 15:48:22 +00:00
|
|
|
if event.kind != yamlStartMap:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(
|
|
|
|
event.startPos, "Expected map start, got " & $event.kind
|
|
|
|
)
|
2016-04-02 15:48:22 +00:00
|
|
|
result = initTable[K, V]()
|
2023-08-29 21:23:15 +00:00
|
|
|
while ctx.input.peek.kind != yamlEndMap:
|
2016-04-02 15:48:22 +00:00
|
|
|
var
|
|
|
|
key: K
|
|
|
|
value: V
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx.constructChild(key)
|
|
|
|
ctx.constructChild(value)
|
2016-04-02 15:48:22 +00:00
|
|
|
if result.contains(key):
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(event.startPos, "Duplicate table key!")
|
2016-04-02 15:48:22 +00:00
|
|
|
result[key] = value
|
2023-08-29 21:23:15 +00:00
|
|
|
discard ctx.input.next()
|
2015-12-29 15:10:47 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc representObject*[K, V](
|
|
|
|
ctx : var SerializationContext,
|
|
|
|
value: Table[K, V],
|
|
|
|
tag : Tag,
|
2023-08-30 17:56:04 +00:00
|
|
|
) {.raises: [YamlSerializationError].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## represents a Nim Table as YAML mapping
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(startMapEvent(tag = tag))
|
2016-09-01 18:56:34 +00:00
|
|
|
for key, value in value.pairs:
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.representChild(key)
|
|
|
|
ctx.representChild(value)
|
|
|
|
ctx.put(endMapEvent())
|
2016-03-25 21:22:42 +00:00
|
|
|
|
2020-11-10 12:55:22 +00:00
|
|
|
proc yamlTag*[K, V](T: typedesc[OrderedTable[K, V]]): Tag
|
2016-04-02 15:48:22 +00:00
|
|
|
{.inline, raises: [].} =
|
2021-03-23 17:51:05 +00:00
|
|
|
return nimTag("tables:OrderedTable(" & safeTagUri(yamlTag(K)) & ';' &
|
|
|
|
safeTagUri(yamlTag(V)) & ")")
|
2016-03-25 21:22:42 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructObject*[K, V](
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var OrderedTable[K, V],
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## constructs a Nim OrderedTable from a YAML mapping
|
2023-08-29 21:23:15 +00:00
|
|
|
var event = ctx.input.next()
|
2016-04-02 15:48:22 +00:00
|
|
|
if event.kind != yamlStartSeq:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(
|
|
|
|
event.startPos, "Expected seq start, got " & $event.kind
|
|
|
|
)
|
2016-04-02 15:48:22 +00:00
|
|
|
result = initOrderedTable[K, V]()
|
2023-08-29 21:23:15 +00:00
|
|
|
while ctx.input.peek.kind != yamlEndSeq:
|
2016-04-02 15:48:22 +00:00
|
|
|
var
|
|
|
|
key: K
|
|
|
|
value: V
|
2023-08-29 21:23:15 +00:00
|
|
|
event = ctx.input.next()
|
2016-09-23 13:42:24 +00:00
|
|
|
if event.kind != yamlStartMap:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(
|
|
|
|
event.startPos, "Expected map start, got " & $event.kind
|
|
|
|
)
|
|
|
|
ctx.constructChild(key)
|
|
|
|
ctx.constructChild(value)
|
|
|
|
event = ctx.input.next()
|
2016-09-23 13:42:24 +00:00
|
|
|
if event.kind != yamlEndMap:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(
|
|
|
|
event.startPos, "Expected map end, got " & $event.kind
|
|
|
|
)
|
2016-04-02 15:48:22 +00:00
|
|
|
if result.contains(key):
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(event.startPos, "Duplicate table key!")
|
2021-05-17 21:51:35 +00:00
|
|
|
result[move(key)] = move(value)
|
2023-08-29 21:23:15 +00:00
|
|
|
discard ctx.input.next()
|
2016-03-25 21:22:42 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc representObject*[K, V](
|
|
|
|
ctx : var SerializationContext,
|
|
|
|
value: OrderedTable[K, V],
|
|
|
|
tag : Tag,
|
2023-08-30 17:56:04 +00:00
|
|
|
) {.raises: [YamlSerializationError].} =
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(startSeqEvent(tag = tag))
|
2016-09-01 18:56:34 +00:00
|
|
|
for key, value in value.pairs:
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(startMapEvent())
|
|
|
|
ctx.representChild(key)
|
|
|
|
ctx.representChild(value)
|
|
|
|
ctx.put(endMapEvent())
|
|
|
|
ctx.put(endSeqEvent())
|
2015-12-29 17:22:55 +00:00
|
|
|
|
2016-05-25 21:32:49 +00:00
|
|
|
proc yamlTag*(T: typedesc[object|enum]):
|
2020-11-10 12:55:22 +00:00
|
|
|
Tag {.inline, raises: [].} =
|
2021-03-23 17:51:05 +00:00
|
|
|
return nimTag("custom:" & (typetraits.name(type(T))))
|
2016-02-01 18:48:42 +00:00
|
|
|
|
2016-05-25 21:32:49 +00:00
|
|
|
proc yamlTag*(T: typedesc[tuple]):
|
2020-11-10 12:55:22 +00:00
|
|
|
Tag {.inline, raises: [].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
var
|
|
|
|
i: T
|
2021-03-23 17:51:05 +00:00
|
|
|
uri = nimyamlTagRepositoryPrefix & "tuple("
|
2016-04-02 15:48:22 +00:00
|
|
|
first = true
|
|
|
|
for name, value in fieldPairs(i):
|
|
|
|
if first: first = false
|
|
|
|
else: uri.add(",")
|
|
|
|
uri.add(safeTagUri(yamlTag(type(value))))
|
|
|
|
uri.add(")")
|
2021-03-23 17:51:05 +00:00
|
|
|
return Tag(uri)
|
2016-04-02 15:48:22 +00:00
|
|
|
|
2016-11-30 17:01:03 +00:00
|
|
|
iterator recListItems(n: NimNode): NimNode =
|
|
|
|
if n.kind == nnkRecList:
|
|
|
|
for item in n.children: yield item
|
|
|
|
else: yield n
|
|
|
|
|
|
|
|
proc recListLen(n: NimNode): int {.compileTime.} =
|
|
|
|
if n.kind == nnkRecList: result = n.len
|
|
|
|
else: result = 1
|
|
|
|
|
|
|
|
proc recListNode(n: NimNode): NimNode {.compileTime.} =
|
|
|
|
if n.kind == nnkRecList: result = n[0]
|
|
|
|
else: result = n
|
|
|
|
|
2023-04-04 17:29:20 +00:00
|
|
|
proc parentType(tDesc: NimNode): NimNode {.compileTime.} =
|
|
|
|
var name: NimNode
|
|
|
|
case tDesc[1].kind
|
|
|
|
of nnkEmpty: return nil
|
|
|
|
of nnkBracketExpr:
|
|
|
|
# happens when parent type is `ref X`
|
|
|
|
name = tDesc[1][1]
|
|
|
|
of nnkObjectTy, nnkSym:
|
|
|
|
name = tDesc[1]
|
|
|
|
else:
|
|
|
|
return nil
|
|
|
|
result = newNimNode(nnkBracketExpr)
|
|
|
|
result.add(bindSym("typeDesc"))
|
|
|
|
result.add(name)
|
|
|
|
|
2019-06-11 19:30:14 +00:00
|
|
|
proc fieldCount(t: NimNode): int {.compiletime.} =
|
2023-04-04 17:29:20 +00:00
|
|
|
result = 0
|
|
|
|
var tTypedesc: NimNode
|
|
|
|
if t.kind == nnkSym:
|
|
|
|
tTypedesc = getType(t)
|
|
|
|
else:
|
|
|
|
tTypedesc = t
|
2019-06-11 19:30:14 +00:00
|
|
|
|
2023-04-04 17:29:20 +00:00
|
|
|
let tDesc = getType(tTypedesc[1])
|
|
|
|
if tDesc.kind == nnkBracketExpr:
|
|
|
|
# tuple
|
|
|
|
result = tDesc.len - 1
|
|
|
|
else:
|
|
|
|
# object
|
|
|
|
let tParent = parentType(tDesc)
|
|
|
|
if tParent != nil:
|
|
|
|
# inherited fields
|
|
|
|
result += fieldCount(tParent)
|
|
|
|
for child in tDesc[2].children:
|
|
|
|
inc(result)
|
|
|
|
if child.kind == nnkRecCase:
|
|
|
|
for bIndex in 1..<len(child):
|
|
|
|
var increment = 0
|
|
|
|
case child[bIndex].kind
|
|
|
|
of nnkOfBranch:
|
|
|
|
let content = child[bIndex][len(child[bIndex])-1]
|
|
|
|
# We cannot assume that child[bIndex][1] is a RecList due to
|
|
|
|
# a one-liner like 'of akDog: barkometer' not resulting in a
|
|
|
|
# RecList but in an Ident node.
|
|
|
|
case content.kind
|
|
|
|
of nnkRecList:
|
|
|
|
increment = len(content)
|
|
|
|
else:
|
|
|
|
increment = 1
|
|
|
|
of nnkElse:
|
|
|
|
# Same goes for the else branch.
|
|
|
|
case child[bIndex][0].kind
|
|
|
|
of nnkRecList:
|
|
|
|
increment = len(child[bIndex][0])
|
|
|
|
else:
|
|
|
|
increment = 1
|
|
|
|
else:
|
|
|
|
internalError("Unexpected child kind: " & $child[bIndex].kind)
|
|
|
|
inc(result, increment)
|
2016-09-21 13:40:03 +00:00
|
|
|
|
|
|
|
macro matchMatrix(t: typedesc): untyped =
|
2016-09-22 12:16:10 +00:00
|
|
|
let numFields = fieldCount(t)
|
2023-03-18 13:50:38 +00:00
|
|
|
if numFields == 0:
|
|
|
|
result = quote do:
|
|
|
|
(seq[bool])(@[])
|
|
|
|
return
|
|
|
|
|
|
|
|
result = newNimNode(nnkBracket)
|
2016-09-22 12:16:10 +00:00
|
|
|
for i in 0..<numFields:
|
|
|
|
result.add(newLit(false))
|
2016-09-21 13:40:03 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc checkDuplicate(
|
|
|
|
s : NimNode,
|
|
|
|
tName : string,
|
|
|
|
name : string,
|
|
|
|
i : int,
|
|
|
|
matched: NimNode,
|
|
|
|
m : NimNode,
|
|
|
|
): NimNode {.compileTime.} =
|
2016-09-21 13:40:03 +00:00
|
|
|
result = newIfStmt((newNimNode(nnkBracketExpr).add(matched, newLit(i)),
|
2020-11-05 19:23:42 +00:00
|
|
|
newNimNode(nnkRaiseStmt).add(newCall(bindSym("constructionError"), s, m,
|
2016-09-24 14:45:49 +00:00
|
|
|
newLit("While constructing " & tName & ": Duplicate field: " &
|
|
|
|
escape(name))))))
|
2016-09-21 13:40:03 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc input(ctx: NimNode): NimNode {.compileTime.} =
|
|
|
|
return newDotExpr(ctx, ident("input"))
|
|
|
|
|
2020-06-26 21:21:22 +00:00
|
|
|
proc hasSparse(t: typedesc): bool {.compileTime.} =
|
|
|
|
when compiles(t.hasCustomPragma(sparse)):
|
|
|
|
return t.hasCustomPragma(sparse)
|
|
|
|
else:
|
|
|
|
return false
|
|
|
|
|
|
|
|
proc getOptionInner(fType: NimNode): NimNode {.compileTime.} =
|
|
|
|
if fType.kind == nnkBracketExpr and len(fType) == 2 and
|
|
|
|
fType[1].kind == nnkSym:
|
|
|
|
return newIdentNode($fType[1])
|
|
|
|
else: return nil
|
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc checkMissing(
|
|
|
|
s, t : NimNode,
|
|
|
|
tName : string,
|
|
|
|
field : NimNode,
|
|
|
|
i : int,
|
|
|
|
matched: NimNode,
|
|
|
|
o, m : NimNode,
|
|
|
|
):
|
2016-10-26 16:32:54 +00:00
|
|
|
NimNode {.compileTime.} =
|
2020-06-26 21:21:22 +00:00
|
|
|
let
|
|
|
|
fType = getTypeInst(field)
|
|
|
|
fName = escape($field)
|
|
|
|
optionInner = getOptionInner(fType)
|
2020-06-26 19:29:41 +00:00
|
|
|
result = quote do:
|
|
|
|
when not `o`.`field`.hasCustomPragma(transient):
|
|
|
|
if not `matched`[`i`]:
|
|
|
|
when `o`.`field`.hasCustomPragma(defaultVal):
|
|
|
|
`o`.`field` = `o`.`field`.getCustomPragmaVal(defaultVal)
|
2020-06-26 21:21:22 +00:00
|
|
|
elif hasSparse(`t`) and `o`.`field` is Option:
|
|
|
|
`o`.`field` = none(`optionInner`)
|
2020-06-26 19:29:41 +00:00
|
|
|
else:
|
2020-11-05 19:23:42 +00:00
|
|
|
raise constructionError(`s`, `m`, "While constructing " & `tName` &
|
2020-06-26 19:29:41 +00:00
|
|
|
": Missing field: " & `fName`)
|
2016-10-26 16:32:54 +00:00
|
|
|
|
|
|
|
proc markAsFound(i: int, matched: NimNode): NimNode {.compileTime.} =
|
|
|
|
newAssignment(newNimNode(nnkBracketExpr).add(matched, newLit(i)),
|
|
|
|
newLit(true))
|
2016-10-19 20:04:46 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc ifNotTransient(
|
|
|
|
o, field : NimNode,
|
|
|
|
content : openarray[NimNode],
|
|
|
|
elseError: bool,
|
|
|
|
s, m : NimNode,
|
|
|
|
tName: string = "",
|
|
|
|
fName: string = "",
|
|
|
|
):
|
2016-10-19 20:04:46 +00:00
|
|
|
NimNode {.compileTime.} =
|
|
|
|
var stmts = newStmtList(content)
|
|
|
|
if elseError:
|
2020-06-26 19:29:41 +00:00
|
|
|
result = quote do:
|
|
|
|
when `o`.`field`.hasCustomPragma(transient):
|
2020-11-06 15:21:58 +00:00
|
|
|
raise constructionError(`s`, `m`, "While constructing " & `tName` &
|
2020-06-26 19:29:41 +00:00
|
|
|
": Field \"" & `fName` & "\" is transient and may not occur in input")
|
|
|
|
else:
|
|
|
|
`stmts`
|
|
|
|
else:
|
|
|
|
result = quote do:
|
|
|
|
when not `o`.`field`.hasCustomPragma(transient):
|
|
|
|
`stmts`
|
2016-10-19 20:04:46 +00:00
|
|
|
|
2023-04-04 17:29:20 +00:00
|
|
|
proc recEnsureAllFieldsPresent(
|
2023-08-29 21:23:15 +00:00
|
|
|
s, tDecl, o: NimNode,
|
|
|
|
matched, m : NimNode,
|
|
|
|
tName: string,
|
|
|
|
field: var int,
|
|
|
|
stmt : NimNode,
|
|
|
|
) {.compileTime.} =
|
2023-04-04 17:29:20 +00:00
|
|
|
var
|
2016-09-24 14:45:49 +00:00
|
|
|
tDesc = getType(tDecl[1])
|
2023-04-04 17:29:20 +00:00
|
|
|
tParent = parentType(tDesc)
|
|
|
|
if tParent != nil:
|
|
|
|
recEnsureAllFieldsPresent(s, tParent, o, matched, m, tName, field, stmt)
|
2016-09-21 13:40:03 +00:00
|
|
|
for child in tDesc[2].children:
|
|
|
|
if child.kind == nnkRecCase:
|
2023-04-04 17:29:20 +00:00
|
|
|
stmt.add(checkMissing(
|
|
|
|
s, tDecl, tName, child[0], field, matched, o, m))
|
2016-09-21 13:40:03 +00:00
|
|
|
for bIndex in 1 .. len(child) - 1:
|
|
|
|
let discChecks = newStmtList()
|
2016-10-19 20:04:46 +00:00
|
|
|
var
|
|
|
|
curValues = newNimNode(nnkCurly)
|
|
|
|
recListIndex = 0
|
|
|
|
case child[bIndex].kind
|
|
|
|
of nnkOfBranch:
|
2016-11-30 17:01:03 +00:00
|
|
|
while recListIndex < child[bIndex].len - 1:
|
|
|
|
expectKind(child[bIndex][recListIndex], nnkIntLit)
|
2016-10-19 20:04:46 +00:00
|
|
|
curValues.add(child[bIndex][recListIndex])
|
|
|
|
inc(recListIndex)
|
|
|
|
of nnkElse: discard
|
|
|
|
else: internalError("Unexpected child kind: " & $child[bIndex].kind)
|
2016-11-30 17:01:03 +00:00
|
|
|
for item in child[bIndex][recListIndex].recListItems:
|
2016-09-22 12:16:10 +00:00
|
|
|
inc(field)
|
2020-06-26 21:21:22 +00:00
|
|
|
discChecks.add(checkMissing(
|
2023-04-04 17:29:20 +00:00
|
|
|
s, tDecl, tName, item, field, matched, o, m))
|
|
|
|
stmt.add(newIfStmt((infix(newDotExpr(o, newIdentNode($child[0])),
|
2020-06-26 19:29:41 +00:00
|
|
|
"in", curValues), discChecks)))
|
2016-09-21 13:40:03 +00:00
|
|
|
else:
|
2023-04-04 17:29:20 +00:00
|
|
|
stmt.add(checkMissing(s, tDecl, tName, child, field, matched, o, m))
|
2016-09-22 12:16:10 +00:00
|
|
|
inc(field)
|
2016-09-21 13:40:03 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
macro ensureAllFieldsPresent(
|
|
|
|
s : YamlStream,
|
|
|
|
t : typedesc,
|
|
|
|
o : typed,
|
|
|
|
matched: typed,
|
|
|
|
m : Mark,
|
|
|
|
) =
|
2023-04-04 17:29:20 +00:00
|
|
|
result = newStmtList()
|
|
|
|
let
|
|
|
|
tDecl = getType(t)
|
|
|
|
tName = $tDecl[1]
|
|
|
|
var field = 0
|
|
|
|
recEnsureAllFieldsPresent(s, tDecl, o, matched, m, tName, field, result)
|
|
|
|
|
2023-03-10 18:57:21 +00:00
|
|
|
proc skipOverValue(s: var YamlStream) =
|
|
|
|
var e = s.next()
|
|
|
|
var depth = int(e.kind in {yamlStartMap, yamlStartSeq})
|
|
|
|
while depth > 0:
|
|
|
|
case s.next().kind
|
|
|
|
of yamlStartMap, yamlStartSeq: inc(depth)
|
|
|
|
of yamlEndMap, yamlEndSeq: dec(depth)
|
|
|
|
of yamlScalar, yamlAlias: discard
|
|
|
|
else: internalError("Unexpected event kind.")
|
|
|
|
|
2023-04-04 17:29:20 +00:00
|
|
|
proc addFieldCases(
|
2023-08-29 21:23:15 +00:00
|
|
|
tDecl, context : NimNode,
|
|
|
|
name, o, matched: NimNode,
|
|
|
|
failOnUnknown, m: NimNode,
|
|
|
|
tName : string,
|
|
|
|
caseStmt : NimNode,
|
|
|
|
fieldIndex: var int,
|
|
|
|
) {.compileTime.} =
|
2023-04-04 17:29:20 +00:00
|
|
|
var
|
2016-09-24 14:45:49 +00:00
|
|
|
tDesc = getType(tDecl[1])
|
2023-04-04 17:29:20 +00:00
|
|
|
tParent = parentType(tDesc)
|
|
|
|
if tParent != nil:
|
2023-08-29 21:23:15 +00:00
|
|
|
addFieldCases(tParent, context, name, o, matched, failOnUnknown, m, tName, caseStmt, fieldIndex)
|
2016-06-05 18:52:43 +00:00
|
|
|
for child in tDesc[2].children:
|
2016-06-05 17:29:16 +00:00
|
|
|
if child.kind == nnkRecCase:
|
|
|
|
let
|
|
|
|
discriminant = newDotExpr(o, newIdentNode($child[0]))
|
|
|
|
discType = newCall("type", discriminant)
|
|
|
|
var disOb = newNimNode(nnkOfBranch).add(newStrLitNode($child[0]))
|
2019-06-16 11:43:08 +00:00
|
|
|
var objConstr = newNimNode(nnkObjConstr).add(newCall("type", o))
|
|
|
|
objConstr.add(newColonExpr(newIdentNode($child[0]), newIdentNode(
|
|
|
|
"value")))
|
|
|
|
for otherChild in tDesc[2].children:
|
|
|
|
if otherChild == child:
|
|
|
|
continue
|
2019-06-16 11:45:21 +00:00
|
|
|
if otherChild.kind != nnkSym:
|
|
|
|
error("Unexpected kind of field '" & $otherChild[0] &
|
|
|
|
"': " & $otherChild.kind)
|
2019-06-16 11:43:08 +00:00
|
|
|
objConstr.add(newColonExpr(newIdentNode($otherChild), newDotExpr(o,
|
|
|
|
newIdentNode($otherChild))))
|
2016-06-05 17:29:16 +00:00
|
|
|
disOb.add(newStmtList(
|
2023-08-29 21:23:15 +00:00
|
|
|
checkDuplicate(input(context), tName, $child[0], fieldIndex, matched, m),
|
2016-06-05 17:29:16 +00:00
|
|
|
newNimNode(nnkVarSection).add(
|
|
|
|
newNimNode(nnkIdentDefs).add(
|
|
|
|
newIdentNode("value"), discType, newEmptyNode())),
|
2023-08-29 21:23:15 +00:00
|
|
|
newCall("constructChild", context, newIdentNode("value")),
|
2019-06-16 11:43:08 +00:00
|
|
|
newAssignment(o, objConstr),
|
2016-09-21 13:40:03 +00:00
|
|
|
markAsFound(fieldIndex, matched)))
|
2016-10-19 20:04:46 +00:00
|
|
|
caseStmt.add(disOb)
|
|
|
|
var alreadyUsedSet = newNimNode(nnkCurly)
|
2016-06-05 17:29:16 +00:00
|
|
|
for bIndex in 1 .. len(child) - 1:
|
2016-10-19 20:04:46 +00:00
|
|
|
var recListIndex = 0
|
|
|
|
var discTest: NimNode
|
|
|
|
case child[bIndex].kind
|
|
|
|
of nnkOfBranch:
|
|
|
|
discTest = newNimNode(nnkCurly)
|
2016-11-30 17:01:03 +00:00
|
|
|
while recListIndex < child[bIndex].len - 1:
|
|
|
|
yAssert child[bIndex][recListIndex].kind == nnkIntLit
|
2016-10-19 20:04:46 +00:00
|
|
|
discTest.add(child[bIndex][recListIndex])
|
|
|
|
alreadyUsedSet.add(child[bIndex][recListIndex])
|
|
|
|
inc(recListIndex)
|
|
|
|
discTest = infix(discriminant, "in", discTest)
|
|
|
|
of nnkElse:
|
|
|
|
discTest = infix(discriminant, "notin", alreadyUsedSet)
|
|
|
|
else:
|
|
|
|
internalError("Unexpected child kind: " & $child[bIndex].kind)
|
|
|
|
|
2016-11-30 17:01:03 +00:00
|
|
|
for item in child[bIndex][recListIndex].recListItems:
|
2016-09-22 12:16:10 +00:00
|
|
|
inc(fieldIndex)
|
2016-08-09 18:47:22 +00:00
|
|
|
yAssert item.kind == nnkSym
|
2016-06-05 17:29:16 +00:00
|
|
|
var ob = newNimNode(nnkOfBranch).add(newStrLitNode($item))
|
|
|
|
let field = newDotExpr(o, newIdentNode($item))
|
|
|
|
var ifStmt = newIfStmt((cond: discTest, body: newStmtList(
|
2023-08-29 21:23:15 +00:00
|
|
|
newCall("constructChild", context, field))))
|
2016-06-08 17:15:50 +00:00
|
|
|
ifStmt.add(newNimNode(nnkElse).add(newNimNode(nnkRaiseStmt).add(
|
2023-08-29 21:23:15 +00:00
|
|
|
newCall(bindSym("constructionError"), input(context), m,
|
2016-06-08 17:15:50 +00:00
|
|
|
infix(newStrLitNode("Field " & $item & " not allowed for " &
|
|
|
|
$child[0] & " == "), "&", prefix(discriminant, "$"))))))
|
2020-06-26 19:29:41 +00:00
|
|
|
ob.add(ifNotTransient(o, item,
|
2023-08-29 21:23:15 +00:00
|
|
|
[checkDuplicate(input(context), tName, $item, fieldIndex, matched, m),
|
|
|
|
ifStmt, markAsFound(fieldIndex, matched)], true,
|
|
|
|
input(context), m, tName, $item))
|
2016-10-19 20:04:46 +00:00
|
|
|
caseStmt.add(ob)
|
2016-06-05 17:29:16 +00:00
|
|
|
else:
|
2016-08-09 18:47:22 +00:00
|
|
|
yAssert child.kind == nnkSym
|
2016-06-05 17:29:16 +00:00
|
|
|
var ob = newNimNode(nnkOfBranch).add(newStrLitNode($child))
|
|
|
|
let field = newDotExpr(o, newIdentNode($child))
|
2020-06-26 19:29:41 +00:00
|
|
|
ob.add(ifNotTransient(o, child,
|
2023-08-29 21:23:15 +00:00
|
|
|
[checkDuplicate(input(context), tName, $child, fieldIndex, matched, m),
|
|
|
|
newCall("constructChild", context, field),
|
|
|
|
markAsFound(fieldIndex, matched)], true, input(context), m, tName, $child))
|
2016-10-19 20:04:46 +00:00
|
|
|
caseStmt.add(ob)
|
2016-09-21 13:40:03 +00:00
|
|
|
inc(fieldIndex)
|
2023-04-04 17:29:20 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
macro constructFieldValue(
|
|
|
|
t: typedesc,
|
|
|
|
context, name, o, matched: untyped,
|
|
|
|
failOnUnknown: bool,
|
|
|
|
m: untyped,
|
|
|
|
) =
|
2023-04-04 17:29:20 +00:00
|
|
|
let
|
|
|
|
tDecl = getType(t)
|
|
|
|
tName = $tDecl[1]
|
|
|
|
result = newStmtList()
|
|
|
|
var caseStmt = newNimNode(nnkCaseStmt).add(name)
|
|
|
|
var fieldIndex = 0
|
2023-08-29 21:23:15 +00:00
|
|
|
addFieldCases(tDecl, context, name, o, matched, failOnUnknown, m, tName, caseStmt, fieldIndex)
|
2017-09-20 16:58:09 +00:00
|
|
|
caseStmt.add(newNimNode(nnkElse).add(newNimNode(nnkWhenStmt).add(
|
|
|
|
newNimNode(nnkElifBranch).add(failOnUnknown,
|
|
|
|
newNimNode(nnkRaiseStmt).add(
|
2023-08-29 21:23:15 +00:00
|
|
|
newCall(bindSym("constructionError"), input(context), m,
|
2017-09-20 16:58:09 +00:00
|
|
|
infix(newLit("While constructing " & tName & ": Unknown field: "), "&",
|
2023-03-10 18:57:21 +00:00
|
|
|
newCall(bindSym("escape"), name)))))
|
|
|
|
).add(newNimNode(nnkElse).add(
|
2023-08-29 21:23:15 +00:00
|
|
|
newCall(bindSym("skipOverValue"), input(context))
|
2023-03-10 18:57:21 +00:00
|
|
|
))))
|
2016-10-19 20:04:46 +00:00
|
|
|
result.add(caseStmt)
|
2016-06-05 17:29:16 +00:00
|
|
|
|
2019-06-11 19:30:14 +00:00
|
|
|
proc isVariantObject(t: NimNode): bool {.compileTime.} =
|
2023-04-04 18:06:56 +00:00
|
|
|
var
|
|
|
|
tResolved: NimNode
|
|
|
|
tDesc: NimNode
|
|
|
|
if t.kind == nnkSym:
|
|
|
|
tResolved = getType(t)
|
|
|
|
else:
|
|
|
|
tResolved = t
|
|
|
|
if tResolved.kind == nnkBracketExpr and tResolved[0].strVal == "typeDesc":
|
|
|
|
tDesc = getType(tResolved[1])
|
|
|
|
else:
|
|
|
|
tDesc = tResolved
|
|
|
|
if tDesc.kind != nnkObjectTy: return false
|
|
|
|
let tParent = parentType(tDesc)
|
|
|
|
if tParent != nil:
|
|
|
|
if isVariantObject(tParent): return true
|
2016-06-08 17:15:50 +00:00
|
|
|
for child in tDesc[2].children:
|
|
|
|
if child.kind == nnkRecCase: return true
|
|
|
|
return false
|
|
|
|
|
2020-06-26 19:29:41 +00:00
|
|
|
proc hasIgnore(t: typedesc): bool {.compileTime.} =
|
|
|
|
when compiles(t.hasCustomPragma(ignore)):
|
|
|
|
return t.hasCustomPragma(ignore)
|
|
|
|
else:
|
|
|
|
return false
|
2017-09-20 16:58:09 +00:00
|
|
|
|
2023-04-04 17:29:20 +00:00
|
|
|
proc constructObjectDefault*(
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var RootObj,
|
|
|
|
) =
|
2023-04-04 17:29:20 +00:00
|
|
|
# specialization of generic proc for RootObj, doesn't do anything
|
|
|
|
return
|
|
|
|
|
2017-03-29 19:42:07 +00:00
|
|
|
proc constructObjectDefault*[O: object|tuple](
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var O,
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].} =
|
2017-03-29 19:42:07 +00:00
|
|
|
## Constructs a Nim object or tuple from a YAML mapping.
|
|
|
|
## This is the default implementation for custom objects and tuples and should
|
|
|
|
## not be redefined. If you are adding a custom constructObject()
|
|
|
|
## implementation, you can use this proc to call the default implementation
|
|
|
|
## within it.
|
2016-09-21 13:40:03 +00:00
|
|
|
var matched = matchMatrix(O)
|
2023-08-29 21:23:15 +00:00
|
|
|
var e = ctx.input.next()
|
2016-06-08 17:15:50 +00:00
|
|
|
const
|
2019-06-11 19:30:14 +00:00
|
|
|
startKind = when isVariantObject(getType(O)): yamlStartSeq else: yamlStartMap
|
|
|
|
endKind = when isVariantObject(getType(O)): yamlEndSeq else: yamlEndMap
|
2016-06-08 17:15:50 +00:00
|
|
|
if e.kind != startKind:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(
|
|
|
|
e.startPos,
|
|
|
|
"While constructing " & typetraits.name(O) &
|
|
|
|
": Expected " & $startKind & ", got " & $e.kind
|
|
|
|
)
|
2020-11-05 19:23:42 +00:00
|
|
|
let startPos = e.startPos
|
2020-06-26 19:29:41 +00:00
|
|
|
when hasIgnore(O):
|
|
|
|
const ignoredKeyList = O.getCustomPragmaVal(ignore)
|
|
|
|
const failOnUnknown = len(ignoredKeyList) > 0
|
|
|
|
else:
|
|
|
|
const failOnUnknown = true
|
2023-08-29 21:23:15 +00:00
|
|
|
while ctx.input.peek.kind != endKind:
|
|
|
|
e = ctx.input.next()
|
2019-06-11 19:30:14 +00:00
|
|
|
when isVariantObject(getType(O)):
|
2016-06-08 17:15:50 +00:00
|
|
|
if e.kind != yamlStartMap:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(
|
|
|
|
e.startPos, "Expected single-pair map, got " & $e.kind
|
|
|
|
)
|
|
|
|
e = ctx.input.next()
|
2016-04-02 15:48:22 +00:00
|
|
|
if e.kind != yamlScalar:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(
|
|
|
|
e.startPos, "Expected field name, got " & $e.kind
|
|
|
|
)
|
2016-04-02 15:48:22 +00:00
|
|
|
let name = e.scalarContent
|
2016-06-05 18:52:43 +00:00
|
|
|
when result is tuple:
|
2016-09-21 13:40:03 +00:00
|
|
|
var i = 0
|
|
|
|
var found = false
|
2016-06-05 18:52:43 +00:00
|
|
|
for fname, value in fieldPairs(result):
|
|
|
|
if fname == name:
|
2016-09-21 13:40:03 +00:00
|
|
|
if matched[i]:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(
|
|
|
|
e.startPos, "While constructing " &
|
|
|
|
typetraits.name(O) & ": Duplicate field: " & escape(name)
|
|
|
|
)
|
|
|
|
ctx.constructChild(value)
|
2016-09-21 13:40:03 +00:00
|
|
|
matched[i] = true
|
|
|
|
found = true
|
2016-06-05 18:52:43 +00:00
|
|
|
break
|
2016-09-21 13:40:03 +00:00
|
|
|
inc(i)
|
2017-09-20 16:58:09 +00:00
|
|
|
when failOnUnknown:
|
|
|
|
if not found:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(
|
|
|
|
e.startPos, "While constructing " &
|
|
|
|
typetraits.name(O) & ": Unknown field: " & escape(name)
|
|
|
|
)
|
2016-06-08 17:15:50 +00:00
|
|
|
else:
|
2020-06-26 19:29:41 +00:00
|
|
|
when hasIgnore(O) and failOnUnknown:
|
|
|
|
if name notin ignoredKeyList:
|
2023-08-29 21:23:15 +00:00
|
|
|
constructFieldValue(O, ctx, name, result, matched, failOnUnknown, e.startPos)
|
2020-06-26 19:29:41 +00:00
|
|
|
else:
|
2023-08-29 21:23:15 +00:00
|
|
|
skipOverValue(ctx.input)
|
2016-10-27 15:58:14 +00:00
|
|
|
else:
|
2023-08-29 21:23:15 +00:00
|
|
|
constructFieldValue(O, ctx, name, result, matched, failOnUnknown, e.startPos)
|
2019-06-11 19:30:14 +00:00
|
|
|
when isVariantObject(getType(O)):
|
2023-08-29 21:23:15 +00:00
|
|
|
e = ctx.input.next()
|
2016-06-08 17:15:50 +00:00
|
|
|
if e.kind != yamlEndMap:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(
|
|
|
|
e.startPos, "Expected end of single-pair map, got " & $e.kind
|
|
|
|
)
|
|
|
|
discard ctx.input.next()
|
2016-09-21 13:40:03 +00:00
|
|
|
when result is tuple:
|
|
|
|
var i = 0
|
|
|
|
for fname, value in fieldPairs(result):
|
|
|
|
if not matched[i]:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(startPos, "While constructing " &
|
2016-09-24 14:45:49 +00:00
|
|
|
typetraits.name(O) & ": Missing field: " & escape(fname))
|
2016-09-21 13:40:03 +00:00
|
|
|
inc(i)
|
2023-08-29 21:23:15 +00:00
|
|
|
else: ensureAllFieldsPresent(ctx.input, O, result, matched, startPos)
|
2016-02-01 18:48:42 +00:00
|
|
|
|
2017-03-29 19:42:07 +00:00
|
|
|
proc constructObject*[O: object|tuple](
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var O,
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].} =
|
2017-03-29 19:42:07 +00:00
|
|
|
## Overridable default implementation for custom object and tuple types
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx.constructObjectDefault(result)
|
2017-03-29 19:42:07 +00:00
|
|
|
|
2023-04-04 18:06:56 +00:00
|
|
|
proc recGenFieldRepresenters(
|
2023-08-29 21:23:15 +00:00
|
|
|
tDecl, value: NimNode,
|
|
|
|
isVO : bool,
|
|
|
|
fieldIndex : var int16,
|
|
|
|
result : NimNode,
|
|
|
|
) {.compileTime.} =
|
2016-10-15 15:58:29 +00:00
|
|
|
let
|
|
|
|
tDesc = getType(tDecl[1])
|
2023-04-04 18:06:56 +00:00
|
|
|
tParent = parentType(tDesc)
|
|
|
|
if tParent != nil:
|
2023-08-29 18:46:26 +00:00
|
|
|
recGenFieldRepresenters(tParent, value, isVO, fieldIndex, result)
|
2016-10-15 15:58:29 +00:00
|
|
|
for child in tDesc[2].children:
|
|
|
|
if child.kind == nnkRecCase:
|
|
|
|
let
|
|
|
|
fieldName = $child[0]
|
|
|
|
fieldAccessor = newDotExpr(value, newIdentNode(fieldName))
|
|
|
|
result.add(quote do:
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(startMapEvent())
|
|
|
|
ctx.put(scalarEvent(
|
|
|
|
`fieldName`,
|
|
|
|
tag = if ctx.emitTag: yTagNimField else: yTagQuestionMark
|
|
|
|
))
|
|
|
|
ctx.representChild(`fieldAccessor`)
|
|
|
|
ctx.put(endMapEvent())
|
2016-10-15 15:58:29 +00:00
|
|
|
)
|
|
|
|
let enumName = $getTypeInst(child[0])
|
|
|
|
var caseStmt = newNimNode(nnkCaseStmt).add(fieldAccessor)
|
|
|
|
for bIndex in 1 .. len(child) - 1:
|
|
|
|
var curBranch: NimNode
|
|
|
|
var recListIndex = 0
|
|
|
|
case child[bIndex].kind
|
|
|
|
of nnkOfBranch:
|
|
|
|
curBranch = newNimNode(nnkOfBranch)
|
2016-11-30 17:01:03 +00:00
|
|
|
while recListIndex < child[bIndex].len - 1:
|
|
|
|
expectKind(child[bIndex][recListIndex], nnkIntLit)
|
2016-10-15 15:58:29 +00:00
|
|
|
curBranch.add(newCall(enumName, newLit(child[bIndex][recListIndex].intVal)))
|
|
|
|
inc(recListIndex)
|
|
|
|
of nnkElse:
|
|
|
|
curBranch = newNimNode(nnkElse)
|
|
|
|
else:
|
|
|
|
internalError("Unexpected child kind: " & $child[bIndex].kind)
|
|
|
|
var curStmtList = newStmtList()
|
2016-11-30 17:01:03 +00:00
|
|
|
if child[bIndex][recListIndex].recListLen > 0:
|
|
|
|
for item in child[bIndex][recListIndex].recListItems():
|
2016-10-15 15:58:29 +00:00
|
|
|
inc(fieldIndex)
|
|
|
|
let
|
|
|
|
name = $item
|
|
|
|
itemAccessor = newDotExpr(value, newIdentNode(name))
|
|
|
|
curStmtList.add(quote do:
|
2020-06-26 19:29:41 +00:00
|
|
|
when not `itemAccessor`.hasCustomPragma(transient):
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(startMapEvent())
|
|
|
|
ctx.put(scalarEvent(
|
|
|
|
`name`,
|
|
|
|
tag = if ctx.emitTag: yTagNimField else: yTagQuestionMark
|
|
|
|
))
|
|
|
|
ctx.representChild(`itemAccessor`)
|
|
|
|
ctx.put(endMapEvent())
|
2016-10-15 15:58:29 +00:00
|
|
|
)
|
|
|
|
else:
|
|
|
|
curStmtList.add(newNimNode(nnkDiscardStmt).add(newEmptyNode()))
|
|
|
|
curBranch.add(curStmtList)
|
|
|
|
caseStmt.add(curBranch)
|
|
|
|
result.add(caseStmt)
|
|
|
|
else:
|
|
|
|
let
|
|
|
|
name = $child
|
2023-08-29 18:46:26 +00:00
|
|
|
templName = genSym(nskTemplate)
|
2016-10-15 15:58:29 +00:00
|
|
|
childAccessor = newDotExpr(value, newIdentNode(name))
|
|
|
|
result.add(quote do:
|
2023-08-29 18:46:26 +00:00
|
|
|
template `templName` {.used.} =
|
|
|
|
when bool(`isVO`): ctx.put(startMapEvent())
|
|
|
|
ctx.put(scalarEvent(
|
|
|
|
`name`,
|
|
|
|
if ctx.emitTag: yTagNimField else: yTagQuestionMark,
|
|
|
|
yAnchorNone
|
|
|
|
))
|
|
|
|
ctx.representChild(`childAccessor`)
|
|
|
|
when bool(`isVO`): ctx.put(endMapEvent())
|
2021-10-22 15:54:40 +00:00
|
|
|
when not `childAccessor`.hasCustomPragma(transient):
|
2023-04-04 18:06:56 +00:00
|
|
|
when hasSparse(`tDecl`) and `child` is Option:
|
2023-08-29 18:46:26 +00:00
|
|
|
if `childAccessor`.isSome: `templName`()
|
2021-10-22 15:54:40 +00:00
|
|
|
else:
|
2023-08-29 18:46:26 +00:00
|
|
|
`templName`()
|
2016-10-15 15:58:29 +00:00
|
|
|
)
|
|
|
|
inc(fieldIndex)
|
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
macro genRepresentObject(t: typedesc, value) =
|
2023-04-04 18:06:56 +00:00
|
|
|
result = newStmtList()
|
|
|
|
let
|
|
|
|
tDecl = getType(t)
|
|
|
|
isVO = isVariantObject(t)
|
|
|
|
var fieldIndex = 0'i16
|
2023-08-29 18:46:26 +00:00
|
|
|
recGenFieldRepresenters(tDecl, value, isVO, fieldIndex, result)
|
2023-04-04 18:06:56 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc representObject*[O: object](
|
|
|
|
ctx : var SerializationContext,
|
|
|
|
value: O,
|
|
|
|
tag : Tag,
|
2023-08-30 17:56:04 +00:00
|
|
|
) {.raises: [YamlSerializationError].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## represents a Nim object or tuple as YAML mapping
|
2023-08-29 18:46:26 +00:00
|
|
|
when isVariantObject(getType(O)): ctx.put(startSeqEvent(tag = tag))
|
|
|
|
else: ctx.put(startMapEvent(tag = tag))
|
|
|
|
genRepresentObject(O, value)
|
|
|
|
when isVariantObject(getType(O)): ctx.put(endSeqEvent())
|
|
|
|
else: ctx.put(endMapEvent())
|
|
|
|
|
|
|
|
proc representObject*[O: tuple](
|
|
|
|
ctx : var SerializationContext,
|
|
|
|
value: O,
|
|
|
|
tag : Tag,
|
2023-08-30 17:56:04 +00:00
|
|
|
) {.raises: [YamlSerializationError].} =
|
2016-10-15 15:58:29 +00:00
|
|
|
var fieldIndex = 0'i16
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(startMapEvent(tag = tag))
|
2016-10-15 15:58:29 +00:00
|
|
|
for name, fvalue in fieldPairs(value):
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(scalarEvent(
|
|
|
|
name,
|
|
|
|
tag = if ctx.emitTag: yTagNimField else: yTagQuestionMark
|
|
|
|
))
|
|
|
|
ctx.representChild(fvalue)
|
2016-10-15 15:58:29 +00:00
|
|
|
inc(fieldIndex)
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(endMapEvent())
|
2016-02-01 18:48:42 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructObject*[O: enum](
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var O,
|
|
|
|
) {.raises: [YamlConstructionError, YamlStreamError].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## constructs a Nim enum from a YAML scalar
|
2023-08-29 21:23:15 +00:00
|
|
|
let e = ctx.input.next()
|
2016-04-02 15:48:22 +00:00
|
|
|
if e.kind != yamlScalar:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(
|
|
|
|
e.startPos, "Expected scalar, got " & $e.kind
|
|
|
|
)
|
2016-04-02 15:48:22 +00:00
|
|
|
try: result = parseEnum[O](e.scalarContent)
|
2023-03-18 12:54:45 +00:00
|
|
|
except ValueError as ve:
|
2023-08-29 21:23:15 +00:00
|
|
|
var ex = ctx.input.constructionError(e.startPos, "Cannot parse '" &
|
|
|
|
escape(e.scalarContent) & "' as " & type(O).name
|
|
|
|
)
|
2023-03-18 12:54:45 +00:00
|
|
|
ex.parent = ve
|
2016-04-02 15:48:22 +00:00
|
|
|
raise ex
|
2016-02-01 19:16:35 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc representObject*[O: enum](
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var SerializationContext,
|
2023-08-29 18:46:26 +00:00
|
|
|
value: O,
|
2023-08-29 21:23:15 +00:00
|
|
|
tag : Tag,
|
2023-08-29 18:46:26 +00:00
|
|
|
) {.raises: [].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## represents a Nim enum as YAML scalar
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(scalarEvent($value, tag, yAnchorNone))
|
2016-02-01 19:16:35 +00:00
|
|
|
|
2020-11-10 12:55:22 +00:00
|
|
|
proc yamlTag*[O](T: typedesc[ref O]): Tag {.inline, raises: [].} = yamlTag(O)
|
2016-01-28 20:59:26 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
macro constructImplicitVariantObject(
|
|
|
|
m, c, r, possibleTags: untyped,
|
|
|
|
t: typedesc,
|
|
|
|
) =
|
2016-06-05 17:29:16 +00:00
|
|
|
let tDesc = getType(getType(t)[1])
|
2016-08-09 18:47:22 +00:00
|
|
|
yAssert tDesc.kind == nnkObjectTy
|
2016-06-05 18:52:43 +00:00
|
|
|
let recCase = tDesc[2][0]
|
2016-08-09 18:47:22 +00:00
|
|
|
yAssert recCase.kind == nnkRecCase
|
2019-06-16 11:43:08 +00:00
|
|
|
result = newNimNode(nnkIfStmt)
|
2016-06-05 17:29:16 +00:00
|
|
|
for i in 1 .. recCase.len - 1:
|
2016-08-09 18:47:22 +00:00
|
|
|
yAssert recCase[i].kind == nnkOfBranch
|
2016-06-05 17:29:16 +00:00
|
|
|
var branch = newNimNode(nnkElifBranch)
|
2019-06-16 11:43:08 +00:00
|
|
|
var branchContent = newStmtList(newAssignment(r,
|
|
|
|
newNimNode(nnkObjConstr).add(
|
|
|
|
newCall("type", r),
|
|
|
|
newColonExpr(newIdentNode($recCase[0]), recCase[i][0])
|
|
|
|
)))
|
2016-11-30 17:01:03 +00:00
|
|
|
case recCase[i][1].recListLen
|
2016-06-05 17:29:16 +00:00
|
|
|
of 0:
|
2020-11-10 12:55:22 +00:00
|
|
|
branch.add(infix(newIdentNode("yTagNull"), "in", possibleTags))
|
2023-08-29 21:23:15 +00:00
|
|
|
branchContent.add(
|
|
|
|
newNimNode(nnkDiscardStmt).add(newCall("next", input(c))))
|
2016-06-05 17:29:16 +00:00
|
|
|
of 1:
|
2016-11-30 17:01:03 +00:00
|
|
|
let field = newDotExpr(r, newIdentNode($recCase[i][1].recListNode))
|
2016-06-05 17:29:16 +00:00
|
|
|
branch.add(infix(
|
2020-11-10 12:55:22 +00:00
|
|
|
newCall("yamlTag", newCall("type", field)), "in", possibleTags))
|
2023-08-29 21:23:15 +00:00
|
|
|
branchContent.add(newCall("constructChild", c, field))
|
2018-05-17 21:18:09 +00:00
|
|
|
else:
|
|
|
|
block:
|
|
|
|
internalError("Too many children: " & $recCase[i][1].recListlen)
|
2016-06-05 17:29:16 +00:00
|
|
|
branch.add(branchContent)
|
2019-06-16 11:43:08 +00:00
|
|
|
result.add(branch)
|
2016-06-05 17:29:16 +00:00
|
|
|
let raiseStmt = newNimNode(nnkRaiseStmt).add(
|
2023-08-29 21:23:15 +00:00
|
|
|
newCall(bindSym("constructionError"), input(c), m,
|
2016-06-05 17:29:16 +00:00
|
|
|
infix(newStrLitNode("This value type does not map to any field in " &
|
2019-06-11 19:50:52 +00:00
|
|
|
getTypeImpl(t)[1].repr & ": "), "&",
|
2020-11-10 12:55:22 +00:00
|
|
|
newCall("$", newNimNode(nnkBracketExpr).add(possibleTags, newIntLitNode(0)))
|
2016-06-05 17:29:16 +00:00
|
|
|
)
|
|
|
|
))
|
2019-06-16 11:43:08 +00:00
|
|
|
result.add(newNimNode(nnkElse).add(newNimNode(nnkTryStmt).add(
|
2016-06-05 17:29:16 +00:00
|
|
|
newStmtList(raiseStmt), newNimNode(nnkExceptBranch).add(
|
2016-10-10 18:16:54 +00:00
|
|
|
newIdentNode("KeyError"),
|
2016-10-08 18:57:53 +00:00
|
|
|
newNimNode(nnkDiscardStmt).add(newEmptyNode())
|
2016-06-05 17:29:16 +00:00
|
|
|
))))
|
|
|
|
|
2020-06-26 19:29:41 +00:00
|
|
|
proc isImplicitVariantObject(t: typedesc): bool {.compileTime.} =
|
|
|
|
when compiles(t.hasCustomPragma(implicit)):
|
|
|
|
return t.hasCustomPragma(implicit)
|
|
|
|
else:
|
|
|
|
return false
|
|
|
|
|
|
|
|
proc canBeImplicit(t: typedesc): bool {.compileTime.} =
|
|
|
|
let tDesc = getType(t)
|
|
|
|
if tDesc.kind != nnkObjectTy: return false
|
|
|
|
if tDesc[2].len != 1: return false
|
|
|
|
if tDesc[2][0].kind != nnkRecCase: return false
|
|
|
|
var foundEmptyBranch = false
|
|
|
|
for i in 1.. tDesc[2][0].len - 1:
|
|
|
|
case tDesc[2][0][i][1].recListlen # branch contents
|
|
|
|
of 0:
|
|
|
|
if foundEmptyBranch: return false
|
|
|
|
else: foundEmptyBranch = true
|
|
|
|
of 1: discard
|
|
|
|
else: return false
|
|
|
|
return true
|
2016-10-08 18:57:53 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructChild*[T](
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var T,
|
|
|
|
) =
|
|
|
|
let item = ctx.input.peek()
|
2020-06-26 19:29:41 +00:00
|
|
|
when isImplicitVariantObject(T):
|
|
|
|
when not canBeImplicit(T):
|
|
|
|
{. fatal: "This type cannot be marked as implicit" .}
|
2020-11-10 12:55:22 +00:00
|
|
|
var possibleTags = newSeq[Tag]()
|
2016-06-05 17:29:16 +00:00
|
|
|
case item.kind
|
|
|
|
of yamlScalar:
|
2020-11-06 15:21:58 +00:00
|
|
|
case item.scalarProperties.tag
|
2016-06-05 17:29:16 +00:00
|
|
|
of yTagQuestionMark:
|
|
|
|
case guessType(item.scalarContent)
|
|
|
|
of yTypeInteger:
|
2020-11-10 12:55:22 +00:00
|
|
|
possibleTags.add([yamlTag(int), yamlTag(int8), yamlTag(int16),
|
2016-06-05 17:29:16 +00:00
|
|
|
yamlTag(int32), yamlTag(int64)])
|
|
|
|
if item.scalarContent[0] != '-':
|
2020-11-10 12:55:22 +00:00
|
|
|
possibleTags.add([yamlTag(uint), yamlTag(uint8), yamlTag(uint16),
|
2016-06-05 17:29:16 +00:00
|
|
|
yamlTag(uint32), yamlTag(uint64)])
|
|
|
|
of yTypeFloat, yTypeFloatInf, yTypeFloatNaN:
|
2020-11-10 12:55:22 +00:00
|
|
|
possibleTags.add([yamlTag(float), yamlTag(float32),
|
2016-06-05 17:29:16 +00:00
|
|
|
yamlTag(float64)])
|
|
|
|
of yTypeBoolTrue, yTypeBoolFalse:
|
2020-11-10 12:55:22 +00:00
|
|
|
possibleTags.add(yamlTag(bool))
|
2016-06-05 17:29:16 +00:00
|
|
|
of yTypeNull:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(item.startPos, "not implemented!")
|
2016-06-05 17:29:16 +00:00
|
|
|
of yTypeUnknown:
|
2020-11-10 12:55:22 +00:00
|
|
|
possibleTags.add(yamlTag(string))
|
2016-11-08 20:13:01 +00:00
|
|
|
of yTypeTimestamp:
|
2020-11-10 12:55:22 +00:00
|
|
|
possibleTags.add(yamlTag(Time))
|
2016-06-05 17:29:16 +00:00
|
|
|
of yTagExclamationMark:
|
2020-11-10 12:55:22 +00:00
|
|
|
possibleTags.add(yamlTag(string))
|
2016-06-05 17:29:16 +00:00
|
|
|
else:
|
2020-11-10 12:55:22 +00:00
|
|
|
possibleTags.add(item.scalarProperties.tag)
|
2016-06-05 17:29:16 +00:00
|
|
|
of yamlStartMap:
|
2020-11-06 15:21:58 +00:00
|
|
|
if item.mapProperties.tag in [yTagQuestionMark, yTagExclamationMark]:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(item.startPos,
|
2016-06-05 17:29:16 +00:00
|
|
|
"Complex value of implicit variant object type must have a tag.")
|
2020-11-10 12:55:22 +00:00
|
|
|
possibleTags.add(item.mapProperties.tag)
|
2016-06-05 17:29:16 +00:00
|
|
|
of yamlStartSeq:
|
2020-11-06 15:21:58 +00:00
|
|
|
if item.seqProperties.tag in [yTagQuestionMark, yTagExclamationMark]:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(item.startPos,
|
2016-06-05 17:29:16 +00:00
|
|
|
"Complex value of implicit variant object type must have a tag.")
|
2020-11-10 12:55:22 +00:00
|
|
|
possibleTags.add(item.seqProperties.tag)
|
2022-09-07 14:38:11 +00:00
|
|
|
of yamlAlias:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(item.startPos,
|
2022-09-07 14:38:11 +00:00
|
|
|
"cannot load non-ref value from alias node")
|
2016-08-09 18:47:22 +00:00
|
|
|
else: internalError("Unexpected item kind: " & $item.kind)
|
2023-08-29 21:23:15 +00:00
|
|
|
constructImplicitVariantObject(item.startPos, ctx, result, possibleTags, T)
|
2016-06-05 17:29:16 +00:00
|
|
|
else:
|
|
|
|
case item.kind
|
|
|
|
of yamlScalar:
|
2020-11-05 19:23:42 +00:00
|
|
|
if item.scalarProperties.tag notin [yTagQuestionMark, yTagExclamationMark,
|
2016-06-05 17:29:16 +00:00
|
|
|
yamlTag(T)]:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(item.startPos, "Wrong tag for " & typetraits.name(T))
|
2020-11-05 19:23:42 +00:00
|
|
|
elif item.scalarProperties.anchor != yAnchorNone:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(item.startPos, "Anchor on non-ref type")
|
2016-06-05 17:29:16 +00:00
|
|
|
of yamlStartMap:
|
2020-11-05 19:23:42 +00:00
|
|
|
if item.mapProperties.tag notin [yTagQuestionMark, yamlTag(T)]:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(item.startPos, "Wrong tag for " & typetraits.name(T))
|
2020-11-05 19:23:42 +00:00
|
|
|
elif item.mapProperties.anchor != yAnchorNone:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(item.startPos, "Anchor on non-ref type")
|
2016-06-05 17:29:16 +00:00
|
|
|
of yamlStartSeq:
|
2020-11-05 19:23:42 +00:00
|
|
|
if item.seqProperties.tag notin [yTagQuestionMark, yamlTag(T)]:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(item.startPos, "Wrong tag for " & typetraits.name(T))
|
2020-11-05 19:23:42 +00:00
|
|
|
elif item.seqProperties.anchor != yAnchorNone:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(item.startPos, "Anchor on non-ref type")
|
2022-09-07 14:38:11 +00:00
|
|
|
of yamlAlias:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(item.startPos,
|
2022-09-07 14:38:11 +00:00
|
|
|
"cannot load non-ref value from alias node")
|
2016-08-09 18:47:22 +00:00
|
|
|
else: internalError("Unexpected item kind: " & $item.kind)
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx.constructObject(result)
|
2016-02-16 18:24:55 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructChild*(
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var string,
|
|
|
|
) =
|
|
|
|
let item = ctx.input.peek()
|
2016-04-04 19:21:24 +00:00
|
|
|
if item.kind == yamlScalar:
|
2020-11-03 21:08:21 +00:00
|
|
|
if item.scalarProperties.tag notin
|
2016-04-04 19:21:24 +00:00
|
|
|
[yTagQuestionMark, yTagExclamationMark, yamlTag(string)]:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(item.startPos, "Wrong tag for string")
|
2020-11-03 21:08:21 +00:00
|
|
|
elif item.scalarProperties.anchor != yAnchorNone:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(item.startPos, "Anchor on non-ref type")
|
|
|
|
ctx.constructObject(result)
|
2016-04-04 19:21:24 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructChild*[T](
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var seq[T],
|
|
|
|
) =
|
|
|
|
let item = ctx.input.peek()
|
2018-10-10 15:43:39 +00:00
|
|
|
if item.kind == yamlStartSeq:
|
2020-11-05 19:23:42 +00:00
|
|
|
if item.seqProperties.tag notin [yTagQuestionMark, yamlTag(seq[T])]:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(item.startPos, "Wrong tag for " & typetraits.name(seq[T]))
|
2020-11-05 19:23:42 +00:00
|
|
|
elif item.seqProperties.anchor != yAnchorNone:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(item.startPos, "Anchor on non-ref type")
|
|
|
|
ctx.constructObject(result)
|
2016-07-08 09:35:31 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructChild*[I, T](
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var array[I, T],
|
|
|
|
) =
|
|
|
|
let item = ctx.input.peek()
|
2023-03-18 13:50:38 +00:00
|
|
|
if item.kind == yamlStartSeq:
|
|
|
|
if item.seqProperties.tag notin [yTagQuestionMark, yamlTag(array[I, T])]:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(item.startPos, "Wrong tag for " & typetraits.name(array[I, T]))
|
2023-03-18 13:50:38 +00:00
|
|
|
elif item.seqProperties.anchor != yAnchorNone:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(item.startPos, "Anchor on non-ref type")
|
|
|
|
ctx.constructObject(result)
|
2023-03-18 13:50:38 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructChild*[T](
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var Option[T],
|
|
|
|
) =
|
2020-03-08 18:58:02 +00:00
|
|
|
## constructs an optional value. A value with a !!null tag will be loaded
|
|
|
|
## an empty value.
|
2023-08-29 21:23:15 +00:00
|
|
|
let event = ctx.input.peek()
|
2020-11-06 15:21:58 +00:00
|
|
|
if event.kind == yamlScalar and event.scalarProperties.tag == yTagNull:
|
2020-03-08 18:58:02 +00:00
|
|
|
result = none(T)
|
2023-08-29 21:23:15 +00:00
|
|
|
discard ctx.input.next()
|
2020-03-08 18:58:02 +00:00
|
|
|
else:
|
|
|
|
var inner: T
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx.constructChild(inner)
|
2020-03-08 18:58:02 +00:00
|
|
|
result = some(inner)
|
|
|
|
|
2017-02-13 16:10:56 +00:00
|
|
|
when defined(JS):
|
|
|
|
# in JS, Time is a ref type. Therefore, we need this specialization so that
|
|
|
|
# it is not handled by the general ref-type handler.
|
2023-08-29 21:23:15 +00:00
|
|
|
proc constructChild*(
|
|
|
|
ctx : var ConstructionContext,
|
|
|
|
result: var Time,
|
|
|
|
) =
|
|
|
|
let e = ctx.input.peek()
|
2017-02-13 16:10:56 +00:00
|
|
|
if e.kind == yamlScalar:
|
2020-11-05 19:23:42 +00:00
|
|
|
if e.scalarProperties.tag notin [yTagQuestionMark, yTagTimestamp]:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(e.startPos, "Wrong tag for Time")
|
2017-02-13 16:10:56 +00:00
|
|
|
elif guessType(e.scalarContent) != yTypeTimestamp:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(e.startPos, "Invalid timestamp")
|
2020-11-05 19:23:42 +00:00
|
|
|
elif e.scalarProperties.anchor != yAnchorNone:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(e.startPos, "Anchor on non-ref type")
|
|
|
|
ctx.constructObject(result)
|
2017-02-13 16:10:56 +00:00
|
|
|
else:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(e.startPos, "Unexpected structure, expected timestamp")
|
2017-02-13 16:10:56 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc constructChild*[O](
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx : var ConstructionContext,
|
2023-08-29 18:46:26 +00:00
|
|
|
result: var ref O,
|
|
|
|
) =
|
2023-08-29 21:23:15 +00:00
|
|
|
var e = ctx.input.peek()
|
2016-04-02 15:48:22 +00:00
|
|
|
if e.kind == yamlScalar:
|
2020-11-05 19:23:42 +00:00
|
|
|
let props = e.scalarProperties
|
|
|
|
if props.tag == yTagNull or (props.tag == yTagQuestionMark and
|
2016-04-02 15:48:22 +00:00
|
|
|
guessType(e.scalarContent) == yTypeNull):
|
|
|
|
result = nil
|
2023-08-29 21:23:15 +00:00
|
|
|
discard ctx.input.next()
|
2016-04-02 15:48:22 +00:00
|
|
|
return
|
|
|
|
elif e.kind == yamlAlias:
|
2023-08-30 19:14:31 +00:00
|
|
|
when nimvm:
|
2023-08-29 21:23:15 +00:00
|
|
|
raise ctx.input.constructionError(e.startPos,
|
2023-08-30 19:14:31 +00:00
|
|
|
"aliases are not supported at compile time")
|
|
|
|
else:
|
|
|
|
let val = ctx.refs.getOrDefault(e.aliasTarget, (yTagNull, pointer(nil)))
|
|
|
|
if val.p == nil:
|
|
|
|
raise ctx.input.constructionError(e.startPos,
|
|
|
|
"alias node refers to anchor in ignored scope")
|
|
|
|
if val.tag != yamlTag(O):
|
|
|
|
raise ctx.input.constructionError(e.startPos,
|
|
|
|
"alias node refers to object of incompatible type")
|
|
|
|
result = cast[ref O](val.p)
|
|
|
|
discard ctx.input.next()
|
|
|
|
return
|
2016-04-02 15:48:22 +00:00
|
|
|
new(result)
|
2020-11-03 21:08:21 +00:00
|
|
|
template removeAnchor(anchor: var Anchor) {.dirty.} =
|
2016-04-02 15:48:22 +00:00
|
|
|
if anchor != yAnchorNone:
|
2023-08-29 18:46:26 +00:00
|
|
|
yAssert(not ctx.refs.hasKey(anchor))
|
2023-08-30 19:14:31 +00:00
|
|
|
when nimvm: discard # no aliases supported at compile time
|
|
|
|
else: ctx.refs[anchor] = (yamlTag(O), cast[pointer](result))
|
2016-04-02 15:48:22 +00:00
|
|
|
anchor = yAnchorNone
|
2016-07-08 09:35:31 +00:00
|
|
|
|
2016-04-02 15:48:22 +00:00
|
|
|
case e.kind
|
2020-11-06 15:21:58 +00:00
|
|
|
of yamlScalar: removeAnchor(e.scalarProperties.anchor)
|
|
|
|
of yamlStartMap: removeAnchor(e.mapProperties.anchor)
|
|
|
|
of yamlStartSeq: removeAnchor(e.seqProperties.anchor)
|
2016-08-09 18:47:22 +00:00
|
|
|
else: internalError("Unexpected event kind: " & $e.kind)
|
2023-08-29 21:23:15 +00:00
|
|
|
ctx.input.peek = e
|
|
|
|
try: ctx.constructChild(result[])
|
2023-03-18 12:54:45 +00:00
|
|
|
except YamlConstructionError as e: raise e
|
|
|
|
except YamlStreamError as e: raise e
|
|
|
|
except CatchableError as ce:
|
|
|
|
var e = newException(YamlStreamError, ce.msg)
|
|
|
|
e.parent = ce
|
2016-04-02 15:48:22 +00:00
|
|
|
raise e
|
2016-01-28 20:59:26 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc representChild*(
|
|
|
|
ctx : var SerializationContext,
|
|
|
|
value: string,
|
|
|
|
) =
|
|
|
|
let tag = ctx.presentTag(string)
|
|
|
|
ctx.representObject(
|
|
|
|
value,
|
|
|
|
if tag == yTagQuestionMark and guessType(value) != yTypeUnknown:
|
|
|
|
yTagExclamationMark
|
|
|
|
else:
|
|
|
|
tag
|
|
|
|
)
|
|
|
|
|
|
|
|
proc representChild*[T](
|
|
|
|
ctx : var SerializationContext,
|
|
|
|
value: seq[T],
|
2023-08-30 17:56:04 +00:00
|
|
|
) {.raises: [YamlSerializationError].} =
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.representObject(value, ctx.presentTag(seq[T]))
|
|
|
|
|
|
|
|
proc representChild*[I, T](
|
|
|
|
ctx : var SerializationContext,
|
|
|
|
value: array[I, T],
|
2023-08-30 17:56:04 +00:00
|
|
|
) {.raises: [YamlSerializationError].} =
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.representObject(value, ctx.presentTag(array[I, T]))
|
|
|
|
|
|
|
|
proc representChild*[O](
|
|
|
|
ctx : var SerializationContext,
|
|
|
|
value: ref O,
|
|
|
|
) =
|
|
|
|
if isNil(value): ctx.put(scalarEvent("~", yTagNull))
|
2016-04-02 15:48:22 +00:00
|
|
|
else:
|
2023-08-30 19:40:20 +00:00
|
|
|
when nimvm: discard
|
|
|
|
else:
|
|
|
|
let p = cast[pointer](value)
|
|
|
|
# when c.anchorStyle == asNone, `referenced` is used as indicator that we are
|
|
|
|
# currently in the process of serializing this node. This enables us to
|
|
|
|
# detect cycles and raise an error.
|
|
|
|
var val = ctx.refs.getOrDefault(
|
|
|
|
p, (ctx.nextAnchorId.Anchor, ctx.options.anchorStyle == asNone)
|
|
|
|
)
|
|
|
|
if val.a != ctx.nextAnchorId.Anchor:
|
|
|
|
if ctx.options.anchorStyle == asNone:
|
|
|
|
if val.referenced:
|
|
|
|
raise newException(YamlSerializationError,
|
|
|
|
"tried to serialize cyclic graph with asNone")
|
|
|
|
else:
|
|
|
|
val = ctx.refs.getOrDefault(p)
|
|
|
|
yAssert(val.a != yAnchorNone)
|
|
|
|
if not val.referenced:
|
|
|
|
ctx.refs[p] = (val.a, true)
|
|
|
|
ctx.put(aliasEvent(val.a))
|
|
|
|
return
|
|
|
|
ctx.refs[p] = val
|
|
|
|
nextAnchor(ctx.nextAnchorId, len(ctx.nextAnchorId) - 1)
|
|
|
|
let origPut = ctx.putImpl
|
|
|
|
ctx.putImpl = proc(ctx: var SerializationContext, e: Event) =
|
|
|
|
var ex = e
|
|
|
|
case ex.kind
|
|
|
|
of yamlStartMap:
|
|
|
|
if ctx.options.anchorStyle != asNone: ex.mapProperties.anchor = val.a
|
|
|
|
of yamlStartSeq:
|
|
|
|
if ctx.options.anchorStyle != asNone: ex.seqProperties.anchor = val.a
|
|
|
|
of yamlScalar:
|
|
|
|
if ctx.options.anchorStyle != asNone: ex.scalarProperties.anchor = val.a
|
|
|
|
if not ctx.emitTag and guessType(ex.scalarContent) != yTypeNull:
|
|
|
|
ex.scalarProperties.tag = yTagQuestionMark
|
|
|
|
else: discard
|
|
|
|
ctx.putImpl = origPut
|
|
|
|
ctx.put(ex)
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.representChild(value[])
|
2023-08-30 19:40:20 +00:00
|
|
|
when nimvm: discard
|
|
|
|
else:
|
|
|
|
if ctx.options.anchorStyle == asNone: ctx.refs[p] = (val.a, false)
|
2023-08-29 18:46:26 +00:00
|
|
|
|
|
|
|
proc representChild*[T](
|
|
|
|
ctx : var SerializationContext,
|
|
|
|
value: Option[T],
|
2023-08-30 17:56:04 +00:00
|
|
|
) {.raises: [YamlSerializationError].} =
|
2020-03-08 18:58:02 +00:00
|
|
|
## represents an optional value. If the value is missing, a !!null scalar
|
|
|
|
## will be produced.
|
|
|
|
if value.isSome:
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.representChild(value.get())
|
2020-03-08 18:58:02 +00:00
|
|
|
else:
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.put(scalarEvent("~", yTagNull))
|
2020-03-08 18:58:02 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc representChild*[O](
|
|
|
|
ctx : var SerializationContext,
|
|
|
|
value: O,
|
|
|
|
) =
|
2020-06-26 19:29:41 +00:00
|
|
|
when isImplicitVariantObject(O):
|
2016-06-05 17:29:16 +00:00
|
|
|
# todo: this would probably be nicer if constructed with a macro
|
|
|
|
var count = 0
|
|
|
|
for name, field in fieldPairs(value):
|
2023-08-29 18:46:26 +00:00
|
|
|
if count > 0: ctx.representChild(field)
|
2016-06-05 17:29:16 +00:00
|
|
|
inc(count)
|
2023-08-29 18:46:26 +00:00
|
|
|
if count == 1: ctx.put(scalarEvent("~", yTagNull))
|
2016-06-05 17:29:16 +00:00
|
|
|
else:
|
2023-08-29 18:46:26 +00:00
|
|
|
ctx.representObject(value, ctx.presentTag(O))
|
2016-06-05 13:43:39 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
proc construct*[T](
|
|
|
|
input : var YamlStream,
|
|
|
|
target: var T,
|
|
|
|
)
|
2019-06-11 19:30:14 +00:00
|
|
|
{.raises: [YamlStreamError, YamlConstructionError].} =
|
2016-09-20 19:53:38 +00:00
|
|
|
## Constructs a Nim value from a YAML stream.
|
2023-08-29 21:23:15 +00:00
|
|
|
var context = initConstructionContext(input)
|
2016-04-02 15:48:22 +00:00
|
|
|
try:
|
2023-08-29 21:23:15 +00:00
|
|
|
var e = input.next()
|
2016-08-09 18:47:22 +00:00
|
|
|
yAssert(e.kind == yamlStartDoc)
|
2016-07-08 09:35:31 +00:00
|
|
|
|
2023-08-29 21:23:15 +00:00
|
|
|
context.constructChild(target)
|
|
|
|
e = input.next()
|
2016-08-09 18:47:22 +00:00
|
|
|
yAssert(e.kind == yamlEndDoc)
|
2023-03-18 12:54:45 +00:00
|
|
|
except YamlConstructionError as e: raise e
|
|
|
|
except YamlStreamError as e: raise e
|
|
|
|
except CatchableError as ce:
|
2023-08-29 21:23:15 +00:00
|
|
|
# may occur while calling ctx.input()
|
2023-03-18 12:54:45 +00:00
|
|
|
var ex = newException(YamlStreamError, "error occurred while constructing")
|
|
|
|
ex.parent = ce
|
2016-04-02 15:48:22 +00:00
|
|
|
raise ex
|
2016-01-26 19:51:21 +00:00
|
|
|
|
2023-08-29 18:46:26 +00:00
|
|
|
proc represent*[T](
|
2023-08-29 21:23:15 +00:00
|
|
|
value : T,
|
|
|
|
options: SerializationOptions = SerializationOptions(),
|
2023-08-29 18:46:26 +00:00
|
|
|
): YamlStream =
|
2016-09-20 19:53:38 +00:00
|
|
|
## Represents a Nim value as ``YamlStream``
|
2021-03-23 17:51:05 +00:00
|
|
|
var
|
|
|
|
bys = newBufferYamlStream()
|
2023-08-29 18:46:26 +00:00
|
|
|
context = initSerializationContext(
|
|
|
|
options,
|
|
|
|
proc(ctx: var SerializationContext, e: Event) = bys.put(e)
|
|
|
|
)
|
2020-11-06 15:21:58 +00:00
|
|
|
bys.put(startStreamEvent())
|
2023-08-29 18:46:26 +00:00
|
|
|
bys.put(startDocEvent(handles = options.handles))
|
|
|
|
context.representChild(value)
|
2016-09-20 19:53:38 +00:00
|
|
|
bys.put(endDocEvent())
|
2020-11-06 15:21:58 +00:00
|
|
|
bys.put(endStreamEvent())
|
2023-08-29 18:46:26 +00:00
|
|
|
if options.anchorStyle == asTidy:
|
2020-11-10 18:07:46 +00:00
|
|
|
var ctx = initAnchorContext()
|
2016-09-20 19:53:38 +00:00
|
|
|
for item in bys.mitems():
|
2016-09-01 18:56:34 +00:00
|
|
|
case item.kind
|
2020-11-10 18:07:46 +00:00
|
|
|
of yamlStartMap: ctx.process(item.mapProperties, context.refs)
|
|
|
|
of yamlStartSeq: ctx.process(item.seqProperties, context.refs)
|
|
|
|
of yamlScalar: ctx.process(item.scalarProperties, context.refs)
|
|
|
|
of yamlAlias: item.aliasTarget = ctx.map(item.aliasTarget)
|
2016-09-01 18:56:34 +00:00
|
|
|
else: discard
|
|
|
|
result = bys
|
2023-08-29 18:46:26 +00:00
|
|
|
|