aboutsummaryrefslogtreecommitdiff
path: root/thirdparty/ryml/api
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/ryml/api')
-rw-r--r--thirdparty/ryml/api/python/.gitignore141
-rw-r--r--thirdparty/ryml/api/python/Makefile94
-rw-r--r--thirdparty/ryml/api/python/bm/bm_parse.py237
-rw-r--r--thirdparty/ryml/api/python/requirements.txt5
-rw-r--r--thirdparty/ryml/api/python/ryml/__init__.py2
-rw-r--r--thirdparty/ryml/api/python/tests/test_parse.py488
-rw-r--r--thirdparty/ryml/api/ryml.i662
7 files changed, 1629 insertions, 0 deletions
diff --git a/thirdparty/ryml/api/python/.gitignore b/thirdparty/ryml/api/python/.gitignore
new file mode 100644
index 000000000..830e7e4d6
--- /dev/null
+++ b/thirdparty/ryml/api/python/.gitignore
@@ -0,0 +1,141 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+pip-wheel-metadata/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Version file generated by setuptools_scm
+version.py
+
+# SWIG produced file
+ryml/ryml.py
diff --git a/thirdparty/ryml/api/python/Makefile b/thirdparty/ryml/api/python/Makefile
new file mode 100644
index 000000000..bdb5a6ffd
--- /dev/null
+++ b/thirdparty/ryml/api/python/Makefile
@@ -0,0 +1,94 @@
+# -*- coding: utf-8 -*-
+# SPDX-License-Identifier: MIT
+
+# Use bash even on Windows
+SHELL := /bin/bash
+
+# On Windows the activate script is stored in a different location.
+ACTIVATE_SCRIPT := venv/bin/activate
+ifeq ($(OS),Windows_NT)
+ACTIVATE_SCRIPT := venv/Scripts/activate
+endif
+
+# How to invoke python
+PYTHON := python
+# How to invoke pytest
+PYTEST := $(PYTHON) -m pytest -vvv
+
+ACTIVATE=[[ -e $(ACTIVATE_SCRIPT) ]] && source $(ACTIVATE_SCRIPT);
+
+.PHONY: clean
+clean:
+ rm -rf dist *.egg-info
+ rm -rf ../../build ../../.egg*
+ rm -rf ryml/*.so ryml/ryml.py ryml/include ryml/lib
+
+.PHONY: venv-clean
+venv-clean:
+ rm -rf venv
+
+
+$(ACTIVATE_SCRIPT): requirements.txt Makefile
+ make venv
+ @touch $(ACTIVATE_SCRIPT)
+
+.PHONY: venv
+venv:
+ virtualenv --python=python3 --always-copy venv
+ # Packaging tooling.
+ ${ACTIVATE} pip install -U pip
+ # Setup requirements.
+ ${ACTIVATE} pip install -v -r requirements.txt
+ ${ACTIVATE} pip install -v -e ../..
+ @${ACTIVATE} $(PYTHON) -c "from ryml.version import version as v; print('Installed version:', v)"
+
+.PHONY: build-sdist
+build-sdist: | $(ACTIVATE_SCRIPT)
+ ${ACTIVATE} (cd ../..; $(PYTHON) -m build --sdist --outdir $(PWD)/dist)
+
+
+.PHONY: build-wheel
+build-wheel: | $(ACTIVATE_SCRIPT)
+ rm -rf dist
+ $(MAKE) build-sdist
+ @ls -l dist/*.tar.gz
+ ${ACTIVATE} pip wheel -v dist/*.tar.gz --wheel-dir $(PWD)/dist
+
+.PHONY: build
+build:
+ rm -rf build dist
+ $(MAKE) build-sdist
+ $(MAKE) build-wheel
+
+# PYPI_TEST = --repository-url https://test.pypi.org/legacy/
+PYPI_TEST = --repository testpypi
+
+.PHONY: upload-test
+upload-test: | $(ACTIVATE_SCRIPT)
+ make clean
+ make build-sdist
+ ${ACTIVATE} twine upload ${PYPI_TEST} dist/*
+
+.PHONY: upload
+upload: | $(ACTIVATE_SCRIPT)
+ make clean
+ make build-sdist
+ ${ACTIVATE} twine upload --verbose dist/*
+
+.PHONY: check
+check: | $(ACTIVATE_SCRIPT)
+ make clean
+ make build-wheel
+ ${ACTIVATE} twine check dist/*.whl
+
+.PHONY: install
+install: | $(ACTIVATE_SCRIPT)
+ ${ACTIVATE} $(PYTHON) setup.py install
+
+.PHONY: test
+test: | $(ACTIVATE_SCRIPT)
+ ${ACTIVATE} $(PYTEST) tests
+
+.PHONY: version
+version: | $(ACTIVATE_SCRIPT)
+ ${ACTIVATE} $(PYTHON) setup.py --version
diff --git a/thirdparty/ryml/api/python/bm/bm_parse.py b/thirdparty/ryml/api/python/bm/bm_parse.py
new file mode 100644
index 000000000..294be679b
--- /dev/null
+++ b/thirdparty/ryml/api/python/bm/bm_parse.py
@@ -0,0 +1,237 @@
+import ryml
+import ruamel.yaml
+import yaml
+import timeit
+import time
+import copy
+import prettytable
+import os.path
+from collections import OrderedDict as odict
+
+
+def _nodbg(*args, **kwargs):
+ pass
+
+
+def _dbg(*args, **kwargs):
+ print(*args, **kwargs, file=sys.stderr, flush=True)
+
+
+dbg = _dbg
+
+
+class RunResults:
+
+ __slots__ = ('name', 'time_ms', 'count', 'avg', 'MBps')
+
+ def __init__(self, name, time_ms, count, num_bytes):
+ self.name = name
+ self.time_ms = time_ms
+ self.count = count
+ self.avg = time_ms / count
+ num_megabytes = count * num_bytes / 1.0e6
+ num_seconds = time_ms / 1000.0
+ self.MBps = num_megabytes / num_seconds
+
+ def __str__(self):
+ fmt = "{}: count={} time={:.3f}ms avg={:.3f}ms MB/s={:.3f}"
+ fmt = fmt.format(self.name, self.count, self.time_ms, self.avg, self.MBps)
+ return fmt
+
+
+class BmCase:
+
+ def __init__(self, filename):
+ with open(filename, "r") as f:
+ src = f.read()
+ self.filename = filename
+ self.src_as_str = src
+ self.src_as_bytes = bytes(src, "utf8")
+ self.src_as_bytearray = bytearray(src, "utf8")
+ self.src_as_bytearray_orig = copy.copy(self.src_as_bytearray)
+ self.emittree = ryml.parse_in_arena(self.src_as_bytearray)
+ self.emitbuf = bytearray(4 * len(self.src_as_str)) # should be enough
+
+ def run(self, bm_method_name, cls):
+ def run_bm(obj, subject):
+ obj.count = 0
+ t = timeit.Timer(subject)
+ delta = time.time()
+ result = t.autorange() #lambda number, time_taken: time_taken > 1.0)
+ delta = 1000. * (time.time() - delta)
+ return delta, obj.count
+ obj = cls(self)
+ if not hasattr(obj, bm_method_name):
+ return None
+ name = bm_method_name + ":" + cls.__name__
+ dbg(name, "...")
+ method = getattr(obj, bm_method_name)
+ reset_name = 'reset_' + bm_method_name
+ reset_fn = getattr(obj, reset_name, None)
+ def bm_fn():
+ method(self)
+ obj.count += 1
+ if reset_fn is not None:
+ reset_fn(self)
+ delta, count = run_bm(obj, bm_fn)
+ # correct the benchmark to account for the time spent
+ # resetting
+ if reset_fn is not None:
+ # find out how much it takes to reset the bytearray
+ if not hasattr(obj, 'bm_reset_done'):
+ def bm_reset():
+ reset_fn(self)
+ obj.count += 1
+ rdelta, rcount = run_bm(obj, bm_reset)
+ obj.bm_reset_time_per_iteration = rdelta / rcount
+ dbg(name, "reset_time_per_iteration={:.3f}us".format(obj.bm_reset_time_per_iteration * 1000.0))
+ obj.bm_reset_done = True
+ reset_correction = count * obj.bm_reset_time_per_iteration
+ dbg(name, "delta={:.3f}ms".format(delta), "reset_correction={:.3f}ms({:.2f}%)".format(reset_correction, 100.0 * reset_correction / delta))
+ delta -= reset_correction
+ ret = RunResults(name, delta, count, len(self.src_as_str))
+ dbg(name, "ok:", ret)
+ return ret
+
+
+def run(case, benchmarks, approaches):
+ for bm in benchmarks:
+ results = odict()
+ for cls in approaches:
+ r = case.run(bm, cls)
+ if r is None:
+ continue
+ results[r.name] = r
+ table = prettytable.PrettyTable()
+ name = os.path.basename(case.filename)
+ table.field_names = [name, "count", "time(ms)", "avg(ms)", "avg(MB/s)"]
+ table.align[name] = "l"
+ def i(v): return "{:5d}".format(v)
+ def f(v): return "{:8.3f}".format(v)
+ for v in results.values():
+ table.add_row([v.name, i(v.count), f(v.time_ms), f(v.avg), f(v.MBps)])
+ print(table)
+
+
+class BmCaseRun:
+ def __init__(self, case):
+ self.reset_bytearray = False
+
+
+class RymlParseInArena(BmCaseRun):
+
+ def parse(self, case):
+ _ = ryml.parse_in_arena(case.src_as_bytearray)
+
+
+class RymlParseInArenaReuse(BmCaseRun):
+
+ def __init__(self, case):
+ self.tree = ryml.Tree()
+
+ def parse(self, case):
+ ryml.parse_in_arena(case.src_as_bytearray, tree=self.tree)
+
+ def reset_parse(self, case):
+ self.tree.clear()
+ self.tree.clear_arena()
+
+
+class RymlParseInPlace(BmCaseRun):
+
+ def parse(self, case):
+ _ = ryml.parse_in_place(case.src_as_bytearray)
+
+ def reset_parse(self, case):
+ case.src_as_bytearray = copy.copy(case.src_as_bytearray_orig)
+
+
+class RymlParseInPlaceReuse(BmCaseRun):
+
+ def __init__(self, case):
+ self.tree = ryml.Tree()
+
+ def parse(self, case):
+ ryml.parse_in_place(case.src_as_bytearray, tree=self.tree)
+
+ def reset_parse(self, case):
+ self.tree.clear()
+ self.tree.clear_arena()
+ case.src_as_bytearray = copy.copy(case.src_as_bytearray_orig)
+
+
+class RuamelYamlParse(BmCaseRun):
+
+ def parse(self, case):
+ _ = ruamel.yaml.load(case.src_as_str, Loader=ruamel.yaml.Loader)
+
+
+class PyYamlParse(BmCaseRun):
+
+ def parse(self, case):
+ _ = yaml.safe_load(case.src_as_str)
+
+
+class RymlEmitToNewBuffer(BmCaseRun):
+
+ def emit_yaml(self, case):
+ _ = ryml.emit_yaml(case.emittree)
+
+ def emit_json(self, case):
+ _ = ryml.emit_json(case.emittree)
+
+
+class RymlEmitReuse(BmCaseRun):
+
+ def emit_yaml(self, case):
+ _ = ryml.emit_yaml_in_place(case.emittree, case.emitbuf)
+
+ def emit_json(self, case):
+ _ = ryml.emit_json_in_place(case.emittree, case.emitbuf)
+
+
+class RuamelYamlEmit:
+
+ def __init__(self, case):
+ case.ruamel_emittree = ruamel.yaml.load(case.src_as_str, Loader=ruamel.yaml.Loader)
+
+ def emit_yaml(self, case):
+ # https://stackoverflow.com/a/47617341/5875572
+ class MyToStr:
+ def __init__(self, *args, **kwargs):
+ self.s = b""
+ def write(self, s):
+ self.s += s
+ dumper = MyToStr()
+ ruamel.yaml.YAML().dump(case.ruamel_emittree, MyToStr())
+
+
+class PyYamlEmit:
+
+ def __init__(self, case):
+ case.pyyaml_emittree = yaml.load(case.src_as_str, Loader=yaml.Loader)
+
+ def emit_yaml(self, case):
+ _ = yaml.dump(case.pyyaml_emittree)
+
+
+if __name__ == "__main__":
+ import sys
+ if len(sys.argv) < 2:
+ raise Exception("")
+ filename = sys.argv[1]
+ if filename.endswith("outer1000_inner1000.yml"): # this one is too heavy for the Python libs
+ exit(0)
+ case = BmCase(filename)
+ run(case, benchmarks=('parse', ),
+ approaches=(RuamelYamlParse,
+ PyYamlParse,
+ RymlParseInArena,
+ RymlParseInArenaReuse,
+ RymlParseInPlace,
+ RymlParseInPlaceReuse))
+ run(case, benchmarks=('emit_yaml', 'emit_json', ),
+ approaches=(RuamelYamlEmit,
+ PyYamlEmit,
+ RymlEmitToNewBuffer,
+ RymlEmitReuse))
diff --git a/thirdparty/ryml/api/python/requirements.txt b/thirdparty/ryml/api/python/requirements.txt
new file mode 100644
index 000000000..86c126420
--- /dev/null
+++ b/thirdparty/ryml/api/python/requirements.txt
@@ -0,0 +1,5 @@
+ruamel.yaml
+ninja
+pyyaml
+prettytable
+pytest
diff --git a/thirdparty/ryml/api/python/ryml/__init__.py b/thirdparty/ryml/api/python/ryml/__init__.py
new file mode 100644
index 000000000..8bbb62199
--- /dev/null
+++ b/thirdparty/ryml/api/python/ryml/__init__.py
@@ -0,0 +1,2 @@
+from ryml.ryml import *
+from .version import *
diff --git a/thirdparty/ryml/api/python/tests/test_parse.py b/thirdparty/ryml/api/python/tests/test_parse.py
new file mode 100644
index 000000000..bb7973cb3
--- /dev/null
+++ b/thirdparty/ryml/api/python/tests/test_parse.py
@@ -0,0 +1,488 @@
+import ryml
+from ryml.ryml import _same_ptr, _same_mem
+import unittest
+
+
+# -----------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
+class TestSubstrInterop(unittest.TestCase):
+
+ # ------------------------------------------------
+ # str
+
+ # CAN create c4::csubstr from string object
+ def test11_str2csubstr(self):
+ s = "asdasd"
+ m = ryml.as_csubstr(s)
+ self.assertTrue(_same_ptr(s, m))
+ self.assertTrue(_same_mem(s, m))
+ self.assertEqual(s, ryml.u(m))
+ #
+ m = ryml.as_csubstr(m)
+ self.assertTrue(_same_ptr(s, m))
+ self.assertTrue(_same_mem(s, m))
+ self.assertEqual(s, ryml.u(m))
+
+ # CANNOT create c4::substr from string object
+ def test12_str2substr(self):
+ s = ""
+ with self.assertRaises(TypeError) as context:
+ _ = ryml.as_substr(s)
+ self.assertTrue(type(context.exception), TypeError)
+
+ # ------------------------------------------------
+ # bytes
+
+ # CAN create c4::csubstr from string object
+ def test21_bytes2csubstr(self):
+ s = b"foo21"
+ m = ryml.as_csubstr(s)
+ self.assertTrue(_same_ptr(s, m))
+ self.assertTrue(_same_mem(s, m))
+ self.assertEqual(s, m)
+ #
+ m = ryml.as_csubstr(m)
+ self.assertTrue(_same_ptr(s, m))
+ self.assertTrue(_same_mem(s, m))
+ self.assertEqual(s, m)
+
+ # CANNOT create c4::csubstr from string object
+ def test22_bytes2substr(self):
+ s = b"foo22"
+ with self.assertRaises(TypeError) as context:
+ _ = ryml.as_substr(s)
+ self.assertTrue(type(context.exception), TypeError)
+
+ # ------------------------------------------------
+ # bytearray
+
+ # CAN create c4::csubstr from string object
+ def test31_bytes2csubstr(self):
+ s = bytearray("foo31", "utf8")
+ m = ryml.as_csubstr(s)
+ self.assertTrue(_same_ptr(s, m))
+ self.assertTrue(_same_mem(s, m))
+ self.assertEqual(s, m)
+ #
+ m = ryml.as_csubstr(m)
+ self.assertTrue(_same_ptr(s, m))
+ self.assertTrue(_same_mem(s, m))
+ self.assertEqual(s, m)
+
+ # CANNOT create c4::csubstr from string object
+ def test32_bytes2substr(self):
+ s = bytearray("foo31", "utf8")
+ m = ryml.as_csubstr(s)
+ self.assertTrue(_same_ptr(s, m))
+ self.assertTrue(_same_mem(s, m))
+ self.assertEqual(s, m)
+ #
+ m = ryml.as_csubstr(m)
+ self.assertTrue(_same_ptr(s, m))
+ self.assertTrue(_same_mem(s, m))
+ self.assertEqual(s, m)
+
+
+# -----------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
+
+def _addmap(t, node, k=None):
+ m = t.append_child(node)
+ if k is None:
+ t.to_map(m)
+ else:
+ t.to_map(m, k)
+ return m
+
+
+def _addseq(t, node, k=None):
+ m = t.append_child(node)
+ if k is None:
+ t.to_seq(m)
+ else:
+ t.to_seq(m, k)
+ return m
+
+
+def _addval(t, node, k, v=None):
+ ch = t.append_child(node)
+ if v is None:
+ t.to_val(ch, k)
+ else:
+ t.to_keyval(ch, k, v)
+ return ch
+
+
+# -----------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
+def check_tree_mod(ut, t):
+ # some convenient shorthands
+ eq = ut.assertEqual
+ def _addval_and_check(node, k, v=None):
+ ch = _addval(t, node, k, v)
+ pos = t.child_pos(node, ch)
+ eq(t.child(node, pos), ch)
+ if v is not None:
+ eq(t.find_child(node, k), ch)
+ eq(t.child(node, pos), t.find_child(node, k))
+ return ch
+ def _addseq_and_check(node, k):
+ ch = _addseq(t, node, k)
+ eq(t.find_child(node, k), ch)
+ return ch
+ def _addmap_and_check(node, k):
+ ch = _addmap(t, node, k)
+ eq(t.find_child(node, k), ch)
+ return ch
+ m = _addmap_and_check(t.root_id(), "check_tree_mod_map")
+ _addval_and_check(m, "k1", "v1")
+ _addval_and_check(m, "k2", "v2")
+ _addval_and_check(m, "k3", "v3")
+ eq(t.num_children(m), 3)
+ eq(t.num_siblings(t.first_child(m)), 3)
+ s = _addseq_and_check(t.root_id(), "check_tree_mod_seq")
+ _addval_and_check(s, "v1")
+ _addval_and_check(s, "v2")
+ _addval_and_check(s, "v3")
+ eq(t.num_children(s), 3)
+ eq(t.num_siblings(t.first_child(m)), 3)
+
+
+# -----------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
+class SimpleTestCase:
+
+ yaml = "{'HELLO': a, foo: \"b\", bar: c, baz: d, seq: [0, 1, 2, 3]}"
+
+ def check(self, ut, t, is_json=False):
+ # some convenient shorthands
+ eq = ut.assertEqual
+ ne = ut.assertNotEqual
+ fs = ut.assertFalse
+ tr = ut.assertTrue
+ #
+ eq(t.size(), 10)
+ tr(t.is_root(0))
+ eq(t.num_children(0), 5)
+ eq(t.find_child(0, b"HELLO"), 1)
+ eq(t.find_child(0, b"foo"), 2)
+ eq(t.find_child(0, b"bar"), 3)
+ eq(t.find_child(0, b"baz"), 4)
+ eq(t.find_child(0, b"seq"), 5)
+ eq(t.parent(0), ryml.NONE)
+ eq(t.parent(1), 0)
+ eq(t.parent(2), 0)
+ eq(t.parent(3), 0)
+ eq(t.parent(4), 0)
+ eq(t.parent(5), 0)
+ fs(t.is_root(1))
+ fs(t.is_root(2))
+ fs(t.is_root(3))
+ fs(t.is_root(4))
+ fs(t.is_root(5))
+ fs(t.has_child(0, b"foozzie"))
+ fs(t.has_child(0, b"bark"))
+ fs(t.has_child(0, b"bart"))
+ fs(t.has_child(0, b"bazk"))
+ eq(t.next_sibling(0), ryml.NONE)
+ eq(t.prev_sibling(0), ryml.NONE)
+ eq(t.prev_sibling(1), ryml.NONE)
+ eq(t.next_sibling(5), ryml.NONE)
+ tr(t.has_child(0, b"HELLO"))
+ tr(t.has_child(0, b"foo"))
+ tr(t.has_child(0, b"bar"))
+ tr(t.has_child(0, b"baz"))
+ eq(t.key(1), b"HELLO")
+ eq(t.key(2), b"foo")
+ eq(t.key(3), b"bar")
+ eq(t.key(4), b"baz")
+ eq(t.key(5), b"seq")
+ eq(t.val(1), b"a")
+ eq(t.val(2), b"b")
+ eq(t.val(3), b"c")
+ eq(t.val(4), b"d")
+ eq(t.val(6), b"0")
+ eq(t.val(7), b"1")
+ eq(t.val(8), b"2")
+ eq(t.val(9), b"3")
+ if not is_json:
+ tr(t.is_key_quoted(1))
+ fs(t.is_key_quoted(2))
+ fs(t.is_key_quoted(3))
+ fs(t.is_key_quoted(4))
+ fs(t.is_key_quoted(5))
+ else:
+ tr(t.is_key_quoted(1))
+ tr(t.is_key_quoted(2))
+ tr(t.is_key_quoted(3))
+ tr(t.is_key_quoted(4))
+ tr(t.is_key_quoted(5))
+ if not is_json:
+ fs(t.is_val_quoted(1))
+ tr(t.is_val_quoted(2))
+ fs(t.is_val_quoted(3))
+ fs(t.is_val_quoted(4))
+ fs(t.is_val_quoted(5))
+ fs(t.is_val_quoted(6))
+ fs(t.is_val_quoted(7))
+ fs(t.is_val_quoted(8))
+ fs(t.is_val_quoted(9))
+ else:
+ tr(t.is_val_quoted(1))
+ tr(t.is_val_quoted(2))
+ tr(t.is_val_quoted(3))
+ tr(t.is_val_quoted(4))
+ fs(t.is_val_quoted(5))
+ fs(t.is_val_quoted(6))
+ fs(t.is_val_quoted(7))
+ fs(t.is_val_quoted(8))
+ fs(t.is_val_quoted(9))
+ if not is_json:
+ tr(t.is_quoted(1))
+ tr(t.is_quoted(2))
+ fs(t.is_quoted(3))
+ fs(t.is_quoted(4))
+ fs(t.is_quoted(5))
+ fs(t.is_quoted(6))
+ fs(t.is_quoted(7))
+ fs(t.is_quoted(8))
+ fs(t.is_quoted(9))
+ else:
+ tr(t.is_quoted(1))
+ tr(t.is_quoted(2))
+ tr(t.is_quoted(3))
+ tr(t.is_quoted(4))
+ tr(t.is_quoted(5))
+ fs(t.is_quoted(6))
+ fs(t.is_quoted(7))
+ fs(t.is_quoted(8))
+ fs(t.is_quoted(9))
+ tr(t.has_sibling(1, b"bar"))
+ tr(t.has_sibling(1, b"baz"))
+ tr(t.has_sibling(2, b"foo"))
+ tr(t.has_sibling(2, b"baz"))
+ tr(t.has_sibling(3, b"foo"))
+ tr(t.has_sibling(3, b"bar"))
+ for i in (1, 2, 3, 4, 5):
+ eq(t.find_sibling(i, b"HELLO"), 1)
+ eq(t.find_sibling(i, b"foo"), 2)
+ eq(t.find_sibling(i, b"bar"), 3)
+ eq(t.find_sibling(i, b"baz"), 4)
+ eq(t.find_sibling(i, b"seq"), 5)
+ #
+ num = 0
+ for id in ryml.children(t):
+ num += 1
+ eq(id, num)
+ eq(num, t.num_children(t.root_id()))
+ eq(num, t.num_siblings(t.first_child(t.root_id())))
+ #
+ num = 0
+ for id in ryml.children(t, 1):
+ num += 1
+ eq(num, 0)
+ #
+ num = 0
+ for id in ryml.siblings(t, 1):
+ num += 1
+ eq(id, num)
+ eq(num, t.num_children(t.root_id()))
+ eq(num, t.num_siblings(t.first_child(t.root_id())))
+ #
+ num = 0
+ for id in ryml.siblings(t, 3):
+ num += 1
+ eq(id, num)
+ eq(num, 5)
+ eq(num, t.num_siblings(t.first_child(t.root_id())))
+ #
+ for i, ch in enumerate(ryml.children(t, 5)):
+ eq(t.val(ch), [b"0", b"1", b"2", b"3"][i])
+ sibs = [b"HELLO", b"foo", b"bar", b"baz", b"seq"]
+ sibs_s = ["HELLO", "foo", "bar", "baz", "seq"]
+ for i, sib in enumerate(ryml.siblings(t, 5)):
+ k = t.key(sib)
+ k_s = str(k, "utf8")
+ eq(k, sibs[i])
+ eq(k_s, sibs_s[i])
+ ne(k, sibs_s[i])
+ ne(k_s, sibs[i])
+ k_s = str(k)
+ ne(k_s, sibs_s[i])
+ ne(k_s, sibs[i])
+ num = 0
+ for id in ryml.siblings(t, 0):
+ num += 1
+ eq(num, 1)
+ #
+ num = 0
+ for id, level in ryml.walk(t):
+ num += 1
+ if t.is_root(id):
+ eq(id, 0)
+ eq(level, 0)
+ if t.is_map(id):
+ eq(id, 0)
+ eq(level, 0)
+ if t.is_seq(id):
+ eq(id, 5)
+ eq(level, 1)
+ if t.is_keyval(id):
+ tr(id > 0 and id < 5)
+ if t.is_val(id):
+ tr(id > 5)
+ eq(level, 2)
+ eq(num, t.size())
+ #
+ num = 0
+ for id in ryml.walk(t, 5):
+ num += 1
+ eq(num, 5)
+ #
+ num = 0
+ for id in ryml.walk(t, 9):
+ num += 1
+ eq(num, 1)
+ check_tree_mod(ut, t)
+
+
+# -----------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
+class TestRunner(unittest.TestCase):
+
+ def setUp(self):
+ self._setUp(SimpleTestCase())
+
+ # allow creating this class with different cases
+ # if they are added
+ def _setUp(self, case):
+ self.case = case
+ self.src_as_str = str(case.yaml)
+ self.src_as_bytes = bytes(case.yaml, "utf8")
+ self.src_as_bytearray = bytearray(case.yaml, "utf8")
+
+ # ----------------------------------------------------------
+ def test11_str__arena(self): # cannot read string buffers (or can we?)
+ tree = ryml.parse_in_arena(self.src_as_str)
+ self.case.check(self, tree)
+
+ def test12_str__arena__reuse_tree(self): # cannot read string buffers (or can we?)
+ t = ryml.Tree()
+ ryml.parse_in_arena(self.src_as_str, tree=t)
+ self.case.check(self, t)
+
+ def test13_str__inplace(self): # cannot mutate string buffers (or can we?)
+ with self.assertRaises(TypeError) as context:
+ ryml.parse_in_place(self.src_as_str)
+ self.assertTrue(type(context.exception), TypeError)
+
+ # ----------------------------------------------------------
+ def test21_bytes__arena(self):
+ tree = ryml.parse_in_arena(self.src_as_bytes)
+ self.case.check(self, tree)
+
+ def test22_bytes__arena__reuse_tree(self):
+ t = ryml.Tree()
+ r = ryml.parse_in_arena(self.src_as_bytes, tree=t)
+ self.assertTrue(r is t)
+ self.case.check(self, t)
+
+ def test23_bytes__inplace(self): # cannot mutate bytes buffers
+ with self.assertRaises(TypeError) as context:
+ ryml.parse_in_place(self.src_as_bytes)
+ self.assertTrue(type(context.exception), TypeError)
+
+ # ----------------------------------------------------------
+ def test31_bytearray__arena(self):
+ tree = ryml.parse_in_arena(self.src_as_bytearray)
+ self.case.check(self, tree)
+
+ def test32_bytearray__arena__reuse_tree(self):
+ t = ryml.Tree()
+ r = ryml.parse_in_arena(self.src_as_bytearray, tree=t)
+ self.assertTrue(r is t)
+ self.case.check(self, t)
+
+ def test33_bytearray__inplace(self): # bytearray buffers are mutable
+ tree = ryml.parse_in_place(self.src_as_bytearray)
+ self.case.check(self, tree)
+
+ def test34_bytearray__inplace__reuse_tree(self): # bytearray buffers are mutable
+ t = ryml.Tree()
+ r = ryml.parse_in_place(self.src_as_bytearray, tree=t)
+ self.assertTrue(r is t)
+ self.case.check(self, t)
+
+ # ----------------------------------------------------------
+ def test41_emit_yaml(self):
+ tree = ryml.parse_in_arena(self.src_as_bytearray)
+ yaml = ryml.emit_yaml(tree)
+ output_tree = ryml.parse_in_arena(yaml)
+ self.case.check(self, output_tree)
+
+ def test41_emit_json(self):
+ tree = ryml.parse_in_arena(self.src_as_bytearray)
+ json = ryml.emit_json(tree)
+ output_tree = ryml.parse_in_arena(json)
+ self.case.check(self, output_tree, is_json=True)
+
+ def test42_compute_emit_yaml_length(self):
+ tree = ryml.parse_in_arena(self.src_as_bytearray)
+ yaml = ryml.emit_yaml(tree)
+ length = ryml.compute_emit_yaml_length(tree)
+ self.assertEqual(len(yaml), length)
+
+ def test42_compute_emit_json_length(self):
+ tree = ryml.parse_in_arena(self.src_as_bytearray)
+ json = ryml.emit_json(tree)
+ length = ryml.compute_emit_json_length(tree)
+ self.assertEqual(len(json), length)
+
+ def test43_emit_yaml_inplace(self):
+ tree = ryml.parse_in_arena(self.src_as_bytearray)
+ yaml = ryml.emit_yaml(tree)
+ length = ryml.compute_emit_yaml_length(tree)
+ self.assertEqual(len(yaml), length)
+ buf = bytearray(length)
+ s = ryml.emit_yaml_in_place(tree, buf)
+ self.assertEqual(len(s), length)
+ self.assertTrue(s.tobytes().decode('utf-8') == yaml)
+ self.assertTrue(buf.decode('utf-8') == yaml)
+
+ def test43_emit_json_inplace(self):
+ tree = ryml.parse_in_arena(self.src_as_bytearray)
+ json = ryml.emit_json(tree)
+ length = ryml.compute_emit_json_length(tree)
+ self.assertEqual(len(json), length)
+ buf = bytearray(length)
+ s = ryml.emit_json_in_place(tree, buf)
+ self.assertEqual(len(s), length)
+ self.assertTrue(s.tobytes().decode('utf-8') == json)
+ self.assertTrue(buf.decode('utf-8') == json)
+
+ def test44_emit_yaml_short_buf(self):
+ tree = ryml.parse_in_arena(self.src_as_bytearray)
+ length = ryml.compute_emit_yaml_length(tree)
+ buf = bytearray(length-1)
+ with self.assertRaises(IndexError):
+ ryml.emit_yaml_in_place(tree, buf)
+
+ def test44_emit_json_short_buf(self):
+ tree = ryml.parse_in_arena(self.src_as_bytearray)
+ length = ryml.compute_emit_json_length(tree)
+ buf = bytearray(length-1)
+ with self.assertRaises(IndexError):
+ ryml.emit_json_in_place(tree, buf)
+
+
+
+# -----------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
+if __name__ == "__main__":
+ unittest.main()
diff --git a/thirdparty/ryml/api/ryml.i b/thirdparty/ryml/api/ryml.i
new file mode 100644
index 000000000..ac226142a
--- /dev/null
+++ b/thirdparty/ryml/api/ryml.i
@@ -0,0 +1,662 @@
+
+%module ryml
+
+
+//-----------------------------------------------------------------------------
+// this block will be pasted verbatim in the generated C++ source file
+
+%{
+// specifies that the resulting C file should be built as a python
+// extension, inserting the module init code
+#define SWIG_FILE_WITH_INIT
+
+#include <c4/yml/yml.hpp>
+
+namespace c4 {
+namespace yml {
+
+using substr = c4::substr;
+using csubstr = c4::csubstr;
+
+} /* namespace yml */
+} /* namespace c4 */
+
+%}
+
+//-----------------------------------------------------------------------------
+
+
+%apply (const char *STRING, size_t LENGTH) { (const char *str, size_t len) };
+%apply (char *STRING, size_t LENGTH) { (char *str, size_t len) };
+%newobject emit_malloc;
+
+%typemap(in) c4::substr {
+#if defined(SWIGPYTHON)
+ Py_buffer view;
+ int ok = PyObject_CheckBuffer($input);
+ if(ok)
+ {
+ ok = (0 == PyObject_GetBuffer($input, &view, PyBUF_SIMPLE|PyBUF_WRITABLE));
+ }
+ if(ok)
+ {
+ $1 = c4::substr((char*)view.buf, view.len);
+ PyBuffer_Release(&view);
+ }
+ else
+ {
+ PyErr_SetString(PyExc_TypeError, "could not get mutable memory for c4::csubstr - have you passed a str?");
+ SWIG_fail;
+ }
+#else
+#error no "in" typemap defined for this export language
+#endif
+};
+
+%typemap(in) c4::csubstr {
+#if defined(SWIGPYTHON)
+ Py_buffer view;
+ view.buf = nullptr;
+ int ok = PyObject_CheckBuffer($input);
+ if(ok)
+ {
+ ok = (0 == PyObject_GetBuffer($input, &view, PyBUF_CONTIG_RO));
+ }
+ if(ok)
+ {
+ $1 = c4::csubstr((const char*)view.buf, view.len);
+ PyBuffer_Release(&view);
+ }
+ else
+ {
+ // https://stackoverflow.com/questions/36098984/python-3-3-c-api-and-utf-8-strings
+ Py_ssize_t sz = 0;
+ const char *buf = PyUnicode_AsUTF8AndSize($input, &sz);
+ if(buf || sz == 0)
+ {
+ $1 = c4::csubstr(buf, sz);
+ }
+ else
+ {
+ PyErr_SetString(PyExc_TypeError, "c4::csubstr: could not get readonly memory from python object");
+ SWIG_fail;
+ }
+ }
+#else
+#error no "in" typemap defined for this export language
+#endif
+};
+// Copy the typecheck code for "char *".
+%typemap(typecheck) c4::substr = char *;
+%typemap(typecheck) c4::csubstr = const char *;
+
+
+%typemap(out) c4::csubstr {
+#if defined(SWIGPYTHON)
+ if($1.str == nullptr) {
+ $result = Py_None;
+ Py_INCREF($result);
+ } else {
+ PyObject *obj = PyMemoryView_FromMemory((char*)$1.str, $1.len, PyBUF_READ);
+ if( ! obj)
+ {
+ PyErr_SetString(PyExc_TypeError, "could not get readonly memory from c4::csubstr - have you passed a str?");
+ SWIG_fail;
+ }
+ $result = obj;
+ }
+#else
+#error no "out" typemap defined for this export language
+#endif
+};
+
+
+%inline %{
+
+void parse_csubstr(c4::csubstr s, c4::yml::Tree *t)
+{
+ c4::yml::parse_in_arena(s, t);
+}
+
+void parse_substr(c4::substr s, c4::yml::Tree *t)
+{
+ c4::yml::parse_in_place(s, t);
+}
+
+char * emit_yaml_malloc(c4::yml::Tree const& t, size_t id)
+{
+ c4::substr buf;
+ c4::substr ret = c4::yml::emit_yaml(t, id, buf, /*error_on_excess*/false);
+ if(ret.str == nullptr && ret.len > 0)
+ {
+ // Use new[] to parse with delete[] in SWIG.
+ char * alloc = new char[ret.len + 1]; // we'll return a c-string and not a csubstr
+ c4::substr alloced_buf(alloc, ret.len);
+ ret = c4::yml::emit_yaml(t, id, alloced_buf, /*error_on_excess*/true);
+ ret.str[ret.len] = 0;
+ }
+ return ret.str;
+}
+
+char * emit_json_malloc(c4::yml::Tree const& t, size_t id)
+{
+ c4::substr buf;
+ c4::substr ret = c4::yml::emit_json(t, id, buf, /*error_on_excess*/false);
+ if(ret.str == nullptr && ret.len > 0)
+ {
+ // Use new[] to parse with delete[] in SWIG.
+ char * alloc = new char[ret.len + 1]; // we'll return a c-string and not a csubstr
+ c4::substr alloced_buf(alloc, ret.len);
+ ret = c4::yml::emit_json(t, id, alloced_buf, /*error_on_excess*/true);
+ ret.str[ret.len] = 0;
+ }
+ return ret.str;
+}
+
+size_t emit_yaml_length(const c4::yml::Tree &t, size_t id)
+{
+ c4::substr buf;
+ c4::substr ret = c4::yml::emit_yaml(t, id, buf, /*error_on_excess*/false);
+ return ret.len;
+}
+
+size_t emit_json_length(const c4::yml::Tree &t, size_t id)
+{
+ c4::substr buf;
+ c4::substr ret = c4::yml::emit_json(t, id, buf, /*error_on_excess*/false);
+ return ret.len;
+}
+
+bool emit_yaml_to_substr(const c4::yml::Tree &t, size_t id, c4::substr s, size_t *OUTPUT)
+{
+ c4::substr result = c4::yml::emit_yaml(t, id, s, /*error_on_excess*/false);
+ *OUTPUT = result.len;
+ return result.str == nullptr;
+}
+
+bool emit_json_to_substr(const c4::yml::Tree &t, size_t id, c4::substr s, size_t *OUTPUT)
+{
+ c4::substr result = c4::yml::emit_json(t, id, s, /*error_on_excess*/false);
+ *OUTPUT = result.len;
+ return result.str == nullptr;
+}
+
+
+// force a roundtrip to C++, which triggers a conversion to csubstr and returns it as a memoryview
+c4::csubstr _get_as_csubstr(c4::csubstr s)
+{
+ //printf("_get_as_csubstr: %p[%zu]'%.*s'\n", s.str, s.len, (int)s.len, s.str);
+ return s;
+}
+
+c4::csubstr _get_as_substr(c4::substr s)
+{
+ //printf("_get_as_substr: %p[%zu]'%.*s'\n", s.str, s.len, (int)s.len, s.str);
+ return s;
+}
+
+
+// utilities for testing
+bool _same_ptr(c4::csubstr l, c4::csubstr r)
+{
+ return l.str == r.str;
+}
+
+bool _same_mem(c4::csubstr l, c4::csubstr r)
+{
+ return l.str == r.str && l.len == r.len;
+}
+
+
+%}
+
+
+//-----------------------------------------------------------------------------
+
+%pythoncode %{
+
+from deprecation import deprecated
+
+
+def as_csubstr(s):
+ return _get_as_csubstr(s)
+
+def as_substr(s):
+ return _get_as_substr(s)
+
+def u(memview):
+ return str(memview, "utf8")
+
+
+def children(tree, node=None):
+ assert tree is not None
+ if node is None:
+ node = tree.root_id()
+ ch = tree.first_child(node)
+ while ch != NONE:
+ yield ch
+ ch = tree.next_sibling(ch)
+
+
+def siblings(tree, node):
+ assert tree is not None
+ if node is None:
+ return
+ ch = tree.first_sibling(node)
+ while ch != NONE:
+ yield ch
+ ch = tree.next_sibling(ch)
+
+
+def walk(tree, node=None, indentation_level=0):
+ assert tree is not None
+ if node is None: node = tree.root_id()
+ yield node, indentation_level
+ ch = tree.first_child(node)
+ while ch != NONE:
+ for gc, il in walk(tree, ch, indentation_level + 1):
+ yield gc, il
+ ch = tree.next_sibling(ch)
+
+
+@deprecated(deprecated_in="0.5.0", details="Use parse_in_arena() instead")
+def parse(buf, **kwargs):
+ return parse_in_arena(tree, id)
+def parse_in_arena(buf, **kwargs):
+ return _call_parse(parse_csubstr, buf, **kwargs)
+def parse_in_place(buf, **kwargs):
+ _check_valid_for_in_situ(buf)
+ return _call_parse(parse_substr, buf, **kwargs)
+
+
+
+def _call_parse(parse_fn, buf, **kwargs):
+ tree = kwargs.get("tree", Tree())
+ parse_fn(buf, tree)
+ return tree
+
+
+def _check_valid_for_in_situ(obj):
+ if type(obj) in (str, bytes):
+ raise TypeError("cannot parse in situ: " + type(obj).__name__)
+
+
+
+@deprecated(deprecated_in="0.5.0", details="Use emit_yaml() instead")
+def emit(tree, id=None):
+ return emit_yaml(tree, id)
+def emit_yaml(tree, id=None):
+ if id is None:
+ id = tree.root_id()
+ return emit_yaml_malloc(tree, id)
+def emit_json(tree, id=None):
+ if id is None:
+ id = tree.root_id()
+ return emit_json_malloc(tree, id)
+
+
+@deprecated(deprecated_in="0.5.0", details="Use compute_emit_yaml_length() instead")
+def compute_emit_length(tree, id=None):
+ return compute_emit_yaml_length(tree, id)
+def compute_emit_yaml_length(tree, id=None):
+ if id is None:
+ id = tree.root_id()
+ return emit_yaml_length(tree, id)
+def compute_emit_json_length(tree, id=None):
+ if id is None:
+ id = tree.root_id()
+ return emit_json_length(tree, id)
+
+
+@deprecated(deprecated_in="0.5.0", details="Use emit_yaml_in_place() instead")
+def emit_in_place(tree, buf, id=None):
+ return emit_yaml_in_place(tree, buf, id)
+def emit_yaml_in_place(tree, buf, id=None):
+ return _emit_fn_in_place(tree, buf, id, emit_yaml_to_substr)
+def emit_json_in_place(tree, buf, id=None):
+ return _emit_fn_in_place(tree, buf, id, emit_json_to_substr)
+def _emit_fn_in_place(tree, buf, id, fn):
+ if id is None:
+ id = tree.root_id()
+ (failed, expected_size) = fn(tree, id, buf)
+ if failed:
+ raise IndexError("Output buffer has {} bytes, but emit requires {} bytes".format(
+ len(buf), expected_size))
+ return memoryview(buf)[:expected_size]
+
+%}
+
+//-----------------------------------------------------------------------------
+
+namespace c4 {
+namespace yml {
+
+constexpr const size_t NONE = (size_t)-1;
+
+typedef enum {
+ NOTYPE = 0, ///< no type is set
+ VAL = (1<<0), ///< a leaf node, has a (possibly empty) value
+ KEY = (1<<1), ///< is member of a map, must have non-empty key
+ MAP = (1<<2), ///< a map: a parent of keyvals
+ SEQ = (1<<3), ///< a seq: a parent of vals
+ DOC = (1<<4), ///< a document
+ STREAM = (1<<5)|SEQ, ///< a stream: a seq of docs
+ KEYREF = (1<<6), ///< a *reference: the key references an &anchor
+ VALREF = (1<<7), ///< a *reference: the val references an &anchor
+ KEYANCH = (1<<8), ///< the key has an &anchor
+ VALANCH = (1<<9), ///< the val has an &anchor
+ KEYTAG = (1<<10), ///< the key has an explicit tag/type
+ VALTAG = (1<<11), ///< the val has an explicit tag/type
+} NodeType_e;
+
+
+struct NodeType
+{
+ NodeType_e type;
+
+ NodeType();
+ NodeType(NodeType_e t);
+ ~NodeType();
+
+ const char *type_str();
+ static const char* type_str(NodeType_e t);
+
+ void set(NodeType_e t);
+ void add(NodeType_e t);
+ void rem(NodeType_e t);
+
+ bool is_stream() const;
+ bool is_doc() const;
+ bool is_container() const;
+ bool is_map() const;
+ bool is_seq() const;
+ bool has_val() const;
+ bool has_key() const;
+ bool is_val() const;
+ bool is_keyval() const;
+ bool has_key_tag() const;
+ bool has_val_tag() const;
+ bool has_key_anchor() const;
+ bool has_val_anchor() const;
+ bool has_anchor() const;
+ bool is_key_ref() const;
+ bool is_val_ref() const;
+ bool is_ref() const;
+ bool is_anchor_or_ref() const;
+ bool is_key_quoted() const;
+ bool is_val_quoted() const;
+ bool is_quoted() const;
+};
+
+
+struct Tree
+{
+ Tree();
+ ~Tree();
+
+ void reserve(size_t node_capacity);
+ void reserve_arena(size_t node_capacity);
+ void clear();
+ void clear_arena();
+
+ size_t size() const;
+ size_t capacity() const;
+ size_t slack() const;
+
+ size_t arena_size() const;
+ size_t arena_capacity() const;
+ size_t arena_slack() const;
+
+ void resolve();
+
+public:
+
+ // getters
+
+ NodeType_e type(size_t node) const;
+ const char* type_str(size_t node) const;
+
+ c4::csubstr key (size_t node) const;
+ c4::csubstr key_tag (size_t node) const;
+ c4::csubstr key_ref (size_t node) const;
+ c4::csubstr key_anchor(size_t node) const;
+ c4::yml::NodeScalar keysc(size_t node) const;
+
+ c4::csubstr val (size_t node) const;
+ c4::csubstr val_tag (size_t node) const;
+ c4::csubstr val_ref (size_t node) const;
+ c4::csubstr val_anchor(size_t node) const;
+ c4::yml::NodeScalar valsc(size_t node) const;
+
+public:
+
+ // node predicates
+
+ bool is_root(size_t node) const;
+ bool is_stream(size_t node) const;
+ bool is_doc(size_t node) const;
+ bool is_container(size_t node) const;
+ bool is_map(size_t node) const;
+ bool is_seq(size_t node) const;
+ bool has_val(size_t node) const;
+ bool has_key(size_t node) const;
+ bool is_val(size_t node) const;
+ bool is_keyval(size_t node) const;
+ bool has_key_tag(size_t node) const;
+ bool has_val_tag(size_t node) const;
+ bool has_key_anchor(size_t node) const;
+ bool has_val_anchor(size_t node) const;
+ bool is_key_ref(size_t node) const;
+ bool is_val_ref(size_t node) const;
+ bool is_ref(size_t node) const;
+ bool is_anchor_or_ref(size_t node) const;
+ bool is_key_quoted(size_t node) const;
+ bool is_val_quoted(size_t node) const;
+ bool is_quoted(size_t node) const;
+ bool is_anchor(size_t node) const;
+ bool parent_is_seq(size_t node) const;
+ bool parent_is_map(size_t node) const;
+ bool empty(size_t node) const;
+ bool has_anchor(size_t node, c4::csubstr a) const;
+
+public:
+
+ // hierarchy predicates
+
+ bool has_parent(size_t node) const;
+ bool has_child(size_t node, c4::csubstr key) const;
+ //bool has_child(size_t node, size_t ch) const;
+ bool has_children(size_t node) const;
+ bool has_sibling(size_t node, c4::csubstr key) const;
+ //bool has_sibling(size_t node, size_t sib) const;
+ bool has_other_siblings(size_t node) const;
+
+public:
+
+ // hierarchy getters
+
+ size_t root_id() const;
+
+ size_t parent(size_t node) const;
+ size_t prev_sibling(size_t node) const;
+ size_t next_sibling(size_t node) const;
+ size_t num_children(size_t node) const;
+ size_t child_pos(size_t node, size_t ch) const;
+ size_t first_child(size_t node) const;
+ size_t last_child(size_t node) const;
+ size_t child(size_t node, size_t pos) const;
+ size_t find_child(size_t node, c4::csubstr key) const;
+ size_t num_siblings(size_t node) const;
+ size_t num_other_siblings(size_t node) const;
+ size_t sibling_pos(size_t node, size_t sib) const;
+ size_t first_sibling(size_t node) const;
+ size_t last_sibling(size_t node) const;
+ size_t sibling(size_t node, size_t pos) const;
+ size_t find_sibling(size_t node, c4::csubstr key) const;
+
+public:
+
+ void to_keyval(size_t node, c4::csubstr key, c4::csubstr val, int more_flags=0);
+ void to_map(size_t node, c4::csubstr key, int more_flags=0);
+ void to_seq(size_t node, c4::csubstr key, int more_flags=0);
+ void to_val(size_t node, c4::csubstr val, int more_flags=0);
+ void to_stream(size_t node, int more_flags=0);
+ void to_map(size_t node, int more_flags=0);
+ void to_seq(size_t node, int more_flags=0);
+ void to_doc(size_t node, int more_flags=0);
+
+ void set_key_tag(size_t node, c4::csubstr tag);
+ void set_key_anchor(size_t node, c4::csubstr anchor);
+ void set_val_anchor(size_t node, c4::csubstr anchor);
+ void set_key_ref (size_t node, c4::csubstr ref );
+ void set_val_ref (size_t node, c4::csubstr ref );
+
+ void _set_key(size_t node, c4::csubstr key, int more_flags=0);
+ void _set_val(size_t node, c4::csubstr val, int more_flags=0);
+
+ void set_val_tag(size_t node, c4::csubstr tag);
+ void rem_key_anchor(size_t node);
+ void rem_val_anchor(size_t node);
+ void rem_key_ref (size_t node);
+ void rem_val_ref (size_t node);
+ void rem_anchor_ref(size_t node);
+
+public:
+
+ /** create and insert a new child of "parent". insert after the (to-be)
+ * sibling "after", which must be a child of "parent". To insert as the
+ * first child, set after to NONE */
+ size_t insert_child(size_t parent, size_t after);
+ size_t prepend_child(size_t parent);
+ size_t append_child(size_t parent);
+
+public:
+
+ //! create and insert a new sibling of n. insert after "after"
+ size_t insert_sibling(size_t node, size_t after);
+ size_t prepend_sibling(size_t node);
+ size_t append_sibling(size_t node);
+
+public:
+
+ //! remove an entire branch at once: ie remove the children and the node itself
+ void remove(size_t node);
+
+ //! remove all the node's children, but keep the node itself
+ void remove_children(size_t node);
+
+public:
+
+ void reorder();
+
+ /** change the node's position in the parent */
+ void move(size_t node, size_t after);
+
+ /** change the node's parent and position */
+ void move(size_t node, size_t new_parent, size_t after);
+ /** change the node's parent and position */
+ size_t move(Tree * src, size_t node, size_t new_parent, size_t after);
+
+ /** recursively duplicate the node */
+ size_t duplicate(size_t node, size_t new_parent, size_t after);
+ /** recursively duplicate a node from a different tree */
+ size_t duplicate(Tree const* src, size_t node, size_t new_parent, size_t after);
+
+ /** recursively duplicate the node's children (but not the node) */
+ void duplicate_children(size_t node, size_t parent, size_t after);
+ /** recursively duplicate the node's children (but not the node), where the node is from a different tree */
+ void duplicate_children(Tree const* src, size_t node, size_t parent, size_t after);
+
+ void duplicate_contents(size_t node, size_t where);
+
+ /** duplicate the node's children (but not the node) in a new parent, but
+ * omit repetitions where a duplicated node has the same key (in maps) or
+ * value (in seqs). If one of the duplicated children has the same key
+ * (in maps) or value (in seqs) as one of the parent's children, the one
+ * that is placed closest to the end will prevail. */
+ void duplicate_children_no_rep(size_t node, size_t parent, size_t after);
+
+};
+
+/*
+%extend Tree {
+
+ bool has_anchor(size_t node, const char *str, size_t len) const
+ {
+ return $self->has_anchor(node, c4::csubstr(str, len));
+ }
+
+ bool has_child(size_t node, const char *str, size_t len) const
+ {
+ return $self->has_child(node, c4::csubstr(str, len));
+ }
+
+ bool has_sibling(size_t node, const char *str, size_t len) const
+ {
+ return $self->has_sibling(node, c4::csubstr(str, len));
+ }
+
+ size_t find_child(size_t node, const char *str, size_t len) const
+ {
+ return $self->find_child(node, c4::csubstr(str, len));
+ }
+
+ size_t find_sibling(size_t node, const char *str, size_t len) const
+ {
+ return $self->find_sibling(node, c4::csubstr(str, len));
+ }
+
+ void to_keyval(size_t node, const char *keystr, size_t keylen, const char *valstr, size_t vallen, int more_flags=0)
+ {
+ return $self->to_keyval(node, c4::csubstr(keystr, keylen), c4::csubstr(valstr, vallen), more_flags);
+ }
+
+ void to_map(size_t node, const char *keystr, size_t keylen, int more_flags=0)
+ {
+ return $self->to_map(node, c4::csubstr(keystr, keylen), more_flags);
+ }
+
+ void to_seq(size_t node, const char *keystr, size_t keylen, int more_flags=0)
+ {
+ return $self->to_seq(node, c4::csubstr(keystr, keylen), more_flags);
+ }
+
+ void to_val(size_t node, const char *valstr, size_t vallen, int more_flags=0)
+ {
+ return $self->to_val(node, c4::csubstr(valstr, vallen), more_flags);
+ }
+
+ void set_key_tag(size_t node, const char *str, size_t len)
+ {
+ return $self->set_key_tag(node, c4::csubstr(str, len));
+ }
+ void set_val_tag(size_t node, const char *str, size_t len)
+ {
+ return $self->set_val_tag(node, c4::csubstr(str, len));
+ }
+
+ void set_key_anchor(size_t node, const char *str, size_t len)
+ {
+ return $self->set_key_anchor(node, c4::csubstr(str, len));
+ }
+ void set_val_anchor(size_t node, const char *str, size_t len)
+ {
+ return $self->set_val_anchor(node, c4::csubstr(str, len));
+ }
+
+ void set_key_ref(size_t node, const char *str, size_t len)
+ {
+ return $self->set_key_ref(node, c4::csubstr(str, len));
+ }
+ void set_val_ref(size_t node, const char *str, size_t len)
+ {
+ return $self->set_val_ref(node, c4::csubstr(str, len));
+ }
+
+};
+*/
+
+} // namespace yml
+} // namespace c4
+
+//-----------------------------------------------------------------------------