diff --git a/README.md b/README.md index 6329755..a31239b 100644 --- a/README.md +++ b/README.md @@ -1,213 +1,11 @@ # NimYAML - YAML implementation for Nim -NimYAML is a YAML implementation in Nim. It aims to implement the complete -YAML 1.2 specification and has a streaming interface that makes it possible to -parse YAML input sequentially, without loading all data into memory at once. It -is able to automatically serialize Nim object to YAML and deserialize them -again. - -**Attention**: NimYAML is work in progress. There is no release yet, and some -features are highly experimental. - -## Quickstart - -### Using the sequential parser - -```Nimrod -import yaml - -let input = """ -an integer: 42 -a boolean: yes -a list: - - 3.14159 - - !!str 23 - - null -""" - -var - parser = newYamlParser(initCoreTagLibrary()) - events = parser.parse(newStringStream(input)) - -for event in events(): - echo $event -``` - -Output: - -``` -yamlStartDocument() -yamlStartMap(tag=?) -yamlScalar(tag=?, typeHint=yTypeUnknown, content="an integer") -yamlScalar(tag=?, typeHint=yTypeInteger, content="42") -yamlScalar(tag=?, typeHint=yTypeUnknown, content="a boolean") -yamlScalar(tag=?, typeHint=yTypeBoolTrue, content="yes") -yamlScalar(tag=?, typeHint=yTypeUnknown, content="a list") -yamlStartSequence(tag=?) -yamlScalar(tag=?, typeHint=yTypeFloat, content="3.14159") -yamlScalar(tag=!!str, typeHint=yTypeInteger, content="23") -yamlScalar(tag=?, typeHint=yTypeNull, content="null") -yamlEndSequence() -yamlEndMap() -yamlEndDocument() -``` - -### Dumping YAML - -```Nimrod -import yaml, streams - -proc example(): YamlStream = - result = iterator(): YamlStreamEvent = - yield startDocEvent() - yield startMapEvent() - yield scalarEvent("an integer") - yield scalarEvent("42", tag = yTagInteger) - yield scalarEvent("a list") - yield startSeqEvent(tag = yTagSequence) - yield scalarEvent("item", tag = yTagString) - yield scalarEvent("no", tag = yTagBoolean) - yield scalarEvent("") - yield endSeqEvent() - yield scalarEvent("a float") - yield scalarEvent("3.14159", tag = yTagFloat) - yield endMapEvent() - yield endDocEvent() - -present(example(), newFileStream(stdout), initCoreTagLibrary(), psBlockOnly) -echo "\n\n" -present(example(), newFileStream(stdout), initCoreTagLibrary(), psCanonical) -echo "\n\n" -present(example(), newFileStream(stdout), initCoreTagLibrary(), psJson) -``` - -Output: - -``` -%YAML 1.2 ---- -an integer: !!int 42 -a list: !!seq - - !!str item - - !!bool no - - "" -a float: !!float 3.14159 - - -%YAML 1.2 ---- -{ - ? "an integer" - : !!int "42", - ? "a list" - : !!seq [ - !!str "item", - !!bool "no", - "" - ], - ? "a float" - : !!float "3.14159" -} - - -{ - "an integer": 42, - "a list": [ - "item", - false, - "" - ], - "a float": 3.14159 -} -``` - -### Using Nim Type Serialization - -**Attention**: This feature is highly experimental! - -```Nimrod -import yaml.serialization -import tables - -serializable: - type - Person = object - firstname, surname: string - age: int32 - additionalAttributes: Table[string, string] - -# loading - -let input = """ -- - firstname: Peter - surname: Pan - age: 12 - additionalAttributes: - canFly: yes - location: Neverland -- - firstname: Karl - surname: Koch - age: 23 - additionalAttributes: - location: Celle - occupation: Hacker -""" - -var persons: seq[Person] -load(newStringStream(input), persons) -assert persons[0].surname == "Pan" -assert persons[1].additionalAttributes["location"] == "Celle" - -# dumping - -dump(persons, newFileStream(stdout), psCanonical) -``` - -Output: - -``` -%YAML 1.2 ---- !nim:seq(nim:Person) -[ - !nim:Person { - ? !!str "firstname" - : !!str "Peter", - ? !!str "surname" - : !!str "Pan", - ? !!str "age" - : !nim:int "12", - ? !!str "additionalAttributes" - : !nim:Table(tag:yaml.org,2002:str,tag:yaml.org,2002:str) { - ? !!str "canFly" - : !!str "yes", - ? !!str "location" - : !!str "Neverland" - } - }, - !nim:Person { - ? !!str "firstname" - : !!str "Karl", - ? !!str "surname" - : !!str "Koch", - ? !!str "age" - : !nim:int "23", - ? !!str "additionalAttributes" - : !nim:Table(tag:yaml.org,2002:str,tag:yaml.org,2002:str) { - ? !!str "occupation" - : !!str "Hacker", - ? !!str "location" - : !!str "Celle" - } - } -] -``` +NimYAML is currently being developed. There is no release yet. See +[the documentation](http://flyx.github.io/NimYAML/) for an overview of already +available features. ## TODO list - * Documentation: - - Document yaml.serialization * Misc: - Add type hints for more scalar types * Serialization: @@ -216,7 +14,6 @@ Output: - Support variant objects - Support transient fields (i.e. fields that will not be (de-)serialized on objects and tuples) - - Make it possible for user to define a tag URI for custom types - Use `concept` type class `Serializable` or something - Check for and avoid name clashes when generating local tags for custom object types. diff --git a/config.nims b/config.nims index b05238c..0d4c5a9 100644 --- a/config.nims +++ b/config.nims @@ -22,6 +22,7 @@ task documentation, "Generate documentation": exec "mkdir -p docout" exec r"nim doc2 -o:docout/yaml.html --docSeeSrcUrl:https://github.com/flyx/NimYAML/blob/`git log -n 1 --format=%H` yaml" exec r"nim rst2html -o:docout/index.html doc/index.txt" + exec r"nim rst2html -o:docout/api.html doc/api.txt" exec "cp doc/docutils.css doc/style.css doc/testing.html doc/processing.svg docout" setCommand "nop" diff --git a/doc/api.txt b/doc/api.txt new file mode 100644 index 0000000..2f6cc9f --- /dev/null +++ b/doc/api.txt @@ -0,0 +1,86 @@ +=== +API +=== + +Overview +======== + +NimYAML advocates parsing YAML input into native Nim types. Basic Nim library +types like integers, floats and strings, as well as all tuples, enums and +objects without private fields are supported out-of-the-box. Reference types are +also supported, and NimYAML is able to detect if a reference occurs more than +once and will serialize it accordingly. This means that NimYAML is able to dump +and load potentially cyclic objects. + +While loading into and dumping from native Nim types is the preferred way to use +NimYAML, it also gives you complete control over each processing step, so that +you can for example only use the parser and process its event stream yourself. +The following diagram gives an overview of NimYAML's features based on the YAML +processing pipeline. The items and terminology YAML defines is shown in +*italic*, NimYAML's implementation name is shown in **bold**. + +.. image:: processing.svg + +Intermediate Representation +=========================== + +The base of all YAML processing with NimYAML is the +`YamlStream `_. This is basically an iterator over +`YamlStreamEvent `_ objects. Every proc that +represents a single stage of the loading or dumping process will either take a +``YamlStream`` as input or return a ``YamlStream``. Procs that implement the +whole process in one step hide the ``YamlStream`` from the user. Every proc that +returns a ``YamlStream`` guarantees that this stream is well-formed according to +the YAML specification. + +This stream-oriented API can efficiently be used to parse large amounts of data. +The drawback is that errors in the input are only discovered while processing +the ``YamlStream``. If the ``YamlStream`` encounters an exception while +producing the next event, it will throw a ``YamlStreamError`` which contains the +original exception as ``parent``. The caller should know which exceptions are +possible as parents of ``YamlStream`` because they know the source of the +``YamlStream`` they provided. + +Loading YAML +============ + +If you want to load YAML character data directly into a native Nim variable, you +can use `load `_. This is the easiest and recommended +way to load YAML data. The following paragraphs will explain the steps involved. + +For parsing, a `YamlParser `_ object is needed. This +object stores some state while parsing that may be useful for error reporting to +the user. The +`parse `_ +proc implements the YAML processing step of the same name. All syntax errors in +the input character stream are processed by ``parse``, which will raise a +``YamlParserError`` if it encounters a syntax error. + +Transforming a ``YamlStream`` to a native YAML object is done via +``construct``. It skips the ``compose`` step for efficiency reasons. As Nim is +statically typed, you have to know the target type when you write your loading +code. This is different from YAML APIs of dynamically typed languages. If you +cannot know the type of your YAML input at compile time, you have to manually +process the ``YamlStream`` to serve your needs. + +Dumping YAML +============ + +Dumping is preferredly done with +`dump `_, +which serializes a native Nim variable to a character stream. Like ``load``, you +can use the steps involved separately. + +You transform a variable into a ``YamlStream`` with +`represent `_. Depending on the +``AnchorStyle`` you specify, this will transform ``ref`` variables with multiple +instances into anchored elements and aliases (for ``asTidy`` and ``asAlways``) +or write the same element into all places it occurs (for ``asNone``). Be aware +that if you use ``asNone``, the value you serialize might not round-trip. + +Transforming a ``YamlStream`` into YAML character data is done with +`present `_. +You can choose from multiple presentation styles. ``psJson`` is not able to +process some features of ``YamlStream``s, the other styles support all features +and are guaranteed to round-trip to the same ``YamlStream`` if you parse the +generated YAML character stream again. diff --git a/doc/index.txt b/doc/index.txt index 2a5f511..13dc367 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -70,78 +70,3 @@ NimYAML's features. of other: echo "child: ", child.name echo "------------------------" dump(persons, newFileStream(stdout)) - -API Overview -============ - -NimYAML advocates parsing YAML input into native Nim types. Basic library types -like integers, floats and strings, as well as all tuples, enums and objects -without private fields are supported out-of-the-box. Reference types are also -supported, and NimYAML is able to detect if a reference occurs more than once -and will serialize it accordingly. This means that NimYAML is able to dump and -load potentially cyclic objects. - -While loading into and dumping from native Nim types is the preferred way to use -NimYAML, it also gives you complete control over each processing step, so that -you can for example only use the parser and process its event stream yourself. -The following diagram gives an overview of NimYAML's features based on the YAML -processing pipeline. The items and terminology YAML defines is shown in -*italic*, NimYAML's implementation name is shown in **bold**. - -.. image:: processing.svg - -Intermediate Representation ---------------------------- - -The base of all YAML processing with NimYAML is the -`YamlStream `_. This is basically an iterator over -`YamlStreamEvent `_ objects. Every proc that -represents a single stage of the loading or dumping process will either take a -``YamlStream`` as input or return a ``YamlStream``. Procs that implement the -whole process in one step hide the ``YamlStream`` from the user. Every proc that -returns a ``YamlStream`` guarantees that this stream is well-formed according to -the YAML specification. - -This stream-oriented API can efficiently be used to parse large amounts of data. -The drawback is that errors in the input are only discovered while processing -the ``YamlStream``. If the ``YamlStream`` encounters an exception while -producing the next event, it will throw a ``YamlStreamError`` which contains the -original exception as ``parent``. The caller should know which exceptions are -possible as parents of ``YamlStream`` because they know the source of the -``YamlStream`` they provided. - -Loading YAML ------------- - -For parsing, a `YamlParser `_ object is needed. This -object stores some state while parsing that may be useful for error reporting to -the user. The -`parse `_ -proc implements the YAML processing step of the same name. All syntax errors in -the input character stream are processed by ``parse``, which will raise a -``YamlParserError`` if it encounters a syntax error. - -Transforming a ``YamlStream`` to a native YAML object is done via -``construct``. It skips the ``compose`` step for efficiency reasons. As Nim is -statically typed, you have to know the target type when you write your loading -code. This is different from YAML APIs of dynamically typed languages. If you -cannot know the type of your YAML input at compile time, you have to manually -process the ``YamlStream`` to serve your needs. - -If you want to load YAML character data directly into a native Nim variable, you -can use `load `_. - -Dumping YAML ------------- - -Dumping YAML is straightforward: You transform a variable into a ``YamlStream`` -with `represent `_ and then write -that to a stream using -`present `_. -If you want to execute both steps at once, you can use -`dump `_. - -The ``present`` step allows you to specify how you want the output YAML -character stream to be formatted. Amongst other options, it is possible to -output pure JSON, but only if the stream does not contain any constructs that -cannot be presented in JSON. \ No newline at end of file diff --git a/doc/processing.svg b/doc/processing.svg index 770433a..8b62802 100644 --- a/doc/processing.svg +++ b/doc/processing.svg @@ -96,9 +96,7 @@ - - represent - + represent @@ -115,9 +113,7 @@ - - construct - + construct diff --git a/private/serialization.nim b/private/serialization.nim index 88a828e..1eb57bf 100644 --- a/private/serialization.nim +++ b/private/serialization.nim @@ -23,17 +23,6 @@ proc initSerializationTagLibrary(): TagLibrary {.raises: [].} = result.tags["tag:yaml.org,2002:timestamp"] = yTagTimestamp result.tags["tag:yaml.org,2002:value"] = yTagValue result.tags["tag:yaml.org,2002:binary"] = yTagBinary - result.tags["!nim:system:int8"] = yTagNimInt8 - result.tags["!nim:system:int16"] = yTagNimInt16 - result.tags["!nim:system:int32"] = yTagNimInt32 - result.tags["!nim:system:int64"] = yTagNimInt64 - result.tags["!nim:system:uint8"] = yTagNimUInt8 - result.tags["!nim:system:uint16"] = yTagNimUInt16 - result.tags["!nim:system:uint32"] = yTagNimUInt32 - result.tags["!nim:system:uint64"] = yTagNimUInt64 - result.tags["!nim:system:float32"] = yTagNimFloat32 - result.tags["!nim:system:float64"] = yTagNimFloat64 - result.tags["!nim:system:char"] = yTagNimChar var serializationTagLibrary* = initSerializationTagLibrary() ## \ @@ -44,7 +33,6 @@ var ## Should not be modified manually. Will be extended by ## `serializable <#serializable,stmt,stmt>`_. - static: iterator objectFields(n: NimNode): tuple[name: NimNode, t: NimNode] {.raises: [].} = @@ -59,6 +47,31 @@ static: template presentTag(t: typedesc, ts: TagStyle): TagId = if ts == tsNone: yTagQuestionMark else: yamlTag(t) +template setTagUriForType*(t: typedesc, uri: string): stmt = + ## Associate the given uri with a certain type. This uri is used as YAML tag + ## when loading and dumping values of this type. + let id {.gensym.} = serializationTagLibrary.registerUri(uri) + proc yamlTag*(T: typedesc[t]): TagId {.inline, raises: [].} = id + +template setTagUriForType*(t: typedesc, uri: string, idName: expr): stmt = + ## Like `setTagUriForType <#setTagUriForType,typedesc,string>`_, but lets + ## you choose a symbol for the `TagId <#TagId>`_ of the uri. This is only + ## necessary if you want to implement serialization / construction yourself. + let idName* = serializationTagLibrary.registerUri(uri) + proc yamlTag*(T: typedesc[t]): TagId {.inline, raises: [].} = idName + +setTagUriForType(char, "!nim:system:char", yTagNimChar) +setTagUriForType(int8, "!nim:system:int8", yTagNimInt8) +setTagUriForType(int16, "!nim:system:int16", yTagNimInt16) +setTagUriForType(int32, "!nim:system:int32", yTagNimInt32) +setTagUriForType(int64, "!nim:system:int64", yTagNimInt64) +setTagUriForType(uint8, "!nim:system:uint8", yTagNimUInt8) +setTagUriForType(uint16, "!nim:system:uint16", yTagNimUInt16) +setTagUriForType(uint32, "!nim:system:uint32", yTagNimUInt32) +setTagUriForType(uint64, "!nim:system:uint64", yTagNimUInt64) +setTagUriForType(float32, "!nim:system:float32", yTagNimFloat32) +setTagUriForType(float64, "!nim:system:float64", yTagNimFloat64) + proc lazyLoadTag*(uri: string): TagId {.inline, raises: [].} = ## Internal function. Do not call explicitly. try: @@ -275,7 +288,8 @@ template constructScalarItem(bs: var YamlStream, item: YamlStreamEvent, e.parent = getCurrentException() raise e -proc yamlTag*(T: typedesc[string]): TagId {.inline, raises: [].} = yTagString +proc yamlTag*(T: typedesc[string]): TagId {.inline, noSideEffect, raises: [].} = + yTagString proc constructObject*(s: var YamlStream, c: ConstructionContext, result: var string) @@ -289,11 +303,6 @@ proc representObject*(value: string, ts: TagStyle = tsNone, result = iterator(): YamlStreamEvent = yield scalarEvent(value, presentTag(string, ts), yAnchorNone) -proc yamlTag*(T: typedesc[int8]): TagId {.inline, raises: [].} = yTagNimInt8 -proc yamlTag*(T: typedesc[int16]): TagId {.inline, raises: [].} = yTagNimInt16 -proc yamlTag*(T: typedesc[int32]): TagId {.inline, raises: [].} = yTagNimInt32 -proc yamlTag*(T: typedesc[int64]): TagId {.inline, raises: [].} = yTagNimInt64 - proc constructObject*[T: int8|int16|int32|int64]( s: var YamlStream, c: ConstructionContext, result: var T) {.raises: [YamlConstructionError, YamlStreamError].} = @@ -317,11 +326,6 @@ template representObject*(value: int, tagStyle: TagStyle, {.fatal: "The length of `int` is platform dependent. Use int[8|16|32|64].".} discard -proc yamlTag*(T: typedesc[uint8]): TagId {.inline, raises: [].} = yTagNimUInt8 -proc yamlTag*(T: typedesc[uint16]): TagId {.inline, raises: [].} = yTagNimUInt16 -proc yamlTag*(T: typedesc[uint32]): TagId {.inline, raises: [].} = yTagNimUInt32 -proc yamlTag*(T: typedesc[uint64]): TagId {.inline, raises: [].} = yTagNimUInt64 - {.push overflowChecks: on.} proc parseBiggestUInt(s: string): uint64 = result = 0 @@ -359,11 +363,6 @@ template representObject*(value: uint, ts: TagStyle, c: SerializationContext): "The length of `uint` is platform dependent. Use uint[8|16|32|64].".} discard -proc yamlTag*(T: typedesc[float32]): TagId {.inline, raises: [].} = - yTagNimFloat32 -proc yamlTag*(T: typedesc[float64]): TagId {.inline, raises: [].} = - yTagNimFloat64 - proc constructObject*[T: float32|float64]( s: var YamlStream, c: ConstructionContext, result: var T) {.raises: [YamlConstructionError, YamlStreamError].} = @@ -431,8 +430,6 @@ proc representObject*(value: bool, ts: TagStyle, yield scalarEvent(if value: "y" else: "n", presentTag(bool, ts), yAnchorNone) -proc yamlTag*(T: typedesc[char]): TagId {.inline, raises: [].} = yTagNimChar - proc constructObject*(s: var YamlStream, c: ConstructionContext, result: var char) {.raises: [YamlConstructionError, YamlStreamError].} = @@ -617,6 +614,11 @@ proc constructObject*[O: enum](s: var YamlStream, c: ConstructionContext, if e.kind != yamlScalar: raise newException(YamlConstructionError, "Expected scalar, got " & $e.kind) + if e.scalarAnchor != yAnchorNone: + raise newException(YamlConstructionError, "Anchor on a non-ref type") + if e.scalarTag notin [yTagQuestionMark, yamlTag(O)]: + raise newException(YamlConstructionError, + "Wrong tag for " & type(O).name) try: result = parseEnum[O](e.scalarContent) except ValueError: var ex = newException(YamlConstructionError, "Cannot parse '" & diff --git a/test/serializing.nim b/test/serializing.nim index fa0de94..9307c9f 100644 --- a/test/serializing.nim +++ b/test/serializing.nim @@ -11,13 +11,17 @@ type tlGreen, tlYellow, tlRed Person = object - firstname, surname: string + firstnamechar: char + surname: string age: int32 Node = object value: string next: ref Node +setTagUriForType(TrafficLight, "!tl") +setTagUriForType(Node, "!example.net:Node") + template assertStringEqual(expected, actual: string) = for i in countup(0, min(expected.len, actual.len)): if expected[i] != actual[i]: @@ -93,7 +97,7 @@ suite "Serialization": output.data test "Serialization: Load Enum": - let input = newStringStream("- tlRed\n- tlGreen\n- tlYellow") + let input = newStringStream("!nim:system:seq(tl)\n- !tl tlRed\n- tlGreen\n- tlYellow") var result: seq[TrafficLight] parser = newYamlParser(tagLib) @@ -130,22 +134,22 @@ suite "Serialization": output.data test "Serialization: Load custom object": - let input = newStringStream("firstname: Peter\nsurname: Pan\nage: 12") + let input = newStringStream("firstnamechar: P\nsurname: Pan\nage: 12") var result: Person parser = newYamlParser(tagLib) events = parser.parse(input) construct(events, result) - assert result.firstname == "Peter" + assert result.firstnamechar == 'P' assert result.surname == "Pan" assert result.age == 12 test "Serialization: Represent custom object": - let input = Person(firstname: "Peter", surname: "Pan", age: 12) + let input = Person(firstnamechar: 'P', surname: "Pan", age: 12) var output = newStringStream() dump(input, output, psBlockOnly, tsNone) assertStringEqual( - "%YAML 1.2\n--- \nfirstname: Peter\nsurname: Pan\nage: 12", + "%YAML 1.2\n--- \nfirstnamechar: P\nsurname: Pan\nage: 12", output.data) test "Serialization: Load sequence with explicit tags": @@ -169,22 +173,22 @@ suite "Serialization": test "Serialization: Load custom object with explicit root tag": let input = newStringStream( - "--- !nim:custom:Person\nfirstname: Peter\nsurname: Pan\nage: 12") + "--- !nim:custom:Person\nfirstnamechar: P\nsurname: Pan\nage: 12") var result: Person parser = newYamlParser(tagLib) events = parser.parse(input) construct(events, result) - assert result.firstname == "Peter" + assert result.firstnamechar == 'P' assert result.surname == "Pan" assert result.age == 12 test "Serialization: Represent custom object with explicit root tag": - let input = Person(firstname: "Peter", surname: "Pan", age: 12) + let input = Person(firstnamechar: 'P', surname: "Pan", age: 12) var output = newStringStream() dump(input, output, psBlockOnly, tsRootOnly) assertStringEqual("%YAML 1.2\n" & - "--- !nim:custom:Person \nfirstname: Peter\nsurname: Pan\nage: 12", + "--- !nim:custom:Person \nfirstnamechar: P\nsurname: Pan\nage: 12", output.data) test "Serialization: Represent cyclic data structure": @@ -198,7 +202,7 @@ suite "Serialization": var output = newStringStream() dump(a, output, psBlockOnly, tsRootOnly) assertStringEqual """%YAML 1.2 ---- !nim:custom:Node &a +--- !example.net:Node &a value: a next: value: b @@ -208,7 +212,7 @@ next: test "Serialization: Load cyclic data structure": let input = newStringStream("""%YAML 1.2 ---- !nim:system:seq(nim:custom:Node) +--- !nim:system:seq(example.net:Node) - &a value: a next: &b diff --git a/yaml.nim b/yaml.nim index 9d49f66..474e532 100644 --- a/yaml.nim +++ b/yaml.nim @@ -126,7 +126,7 @@ type ## ## When `YamlParser <#YamlParser>`_ encounters tags not existing in the ## tag library, it will use - ## `registerTagUri <#registerTagUri,TagLibrary,string>`_ to add + ## `registerUri <#registerUri,TagLibrary,string>`_ to add ## the tag to the library. ## ## You can base your tag library on common tag libraries by initializing @@ -343,18 +343,6 @@ const yamlTagRepositoryPrefix* = "tag:yaml.org,2002:" - yTagNimInt8* = 100.TagId ## tag for Nim's ``int8`` - yTagNimInt16* = 101.TagId ## tag for Nim's ``int16`` - yTagNimInt32* = 102.TagId ## tag for Nim's ``int32`` - yTagNimInt64* = 103.TagId ## tag for Nim's ``int64`` - yTagNimUInt8* = 104.TagId ## tag for Nim's ``uint8`` - yTagNimUInt16* = 105.TagId ## tag for Nim's ``uint16`` - yTagNimUInt32* = 106.TagId ## tag for Nim's ``uint32`` - yTagNimUInt64* = 107.TagId ## tag for Nim's ``uint64`` - yTagNimFloat32* = 108.TagId ## tag for Nim's ``float32`` - yTagNimFloat64* = 109.TagId ## tag for Nim's ``float64`` - yTagNimChar* = 110.TagId ## tag for Nim's ``char`` - # interface proc `==`*(left: YamlStreamEvent, right: YamlStreamEvent): bool {.raises: [].}