diff --git a/CMakeLists.txt b/CMakeLists.txt index ecd089ada..0fbf1a102 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ c4_add_library(ryml c4/yml/emit.hpp c4/yml/event_handler_tree.hpp c4/yml/filter_processor.hpp + c4/yml/fwd.hpp c4/yml/export.hpp c4/yml/node.hpp c4/yml/node.cpp diff --git a/src/c4/yml/fwd.hpp b/src/c4/yml/fwd.hpp new file mode 100644 index 000000000..7fa1f1769 --- /dev/null +++ b/src/c4/yml/fwd.hpp @@ -0,0 +1,24 @@ +#ifndef _C4_YML_FWD_HPP_ +#define _C4_YML_FWD_HPP_ + +/** @file fwd.hpp forward declarations */ + +namespace c4 { +namespace yml { + +struct NodeScalar; +struct NodeInit; +struct NodeData; +struct NodeType; +class NodeRef; +class ConstNodeRef; +class Tree; +struct ReferenceResolver; +template class ParseEngine; +struct EventHandlerTree; +using Parser = ParseEngine; + +} // namespace c4 +} // namespace yml + +#endif /* _C4_YML_FWD_HPP_ */ diff --git a/src/c4/yml/parse_engine.def.hpp b/src/c4/yml/parse_engine.def.hpp index 6d34ec9d3..15a651493 100644 --- a/src/c4/yml/parse_engine.def.hpp +++ b/src/c4/yml/parse_engine.def.hpp @@ -447,6 +447,17 @@ void ParseEngine::_skipchars(const char (&chars)[N]) _line_progressed(pos); } +template +void ParseEngine::_maybe_skip_comment() +{ + csubstr s = m_state->line_contents.rem.triml(' '); + if(s.begins_with('#')) + { + _c4dbgpf("comment was '{}'", s); + _line_progressed(m_state->line_contents.rem.len); + } +} + template bool ParseEngine::_maybe_scan_following_colon() noexcept { @@ -1361,6 +1372,135 @@ void ParseEngine::_save_indentation() //----------------------------------------------------------------------------- +template +void ParseEngine::_end_map_blck() +{ + _c4dbgp("mapblck: end"); + if(has_any(RKCL|RVAL)) + { + _c4dbgp("mapblck: set missing val"); + _handle_annotations_before_blck_val_scalar(); + m_evt_handler->set_val_scalar_plain({}); + } + else if(has_any(QMRK)) + { + _c4dbgp("mapblck: set missing keyval"); + _handle_annotations_before_blck_key_scalar(); + m_evt_handler->set_key_scalar_plain({}); + _handle_annotations_before_blck_val_scalar(); + m_evt_handler->set_val_scalar_plain({}); + } + m_evt_handler->end_map(); +} + +template +void ParseEngine::_end_seq_blck() +{ + if(has_any(RVAL)) + { + _c4dbgp("seqblck: set missing val"); + _handle_annotations_before_blck_val_scalar(); + m_evt_handler->set_val_scalar_plain({}); + } + m_evt_handler->end_seq(); +} + +template +void ParseEngine::_end2_map() +{ + _c4dbgp("map: end"); + _RYML_CB_ASSERT(m_evt_handler->m_stack.m_callbacks, has_any(RMAP)); + if(has_any(BLCK)) + { + _end_map_blck(); + } + else + { + _RYML_CB_ASSERT(m_evt_handler->m_stack.m_callbacks, has_none(FLOW)); + _RYML_CB_ASSERT(m_evt_handler->m_stack.m_callbacks, has_any(USTY)); + m_evt_handler->_pop(); + } +} + +template +void ParseEngine::_end2_seq() +{ + _c4dbgp("seq: end"); + _RYML_CB_ASSERT(m_evt_handler->m_stack.m_callbacks, has_any(RSEQ)); + if(has_any(BLCK)) + { + _end_seq_blck(); + } + else + { + _RYML_CB_ASSERT(m_evt_handler->m_stack.m_callbacks, has_none(FLOW)); + _RYML_CB_ASSERT(m_evt_handler->m_stack.m_callbacks, has_any(USTY)); + m_evt_handler->_pop(); + } +} + +template +void ParseEngine::_begin2_doc() +{ + m_doc_empty = true; + add_flags(RDOC); + m_evt_handler->begin_doc(); + m_evt_handler->m_curr->indref = 0; // ? +} + +template +void ParseEngine::_begin2_doc_expl() +{ + m_doc_empty = true; + add_flags(RDOC); + m_evt_handler->begin_doc_expl(); + m_evt_handler->m_curr->indref = 0; // ? +} + +template +void ParseEngine::_end2_doc() +{ + _c4dbgp("doc: end"); + _RYML_CB_ASSERT(m_evt_handler->m_stack.m_callbacks, has_any(RDOC)); + if(m_doc_empty) + { + _c4dbgp("doc was empty; add empty val"); + m_evt_handler->set_val_scalar_plain({}); + } + m_evt_handler->end_doc(); +} + +template +void ParseEngine::_end2_doc_expl() +{ + _c4dbgp("doc: end"); + if(m_doc_empty) + { + _c4dbgp("doc: no children; add empty val"); + m_evt_handler->set_val_scalar_plain({}); + } + m_evt_handler->end_doc_expl(); +} + +template +void ParseEngine::_maybe_begin_doc() +{ + if(has_none(RDOC)) + { + _c4dbgp("doc must be started"); + _begin2_doc(); + } +} +template +void ParseEngine::_maybe_end_doc() +{ + if(has_any(RDOC)) + { + _c4dbgp("doc must be finished"); + _end2_doc(); + } +} + template void ParseEngine::_end_doc_suddenly__pop() { @@ -1540,19 +1680,6 @@ void ParseEngine::_handle_indentation_pop_from_block_map() } -//----------------------------------------------------------------------------- -template -void ParseEngine::_maybe_skip_comment() -{ - csubstr s = m_state->line_contents.rem.triml(' '); - if(s.begins_with('#')) - { - _c4dbgpf("comment was '{}'", s); - _line_progressed(m_state->line_contents.rem.len); - } -} - - //----------------------------------------------------------------------------- template typename ParseEngine::ScannedScalar ParseEngine::_scan_scalar_squot() @@ -5637,7 +5764,7 @@ bool ParseEngine::_handle_seq_block() if(_finished_file()) { _c4dbgp("seqblck: finish!"); - _end2_seq_blck(); + _end_seq_blck(); goto seqblck_finish; } _c4dbgnextline(); @@ -6783,7 +6910,7 @@ bool ParseEngine::_handle_map_block() if(_finished_file()) { _c4dbgp("mapblck: file finished!"); - _end2_map_blck(); + _end_map_blck(); goto mapblck_finish; } _c4dbgnextline(); diff --git a/src/c4/yml/parse_engine.hpp b/src/c4/yml/parse_engine.hpp index c3701d9bc..ec93f77fd 100644 --- a/src/c4/yml/parse_engine.hpp +++ b/src/c4/yml/parse_engine.hpp @@ -361,142 +361,18 @@ class ParseEngine void _handle_flow_skip_whitespace(); - void _end2_map_flow() - { - _c4dbgp("mapflow: end"); - m_evt_handler->end_map(); - } - void _end2_seq_flow() - { - _c4dbgp("seqflow: end"); - if(m_evt_handler->has_val_anchor() || m_evt_handler->has_val_tag()) - { - _c4dbgp("seqflow: set missing val"); - m_evt_handler->set_val_scalar_plain({}); - } - m_evt_handler->end_seq(); - } - - void _end2_map_blck() - { - _c4dbgp("mapblck: end"); - if(has_any(RKCL|RVAL)) - { - _c4dbgp("mapblck: set missing val"); - _handle_annotations_before_blck_val_scalar(); - m_evt_handler->set_val_scalar_plain({}); - } - else if(has_any(QMRK)) - { - _c4dbgp("mapblck: set missing keyval"); - _handle_annotations_before_blck_key_scalar(); - m_evt_handler->set_key_scalar_plain({}); - _handle_annotations_before_blck_val_scalar(); - m_evt_handler->set_val_scalar_plain({}); - } - m_evt_handler->end_map(); - } - void _end2_seq_blck() - { - if(has_any(RVAL)) - { - _c4dbgp("seqblck: set missing val"); - _handle_annotations_before_blck_val_scalar(); - m_evt_handler->set_val_scalar_plain({}); - } - m_evt_handler->end_seq(); - } - - void _end2_map() - { - _c4dbgp("map: end"); - _RYML_CB_ASSERT(m_evt_handler->m_stack.m_callbacks, has_any(RMAP)); - if(has_any(FLOW)) - { - _end2_map_flow(); - } - else if(has_any(BLCK)) - { - _end2_map_blck(); - } - else - { - _RYML_CB_ASSERT(m_evt_handler->m_stack.m_callbacks, has_any(USTY)); - m_evt_handler->_pop(); - } - } - void _end2_seq() - { - _c4dbgp("seq: end"); - _RYML_CB_ASSERT(m_evt_handler->m_stack.m_callbacks, has_any(RSEQ)); - if(has_any(FLOW)) - { - _end2_seq_flow(); - } - else if(has_any(BLCK)) - { - _end2_seq_blck(); - } - else - { - _RYML_CB_ASSERT(m_evt_handler->m_stack.m_callbacks, has_any(USTY)); - m_evt_handler->_pop(); - } - } + void _end_map_blck(); + void _end_seq_blck(); + void _end2_map(); + void _end2_seq(); - void _begin2_doc() - { - m_doc_empty = true; - add_flags(RDOC); - m_evt_handler->begin_doc(); - m_evt_handler->m_curr->indref = 0; // ? - } - void _begin2_doc_expl() - { - m_doc_empty = true; - add_flags(RDOC); - m_evt_handler->begin_doc_expl(); - m_evt_handler->m_curr->indref = 0; // ? - } - void _end2_doc() - { - _c4dbgp("doc: end"); - _RYML_CB_ASSERT(m_evt_handler->m_stack.m_callbacks, has_any(RDOC)); - if(m_doc_empty) - { - _c4dbgp("doc was empty; add empty val"); - m_evt_handler->set_val_scalar_plain({}); - } - m_evt_handler->end_doc(); - } - void _end2_doc_expl() - { - _c4dbgp("doc: end"); - //_RYML_CB_ASSERT(m_evt_handler->m_stack.m_callbacks, has_any(RDOC)); - if(m_doc_empty) - { - _c4dbgp("doc: no children; add empty val"); - m_evt_handler->set_val_scalar_plain({}); - } - m_evt_handler->end_doc_expl(); - } + void _begin2_doc(); + void _begin2_doc_expl(); + void _end2_doc(); + void _end2_doc_expl(); - void _maybe_begin_doc() - { - if(has_none(RDOC)) - { - _c4dbgp("doc must be started"); - _begin2_doc(); - } - } - void _maybe_end_doc() - { - if(has_any(RDOC)) - { - _c4dbgp("doc must be finished"); - _end2_doc(); - } - } + void _maybe_begin_doc(); + void _maybe_end_doc(); void _start_doc_suddenly(); void _end_doc_suddenly(); @@ -505,6 +381,9 @@ class ParseEngine void _set_indentation(size_t indentation); void _save_indentation(); + void _handle_indentation_pop_from_block_seq(); + void _handle_indentation_pop_from_block_map(); + void _handle_indentation_pop(ParserState const* dst); void _maybe_skip_comment(); void _maybe_skip_whitespace_tokens(); @@ -564,20 +443,6 @@ class ParseEngine private: - void _handle_indentation_pop_from_block_seq(); - void _handle_indentation_pop_from_block_map(); - void _handle_indentation_pop(ParserState const* dst); - - void _prepare_pop() - { - RYML_ASSERT(m_evt_handler->m_stack.size() > 1); - auto const& curr = m_evt_handler->m_stack.top(); - auto & next = m_evt_handler->m_stack.top(1); - next.pos = curr.pos; - next.line_contents = curr.line_contents; - next.scalar = curr.scalar; - } - C4_ALWAYS_INLINE bool has_all(ParserFlag_t f) const noexcept { return (m_evt_handler->m_curr->flags & f) == f; } C4_ALWAYS_INLINE bool has_any(ParserFlag_t f) const noexcept { return (m_evt_handler->m_curr->flags & f) != 0; } C4_ALWAYS_INLINE bool has_none(ParserFlag_t f) const noexcept { return (m_evt_handler->m_curr->flags & f) == 0; } diff --git a/src/c4/yml/tree.cpp b/src/c4/yml/tree.cpp index 4752296cb..f37ea4e83 100644 --- a/src/c4/yml/tree.cpp +++ b/src/c4/yml/tree.cpp @@ -1115,6 +1115,7 @@ void Tree::resolve(ReferenceResolver *C4_RESTRICT rr) rr->resolve(this); } + //----------------------------------------------------------------------------- size_t Tree::num_children(size_t node) const diff --git a/src/c4/yml/tree.hpp b/src/c4/yml/tree.hpp index 3a8b14bba..c127df0ff 100644 --- a/src/c4/yml/tree.hpp +++ b/src/c4/yml/tree.hpp @@ -3,6 +3,9 @@ #include "c4/error.hpp" #include "c4/types.hpp" +#ifndef _C4_YML_FWD_HPP_ +#include "c4/yml/fwd.hpp" +#endif #ifndef _C4_YML_COMMON_HPP_ #include "c4/yml/common.hpp" #endif @@ -31,15 +34,6 @@ C4_SUPPRESS_WARNING_GCC("-Wtype-limits") namespace c4 { namespace yml { -struct NodeScalar; -struct NodeInit; -struct NodeData; -class NodeRef; -class ConstNodeRef; -class Tree; -struct ReferenceResolver; - - /** encode a floating point value to a string. */ template size_t to_chars_float(substr buf, T val) @@ -160,12 +154,14 @@ struct NodeInit NodeInit(NodeType_e t) : type(t), key(), val() {} /// initialize as a sequence member NodeInit(NodeScalar const& v) : type(VAL), key(), val(v) { _add_flags(); } + /// initialize as a sequence member with explicit type + NodeInit(NodeScalar const& v, NodeType_e t) : type(t|VAL), key(), val(v) { _add_flags(); } /// initialize as a mapping member - NodeInit( NodeScalar const& k, NodeScalar const& v) : type(KEYVAL), key(k.tag, k.scalar), val(v.tag, v.scalar) { _add_flags(); } + NodeInit( NodeScalar const& k, NodeScalar const& v) : type(KEYVAL), key(k), val(v) { _add_flags(); } /// initialize as a mapping member with explicit type - NodeInit(NodeType_e t, NodeScalar const& k, NodeScalar const& v) : type(t ), key(k.tag, k.scalar), val(v.tag, v.scalar) { _add_flags(); } - /// initialize as a mapping member with explicit type (eg SEQ or MAP) - NodeInit(NodeType_e t, NodeScalar const& k ) : type(t ), key(k.tag, k.scalar), val( ) { _add_flags(KEY); } + NodeInit(NodeType_e t, NodeScalar const& k, NodeScalar const& v) : type(t), key(k), val(v) { _add_flags(); } + /// initialize as a mapping member with explicit type (eg for SEQ or MAP) + NodeInit(NodeType_e t, NodeScalar const& k ) : type(t), key(k), val( ) { _add_flags(KEY); } public: diff --git a/test/test_filter.cpp b/test/test_filter.cpp index 124bcab9c..027a14bb1 100644 --- a/test/test_filter.cpp +++ b/test/test_filter.cpp @@ -2,7 +2,11 @@ #include "ryml_all.hpp" #else #include "c4/yml/filter_processor.hpp" +#include "c4/yml/event_handler_tree.hpp" #include "c4/yml/parse_engine.hpp" +#include "c4/yml/parse.hpp" +#include "c4/yml/tree.hpp" +#include "c4/yml/node.hpp" #include "c4/yml/std/string.hpp" #endif #include @@ -1456,6 +1460,88 @@ TEST(Filter, _find_last_newline_and_larger_indentation) EXPECT_EQ(_find_last_newline_and_larger_indentation("ab\n \n \n", 1), 2u); } + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +TEST(Parser, parser_options_filtering) +{ + ParserOptions opt = {}; + EXPECT_TRUE(opt.scalar_filtering()); + opt.scalar_filtering(false); + EXPECT_FALSE(opt.scalar_filtering()); + opt.scalar_filtering(true); + EXPECT_TRUE(opt.scalar_filtering()); +} + +TEST(Parser, parse_without_filtering_where_none_required) +{ + EventHandlerTree evt_handler = {}; + Parser parser(&evt_handler, ParserOptions().scalar_filtering(false)); + csubstr yaml = R"(plain key: plain val +'squo key': 'squo val' +"dquo key": "dquo val" +? | +: | +? > +: > +)"; + const Tree tree = parse_in_arena(&parser, yaml); + EXPECT_FALSE(tree[0].type() & _WIP_KEY_UNFILT); + EXPECT_FALSE(tree[0].type() & _WIP_VAL_UNFILT); + EXPECT_FALSE(tree[1].type() & _WIP_KEY_UNFILT); + EXPECT_FALSE(tree[1].type() & _WIP_VAL_UNFILT); + EXPECT_FALSE(tree[2].type() & _WIP_KEY_UNFILT); + EXPECT_FALSE(tree[2].type() & _WIP_VAL_UNFILT); + EXPECT_TRUE(tree[3].type() & _WIP_KEY_UNFILT); // block scalars always require filtering + EXPECT_TRUE(tree[3].type() & _WIP_VAL_UNFILT); // block scalars always require filtering + EXPECT_TRUE(tree[4].type() & _WIP_KEY_UNFILT); // block scalars always require filtering + EXPECT_TRUE(tree[4].type() & _WIP_VAL_UNFILT); // block scalars always require filtering +} + +TEST(Parser, parse_without_filtering_where_it_is_required) +{ + EventHandlerTree evt_handler = {}; + Parser parser(&evt_handler, ParserOptions().scalar_filtering(false)); + csubstr yaml = R"(? plain + key +: plain + val +? 'squo + key' +: 'squo + val' +? "dquo + key" +: "dquo + val" +? | + literal + key +: | + literal + value +? > + folded + key +: > + folded + value +)"; + const Tree tree = parse_in_arena(&parser, yaml); + EXPECT_TRUE(tree[0].type() & _WIP_KEY_UNFILT); + EXPECT_TRUE(tree[0].type() & _WIP_VAL_UNFILT); + EXPECT_TRUE(tree[1].type() & _WIP_KEY_UNFILT); + EXPECT_TRUE(tree[1].type() & _WIP_VAL_UNFILT); + EXPECT_TRUE(tree[2].type() & _WIP_KEY_UNFILT); + EXPECT_TRUE(tree[2].type() & _WIP_VAL_UNFILT); + EXPECT_TRUE(tree[3].type() & _WIP_KEY_UNFILT); + EXPECT_TRUE(tree[3].type() & _WIP_VAL_UNFILT); + EXPECT_TRUE(tree[4].type() & _WIP_KEY_UNFILT); + EXPECT_TRUE(tree[4].type() & _WIP_VAL_UNFILT); +} + } // namespace yml } // namespace c4 diff --git a/test/test_parser.cpp b/test/test_parser.cpp index d5c442b17..53c4a5da6 100644 --- a/test/test_parser.cpp +++ b/test/test_parser.cpp @@ -1135,6 +1135,7 @@ TEST_F(ParseOverloadJsonTest, in_arena_parser_4_2) check_tree(actual, actual.arena()); } + } // namespace yml } // namespace c4 diff --git a/test/test_tree.cpp b/test/test_tree.cpp index cd557a001..f8b71ccac 100644 --- a/test/test_tree.cpp +++ b/test/test_tree.cpp @@ -446,6 +446,143 @@ TEST(NodeInit, ctor__val_only) } } +TEST(NodeInit, ctor__val_type_only) +{ + { + const char sarr[] = "foo"; + const char *sptr = "foo"; size_t sptrlen = 3; + csubstr ssp = "foo"; + + { + SCOPED_TRACE("here 0"); + { + NodeInit s(sarr, _WIP_VAL_PLAIN); + node_scalar_test_foo(s.val); + node_scalar_test_empty(s.key); + EXPECT_TRUE(s.type & _WIP_VAL_PLAIN); + s.clear(); + } + { + NodeInit s{to_csubstr(sptr), _WIP_VAL_PLAIN}; + node_scalar_test_foo(s.val); + node_scalar_test_empty(s.key); + EXPECT_TRUE(s.type & _WIP_VAL_PLAIN); + s.clear(); + } + { + NodeInit s{sarr, _WIP_VAL_PLAIN}; + node_scalar_test_foo(s.val); + node_scalar_test_empty(s.key); + EXPECT_TRUE(s.type & _WIP_VAL_PLAIN); + s.clear(); + } + } + + { + SCOPED_TRACE("here 1"); + { + NodeInit s(sarr, _WIP_VAL_PLAIN); + node_scalar_test_foo(s.val); + node_scalar_test_empty(s.key); + EXPECT_TRUE(s.type & _WIP_VAL_PLAIN); + s.clear(); + } + { + NodeInit s(to_csubstr(sptr), _WIP_VAL_PLAIN); + node_scalar_test_foo(s.val); + node_scalar_test_empty(s.key); + EXPECT_TRUE(s.type & _WIP_VAL_PLAIN); + s.clear(); + } + { + NodeInit s(sarr, _WIP_VAL_PLAIN); + node_scalar_test_foo(s.val); + node_scalar_test_empty(s.key); + EXPECT_TRUE(s.type & _WIP_VAL_PLAIN); + s.clear(); + } + } + + { + SCOPED_TRACE("here 2"); + NodeInit s; + s = {sarr, _WIP_VAL_PLAIN}; + node_scalar_test_foo(s.val); + node_scalar_test_empty(s.key); + EXPECT_TRUE(s.type & _WIP_VAL_PLAIN); + s.clear(); + s = {to_csubstr(sptr), _WIP_VAL_PLAIN}; + node_scalar_test_foo(s.val); + node_scalar_test_empty(s.key); + EXPECT_TRUE(s.type & _WIP_VAL_PLAIN); + s.clear(); + s = {ssp, _WIP_VAL_PLAIN}; + node_scalar_test_foo(s.val); + node_scalar_test_empty(s.key); + EXPECT_TRUE(s.type & _WIP_VAL_PLAIN); + s.clear(); + } + + for(auto s : { + NodeInit(sarr, _WIP_VAL_PLAIN), + NodeInit(to_csubstr(sptr), _WIP_VAL_PLAIN), + NodeInit(csubstr{sptr, sptrlen}, _WIP_VAL_PLAIN), + NodeInit(ssp, _WIP_VAL_PLAIN)}) + { + SCOPED_TRACE("here LOOP"); + node_scalar_test_foo(s.val); + node_scalar_test_empty(s.key); + EXPECT_TRUE(s.type & _WIP_VAL_PLAIN); + } + } + + { + const char sarr[] = "foo3"; + const char *sptr = "foo3"; size_t sptrlen = 4; + csubstr ssp = "foo3"; + + { + SCOPED_TRACE("here 0"); + NodeInit s = {sarr, _WIP_VAL_PLAIN}; + node_scalar_test_foo3(s.val); + node_scalar_test_empty(s.key); + EXPECT_TRUE(s.type & _WIP_VAL_PLAIN); + } + { // FAILS + //SCOPED_TRACE("here 1"); + //NodeInit s = sarr; + //node_scalar_test_foo3(s.val); + //node_scalar_test_empty(s.key); + } + { + SCOPED_TRACE("here 2"); + NodeInit s{sarr, _WIP_VAL_PLAIN}; + node_scalar_test_foo3(s.val); + node_scalar_test_empty(s.key); + EXPECT_TRUE(s.type & _WIP_VAL_PLAIN); + } + { + SCOPED_TRACE("here 3"); + NodeInit s(sarr, _WIP_VAL_PLAIN); + node_scalar_test_foo3(s.val); + node_scalar_test_empty(s.key); + EXPECT_TRUE(s.type & _WIP_VAL_PLAIN); + } + + for(auto s : { + NodeInit(sarr, _WIP_VAL_PLAIN), + NodeInit(to_csubstr(sptr), _WIP_VAL_PLAIN), + NodeInit(csubstr{sptr, sptrlen}, _WIP_VAL_PLAIN), + NodeInit(ssp, _WIP_VAL_PLAIN)}) + { + SCOPED_TRACE("here LOOP"); + node_scalar_test_foo3(s.val); + node_scalar_test_empty(s.key); + EXPECT_TRUE(s.type & _WIP_VAL_PLAIN); + } + } +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- diff --git a/tools/amalgamate.py b/tools/amalgamate.py index b955c4961..206de7b4e 100644 --- a/tools/amalgamate.py +++ b/tools/amalgamate.py @@ -86,6 +86,7 @@ def amalgamate_ryml(filename: str, am.onlyif(with_c4core, am.injcode(c4core_def_code)), am.onlyif(with_c4core, c4core_amalgamated), "src/c4/yml/export.hpp", + "src/c4/yml/fwd.hpp", "src/c4/yml/common.hpp", "src/c4/yml/node_type.hpp", "src/c4/yml/tag.hpp",