aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeongik Cha <jeongik@google.com>2023-09-27 08:11:48 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-09-27 08:11:48 +0000
commitd4fc5355c0dcf3c3eb49c19a81309ea98bcd4f74 (patch)
tree98bc72fe46f55eb92e2851e6276aba06050db3ac
parentc42a2ff4fdf2864949bff5d95bad38fa160a82e3 (diff)
parente051765bcb4a7417cf28cc3b28e7268f241be530 (diff)
downloadyaml-rust-d4fc5355c0dcf3c3eb49c19a81309ea98bcd4f74.tar.gz
Import yaml-rust am: e051765bcb
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/yaml-rust/+/2754405 Change-Id: Iccd9531836d6efbc1432c821c1336c336ebaa20e Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json5
-rw-r--r--.gitignore5
-rw-r--r--.travis.yml18
-rw-r--r--Android.bp17
-rw-r--r--Cargo.toml27
-rw-r--r--Cargo.toml.orig17
-rw-r--r--LICENSE223
-rw-r--r--LICENSE-APACHE201
-rw-r--r--LICENSE-MIT19
-rw-r--r--METADATA20
-rw-r--r--MODULE_LICENSE_APACHE20
-rw-r--r--MODULE_LICENSE_MIT0
-rw-r--r--OWNERS1
-rw-r--r--README.md125
-rw-r--r--appveyor.yml65
-rw-r--r--examples/dump_yaml.rs46
-rw-r--r--src/emitter.rs635
-rw-r--r--src/lib.rs121
-rw-r--r--src/parser.rs858
-rw-r--r--src/scanner.rs2182
-rw-r--r--src/yaml.rs739
-rw-r--r--tests/quickcheck.rs21
-rw-r--r--tests/spec_test.rs140
-rw-r--r--tests/spec_test.rs.inc1513
-rw-r--r--tests/specexamples.rs.inc337
-rwxr-xr-xtests/specs/cpp2rust.rb78
-rw-r--r--tests/specs/handler_spec_test.cpp1532
-rw-r--r--tests/specs/libyaml_fail-01.yaml6
-rw-r--r--tests/specs/libyaml_fail-02.yaml7
-rw-r--r--tests/specs/libyaml_fail-03.yaml5
-rw-r--r--tests/test_round_trip.rs65
31 files changed, 9028 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..6b26bde
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,5 @@
+{
+ "git": {
+ "sha1": "6cd3ce4abe6894443645c48bdc375808ec911493"
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b7b38e4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+target
+Cargo.lock
+*.swp
+/perf.*
+/coverage.sh
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..46b9569
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,18 @@
+language: rust
+
+matrix:
+ include:
+ - rust: stable
+ - rust: beta
+ - rust: nightly
+ - rust: 1.33.0
+ - rust: nightly
+ env: CLIPPY
+ script: |
+ if rustup component add clippy-preview; then
+ cargo clippy -- -Dclippy
+ fi
+
+script:
+ - cargo build
+ - cargo test
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..f5f5754
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,17 @@
+// This file is generated by cargo2android.py --run.
+// Do not modify this file as changes will be overridden on upgrade.
+
+
+
+rust_library_host {
+ name: "libyaml_rust",
+ // has rustc warnings
+ crate_name: "yaml_rust",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.4.5",
+ srcs: ["src/lib.rs"],
+ edition: "2018",
+ rustlibs: [
+ "liblinked_hash_map",
+ ],
+}
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..42fb742
--- /dev/null
+++ b/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/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..91d7da4
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,17 @@
+[package]
+name = "yaml-rust"
+version = "0.4.5" # remember to update html_root_url
+authors = ["Yuheng Chen <yuhengchen@sensetime.com>"]
+homepage = "http://chyh1990.github.io/yaml-rust/"
+documentation = "https://docs.rs/yaml-rust"
+license = "MIT/Apache-2.0"
+description = "The missing YAML 1.2 parser for rust"
+repository = "https://github.com/chyh1990/yaml-rust"
+readme = "README.md"
+edition = "2018"
+
+[dependencies]
+linked-hash-map = "0.5.3"
+
+[dev-dependencies]
+quickcheck = "0.9"
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b9cccf8
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,223 @@
+ 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.
+
+---
+
+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/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644
index 0000000..16fe87b
--- /dev/null
+++ b/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/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000..099fbd1
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,19 @@
+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/METADATA b/METADATA
new file mode 100644
index 0000000..aa47ce9
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,20 @@
+name: "yaml-rust"
+description: "The missing YAML 1.2 parser for rust"
+third_party {
+ identifier {
+ type: "crates.io"
+ value: "https://crates.io/crates/yaml-rust"
+ }
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/yaml-rust/yaml-rust-0.4.5.crate"
+ }
+ version: "0.4.5"
+ # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same.
+ license_type: NOTICE
+ last_upgrade_date {
+ year: 2023
+ month: 8
+ day: 23
+ }
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/MODULE_LICENSE_MIT b/MODULE_LICENSE_MIT
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_MIT
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..45dc4dd
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1 @@
+include platform/prebuilts/rust:master:/OWNERS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a31faf1
--- /dev/null
+++ b/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/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..5b3ee4c
--- /dev/null
+++ b/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/examples/dump_yaml.rs b/examples/dump_yaml.rs
new file mode 100644
index 0000000..8fce0f3
--- /dev/null
+++ b/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/src/emitter.rs b/src/emitter.rs
new file mode 100644
index 0000000..f20a3ed
--- /dev/null
+++ b/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/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..6cf87c7
--- /dev/null
+++ b/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/src/parser.rs b/src/parser.rs
new file mode 100644
index 0000000..4a63146
--- /dev/null
+++ b/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/src/scanner.rs b/src/scanner.rs
new file mode 100644
index 0000000..a8659a8
--- /dev/null
+++ b/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/src/yaml.rs b/src/yaml.rs
new file mode 100644
index 0000000..4bb70da
--- /dev/null
+++ b/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/tests/quickcheck.rs b/tests/quickcheck.rs
new file mode 100644
index 0000000..0efd679
--- /dev/null
+++ b/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/tests/spec_test.rs b/tests/spec_test.rs
new file mode 100644
index 0000000..442728f
--- /dev/null
+++ b/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/tests/spec_test.rs.inc b/tests/spec_test.rs.inc
new file mode 100644
index 0000000..bb50b3b
--- /dev/null
+++ b/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/tests/specexamples.rs.inc b/tests/specexamples.rs.inc
new file mode 100644
index 0000000..a5398c3
--- /dev/null
+++ b/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/tests/specs/cpp2rust.rb b/tests/specs/cpp2rust.rb
new file mode 100755
index 0000000..25813c8
--- /dev/null
+++ b/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/tests/specs/handler_spec_test.cpp b/tests/specs/handler_spec_test.cpp
new file mode 100644
index 0000000..aa4f7ca
--- /dev/null
+++ b/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/tests/specs/libyaml_fail-01.yaml b/tests/specs/libyaml_fail-01.yaml
new file mode 100644
index 0000000..5e6c0dc
--- /dev/null
+++ b/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/tests/specs/libyaml_fail-02.yaml b/tests/specs/libyaml_fail-02.yaml
new file mode 100644
index 0000000..60074de
--- /dev/null
+++ b/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/tests/specs/libyaml_fail-03.yaml b/tests/specs/libyaml_fail-03.yaml
new file mode 100644
index 0000000..fc821dc
--- /dev/null
+++ b/tests/specs/libyaml_fail-03.yaml
@@ -0,0 +1,5 @@
+# ex 7.2
+{
+ foo : !!str,
+ !!str : bar,
+}
diff --git a/tests/test_round_trip.rs b/tests/test_round_trip.rs
new file mode 100644
index 0000000..dc5e85e
--- /dev/null
+++ b/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
+
+}