diff options
Diffstat (limited to '')
23 files changed, 8901 insertions, 0 deletions
diff --git a/third_party/rust/yaml-rust/.cargo-checksum.json b/third_party/rust/yaml-rust/.cargo-checksum.json new file mode 100644 index 0000000000..691d433831 --- /dev/null +++ b/third_party/rust/yaml-rust/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"a75998481fa3bb473289f01e8c55137749142d746aed2f2ef9feccd6141d43cf","Cargo.toml":"4adb2c36b8ac7d33a6e8da1b183d6c74aa988e8b1417e48b23875a1add1b6755","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"70480dc70c2fe1d9bbb64dab9a0abce266332905ffef9cabeae26dd027716ecb","README.md":"281e1d57e21d65d58d2de671393d497e67f5f21e6d38caba54dc64995a728a9a","appveyor.yml":"becabe3922fd9bc72331ea1314a691898bb196b6555b4d32d20a67820c122687","examples/dump_yaml.rs":"fb108a4284f094b3addb2c45901ef49edd7fb7ed17bc7316144902030418ef08","src/emitter.rs":"56d1d454684e40fb5f18fecea84598182d8670fca30a6dbf202f834677d15290","src/lib.rs":"a33c8bfc201b85a460a29974a2029994cdeced07b740ab625df75fa13a39709f","src/parser.rs":"bc062df4df3299aa9d0d86b3b0724a786e2593558b514831f073c2fa130733e8","src/scanner.rs":"cfa7e1c6257b036c446f229547af6f9e06f607b1d3e71d80b078cebae7589b9e","src/yaml.rs":"db63ad6c3d35bfb93595902da0de35e16d84b6219c05fe5906614c28f34ed4f7","tests/quickcheck.rs":"ceda16fabd3af862d5eb1c591f065f7a13355fb8ff9c46938803bd4dd42c6507","tests/spec_test.rs":"1b02d189c98b7bfa2774ea49196e90986898e54950be5e48c2431d2c72584486","tests/spec_test.rs.inc":"544c4d483bbde2401d869c6bf3f2246d3b79fbd13686186acf008f1e19cc86f6","tests/specexamples.rs.inc":"9fa45b9ab96063371878f13f4c3560c560a9c3ba0d71b678265c54523148651c","tests/specs/cpp2rust.rb":"054eca3596adfa59aeec65a15467e41eed472fdcab94900705779cb3376b1c7b","tests/specs/handler_spec_test.cpp":"da8bd253c852ede98e77e8f9196bfaccdae48b77409c3c481b80b6ef56fa40df","tests/specs/libyaml_fail-01.yaml":"c71bced67468f970c195caf605c65fd94009a3be692287a0ad119d9b8e976788","tests/specs/libyaml_fail-02.yaml":"c2e77dd417a474b257651f73f685009d267388be62235d11f195a350a7751ec5","tests/specs/libyaml_fail-03.yaml":"6b0db56f8ce8b2e0ba0a361b86067718a67839b2ce62eb91165ea93f22a9a4a7","tests/test_round_trip.rs":"f576109a713baf50d82e58c04598f286010eca9e75d713f1dca29ebbae7b40ca"},"package":"56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"}
\ No newline at end of file diff --git a/third_party/rust/yaml-rust/Cargo.lock b/third_party/rust/yaml-rust/Cargo.lock new file mode 100644 index 0000000000..40e083a94e --- /dev/null +++ b/third_party/rust/yaml-rust/Cargo.lock @@ -0,0 +1,176 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" +dependencies = [ + "memchr", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" + +[[package]] +name = "linked-hash-map" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "quickcheck" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" +dependencies = [ + "env_logger", + "log", + "rand", + "rand_core", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex" +version = "1.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +dependencies = [ + "linked-hash-map", + "quickcheck", +] diff --git a/third_party/rust/yaml-rust/Cargo.toml b/third_party/rust/yaml-rust/Cargo.toml new file mode 100644 index 0000000000..42fb742442 --- /dev/null +++ b/third_party/rust/yaml-rust/Cargo.toml @@ -0,0 +1,27 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "yaml-rust" +version = "0.4.5" +authors = ["Yuheng Chen <yuhengchen@sensetime.com>"] +description = "The missing YAML 1.2 parser for rust" +homepage = "http://chyh1990.github.io/yaml-rust/" +documentation = "https://docs.rs/yaml-rust" +readme = "README.md" +license = "MIT/Apache-2.0" +repository = "https://github.com/chyh1990/yaml-rust" +[dependencies.linked-hash-map] +version = "0.5.3" +[dev-dependencies.quickcheck] +version = "0.9" diff --git a/third_party/rust/yaml-rust/LICENSE-APACHE b/third_party/rust/yaml-rust/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/third_party/rust/yaml-rust/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/third_party/rust/yaml-rust/LICENSE-MIT b/third_party/rust/yaml-rust/LICENSE-MIT new file mode 100644 index 0000000000..2526547425 --- /dev/null +++ b/third_party/rust/yaml-rust/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Chen Yuheng + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/rust/yaml-rust/README.md b/third_party/rust/yaml-rust/README.md new file mode 100644 index 0000000000..a31faf12e8 --- /dev/null +++ b/third_party/rust/yaml-rust/README.md @@ -0,0 +1,125 @@ +# yaml-rust + +The missing YAML 1.2 implementation for Rust. + +[![Travis](https://travis-ci.org/chyh1990/yaml-rust.svg?branch=master)](https://travis-ci.org/chyh1990/yaml-rust) +[![AppVeyor](https://ci.appveyor.com/api/projects/status/scf47535ckp4ylg4?svg=true)](https://ci.appveyor.com/project/chyh1990/yaml-rust) +[![crates.io](https://img.shields.io/crates/v/yaml-rust.svg)](https://crates.io/crates/yaml-rust) +[![docs.rs](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/yaml-rust) + +`yaml-rust` is a pure Rust YAML 1.2 implementation, +which enjoys the memory safety +property and other benefits from the Rust language. +The parser is heavily influenced by `libyaml` and `yaml-cpp`. + +## Quick Start + +Add the following to the Cargo.toml of your project: + +```toml +[dependencies] +yaml-rust = "0.4" +``` + +and import: + +```rust +extern crate yaml_rust; +``` + +Use `yaml::YamlLoader` to load the YAML documents and access it +as Vec/HashMap: + +```rust +extern crate yaml_rust; +use yaml_rust::{YamlLoader, YamlEmitter}; + +fn main() { + let s = +" +foo: + - list1 + - list2 +bar: + - 1 + - 2.0 +"; + let docs = YamlLoader::load_from_str(s).unwrap(); + + // Multi document support, doc is a yaml::Yaml + let doc = &docs[0]; + + // Debug support + println!("{:?}", doc); + + // Index access for map & array + assert_eq!(doc["foo"][0].as_str().unwrap(), "list1"); + assert_eq!(doc["bar"][1].as_f64().unwrap(), 2.0); + + // Chained key/array access is checked and won't panic, + // return BadValue if they are not exist. + assert!(doc["INVALID_KEY"][100].is_badvalue()); + + // Dump the YAML object + let mut out_str = String::new(); + { + let mut emitter = YamlEmitter::new(&mut out_str); + emitter.dump(doc).unwrap(); // dump the YAML object to a String + } + println!("{}", out_str); +} +``` + +Note that `yaml_rust::Yaml` implements `Index<&'a str>` & `Index<usize>`: + +* `Index<usize>` assumes the container is an Array +* `Index<&'a str>` assumes the container is a string to value Map +* otherwise, `Yaml::BadValue` is returned + +If your document does not conform to this convention (e.g. map with +complex type key), you can use the `Yaml::as_XXX` family API to access your +documents. + +## Features + +* Pure Rust +* Ruby-like Array/Hash access API +* Low-level YAML events emission + +## Specification Compliance + +This implementation aims to provide YAML parser fully compatible with +the YAML 1.2 specification. The parser can correctly parse almost all +examples in the specification, except for the following known bugs: + +* Empty plain scalar in certain contexts + +However, the widely used library `libyaml` also fails to parse these examples, +so it may not be a huge problem for most users. + +## Goals + +* Encoder +* Tag directive +* Alias while deserialization + +## Minimum Rust version policy + +This crate's minimum supported `rustc` version is 1.31 (released with Rust 2018, after v0.4.3), as this is the currently known minimum version for [`regex`](https://crates.io/crates/regex#minimum-rust-version-policy) as well. + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +## Contribution + +Fork & PR on Github. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any +additional terms or conditions. diff --git a/third_party/rust/yaml-rust/appveyor.yml b/third_party/rust/yaml-rust/appveyor.yml new file mode 100644 index 0000000000..5b3ee4c1c3 --- /dev/null +++ b/third_party/rust/yaml-rust/appveyor.yml @@ -0,0 +1,65 @@ +clone_depth: 1 + +branches: + only: + - master + +environment: + LLVM_VERSION: 9.0.1 + PLATFORM: x64 + matrix: + - channel: stable + target: i686-pc-windows-msvc + type: msvc + - channel: stable + target: x86_64-pc-windows-msvc + type: msvc + - channel: stable + target: i686-pc-windows-gnu + type: gnu + - channel: stable + target: x86_64-pc-windows-gnu + type: gnu + - channel: nightly + target: i686-pc-windows-msvc + type: msvc + - channel: nightly + target: x86_64-pc-windows-msvc + type: msvc + - channel: nightly + target: i686-pc-windows-gnu + type: gnu + - channel: nightly + target: x86_64-pc-windows-gnu + type: gnu + +install: + - if %PLATFORM% == x86 (set RUST_PLATFORM=i686&set MINGW_BITS=32) else (set RUST_PLATFORM=x86_64&set MINGW_BITS=64) + - ps: >- + If ($env:target -eq 'x86_64-pc-windows-gnu') { + $env:PATH += ';C:\msys64\mingw64\bin' + } ElseIf ($env:target -eq 'i686-pc-windows-gnu') { + $env:PATH += ';C:\msys64\mingw32\bin' + } + - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe + - rustup-init -yv --default-toolchain %channel% --default-host %target% + - set PATH=%PATH%;%USERPROFILE%\.cargo\bin + - rustc -vV + - cargo -vV + # Install LLVM for GNU + - if %type%==gnu set PATH=C:\msys64\mingw%MINGW_BITS%\bin;C:\msys64\usr\bin;%PATH% + - if %type%==gnu set "MINGW_URL=http://repo.msys2.org/mingw/%RUST_PLATFORM%/mingw-w64-%RUST_PLATFORM%" + - if %type%==gnu set "URL_VER=%LLVM_VERSION%-1-any.pkg.tar.xz" + - if %type%==gnu bash -lc "pacman -U --noconfirm $MINGW_URL-clang-$URL_VER $MINGW_URL-llvm-$URL_VER" + - if %type%==gnu bash -lc "clang --version" + # Use preinstalled LLVM for MSVC + - if %type%==msvc set PATH=%PATH%;C:\Program Files\LLVM\bin + - if %type%==msvc where clang + - if %type%==msvc clang --version + +build_script: + - cargo build -vv +test_script: + - cargo test -vv +deploy: off + diff --git a/third_party/rust/yaml-rust/examples/dump_yaml.rs b/third_party/rust/yaml-rust/examples/dump_yaml.rs new file mode 100644 index 0000000000..8fce0f3c1a --- /dev/null +++ b/third_party/rust/yaml-rust/examples/dump_yaml.rs @@ -0,0 +1,46 @@ +extern crate yaml_rust; + +use std::env; +use std::fs::File; +use std::io::prelude::*; +use yaml_rust::yaml; + +fn print_indent(indent: usize) { + for _ in 0..indent { + print!(" "); + } +} + +fn dump_node(doc: &yaml::Yaml, indent: usize) { + match *doc { + yaml::Yaml::Array(ref v) => { + for x in v { + dump_node(x, indent + 1); + } + } + yaml::Yaml::Hash(ref h) => { + for (k, v) in h { + print_indent(indent); + println!("{:?}:", k); + dump_node(v, indent + 1); + } + } + _ => { + print_indent(indent); + println!("{:?}", doc); + } + } +} + +fn main() { + let args: Vec<_> = env::args().collect(); + let mut f = File::open(&args[1]).unwrap(); + let mut s = String::new(); + f.read_to_string(&mut s).unwrap(); + + let docs = yaml::YamlLoader::load_from_str(&s).unwrap(); + for doc in &docs { + println!("---"); + dump_node(doc, 0); + } +} diff --git a/third_party/rust/yaml-rust/src/emitter.rs b/third_party/rust/yaml-rust/src/emitter.rs new file mode 100644 index 0000000000..f20a3ed679 --- /dev/null +++ b/third_party/rust/yaml-rust/src/emitter.rs @@ -0,0 +1,635 @@ +use std::convert::From; +use std::error::Error; +use std::fmt::{self, Display}; +use crate::yaml::{Hash, Yaml}; + +#[derive(Copy, Clone, Debug)] +pub enum EmitError { + FmtError(fmt::Error), + BadHashmapKey, +} + +impl Error for EmitError { + fn cause(&self) -> Option<&dyn Error> { + None + } +} + +impl Display for EmitError { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match *self { + EmitError::FmtError(ref err) => Display::fmt(err, formatter), + EmitError::BadHashmapKey => formatter.write_str("bad hashmap key"), + } + } +} + +impl From<fmt::Error> for EmitError { + fn from(f: fmt::Error) -> Self { + EmitError::FmtError(f) + } +} + +pub struct YamlEmitter<'a> { + writer: &'a mut dyn fmt::Write, + best_indent: usize, + compact: bool, + + level: isize, +} + +pub type EmitResult = Result<(), EmitError>; + +// from serialize::json +fn escape_str(wr: &mut dyn fmt::Write, v: &str) -> Result<(), fmt::Error> { + wr.write_str("\"")?; + + let mut start = 0; + + for (i, byte) in v.bytes().enumerate() { + let escaped = match byte { + b'"' => "\\\"", + b'\\' => "\\\\", + b'\x00' => "\\u0000", + b'\x01' => "\\u0001", + b'\x02' => "\\u0002", + b'\x03' => "\\u0003", + b'\x04' => "\\u0004", + b'\x05' => "\\u0005", + b'\x06' => "\\u0006", + b'\x07' => "\\u0007", + b'\x08' => "\\b", + b'\t' => "\\t", + b'\n' => "\\n", + b'\x0b' => "\\u000b", + b'\x0c' => "\\f", + b'\r' => "\\r", + b'\x0e' => "\\u000e", + b'\x0f' => "\\u000f", + b'\x10' => "\\u0010", + b'\x11' => "\\u0011", + b'\x12' => "\\u0012", + b'\x13' => "\\u0013", + b'\x14' => "\\u0014", + b'\x15' => "\\u0015", + b'\x16' => "\\u0016", + b'\x17' => "\\u0017", + b'\x18' => "\\u0018", + b'\x19' => "\\u0019", + b'\x1a' => "\\u001a", + b'\x1b' => "\\u001b", + b'\x1c' => "\\u001c", + b'\x1d' => "\\u001d", + b'\x1e' => "\\u001e", + b'\x1f' => "\\u001f", + b'\x7f' => "\\u007f", + _ => continue, + }; + + if start < i { + wr.write_str(&v[start..i])?; + } + + wr.write_str(escaped)?; + + start = i + 1; + } + + if start != v.len() { + wr.write_str(&v[start..])?; + } + + wr.write_str("\"")?; + Ok(()) +} + +impl<'a> YamlEmitter<'a> { + pub fn new(writer: &'a mut dyn fmt::Write) -> YamlEmitter { + YamlEmitter { + writer, + best_indent: 2, + compact: true, + level: -1, + } + } + + /// Set 'compact inline notation' on or off, as described for block + /// [sequences](http://www.yaml.org/spec/1.2/spec.html#id2797382) + /// and + /// [mappings](http://www.yaml.org/spec/1.2/spec.html#id2798057). + /// + /// In this form, blocks cannot have any properties (such as anchors + /// or tags), which should be OK, because this emitter doesn't + /// (currently) emit those anyways. + pub fn compact(&mut self, compact: bool) { + self.compact = compact; + } + + /// Determine if this emitter is using 'compact inline notation'. + pub fn is_compact(&self) -> bool { + self.compact + } + + pub fn dump(&mut self, doc: &Yaml) -> EmitResult { + // write DocumentStart + writeln!(self.writer, "---")?; + self.level = -1; + self.emit_node(doc) + } + + fn write_indent(&mut self) -> EmitResult { + if self.level <= 0 { + return Ok(()); + } + for _ in 0..self.level { + for _ in 0..self.best_indent { + write!(self.writer, " ")?; + } + } + Ok(()) + } + + fn emit_node(&mut self, node: &Yaml) -> EmitResult { + match *node { + Yaml::Array(ref v) => self.emit_array(v), + Yaml::Hash(ref h) => self.emit_hash(h), + Yaml::String(ref v) => { + if need_quotes(v) { + escape_str(self.writer, v)?; + } else { + write!(self.writer, "{}", v)?; + } + Ok(()) + } + Yaml::Boolean(v) => { + if v { + self.writer.write_str("true")?; + } else { + self.writer.write_str("false")?; + } + Ok(()) + } + Yaml::Integer(v) => { + write!(self.writer, "{}", v)?; + Ok(()) + } + Yaml::Real(ref v) => { + write!(self.writer, "{}", v)?; + Ok(()) + } + Yaml::Null | Yaml::BadValue => { + write!(self.writer, "~")?; + Ok(()) + } + // XXX(chenyh) Alias + _ => Ok(()), + } + } + + fn emit_array(&mut self, v: &[Yaml]) -> EmitResult { + if v.is_empty() { + write!(self.writer, "[]")?; + } else { + self.level += 1; + for (cnt, x) in v.iter().enumerate() { + if cnt > 0 { + writeln!(self.writer)?; + self.write_indent()?; + } + write!(self.writer, "-")?; + self.emit_val(true, x)?; + } + self.level -= 1; + } + Ok(()) + } + + fn emit_hash(&mut self, h: &Hash) -> EmitResult { + if h.is_empty() { + self.writer.write_str("{}")?; + } else { + self.level += 1; + for (cnt, (k, v)) in h.iter().enumerate() { + let complex_key = match *k { + Yaml::Hash(_) | Yaml::Array(_) => true, + _ => false, + }; + if cnt > 0 { + writeln!(self.writer)?; + self.write_indent()?; + } + if complex_key { + write!(self.writer, "?")?; + self.emit_val(true, k)?; + writeln!(self.writer)?; + self.write_indent()?; + write!(self.writer, ":")?; + self.emit_val(true, v)?; + } else { + self.emit_node(k)?; + write!(self.writer, ":")?; + self.emit_val(false, v)?; + } + } + self.level -= 1; + } + Ok(()) + } + + /// Emit a yaml as a hash or array value: i.e., which should appear + /// following a ":" or "-", either after a space, or on a new line. + /// If `inline` is true, then the preceding characters are distinct + /// and short enough to respect the compact flag. + fn emit_val(&mut self, inline: bool, val: &Yaml) -> EmitResult { + match *val { + Yaml::Array(ref v) => { + if (inline && self.compact) || v.is_empty() { + write!(self.writer, " ")?; + } else { + writeln!(self.writer)?; + self.level += 1; + self.write_indent()?; + self.level -= 1; + } + self.emit_array(v) + } + Yaml::Hash(ref h) => { + if (inline && self.compact) || h.is_empty() { + write!(self.writer, " ")?; + } else { + writeln!(self.writer)?; + self.level += 1; + self.write_indent()?; + self.level -= 1; + } + self.emit_hash(h) + } + _ => { + write!(self.writer, " ")?; + self.emit_node(val) + } + } + } +} + +/// Check if the string requires quoting. +/// Strings starting with any of the following characters must be quoted. +/// :, &, *, ?, |, -, <, >, =, !, %, @ +/// Strings containing any of the following characters must be quoted. +/// {, }, [, ], ,, #, ` +/// +/// If the string contains any of the following control characters, it must be escaped with double quotes: +/// \0, \x01, \x02, \x03, \x04, \x05, \x06, \a, \b, \t, \n, \v, \f, \r, \x0e, \x0f, \x10, \x11, \x12, \x13, \x14, \x15, \x16, \x17, \x18, \x19, \x1a, \e, \x1c, \x1d, \x1e, \x1f, \N, \_, \L, \P +/// +/// Finally, there are other cases when the strings must be quoted, no matter if you're using single or double quotes: +/// * When the string is true or false (otherwise, it would be treated as a boolean value); +/// * When the string is null or ~ (otherwise, it would be considered as a null value); +/// * When the string looks like a number, such as integers (e.g. 2, 14, etc.), floats (e.g. 2.6, 14.9) and exponential numbers (e.g. 12e7, etc.) (otherwise, it would be treated as a numeric value); +/// * When the string looks like a date (e.g. 2014-12-31) (otherwise it would be automatically converted into a Unix timestamp). +fn need_quotes(string: &str) -> bool { + fn need_quotes_spaces(string: &str) -> bool { + string.starts_with(' ') || string.ends_with(' ') + } + + string == "" + || need_quotes_spaces(string) + || string.starts_with(|character: char| match character { + '&' | '*' | '?' | '|' | '-' | '<' | '>' | '=' | '!' | '%' | '@' => true, + _ => false, + }) + || string.contains(|character: char| match character { + ':' + | '{' + | '}' + | '[' + | ']' + | ',' + | '#' + | '`' + | '\"' + | '\'' + | '\\' + | '\0'..='\x06' + | '\t' + | '\n' + | '\r' + | '\x0e'..='\x1a' + | '\x1c'..='\x1f' => true, + _ => false, + }) + || [ + // http://yaml.org/type/bool.html + // Note: 'y', 'Y', 'n', 'N', is not quoted deliberately, as in libyaml. PyYAML also parse + // them as string, not booleans, although it is violating the YAML 1.1 specification. + // See https://github.com/dtolnay/serde-yaml/pull/83#discussion_r152628088. + "yes", "Yes", "YES", "no", "No", "NO", "True", "TRUE", "true", "False", "FALSE", + "false", "on", "On", "ON", "off", "Off", "OFF", + // http://yaml.org/type/null.html + "null", "Null", "NULL", "~", + ] + .contains(&string) + || string.starts_with('.') + || string.starts_with("0x") + || string.parse::<i64>().is_ok() + || string.parse::<f64>().is_ok() +} + +#[cfg(test)] +mod test { + use super::*; + use crate::YamlLoader; + + #[test] + fn test_emit_simple() { + let s = " +# comment +a0 bb: val +a1: + b1: 4 + b2: d +a2: 4 # i'm comment +a3: [1, 2, 3] +a4: + - [a1, a2] + - 2 +"; + + let docs = YamlLoader::load_from_str(&s).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + println!("original:\n{}", s); + println!("emitted:\n{}", writer); + let docs_new = match YamlLoader::load_from_str(&writer) { + Ok(y) => y, + Err(e) => panic!(format!("{}", e)), + }; + let doc_new = &docs_new[0]; + + assert_eq!(doc, doc_new); + } + + #[test] + fn test_emit_complex() { + let s = r#" +cataloge: + product: &coffee { name: Coffee, price: 2.5 , unit: 1l } + product: &cookies { name: Cookies!, price: 3.40 , unit: 400g} + +products: + *coffee: + amount: 4 + *cookies: + amount: 4 + [1,2,3,4]: + array key + 2.4: + real key + true: + bool key + {}: + empty hash key + "#; + let docs = YamlLoader::load_from_str(&s).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + let docs_new = match YamlLoader::load_from_str(&writer) { + Ok(y) => y, + Err(e) => panic!(format!("{}", e)), + }; + let doc_new = &docs_new[0]; + assert_eq!(doc, doc_new); + } + + #[test] + fn test_emit_avoid_quotes() { + let s = r#"--- +a7: 你好 +boolean: "true" +boolean2: "false" +date: 2014-12-31 +empty_string: "" +empty_string1: " " +empty_string2: " a" +empty_string3: " a " +exp: "12e7" +field: ":" +field2: "{" +field3: "\\" +field4: "\n" +field5: "can't avoid quote" +float: "2.6" +int: "4" +nullable: "null" +nullable2: "~" +products: + "*coffee": + amount: 4 + "*cookies": + amount: 4 + ".milk": + amount: 1 + "2.4": real key + "[1,2,3,4]": array key + "true": bool key + "{}": empty hash key +x: test +y: avoid quoting here +z: string with spaces"#; + + let docs = YamlLoader::load_from_str(&s).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + + assert_eq!(s, writer, "actual:\n\n{}\n", writer); + } + + #[test] + fn emit_quoted_bools() { + let input = r#"--- +string0: yes +string1: no +string2: "true" +string3: "false" +string4: "~" +null0: ~ +[true, false]: real_bools +[True, TRUE, False, FALSE, y,Y,yes,Yes,YES,n,N,no,No,NO,on,On,ON,off,Off,OFF]: false_bools +bool0: true +bool1: false"#; + let expected = r#"--- +string0: "yes" +string1: "no" +string2: "true" +string3: "false" +string4: "~" +null0: ~ +? - true + - false +: real_bools +? - "True" + - "TRUE" + - "False" + - "FALSE" + - y + - Y + - "yes" + - "Yes" + - "YES" + - n + - N + - "no" + - "No" + - "NO" + - "on" + - "On" + - "ON" + - "off" + - "Off" + - "OFF" +: false_bools +bool0: true +bool1: false"#; + + let docs = YamlLoader::load_from_str(&input).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + + assert_eq!( + expected, writer, + "expected:\n{}\nactual:\n{}\n", + expected, writer + ); + } + + #[test] + fn test_empty_and_nested() { + test_empty_and_nested_flag(false) + } + + #[test] + fn test_empty_and_nested_compact() { + test_empty_and_nested_flag(true) + } + + fn test_empty_and_nested_flag(compact: bool) { + let s = if compact { + r#"--- +a: + b: + c: hello + d: {} +e: + - f + - g + - h: []"# + } else { + r#"--- +a: + b: + c: hello + d: {} +e: + - f + - g + - + h: []"# + }; + + let docs = YamlLoader::load_from_str(&s).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.compact(compact); + emitter.dump(doc).unwrap(); + } + + assert_eq!(s, writer); + } + + #[test] + fn test_nested_arrays() { + let s = r#"--- +a: + - b + - - c + - d + - - e + - f"#; + + let docs = YamlLoader::load_from_str(&s).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + println!("original:\n{}", s); + println!("emitted:\n{}", writer); + + assert_eq!(s, writer); + } + + #[test] + fn test_deeply_nested_arrays() { + let s = r#"--- +a: + - b + - - c + - d + - - e + - - f + - - e"#; + + let docs = YamlLoader::load_from_str(&s).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + println!("original:\n{}", s); + println!("emitted:\n{}", writer); + + assert_eq!(s, writer); + } + + #[test] + fn test_nested_hashes() { + let s = r#"--- +a: + b: + c: + d: + e: f"#; + + let docs = YamlLoader::load_from_str(&s).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + println!("original:\n{}", s); + println!("emitted:\n{}", writer); + + assert_eq!(s, writer); + } + +} diff --git a/third_party/rust/yaml-rust/src/lib.rs b/third_party/rust/yaml-rust/src/lib.rs new file mode 100644 index 0000000000..6cf87c7c5e --- /dev/null +++ b/third_party/rust/yaml-rust/src/lib.rs @@ -0,0 +1,121 @@ +// Copyright 2015, Yuheng Chen. See the LICENSE file at the top-level +// directory of this distribution. + +//! YAML 1.2 implementation in pure Rust. +//! +//! # Usage +//! +//! This crate is [on github](https://github.com/chyh1990/yaml-rust) and can be +//! used by adding `yaml-rust` to the dependencies in your project's `Cargo.toml`. +//! +//! ```toml +//! [dependencies.yaml-rust] +//! git = "https://github.com/chyh1990/yaml-rust.git" +//! ``` +//! +//! And this in your crate root: +//! +//! ```rust +//! extern crate yaml_rust; +//! ``` +//! +//! Parse a string into `Vec<Yaml>` and then serialize it as a YAML string. +//! +//! # Examples +//! +//! ``` +//! use yaml_rust::{YamlLoader, YamlEmitter}; +//! +//! let docs = YamlLoader::load_from_str("[1, 2, 3]").unwrap(); +//! let doc = &docs[0]; // select the first document +//! assert_eq!(doc[0].as_i64().unwrap(), 1); // access elements by index +//! +//! let mut out_str = String::new(); +//! let mut emitter = YamlEmitter::new(&mut out_str); +//! emitter.dump(doc).unwrap(); // dump the YAML object to a String +//! +//! ``` + +#![doc(html_root_url = "https://docs.rs/yaml-rust/0.4.5")] +#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))] +#![cfg_attr(feature = "cargo-clippy", warn(cyclomatic_complexity))] +#![cfg_attr( + feature = "cargo-clippy", + allow(match_same_arms, should_implement_trait) +)] + +extern crate linked_hash_map; + +pub mod emitter; +pub mod parser; +pub mod scanner; +pub mod yaml; + +// reexport key APIs +pub use crate::emitter::{EmitError, YamlEmitter}; +pub use crate::parser::Event; +pub use crate::scanner::ScanError; +pub use crate::yaml::{Yaml, YamlLoader}; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_api() { + let s = " +# from yaml-cpp example +- name: Ogre + position: [0, 5, 0] + powers: + - name: Club + damage: 10 + - name: Fist + damage: 8 +- name: Dragon + position: [1, 0, 10] + powers: + - name: Fire Breath + damage: 25 + - name: Claws + damage: 15 +- name: Wizard + position: [5, -3, 0] + powers: + - name: Acid Rain + damage: 50 + - name: Staff + damage: 3 +"; + let docs = YamlLoader::load_from_str(s).unwrap(); + let doc = &docs[0]; + + assert_eq!(doc[0]["name"].as_str().unwrap(), "Ogre"); + + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + + assert!(!writer.is_empty()); + } + + fn try_fail(s: &str) -> Result<Vec<Yaml>, ScanError> { + let t = YamlLoader::load_from_str(s)?; + Ok(t) + } + + #[test] + fn test_fail() { + let s = " +# syntax error +scalar +key: [1, 2]] +key1:a2 +"; + assert!(YamlLoader::load_from_str(s).is_err()); + assert!(try_fail(s).is_err()); + } + +} diff --git a/third_party/rust/yaml-rust/src/parser.rs b/third_party/rust/yaml-rust/src/parser.rs new file mode 100644 index 0000000000..4a63146f13 --- /dev/null +++ b/third_party/rust/yaml-rust/src/parser.rs @@ -0,0 +1,858 @@ +use crate::scanner::*; +use std::collections::HashMap; + +#[derive(Clone, Copy, PartialEq, Debug, Eq)] +enum State { + StreamStart, + ImplicitDocumentStart, + DocumentStart, + DocumentContent, + DocumentEnd, + BlockNode, + // BlockNodeOrIndentlessSequence, + // FlowNode, + BlockSequenceFirstEntry, + BlockSequenceEntry, + IndentlessSequenceEntry, + BlockMappingFirstKey, + BlockMappingKey, + BlockMappingValue, + FlowSequenceFirstEntry, + FlowSequenceEntry, + FlowSequenceEntryMappingKey, + FlowSequenceEntryMappingValue, + FlowSequenceEntryMappingEnd, + FlowMappingFirstKey, + FlowMappingKey, + FlowMappingValue, + FlowMappingEmptyValue, + End, +} + +/// `Event` is used with the low-level event base parsing API, +/// see `EventReceiver` trait. +#[derive(Clone, PartialEq, Debug, Eq)] +pub enum Event { + /// Reserved for internal use + Nothing, + StreamStart, + StreamEnd, + DocumentStart, + DocumentEnd, + /// Refer to an anchor ID + Alias(usize), + /// Value, style, anchor_id, tag + Scalar(String, TScalarStyle, usize, Option<TokenType>), + /// Anchor ID + SequenceStart(usize), + SequenceEnd, + /// Anchor ID + MappingStart(usize), + MappingEnd, +} + +impl Event { + fn empty_scalar() -> Event { + // a null scalar + Event::Scalar("~".to_owned(), TScalarStyle::Plain, 0, None) + } + + fn empty_scalar_with_anchor(anchor: usize, tag: Option<TokenType>) -> Event { + Event::Scalar("".to_owned(), TScalarStyle::Plain, anchor, tag) + } +} + +#[derive(Debug)] +pub struct Parser<T> { + scanner: Scanner<T>, + states: Vec<State>, + state: State, + marks: Vec<Marker>, + token: Option<Token>, + current: Option<(Event, Marker)>, + anchors: HashMap<String, usize>, + anchor_id: usize, +} + +pub trait EventReceiver { + fn on_event(&mut self, ev: Event); +} + +pub trait MarkedEventReceiver { + fn on_event(&mut self, ev: Event, _mark: Marker); +} + +impl<R: EventReceiver> MarkedEventReceiver for R { + fn on_event(&mut self, ev: Event, _mark: Marker) { + self.on_event(ev) + } +} + +pub type ParseResult = Result<(Event, Marker), ScanError>; + +impl<T: Iterator<Item = char>> Parser<T> { + pub fn new(src: T) -> Parser<T> { + Parser { + scanner: Scanner::new(src), + states: Vec::new(), + state: State::StreamStart, + marks: Vec::new(), + token: None, + current: None, + + anchors: HashMap::new(), + // valid anchor_id starts from 1 + anchor_id: 1, + } + } + + pub fn peek(&mut self) -> Result<&(Event, Marker), ScanError> { + match self.current { + Some(ref x) => Ok(x), + None => { + self.current = Some(self.next()?); + self.peek() + } + } + } + + pub fn next(&mut self) -> ParseResult { + match self.current { + None => self.parse(), + Some(_) => Ok(self.current.take().unwrap()), + } + } + + fn peek_token(&mut self) -> Result<&Token, ScanError> { + match self.token { + None => { + self.token = Some(self.scan_next_token()?); + Ok(self.token.as_ref().unwrap()) + } + Some(ref tok) => Ok(tok), + } + } + + fn scan_next_token(&mut self) -> Result<Token, ScanError> { + let token = self.scanner.next(); + match token { + None => match self.scanner.get_error() { + None => Err(ScanError::new(self.scanner.mark(), "unexpected eof")), + Some(e) => Err(e), + }, + Some(tok) => Ok(tok), + } + } + + fn fetch_token(&mut self) -> Token { + self.token + .take() + .expect("fetch_token needs to be preceded by peek_token") + } + + fn skip(&mut self) { + self.token = None; + //self.peek_token(); + } + fn pop_state(&mut self) { + self.state = self.states.pop().unwrap() + } + fn push_state(&mut self, state: State) { + self.states.push(state); + } + + fn parse(&mut self) -> ParseResult { + if self.state == State::End { + return Ok((Event::StreamEnd, self.scanner.mark())); + } + let (ev, mark) = self.state_machine()?; + // println!("EV {:?}", ev); + Ok((ev, mark)) + } + + pub fn load<R: MarkedEventReceiver>( + &mut self, + recv: &mut R, + multi: bool, + ) -> Result<(), ScanError> { + if !self.scanner.stream_started() { + let (ev, mark) = self.next()?; + assert_eq!(ev, Event::StreamStart); + recv.on_event(ev, mark); + } + + if self.scanner.stream_ended() { + // XXX has parsed? + recv.on_event(Event::StreamEnd, self.scanner.mark()); + return Ok(()); + } + loop { + let (ev, mark) = self.next()?; + if ev == Event::StreamEnd { + recv.on_event(ev, mark); + return Ok(()); + } + // clear anchors before a new document + self.anchors.clear(); + self.load_document(ev, mark, recv)?; + if !multi { + break; + } + } + Ok(()) + } + + fn load_document<R: MarkedEventReceiver>( + &mut self, + first_ev: Event, + mark: Marker, + recv: &mut R, + ) -> Result<(), ScanError> { + assert_eq!(first_ev, Event::DocumentStart); + recv.on_event(first_ev, mark); + + let (ev, mark) = self.next()?; + self.load_node(ev, mark, recv)?; + + // DOCUMENT-END is expected. + let (ev, mark) = self.next()?; + assert_eq!(ev, Event::DocumentEnd); + recv.on_event(ev, mark); + + Ok(()) + } + + fn load_node<R: MarkedEventReceiver>( + &mut self, + first_ev: Event, + mark: Marker, + recv: &mut R, + ) -> Result<(), ScanError> { + match first_ev { + Event::Alias(..) | Event::Scalar(..) => { + recv.on_event(first_ev, mark); + Ok(()) + } + Event::SequenceStart(_) => { + recv.on_event(first_ev, mark); + self.load_sequence(recv) + } + Event::MappingStart(_) => { + recv.on_event(first_ev, mark); + self.load_mapping(recv) + } + _ => { + println!("UNREACHABLE EVENT: {:?}", first_ev); + unreachable!(); + } + } + } + + fn load_mapping<R: MarkedEventReceiver>(&mut self, recv: &mut R) -> Result<(), ScanError> { + let (mut key_ev, mut key_mark) = self.next()?; + while key_ev != Event::MappingEnd { + // key + self.load_node(key_ev, key_mark, recv)?; + + // value + let (ev, mark) = self.next()?; + self.load_node(ev, mark, recv)?; + + // next event + let (ev, mark) = self.next()?; + key_ev = ev; + key_mark = mark; + } + recv.on_event(key_ev, key_mark); + Ok(()) + } + + fn load_sequence<R: MarkedEventReceiver>(&mut self, recv: &mut R) -> Result<(), ScanError> { + let (mut ev, mut mark) = self.next()?; + while ev != Event::SequenceEnd { + self.load_node(ev, mark, recv)?; + + // next event + let (next_ev, next_mark) = self.next()?; + ev = next_ev; + mark = next_mark; + } + recv.on_event(ev, mark); + Ok(()) + } + + fn state_machine(&mut self) -> ParseResult { + // let next_tok = self.peek_token()?; + // println!("cur_state {:?}, next tok: {:?}", self.state, next_tok); + match self.state { + State::StreamStart => self.stream_start(), + + State::ImplicitDocumentStart => self.document_start(true), + State::DocumentStart => self.document_start(false), + State::DocumentContent => self.document_content(), + State::DocumentEnd => self.document_end(), + + State::BlockNode => self.parse_node(true, false), + // State::BlockNodeOrIndentlessSequence => self.parse_node(true, true), + // State::FlowNode => self.parse_node(false, false), + State::BlockMappingFirstKey => self.block_mapping_key(true), + State::BlockMappingKey => self.block_mapping_key(false), + State::BlockMappingValue => self.block_mapping_value(), + + State::BlockSequenceFirstEntry => self.block_sequence_entry(true), + State::BlockSequenceEntry => self.block_sequence_entry(false), + + State::FlowSequenceFirstEntry => self.flow_sequence_entry(true), + State::FlowSequenceEntry => self.flow_sequence_entry(false), + + State::FlowMappingFirstKey => self.flow_mapping_key(true), + State::FlowMappingKey => self.flow_mapping_key(false), + State::FlowMappingValue => self.flow_mapping_value(false), + + State::IndentlessSequenceEntry => self.indentless_sequence_entry(), + + State::FlowSequenceEntryMappingKey => self.flow_sequence_entry_mapping_key(), + State::FlowSequenceEntryMappingValue => self.flow_sequence_entry_mapping_value(), + State::FlowSequenceEntryMappingEnd => self.flow_sequence_entry_mapping_end(), + State::FlowMappingEmptyValue => self.flow_mapping_value(true), + + /* impossible */ + State::End => unreachable!(), + } + } + + fn stream_start(&mut self) -> ParseResult { + match *self.peek_token()? { + Token(mark, TokenType::StreamStart(_)) => { + self.state = State::ImplicitDocumentStart; + self.skip(); + Ok((Event::StreamStart, mark)) + } + Token(mark, _) => Err(ScanError::new(mark, "did not find expected <stream-start>")), + } + } + + fn document_start(&mut self, implicit: bool) -> ParseResult { + if !implicit { + while let TokenType::DocumentEnd = self.peek_token()?.1 { + self.skip(); + } + } + + match *self.peek_token()? { + Token(mark, TokenType::StreamEnd) => { + self.state = State::End; + self.skip(); + Ok((Event::StreamEnd, mark)) + } + Token(_, TokenType::VersionDirective(..)) + | Token(_, TokenType::TagDirective(..)) + | Token(_, TokenType::DocumentStart) => { + // explicit document + self._explicit_document_start() + } + Token(mark, _) if implicit => { + self.parser_process_directives()?; + self.push_state(State::DocumentEnd); + self.state = State::BlockNode; + Ok((Event::DocumentStart, mark)) + } + _ => { + // explicit document + self._explicit_document_start() + } + } + } + + fn parser_process_directives(&mut self) -> Result<(), ScanError> { + loop { + match self.peek_token()?.1 { + TokenType::VersionDirective(_, _) => { + // XXX parsing with warning according to spec + //if major != 1 || minor > 2 { + // return Err(ScanError::new(tok.0, + // "found incompatible YAML document")); + //} + } + TokenType::TagDirective(..) => { + // TODO add tag directive + } + _ => break, + } + self.skip(); + } + // TODO tag directive + Ok(()) + } + + fn _explicit_document_start(&mut self) -> ParseResult { + self.parser_process_directives()?; + match *self.peek_token()? { + Token(mark, TokenType::DocumentStart) => { + self.push_state(State::DocumentEnd); + self.state = State::DocumentContent; + self.skip(); + Ok((Event::DocumentStart, mark)) + } + Token(mark, _) => Err(ScanError::new( + mark, + "did not find expected <document start>", + )), + } + } + + fn document_content(&mut self) -> ParseResult { + match *self.peek_token()? { + Token(mark, TokenType::VersionDirective(..)) + | Token(mark, TokenType::TagDirective(..)) + | Token(mark, TokenType::DocumentStart) + | Token(mark, TokenType::DocumentEnd) + | Token(mark, TokenType::StreamEnd) => { + self.pop_state(); + // empty scalar + Ok((Event::empty_scalar(), mark)) + } + _ => self.parse_node(true, false), + } + } + + fn document_end(&mut self) -> ParseResult { + let mut _implicit = true; + let marker: Marker = match *self.peek_token()? { + Token(mark, TokenType::DocumentEnd) => { + self.skip(); + _implicit = false; + mark + } + Token(mark, _) => mark, + }; + + // TODO tag handling + self.state = State::DocumentStart; + Ok((Event::DocumentEnd, marker)) + } + + fn register_anchor(&mut self, name: String, _: &Marker) -> Result<usize, ScanError> { + // anchors can be overridden/reused + // if self.anchors.contains_key(name) { + // return Err(ScanError::new(*mark, + // "while parsing anchor, found duplicated anchor")); + // } + let new_id = self.anchor_id; + self.anchor_id += 1; + self.anchors.insert(name, new_id); + Ok(new_id) + } + + fn parse_node(&mut self, block: bool, indentless_sequence: bool) -> ParseResult { + let mut anchor_id = 0; + let mut tag = None; + match *self.peek_token()? { + Token(_, TokenType::Alias(_)) => { + self.pop_state(); + if let Token(mark, TokenType::Alias(name)) = self.fetch_token() { + match self.anchors.get(&name) { + None => { + return Err(ScanError::new( + mark, + "while parsing node, found unknown anchor", + )) + } + Some(id) => return Ok((Event::Alias(*id), mark)), + } + } else { + unreachable!() + } + } + Token(_, TokenType::Anchor(_)) => { + if let Token(mark, TokenType::Anchor(name)) = self.fetch_token() { + anchor_id = self.register_anchor(name, &mark)?; + if let TokenType::Tag(..) = self.peek_token()?.1 { + if let tg @ TokenType::Tag(..) = self.fetch_token().1 { + tag = Some(tg); + } else { + unreachable!() + } + } + } else { + unreachable!() + } + } + Token(_, TokenType::Tag(..)) => { + if let tg @ TokenType::Tag(..) = self.fetch_token().1 { + tag = Some(tg); + if let TokenType::Anchor(_) = self.peek_token()?.1 { + if let Token(mark, TokenType::Anchor(name)) = self.fetch_token() { + anchor_id = self.register_anchor(name, &mark)?; + } else { + unreachable!() + } + } + } else { + unreachable!() + } + } + _ => {} + } + match *self.peek_token()? { + Token(mark, TokenType::BlockEntry) if indentless_sequence => { + self.state = State::IndentlessSequenceEntry; + Ok((Event::SequenceStart(anchor_id), mark)) + } + Token(_, TokenType::Scalar(..)) => { + self.pop_state(); + if let Token(mark, TokenType::Scalar(style, v)) = self.fetch_token() { + Ok((Event::Scalar(v, style, anchor_id, tag), mark)) + } else { + unreachable!() + } + } + Token(mark, TokenType::FlowSequenceStart) => { + self.state = State::FlowSequenceFirstEntry; + Ok((Event::SequenceStart(anchor_id), mark)) + } + Token(mark, TokenType::FlowMappingStart) => { + self.state = State::FlowMappingFirstKey; + Ok((Event::MappingStart(anchor_id), mark)) + } + Token(mark, TokenType::BlockSequenceStart) if block => { + self.state = State::BlockSequenceFirstEntry; + Ok((Event::SequenceStart(anchor_id), mark)) + } + Token(mark, TokenType::BlockMappingStart) if block => { + self.state = State::BlockMappingFirstKey; + Ok((Event::MappingStart(anchor_id), mark)) + } + // ex 7.2, an empty scalar can follow a secondary tag + Token(mark, _) if tag.is_some() || anchor_id > 0 => { + self.pop_state(); + Ok((Event::empty_scalar_with_anchor(anchor_id, tag), mark)) + } + Token(mark, _) => Err(ScanError::new( + mark, + "while parsing a node, did not find expected node content", + )), + } + } + + fn block_mapping_key(&mut self, first: bool) -> ParseResult { + // skip BlockMappingStart + if first { + let _ = self.peek_token()?; + //self.marks.push(tok.0); + self.skip(); + } + match *self.peek_token()? { + Token(_, TokenType::Key) => { + self.skip(); + match *self.peek_token()? { + Token(mark, TokenType::Key) + | Token(mark, TokenType::Value) + | Token(mark, TokenType::BlockEnd) => { + self.state = State::BlockMappingValue; + // empty scalar + Ok((Event::empty_scalar(), mark)) + } + _ => { + self.push_state(State::BlockMappingValue); + self.parse_node(true, true) + } + } + } + // XXX(chenyh): libyaml failed to parse spec 1.2, ex8.18 + Token(mark, TokenType::Value) => { + self.state = State::BlockMappingValue; + Ok((Event::empty_scalar(), mark)) + } + Token(mark, TokenType::BlockEnd) => { + self.pop_state(); + self.skip(); + Ok((Event::MappingEnd, mark)) + } + Token(mark, _) => Err(ScanError::new( + mark, + "while parsing a block mapping, did not find expected key", + )), + } + } + + fn block_mapping_value(&mut self) -> ParseResult { + match *self.peek_token()? { + Token(_, TokenType::Value) => { + self.skip(); + match *self.peek_token()? { + Token(mark, TokenType::Key) + | Token(mark, TokenType::Value) + | Token(mark, TokenType::BlockEnd) => { + self.state = State::BlockMappingKey; + // empty scalar + Ok((Event::empty_scalar(), mark)) + } + _ => { + self.push_state(State::BlockMappingKey); + self.parse_node(true, true) + } + } + } + Token(mark, _) => { + self.state = State::BlockMappingKey; + // empty scalar + Ok((Event::empty_scalar(), mark)) + } + } + } + + fn flow_mapping_key(&mut self, first: bool) -> ParseResult { + if first { + let _ = self.peek_token()?; + self.skip(); + } + let marker: Marker = + { + match *self.peek_token()? { + Token(mark, TokenType::FlowMappingEnd) => mark, + Token(mark, _) => { + if !first { + match *self.peek_token()? { + Token(_, TokenType::FlowEntry) => self.skip(), + Token(mark, _) => return Err(ScanError::new(mark, + "while parsing a flow mapping, did not find expected ',' or '}'")) + } + } + + match *self.peek_token()? { + Token(_, TokenType::Key) => { + self.skip(); + match *self.peek_token()? { + Token(mark, TokenType::Value) + | Token(mark, TokenType::FlowEntry) + | Token(mark, TokenType::FlowMappingEnd) => { + self.state = State::FlowMappingValue; + return Ok((Event::empty_scalar(), mark)); + } + _ => { + self.push_state(State::FlowMappingValue); + return self.parse_node(false, false); + } + } + } + Token(marker, TokenType::Value) => { + self.state = State::FlowMappingValue; + return Ok((Event::empty_scalar(), marker)); + } + Token(_, TokenType::FlowMappingEnd) => (), + _ => { + self.push_state(State::FlowMappingEmptyValue); + return self.parse_node(false, false); + } + } + + mark + } + } + }; + + self.pop_state(); + self.skip(); + Ok((Event::MappingEnd, marker)) + } + + fn flow_mapping_value(&mut self, empty: bool) -> ParseResult { + let mark: Marker = { + if empty { + let Token(mark, _) = *self.peek_token()?; + self.state = State::FlowMappingKey; + return Ok((Event::empty_scalar(), mark)); + } else { + match *self.peek_token()? { + Token(marker, TokenType::Value) => { + self.skip(); + match self.peek_token()?.1 { + TokenType::FlowEntry | TokenType::FlowMappingEnd => {} + _ => { + self.push_state(State::FlowMappingKey); + return self.parse_node(false, false); + } + } + marker + } + Token(marker, _) => marker, + } + } + }; + + self.state = State::FlowMappingKey; + Ok((Event::empty_scalar(), mark)) + } + + fn flow_sequence_entry(&mut self, first: bool) -> ParseResult { + // skip FlowMappingStart + if first { + let _ = self.peek_token()?; + //self.marks.push(tok.0); + self.skip(); + } + match *self.peek_token()? { + Token(mark, TokenType::FlowSequenceEnd) => { + self.pop_state(); + self.skip(); + return Ok((Event::SequenceEnd, mark)); + } + Token(_, TokenType::FlowEntry) if !first => { + self.skip(); + } + Token(mark, _) if !first => { + return Err(ScanError::new( + mark, + "while parsing a flow sequence, expected ',' or ']'", + )); + } + _ => { /* next */ } + } + match *self.peek_token()? { + Token(mark, TokenType::FlowSequenceEnd) => { + self.pop_state(); + self.skip(); + Ok((Event::SequenceEnd, mark)) + } + Token(mark, TokenType::Key) => { + self.state = State::FlowSequenceEntryMappingKey; + self.skip(); + Ok((Event::MappingStart(0), mark)) + } + _ => { + self.push_state(State::FlowSequenceEntry); + self.parse_node(false, false) + } + } + } + + fn indentless_sequence_entry(&mut self) -> ParseResult { + match *self.peek_token()? { + Token(_, TokenType::BlockEntry) => (), + Token(mark, _) => { + self.pop_state(); + return Ok((Event::SequenceEnd, mark)); + } + } + self.skip(); + match *self.peek_token()? { + Token(mark, TokenType::BlockEntry) + | Token(mark, TokenType::Key) + | Token(mark, TokenType::Value) + | Token(mark, TokenType::BlockEnd) => { + self.state = State::IndentlessSequenceEntry; + Ok((Event::empty_scalar(), mark)) + } + _ => { + self.push_state(State::IndentlessSequenceEntry); + self.parse_node(true, false) + } + } + } + + fn block_sequence_entry(&mut self, first: bool) -> ParseResult { + // BLOCK-SEQUENCE-START + if first { + let _ = self.peek_token()?; + //self.marks.push(tok.0); + self.skip(); + } + match *self.peek_token()? { + Token(mark, TokenType::BlockEnd) => { + self.pop_state(); + self.skip(); + Ok((Event::SequenceEnd, mark)) + } + Token(_, TokenType::BlockEntry) => { + self.skip(); + match *self.peek_token()? { + Token(mark, TokenType::BlockEntry) | Token(mark, TokenType::BlockEnd) => { + self.state = State::BlockSequenceEntry; + Ok((Event::empty_scalar(), mark)) + } + _ => { + self.push_state(State::BlockSequenceEntry); + self.parse_node(true, false) + } + } + } + Token(mark, _) => Err(ScanError::new( + mark, + "while parsing a block collection, did not find expected '-' indicator", + )), + } + } + + fn flow_sequence_entry_mapping_key(&mut self) -> ParseResult { + match *self.peek_token()? { + Token(mark, TokenType::Value) + | Token(mark, TokenType::FlowEntry) + | Token(mark, TokenType::FlowSequenceEnd) => { + self.skip(); + self.state = State::FlowSequenceEntryMappingValue; + Ok((Event::empty_scalar(), mark)) + } + _ => { + self.push_state(State::FlowSequenceEntryMappingValue); + self.parse_node(false, false) + } + } + } + + fn flow_sequence_entry_mapping_value(&mut self) -> ParseResult { + match *self.peek_token()? { + Token(_, TokenType::Value) => { + self.skip(); + self.state = State::FlowSequenceEntryMappingValue; + match *self.peek_token()? { + Token(mark, TokenType::FlowEntry) | Token(mark, TokenType::FlowSequenceEnd) => { + self.state = State::FlowSequenceEntryMappingEnd; + Ok((Event::empty_scalar(), mark)) + } + _ => { + self.push_state(State::FlowSequenceEntryMappingEnd); + self.parse_node(false, false) + } + } + } + Token(mark, _) => { + self.state = State::FlowSequenceEntryMappingEnd; + Ok((Event::empty_scalar(), mark)) + } + } + } + + fn flow_sequence_entry_mapping_end(&mut self) -> ParseResult { + self.state = State::FlowSequenceEntry; + Ok((Event::MappingEnd, self.scanner.mark())) + } +} + +#[cfg(test)] +mod test { + use super::{Event, Parser}; + + #[test] + fn test_peek_eq_parse() { + let s = " +a0 bb: val +a1: &x + b1: 4 + b2: d +a2: 4 +a3: [1, 2, 3] +a4: + - [a1, a2] + - 2 +a5: *x +"; + let mut p = Parser::new(s.chars()); + while { + let event_peek = p.peek().unwrap().clone(); + let event = p.next().unwrap(); + assert_eq!(event, event_peek); + event.0 != Event::StreamEnd + } {} + } +} diff --git a/third_party/rust/yaml-rust/src/scanner.rs b/third_party/rust/yaml-rust/src/scanner.rs new file mode 100644 index 0000000000..a8659a8522 --- /dev/null +++ b/third_party/rust/yaml-rust/src/scanner.rs @@ -0,0 +1,2182 @@ +use std::collections::VecDeque; +use std::error::Error; +use std::{char, fmt}; + +#[derive(Clone, Copy, PartialEq, Debug, Eq)] +pub enum TEncoding { + Utf8, +} + +#[derive(Clone, Copy, PartialEq, Debug, Eq)] +pub enum TScalarStyle { + Any, + Plain, + SingleQuoted, + DoubleQuoted, + + Literal, + Foled, +} + +#[derive(Clone, Copy, PartialEq, Debug, Eq)] +pub struct Marker { + index: usize, + line: usize, + col: usize, +} + +impl Marker { + fn new(index: usize, line: usize, col: usize) -> Marker { + Marker { index, line, col } + } + + pub fn index(&self) -> usize { + self.index + } + + pub fn line(&self) -> usize { + self.line + } + + pub fn col(&self) -> usize { + self.col + } +} + +#[derive(Clone, PartialEq, Debug, Eq)] +pub struct ScanError { + mark: Marker, + info: String, +} + +impl ScanError { + pub fn new(loc: Marker, info: &str) -> ScanError { + ScanError { + mark: loc, + info: info.to_owned(), + } + } + + pub fn marker(&self) -> &Marker { + &self.mark + } +} + +impl Error for ScanError { + fn description(&self) -> &str { + self.info.as_ref() + } + + fn cause(&self) -> Option<&dyn Error> { + None + } +} + +impl fmt::Display for ScanError { + // col starts from 0 + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!( + formatter, + "{} at line {} column {}", + self.info, + self.mark.line, + self.mark.col + 1 + ) + } +} + +#[derive(Clone, PartialEq, Debug, Eq)] +pub enum TokenType { + NoToken, + StreamStart(TEncoding), + StreamEnd, + /// major, minor + VersionDirective(u32, u32), + /// handle, prefix + TagDirective(String, String), + DocumentStart, + DocumentEnd, + BlockSequenceStart, + BlockMappingStart, + BlockEnd, + FlowSequenceStart, + FlowSequenceEnd, + FlowMappingStart, + FlowMappingEnd, + BlockEntry, + FlowEntry, + Key, + Value, + Alias(String), + Anchor(String), + /// handle, suffix + Tag(String, String), + Scalar(TScalarStyle, String), +} + +#[derive(Clone, PartialEq, Debug, Eq)] +pub struct Token(pub Marker, pub TokenType); + +#[derive(Clone, PartialEq, Debug, Eq)] +struct SimpleKey { + possible: bool, + required: bool, + token_number: usize, + mark: Marker, +} + +impl SimpleKey { + fn new(mark: Marker) -> SimpleKey { + SimpleKey { + possible: false, + required: false, + token_number: 0, + mark, + } + } +} + +#[derive(Debug)] +pub struct Scanner<T> { + rdr: T, + mark: Marker, + tokens: VecDeque<Token>, + buffer: VecDeque<char>, + error: Option<ScanError>, + + stream_start_produced: bool, + stream_end_produced: bool, + adjacent_value_allowed_at: usize, + simple_key_allowed: bool, + simple_keys: Vec<SimpleKey>, + indent: isize, + indents: Vec<isize>, + flow_level: u8, + tokens_parsed: usize, + token_available: bool, +} + +impl<T: Iterator<Item = char>> Iterator for Scanner<T> { + type Item = Token; + fn next(&mut self) -> Option<Token> { + if self.error.is_some() { + return None; + } + match self.next_token() { + Ok(tok) => tok, + Err(e) => { + self.error = Some(e); + None + } + } + } +} + +#[inline] +fn is_z(c: char) -> bool { + c == '\0' +} +#[inline] +fn is_break(c: char) -> bool { + c == '\n' || c == '\r' +} +#[inline] +fn is_breakz(c: char) -> bool { + is_break(c) || is_z(c) +} +#[inline] +fn is_blank(c: char) -> bool { + c == ' ' || c == '\t' +} +#[inline] +fn is_blankz(c: char) -> bool { + is_blank(c) || is_breakz(c) +} +#[inline] +fn is_digit(c: char) -> bool { + c >= '0' && c <= '9' +} +#[inline] +fn is_alpha(c: char) -> bool { + match c { + '0'..='9' | 'a'..='z' | 'A'..='Z' => true, + '_' | '-' => true, + _ => false, + } +} +#[inline] +fn is_hex(c: char) -> bool { + (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') +} +#[inline] +fn as_hex(c: char) -> u32 { + match c { + '0'..='9' => (c as u32) - ('0' as u32), + 'a'..='f' => (c as u32) - ('a' as u32) + 10, + 'A'..='F' => (c as u32) - ('A' as u32) + 10, + _ => unreachable!(), + } +} +#[inline] +fn is_flow(c: char) -> bool { + match c { + ',' | '[' | ']' | '{' | '}' => true, + _ => false, + } +} + +pub type ScanResult = Result<(), ScanError>; + +impl<T: Iterator<Item = char>> Scanner<T> { + /// Creates the YAML tokenizer. + pub fn new(rdr: T) -> Scanner<T> { + Scanner { + rdr, + buffer: VecDeque::new(), + mark: Marker::new(0, 1, 0), + tokens: VecDeque::new(), + error: None, + + stream_start_produced: false, + stream_end_produced: false, + adjacent_value_allowed_at: 0, + simple_key_allowed: true, + simple_keys: Vec::new(), + indent: -1, + indents: Vec::new(), + flow_level: 0, + tokens_parsed: 0, + token_available: false, + } + } + #[inline] + pub fn get_error(&self) -> Option<ScanError> { + match self.error { + None => None, + Some(ref e) => Some(e.clone()), + } + } + + #[inline] + fn lookahead(&mut self, count: usize) { + if self.buffer.len() >= count { + return; + } + for _ in 0..(count - self.buffer.len()) { + self.buffer.push_back(self.rdr.next().unwrap_or('\0')); + } + } + #[inline] + fn skip(&mut self) { + let c = self.buffer.pop_front().unwrap(); + + self.mark.index += 1; + if c == '\n' { + self.mark.line += 1; + self.mark.col = 0; + } else { + self.mark.col += 1; + } + } + #[inline] + fn skip_line(&mut self) { + if self.buffer[0] == '\r' && self.buffer[1] == '\n' { + self.skip(); + self.skip(); + } else if is_break(self.buffer[0]) { + self.skip(); + } + } + #[inline] + fn ch(&self) -> char { + self.buffer[0] + } + #[inline] + fn ch_is(&self, c: char) -> bool { + self.buffer[0] == c + } + #[allow(dead_code)] + #[inline] + fn eof(&self) -> bool { + self.ch_is('\0') + } + #[inline] + pub fn stream_started(&self) -> bool { + self.stream_start_produced + } + #[inline] + pub fn stream_ended(&self) -> bool { + self.stream_end_produced + } + #[inline] + pub fn mark(&self) -> Marker { + self.mark + } + #[inline] + fn read_break(&mut self, s: &mut String) { + if self.buffer[0] == '\r' && self.buffer[1] == '\n' { + s.push('\n'); + self.skip(); + self.skip(); + } else if self.buffer[0] == '\r' || self.buffer[0] == '\n' { + s.push('\n'); + self.skip(); + } else { + unreachable!(); + } + } + fn insert_token(&mut self, pos: usize, tok: Token) { + let old_len = self.tokens.len(); + assert!(pos <= old_len); + self.tokens.push_back(tok); + for i in 0..old_len - pos { + self.tokens.swap(old_len - i, old_len - i - 1); + } + } + fn allow_simple_key(&mut self) { + self.simple_key_allowed = true; + } + fn disallow_simple_key(&mut self) { + self.simple_key_allowed = false; + } + + pub fn fetch_next_token(&mut self) -> ScanResult { + self.lookahead(1); + // println!("--> fetch_next_token Cur {:?} {:?}", self.mark, self.ch()); + + if !self.stream_start_produced { + self.fetch_stream_start(); + return Ok(()); + } + self.skip_to_next_token(); + + self.stale_simple_keys()?; + + let mark = self.mark; + self.unroll_indent(mark.col as isize); + + self.lookahead(4); + + if is_z(self.ch()) { + self.fetch_stream_end()?; + return Ok(()); + } + + // Is it a directive? + if self.mark.col == 0 && self.ch_is('%') { + return self.fetch_directive(); + } + + if self.mark.col == 0 + && self.buffer[0] == '-' + && self.buffer[1] == '-' + && self.buffer[2] == '-' + && is_blankz(self.buffer[3]) + { + self.fetch_document_indicator(TokenType::DocumentStart)?; + return Ok(()); + } + + if self.mark.col == 0 + && self.buffer[0] == '.' + && self.buffer[1] == '.' + && self.buffer[2] == '.' + && is_blankz(self.buffer[3]) + { + self.fetch_document_indicator(TokenType::DocumentEnd)?; + return Ok(()); + } + + let c = self.buffer[0]; + let nc = self.buffer[1]; + match c { + '[' => self.fetch_flow_collection_start(TokenType::FlowSequenceStart), + '{' => self.fetch_flow_collection_start(TokenType::FlowMappingStart), + ']' => self.fetch_flow_collection_end(TokenType::FlowSequenceEnd), + '}' => self.fetch_flow_collection_end(TokenType::FlowMappingEnd), + ',' => self.fetch_flow_entry(), + '-' if is_blankz(nc) => self.fetch_block_entry(), + '?' if is_blankz(nc) => self.fetch_key(), + ':' if is_blankz(nc) + || (self.flow_level > 0 + && (is_flow(nc) || self.mark.index == self.adjacent_value_allowed_at)) => + { + self.fetch_value() + } + // Is it an alias? + '*' => self.fetch_anchor(true), + // Is it an anchor? + '&' => self.fetch_anchor(false), + '!' => self.fetch_tag(), + // Is it a literal scalar? + '|' if self.flow_level == 0 => self.fetch_block_scalar(true), + // Is it a folded scalar? + '>' if self.flow_level == 0 => self.fetch_block_scalar(false), + '\'' => self.fetch_flow_scalar(true), + '"' => self.fetch_flow_scalar(false), + // plain scalar + '-' if !is_blankz(nc) => self.fetch_plain_scalar(), + ':' | '?' if !is_blankz(nc) && self.flow_level == 0 => self.fetch_plain_scalar(), + '%' | '@' | '`' => Err(ScanError::new( + self.mark, + &format!("unexpected character: `{}'", c), + )), + _ => self.fetch_plain_scalar(), + } + } + + pub fn next_token(&mut self) -> Result<Option<Token>, ScanError> { + if self.stream_end_produced { + return Ok(None); + } + + if !self.token_available { + self.fetch_more_tokens()?; + } + let t = self.tokens.pop_front().unwrap(); + self.token_available = false; + self.tokens_parsed += 1; + + if let TokenType::StreamEnd = t.1 { + self.stream_end_produced = true; + } + Ok(Some(t)) + } + + pub fn fetch_more_tokens(&mut self) -> ScanResult { + let mut need_more; + loop { + need_more = false; + if self.tokens.is_empty() { + need_more = true; + } else { + self.stale_simple_keys()?; + for sk in &self.simple_keys { + if sk.possible && sk.token_number == self.tokens_parsed { + need_more = true; + break; + } + } + } + + if !need_more { + break; + } + self.fetch_next_token()?; + } + self.token_available = true; + + Ok(()) + } + + fn stale_simple_keys(&mut self) -> ScanResult { + for sk in &mut self.simple_keys { + if sk.possible + && (sk.mark.line < self.mark.line || sk.mark.index + 1024 < self.mark.index) + { + if sk.required { + return Err(ScanError::new(self.mark, "simple key expect ':'")); + } + sk.possible = false; + } + } + Ok(()) + } + + fn skip_to_next_token(&mut self) { + loop { + self.lookahead(1); + // TODO(chenyh) BOM + match self.ch() { + ' ' => self.skip(), + '\t' if self.flow_level > 0 || !self.simple_key_allowed => self.skip(), + '\n' | '\r' => { + self.lookahead(2); + self.skip_line(); + if self.flow_level == 0 { + self.allow_simple_key(); + } + } + '#' => { + while !is_breakz(self.ch()) { + self.skip(); + self.lookahead(1); + } + } + _ => break, + } + } + } + + fn fetch_stream_start(&mut self) { + let mark = self.mark; + self.indent = -1; + self.stream_start_produced = true; + self.allow_simple_key(); + self.tokens + .push_back(Token(mark, TokenType::StreamStart(TEncoding::Utf8))); + self.simple_keys.push(SimpleKey::new(Marker::new(0, 0, 0))); + } + + fn fetch_stream_end(&mut self) -> ScanResult { + // force new line + if self.mark.col != 0 { + self.mark.col = 0; + self.mark.line += 1; + } + + self.unroll_indent(-1); + self.remove_simple_key()?; + self.disallow_simple_key(); + + self.tokens + .push_back(Token(self.mark, TokenType::StreamEnd)); + Ok(()) + } + + fn fetch_directive(&mut self) -> ScanResult { + self.unroll_indent(-1); + self.remove_simple_key()?; + + self.disallow_simple_key(); + + let tok = self.scan_directive()?; + + self.tokens.push_back(tok); + + Ok(()) + } + + fn scan_directive(&mut self) -> Result<Token, ScanError> { + let start_mark = self.mark; + self.skip(); + + let name = self.scan_directive_name()?; + let tok = match name.as_ref() { + "YAML" => self.scan_version_directive_value(&start_mark)?, + "TAG" => self.scan_tag_directive_value(&start_mark)?, + // XXX This should be a warning instead of an error + _ => { + // skip current line + self.lookahead(1); + while !is_breakz(self.ch()) { + self.skip(); + self.lookahead(1); + } + // XXX return an empty TagDirective token + Token( + start_mark, + TokenType::TagDirective(String::new(), String::new()), + ) + // return Err(ScanError::new(start_mark, + // "while scanning a directive, found unknown directive name")) + } + }; + self.lookahead(1); + + while is_blank(self.ch()) { + self.skip(); + self.lookahead(1); + } + + if self.ch() == '#' { + while !is_breakz(self.ch()) { + self.skip(); + self.lookahead(1); + } + } + + if !is_breakz(self.ch()) { + return Err(ScanError::new( + start_mark, + "while scanning a directive, did not find expected comment or line break", + )); + } + + // Eat a line break + if is_break(self.ch()) { + self.lookahead(2); + self.skip_line(); + } + + Ok(tok) + } + + fn scan_version_directive_value(&mut self, mark: &Marker) -> Result<Token, ScanError> { + self.lookahead(1); + + while is_blank(self.ch()) { + self.skip(); + self.lookahead(1); + } + + let major = self.scan_version_directive_number(mark)?; + + if self.ch() != '.' { + return Err(ScanError::new( + *mark, + "while scanning a YAML directive, did not find expected digit or '.' character", + )); + } + + self.skip(); + + let minor = self.scan_version_directive_number(mark)?; + + Ok(Token(*mark, TokenType::VersionDirective(major, minor))) + } + + fn scan_directive_name(&mut self) -> Result<String, ScanError> { + let start_mark = self.mark; + let mut string = String::new(); + self.lookahead(1); + while is_alpha(self.ch()) { + string.push(self.ch()); + self.skip(); + self.lookahead(1); + } + + if string.is_empty() { + return Err(ScanError::new( + start_mark, + "while scanning a directive, could not find expected directive name", + )); + } + + if !is_blankz(self.ch()) { + return Err(ScanError::new( + start_mark, + "while scanning a directive, found unexpected non-alphabetical character", + )); + } + + Ok(string) + } + + fn scan_version_directive_number(&mut self, mark: &Marker) -> Result<u32, ScanError> { + let mut val = 0u32; + let mut length = 0usize; + self.lookahead(1); + while is_digit(self.ch()) { + if length + 1 > 9 { + return Err(ScanError::new( + *mark, + "while scanning a YAML directive, found extremely long version number", + )); + } + length += 1; + val = val * 10 + ((self.ch() as u32) - ('0' as u32)); + self.skip(); + self.lookahead(1); + } + + if length == 0 { + return Err(ScanError::new( + *mark, + "while scanning a YAML directive, did not find expected version number", + )); + } + + Ok(val) + } + + fn scan_tag_directive_value(&mut self, mark: &Marker) -> Result<Token, ScanError> { + self.lookahead(1); + /* Eat whitespaces. */ + while is_blank(self.ch()) { + self.skip(); + self.lookahead(1); + } + let handle = self.scan_tag_handle(true, mark)?; + + self.lookahead(1); + /* Eat whitespaces. */ + while is_blank(self.ch()) { + self.skip(); + self.lookahead(1); + } + + let is_secondary = handle == "!!"; + let prefix = self.scan_tag_uri(true, is_secondary, &String::new(), mark)?; + + self.lookahead(1); + + if is_blankz(self.ch()) { + Ok(Token(*mark, TokenType::TagDirective(handle, prefix))) + } else { + Err(ScanError::new( + *mark, + "while scanning TAG, did not find expected whitespace or line break", + )) + } + } + + fn fetch_tag(&mut self) -> ScanResult { + self.save_simple_key()?; + self.disallow_simple_key(); + + let tok = self.scan_tag()?; + self.tokens.push_back(tok); + Ok(()) + } + + fn scan_tag(&mut self) -> Result<Token, ScanError> { + let start_mark = self.mark; + let mut handle = String::new(); + let mut suffix; + let mut secondary = false; + + // Check if the tag is in the canonical form (verbatim). + self.lookahead(2); + + if self.buffer[1] == '<' { + // Eat '!<' + self.skip(); + self.skip(); + suffix = self.scan_tag_uri(false, false, &String::new(), &start_mark)?; + + if self.ch() != '>' { + return Err(ScanError::new( + start_mark, + "while scanning a tag, did not find the expected '>'", + )); + } + + self.skip(); + } else { + // The tag has either the '!suffix' or the '!handle!suffix' + handle = self.scan_tag_handle(false, &start_mark)?; + // Check if it is, indeed, handle. + if handle.len() >= 2 && handle.starts_with('!') && handle.ends_with('!') { + if handle == "!!" { + secondary = true; + } + suffix = self.scan_tag_uri(false, secondary, &String::new(), &start_mark)?; + } else { + suffix = self.scan_tag_uri(false, false, &handle, &start_mark)?; + handle = "!".to_owned(); + // A special case: the '!' tag. Set the handle to '' and the + // suffix to '!'. + if suffix.is_empty() { + handle.clear(); + suffix = "!".to_owned(); + } + } + } + + self.lookahead(1); + if is_blankz(self.ch()) { + // XXX: ex 7.2, an empty scalar can follow a secondary tag + Ok(Token(start_mark, TokenType::Tag(handle, suffix))) + } else { + Err(ScanError::new( + start_mark, + "while scanning a tag, did not find expected whitespace or line break", + )) + } + } + + fn scan_tag_handle(&mut self, directive: bool, mark: &Marker) -> Result<String, ScanError> { + let mut string = String::new(); + self.lookahead(1); + if self.ch() != '!' { + return Err(ScanError::new( + *mark, + "while scanning a tag, did not find expected '!'", + )); + } + + string.push(self.ch()); + self.skip(); + + self.lookahead(1); + while is_alpha(self.ch()) { + string.push(self.ch()); + self.skip(); + self.lookahead(1); + } + + // Check if the trailing character is '!' and copy it. + if self.ch() == '!' { + string.push(self.ch()); + self.skip(); + } else if directive && string != "!" { + // It's either the '!' tag or not really a tag handle. If it's a %TAG + // directive, it's an error. If it's a tag token, it must be a part of + // URI. + return Err(ScanError::new( + *mark, + "while parsing a tag directive, did not find expected '!'", + )); + } + Ok(string) + } + + fn scan_tag_uri( + &mut self, + directive: bool, + _is_secondary: bool, + head: &str, + mark: &Marker, + ) -> Result<String, ScanError> { + let mut length = head.len(); + let mut string = String::new(); + + // Copy the head if needed. + // Note that we don't copy the leading '!' character. + if length > 1 { + string.extend(head.chars().skip(1)); + } + + self.lookahead(1); + /* + * The set of characters that may appear in URI is as follows: + * + * '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&', + * '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']', + * '%'. + */ + while match self.ch() { + ';' | '/' | '?' | ':' | '@' | '&' => true, + '=' | '+' | '$' | ',' | '.' | '!' | '~' | '*' | '\'' | '(' | ')' | '[' | ']' => true, + '%' => true, + c if is_alpha(c) => true, + _ => false, + } { + // Check if it is a URI-escape sequence. + if self.ch() == '%' { + string.push(self.scan_uri_escapes(directive, mark)?); + } else { + string.push(self.ch()); + self.skip(); + } + + length += 1; + self.lookahead(1); + } + + if length == 0 { + return Err(ScanError::new( + *mark, + "while parsing a tag, did not find expected tag URI", + )); + } + + Ok(string) + } + + fn scan_uri_escapes(&mut self, _directive: bool, mark: &Marker) -> Result<char, ScanError> { + let mut width = 0usize; + let mut code = 0u32; + loop { + self.lookahead(3); + + if !(self.ch() == '%' && is_hex(self.buffer[1]) && is_hex(self.buffer[2])) { + return Err(ScanError::new( + *mark, + "while parsing a tag, did not find URI escaped octet", + )); + } + + let octet = (as_hex(self.buffer[1]) << 4) + as_hex(self.buffer[2]); + if width == 0 { + width = match octet { + _ if octet & 0x80 == 0x00 => 1, + _ if octet & 0xE0 == 0xC0 => 2, + _ if octet & 0xF0 == 0xE0 => 3, + _ if octet & 0xF8 == 0xF0 => 4, + _ => { + return Err(ScanError::new( + *mark, + "while parsing a tag, found an incorrect leading UTF-8 octet", + )); + } + }; + code = octet; + } else { + if octet & 0xc0 != 0x80 { + return Err(ScanError::new( + *mark, + "while parsing a tag, found an incorrect trailing UTF-8 octet", + )); + } + code = (code << 8) + octet; + } + + self.skip(); + self.skip(); + self.skip(); + + width -= 1; + if width == 0 { + break; + } + } + + match char::from_u32(code) { + Some(ch) => Ok(ch), + None => Err(ScanError::new( + *mark, + "while parsing a tag, found an invalid UTF-8 codepoint", + )), + } + } + + fn fetch_anchor(&mut self, alias: bool) -> ScanResult { + self.save_simple_key()?; + self.disallow_simple_key(); + + let tok = self.scan_anchor(alias)?; + + self.tokens.push_back(tok); + + Ok(()) + } + + fn scan_anchor(&mut self, alias: bool) -> Result<Token, ScanError> { + let mut string = String::new(); + let start_mark = self.mark; + + self.skip(); + self.lookahead(1); + while is_alpha(self.ch()) { + string.push(self.ch()); + self.skip(); + self.lookahead(1); + } + + if string.is_empty() + || match self.ch() { + c if is_blankz(c) => false, + '?' | ':' | ',' | ']' | '}' | '%' | '@' | '`' => false, + _ => true, + } + { + return Err(ScanError::new(start_mark, "while scanning an anchor or alias, did not find expected alphabetic or numeric character")); + } + + if alias { + Ok(Token(start_mark, TokenType::Alias(string))) + } else { + Ok(Token(start_mark, TokenType::Anchor(string))) + } + } + + fn fetch_flow_collection_start(&mut self, tok: TokenType) -> ScanResult { + // The indicators '[' and '{' may start a simple key. + self.save_simple_key()?; + + self.increase_flow_level()?; + + self.allow_simple_key(); + + let start_mark = self.mark; + self.skip(); + + self.tokens.push_back(Token(start_mark, tok)); + Ok(()) + } + + fn fetch_flow_collection_end(&mut self, tok: TokenType) -> ScanResult { + self.remove_simple_key()?; + self.decrease_flow_level(); + + self.disallow_simple_key(); + + let start_mark = self.mark; + self.skip(); + + self.tokens.push_back(Token(start_mark, tok)); + Ok(()) + } + + fn fetch_flow_entry(&mut self) -> ScanResult { + self.remove_simple_key()?; + self.allow_simple_key(); + + let start_mark = self.mark; + self.skip(); + + self.tokens + .push_back(Token(start_mark, TokenType::FlowEntry)); + Ok(()) + } + + fn increase_flow_level(&mut self) -> ScanResult { + self.simple_keys.push(SimpleKey::new(Marker::new(0, 0, 0))); + self.flow_level = self + .flow_level + .checked_add(1) + .ok_or_else(|| ScanError::new(self.mark, "recursion limit exceeded"))?; + Ok(()) + } + fn decrease_flow_level(&mut self) { + if self.flow_level > 0 { + self.flow_level -= 1; + self.simple_keys.pop().unwrap(); + } + } + + fn fetch_block_entry(&mut self) -> ScanResult { + if self.flow_level == 0 { + // Check if we are allowed to start a new entry. + if !self.simple_key_allowed { + return Err(ScanError::new( + self.mark, + "block sequence entries are not allowed in this context", + )); + } + + let mark = self.mark; + // generate BLOCK-SEQUENCE-START if indented + self.roll_indent(mark.col, None, TokenType::BlockSequenceStart, mark); + } else { + // - * only allowed in block + return Err(ScanError::new( + self.mark, + r#""-" is only valid inside a block"#, + )); + } + self.remove_simple_key()?; + self.allow_simple_key(); + + let start_mark = self.mark; + self.skip(); + + self.tokens + .push_back(Token(start_mark, TokenType::BlockEntry)); + Ok(()) + } + + fn fetch_document_indicator(&mut self, t: TokenType) -> ScanResult { + self.unroll_indent(-1); + self.remove_simple_key()?; + self.disallow_simple_key(); + + let mark = self.mark; + + self.skip(); + self.skip(); + self.skip(); + + self.tokens.push_back(Token(mark, t)); + Ok(()) + } + + fn fetch_block_scalar(&mut self, literal: bool) -> ScanResult { + self.save_simple_key()?; + self.allow_simple_key(); + let tok = self.scan_block_scalar(literal)?; + + self.tokens.push_back(tok); + Ok(()) + } + + fn scan_block_scalar(&mut self, literal: bool) -> Result<Token, ScanError> { + let start_mark = self.mark; + let mut chomping: i32 = 0; + let mut increment: usize = 0; + let mut indent: usize = 0; + let mut trailing_blank: bool; + let mut leading_blank: bool = false; + + let mut string = String::new(); + let mut leading_break = String::new(); + let mut trailing_breaks = String::new(); + + // skip '|' or '>' + self.skip(); + self.lookahead(1); + + if self.ch() == '+' || self.ch() == '-' { + if self.ch() == '+' { + chomping = 1; + } else { + chomping = -1; + } + self.skip(); + self.lookahead(1); + if is_digit(self.ch()) { + if self.ch() == '0' { + return Err(ScanError::new( + start_mark, + "while scanning a block scalar, found an indentation indicator equal to 0", + )); + } + increment = (self.ch() as usize) - ('0' as usize); + self.skip(); + } + } else if is_digit(self.ch()) { + if self.ch() == '0' { + return Err(ScanError::new( + start_mark, + "while scanning a block scalar, found an indentation indicator equal to 0", + )); + } + + increment = (self.ch() as usize) - ('0' as usize); + self.skip(); + self.lookahead(1); + if self.ch() == '+' || self.ch() == '-' { + if self.ch() == '+' { + chomping = 1; + } else { + chomping = -1; + } + self.skip(); + } + } + + // Eat whitespaces and comments to the end of the line. + self.lookahead(1); + + while is_blank(self.ch()) { + self.skip(); + self.lookahead(1); + } + + if self.ch() == '#' { + while !is_breakz(self.ch()) { + self.skip(); + self.lookahead(1); + } + } + + // Check if we are at the end of the line. + if !is_breakz(self.ch()) { + return Err(ScanError::new( + start_mark, + "while scanning a block scalar, did not find expected comment or line break", + )); + } + + if is_break(self.ch()) { + self.lookahead(2); + self.skip_line(); + } + + if increment > 0 { + indent = if self.indent >= 0 { + (self.indent + increment as isize) as usize + } else { + increment + } + } + // Scan the leading line breaks and determine the indentation level if needed. + self.block_scalar_breaks(&mut indent, &mut trailing_breaks)?; + + self.lookahead(1); + + let start_mark = self.mark; + + while self.mark.col == indent && !is_z(self.ch()) { + // We are at the beginning of a non-empty line. + trailing_blank = is_blank(self.ch()); + if !literal && !leading_break.is_empty() && !leading_blank && !trailing_blank { + if trailing_breaks.is_empty() { + string.push(' '); + } + leading_break.clear(); + } else { + string.push_str(&leading_break); + leading_break.clear(); + } + + string.push_str(&trailing_breaks); + trailing_breaks.clear(); + + leading_blank = is_blank(self.ch()); + + while !is_breakz(self.ch()) { + string.push(self.ch()); + self.skip(); + self.lookahead(1); + } + // break on EOF + if is_z(self.ch()) { + break; + } + + self.lookahead(2); + self.read_break(&mut leading_break); + + // Eat the following indentation spaces and line breaks. + self.block_scalar_breaks(&mut indent, &mut trailing_breaks)?; + } + + // Chomp the tail. + if chomping != -1 { + string.push_str(&leading_break); + } + + if chomping == 1 { + string.push_str(&trailing_breaks); + } + + if literal { + Ok(Token( + start_mark, + TokenType::Scalar(TScalarStyle::Literal, string), + )) + } else { + Ok(Token( + start_mark, + TokenType::Scalar(TScalarStyle::Foled, string), + )) + } + } + + fn block_scalar_breaks(&mut self, indent: &mut usize, breaks: &mut String) -> ScanResult { + let mut max_indent = 0; + loop { + self.lookahead(1); + while (*indent == 0 || self.mark.col < *indent) && self.buffer[0] == ' ' { + self.skip(); + self.lookahead(1); + } + + if self.mark.col > max_indent { + max_indent = self.mark.col; + } + + // Check for a tab character messing the indentation. + if (*indent == 0 || self.mark.col < *indent) && self.buffer[0] == '\t' { + return Err(ScanError::new(self.mark, + "while scanning a block scalar, found a tab character where an indentation space is expected")); + } + + if !is_break(self.ch()) { + break; + } + + self.lookahead(2); + // Consume the line break. + self.read_break(breaks); + } + + if *indent == 0 { + *indent = max_indent; + if *indent < (self.indent + 1) as usize { + *indent = (self.indent + 1) as usize; + } + if *indent < 1 { + *indent = 1; + } + } + Ok(()) + } + + fn fetch_flow_scalar(&mut self, single: bool) -> ScanResult { + self.save_simple_key()?; + self.disallow_simple_key(); + + let tok = self.scan_flow_scalar(single)?; + + // From spec: To ensure JSON compatibility, if a key inside a flow mapping is JSON-like, + // YAML allows the following value to be specified adjacent to the “:”. + self.adjacent_value_allowed_at = self.mark.index; + + self.tokens.push_back(tok); + Ok(()) + } + + fn scan_flow_scalar(&mut self, single: bool) -> Result<Token, ScanError> { + let start_mark = self.mark; + + let mut string = String::new(); + let mut leading_break = String::new(); + let mut trailing_breaks = String::new(); + let mut whitespaces = String::new(); + let mut leading_blanks; + + /* Eat the left quote. */ + self.skip(); + + loop { + /* Check for a document indicator. */ + self.lookahead(4); + + if self.mark.col == 0 + && (((self.buffer[0] == '-') && (self.buffer[1] == '-') && (self.buffer[2] == '-')) + || ((self.buffer[0] == '.') + && (self.buffer[1] == '.') + && (self.buffer[2] == '.'))) + && is_blankz(self.buffer[3]) + { + return Err(ScanError::new( + start_mark, + "while scanning a quoted scalar, found unexpected document indicator", + )); + } + + if is_z(self.ch()) { + return Err(ScanError::new( + start_mark, + "while scanning a quoted scalar, found unexpected end of stream", + )); + } + + self.lookahead(2); + + leading_blanks = false; + // Consume non-blank characters. + + while !is_blankz(self.ch()) { + match self.ch() { + // Check for an escaped single quote. + '\'' if self.buffer[1] == '\'' && single => { + string.push('\''); + self.skip(); + self.skip(); + } + // Check for the right quote. + '\'' if single => break, + '"' if !single => break, + // Check for an escaped line break. + '\\' if !single && is_break(self.buffer[1]) => { + self.lookahead(3); + self.skip(); + self.skip_line(); + leading_blanks = true; + break; + } + // Check for an escape sequence. + '\\' if !single => { + let mut code_length = 0usize; + match self.buffer[1] { + '0' => string.push('\0'), + 'a' => string.push('\x07'), + 'b' => string.push('\x08'), + 't' | '\t' => string.push('\t'), + 'n' => string.push('\n'), + 'v' => string.push('\x0b'), + 'f' => string.push('\x0c'), + 'r' => string.push('\x0d'), + 'e' => string.push('\x1b'), + ' ' => string.push('\x20'), + '"' => string.push('"'), + '\'' => string.push('\''), + '\\' => string.push('\\'), + // NEL (#x85) + 'N' => string.push(char::from_u32(0x85).unwrap()), + // #xA0 + '_' => string.push(char::from_u32(0xA0).unwrap()), + // LS (#x2028) + 'L' => string.push(char::from_u32(0x2028).unwrap()), + // PS (#x2029) + 'P' => string.push(char::from_u32(0x2029).unwrap()), + 'x' => code_length = 2, + 'u' => code_length = 4, + 'U' => code_length = 8, + _ => { + return Err(ScanError::new( + start_mark, + "while parsing a quoted scalar, found unknown escape character", + )) + } + } + self.skip(); + self.skip(); + // Consume an arbitrary escape code. + if code_length > 0 { + self.lookahead(code_length); + let mut value = 0u32; + for i in 0..code_length { + if !is_hex(self.buffer[i]) { + return Err(ScanError::new(start_mark, + "while parsing a quoted scalar, did not find expected hexadecimal number")); + } + value = (value << 4) + as_hex(self.buffer[i]); + } + + let ch = match char::from_u32(value) { + Some(v) => v, + None => { + return Err(ScanError::new(start_mark, + "while parsing a quoted scalar, found invalid Unicode character escape code")); + } + }; + string.push(ch); + + for _ in 0..code_length { + self.skip(); + } + } + } + c => { + string.push(c); + self.skip(); + } + } + self.lookahead(2); + } + self.lookahead(1); + match self.ch() { + '\'' if single => break, + '"' if !single => break, + _ => {} + } + + // Consume blank characters. + while is_blank(self.ch()) || is_break(self.ch()) { + if is_blank(self.ch()) { + // Consume a space or a tab character. + if leading_blanks { + self.skip(); + } else { + whitespaces.push(self.ch()); + self.skip(); + } + } else { + self.lookahead(2); + // Check if it is a first line break. + if leading_blanks { + self.read_break(&mut trailing_breaks); + } else { + whitespaces.clear(); + self.read_break(&mut leading_break); + leading_blanks = true; + } + } + self.lookahead(1); + } + // Join the whitespaces or fold line breaks. + if leading_blanks { + if leading_break.is_empty() { + string.push_str(&leading_break); + string.push_str(&trailing_breaks); + trailing_breaks.clear(); + leading_break.clear(); + } else { + if trailing_breaks.is_empty() { + string.push(' '); + } else { + string.push_str(&trailing_breaks); + trailing_breaks.clear(); + } + leading_break.clear(); + } + } else { + string.push_str(&whitespaces); + whitespaces.clear(); + } + } // loop + + // Eat the right quote. + self.skip(); + + if single { + Ok(Token( + start_mark, + TokenType::Scalar(TScalarStyle::SingleQuoted, string), + )) + } else { + Ok(Token( + start_mark, + TokenType::Scalar(TScalarStyle::DoubleQuoted, string), + )) + } + } + + fn fetch_plain_scalar(&mut self) -> ScanResult { + self.save_simple_key()?; + self.disallow_simple_key(); + + let tok = self.scan_plain_scalar()?; + + self.tokens.push_back(tok); + Ok(()) + } + + fn scan_plain_scalar(&mut self) -> Result<Token, ScanError> { + let indent = self.indent + 1; + let start_mark = self.mark; + + let mut string = String::new(); + let mut leading_break = String::new(); + let mut trailing_breaks = String::new(); + let mut whitespaces = String::new(); + let mut leading_blanks = false; + + loop { + /* Check for a document indicator. */ + self.lookahead(4); + + if self.mark.col == 0 + && (((self.buffer[0] == '-') && (self.buffer[1] == '-') && (self.buffer[2] == '-')) + || ((self.buffer[0] == '.') + && (self.buffer[1] == '.') + && (self.buffer[2] == '.'))) + && is_blankz(self.buffer[3]) + { + break; + } + + if self.ch() == '#' { + break; + } + while !is_blankz(self.ch()) { + // indicators can end a plain scalar, see 7.3.3. Plain Style + match self.ch() { + ':' if is_blankz(self.buffer[1]) + || (self.flow_level > 0 && is_flow(self.buffer[1])) => + { + break; + } + ',' | '[' | ']' | '{' | '}' if self.flow_level > 0 => break, + _ => {} + } + + if leading_blanks || !whitespaces.is_empty() { + if leading_blanks { + if leading_break.is_empty() { + string.push_str(&leading_break); + string.push_str(&trailing_breaks); + trailing_breaks.clear(); + leading_break.clear(); + } else { + if trailing_breaks.is_empty() { + string.push(' '); + } else { + string.push_str(&trailing_breaks); + trailing_breaks.clear(); + } + leading_break.clear(); + } + leading_blanks = false; + } else { + string.push_str(&whitespaces); + whitespaces.clear(); + } + } + + string.push(self.ch()); + self.skip(); + self.lookahead(2); + } + // is the end? + if !(is_blank(self.ch()) || is_break(self.ch())) { + break; + } + self.lookahead(1); + + while is_blank(self.ch()) || is_break(self.ch()) { + if is_blank(self.ch()) { + if leading_blanks && (self.mark.col as isize) < indent && self.ch() == '\t' { + return Err(ScanError::new( + start_mark, + "while scanning a plain scalar, found a tab", + )); + } + + if leading_blanks { + self.skip(); + } else { + whitespaces.push(self.ch()); + self.skip(); + } + } else { + self.lookahead(2); + // Check if it is a first line break + if leading_blanks { + self.read_break(&mut trailing_breaks); + } else { + whitespaces.clear(); + self.read_break(&mut leading_break); + leading_blanks = true; + } + } + self.lookahead(1); + } + + // check indentation level + if self.flow_level == 0 && (self.mark.col as isize) < indent { + break; + } + } + + if leading_blanks { + self.allow_simple_key(); + } + + Ok(Token( + start_mark, + TokenType::Scalar(TScalarStyle::Plain, string), + )) + } + + fn fetch_key(&mut self) -> ScanResult { + let start_mark = self.mark; + if self.flow_level == 0 { + // Check if we are allowed to start a new key (not necessarily simple). + if !self.simple_key_allowed { + return Err(ScanError::new( + self.mark, + "mapping keys are not allowed in this context", + )); + } + self.roll_indent( + start_mark.col, + None, + TokenType::BlockMappingStart, + start_mark, + ); + } + + self.remove_simple_key()?; + + if self.flow_level == 0 { + self.allow_simple_key(); + } else { + self.disallow_simple_key(); + } + + self.skip(); + self.tokens.push_back(Token(start_mark, TokenType::Key)); + Ok(()) + } + + fn fetch_value(&mut self) -> ScanResult { + let sk = self.simple_keys.last().unwrap().clone(); + let start_mark = self.mark; + if sk.possible { + // insert simple key + let tok = Token(sk.mark, TokenType::Key); + let tokens_parsed = self.tokens_parsed; + self.insert_token(sk.token_number - tokens_parsed, tok); + + // Add the BLOCK-MAPPING-START token if needed. + self.roll_indent( + sk.mark.col, + Some(sk.token_number), + TokenType::BlockMappingStart, + start_mark, + ); + + self.simple_keys.last_mut().unwrap().possible = false; + self.disallow_simple_key(); + } else { + // The ':' indicator follows a complex key. + if self.flow_level == 0 { + if !self.simple_key_allowed { + return Err(ScanError::new( + start_mark, + "mapping values are not allowed in this context", + )); + } + + self.roll_indent( + start_mark.col, + None, + TokenType::BlockMappingStart, + start_mark, + ); + } + + if self.flow_level == 0 { + self.allow_simple_key(); + } else { + self.disallow_simple_key(); + } + } + self.skip(); + self.tokens.push_back(Token(start_mark, TokenType::Value)); + + Ok(()) + } + + fn roll_indent(&mut self, col: usize, number: Option<usize>, tok: TokenType, mark: Marker) { + if self.flow_level > 0 { + return; + } + + if self.indent < col as isize { + self.indents.push(self.indent); + self.indent = col as isize; + let tokens_parsed = self.tokens_parsed; + match number { + Some(n) => self.insert_token(n - tokens_parsed, Token(mark, tok)), + None => self.tokens.push_back(Token(mark, tok)), + } + } + } + + fn unroll_indent(&mut self, col: isize) { + if self.flow_level > 0 { + return; + } + while self.indent > col { + self.tokens.push_back(Token(self.mark, TokenType::BlockEnd)); + self.indent = self.indents.pop().unwrap(); + } + } + + fn save_simple_key(&mut self) -> Result<(), ScanError> { + let required = self.flow_level > 0 && self.indent == (self.mark.col as isize); + if self.simple_key_allowed { + let mut sk = SimpleKey::new(self.mark); + sk.possible = true; + sk.required = required; + sk.token_number = self.tokens_parsed + self.tokens.len(); + + self.remove_simple_key()?; + + self.simple_keys.pop(); + self.simple_keys.push(sk); + } + Ok(()) + } + + fn remove_simple_key(&mut self) -> ScanResult { + let last = self.simple_keys.last_mut().unwrap(); + if last.possible && last.required { + return Err(ScanError::new(self.mark, "simple key expected")); + } + + last.possible = false; + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::TokenType::*; + use super::*; + + macro_rules! next { + ($p:ident, $tk:pat) => {{ + let tok = $p.next().unwrap(); + match tok.1 { + $tk => {} + _ => panic!("unexpected token: {:?}", tok), + } + }}; + } + + macro_rules! next_scalar { + ($p:ident, $tk:expr, $v:expr) => {{ + let tok = $p.next().unwrap(); + match tok.1 { + Scalar(style, ref v) => { + assert_eq!(style, $tk); + assert_eq!(v, $v); + } + _ => panic!("unexpected token: {:?}", tok), + } + }}; + } + + macro_rules! end { + ($p:ident) => {{ + assert_eq!($p.next(), None); + }}; + } + /// test cases in libyaml scanner.c + #[test] + fn test_empty() { + let s = ""; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, StreamEnd); + end!(p); + } + + #[test] + fn test_scalar() { + let s = "a scalar"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, Scalar(TScalarStyle::Plain, _)); + next!(p, StreamEnd); + end!(p); + } + + #[test] + fn test_explicit_scalar() { + let s = "--- +'a scalar' +... +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, DocumentStart); + next!(p, Scalar(TScalarStyle::SingleQuoted, _)); + next!(p, DocumentEnd); + next!(p, StreamEnd); + end!(p); + } + + #[test] + fn test_multiple_documents() { + let s = " +'a scalar' +--- +'a scalar' +--- +'a scalar' +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, Scalar(TScalarStyle::SingleQuoted, _)); + next!(p, DocumentStart); + next!(p, Scalar(TScalarStyle::SingleQuoted, _)); + next!(p, DocumentStart); + next!(p, Scalar(TScalarStyle::SingleQuoted, _)); + next!(p, StreamEnd); + end!(p); + } + + #[test] + fn test_a_flow_sequence() { + let s = "[item 1, item 2, item 3]"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, FlowSequenceStart); + next_scalar!(p, TScalarStyle::Plain, "item 1"); + next!(p, FlowEntry); + next!(p, Scalar(TScalarStyle::Plain, _)); + next!(p, FlowEntry); + next!(p, Scalar(TScalarStyle::Plain, _)); + next!(p, FlowSequenceEnd); + next!(p, StreamEnd); + end!(p); + } + + #[test] + fn test_a_flow_mapping() { + let s = " +{ + a simple key: a value, # Note that the KEY token is produced. + ? a complex key: another value, +} +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, FlowMappingStart); + next!(p, Key); + next!(p, Scalar(TScalarStyle::Plain, _)); + next!(p, Value); + next!(p, Scalar(TScalarStyle::Plain, _)); + next!(p, FlowEntry); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "a complex key"); + next!(p, Value); + next!(p, Scalar(TScalarStyle::Plain, _)); + next!(p, FlowEntry); + next!(p, FlowMappingEnd); + next!(p, StreamEnd); + end!(p); + } + + #[test] + fn test_block_sequences() { + let s = " +- item 1 +- item 2 +- + - item 3.1 + - item 3.2 +- + key 1: value 1 + key 2: value 2 +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, BlockSequenceStart); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 1"); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 2"); + next!(p, BlockEntry); + next!(p, BlockSequenceStart); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 3.1"); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 3.2"); + next!(p, BlockEnd); + next!(p, BlockEntry); + next!(p, BlockMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "key 1"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "value 1"); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "key 2"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "value 2"); + next!(p, BlockEnd); + next!(p, BlockEnd); + next!(p, StreamEnd); + end!(p); + } + + #[test] + fn test_block_mappings() { + let s = " +a simple key: a value # The KEY token is produced here. +? a complex key +: another value +a mapping: + key 1: value 1 + key 2: value 2 +a sequence: + - item 1 + - item 2 +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, BlockMappingStart); + next!(p, Key); + next!(p, Scalar(_, _)); + next!(p, Value); + next!(p, Scalar(_, _)); + next!(p, Key); + next!(p, Scalar(_, _)); + next!(p, Value); + next!(p, Scalar(_, _)); + next!(p, Key); + next!(p, Scalar(_, _)); + next!(p, Value); // libyaml comment seems to be wrong + next!(p, BlockMappingStart); + next!(p, Key); + next!(p, Scalar(_, _)); + next!(p, Value); + next!(p, Scalar(_, _)); + next!(p, Key); + next!(p, Scalar(_, _)); + next!(p, Value); + next!(p, Scalar(_, _)); + next!(p, BlockEnd); + next!(p, Key); + next!(p, Scalar(_, _)); + next!(p, Value); + next!(p, BlockSequenceStart); + next!(p, BlockEntry); + next!(p, Scalar(_, _)); + next!(p, BlockEntry); + next!(p, Scalar(_, _)); + next!(p, BlockEnd); + next!(p, BlockEnd); + next!(p, StreamEnd); + end!(p); + } + + #[test] + fn test_no_block_sequence_start() { + let s = " +key: +- item 1 +- item 2 +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, BlockMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "key"); + next!(p, Value); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 1"); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 2"); + next!(p, BlockEnd); + next!(p, StreamEnd); + end!(p); + } + + #[test] + fn test_collections_in_sequence() { + let s = " +- - item 1 + - item 2 +- key 1: value 1 + key 2: value 2 +- ? complex key + : complex value +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, BlockSequenceStart); + next!(p, BlockEntry); + next!(p, BlockSequenceStart); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 1"); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 2"); + next!(p, BlockEnd); + next!(p, BlockEntry); + next!(p, BlockMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "key 1"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "value 1"); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "key 2"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "value 2"); + next!(p, BlockEnd); + next!(p, BlockEntry); + next!(p, BlockMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "complex key"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "complex value"); + next!(p, BlockEnd); + next!(p, BlockEnd); + next!(p, StreamEnd); + end!(p); + } + + #[test] + fn test_collections_in_mapping() { + let s = " +? a sequence +: - item 1 + - item 2 +? a mapping +: key 1: value 1 + key 2: value 2 +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, BlockMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "a sequence"); + next!(p, Value); + next!(p, BlockSequenceStart); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 1"); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 2"); + next!(p, BlockEnd); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "a mapping"); + next!(p, Value); + next!(p, BlockMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "key 1"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "value 1"); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "key 2"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "value 2"); + next!(p, BlockEnd); + next!(p, BlockEnd); + next!(p, StreamEnd); + end!(p); + } + + #[test] + fn test_spec_ex7_3() { + let s = " +{ + ? foo :, + : bar, +} +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, FlowMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "foo"); + next!(p, Value); + next!(p, FlowEntry); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "bar"); + next!(p, FlowEntry); + next!(p, FlowMappingEnd); + next!(p, StreamEnd); + end!(p); + } + + #[test] + fn test_plain_scalar_starting_with_indicators_in_flow() { + // "Plain scalars must not begin with most indicators, as this would cause ambiguity with + // other YAML constructs. However, the “:”, “?” and “-” indicators may be used as the first + // character if followed by a non-space “safe” character, as this causes no ambiguity." + + let s = "{a: :b}"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, FlowMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "a"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, ":b"); + next!(p, FlowMappingEnd); + next!(p, StreamEnd); + end!(p); + + let s = "{a: ?b}"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, FlowMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "a"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "?b"); + next!(p, FlowMappingEnd); + next!(p, StreamEnd); + end!(p); + } + + #[test] + fn test_plain_scalar_starting_with_indicators_in_block() { + let s = ":a"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next_scalar!(p, TScalarStyle::Plain, ":a"); + next!(p, StreamEnd); + end!(p); + + let s = "?a"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next_scalar!(p, TScalarStyle::Plain, "?a"); + next!(p, StreamEnd); + end!(p); + } + + #[test] + fn test_plain_scalar_containing_indicators_in_block() { + let s = "a:,b"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next_scalar!(p, TScalarStyle::Plain, "a:,b"); + next!(p, StreamEnd); + end!(p); + + let s = ":,b"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next_scalar!(p, TScalarStyle::Plain, ":,b"); + next!(p, StreamEnd); + end!(p); + } + + #[test] + fn test_scanner_cr() { + let s = "---\r\n- tok1\r\n- tok2"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, DocumentStart); + next!(p, BlockSequenceStart); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "tok1"); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "tok2"); + next!(p, BlockEnd); + next!(p, StreamEnd); + end!(p); + } + + #[test] + fn test_uri() { + // TODO + } + + #[test] + fn test_uri_escapes() { + // TODO + } +} diff --git a/third_party/rust/yaml-rust/src/yaml.rs b/third_party/rust/yaml-rust/src/yaml.rs new file mode 100644 index 0000000000..4bb70da531 --- /dev/null +++ b/third_party/rust/yaml-rust/src/yaml.rs @@ -0,0 +1,739 @@ +use linked_hash_map::LinkedHashMap; +use crate::parser::*; +use crate::scanner::{Marker, ScanError, TScalarStyle, TokenType}; +use std::collections::BTreeMap; +use std::f64; +use std::i64; +use std::mem; +use std::ops::Index; +use std::string; +use std::vec; + +/// A YAML node is stored as this `Yaml` enumeration, which provides an easy way to +/// access your YAML document. +/// +/// # Examples +/// +/// ``` +/// use yaml_rust::Yaml; +/// let foo = Yaml::from_str("-123"); // convert the string to the appropriate YAML type +/// assert_eq!(foo.as_i64().unwrap(), -123); +/// +/// // iterate over an Array +/// let vec = Yaml::Array(vec![Yaml::Integer(1), Yaml::Integer(2)]); +/// for v in vec.as_vec().unwrap() { +/// assert!(v.as_i64().is_some()); +/// } +/// ``` +#[derive(Clone, PartialEq, PartialOrd, Debug, Eq, Ord, Hash)] +pub enum Yaml { + /// Float types are stored as String and parsed on demand. + /// Note that f64 does NOT implement Eq trait and can NOT be stored in BTreeMap. + Real(string::String), + /// YAML int is stored as i64. + Integer(i64), + /// YAML scalar. + String(string::String), + /// YAML bool, e.g. `true` or `false`. + Boolean(bool), + /// YAML array, can be accessed as a `Vec`. + Array(self::Array), + /// YAML hash, can be accessed as a `LinkedHashMap`. + /// + /// Insertion order will match the order of insertion into the map. + Hash(self::Hash), + /// Alias, not fully supported yet. + Alias(usize), + /// YAML null, e.g. `null` or `~`. + Null, + /// Accessing a nonexistent node via the Index trait returns `BadValue`. This + /// simplifies error handling in the calling code. Invalid type conversion also + /// returns `BadValue`. + BadValue, +} + +pub type Array = Vec<Yaml>; +pub type Hash = LinkedHashMap<Yaml, Yaml>; + +// parse f64 as Core schema +// See: https://github.com/chyh1990/yaml-rust/issues/51 +fn parse_f64(v: &str) -> Option<f64> { + match v { + ".inf" | ".Inf" | ".INF" | "+.inf" | "+.Inf" | "+.INF" => Some(f64::INFINITY), + "-.inf" | "-.Inf" | "-.INF" => Some(f64::NEG_INFINITY), + ".nan" | "NaN" | ".NAN" => Some(f64::NAN), + _ => v.parse::<f64>().ok(), + } +} + +pub struct YamlLoader { + docs: Vec<Yaml>, + // states + // (current node, anchor_id) tuple + doc_stack: Vec<(Yaml, usize)>, + key_stack: Vec<Yaml>, + anchor_map: BTreeMap<usize, Yaml>, +} + +impl MarkedEventReceiver for YamlLoader { + fn on_event(&mut self, ev: Event, _: Marker) { + // println!("EV {:?}", ev); + match ev { + Event::DocumentStart => { + // do nothing + } + Event::DocumentEnd => { + match self.doc_stack.len() { + // empty document + 0 => self.docs.push(Yaml::BadValue), + 1 => self.docs.push(self.doc_stack.pop().unwrap().0), + _ => unreachable!(), + } + } + Event::SequenceStart(aid) => { + self.doc_stack.push((Yaml::Array(Vec::new()), aid)); + } + Event::SequenceEnd => { + let node = self.doc_stack.pop().unwrap(); + self.insert_new_node(node); + } + Event::MappingStart(aid) => { + self.doc_stack.push((Yaml::Hash(Hash::new()), aid)); + self.key_stack.push(Yaml::BadValue); + } + Event::MappingEnd => { + self.key_stack.pop().unwrap(); + let node = self.doc_stack.pop().unwrap(); + self.insert_new_node(node); + } + Event::Scalar(v, style, aid, tag) => { + let node = if style != TScalarStyle::Plain { + Yaml::String(v) + } else if let Some(TokenType::Tag(ref handle, ref suffix)) = tag { + // XXX tag:yaml.org,2002: + if handle == "!!" { + match suffix.as_ref() { + "bool" => { + // "true" or "false" + match v.parse::<bool>() { + Err(_) => Yaml::BadValue, + Ok(v) => Yaml::Boolean(v), + } + } + "int" => match v.parse::<i64>() { + Err(_) => Yaml::BadValue, + Ok(v) => Yaml::Integer(v), + }, + "float" => match parse_f64(&v) { + Some(_) => Yaml::Real(v), + None => Yaml::BadValue, + }, + "null" => match v.as_ref() { + "~" | "null" => Yaml::Null, + _ => Yaml::BadValue, + }, + _ => Yaml::String(v), + } + } else { + Yaml::String(v) + } + } else { + // Datatype is not specified, or unrecognized + Yaml::from_str(&v) + }; + + self.insert_new_node((node, aid)); + } + Event::Alias(id) => { + let n = match self.anchor_map.get(&id) { + Some(v) => v.clone(), + None => Yaml::BadValue, + }; + self.insert_new_node((n, 0)); + } + _ => { /* ignore */ } + } + // println!("DOC {:?}", self.doc_stack); + } +} + +impl YamlLoader { + fn insert_new_node(&mut self, node: (Yaml, usize)) { + // valid anchor id starts from 1 + if node.1 > 0 { + self.anchor_map.insert(node.1, node.0.clone()); + } + if self.doc_stack.is_empty() { + self.doc_stack.push(node); + } else { + let parent = self.doc_stack.last_mut().unwrap(); + match *parent { + (Yaml::Array(ref mut v), _) => v.push(node.0), + (Yaml::Hash(ref mut h), _) => { + let cur_key = self.key_stack.last_mut().unwrap(); + // current node is a key + if cur_key.is_badvalue() { + *cur_key = node.0; + // current node is a value + } else { + let mut newkey = Yaml::BadValue; + mem::swap(&mut newkey, cur_key); + h.insert(newkey, node.0); + } + } + _ => unreachable!(), + } + } + } + + pub fn load_from_str(source: &str) -> Result<Vec<Yaml>, ScanError> { + let mut loader = YamlLoader { + docs: Vec::new(), + doc_stack: Vec::new(), + key_stack: Vec::new(), + anchor_map: BTreeMap::new(), + }; + let mut parser = Parser::new(source.chars()); + parser.load(&mut loader, true)?; + Ok(loader.docs) + } +} + +macro_rules! define_as ( + ($name:ident, $t:ident, $yt:ident) => ( +pub fn $name(&self) -> Option<$t> { + match *self { + Yaml::$yt(v) => Some(v), + _ => None + } +} + ); +); + +macro_rules! define_as_ref ( + ($name:ident, $t:ty, $yt:ident) => ( +pub fn $name(&self) -> Option<$t> { + match *self { + Yaml::$yt(ref v) => Some(v), + _ => None + } +} + ); +); + +macro_rules! define_into ( + ($name:ident, $t:ty, $yt:ident) => ( +pub fn $name(self) -> Option<$t> { + match self { + Yaml::$yt(v) => Some(v), + _ => None + } +} + ); +); + +impl Yaml { + define_as!(as_bool, bool, Boolean); + define_as!(as_i64, i64, Integer); + + define_as_ref!(as_str, &str, String); + define_as_ref!(as_hash, &Hash, Hash); + define_as_ref!(as_vec, &Array, Array); + + define_into!(into_bool, bool, Boolean); + define_into!(into_i64, i64, Integer); + define_into!(into_string, String, String); + define_into!(into_hash, Hash, Hash); + define_into!(into_vec, Array, Array); + + pub fn is_null(&self) -> bool { + match *self { + Yaml::Null => true, + _ => false, + } + } + + pub fn is_badvalue(&self) -> bool { + match *self { + Yaml::BadValue => true, + _ => false, + } + } + + pub fn is_array(&self) -> bool { + match *self { + Yaml::Array(_) => true, + _ => false, + } + } + + pub fn as_f64(&self) -> Option<f64> { + match *self { + Yaml::Real(ref v) => parse_f64(v), + _ => None, + } + } + + pub fn into_f64(self) -> Option<f64> { + match self { + Yaml::Real(ref v) => parse_f64(v), + _ => None, + } + } +} + +#[cfg_attr(feature = "cargo-clippy", allow(should_implement_trait))] +impl Yaml { + // Not implementing FromStr because there is no possibility of Error. + // This function falls back to Yaml::String if nothing else matches. + pub fn from_str(v: &str) -> Yaml { + if v.starts_with("0x") { + if let Ok(i) = i64::from_str_radix(&v[2..], 16) { + return Yaml::Integer(i); + } + } + if v.starts_with("0o") { + if let Ok(i) = i64::from_str_radix(&v[2..], 8) { + return Yaml::Integer(i); + } + } + if v.starts_with('+') { + if let Ok(i) = v[1..].parse::<i64>() { + return Yaml::Integer(i); + } + } + match v { + "~" | "null" => Yaml::Null, + "true" => Yaml::Boolean(true), + "false" => Yaml::Boolean(false), + _ if v.parse::<i64>().is_ok() => Yaml::Integer(v.parse::<i64>().unwrap()), + // try parsing as f64 + _ if parse_f64(v).is_some() => Yaml::Real(v.to_owned()), + _ => Yaml::String(v.to_owned()), + } + } +} + +static BAD_VALUE: Yaml = Yaml::BadValue; +impl<'a> Index<&'a str> for Yaml { + type Output = Yaml; + + fn index(&self, idx: &'a str) -> &Yaml { + let key = Yaml::String(idx.to_owned()); + match self.as_hash() { + Some(h) => h.get(&key).unwrap_or(&BAD_VALUE), + None => &BAD_VALUE, + } + } +} + +impl Index<usize> for Yaml { + type Output = Yaml; + + fn index(&self, idx: usize) -> &Yaml { + if let Some(v) = self.as_vec() { + v.get(idx).unwrap_or(&BAD_VALUE) + } else if let Some(v) = self.as_hash() { + let key = Yaml::Integer(idx as i64); + v.get(&key).unwrap_or(&BAD_VALUE) + } else { + &BAD_VALUE + } + } +} + +impl IntoIterator for Yaml { + type Item = Yaml; + type IntoIter = YamlIter; + + fn into_iter(self) -> Self::IntoIter { + YamlIter { + yaml: self.into_vec().unwrap_or_else(Vec::new).into_iter(), + } + } +} + +pub struct YamlIter { + yaml: vec::IntoIter<Yaml>, +} + +impl Iterator for YamlIter { + type Item = Yaml; + + fn next(&mut self) -> Option<Yaml> { + self.yaml.next() + } +} + +#[cfg(test)] +mod test { + use std::f64; + use crate::yaml::*; + #[test] + fn test_coerce() { + let s = "--- +a: 1 +b: 2.2 +c: [1, 2] +"; + let out = YamlLoader::load_from_str(&s).unwrap(); + let doc = &out[0]; + assert_eq!(doc["a"].as_i64().unwrap(), 1i64); + assert_eq!(doc["b"].as_f64().unwrap(), 2.2f64); + assert_eq!(doc["c"][1].as_i64().unwrap(), 2i64); + assert!(doc["d"][0].is_badvalue()); + } + + #[test] + fn test_empty_doc() { + let s: String = "".to_owned(); + YamlLoader::load_from_str(&s).unwrap(); + let s: String = "---".to_owned(); + assert_eq!(YamlLoader::load_from_str(&s).unwrap()[0], Yaml::Null); + } + + #[test] + fn test_parser() { + let s: String = " +# comment +a0 bb: val +a1: + b1: 4 + b2: d +a2: 4 # i'm comment +a3: [1, 2, 3] +a4: + - - a1 + - a2 + - 2 +a5: 'single_quoted' +a6: \"double_quoted\" +a7: 你好 +" + .to_owned(); + let out = YamlLoader::load_from_str(&s).unwrap(); + let doc = &out[0]; + assert_eq!(doc["a7"].as_str().unwrap(), "你好"); + } + + #[test] + fn test_multi_doc() { + let s = " +'a scalar' +--- +'a scalar' +--- +'a scalar' +"; + let out = YamlLoader::load_from_str(&s).unwrap(); + assert_eq!(out.len(), 3); + } + + #[test] + fn test_anchor() { + let s = " +a1: &DEFAULT + b1: 4 + b2: d +a2: *DEFAULT +"; + let out = YamlLoader::load_from_str(&s).unwrap(); + let doc = &out[0]; + assert_eq!(doc["a2"]["b1"].as_i64().unwrap(), 4); + } + + #[test] + fn test_bad_anchor() { + let s = " +a1: &DEFAULT + b1: 4 + b2: *DEFAULT +"; + let out = YamlLoader::load_from_str(&s).unwrap(); + let doc = &out[0]; + assert_eq!(doc["a1"]["b2"], Yaml::BadValue); + } + + #[test] + fn test_github_27() { + // https://github.com/chyh1990/yaml-rust/issues/27 + let s = "&a"; + let out = YamlLoader::load_from_str(&s).unwrap(); + let doc = &out[0]; + assert_eq!(doc.as_str().unwrap(), ""); + } + + #[test] + fn test_plain_datatype() { + let s = " +- 'string' +- \"string\" +- string +- 123 +- -321 +- 1.23 +- -1e4 +- ~ +- null +- true +- false +- !!str 0 +- !!int 100 +- !!float 2 +- !!null ~ +- !!bool true +- !!bool false +- 0xFF +# bad values +- !!int string +- !!float string +- !!bool null +- !!null val +- 0o77 +- [ 0xF, 0xF ] +- +12345 +- [ true, false ] +"; + let out = YamlLoader::load_from_str(&s).unwrap(); + let doc = &out[0]; + + assert_eq!(doc[0].as_str().unwrap(), "string"); + assert_eq!(doc[1].as_str().unwrap(), "string"); + assert_eq!(doc[2].as_str().unwrap(), "string"); + assert_eq!(doc[3].as_i64().unwrap(), 123); + assert_eq!(doc[4].as_i64().unwrap(), -321); + assert_eq!(doc[5].as_f64().unwrap(), 1.23); + assert_eq!(doc[6].as_f64().unwrap(), -1e4); + assert!(doc[7].is_null()); + assert!(doc[8].is_null()); + assert_eq!(doc[9].as_bool().unwrap(), true); + assert_eq!(doc[10].as_bool().unwrap(), false); + assert_eq!(doc[11].as_str().unwrap(), "0"); + assert_eq!(doc[12].as_i64().unwrap(), 100); + assert_eq!(doc[13].as_f64().unwrap(), 2.0); + assert!(doc[14].is_null()); + assert_eq!(doc[15].as_bool().unwrap(), true); + assert_eq!(doc[16].as_bool().unwrap(), false); + assert_eq!(doc[17].as_i64().unwrap(), 255); + assert!(doc[18].is_badvalue()); + assert!(doc[19].is_badvalue()); + assert!(doc[20].is_badvalue()); + assert!(doc[21].is_badvalue()); + assert_eq!(doc[22].as_i64().unwrap(), 63); + assert_eq!(doc[23][0].as_i64().unwrap(), 15); + assert_eq!(doc[23][1].as_i64().unwrap(), 15); + assert_eq!(doc[24].as_i64().unwrap(), 12345); + assert!(doc[25][0].as_bool().unwrap()); + assert!(!doc[25][1].as_bool().unwrap()); + } + + #[test] + fn test_bad_hyphen() { + // See: https://github.com/chyh1990/yaml-rust/issues/23 + let s = "{-"; + assert!(YamlLoader::load_from_str(&s).is_err()); + } + + #[test] + fn test_issue_65() { + // See: https://github.com/chyh1990/yaml-rust/issues/65 + let b = "\n\"ll\\\"ll\\\r\n\"ll\\\"ll\\\r\r\r\rU\r\r\rU"; + assert!(YamlLoader::load_from_str(&b).is_err()); + } + + #[test] + fn test_bad_docstart() { + assert!(YamlLoader::load_from_str("---This used to cause an infinite loop").is_ok()); + assert_eq!( + YamlLoader::load_from_str("----"), + Ok(vec![Yaml::String(String::from("----"))]) + ); + assert_eq!( + YamlLoader::load_from_str("--- #here goes a comment"), + Ok(vec![Yaml::Null]) + ); + assert_eq!( + YamlLoader::load_from_str("---- #here goes a comment"), + Ok(vec![Yaml::String(String::from("----"))]) + ); + } + + #[test] + fn test_plain_datatype_with_into_methods() { + let s = " +- 'string' +- \"string\" +- string +- 123 +- -321 +- 1.23 +- -1e4 +- true +- false +- !!str 0 +- !!int 100 +- !!float 2 +- !!bool true +- !!bool false +- 0xFF +- 0o77 +- +12345 +- -.INF +- .NAN +- !!float .INF +"; + let mut out = YamlLoader::load_from_str(&s).unwrap().into_iter(); + let mut doc = out.next().unwrap().into_iter(); + + assert_eq!(doc.next().unwrap().into_string().unwrap(), "string"); + assert_eq!(doc.next().unwrap().into_string().unwrap(), "string"); + assert_eq!(doc.next().unwrap().into_string().unwrap(), "string"); + assert_eq!(doc.next().unwrap().into_i64().unwrap(), 123); + assert_eq!(doc.next().unwrap().into_i64().unwrap(), -321); + assert_eq!(doc.next().unwrap().into_f64().unwrap(), 1.23); + assert_eq!(doc.next().unwrap().into_f64().unwrap(), -1e4); + assert_eq!(doc.next().unwrap().into_bool().unwrap(), true); + assert_eq!(doc.next().unwrap().into_bool().unwrap(), false); + assert_eq!(doc.next().unwrap().into_string().unwrap(), "0"); + assert_eq!(doc.next().unwrap().into_i64().unwrap(), 100); + assert_eq!(doc.next().unwrap().into_f64().unwrap(), 2.0); + assert_eq!(doc.next().unwrap().into_bool().unwrap(), true); + assert_eq!(doc.next().unwrap().into_bool().unwrap(), false); + assert_eq!(doc.next().unwrap().into_i64().unwrap(), 255); + assert_eq!(doc.next().unwrap().into_i64().unwrap(), 63); + assert_eq!(doc.next().unwrap().into_i64().unwrap(), 12345); + assert_eq!(doc.next().unwrap().into_f64().unwrap(), f64::NEG_INFINITY); + assert!(doc.next().unwrap().into_f64().is_some()); + assert_eq!(doc.next().unwrap().into_f64().unwrap(), f64::INFINITY); + } + + #[test] + fn test_hash_order() { + let s = "--- +b: ~ +a: ~ +c: ~ +"; + let out = YamlLoader::load_from_str(&s).unwrap(); + let first = out.into_iter().next().unwrap(); + let mut iter = first.into_hash().unwrap().into_iter(); + assert_eq!( + Some((Yaml::String("b".to_owned()), Yaml::Null)), + iter.next() + ); + assert_eq!( + Some((Yaml::String("a".to_owned()), Yaml::Null)), + iter.next() + ); + assert_eq!( + Some((Yaml::String("c".to_owned()), Yaml::Null)), + iter.next() + ); + assert_eq!(None, iter.next()); + } + + #[test] + fn test_integer_key() { + let s = " +0: + important: true +1: + important: false +"; + let out = YamlLoader::load_from_str(&s).unwrap(); + let first = out.into_iter().next().unwrap(); + assert_eq!(first[0]["important"].as_bool().unwrap(), true); + } + + #[test] + fn test_indentation_equality() { + let four_spaces = YamlLoader::load_from_str( + r#" +hash: + with: + indentations +"#, + ) + .unwrap() + .into_iter() + .next() + .unwrap(); + + let two_spaces = YamlLoader::load_from_str( + r#" +hash: + with: + indentations +"#, + ) + .unwrap() + .into_iter() + .next() + .unwrap(); + + let one_space = YamlLoader::load_from_str( + r#" +hash: + with: + indentations +"#, + ) + .unwrap() + .into_iter() + .next() + .unwrap(); + + let mixed_spaces = YamlLoader::load_from_str( + r#" +hash: + with: + indentations +"#, + ) + .unwrap() + .into_iter() + .next() + .unwrap(); + + assert_eq!(four_spaces, two_spaces); + assert_eq!(two_spaces, one_space); + assert_eq!(four_spaces, mixed_spaces); + } + + #[test] + fn test_two_space_indentations() { + // https://github.com/kbknapp/clap-rs/issues/965 + + let s = r#" +subcommands: + - server: + about: server related commands +subcommands2: + - server: + about: server related commands +subcommands3: + - server: + about: server related commands + "#; + + let out = YamlLoader::load_from_str(&s).unwrap(); + let doc = &out.into_iter().next().unwrap(); + + println!("{:#?}", doc); + assert_eq!(doc["subcommands"][0]["server"], Yaml::Null); + assert!(doc["subcommands2"][0]["server"].as_hash().is_some()); + assert!(doc["subcommands3"][0]["server"].as_hash().is_some()); + } + + #[test] + fn test_recursion_depth_check_objects() { + let s = "{a:".repeat(10_000) + &"}".repeat(10_000); + assert!(YamlLoader::load_from_str(&s).is_err()); + } + + #[test] + fn test_recursion_depth_check_arrays() { + let s = "[".repeat(10_000) + &"]".repeat(10_000); + assert!(YamlLoader::load_from_str(&s).is_err()); + } +} diff --git a/third_party/rust/yaml-rust/tests/quickcheck.rs b/third_party/rust/yaml-rust/tests/quickcheck.rs new file mode 100644 index 0000000000..0efd679978 --- /dev/null +++ b/third_party/rust/yaml-rust/tests/quickcheck.rs @@ -0,0 +1,21 @@ +extern crate yaml_rust; +#[macro_use] +extern crate quickcheck; + +use quickcheck::TestResult; +use yaml_rust::{Yaml, YamlEmitter, YamlLoader}; + +quickcheck! { + fn test_check_weird_keys(xs: Vec<String>) -> TestResult { + let mut out_str = String::new(); + let input = Yaml::Array(xs.into_iter().map(Yaml::String).collect()); + { + let mut emitter = YamlEmitter::new(&mut out_str); + emitter.dump(&input).unwrap(); + } + match YamlLoader::load_from_str(&out_str) { + Ok(output) => TestResult::from_bool(output.len() == 1 && input == output[0]), + Err(err) => TestResult::error(err.to_string()), + } + } +} diff --git a/third_party/rust/yaml-rust/tests/spec_test.rs b/third_party/rust/yaml-rust/tests/spec_test.rs new file mode 100644 index 0000000000..442728f4f3 --- /dev/null +++ b/third_party/rust/yaml-rust/tests/spec_test.rs @@ -0,0 +1,140 @@ +#![allow(dead_code)] +#![allow(non_upper_case_globals)] +extern crate yaml_rust; + +use yaml_rust::parser::{Event, EventReceiver, Parser}; +use yaml_rust::scanner::TScalarStyle; + +// These names match the names used in the C++ test suite. +#[cfg_attr(feature = "cargo-clippy", allow(enum_variant_names))] +#[derive(Clone, PartialEq, PartialOrd, Debug)] +enum TestEvent { + OnDocumentStart, + OnDocumentEnd, + OnSequenceStart, + OnSequenceEnd, + OnMapStart, + OnMapEnd, + OnScalar, + OnAlias, + OnNull, +} + +struct YamlChecker { + pub evs: Vec<TestEvent>, +} + +impl EventReceiver for YamlChecker { + fn on_event(&mut self, ev: Event) { + let tev = match ev { + Event::DocumentStart => TestEvent::OnDocumentStart, + Event::DocumentEnd => TestEvent::OnDocumentEnd, + Event::SequenceStart(..) => TestEvent::OnSequenceStart, + Event::SequenceEnd => TestEvent::OnSequenceEnd, + Event::MappingStart(..) => TestEvent::OnMapStart, + Event::MappingEnd => TestEvent::OnMapEnd, + Event::Scalar(ref v, style, _, _) => { + if v == "~" && style == TScalarStyle::Plain { + TestEvent::OnNull + } else { + TestEvent::OnScalar + } + } + Event::Alias(_) => TestEvent::OnAlias, + _ => return, // ignore other events + }; + self.evs.push(tev); + } +} + +fn str_to_test_events(docs: &str) -> Vec<TestEvent> { + let mut p = YamlChecker { evs: Vec::new() }; + let mut parser = Parser::new(docs.chars()); + parser.load(&mut p, true).unwrap(); + p.evs +} + +macro_rules! assert_next { + ($v:expr, $p:pat) => { + match $v.next().unwrap() { + $p => {} + e => { + panic!("unexpected event: {:?}", e); + } + } + }; +} + +// auto generated from handler_spec_test.cpp +include!("specexamples.rs.inc"); +include!("spec_test.rs.inc"); + +// hand-crafted tests +//#[test] +//fn test_hc_alias() { +//} + +#[test] +fn test_mapvec_legal() { + use yaml_rust::yaml::{Array, Hash, Yaml}; + use yaml_rust::{YamlEmitter, YamlLoader}; + + // Emitting a `map<map<seq<_>>, _>` should result in legal yaml that + // we can parse. + + let mut key = Array::new(); + key.push(Yaml::Integer(1)); + key.push(Yaml::Integer(2)); + key.push(Yaml::Integer(3)); + + let mut keyhash = Hash::new(); + keyhash.insert(Yaml::String("key".into()), Yaml::Array(key)); + + let mut val = Array::new(); + val.push(Yaml::Integer(4)); + val.push(Yaml::Integer(5)); + val.push(Yaml::Integer(6)); + + let mut hash = Hash::new(); + hash.insert(Yaml::Hash(keyhash), Yaml::Array(val)); + + let mut out_str = String::new(); + { + let mut emitter = YamlEmitter::new(&mut out_str); + emitter.dump(&Yaml::Hash(hash)).unwrap(); + } + + // At this point, we are tempted to naively render like this: + // + // ```yaml + // --- + // {key: + // - 1 + // - 2 + // - 3}: + // - 4 + // - 5 + // - 6 + // ``` + // + // However, this doesn't work, because the key sequence [1, 2, 3] is + // rendered in block mode, which is not legal (as far as I can tell) + // inside the flow mode of the key. We need to either fully render + // everything that's in a key in flow mode (which may make for some + // long lines), or use the explicit map identifier '?': + // + // ```yaml + // --- + // ? + // key: + // - 1 + // - 2 + // - 3 + // : + // - 4 + // - 5 + // - 6 + // ``` + + YamlLoader::load_from_str(&out_str).unwrap(); +} diff --git a/third_party/rust/yaml-rust/tests/spec_test.rs.inc b/third_party/rust/yaml-rust/tests/spec_test.rs.inc new file mode 100644 index 0000000000..bb50b3b3cc --- /dev/null +++ b/third_party/rust/yaml-rust/tests/spec_test.rs.inc @@ -0,0 +1,1513 @@ +#[test] +fn test_ex2_1_seq_scalars() { + let mut v = str_to_test_events(EX2_1).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_2_mapping_scalars_to_scalars() { + let mut v = str_to_test_events(EX2_2).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_3_mapping_scalars_to_sequences() { + let mut v = str_to_test_events(EX2_3).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_4_sequence_of_mappings() { + let mut v = str_to_test_events(EX2_4).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_5_sequence_of_sequences() { + let mut v = str_to_test_events(EX2_5).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_6_mapping_of_mappings() { + let mut v = str_to_test_events(EX2_6).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_7_two_documents_in_a_stream() { + let mut v = str_to_test_events(EX2_7).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_8_play_by_play_feed() { + let mut v = str_to_test_events(EX2_8).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_9_single_document_with_two_comments() { + let mut v = str_to_test_events(EX2_9).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_10_simple_anchor() { + let mut v = str_to_test_events(EX2_10).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnAlias); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_11_mapping_between_sequences() { + let mut v = str_to_test_events(EX2_11).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_12_compact_nested_mapping() { + let mut v = str_to_test_events(EX2_12).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_13_in_literals_newlines_are_preserved() { + let mut v = str_to_test_events(EX2_13).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_14_in_folded_scalars_newlines_become_spaces() { + let mut v = str_to_test_events(EX2_14).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_15_folded_newlines_are_preserved_for_more_indented_and_blank_lines() { + let mut v = str_to_test_events(EX2_15).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_16_indentation_determines_scope() { + let mut v = str_to_test_events(EX2_16).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_17_quoted_scalars() { + let mut v = str_to_test_events(EX2_17).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_18_multi_line_flow_scalars() { + let mut v = str_to_test_events(EX2_18).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_23_various_explicit_tags() { + let mut v = str_to_test_events(EX2_23).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_24_global_tags() { + let mut v = str_to_test_events(EX2_24).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnAlias); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnAlias); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_25_unordered_sets() { + let mut v = str_to_test_events(EX2_25).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnNull); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnNull); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnNull); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_26_ordered_mappings() { + let mut v = str_to_test_events(EX2_26).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_27_invoice() { + let mut v = str_to_test_events(EX2_27).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnAlias); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex2_28_log_file() { + let mut v = str_to_test_events(EX2_28).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex5_3_block_structure_indicators() { + let mut v = str_to_test_events(EX5_3).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex5_4_flow_structure_indicators() { + let mut v = str_to_test_events(EX5_4).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex5_6_node_property_indicators() { + let mut v = str_to_test_events(EX5_6).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnAlias); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex5_7_block_scalar_indicators() { + let mut v = str_to_test_events(EX5_7).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex5_8_quoted_scalar_indicators() { + let mut v = str_to_test_events(EX5_8).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex5_11_line_break_characters() { + let mut v = str_to_test_events(EX5_11).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex5_12_tabs_and_spaces() { + let mut v = str_to_test_events(EX5_12).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex5_13_escaped_characters() { + let mut v = str_to_test_events(EX5_13).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_1_indentation_spaces() { + let mut v = str_to_test_events(EX6_1).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_2_indentation_indicators() { + let mut v = str_to_test_events(EX6_2).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_3_separation_spaces() { + let mut v = str_to_test_events(EX6_3).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_4_line_prefixes() { + let mut v = str_to_test_events(EX6_4).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_5_empty_lines() { + let mut v = str_to_test_events(EX6_5).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_6_line_folding() { + let mut v = str_to_test_events(EX6_6).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_7_block_folding() { + let mut v = str_to_test_events(EX6_7).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_8_flow_folding() { + let mut v = str_to_test_events(EX6_8).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_9_separated_comment() { + let mut v = str_to_test_events(EX6_9).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_12_separation_spaces_ii() { + let mut v = str_to_test_events(EX6_12).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_13_reserved_directives() { + let mut v = str_to_test_events(EX6_13).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_14_yaml_directive() { + let mut v = str_to_test_events(EX6_14).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_16_tag_directive() { + let mut v = str_to_test_events(EX6_16).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_18_primary_tag_handle() { + let mut v = str_to_test_events(EX6_18).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_19_secondary_tag_handle() { + let mut v = str_to_test_events(EX6_19).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_20_tag_handles() { + let mut v = str_to_test_events(EX6_20).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_21_local_tag_prefix() { + let mut v = str_to_test_events(EX6_21).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_22_global_tag_prefix() { + let mut v = str_to_test_events(EX6_22).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_23_node_properties() { + let mut v = str_to_test_events(EX6_23).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnAlias); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_24_verbatim_tags() { + let mut v = str_to_test_events(EX6_24).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_26_tag_shorthands() { + let mut v = str_to_test_events(EX6_26).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_28_non_specific_tags() { + let mut v = str_to_test_events(EX6_28).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex6_29_node_anchors() { + let mut v = str_to_test_events(EX6_29).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnAlias); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_1_alias_nodes() { + let mut v = str_to_test_events(EX7_1).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnAlias); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnAlias); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[allow(dead_code)] +fn test_ex7_2_empty_nodes() { + let mut v = str_to_test_events(EX7_2).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_3_completely_empty_nodes() { + let mut v = str_to_test_events(EX7_3).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnNull); + assert_next!(v, TestEvent::OnNull); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_4_double_quoted_implicit_keys() { + let mut v = str_to_test_events(EX7_4).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_5_double_quoted_line_breaks() { + let mut v = str_to_test_events(EX7_5).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_6_double_quoted_lines() { + let mut v = str_to_test_events(EX7_6).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_7_single_quoted_characters() { + let mut v = str_to_test_events(EX7_7).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_8_single_quoted_implicit_keys() { + let mut v = str_to_test_events(EX7_8).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_9_single_quoted_lines() { + let mut v = str_to_test_events(EX7_9).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[allow(dead_code)] +fn test_ex7_10_plain_characters() { + let mut v = str_to_test_events(EX7_10).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_11_plain_implicit_keys() { + let mut v = str_to_test_events(EX7_11).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_12_plain_lines() { + let mut v = str_to_test_events(EX7_12).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_13_flow_sequence() { + let mut v = str_to_test_events(EX7_13).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_14_flow_sequence_entries() { + let mut v = str_to_test_events(EX7_14).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_15_flow_mappings() { + let mut v = str_to_test_events(EX7_15).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_16_flow_mapping_entries() { + let mut v = str_to_test_events(EX7_16).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnNull); + assert_next!(v, TestEvent::OnNull); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[allow(dead_code)] +fn test_ex7_17_flow_mapping_separate_values() { + let mut v = str_to_test_events(EX7_17).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnNull); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnNull); + assert_next!(v, TestEvent::OnNull); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_18_flow_mapping_adjacent_values() { + let mut v = str_to_test_events(EX7_18).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnNull); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_19_single_pair_flow_mappings() { + let mut v = str_to_test_events(EX7_19).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_20_single_pair_explicit_entry() { + let mut v = str_to_test_events(EX7_20).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[allow(dead_code)] +fn test_ex7_21_single_pair_implicit_entries() { + let mut v = str_to_test_events(EX7_21).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnNull); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_23_flow_content() { + let mut v = str_to_test_events(EX7_23).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex7_24_flow_nodes() { + let mut v = str_to_test_events(EX7_24).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnAlias); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_1_block_scalar_header() { + let mut v = str_to_test_events(EX8_1).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[allow(dead_code)] +fn test_ex8_2_block_indentation_header() { + let mut v = str_to_test_events(EX8_2).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_4_chomping_final_line_break() { + let mut v = str_to_test_events(EX8_4).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_6_empty_scalar_chomping() { + let mut v = str_to_test_events(EX8_6).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_7_literal_scalar() { + let mut v = str_to_test_events(EX8_7).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_8_literal_content() { + let mut v = str_to_test_events(EX8_8).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_9_folded_scalar() { + let mut v = str_to_test_events(EX8_9).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_10_folded_lines() { + let mut v = str_to_test_events(EX8_10).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_11_more_indented_lines() { + let mut v = str_to_test_events(EX8_11).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_12_empty_separation_lines() { + let mut v = str_to_test_events(EX8_12).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_13_final_empty_lines() { + let mut v = str_to_test_events(EX8_13).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_14_block_sequence() { + let mut v = str_to_test_events(EX8_14).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_15_block_sequence_entry_types() { + let mut v = str_to_test_events(EX8_15).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnNull); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_16_block_mappings() { + let mut v = str_to_test_events(EX8_16).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_17_explicit_block_mapping_entries() { + let mut v = str_to_test_events(EX8_17).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnNull); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_18_implicit_block_mapping_entries() { + let mut v = str_to_test_events(EX8_18).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnNull); + assert_next!(v, TestEvent::OnNull); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_19_compact_block_mappings() { + let mut v = str_to_test_events(EX8_19).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_20_block_node_types() { + let mut v = str_to_test_events(EX8_20).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + +#[test] +fn test_ex8_22_block_collection_nodes() { + let mut v = str_to_test_events(EX8_22).into_iter(); + assert_next!(v, TestEvent::OnDocumentStart); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnSequenceEnd); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapStart); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnScalar); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnMapEnd); + assert_next!(v, TestEvent::OnDocumentEnd); +} + diff --git a/third_party/rust/yaml-rust/tests/specexamples.rs.inc b/third_party/rust/yaml-rust/tests/specexamples.rs.inc new file mode 100644 index 0000000000..a5398c3ab1 --- /dev/null +++ b/third_party/rust/yaml-rust/tests/specexamples.rs.inc @@ -0,0 +1,337 @@ +const EX2_1 : &'static str = + "- Mark McGwire\n- Sammy Sosa\n- Ken Griffey"; + +const EX2_2 : &'static str = + "hr: 65 # Home runs\navg: 0.278 # Batting average\nrbi: 147 # Runs Batted In"; + +const EX2_3 : &'static str = + "american:\n- Boston Red Sox\n- Detroit Tigers\n- New York Yankees\nnational:\n- New York Mets\n- Chicago Cubs\n- Atlanta Braves"; + +const EX2_4 : &'static str = + "-\n name: Mark McGwire\n hr: 65\n avg: 0.278\n-\n name: Sammy Sosa\n hr: 63\n avg: 0.288"; + +const EX2_5 : &'static str = + "- [name , hr, avg ]\n- [Mark McGwire, 65, 0.278]\n- [Sammy Sosa , 63, 0.288]"; + +const EX2_6 : &'static str = + "Mark McGwire: {hr: 65, avg: 0.278}\nSammy Sosa: {\n hr: 63,\n avg: 0.288\n }"; + +const EX2_7 : &'static str = + "# Ranking of 1998 home runs\n---\n- Mark McGwire\n- Sammy Sosa\n- Ken Griffey\n\n# Team ranking\n---\n- Chicago Cubs\n- St Louis Cardinals"; + +const EX2_8 : &'static str = + "---\ntime: 20:03:20\nplayer: Sammy Sosa\naction: strike (miss)\n...\n---\ntime: 20:03:47\nplayer: Sammy Sosa\naction: grand slam\n..."; + +const EX2_9 : &'static str = + "---\nhr: # 1998 hr ranking\n - Mark McGwire\n - Sammy Sosa\nrbi:\n # 1998 rbi ranking\n - Sammy Sosa\n - Ken Griffey"; + +const EX2_10 : &'static str = + "---\nhr:\n - Mark McGwire\n # Following node labeled SS\n - &SS Sammy Sosa\nrbi:\n - *SS # Subsequent occurrence\n - Ken Griffey"; + +const EX2_11 : &'static str = + "? - Detroit Tigers\n - Chicago cubs\n:\n - 2001-07-23\n\n? [ New York Yankees,\n Atlanta Braves ]\n: [ 2001-07-02, 2001-08-12,\n 2001-08-14 ]"; + +const EX2_12 : &'static str = + "---\n# Products purchased\n- item : Super Hoop\n quantity: 1\n- item : Basketball\n quantity: 4\n- item : Big Shoes\n quantity: 1"; + +const EX2_13 : &'static str = + "# ASCII Art\n--- |\n \\//||\\/||\n // || ||__"; + +const EX2_14 : &'static str = + "--- >\n Mark McGwire's\n year was crippled\n by a knee injury."; + +const EX2_15 : &'static str = + ">\n Sammy Sosa completed another\n fine season with great stats.\n \n 63 Home Runs\n 0.288 Batting Average\n \n What a year!"; + +const EX2_16 : &'static str = + "name: Mark McGwire\naccomplishment: >\n Mark set a major league\n home run record in 1998.\nstats: |\n 65 Home Runs\n 0.278 Batting Average\n"; + +const EX2_17 : &'static str = + "unicode: \"Sosa did fine.\\u263A\"\ncontrol: \"\\b1998\\t1999\\t2000\\n\"\nhex esc: \"\\x0d\\x0a is \\r\\n\"\n\nsingle: '\"Howdy!\" he cried.'\nquoted: ' # Not a ''comment''.'\ntie-fighter: '|\\-*-/|'"; + +const EX2_18 : &'static str = + "plain:\n This unquoted scalar\n spans many lines.\n\nquoted: \"So does this\n quoted scalar.\\n\""; + +// TODO: 2.19 - 2.22 schema tags + +const EX2_23 : &'static str = + "---\nnot-date: !!str 2002-04-28\n\npicture: !!binary |\n R0lGODlhDAAMAIQAAP//9/X\n 17unp5WZmZgAAAOfn515eXv\n Pz7Y6OjuDg4J+fn5OTk6enp\n 56enmleECcgggoBADs=\n\napplication specific tag: !something |\n The semantics of the tag\n above may be different for\n different documents."; + +const EX2_24 : &'static str = + "%TAG ! tag:clarkevans.com,2002:\n--- !shape\n # Use the ! handle for presenting\n # tag:clarkevans.com,2002:circle\n- !circle\n center: &ORIGIN {x: 73, y: 129}\n radius: 7\n- !line\n start: *ORIGIN\n finish: { x: 89, y: 102 }\n- !label\n start: *ORIGIN\n color: 0xFFEEBB\n text: Pretty vector drawing."; + +const EX2_25 : &'static str = + "# Sets are represented as a\n# Mapping where each key is\n# associated with a null value\n--- !!set\n? Mark McGwire\n? Sammy Sosa\n? Ken Griffey"; + +const EX2_26 : &'static str = + "# Ordered maps are represented as\n# A sequence of mappings, with\n# each mapping having one key\n--- !!omap\n- Mark McGwire: 65\n- Sammy Sosa: 63\n- Ken Griffey: 58"; + +const EX2_27 : &'static str = + "--- !<tag:clarkevans.com,2002:invoice>\ninvoice: 34843\ndate : 2001-01-23\nbill-to: &id001\n given : Chris\n family : Dumars\n address:\n lines: |\n 458 Walkman Dr.\n Suite #292\n city : Royal Oak\n state : MI\n postal : 48046\nship-to: *id001\nproduct:\n - sku : BL394D\n quantity : 4\n description : Basketball\n price : 450.00\n - sku : BL4438H\n quantity : 1\n description : Super Hoop\n price : 2392.00\ntax : 251.42\ntotal: 4443.52\ncomments:\n Late afternoon is best.\n Backup contact is Nancy\n Billsmer @ 338-4338."; + +const EX2_28 : &'static str = + "---\nTime: 2001-11-23 15:01:42 -5\nUser: ed\nWarning:\n This is an error message\n for the log file\n---\nTime: 2001-11-23 15:02:31 -5\nUser: ed\nWarning:\n A slightly different error\n message.\n---\nDate: 2001-11-23 15:03:17 -5\nUser: ed\nFatal:\n Unknown variable \"bar\"\nStack:\n - file: TopClass.py\n line: 23\n code: |\n x = MoreObject(\"345\\n\")\n - file: MoreClass.py\n line: 58\n code: |-\n foo = bar"; + +// TODO: 5.1 - 5.2 BOM + +const EX5_3 : &'static str = + "sequence:\n- one\n- two\nmapping:\n ? sky\n : blue\n sea : green"; + +const EX5_4 : &'static str = + "sequence: [ one, two, ]\nmapping: { sky: blue, sea: green }"; + +const EX5_5 : &'static str = "# Comment only."; + +const EX5_6 : &'static str = + "anchored: !local &anchor value\nalias: *anchor"; + +const EX5_7 : &'static str = + "literal: |\n some\n text\nfolded: >\n some\n text\n"; + +const EX5_8 : &'static str = + "single: 'text'\ndouble: \"text\""; + +// TODO: 5.9 directive +// TODO: 5.10 reserved indicator + +const EX5_11 : &'static str = + "|\n Line break (no glyph)\n Line break (glyphed)\n"; + +const EX5_12 : &'static str = + "# Tabs and spaces\nquoted: \"Quoted\t\"\nblock: |\n void main() {\n \tprintf(\"Hello, world!\\n\");\n }"; + +const EX5_13 : &'static str = + "\"Fun with \\\\\n\\\" \\a \\b \\e \\f \\\n\\n \\r \\t \\v \\0 \\\n\\ \\_ \\N \\L \\P \\\n\\x41 \\u0041 \\U00000041\""; + +const EX5_14 : &'static str = + "Bad escapes:\n \"\\c\n \\xq-\""; + +const EX6_1 : &'static str = + " # Leading comment line spaces are\n # neither content nor indentation.\n \nNot indented:\n By one space: |\n By four\n spaces\n Flow style: [ # Leading spaces\n By two, # in flow style\n Also by two, # are neither\n \tStill by two # content nor\n ] # indentation."; + +const EX6_2 : &'static str = + "? a\n: -\tb\n - -\tc\n - d"; + +const EX6_3 : &'static str = + "- foo:\t bar\n- - baz\n -\tbaz"; + +const EX6_4 : &'static str = + "plain: text\n lines\nquoted: \"text\n \tlines\"\nblock: |\n text\n \tlines\n"; + +const EX6_5 : &'static str = + "Folding:\n \"Empty line\n \t\n as a line feed\"\nChomping: |\n Clipped empty lines\n "; + +const EX6_6 : &'static str = + ">-\n trimmed\n \n \n\n as\n space"; + +const EX6_7 : &'static str = + ">\n foo \n \n \t bar\n\n baz\n"; + +const EX6_8 : &'static str = + "\"\n foo \n \n \t bar\n\n baz\n\""; + +const EX6_9 : &'static str = + "key: # Comment\n value"; + +const EX6_10 : &'static str = + " # Comment\n \n\n"; + +const EX6_11 : &'static str = + "key: # Comment\n # lines\n value\n\n"; + +const EX6_12 : &'static str = + "{ first: Sammy, last: Sosa }:\n# Statistics:\n hr: # Home runs\n 65\n avg: # Average\n 0.278"; + +const EX6_13 : &'static str = + "%FOO bar baz # Should be ignored\n # with a warning.\n--- \"foo\""; + +const EX6_14 : &'static str = + "%YAML 1.3 # Attempt parsing\n # with a warning\n---\n\"foo\""; + +const EX6_15 : &'static str = + "%YAML 1.2\n%YAML 1.1\nfoo"; + +const EX6_16 : &'static str = + "%TAG !yaml! tag:yaml.org,2002:\n---\n!yaml!str \"foo\""; + +const EX6_17 : &'static str = + "%TAG ! !foo\n%TAG ! !foo\nbar"; + +const EX6_18 : &'static str = + "# Private\n!foo \"bar\"\n...\n# Global\n%TAG ! tag:example.com,2000:app/\n---\n!foo \"bar\""; + +const EX6_19 : &'static str = + "%TAG !! tag:example.com,2000:app/\n---\n!!int 1 - 3 # Interval, not integer"; + +const EX6_20 : &'static str = + "%TAG !e! tag:example.com,2000:app/\n---\n!e!foo \"bar\""; + +const EX6_21 : &'static str = + "%TAG !m! !my-\n--- # Bulb here\n!m!light fluorescent\n...\n%TAG !m! !my-\n--- # Color here\n!m!light green"; + +const EX6_22 : &'static str = + "%TAG !e! tag:example.com,2000:app/\n---\n- !e!foo \"bar\""; + +const EX6_23 : &'static str = + "!!str &a1 \"foo\":\n !!str bar\n&a2 baz : *a1"; + +const EX6_24 : &'static str = + "!<tag:yaml.org,2002:str> foo :\n !<!bar> baz"; + +const EX6_25 : &'static str = + "- !<!> foo\n- !<$:?> bar\n"; + +const EX6_26 : &'static str = + "%TAG !e! tag:example.com,2000:app/\n---\n- !local foo\n- !!str bar\n- !e!tag%21 baz\n"; + +const EX6_27a : &'static str = + "%TAG !e! tag:example,2000:app/\n---\n- !e! foo"; + +const EX6_27b : &'static str = + "%TAG !e! tag:example,2000:app/\n---\n- !h!bar baz"; + +const EX6_28 : &'static str = + "# Assuming conventional resolution:\n- \"12\"\n- 12\n- ! 12"; + +const EX6_29 : &'static str = + "First occurrence: &anchor Value\nSecond occurrence: *anchor"; + +const EX7_1 : &'static str = + "First occurrence: &anchor Foo\nSecond occurrence: *anchor\nOverride anchor: &anchor Bar\nReuse anchor: *anchor"; + +const EX7_2 : &'static str = + "{\n foo : !!str,\n !!str : bar,\n}"; + +const EX7_3 : &'static str = + "{\n ? foo :,\n : bar,\n}\n"; + +const EX7_4 : &'static str = + "\"implicit block key\" : [\n \"implicit flow key\" : value,\n ]"; + +const EX7_5 : &'static str = + "\"folded \nto a space,\t\n \nto a line feed, or \t\\\n \\ \tnon-content\""; + +const EX7_6 : &'static str = + "\" 1st non-empty\n\n 2nd non-empty \n\t3rd non-empty \""; + +const EX7_7 : &'static str = " 'here''s to \"quotes\"'"; + +const EX7_8 : &'static str = + "'implicit block key' : [\n 'implicit flow key' : value,\n ]"; + +const EX7_9 : &'static str = + "' 1st non-empty\n\n 2nd non-empty \n\t3rd non-empty '"; + +const EX7_10 : &'static str = + "# Outside flow collection:\n- ::vector\n- \": - ()\"\n- Up, up, and away!\n- -123\n- http://example.com/foo#bar\n# Inside flow collection:\n- [ ::vector,\n \": - ()\",\n \"Up, up, and away!\",\n -123,\n http://example.com/foo#bar ]"; + +const EX7_11 : &'static str = + "implicit block key : [\n implicit flow key : value,\n ]"; + +const EX7_12 : &'static str = + "1st non-empty\n\n 2nd non-empty \n\t3rd non-empty"; + +const EX7_13 : &'static str = + "- [ one, two, ]\n- [three ,four]"; + +const EX7_14 : &'static str = + "[\n\"double\n quoted\", 'single\n quoted',\nplain\n text, [ nested ],\nsingle: pair,\n]"; + +const EX7_15 : &'static str = + "- { one : two , three: four , }\n- {five: six,seven : eight}"; + +const EX7_16 : &'static str = + "{\n? explicit: entry,\nimplicit: entry,\n?\n}"; + +const EX7_17 : &'static str = + "{\nunquoted : \"separate\",\nhttp://foo.com,\nomitted value:,\n: omitted key,\n}"; + +const EX7_18 : &'static str = + "{\n\"adjacent\":value,\n\"readable\":value,\n\"empty\":\n}"; + +const EX7_19 : &'static str = + "[\nfoo: bar\n]"; + +const EX7_20 : &'static str = + "[\n? foo\n bar : baz\n]"; + +const EX7_21 : &'static str = + "- [ YAML : separate ]\n- [ : empty key entry ]\n- [ {JSON: like}:adjacent ]"; + +const EX7_22 : &'static str = + "[ foo\n bar: invalid,"; // Note: we don't check (on purpose) the >1K chars for an + // implicit key + +const EX7_23 : &'static str = + "- [ a, b ]\n- { a: b }\n- \"a\"\n- 'b'\n- c"; + +const EX7_24 : &'static str = + "- !!str \"a\"\n- 'b'\n- &anchor \"c\"\n- *anchor\n- !!str"; + +const EX8_1 : &'static str = + "- | # Empty header\n literal\n- >1 # Indentation indicator\n folded\n- |+ # Chomping indicator\n keep\n\n- >1- # Both indicators\n strip\n"; + +const EX8_2 : &'static str = + "- |\n detected\n- >\n \n \n # detected\n- |1\n explicit\n- >\n \t\n detected\n"; + +const EX8_3a : &'static str = + "- |\n \n text"; + +const EX8_3b : &'static str = + "- >\n text\n text"; + +const EX8_3c : &'static str = + "- |2\n text"; + +const EX8_4 : &'static str = + "strip: |-\n text\nclip: |\n text\nkeep: |+\n text\n"; + +const EX8_5 : &'static str = + " # Strip\n # Comments:\nstrip: |-\n # text\n \n # Clip\n # comments:\n\nclip: |\n # text\n \n # Keep\n # comments:\n\nkeep: |+\n # text\n\n # Trail\n # Comments\n"; + +const EX8_6 : &'static str = + "strip: >-\n\nclip: >\n\nkeep: |+\n\n"; + +const EX8_7 : &'static str = + "|\n literal\n \ttext\n\n"; + +const EX8_8 : &'static str = + "|\n \n \n literal\n \n \n text\n\n # Comment\n"; + +const EX8_9 : &'static str = + ">\n folded\n text\n\n"; + +const EX8_10 : &'static str = + ">\n\n folded\n line\n\n next\n line\n * bullet\n\n * list\n * lines\n\n last\n line\n\n# Comment\n"; + +const EX8_11 : &'static str = EX8_10; +const EX8_12 : &'static str = EX8_10; +const EX8_13 : &'static str = EX8_10; + +const EX8_14 : &'static str = + "block sequence:\n - one\n - two : three\n"; + +const EX8_15 : &'static str = + "- # Empty\n- |\n block node\n- - one # Compact\n - two # sequence\n- one: two # Compact mapping\n"; + +const EX8_16 : &'static str = + "block mapping:\n key: value\n"; + +const EX8_17 : &'static str = + "? explicit key # Empty value\n? |\n block key\n: - one # Explicit compact\n - two # block value\n"; + +// XXX libyaml failed this test +const EX8_18 : &'static str = + "plain key: in-line value\n: # Both empty\n\"quoted key\":\n- entry\n"; + +const EX8_19 : &'static str = + "- sun: yellow\n- ? earth: blue\n : moon: white\n"; + +const EX8_20 : &'static str = + "-\n \"flow in block\"\n- >\n Block scalar\n- !!map # Block collection\n foo : bar\n"; + +const EX8_21 : &'static str = + "literal: |2\n value\nfolded:\n !foo\n >1\n value\n"; + +const EX8_22 : &'static str = + "sequence: !!seq\n- entry\n- !!seq\n - nested\nmapping: !!map\n foo: bar\n"; diff --git a/third_party/rust/yaml-rust/tests/specs/cpp2rust.rb b/third_party/rust/yaml-rust/tests/specs/cpp2rust.rb new file mode 100755 index 0000000000..25813c87a6 --- /dev/null +++ b/third_party/rust/yaml-rust/tests/specs/cpp2rust.rb @@ -0,0 +1,78 @@ +#!/usr/bin/env ruby + +TEST_REGEX = /TEST_F\([a-zA-Z0-9_]+,\s+([a-zA-Z0-9_]+)\)/ + +DISABLED_TESTS = %w( + test_ex7_10_plain_characters + test_ex7_17_flow_mapping_separate_values + test_ex7_21_single_pair_implicit_entries + test_ex7_2_empty_nodes + test_ex8_2_block_indentation_header +) + +class Context + attr_accessor :name, :ev, :src + def initialize + @name = "" + @src = "" + @ev = [] + end +end + +class String + def snakecase + self + .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') + .gsub(/([a-z\d])([A-Z])/, '\1_\2') + .tr('-', '_') + .gsub(/\s/, '_') + .gsub(/__+/, '_') + .downcase + end +end + +ctx = nil + +tests = [] +IO.foreach(ARGV[0]) do |line| + line.strip! + if ctx + fail "unexpected TEST_F" if line =~ TEST_REGEX + if line =~ /^}/ + tests << ctx + ctx = nil + end + if line =~ /^EXPECT_CALL/ + fail 'not end with ;' unless line[-1] == ';' + v = line.gsub('(', ' ').gsub(')', ' ').split + ctx.ev << v[2] + end + else + next unless line =~ TEST_REGEX + name = $1 + next unless name =~ /^(Ex\d+_\d+)/ + str = $1.upcase + $stderr.puts "found #{name}" + ctx = Context.new + ctx.name = "test_#{name.snakecase}" + ctx.src = str + end +end + +# code gen +tests.each do |t| + next if t.ev.size == 0 + if DISABLED_TESTS.include? t.name + puts "#[allow(dead_code)]" + else + puts "#[test]" + end + puts "fn #{t.name}() {" + puts " let mut v = str_to_test_events(#{t.src}).into_iter();" + t.ev.each do |e| + puts " assert_next!(v, TestEvent::#{e});" + end + puts "}" + puts +end + diff --git a/third_party/rust/yaml-rust/tests/specs/handler_spec_test.cpp b/third_party/rust/yaml-rust/tests/specs/handler_spec_test.cpp new file mode 100644 index 0000000000..aa4f7ca238 --- /dev/null +++ b/third_party/rust/yaml-rust/tests/specs/handler_spec_test.cpp @@ -0,0 +1,1532 @@ +#include "handler_test.h" +#include "specexamples.h" // IWYU pragma: keep +#include "yaml-cpp/yaml.h" // IWYU pragma: keep + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using ::testing::_; + +#define EXPECT_THROW_PARSER_EXCEPTION(statement, message) \ + ASSERT_THROW(statement, ParserException); \ + try { \ + statement; \ + } catch (const ParserException& e) { \ + EXPECT_EQ(e.msg, message); \ + } + +namespace YAML { +namespace { + +typedef HandlerTest HandlerSpecTest; + +TEST_F(HandlerSpecTest, Ex2_1_SeqScalars) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Mark McGwire")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Sammy Sosa")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Ken Griffey")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_1); +} + +TEST_F(HandlerSpecTest, Ex2_2_MappingScalarsToScalars) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "hr")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "65")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "avg")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "0.278")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "rbi")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "147")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_2); +} + +TEST_F(HandlerSpecTest, Ex2_3_MappingScalarsToSequences) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "american")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Boston Red Sox")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Detroit Tigers")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "New York Yankees")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "national")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "New York Mets")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Chicago Cubs")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Atlanta Braves")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_3); +} + +TEST_F(HandlerSpecTest, Ex2_4_SequenceOfMappings) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "name")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Mark McGwire")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "hr")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "65")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "avg")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "0.278")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "name")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Sammy Sosa")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "hr")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "63")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "avg")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "0.288")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_4); +} + +TEST_F(HandlerSpecTest, Ex2_5_SequenceOfSequences) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "name")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "hr")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "avg")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Mark McGwire")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "65")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "0.278")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Sammy Sosa")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "63")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "0.288")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_5); +} + +TEST_F(HandlerSpecTest, Ex2_6_MappingOfMappings) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Mark McGwire")); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "hr")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "65")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "avg")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "0.278")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Sammy Sosa")); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "hr")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "63")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "avg")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "0.288")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_6); +} + +TEST_F(HandlerSpecTest, Ex2_7_TwoDocumentsInAStream) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Mark McGwire")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Sammy Sosa")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Ken Griffey")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Chicago Cubs")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "St Louis Cardinals")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_7); +} + +TEST_F(HandlerSpecTest, Ex2_8_PlayByPlayFeed) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "time")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "20:03:20")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "player")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Sammy Sosa")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "action")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "strike (miss)")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "time")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "20:03:47")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "player")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Sammy Sosa")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "action")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "grand slam")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_8); +} + +TEST_F(HandlerSpecTest, Ex2_9_SingleDocumentWithTwoComments) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "hr")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Mark McGwire")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Sammy Sosa")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "rbi")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Sammy Sosa")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Ken Griffey")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_9); +} + +TEST_F(HandlerSpecTest, Ex2_10_SimpleAnchor) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "hr")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Mark McGwire")); + EXPECT_CALL(handler, OnScalar(_, "?", 1, "Sammy Sosa")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "rbi")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnAlias(_, 1)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Ken Griffey")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_10); +} + +TEST_F(HandlerSpecTest, Ex2_11_MappingBetweenSequences) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Detroit Tigers")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Chicago cubs")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "2001-07-23")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "New York Yankees")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Atlanta Braves")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "2001-07-02")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "2001-08-12")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "2001-08-14")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_11); +} + +TEST_F(HandlerSpecTest, Ex2_12_CompactNestedMapping) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "item")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Super Hoop")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "quantity")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "1")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "item")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Basketball")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "quantity")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "4")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "item")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Big Shoes")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "quantity")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "1")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_12); +} + +TEST_F(HandlerSpecTest, Ex2_13_InLiteralsNewlinesArePreserved) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "\\//||\\/||\n// || ||__")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_13); +} + +TEST_F(HandlerSpecTest, Ex2_14_InFoldedScalarsNewlinesBecomeSpaces) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "Mark McGwire's year was crippled by a knee injury.")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_14); +} + +TEST_F(HandlerSpecTest, Ex2_15_FoldedNewlinesArePreservedForMoreIndentedAndBlankLines) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "Sammy Sosa completed another fine season with great stats.\n\n 63 Home Runs\n 0.288 Batting Average\n\nWhat a year!")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_15); +} + +TEST_F(HandlerSpecTest, Ex2_16_IndentationDeterminesScope) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "name")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Mark McGwire")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "accomplishment")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "Mark set a major league home run record in 1998.\n")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "stats")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "65 Home Runs\n0.278 Batting Average\n")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_16); +} + +TEST_F(HandlerSpecTest, Ex2_17_QuotedScalars) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "unicode")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "Sosa did fine.\xE2\x98\xBA")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "control")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "\b1998\t1999\t2000\n")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "hex esc")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "\x0d\x0a is \r\n")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "single")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "\"Howdy!\" he cried.")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "quoted")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, " # Not a 'comment'.")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "tie-fighter")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "|\\-*-/|")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_17); +} + +TEST_F(HandlerSpecTest, Ex2_18_MultiLineFlowScalars) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "plain")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "This unquoted scalar spans many lines.")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "quoted")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "So does this quoted scalar.\n")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_18); +} + +// TODO: 2.19 - 2.22 schema tags + +TEST_F(HandlerSpecTest, Ex2_23_VariousExplicitTags) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "not-date")); + EXPECT_CALL(handler, OnScalar(_, "tag:yaml.org,2002:str", 0, "2002-04-28")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "picture")); + EXPECT_CALL(handler, OnScalar(_, "tag:yaml.org,2002:binary", 0, "R0lGODlhDAAMAIQAAP//9/X\n17unp5WZmZgAAAOfn515eXv\nPz7Y6OjuDg4J+fn5OTk6enp\n56enmleECcgggoBADs=\n")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "application specific tag")); + EXPECT_CALL(handler, OnScalar(_, "!something", 0, "The semantics of the tag\nabove may be different for\ndifferent documents.")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_23); +} + +TEST_F(HandlerSpecTest, Ex2_24_GlobalTags) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "tag:clarkevans.com,2002:shape", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnMapStart(_, "tag:clarkevans.com,2002:circle", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "center")); + EXPECT_CALL(handler, OnMapStart(_, "?", 1, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "x")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "73")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "y")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "129")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "radius")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "7")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapStart(_, "tag:clarkevans.com,2002:line", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "start")); + EXPECT_CALL(handler, OnAlias(_, 1)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "finish")); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "x")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "89")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "y")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "102")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapStart(_, "tag:clarkevans.com,2002:label", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "start")); + EXPECT_CALL(handler, OnAlias(_, 1)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "color")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "0xFFEEBB")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "text")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Pretty vector drawing.")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_24); +} + +TEST_F(HandlerSpecTest, Ex2_25_UnorderedSets) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "tag:yaml.org,2002:set", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Mark McGwire")); + EXPECT_CALL(handler, OnNull(_, 0)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Sammy Sosa")); + EXPECT_CALL(handler, OnNull(_, 0)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Ken Griffey")); + EXPECT_CALL(handler, OnNull(_, 0)); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_25); +} + +TEST_F(HandlerSpecTest, Ex2_26_OrderedMappings) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "tag:yaml.org,2002:omap", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Mark McGwire")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "65")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Sammy Sosa")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "63")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Ken Griffey")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "58")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_26); +} + +TEST_F(HandlerSpecTest, Ex2_27_Invoice) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "tag:clarkevans.com,2002:invoice", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "invoice")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "34843")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "date")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "2001-01-23")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "bill-to")); + EXPECT_CALL(handler, OnMapStart(_, "?", 1, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "given")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Chris")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "family")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Dumars")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "address")); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "lines")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "458 Walkman Dr.\nSuite #292\n")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "city")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Royal Oak")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "state")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "MI")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "postal")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "48046")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "ship-to")); + EXPECT_CALL(handler, OnAlias(_, 1)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "product")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "sku")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "BL394D")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "quantity")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "4")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "description")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Basketball")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "price")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "450.00")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "sku")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "BL4438H")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "quantity")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "1")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "description")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Super Hoop")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "price")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "2392.00")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "tax")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "251.42")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "total")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "4443.52")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "comments")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_27); +} + +TEST_F(HandlerSpecTest, Ex2_28_LogFile) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Time")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "2001-11-23 15:01:42 -5")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "User")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "ed")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Warning")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "This is an error message for the log file")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Time")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "2001-11-23 15:02:31 -5")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "User")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "ed")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Warning")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "A slightly different error message.")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Date")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "2001-11-23 15:03:17 -5")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "User")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "ed")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Fatal")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Unknown variable \"bar\"")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Stack")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "file")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "TopClass.py")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "line")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "23")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "code")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "x = MoreObject(\"345\\n\")\n")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "file")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "MoreClass.py")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "line")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "58")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "code")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "foo = bar")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex2_28); +} + +// TODO: 5.1 - 5.2 BOM + +TEST_F(HandlerSpecTest, Ex5_3_BlockStructureIndicators) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "sequence")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "one")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "two")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "mapping")); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "sky")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "blue")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "sea")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "green")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex5_3); +} + +TEST_F(HandlerSpecTest, Ex5_4_FlowStructureIndicators) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "sequence")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "one")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "two")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "mapping")); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "sky")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "blue")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "sea")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "green")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex5_4); +} + + +TEST_F(HandlerSpecTest, Ex5_6_NodePropertyIndicators) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "anchored")); + EXPECT_CALL(handler, OnScalar(_, "!local", 1, "value")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "alias")); + EXPECT_CALL(handler, OnAlias(_, 1)); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex5_6); +} + +TEST_F(HandlerSpecTest, Ex5_7_BlockScalarIndicators) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "literal")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "some\ntext\n")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "folded")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "some text\n")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex5_7); +} + +TEST_F(HandlerSpecTest, Ex5_8_QuotedScalarIndicators) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "single")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "text")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "double")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "text")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex5_8); +} + +// TODO: 5.9 directive +// TODO: 5.10 reserved indicator + +TEST_F(HandlerSpecTest, Ex5_11_LineBreakCharacters) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "Line break (no glyph)\nLine break (glyphed)\n")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex5_11); +} + +TEST_F(HandlerSpecTest, Ex5_12_TabsAndSpaces) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "quoted")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "Quoted\t")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "block")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "void main() {\n\tprintf(\"Hello, world!\\n\");\n}")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex5_12); +} + +TEST_F(HandlerSpecTest, Ex5_13_EscapedCharacters) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "Fun with \x5C \x22 \x07 \x08 \x1B \x0C \x0A \x0D \x09 \x0B \x00 \x20 \xA0 \x85 \xe2\x80\xa8 \xe2\x80\xa9 A A A")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex5_13); +} + +TEST_F(HandlerSpecTest, Ex5_14_InvalidEscapedCharacters) { + EXPECT_THROW_PARSER_EXCEPTION(IgnoreParse(ex5_14), std::string(ErrorMsg::INVALID_ESCAPE) + "c"); +} + +TEST_F(HandlerSpecTest, Ex6_1_IndentationSpaces) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Not indented")); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "By one space")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "By four\n spaces\n")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Flow style")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "By two")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Also by two")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Still by two")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_1); +} + +TEST_F(HandlerSpecTest, Ex6_2_IndentationIndicators) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "a")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "b")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "c")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "d")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_2); +} + +TEST_F(HandlerSpecTest, Ex6_3_SeparationSpaces) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "foo")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "bar")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "baz")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "baz")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_3); +} + +TEST_F(HandlerSpecTest, Ex6_4_LinePrefixes) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "plain")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "text lines")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "quoted")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "text lines")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "block")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "text\n \tlines\n")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_4); +} + +TEST_F(HandlerSpecTest, Ex6_5_EmptyLines) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Folding")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "Empty line\nas a line feed")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Chomping")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "Clipped empty lines\n")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_5); +} + +TEST_F(HandlerSpecTest, Ex6_6_LineFolding) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "trimmed\n\n\nas space")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_6); +} + +TEST_F(HandlerSpecTest, Ex6_7_BlockFolding) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "foo \n\n\t bar\n\nbaz\n")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_7); +} + +TEST_F(HandlerSpecTest, Ex6_8_FlowFolding) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, " foo\nbar\nbaz ")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_8); +} + +TEST_F(HandlerSpecTest, Ex6_9_SeparatedComment) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "key")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "value")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_9); +} + + +TEST_F(HandlerSpecTest, _MultiLineComments) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "key")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "value")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_11); +} + +TEST_F(HandlerSpecTest, Ex6_12_SeparationSpacesII) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "first")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Sammy")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "last")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Sosa")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "hr")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "65")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "avg")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "0.278")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_12); +} + +TEST_F(HandlerSpecTest, Ex6_13_ReservedDirectives) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "foo")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_13); +} + +TEST_F(HandlerSpecTest, Ex6_14_YAMLDirective) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "foo")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_14); +} + +TEST_F(HandlerSpecTest, Ex6_15_InvalidRepeatedYAMLDirective) { + EXPECT_THROW_PARSER_EXCEPTION(IgnoreParse(ex6_15), ErrorMsg::REPEATED_YAML_DIRECTIVE); +} + +TEST_F(HandlerSpecTest, Ex6_16_TagDirective) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "tag:yaml.org,2002:str", 0, "foo")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_16); +} + +TEST_F(HandlerSpecTest, Ex6_17_InvalidRepeatedTagDirective) { + EXPECT_THROW_PARSER_EXCEPTION(IgnoreParse(ex6_17), ErrorMsg::REPEATED_TAG_DIRECTIVE); +} + +TEST_F(HandlerSpecTest, Ex6_18_PrimaryTagHandle) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!foo", 0, "bar")); + EXPECT_CALL(handler, OnDocumentEnd()); + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "tag:example.com,2000:app/foo", 0, "bar")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_18); +} + +TEST_F(HandlerSpecTest, Ex6_19_SecondaryTagHandle) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "tag:example.com,2000:app/int", 0, "1 - 3")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_19); +} + +TEST_F(HandlerSpecTest, Ex6_20_TagHandles) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "tag:example.com,2000:app/foo", 0, "bar")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_20); +} + +TEST_F(HandlerSpecTest, Ex6_21_LocalTagPrefix) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!my-light", 0, "fluorescent")); + EXPECT_CALL(handler, OnDocumentEnd()); + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!my-light", 0, "green")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_21); +} + +TEST_F(HandlerSpecTest, Ex6_22_GlobalTagPrefix) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "tag:example.com,2000:app/foo", 0, "bar")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_22); +} + +TEST_F(HandlerSpecTest, Ex6_23_NodeProperties) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "tag:yaml.org,2002:str", 1, "foo")); + EXPECT_CALL(handler, OnScalar(_, "tag:yaml.org,2002:str", 0, "bar")); + EXPECT_CALL(handler, OnScalar(_, "?", 2, "baz")); + EXPECT_CALL(handler, OnAlias(_, 1)); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_23); +} + +TEST_F(HandlerSpecTest, Ex6_24_VerbatimTags) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "tag:yaml.org,2002:str", 0, "foo")); + EXPECT_CALL(handler, OnScalar(_, "!bar", 0, "baz")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_24); +} + +// TODO: Implement +TEST_F(HandlerSpecTest, DISABLED_Ex6_25_InvalidVerbatimTags) { + Parse(ex6_25); + FAIL() << "not implemented yet"; +} + +TEST_F(HandlerSpecTest, Ex6_26_TagShorthands) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "!local", 0, "foo")); + EXPECT_CALL(handler, OnScalar(_, "tag:yaml.org,2002:str", 0, "bar")); + EXPECT_CALL(handler, OnScalar(_, "tag:example.com,2000:app/tag%21", 0, "baz")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_26); +} + +TEST_F(HandlerSpecTest, Ex6_27a_InvalidTagShorthands) { + EXPECT_THROW_PARSER_EXCEPTION(IgnoreParse(ex6_27a), ErrorMsg::TAG_WITH_NO_SUFFIX); +} + +// TODO: should we reject this one (since !h! is not declared)? +TEST_F(HandlerSpecTest, DISABLED_Ex6_27b_InvalidTagShorthands) { + Parse(ex6_27b); + FAIL() << "not implemented yet"; +} + +TEST_F(HandlerSpecTest, Ex6_28_NonSpecificTags) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "12")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "12")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "12")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_28); +} + +TEST_F(HandlerSpecTest, Ex6_29_NodeAnchors) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "First occurrence")); + EXPECT_CALL(handler, OnScalar(_, "?", 1, "Value")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Second occurrence")); + EXPECT_CALL(handler, OnAlias(_, 1)); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex6_29); +} + +TEST_F(HandlerSpecTest, Ex7_1_AliasNodes) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "First occurrence")); + EXPECT_CALL(handler, OnScalar(_, "?", 1, "Foo")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Second occurrence")); + EXPECT_CALL(handler, OnAlias(_, 1)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Override anchor")); + EXPECT_CALL(handler, OnScalar(_, "?", 2, "Bar")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Reuse anchor")); + EXPECT_CALL(handler, OnAlias(_, 2)); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_1); +} + +TEST_F(HandlerSpecTest, Ex7_2_EmptyNodes) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "foo")); + EXPECT_CALL(handler, OnScalar(_, "tag:yaml.org,2002:str", 0, "")); + EXPECT_CALL(handler, OnScalar(_, "tag:yaml.org,2002:str", 0, "")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "bar")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_2); +} + +TEST_F(HandlerSpecTest, Ex7_3_CompletelyEmptyNodes) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "foo")); + EXPECT_CALL(handler, OnNull(_, 0)); + EXPECT_CALL(handler, OnNull(_, 0)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "bar")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_3); +} + +TEST_F(HandlerSpecTest, Ex7_4_DoubleQuotedImplicitKeys) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "implicit block key")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "implicit flow key")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "value")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_4); +} + +TEST_F(HandlerSpecTest, Ex7_5_DoubleQuotedLineBreaks) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "folded to a space,\nto a line feed, or \t \tnon-content")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_5); +} + +TEST_F(HandlerSpecTest, Ex7_6_DoubleQuotedLines) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, " 1st non-empty\n2nd non-empty 3rd non-empty ")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_6); +} + +TEST_F(HandlerSpecTest, Ex7_7_SingleQuotedCharacters) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "here's to \"quotes\"")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_7); +} + +TEST_F(HandlerSpecTest, Ex7_8_SingleQuotedImplicitKeys) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "implicit block key")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "implicit flow key")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "value")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_8); +} + +TEST_F(HandlerSpecTest, Ex7_9_SingleQuotedLines) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, " 1st non-empty\n2nd non-empty 3rd non-empty ")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_9); +} + +TEST_F(HandlerSpecTest, Ex7_10_PlainCharacters) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "::vector")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, ": - ()")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "Up, up, and away!")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "-123")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "http://example.com/foo#bar")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "::vector")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, ": - ()")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "Up, up, and away!")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "-123")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "http://example.com/foo#bar")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_10); +} + +TEST_F(HandlerSpecTest, Ex7_11_PlainImplicitKeys) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "implicit block key")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "implicit flow key")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "value")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_11); +} + +TEST_F(HandlerSpecTest, Ex7_12_PlainLines) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "1st non-empty\n2nd non-empty 3rd non-empty")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_12); +} + +TEST_F(HandlerSpecTest, Ex7_13_FlowSequence) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "one")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "two")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "three")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "four")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_13); +} + +TEST_F(HandlerSpecTest, Ex7_14_FlowSequenceEntries) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "double quoted")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "single quoted")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "plain text")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "nested")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "single")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "pair")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_14); +} + +TEST_F(HandlerSpecTest, Ex7_15_FlowMappings) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "one")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "two")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "three")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "four")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "five")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "six")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "seven")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "eight")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_15); +} + +TEST_F(HandlerSpecTest, Ex7_16_FlowMappingEntries) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "explicit")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "entry")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "implicit")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "entry")); + EXPECT_CALL(handler, OnNull(_, 0)); + EXPECT_CALL(handler, OnNull(_, 0)); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_16); +} + +TEST_F(HandlerSpecTest, Ex7_17_FlowMappingSeparateValues) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "unquoted")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "separate")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "http://foo.com")); + EXPECT_CALL(handler, OnNull(_, 0)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "omitted value")); + EXPECT_CALL(handler, OnNull(_, 0)); + EXPECT_CALL(handler, OnNull(_, 0)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "omitted key")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_17); +} + +TEST_F(HandlerSpecTest, Ex7_18_FlowMappingAdjacentValues) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "adjacent")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "value")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "readable")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "value")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "empty")); + EXPECT_CALL(handler, OnNull(_, 0)); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_18); +} + +TEST_F(HandlerSpecTest, Ex7_19_SinglePairFlowMappings) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "foo")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "bar")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_19); +} + +TEST_F(HandlerSpecTest, Ex7_20_SinglePairExplicitEntry) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "foo bar")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "baz")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_20); +} + +TEST_F(HandlerSpecTest, Ex7_21_SinglePairImplicitEntries) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "YAML")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "separate")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Default)); + EXPECT_CALL(handler, OnNull(_, 0)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "empty key entry")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "JSON")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "like")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "adjacent")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_21); +} + +TEST_F(HandlerSpecTest, Ex7_22_InvalidImplicitKeys) { + EXPECT_THROW_PARSER_EXCEPTION(IgnoreParse(ex7_22), ErrorMsg::END_OF_SEQ_FLOW); +} + +TEST_F(HandlerSpecTest, Ex7_23_FlowContent) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "a")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "b")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Flow)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "a")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "b")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "a")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "b")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "c")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_23); +} + +TEST_F(HandlerSpecTest, Ex7_24_FlowNodes) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "tag:yaml.org,2002:str", 0, "a")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "b")); + EXPECT_CALL(handler, OnScalar(_, "!", 1, "c")); + EXPECT_CALL(handler, OnAlias(_, 1)); + EXPECT_CALL(handler, OnScalar(_, "tag:yaml.org,2002:str", 0, "")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex7_24); +} + +TEST_F(HandlerSpecTest, Ex8_1_BlockScalarHeader) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "literal\n")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, " folded\n")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "keep\n\n")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, " strip")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_1); +} + +TEST_F(HandlerSpecTest, Ex8_2_BlockIndentationHeader) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "detected\n")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "\n\n# detected\n")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, " explicit\n")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "\t\ndetected\n")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_2); +} + +TEST_F(HandlerSpecTest, Ex8_3a_InvalidBlockScalarIndentationIndicators) { + EXPECT_THROW_PARSER_EXCEPTION(IgnoreParse(ex8_3a), ErrorMsg::END_OF_SEQ); +} + +TEST_F(HandlerSpecTest, Ex8_3b_InvalidBlockScalarIndentationIndicators) { + EXPECT_THROW_PARSER_EXCEPTION(IgnoreParse(ex8_3b), ErrorMsg::END_OF_SEQ); +} + +TEST_F(HandlerSpecTest, Ex8_3c_InvalidBlockScalarIndentationIndicators) { + EXPECT_THROW_PARSER_EXCEPTION(IgnoreParse(ex8_3c), ErrorMsg::END_OF_SEQ); +} + +TEST_F(HandlerSpecTest, Ex8_4_ChompingFinalLineBreak) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "strip")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "text")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "clip")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "text\n")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "keep")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "text\n")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_4); +} + +TEST_F(HandlerSpecTest, DISABLED_Ex8_5_ChompingTrailingLines) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "strip")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "# text")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "clip")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "# text\n")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "keep")); + // NOTE: I believe this is a bug in the YAML spec - + // it should be "# text\n\n" + EXPECT_CALL(handler, OnScalar(_, "!", 0, "# text\n")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_5); +} + +TEST_F(HandlerSpecTest, Ex8_6_EmptyScalarChomping) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "strip")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "clip")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "keep")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "\n")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_6); +} + +TEST_F(HandlerSpecTest, Ex8_7_LiteralScalar) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "literal\n\ttext\n")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_7); +} + +TEST_F(HandlerSpecTest, Ex8_8_LiteralContent) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "\n\nliteral\n \n\ntext\n")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_8); +} + +TEST_F(HandlerSpecTest, Ex8_9_FoldedScalar) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "folded text\n")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_9); +} + +TEST_F(HandlerSpecTest, Ex8_10_FoldedLines) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "\nfolded line\nnext line\n * bullet\n\n * list\n * lines\n\nlast line\n")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_10); +} + +TEST_F(HandlerSpecTest, Ex8_11_MoreIndentedLines) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "\nfolded line\nnext line\n * bullet\n\n * list\n * lines\n\nlast line\n")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_11); +} + +TEST_F(HandlerSpecTest, Ex8_12_EmptySeparationLines) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "\nfolded line\nnext line\n * bullet\n\n * list\n * lines\n\nlast line\n")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_12); +} + +TEST_F(HandlerSpecTest, Ex8_13_FinalEmptyLines) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "\nfolded line\nnext line\n * bullet\n\n * list\n * lines\n\nlast line\n")); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_13); +} + +TEST_F(HandlerSpecTest, Ex8_14_BlockSequence) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "block sequence")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "one")); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "two")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "three")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_14); +} + +TEST_F(HandlerSpecTest, Ex8_15_BlockSequenceEntryTypes) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnNull(_, 0)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "block node\n")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "one")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "two")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "one")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "two")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_15); +} + +TEST_F(HandlerSpecTest, Ex8_16_BlockMappings) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "block mapping")); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "key")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "value")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_16); +} + +TEST_F(HandlerSpecTest, Ex8_17_ExplicitBlockMappingEntries) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "explicit key")); + EXPECT_CALL(handler, OnNull(_, 0)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "block key\n")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "one")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "two")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_17); +} + +TEST_F(HandlerSpecTest, Ex8_18_ImplicitBlockMappingEntries) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "plain key")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "in-line value")); + EXPECT_CALL(handler, OnNull(_, 0)); + EXPECT_CALL(handler, OnNull(_, 0)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "quoted key")); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "entry")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_18); +} + +TEST_F(HandlerSpecTest, Ex8_19_CompactBlockMappings) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "sun")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "yellow")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "earth")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "blue")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "moon")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "white")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_19); +} + +TEST_F(HandlerSpecTest, Ex8_20_BlockNodeTypes) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "flow in block")); + EXPECT_CALL(handler, OnScalar(_, "!", 0, "Block scalar\n")); + EXPECT_CALL(handler, OnMapStart(_, "tag:yaml.org,2002:map", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "foo")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "bar")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_20); +} + +TEST_F(HandlerSpecTest, DISABLED_Ex8_21_BlockScalarNodes) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "literal")); + // NOTE: I believe this is a bug in the YAML spec + // - it should be "value\n" + EXPECT_CALL(handler, OnScalar(_, "!", 0, "value")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "folded")); + EXPECT_CALL(handler, OnScalar(_, "!foo", 0, "value")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_21); +} + +TEST_F(HandlerSpecTest, Ex8_22_BlockCollectionNodes) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "sequence")); + EXPECT_CALL(handler, OnSequenceStart(_, "tag:yaml.org,2002:seq", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "entry")); + EXPECT_CALL(handler, OnSequenceStart(_, "tag:yaml.org,2002:seq", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "nested")); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnSequenceEnd()); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "mapping")); + EXPECT_CALL(handler, OnMapStart(_, "tag:yaml.org,2002:map", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "foo")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "bar")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse(ex8_22); +} +} +} diff --git a/third_party/rust/yaml-rust/tests/specs/libyaml_fail-01.yaml b/third_party/rust/yaml-rust/tests/specs/libyaml_fail-01.yaml new file mode 100644 index 0000000000..5e6c0dc56a --- /dev/null +++ b/third_party/rust/yaml-rust/tests/specs/libyaml_fail-01.yaml @@ -0,0 +1,6 @@ +# Ex 8.18 +plain key: in-line value +: # Both empty +"quoted key": +- entry + diff --git a/third_party/rust/yaml-rust/tests/specs/libyaml_fail-02.yaml b/third_party/rust/yaml-rust/tests/specs/libyaml_fail-02.yaml new file mode 100644 index 0000000000..60074dea2b --- /dev/null +++ b/third_party/rust/yaml-rust/tests/specs/libyaml_fail-02.yaml @@ -0,0 +1,7 @@ +# Ex 7.17 +{ +unqoted : "separate", +http://foo.com, +omitted value:, +: omitted key, +} diff --git a/third_party/rust/yaml-rust/tests/specs/libyaml_fail-03.yaml b/third_party/rust/yaml-rust/tests/specs/libyaml_fail-03.yaml new file mode 100644 index 0000000000..fc821dc73e --- /dev/null +++ b/third_party/rust/yaml-rust/tests/specs/libyaml_fail-03.yaml @@ -0,0 +1,5 @@ +# ex 7.2 +{ + foo : !!str, + !!str : bar, +} diff --git a/third_party/rust/yaml-rust/tests/test_round_trip.rs b/third_party/rust/yaml-rust/tests/test_round_trip.rs new file mode 100644 index 0000000000..dc5e85ef39 --- /dev/null +++ b/third_party/rust/yaml-rust/tests/test_round_trip.rs @@ -0,0 +1,65 @@ +extern crate yaml_rust; + +use yaml_rust::{Yaml, YamlEmitter, YamlLoader}; + +fn roundtrip(original: &Yaml) { + let mut emitted = String::new(); + YamlEmitter::new(&mut emitted).dump(original).unwrap(); + + let documents = YamlLoader::load_from_str(&emitted).unwrap(); + println!("emitted {}", emitted); + + assert_eq!(documents.len(), 1); + assert_eq!(documents[0], *original); +} + +fn double_roundtrip(original: &str) { + let parsed = YamlLoader::load_from_str(&original).unwrap(); + + let mut serialized = String::new(); + YamlEmitter::new(&mut serialized).dump(&parsed[0]).unwrap(); + + let reparsed = YamlLoader::load_from_str(&serialized).unwrap(); + + assert_eq!(parsed, reparsed); +} + +#[test] +fn test_escape_character() { + let y = Yaml::String("\x1b".to_owned()); + roundtrip(&y); +} + +#[test] +fn test_colon_in_string() { + let y = Yaml::String("x: %".to_owned()); + roundtrip(&y); +} + +#[test] +fn test_numberlike_strings() { + let docs = [ + r#"x: "1234""#, r#"x: "01234""#, r#""1234""#, + r#""01234""#, r#"" 01234""#, r#""0x1234""#, + r#"" 0x1234""#, + ]; + + for doc in &docs { + roundtrip(&Yaml::String(doc.to_string())); + double_roundtrip(&doc); + } +} + +/// Example from https://github.com/chyh1990/yaml-rust/issues/133 +#[test] +fn test_issue133() { + + let doc = YamlLoader::load_from_str("\"0x123\"").unwrap().pop().unwrap(); + assert_eq!(doc, Yaml::String("0x123".to_string())); + + let mut out_str = String::new(); + YamlEmitter::new(&mut out_str).dump(&doc).unwrap(); + let doc2 = YamlLoader::load_from_str(&out_str).unwrap().pop().unwrap(); + assert_eq!(doc, doc2); // This failed because the type has changed to a number now + +} |