NimYAML/yaml/stream.nim

142 lines
5.1 KiB
Nim
Raw Normal View History

# NimYAML - YAML implementation in Nim
# (c) Copyright 2015-2023 Felix Krause
2016-09-20 19:53:38 +00:00
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
## ==================
2021-05-17 22:31:47 +00:00
## Module yaml/stream
## ==================
##
## The stream API provides the basic data structure on which all low-level APIs
## operate. It is not named ``streams`` to not confuse it with the modle in the
## stdlib with that name.
2020-11-03 20:17:31 +00:00
import data
2016-09-20 19:53:38 +00:00
2018-08-18 02:43:52 +00:00
when defined(nimNoNil):
{.experimental: "notnil".}
2020-07-05 20:21:43 +00:00
2016-09-20 19:53:38 +00:00
type
2023-03-18 12:54:45 +00:00
YamlStreamError* = object of ValueError
## Exception that may be raised by a ``YamlStream`` when the underlying
## backend raises an exception. The error that has occurred is
## available from ``parent``.
2016-09-20 19:53:38 +00:00
YamlStream* = ref object of RootObj ## \
## A ``YamlStream`` is an iterator-like object that yields a
## well-formed stream of ``YamlStreamEvents``. Well-formed means that
## every ``yamlStartMap`` is terminated by a ``yamlEndMap``, every
## ``yamlStartSeq`` is terminated by a ``yamlEndSeq`` and every
## ``yamlStartDoc`` is terminated by a ``yamlEndDoc``. Moreover, every
## emitted mapping has an even number of children.
##
## The creator of a ``YamlStream`` is responsible for it being
## well-formed. A user of the stream may assume that it is well-formed
## and is not required to check for it. The procs in this module will
## always yield a well-formed ``YamlStream`` and expect it to be
## well-formed if they take it as input parameter.
2023-03-18 12:54:45 +00:00
nextImpl*: proc(s: YamlStream, e: var Event): bool {.gcSafe, raises: [CatchableError] .}
2016-09-20 19:53:38 +00:00
lastTokenContextImpl*:
proc(s: YamlStream, lineContent: var string): bool {.raises: [].}
2016-09-20 19:53:38 +00:00
peeked: bool
2020-11-03 20:17:31 +00:00
cached: Event
2016-09-20 19:53:38 +00:00
proc noLastContext(s: YamlStream, lineContent: var string): bool {.raises: [].} =
2016-09-20 19:53:38 +00:00
result = false
proc basicInit*(s: YamlStream, lastTokenContextImpl:
proc(s: YamlStream, lineContent: var string): bool
{.raises: [].} = noLastContext) {.raises: [].} =
2016-09-20 19:53:38 +00:00
## initialize basic values of the YamlStream. Call this in your constructor
## if you subclass YamlStream.
s.peeked = false
s.lastTokenContextImpl = lastTokenContextImpl
when not defined(JS):
type IteratorYamlStream = ref object of YamlStream
2023-03-18 12:54:45 +00:00
backend: iterator(): Event {.gcSafe, raises: [CatchableError].}
2016-09-20 19:53:38 +00:00
proc initYamlStream*(
backend: iterator(): Event {.gcSafe, raises: [CatchableError].}
): YamlStream {.raises: [].} =
2016-09-20 19:53:38 +00:00
## Creates a new ``YamlStream`` that uses the given iterator as backend.
result = new(IteratorYamlStream)
result.basicInit()
IteratorYamlStream(result).backend = backend
2023-03-18 12:54:45 +00:00
result.nextImpl = proc(s: YamlStream, e: var Event): bool {.gcSafe, raises: [CatchableError].} =
2016-09-20 19:53:38 +00:00
e = IteratorYamlStream(s).backend()
2020-11-03 20:17:31 +00:00
result = true
2016-09-20 19:53:38 +00:00
type
BufferYamlStream* = ref object of YamlStream
pos: int
2020-11-03 20:17:31 +00:00
buf: seq[Event]
2016-09-20 19:53:38 +00:00
proc newBufferYamlStream*(): BufferYamlStream not nil =
result = cast[BufferYamlStream not nil](new(BufferYamlStream))
result.basicInit()
result.buf = @[]
result.pos = 0
2020-11-03 20:17:31 +00:00
result.nextImpl = proc(s: YamlStream, e: var Event): bool =
2016-09-20 19:53:38 +00:00
let bys = BufferYamlStream(s)
2020-11-03 20:17:31 +00:00
e = bys.buf[bys.pos]
inc(bys.pos)
result = true
2016-09-20 19:53:38 +00:00
2020-11-03 20:17:31 +00:00
proc put*(bys: BufferYamlStream, e: Event) {.raises: [].} =
2016-09-20 19:53:38 +00:00
bys.buf.add(e)
proc next*(s: YamlStream): Event {.raises: [YamlStreamError], gcSafe.} =
2016-09-20 19:53:38 +00:00
## Get the next item of the stream. Requires ``finished(s) == true``.
## If the backend yields an exception, that exception will be encapsulated
## into a ``YamlStreamError``, which will be raised.
if s.peeked:
s.peeked = false
2020-11-03 20:17:31 +00:00
return move(s.cached)
2016-09-20 19:53:38 +00:00
else:
try:
while true:
if s.nextImpl(s, result): break
2023-03-18 12:54:45 +00:00
except YamlStreamError as e: raise e
except CatchableError as ce:
var e = newException(YamlStreamError, ce.msg)
e.parent = ce
2016-09-20 19:53:38 +00:00
raise e
2022-08-29 13:11:11 +00:00
proc peek*(s: YamlStream): lent Event {.raises: [YamlStreamError].} =
2016-09-20 19:53:38 +00:00
## Get the next item of the stream without advancing the stream.
## Requires ``finished(s) == true``. Handles exceptions of the backend like
## ``next()``.
if not s.peeked:
2020-11-03 20:17:31 +00:00
s.cached = s.next()
2016-09-20 19:53:38 +00:00
s.peeked = true
2022-08-29 13:11:11 +00:00
result = s.cached
2016-09-20 19:53:38 +00:00
2020-11-03 20:17:31 +00:00
proc `peek=`*(s: YamlStream, value: Event) {.raises: [].} =
2016-09-20 19:53:38 +00:00
## Set the next item of the stream. Will replace a previously peeked item,
## if one exists.
s.cached = value
s.peeked = true
proc getLastTokenContext*(s: YamlStream, lineContent: var string): bool =
2016-09-20 19:53:38 +00:00
## ``true`` if source context information is available about the last returned
## token. If ``true``, line, column and lineContent are set to position and
## line content where the last token has been read from.
result = s.lastTokenContextImpl(s, lineContent)
2016-09-20 19:53:38 +00:00
2020-11-03 20:17:31 +00:00
iterator items*(s: YamlStream): Event
2016-09-20 19:53:38 +00:00
{.raises: [YamlStreamError].} =
## Iterate over all items of the stream. You may not use ``peek()`` on the
## stream while iterating.
2020-11-03 20:17:31 +00:00
while true:
let e = s.next()
var last = e.kind == yamlEndStream
yield e
if last: break
2016-09-20 19:53:38 +00:00
2020-11-03 20:17:31 +00:00
iterator mitems*(bys: BufferYamlStream): var Event {.raises: [].} =
2016-09-20 19:53:38 +00:00
## Iterate over all items of the stream. You may not use ``peek()`` on the
## stream while iterating.
for e in bys.buf.mitems(): yield e