diff --git a/CMakeLists.txt b/CMakeLists.txt index 0fe8a77c7..fea9730e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,10 +58,10 @@ if(NOT TARGET pugixml) endif() # mx_core: the generated C++ typed model plus its hand-written runtime -# (Decimal, Result, Error, Lexical, Token, NameToken, OneOrMore, Xml). The -# generated per-type sources live in src/private/mx/core/generated/ (their -# own directory, so generator-owned code is recognizable at a glance) and -# arrive via sources.cmake (emitted by `python3 -m gen gen/cpp/config.toml`). +# (Decimal, Result, Error, Lexical, ParseContext, Token, NameToken, OneOrMore, +# Xml). The generated per-type sources live in src/private/mx/core/generated/ +# (their own directory, so generator-owned code is recognizable at a glance) +# and arrive via sources.cmake (emitted by `python3 -m gen gen/cpp/config.toml`). set(MX_CORE_RUNTIME_SOURCES ${PRIVATE_DIR}/mx/core/Attribution.cpp ${PRIVATE_DIR}/mx/core/Attribution.h @@ -73,6 +73,8 @@ set(MX_CORE_RUNTIME_SOURCES ${PRIVATE_DIR}/mx/core/NameToken.cpp ${PRIVATE_DIR}/mx/core/NameToken.h ${PRIVATE_DIR}/mx/core/OneOrMore.h + ${PRIVATE_DIR}/mx/core/ParseContext.cpp + ${PRIVATE_DIR}/mx/core/ParseContext.h ${PRIVATE_DIR}/mx/core/Result.h ${PRIVATE_DIR}/mx/core/Token.cpp ${PRIVATE_DIR}/mx/core/Token.h diff --git a/README.md b/README.md index cc128e0dc..26f870772 100644 --- a/README.md +++ b/README.md @@ -489,12 +489,14 @@ attributes. `mx::core` is generated from the MusicXML 4.0 XSD by the generator i hand-written runtime lives in `src/private/mx/core/`, the generated model in `src/private/mx/core/generated/` -- the directory is the generated/hand-written boundary). + The generated model is valid-by-construction: enum wrappers whose named factories are the only constructors, clamp-on-construct number wrappers, composites with named fields in schema order (the serializer walks declaration order, so wrong element order is unrepresentable), `std::variant`-based choice classes, `OneOrMore` for required repeats, and `Result`-returning bounded appends (e.g. beam <= 8). Value semantics throughout -- no shared pointers. Errors exist -in exactly two places: `mx::core::parse(const pugi::xml_document&) -> Result` (strict on +in exactly two places: +`mx::core::parse(const pugi::xml_document&, const ParseContext&) -> Result` (strict on names and structure, lenient on values) and the bounded `add...` methods. Serialization is `mx::core::serialize(const Document&, pugi::xml_document&)`. The design and its rationale are recorded in `docs/ai/design/mx-core-plan.md`. diff --git a/docs/ai/design/mx-core-plan.md b/docs/ai/design/mx-core-plan.md index 37e11216c..3d04e9271 100644 --- a/docs/ai/design/mx-core-plan.md +++ b/docs/ai/design/mx-core-plan.md @@ -19,6 +19,7 @@ named groups onto the Plates is *neutral schema fact* work that serves all targe ## 1. The non-negotiables (owner's cardinal requirements) + 1. **Valid by construction.** It must be impossible to use the public C++ API to produce a document that is invalid against the MusicXML 4.0 spec. Invalid states are made *unrepresentable* by the type system wherever possible — including bespoke structural diff --git a/gen/cpp/templates/attr_parse_expr.tmpl b/gen/cpp/templates/attr_parse_expr.tmpl index d1bcbed36..30eb47782 100644 --- a/gen/cpp/templates/attr_parse_expr.tmpl +++ b/gen/cpp/templates/attr_parse_expr.tmpl @@ -1 +1 @@ -{{#type_ref.is_value}}{{type_ref.ident}}::parse(a.value()){{/type_ref.is_value}}{{#type_ref.name_token}}{{type_ref.ident}}::parse(a.value()){{/type_ref.name_token}}{{#type_ref.is_primitive_string}}{{^type_ref.name_token}}std::string{a.value()}{{/type_ref.name_token}}{{/type_ref.is_primitive_string}}{{#type_ref.is_primitive_integer}}parseInt(a.value()){{/type_ref.is_primitive_integer}}{{#type_ref.is_primitive_decimal}}Decimal::parse(a.value()){{/type_ref.is_primitive_decimal}} \ No newline at end of file +{{#type_ref.is_value}}parseValue<{{type_ref.ident}}>(a.value(), context, el, {{name.wire_q}}){{/type_ref.is_value}}{{#type_ref.name_token}}{{#type_ref.unique_id}}parseIdValue<{{type_ref.ident}}>(a.value(), context, el, {{name.wire_q}}){{/type_ref.unique_id}}{{^type_ref.unique_id}}parseValue<{{type_ref.ident}}>(a.value(), context, el, {{name.wire_q}}){{/type_ref.unique_id}}{{/type_ref.name_token}}{{#type_ref.is_primitive_string}}{{^type_ref.name_token}}std::string{a.value()}{{/type_ref.name_token}}{{/type_ref.is_primitive_string}}{{#type_ref.is_primitive_integer}}parseIntegerValue(a.value(), context, el, {{name.wire_q}}){{/type_ref.is_primitive_integer}}{{#type_ref.is_primitive_decimal}}parseDecimalValue(a.value(), context, el, {{name.wire_q}}){{/type_ref.is_primitive_decimal}} \ No newline at end of file diff --git a/gen/cpp/templates/attributes_parse.tmpl b/gen/cpp/templates/attributes_parse.tmpl index 3c54b15ad..8b1ad7e85 100644 --- a/gen/cpp/templates/attributes_parse.tmpl +++ b/gen/cpp/templates/attributes_parse.tmpl @@ -34,6 +34,7 @@ {{#import_default}} // Configured import repair (plan §2.4): inject the default. out.set{{name.pascal}}({{>attr_default_expr}}); + reportAttributeDefaulted(context, el, {{name.wire_q}}, {{import_default_q}}); {{/import_default}} {{^import_default}} throwMissingAttribute(el, {{name.wire_q}}); diff --git a/gen/cpp/templates/attrs.cpp.tmpl b/gen/cpp/templates/attrs.cpp.tmpl index 682c910d2..047b628ce 100644 --- a/gen/cpp/templates/attrs.cpp.tmpl +++ b/gen/cpp/templates/attrs.cpp.tmpl @@ -3,6 +3,7 @@ #include "mx/core/generated/{{name.pascal}}.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -11,17 +12,18 @@ namespace {{vars.namespace}} { {{>attributes_impl}} -{{ident}} parse{{ident}}(pugi::xml_node el) +{{ident}} parse{{ident}}(pugi::xml_node el, const ParseContext &context) { {{ident}} out; {{>attributes_parse}} - parse{{ident}}Content(out, el); + parse{{ident}}Content(out, el, context); return out; } -void parse{{ident}}Content({{ident}} &out, pugi::xml_node el) +void parse{{ident}}Content({{ident}} &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/gen/cpp/templates/attrs.h.tmpl b/gen/cpp/templates/attrs.h.tmpl index 5b0feb2a1..3c9bef23c 100644 --- a/gen/cpp/templates/attrs.h.tmpl +++ b/gen/cpp/templates/attrs.h.tmpl @@ -24,6 +24,8 @@ class xml_node; namespace {{vars.namespace}} { +class ParseContext; + {{#doc_lines}} /// {{value}} {{/doc_lines}} @@ -37,9 +39,9 @@ private: {{>attributes_members}} }; -{{ident}} parse{{ident}}(pugi::xml_node el); +{{ident}} parse{{ident}}(pugi::xml_node el, const ParseContext &context); -void parse{{ident}}Content({{ident}} &out, pugi::xml_node el); +void parse{{ident}}Content({{ident}} &out, pugi::xml_node el, const ParseContext &context); void serialize{{ident}}(const {{ident}} &v, pugi::xml_node parent, const char *tag); diff --git a/gen/cpp/templates/choice.cpp.tmpl b/gen/cpp/templates/choice.cpp.tmpl index 3b7584467..325aae54c 100644 --- a/gen/cpp/templates/choice.cpp.tmpl +++ b/gen/cpp/templates/choice.cpp.tmpl @@ -3,6 +3,7 @@ #include "mx/core/generated/{{name.pascal}}.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -26,7 +27,7 @@ namespace {{vars.namespace}} } {{/alternatives}} -{{ident}} parse{{ident}}(pugi::xml_node el, pugi::xml_node &cursor) +{{ident}} parse{{ident}}(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { {{#alternatives}} if ({{>field_first_match}}) @@ -60,21 +61,21 @@ namespace {{vars.namespace}} {{/is_element}} {{^is_element}} {{#is_required}} - return {{type.ident}}::{{ident}}(parse{{type_ref.ident}}(el, cursor)); + return {{type.ident}}::{{ident}}(parse{{type_ref.ident}}(el, cursor, context)); {{/is_required}} {{#is_vector}} {{#min1}} - OneOrMore<{{type_ref.ident}}> items{parse{{type_ref.ident}}(el, cursor)}; + OneOrMore<{{type_ref.ident}}> items{parse{{type_ref.ident}}(el, cursor, context)}; while ({{>field_first_match}}) { - items.add(parse{{type_ref.ident}}(el, cursor)); + items.add(parse{{type_ref.ident}}(el, cursor, context)); } {{/min1}} {{^min1}} std::vector<{{type_ref.ident}}> items; while ({{>field_first_match}}) { - items.push_back(parse{{type_ref.ident}}(el, cursor)); + items.push_back(parse{{type_ref.ident}}(el, cursor, context)); } {{/min1}} return {{type.ident}}::{{ident}}(std::move(items)); diff --git a/gen/cpp/templates/choice.h.tmpl b/gen/cpp/templates/choice.h.tmpl index 69200ef2e..f82459128 100644 --- a/gen/cpp/templates/choice.h.tmpl +++ b/gen/cpp/templates/choice.h.tmpl @@ -25,6 +25,8 @@ class xml_node; namespace {{vars.namespace}} { +class ParseContext; + {{#doc_lines}} /// {{value}} {{/doc_lines}} @@ -87,7 +89,7 @@ private: /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -{{ident}} parse{{ident}}(pugi::xml_node el, pugi::xml_node &cursor); +{{ident}} parse{{ident}}(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serialize{{ident}}(const {{ident}} &v, pugi::xml_node el); diff --git a/gen/cpp/templates/choice_boxed.cpp.tmpl b/gen/cpp/templates/choice_boxed.cpp.tmpl index 7b1063a93..a152297ae 100644 --- a/gen/cpp/templates/choice_boxed.cpp.tmpl +++ b/gen/cpp/templates/choice_boxed.cpp.tmpl @@ -2,6 +2,7 @@ #include "mx/core/generated/{{name.pascal}}.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -54,12 +55,12 @@ bool {{ident}}::operator==(const {{ident}} &other) const } {{/alternatives}} -{{ident}} parse{{ident}}(pugi::xml_node el, pugi::xml_node &cursor) +{{ident}} parse{{ident}}(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { {{#alternatives}} if (cursorIs(cursor, {{tag_q}})) { - {{type_ref.ident}} value = parse{{type_ref.ident}}(cursor); + {{type_ref.ident}} value = parse{{type_ref.ident}}(cursor, context); cursor = nextElement(cursor); return {{type.ident}}::{{ident}}(std::move(value)); } diff --git a/gen/cpp/templates/choice_boxed.h.tmpl b/gen/cpp/templates/choice_boxed.h.tmpl index 14e333b15..9bb122f9e 100644 --- a/gen/cpp/templates/choice_boxed.h.tmpl +++ b/gen/cpp/templates/choice_boxed.h.tmpl @@ -18,6 +18,8 @@ class xml_node; namespace {{vars.namespace}} { +class ParseContext; + {{#doc_lines}} /// {{value}} {{/doc_lines}} @@ -89,7 +91,7 @@ private: /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -{{ident}} parse{{ident}}(pugi::xml_node el, pugi::xml_node &cursor); +{{ident}} parse{{ident}}(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serialize{{ident}}(const {{ident}} &v, pugi::xml_node el); diff --git a/gen/cpp/templates/color.cpp.tmpl b/gen/cpp/templates/color.cpp.tmpl index 71a4d95f5..b999d6a5a 100644 --- a/gen/cpp/templates/color.cpp.tmpl +++ b/gen/cpp/templates/color.cpp.tmpl @@ -96,4 +96,16 @@ bool {{ident}}::tryParse(std::string_view text, {{ident}} &out) noexcept return out; } +{{ident}} {{ident}}::parse(std::string_view text, ValueParseOutcome &outcome) noexcept +{ + {{ident}} out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace {{vars.namespace}} diff --git a/gen/cpp/templates/color.h.tmpl b/gen/cpp/templates/color.h.tmpl index 2a785858d..77dd691bd 100644 --- a/gen/cpp/templates/color.h.tmpl +++ b/gen/cpp/templates/color.h.tmpl @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include #include @@ -81,6 +83,9 @@ public: /// Lenient: malformed text yields opaque black. static {{ident}} parse(std::string_view text) noexcept; + /// Lenient, and says whether the text had to be repaired. + static {{ident}} parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const {{ident}} &other) const noexcept = default; private: diff --git a/gen/cpp/templates/comma_separated_text.cpp.tmpl b/gen/cpp/templates/comma_separated_text.cpp.tmpl index e1e8fc0db..d0dd21ebe 100644 --- a/gen/cpp/templates/comma_separated_text.cpp.tmpl +++ b/gen/cpp/templates/comma_separated_text.cpp.tmpl @@ -120,4 +120,16 @@ bool {{ident}}::tryParse(std::string_view text, {{ident}} &out) return out; } +{{ident}} {{ident}}::parse(std::string_view text, ValueParseOutcome &outcome) +{ + {{ident}} out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace {{vars.namespace}} diff --git a/gen/cpp/templates/comma_separated_text.h.tmpl b/gen/cpp/templates/comma_separated_text.h.tmpl index b70bb923e..ff6350e5a 100644 --- a/gen/cpp/templates/comma_separated_text.h.tmpl +++ b/gen/cpp/templates/comma_separated_text.h.tmpl @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include #include @@ -43,6 +45,9 @@ public: /// Lenient: repairs into the nearest valid list. static {{ident}} parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. + static {{ident}} parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const {{ident}} &other) const noexcept { return m_items == other.m_items; diff --git a/gen/cpp/templates/composite.cpp.tmpl b/gen/cpp/templates/composite.cpp.tmpl index 29f85c195..124f7c95b 100644 --- a/gen/cpp/templates/composite.cpp.tmpl +++ b/gen/cpp/templates/composite.cpp.tmpl @@ -3,6 +3,7 @@ #include "mx/core/generated/{{name.pascal}}.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -12,15 +13,15 @@ namespace {{vars.namespace}} {{>attributes_impl}} {{>fields_impl}} -{{ident}} parse{{ident}}(pugi::xml_node el) +{{ident}} parse{{ident}}(pugi::xml_node el, const ParseContext &context) { {{ident}} out; {{>attributes_parse}} - parse{{ident}}Content(out, el); + parse{{ident}}Content(out, el, context); return out; } -void parse{{ident}}Content({{ident}} &out, pugi::xml_node el) +void parse{{ident}}Content({{ident}} &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); {{>fields_parse}} diff --git a/gen/cpp/templates/composite.h.tmpl b/gen/cpp/templates/composite.h.tmpl index feaa84f37..d3235bd91 100644 --- a/gen/cpp/templates/composite.h.tmpl +++ b/gen/cpp/templates/composite.h.tmpl @@ -32,6 +32,8 @@ class xml_node; namespace {{vars.namespace}} { +class ParseContext; + {{#doc_lines}} /// {{value}} {{/doc_lines}} @@ -49,9 +51,9 @@ private: {{>fields_members}} }; -{{ident}} parse{{ident}}(pugi::xml_node el); +{{ident}} parse{{ident}}(pugi::xml_node el, const ParseContext &context); -void parse{{ident}}Content({{ident}} &out, pugi::xml_node el); +void parse{{ident}}Content({{ident}} &out, pugi::xml_node el, const ParseContext &context); void serialize{{ident}}(const {{ident}} &v, pugi::xml_node parent, const char *tag); diff --git a/gen/cpp/templates/defaults.cpp.tmpl b/gen/cpp/templates/defaults.cpp.tmpl index 330281965..61197a18d 100644 --- a/gen/cpp/templates/defaults.cpp.tmpl +++ b/gen/cpp/templates/defaults.cpp.tmpl @@ -8,6 +8,7 @@ {{#groups}} #include "mx/core/generated/{{ident}}.h" {{/groups}} +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" namespace {{vars.namespace}} @@ -19,7 +20,7 @@ void roundTripDefaults() { pugi::xml_document doc; serialize{{ident}}({{ident}}{}, doc, "probe"); - parse{{ident}}(doc.document_element()); + parse{{ident}}(doc.document_element(), ParseContext{}); } {{/complex_types}} {{#groups}} @@ -29,7 +30,7 @@ void roundTripDefaults() serialize{{ident}}({{ident}}{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parse{{ident}}(el, cursor); + parse{{ident}}(el, cursor, ParseContext{}); } } {{/groups}} diff --git a/gen/cpp/templates/derived.cpp.tmpl b/gen/cpp/templates/derived.cpp.tmpl index 600c05924..aa49a7dff 100644 --- a/gen/cpp/templates/derived.cpp.tmpl +++ b/gen/cpp/templates/derived.cpp.tmpl @@ -3,6 +3,7 @@ #include "mx/core/generated/{{name.pascal}}.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -11,11 +12,11 @@ namespace {{vars.namespace}} { {{>attributes_impl}} -{{ident}} parse{{ident}}(pugi::xml_node el) +{{ident}} parse{{ident}}(pugi::xml_node el, const ParseContext &context) { {{ident}} out; {{>attributes_parse}} - parse{{base.ident}}Content(out, el); + parse{{base.ident}}Content(out, el, context); return out; } diff --git a/gen/cpp/templates/derived.h.tmpl b/gen/cpp/templates/derived.h.tmpl index ddc72c1b2..98e479f4e 100644 --- a/gen/cpp/templates/derived.h.tmpl +++ b/gen/cpp/templates/derived.h.tmpl @@ -24,6 +24,8 @@ class xml_node; namespace {{vars.namespace}} { +class ParseContext; + {{#doc_lines}} /// {{value}} {{/doc_lines}} @@ -39,7 +41,7 @@ private: {{>attributes_members}} }; -{{ident}} parse{{ident}}(pugi::xml_node el); +{{ident}} parse{{ident}}(pugi::xml_node el, const ParseContext &context); void serialize{{ident}}(const {{ident}} &v, pugi::xml_node parent, const char *tag); diff --git a/gen/cpp/templates/document.cpp.tmpl b/gen/cpp/templates/document.cpp.tmpl index f2208595a..bb88cce56 100644 --- a/gen/cpp/templates/document.cpp.tmpl +++ b/gen/cpp/templates/document.cpp.tmpl @@ -4,6 +4,7 @@ #include "mx/core/Error.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/generated/Version.h" #include "mx/core/Xml.h" @@ -13,7 +14,7 @@ namespace {{vars.namespace}} { -Result parse(const pugi::xml_document &doc) +Result parse(const pugi::xml_document &doc, const ParseContext &context) { try { @@ -42,7 +43,7 @@ Result parse(const pugi::xml_document &doc) {{#roots}} {{#is_first}}if{{/is_first}}{{^is_first}}else if{{/is_first}} (tag == {{name.wire_q}}) { - d = Document{parse{{ident}}(root)}; + d = Document{parse{{ident}}(root, context)}; } {{/roots}} else diff --git a/gen/cpp/templates/document.h.tmpl b/gen/cpp/templates/document.h.tmpl index a9cbf2003..b39762264 100644 --- a/gen/cpp/templates/document.h.tmpl +++ b/gen/cpp/templates/document.h.tmpl @@ -20,6 +20,8 @@ class xml_document; namespace {{vars.namespace}} { +class ParseContext; + /// A namespace declaration preserved verbatim from the parsed root element /// (xmlns, xmlns:xlink, ...): document plumbing, not schema data, but /// round-trip parity requires writing it back. @@ -90,8 +92,9 @@ private: /// The parse error boundary (plan §2.5): strict on names and structure, /// lenient on values; a root declaring a version newer than /// SupportedMusicXMLVersion is rejected with unsupportedVersion. -Result parse(const pugi::xml_document &doc); +Result parse(const pugi::xml_document &doc, const ParseContext &context); +// TODO: document the unique ID exception /// Total for every Document (§1.1 guarantees validity): writes the XML /// declaration, the matching DOCTYPE, the preserved root namespace /// declarations, and the typed tree. diff --git a/gen/cpp/templates/empty.cpp.tmpl b/gen/cpp/templates/empty.cpp.tmpl index 47fff5d3f..e70fe4af9 100644 --- a/gen/cpp/templates/empty.cpp.tmpl +++ b/gen/cpp/templates/empty.cpp.tmpl @@ -2,12 +2,13 @@ #include "mx/core/generated/{{name.pascal}}.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" namespace {{vars.namespace}} { -{{ident}} parse{{ident}}(pugi::xml_node el) +{{ident}} parse{{ident}}(pugi::xml_node el, const ParseContext &context) { {{ident}} out; for (pugi::xml_attribute a : el.attributes()) @@ -19,13 +20,14 @@ namespace {{vars.namespace}} } throwUnknownAttribute(el, a.name()); } - parse{{ident}}Content(out, el); + parse{{ident}}Content(out, el, context); return out; } -void parse{{ident}}Content({{ident}} &out, pugi::xml_node el) +void parse{{ident}}Content({{ident}} &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/gen/cpp/templates/empty.h.tmpl b/gen/cpp/templates/empty.h.tmpl index f5b392a12..776f0cf28 100644 --- a/gen/cpp/templates/empty.h.tmpl +++ b/gen/cpp/templates/empty.h.tmpl @@ -10,6 +10,8 @@ class xml_node; namespace {{vars.namespace}} { +class ParseContext; + {{#doc_lines}} /// {{value}} {{/doc_lines}} @@ -22,9 +24,9 @@ public: bool operator==(const {{ident}} &) const noexcept = default; }; -{{ident}} parse{{ident}}(pugi::xml_node el); +{{ident}} parse{{ident}}(pugi::xml_node el, const ParseContext &context); -void parse{{ident}}Content({{ident}} &out, pugi::xml_node el); +void parse{{ident}}Content({{ident}} &out, pugi::xml_node el, const ParseContext &context); void serialize{{ident}}(const {{ident}} &v, pugi::xml_node parent, const char *tag); diff --git a/gen/cpp/templates/ending_number.cpp.tmpl b/gen/cpp/templates/ending_number.cpp.tmpl index 2a386a159..a439b220a 100644 --- a/gen/cpp/templates/ending_number.cpp.tmpl +++ b/gen/cpp/templates/ending_number.cpp.tmpl @@ -101,4 +101,16 @@ bool {{ident}}::tryParse(std::string_view text, {{ident}} &out) return out; } +{{ident}} {{ident}}::parse(std::string_view text, ValueParseOutcome &outcome) +{ + {{ident}} out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace {{vars.namespace}} diff --git a/gen/cpp/templates/ending_number.h.tmpl b/gen/cpp/templates/ending_number.h.tmpl index 40d390562..bad22539f 100644 --- a/gen/cpp/templates/ending_number.h.tmpl +++ b/gen/cpp/templates/ending_number.h.tmpl @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include #include @@ -43,6 +45,9 @@ public: /// Lenient: repairs into the nearest valid value. static {{ident}} parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. + static {{ident}} parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const {{ident}} &other) const noexcept { return m_values == other.m_values; diff --git a/gen/cpp/templates/enum.cpp.tmpl b/gen/cpp/templates/enum.cpp.tmpl index 1b7254392..504fe4b5e 100644 --- a/gen/cpp/templates/enum.cpp.tmpl +++ b/gen/cpp/templates/enum.cpp.tmpl @@ -40,9 +40,15 @@ bool {{ident}}::tryParse(std::string_view text, {{ident}} &out) noexcept } {{ident}} {{ident}}::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +{{ident}} {{ident}}::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { {{ident}} v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/gen/cpp/templates/enum.h.tmpl b/gen/cpp/templates/enum.h.tmpl index bd3fdb8b0..6f063eee9 100644 --- a/gen/cpp/templates/enum.h.tmpl +++ b/gen/cpp/templates/enum.h.tmpl @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace {{vars.namespace}} @@ -44,6 +46,9 @@ public: /// (the import leniency policy; never produces an invalid value). static {{ident}} parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static {{ident}} parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const {{ident}} &other) const noexcept = default; private: diff --git a/gen/cpp/templates/field_parse_expr.tmpl b/gen/cpp/templates/field_parse_expr.tmpl index b98874cf8..19d9ee648 100644 --- a/gen/cpp/templates/field_parse_expr.tmpl +++ b/gen/cpp/templates/field_parse_expr.tmpl @@ -1 +1 @@ -{{#type_ref.is_complex}}parse{{type_ref.ident}}(cursor){{/type_ref.is_complex}}{{#type_ref.is_value}}{{type_ref.ident}}::parse(childText(cursor)){{/type_ref.is_value}}{{#type_ref.is_primitive_string}}std::string{childText(cursor)}{{/type_ref.is_primitive_string}}{{#type_ref.is_primitive_integer}}parseInt(childText(cursor)){{/type_ref.is_primitive_integer}}{{#type_ref.is_primitive_decimal}}Decimal::parse(childText(cursor)){{/type_ref.is_primitive_decimal}} \ No newline at end of file +{{#type_ref.is_complex}}parse{{type_ref.ident}}(cursor, context){{/type_ref.is_complex}}{{#type_ref.is_value}}parseValue<{{type_ref.ident}}>(childText(cursor), context, cursor, nullptr){{/type_ref.is_value}}{{#type_ref.is_primitive_string}}std::string{childText(cursor)}{{/type_ref.is_primitive_string}}{{#type_ref.is_primitive_integer}}parseIntegerValue(childText(cursor), context, cursor, nullptr){{/type_ref.is_primitive_integer}}{{#type_ref.is_primitive_decimal}}parseDecimalValue(childText(cursor), context, cursor, nullptr){{/type_ref.is_primitive_decimal}} \ No newline at end of file diff --git a/gen/cpp/templates/fields_parse.tmpl b/gen/cpp/templates/fields_parse.tmpl index 9c95d5aee..e064ae866 100644 --- a/gen/cpp/templates/fields_parse.tmpl +++ b/gen/cpp/templates/fields_parse.tmpl @@ -15,7 +15,7 @@ if (cursorIs(cursor, {{tag_q}})) { {{#presence}} - parse{{type_ref.ident}}(cursor); + parse{{type_ref.ident}}(cursor, context); out.set{{name.pascal}}(true); {{/presence}} {{^presence}} @@ -59,7 +59,7 @@ {{#is_required}} if ({{>field_first_match}}) { - out.set{{name.pascal}}(parse{{type_ref.ident}}(el, cursor)); + out.set{{name.pascal}}(parse{{type_ref.ident}}(el, cursor, context)); } {{^nullable}} else @@ -71,7 +71,7 @@ {{#is_optional}} if ({{>field_first_match}}) { - out.set{{name.pascal}}(parse{{type_ref.ident}}(el, cursor)); + out.set{{name.pascal}}(parse{{type_ref.ident}}(el, cursor, context)); } {{/is_optional}} {{#is_vector}} @@ -80,16 +80,16 @@ { throwMissingElement(el, {{#first}}{{#is_first}}{{value_q}}{{/is_first}}{{/first}}); } - out.set{{name.pascal}}(OneOrMore<{{type_ref.ident}}>{parse{{type_ref.ident}}(el, cursor)}); + out.set{{name.pascal}}(OneOrMore<{{type_ref.ident}}>{parse{{type_ref.ident}}(el, cursor, context)}); while ({{>field_first_match}}) { - out.add{{name.pascal}}(parse{{type_ref.ident}}(el, cursor)); + out.add{{name.pascal}}(parse{{type_ref.ident}}(el, cursor, context)); } {{/min1}} {{^min1}} while ({{>field_first_match}}) { - out.add{{name.pascal}}(parse{{type_ref.ident}}(el, cursor)); + out.add{{name.pascal}}(parse{{type_ref.ident}}(el, cursor, context)); } {{/min1}} {{/is_vector}} diff --git a/gen/cpp/templates/group.cpp.tmpl b/gen/cpp/templates/group.cpp.tmpl index 63f3c1ffc..2fd86dda2 100644 --- a/gen/cpp/templates/group.cpp.tmpl +++ b/gen/cpp/templates/group.cpp.tmpl @@ -3,6 +3,7 @@ #include "mx/core/generated/{{name.pascal}}.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -11,7 +12,7 @@ namespace {{vars.namespace}} { {{>fields_impl}} -{{ident}} parse{{ident}}(pugi::xml_node el, pugi::xml_node &cursor) +{{ident}} parse{{ident}}(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { {{ident}} out; {{>fields_parse}} diff --git a/gen/cpp/templates/group.h.tmpl b/gen/cpp/templates/group.h.tmpl index 04e4e19b6..7eeacfb09 100644 --- a/gen/cpp/templates/group.h.tmpl +++ b/gen/cpp/templates/group.h.tmpl @@ -28,6 +28,8 @@ class xml_node; namespace {{vars.namespace}} { +class ParseContext; + {{#doc_lines}} /// {{value}} {{/doc_lines}} @@ -46,7 +48,7 @@ private: /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -{{ident}} parse{{ident}}(pugi::xml_node el, pugi::xml_node &cursor); +{{ident}} parse{{ident}}(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serialize{{ident}}(const {{ident}} &v, pugi::xml_node el); diff --git a/gen/cpp/templates/nmtoken_string.cpp.tmpl b/gen/cpp/templates/nmtoken_string.cpp.tmpl index 26a4c5f04..0977ea0e5 100644 --- a/gen/cpp/templates/nmtoken_string.cpp.tmpl +++ b/gen/cpp/templates/nmtoken_string.cpp.tmpl @@ -70,4 +70,16 @@ bool {{ident}}::tryParse(std::string_view text, {{ident}} &out) return {{ident}}{std::string{text}}; } +{{ident}} {{ident}}::parse(std::string_view text, ValueParseOutcome &outcome) +{ + {{ident}} out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace {{vars.namespace}} diff --git a/gen/cpp/templates/nmtoken_string.h.tmpl b/gen/cpp/templates/nmtoken_string.h.tmpl index ef4a5171a..3d4a004d6 100644 --- a/gen/cpp/templates/nmtoken_string.h.tmpl +++ b/gen/cpp/templates/nmtoken_string.h.tmpl @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -40,6 +42,9 @@ public: /// Lenient: repairs into the nearest valid token. static {{ident}} parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. + static {{ident}} parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const {{ident}} &other) const noexcept = default; private: diff --git a/gen/cpp/templates/number.cpp.tmpl b/gen/cpp/templates/number.cpp.tmpl index b08d6cb82..2eec2c57e 100644 --- a/gen/cpp/templates/number.cpp.tmpl +++ b/gen/cpp/templates/number.cpp.tmpl @@ -53,30 +53,39 @@ std::string {{ident}}::toString() const bool {{ident}}::tryParse(std::string_view text, {{ident}} &out) { -{{#is_decimal}} - Decimal v; - if (!Decimal::tryParse(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + {{ident}} parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = {{ident}}{std::move(v)}; + out = std::move(parsed); + return true; +} + +{{ident}} {{ident}}::parse(std::string_view text) +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +{{ident}} {{ident}}::parse(std::string_view text, ValueParseOutcome &outcome) +{ +{{#is_decimal}} + Decimal v; + if (!Decimal::tryParse(text, v)) {{/is_decimal}} {{#is_integer}} int v{}; if (!tryParseInt(text, v)) +{{/is_integer}} { - return false; + outcome = ValueParseOutcome::invalid; + return {{ident}}{}; } - out = {{ident}}{v}; -{{/is_integer}} - return true; -} - -{{ident}} {{ident}}::parse(std::string_view text) -{ - {{ident}} v; - tryParse(text, v); - return v; + {{ident}} out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace {{vars.namespace}} diff --git a/gen/cpp/templates/number.h.tmpl b/gen/cpp/templates/number.h.tmpl index 6d7111d82..6d821926f 100644 --- a/gen/cpp/templates/number.h.tmpl +++ b/gen/cpp/templates/number.h.tmpl @@ -5,6 +5,7 @@ {{#is_decimal}} #include "mx/core/Decimal.h" {{/is_decimal}} +#include "mx/core/Lexical.h" #include #include @@ -49,6 +50,9 @@ public: /// Lenient: non-numeric text yields the clamped zero. static {{ident}} parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static {{ident}} parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const {{ident}} &other) const noexcept = default; private: diff --git a/gen/cpp/templates/smufl_prefixed.cpp.tmpl b/gen/cpp/templates/smufl_prefixed.cpp.tmpl index a3eddd9c4..82e179c3c 100644 --- a/gen/cpp/templates/smufl_prefixed.cpp.tmpl +++ b/gen/cpp/templates/smufl_prefixed.cpp.tmpl @@ -143,4 +143,16 @@ bool {{ident}}::tryParse(std::string_view text, {{ident}} &out) return {{ident}}{std::string{text}}; } +{{ident}} {{ident}}::parse(std::string_view text, ValueParseOutcome &outcome) +{ + {{ident}} out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace {{vars.namespace}} diff --git a/gen/cpp/templates/smufl_prefixed.h.tmpl b/gen/cpp/templates/smufl_prefixed.h.tmpl index 3180d54e7..cf841508f 100644 --- a/gen/cpp/templates/smufl_prefixed.h.tmpl +++ b/gen/cpp/templates/smufl_prefixed.h.tmpl @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -59,6 +61,9 @@ public: /// Lenient: repairs into the nearest valid glyph name. static {{ident}} parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. + static {{ident}} parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const {{ident}} &other) const noexcept = default; private: diff --git a/gen/cpp/templates/smufl_wavy.cpp.tmpl b/gen/cpp/templates/smufl_wavy.cpp.tmpl index 481d79a02..d054d9c2a 100644 --- a/gen/cpp/templates/smufl_wavy.cpp.tmpl +++ b/gen/cpp/templates/smufl_wavy.cpp.tmpl @@ -120,4 +120,16 @@ bool {{ident}}::tryParse(std::string_view text, {{ident}} &out) return {{ident}}{Kind::wiggle, std::string{text}}; } +{{ident}} {{ident}}::parse(std::string_view text, ValueParseOutcome &outcome) +{ + {{ident}} out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace {{vars.namespace}} diff --git a/gen/cpp/templates/smufl_wavy.h.tmpl b/gen/cpp/templates/smufl_wavy.h.tmpl index 425f410ae..9e8b8be68 100644 --- a/gen/cpp/templates/smufl_wavy.h.tmpl +++ b/gen/cpp/templates/smufl_wavy.h.tmpl @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -52,6 +54,9 @@ public: /// Lenient: repairs into the nearest valid glyph name. static {{ident}} parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. + static {{ident}} parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const {{ident}} &other) const noexcept = default; private: diff --git a/gen/cpp/templates/string.cpp.tmpl b/gen/cpp/templates/string.cpp.tmpl index 9334f53c7..d015907ea 100644 --- a/gen/cpp/templates/string.cpp.tmpl +++ b/gen/cpp/templates/string.cpp.tmpl @@ -46,4 +46,11 @@ bool {{ident}}::tryParse(std::string_view text, {{ident}} &out) return {{ident}}{std::string{text}}; } +{{ident}} {{ident}}::parse(std::string_view text, ValueParseOutcome &outcome) +{ + {{ident}} out{std::string{text}}; + outcome = out.value() == text ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; +} + } // namespace {{vars.namespace}} diff --git a/gen/cpp/templates/string.h.tmpl b/gen/cpp/templates/string.h.tmpl index f792b77ac..c759b0aa7 100644 --- a/gen/cpp/templates/string.h.tmpl +++ b/gen/cpp/templates/string.h.tmpl @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -37,6 +39,9 @@ public: static {{ident}} parse(std::string_view text); + /// Says whether the text had to be repaired. + static {{ident}} parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const {{ident}} &other) const noexcept = default; private: diff --git a/gen/cpp/templates/time_only.cpp.tmpl b/gen/cpp/templates/time_only.cpp.tmpl index 032951199..d296f3b3b 100644 --- a/gen/cpp/templates/time_only.cpp.tmpl +++ b/gen/cpp/templates/time_only.cpp.tmpl @@ -103,4 +103,16 @@ bool {{ident}}::tryParse(std::string_view text, {{ident}} &out) return out; } +{{ident}} {{ident}}::parse(std::string_view text, ValueParseOutcome &outcome) +{ + {{ident}} out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace {{vars.namespace}} diff --git a/gen/cpp/templates/time_only.h.tmpl b/gen/cpp/templates/time_only.h.tmpl index eeb718fb8..24d096bbb 100644 --- a/gen/cpp/templates/time_only.h.tmpl +++ b/gen/cpp/templates/time_only.h.tmpl @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include #include @@ -41,6 +43,9 @@ public: /// Lenient: repairs into the nearest valid list. static {{ident}} parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. + static {{ident}} parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const {{ident}} &other) const noexcept { return m_values == other.m_values; diff --git a/gen/cpp/templates/union.cpp.tmpl b/gen/cpp/templates/union.cpp.tmpl index e53e6d439..18f962cfb 100644 --- a/gen/cpp/templates/union.cpp.tmpl +++ b/gen/cpp/templates/union.cpp.tmpl @@ -169,12 +169,36 @@ bool {{ident}}::tryParse(std::string_view text, {{ident}} &out) } {{ident}} {{ident}}::parse(std::string_view text) +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +{{ident}} {{ident}}::parse(std::string_view text, ValueParseOutcome &outcome) { {{ident}} out; if (tryParse(text, out)) { + outcome = ValueParseOutcome::valid; +{{#cases}} +{{#has_clamp}} +{{#is_primitive_integer}} + if (int raw{}; out.is{{name.pascal}}() && tryParseInt(text, raw) && out.as{{name.pascal}}() != raw) + { + outcome = ValueParseOutcome::adjusted; + } +{{/is_primitive_integer}} +{{#is_primitive_decimal}} + if (Decimal raw; out.is{{name.pascal}}() && Decimal::tryParse(text, raw) && out.as{{name.pascal}}() != raw) + { + outcome = ValueParseOutcome::adjusted; + } +{{/is_primitive_decimal}} +{{/has_clamp}} +{{/cases}} return out; } + outcome = ValueParseOutcome::invalid; {{#cases}} {{#is_first}} {{#is_literal}} diff --git a/gen/cpp/templates/union.h.tmpl b/gen/cpp/templates/union.h.tmpl index 4d511bfb9..29456aec7 100644 --- a/gen/cpp/templates/union.h.tmpl +++ b/gen/cpp/templates/union.h.tmpl @@ -10,6 +10,7 @@ #include "mx/core/Decimal.h" {{/is_primitive_decimal}} {{/cases}} +#include "mx/core/Lexical.h" #include #include @@ -86,6 +87,10 @@ public: /// parse (never produces an invalid value). static {{ident}} parse(std::string_view text); + /// Lenient, and says whether no member matched or a matched number was + /// clamped. + static {{ident}} parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const {{ident}} &other) const = default; private: diff --git a/gen/cpp/templates/value_class.cpp.tmpl b/gen/cpp/templates/value_class.cpp.tmpl index e96f060cd..894c8cb03 100644 --- a/gen/cpp/templates/value_class.cpp.tmpl +++ b/gen/cpp/templates/value_class.cpp.tmpl @@ -3,6 +3,7 @@ #include "mx/core/generated/{{name.pascal}}.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -23,18 +24,18 @@ void {{type.ident}}::set{{name.pascal}}({{type_ref.ident}} value) } {{/value}} -{{ident}} parse{{ident}}(pugi::xml_node el) +{{ident}} parse{{ident}}(pugi::xml_node el, const ParseContext &context) { {{ident}} out; {{>attributes_parse}} - parse{{ident}}Content(out, el); + parse{{ident}}Content(out, el, context); return out; } -void parse{{ident}}Content({{ident}} &out, pugi::xml_node el) +void parse{{ident}}Content({{ident}} &out, pugi::xml_node el, const ParseContext &context) { {{#value}} - out.set{{name.pascal}}({{#type_ref.is_value}}{{type_ref.ident}}::parse(childText(el)){{/type_ref.is_value}}{{#type_ref.is_primitive_string}}std::string{childText(el)}{{/type_ref.is_primitive_string}}{{#type_ref.is_primitive_integer}}parseInt(childText(el)){{/type_ref.is_primitive_integer}}{{#type_ref.is_primitive_decimal}}Decimal::parse(childText(el)){{/type_ref.is_primitive_decimal}}); + out.set{{name.pascal}}({{#type_ref.is_value}}parseValue<{{type_ref.ident}}>(childText(el), context, el, nullptr){{/type_ref.is_value}}{{#type_ref.is_primitive_string}}std::string{childText(el)}{{/type_ref.is_primitive_string}}{{#type_ref.is_primitive_integer}}parseIntegerValue(childText(el), context, el, nullptr){{/type_ref.is_primitive_integer}}{{#type_ref.is_primitive_decimal}}parseDecimalValue(childText(el), context, el, nullptr){{/type_ref.is_primitive_decimal}}); {{/value}} if (firstElement(el)) { diff --git a/gen/cpp/templates/value_class.h.tmpl b/gen/cpp/templates/value_class.h.tmpl index 2b943cea4..c3a17ca9c 100644 --- a/gen/cpp/templates/value_class.h.tmpl +++ b/gen/cpp/templates/value_class.h.tmpl @@ -24,6 +24,8 @@ class xml_node; namespace {{vars.namespace}} { +class ParseContext; + {{#doc_lines}} /// {{value}} {{/doc_lines}} @@ -44,9 +46,9 @@ private: {{/value}} }; -{{ident}} parse{{ident}}(pugi::xml_node el); +{{ident}} parse{{ident}}(pugi::xml_node el, const ParseContext &context); -void parse{{ident}}Content({{ident}} &out, pugi::xml_node el); +void parse{{ident}}Content({{ident}} &out, pugi::xml_node el, const ParseContext &context); void serialize{{ident}}(const {{ident}} &v, pugi::xml_node parent, const char *tag); diff --git a/gen/cpp/templates/yyyy_mm_dd.cpp.tmpl b/gen/cpp/templates/yyyy_mm_dd.cpp.tmpl index be628071b..18cb7c8cb 100644 --- a/gen/cpp/templates/yyyy_mm_dd.cpp.tmpl +++ b/gen/cpp/templates/yyyy_mm_dd.cpp.tmpl @@ -131,4 +131,16 @@ bool {{ident}}::tryParse(std::string_view text, {{ident}} &out) noexcept return out; } +{{ident}} {{ident}}::parse(std::string_view text, ValueParseOutcome &outcome) noexcept +{ + {{ident}} out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace {{vars.namespace}} diff --git a/gen/cpp/templates/yyyy_mm_dd.h.tmpl b/gen/cpp/templates/yyyy_mm_dd.h.tmpl index f03265ee7..a8ee7164e 100644 --- a/gen/cpp/templates/yyyy_mm_dd.h.tmpl +++ b/gen/cpp/templates/yyyy_mm_dd.h.tmpl @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -52,6 +54,9 @@ public: /// components clamp. static {{ident}} parse(std::string_view text) noexcept; + /// Lenient, and says whether the text had to be repaired. + static {{ident}} parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const {{ident}} &other) const noexcept = default; private: diff --git a/gen/plates/build.py b/gen/plates/build.py index d32b5f236..c07dbeb4f 100644 --- a/gen/plates/build.py +++ b/gen/plates/build.py @@ -512,6 +512,7 @@ def _plate_ref(self, ref: ir.Ref) -> PlateRef: if ref.name in _PRIM_NUMERIC else "primitive-string", name_token=ref.name in _PRIM_NAME_TOKEN, + unique_id=ref.name == "id", ) if ref.category == "value": kind = self.values_by_name[ref.name].kind diff --git a/gen/plates/model.py b/gen/plates/model.py index dff1c57f6..838668ce4 100644 --- a/gen/plates/model.py +++ b/gen/plates/model.py @@ -59,6 +59,9 @@ class PlateRef: # lexically narrower than a free string. A validating target renders a # repairing wrapper for it; others ignore the flag. Neutral schema fact. name_token: bool = False + # True when the referent is the xs:ID primitive, whose value must be unique + # within the document. Neutral schema fact. + unique_id: bool = False @dataclass diff --git a/src/include/mx/api/Diagnostics.h b/src/include/mx/api/Diagnostics.h index a10063fdb..37e538c05 100644 --- a/src/include/mx/api/Diagnostics.h +++ b/src/include/mx/api/Diagnostics.h @@ -26,8 +26,11 @@ enum class Severity // The recoverable decision reported by a diagnostic. enum class DiagnosticCode { - valueAdjusted, // a value was changed to one that MusicXML can represent - unmatchedSpanner // a spanner endpoint had no matching endpoint + valueAdjusted, // a value was changed to one that MusicXML can represent + unmatchedSpanner, // a spanner endpoint had no matching endpoint + invalidValue, // a value could not be read, so a default was used + missingValueDefaulted, // a required value was missing, so a default was used + droppedData // data could not be read or written, so it was left out }; // A non-fatal problem noticed while producing a score or MusicXML document. diff --git a/src/include/mx/api/Id.h b/src/include/mx/api/Id.h index 3640e0520..7fdca905d 100644 --- a/src/include/mx/api/Id.h +++ b/src/include/mx/api/Id.h @@ -16,6 +16,7 @@ namespace mx namespace api { +// TODO: document the unique ID exception // The id attribute of a MusicXML element. An id is a name that identifies one element within the // document. Software uses it to point at a particular note, measure, or marking -- to line playback // up with the score, to hang an annotation on a note, or to link one file to another. @@ -26,8 +27,9 @@ namespace api // becomes "X". Building the Id is the only place this happens: mx writes the id exactly as the Id // holds it. // -// Scrubbing is silent. To find out whether your text was already a legal id, compare it with the -// id you built: +// Writing the score with fromScore reports each id that building scrubbed, in the Diagnostics you +// pass it. To find out sooner whether your text was already a legal id, compare it with the id you +// built: // // const auto id = Id{myText}; // if (id.value() != myText) diff --git a/src/include/mx/api/MusicXml.h b/src/include/mx/api/MusicXml.h index 83959582f..0a3eeab1b 100644 --- a/src/include/mx/api/MusicXml.h +++ b/src/include/mx/api/MusicXml.h @@ -29,10 +29,19 @@ class MusicXml // represented by an error result. static Result fromFile(const std::string &filePath); + // Parses a MusicXML file and reports values that had to be repaired to + // be read, such as an unknown note type or an octave above 9. The same + // Diagnostics can be passed on to getScore to collect both in one place. + static Result fromFile(const std::string &filePath, Diagnostics &diagnostics); + // Parses a MusicXML document from a character stream. Logical errors and // caught exceptions are represented by an error result. static Result fromStream(std::istream &stream); + // Parses a MusicXML document from a character stream and reports values + // that had to be repaired to be read. + static Result fromStream(std::istream &stream, Diagnostics &diagnostics); + MusicXml(const MusicXml &other) = delete; MusicXml &operator=(const MusicXml &other) = delete; MusicXml(MusicXml &&other); @@ -49,6 +58,8 @@ class MusicXml // Writes the document to a character stream. Result writeToStream(std::ostream &stream) const; + // TODO: document ID validity loophole + // // This is an escape hatch in case mx::api does not do what you need and // you want to edit the core DOM directly. You will need to include the // private mx::core headers in your header search paths to do so. Not diff --git a/src/private/mx/api/Id.cpp b/src/private/mx/api/Id.cpp index 4d0fb41fe..dd1e08504 100644 --- a/src/private/mx/api/Id.cpp +++ b/src/private/mx/api/Id.cpp @@ -6,6 +6,8 @@ #include "mx/api/IdAccess.h" +#include +#include #include namespace mx @@ -25,11 +27,24 @@ class Id::Impl { } + explicit Impl(const std::string &text) + { + core::ValueParseOutcome outcome = core::ValueParseOutcome::valid; + token = core::Token::parse(text, outcome); + if (outcome != core::ValueParseOutcome::valid) + { + scrubbedText = text; + } + } + // What a moved-from Id is left holding. An Id always holds an Impl, so reading one that has // been moved from is harmless rather than undefined. static const std::shared_ptr &movedFrom(); core::Token token; + + // The text the Id was built from, when building it scrubbed the text. + std::optional scrubbedText; }; const std::shared_ptr &Id::Impl::movedFrom() @@ -38,7 +53,7 @@ const std::shared_ptr &Id::Impl::movedFrom() return value; } -Id::Id(std::string text) : myImpl{std::make_shared(core::Token{std::move(text)})} +Id::Id(std::string text) : myImpl{std::make_shared(text)} { } @@ -91,6 +106,11 @@ const core::Token &IdAccess::token(const Id &inId) return inId.myImpl->token; } +const std::optional &IdAccess::scrubbedText(const Id &inId) +{ + return inId.myImpl->scrubbedText; +} + Id IdAccess::make(core::Token inToken) { return Id{std::make_shared(std::move(inToken))}; diff --git a/src/private/mx/api/IdAccess.h b/src/private/mx/api/IdAccess.h index 004807ab0..713e6e98c 100644 --- a/src/private/mx/api/IdAccess.h +++ b/src/private/mx/api/IdAccess.h @@ -7,6 +7,9 @@ #include "mx/api/Id.h" #include "mx/core/Token.h" +#include +#include + namespace mx { namespace api @@ -20,6 +23,9 @@ struct IdAccess // The token an Id holds. Pass it straight to a core element's setID. static const core::Token &token(const Id &inId); + // The text an Id was built from, when building the Id scrubbed it; otherwise empty. + static const std::optional &scrubbedText(const Id &inId); + // An Id holding a token that mx::core already read from a file. static Id make(core::Token inToken); }; diff --git a/src/private/mx/api/MusicXml.cpp b/src/private/mx/api/MusicXml.cpp index d230b4bec..3eb25f521 100644 --- a/src/private/mx/api/MusicXml.cpp +++ b/src/private/mx/api/MusicXml.cpp @@ -5,6 +5,7 @@ #include "mx/api/MusicXml.h" #include "mx/core/Attribution.h" #include "mx/core/Error.h" +#include "mx/core/ParseContext.h" #include "mx/core/generated/Document.h" #include "mx/impl/ScoreConversions.h" #include "mx/impl/ScoreReader.h" @@ -75,6 +76,31 @@ ApiError mirrorToApiError(const core::Error &error) return ApiError{mirrorToApiResultCode(error.code), location, error.message}; } +DiagnosticCode mirrorToApiDiagnosticCode(core::DiagnosticCode code) +{ + switch (code) + { + case core::DiagnosticCode::invalidValue: + return DiagnosticCode::invalidValue; + case core::DiagnosticCode::valueAdjusted: + return DiagnosticCode::valueAdjusted; + case core::DiagnosticCode::missingValueDefaulted: + return DiagnosticCode::missingValueDefaulted; + } + return DiagnosticCode::invalidValue; +} + +// Every import repair leaves a usable document, so each one is a warning. +core::ParseContext parseContextReportingTo(Diagnostics &diagnostics) +{ + return core::ParseContext{[&diagnostics](const core::Diagnostic &diagnostic) { + Location location; + location.xmlPath = diagnostic.path; + diagnostics.add(Diagnostic{Severity::warning, mirrorToApiDiagnosticCode(diagnostic.code), std::move(location), + diagnostic.message}); + }}; +} + // Builds the error for a caught exception. Call it inside a catch block only: // current_exception() keeps the exception alive in `cause`, so a caller can // look at it or throw it again. Most entry points repeat the same three @@ -145,6 +171,12 @@ MusicXml::~MusicXml() } Result MusicXml::fromFile(const std::string &filePath) +{ + Diagnostics diagnostics; + return fromFile(filePath, diagnostics); +} + +Result MusicXml::fromFile(const std::string &filePath, Diagnostics &diagnostics) { try { @@ -171,7 +203,7 @@ Result MusicXml::fromFile(const std::string &filePath) return ApiError{ResultCode::xmlSyntaxError, location, loaded.description()}; } - auto parsed = core::parse(xdoc); + auto parsed = core::parse(xdoc, parseContextReportingTo(diagnostics)); if (!parsed) { return mirrorToApiError(parsed.error()); @@ -194,6 +226,12 @@ Result MusicXml::fromFile(const std::string &filePath) } Result MusicXml::fromStream(std::istream &stream) +{ + Diagnostics diagnostics; + return fromStream(stream, diagnostics); +} + +Result MusicXml::fromStream(std::istream &stream, Diagnostics &diagnostics) { try { @@ -206,7 +244,7 @@ Result MusicXml::fromStream(std::istream &stream) return ApiError{ResultCode::xmlSyntaxError, location, loaded.description()}; } - auto parsed = core::parse(xdoc); + auto parsed = core::parse(xdoc, parseContextReportingTo(diagnostics)); if (!parsed) { return mirrorToApiError(parsed.error()); @@ -321,12 +359,14 @@ Result fromScore(const ScoreData &score, Diagnostics &diagnostics) { try { - impl::ScoreWriter writer{score, impl::DiagnosticsContext{diagnostics}}; + const impl::DiagnosticsContext context{diagnostics}; + impl::ScoreWriter writer{score, context}; core::ScorePartwise scorePartwise = writer.getScorePartwise(); if (score.musicXmlType == "timewise") { - return MusicXml{core::Document{impl::partwiseTimewise(scorePartwise)}, score.encoding.writeMxVersion}; + return MusicXml{core::Document{impl::partwiseTimewise(scorePartwise, context)}, + score.encoding.writeMxVersion}; } return MusicXml{core::Document{std::move(scorePartwise)}, score.encoding.writeMxVersion}; @@ -367,8 +407,9 @@ Result getScore(const MusicXml &document, Diagnostics &diagnostics) // that; the owned document is untouched. if (coreDocument.isScoreTimewise()) { - const core::ScorePartwise scorePartwise = impl::timewisePartwise(coreDocument.asScoreTimewise()); - impl::ScoreReader reader{scorePartwise, impl::DiagnosticsContext{diagnostics}}; + const impl::DiagnosticsContext context{diagnostics}; + const core::ScorePartwise scorePartwise = impl::timewisePartwise(coreDocument.asScoreTimewise(), context); + impl::ScoreReader reader{scorePartwise, context}; auto score = reader.getScoreData(); score.musicXmlType = "timewise"; return score; diff --git a/src/private/mx/core/Lexical.h b/src/private/mx/core/Lexical.h index 5e92e2446..cda64f422 100644 --- a/src/private/mx/core/Lexical.h +++ b/src/private/mx/core/Lexical.h @@ -18,6 +18,15 @@ namespace mx::core // nothing else). The lenient flavors return 0 on garbage — the import // leniency policy (plan §2.4); range clamping happens in the caller. +/// What a lenient value parse did with its input. Import diagnostics are +/// built from this; the parsed value is the same whatever the outcome says. +enum class ValueParseOutcome +{ + valid, // the text was already a valid value + invalid, // the text could not be read, so a default was used + adjusted, // the text was readable but was changed to a valid value +}; + /// The input minus leading/trailing XML whitespace. std::string_view trimWhitespace(std::string_view text); diff --git a/src/private/mx/core/NameToken.cpp b/src/private/mx/core/NameToken.cpp index bc7156e68..b581fa716 100644 --- a/src/private/mx/core/NameToken.cpp +++ b/src/private/mx/core/NameToken.cpp @@ -71,4 +71,11 @@ NameToken NameToken::parse(std::string_view text) return NameToken{std::string{text}}; } +NameToken NameToken::parse(std::string_view text, ValueParseOutcome &outcome) +{ + NameToken strict; + outcome = tryParse(trimWhitespace(text), strict) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; + return parse(text); +} + } // namespace mx::core diff --git a/src/private/mx/core/NameToken.h b/src/private/mx/core/NameToken.h index 678f91ccc..1536693a7 100644 --- a/src/private/mx/core/NameToken.h +++ b/src/private/mx/core/NameToken.h @@ -6,6 +6,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -42,6 +44,10 @@ class NameToken final /// Lenient: repairs into the nearest valid token. static NameToken parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. Surrounding + /// XML whitespace is not a repair. + static NameToken parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const NameToken &other) const noexcept = default; private: diff --git a/src/private/mx/core/ParseContext.cpp b/src/private/mx/core/ParseContext.cpp new file mode 100644 index 000000000..f1705668a --- /dev/null +++ b/src/private/mx/core/ParseContext.cpp @@ -0,0 +1,96 @@ +// MusicXML Class Library +// Copyright (c) by Matthew James Briggs +// Distributed under the MIT License + +#include "mx/core/ParseContext.h" + +#include "mx/core/Xml.h" + +#include + +namespace mx::core +{ + +ParseContext::ParseContext(DiagnosticHandler handler) : m_handler{std::move(handler)} +{ +} + +void ParseContext::report(Diagnostic diagnostic) const +{ + if (m_handler) + { + m_handler(diagnostic); + } +} + +namespace +{ + +Diagnostic valueRepairDiagnostic(ValueParseOutcome outcome, pugi::xml_node el, const char *attribute, + std::string_view text, std::string_view replacement) +{ + const bool invalid = outcome == ValueParseOutcome::invalid; + std::string message = invalid ? "invalid value \"" : "value \""; + message += text; + message += "\""; + if (attribute) + { + message += " in attribute \""; + message += attribute; + message += "\""; + } + message += invalid ? "; using \"" : " adjusted to \""; + message += replacement; + message += "\""; + return Diagnostic{invalid ? DiagnosticCode::invalidValue : DiagnosticCode::valueAdjusted, nodePath(el), + std::move(message)}; +} + +} // namespace + +void reportValueRepair(const ParseContext &context, ValueParseOutcome outcome, pugi::xml_node el, const char *attribute, + std::string_view text, std::string_view replacement) +{ + context.report(valueRepairDiagnostic(outcome, el, attribute, text, replacement)); +} + +void reportIdRepair(const ParseContext &context, ValueParseOutcome outcome, pugi::xml_node el, const char *attribute, + std::string_view text, std::string_view replacement) +{ + Diagnostic diagnostic = valueRepairDiagnostic(outcome, el, attribute, text, replacement); + diagnostic.message += "; the repaired ID may duplicate another ID in the document"; + context.report(std::move(diagnostic)); +} + +void reportAttributeDefaulted(const ParseContext &context, pugi::xml_node el, const char *attribute, + std::string_view value) +{ + std::string message = "missing attribute \""; + message += attribute; + message += "\"; using \""; + message += value; + message += "\""; + context.report(Diagnostic{DiagnosticCode::missingValueDefaulted, nodePath(el), std::move(message)}); +} + +int parseIntegerValue(std::string_view text, const ParseContext &context, pugi::xml_node el, const char *attribute) +{ + int value = 0; + if (!tryParseInt(text, value)) + { + reportValueRepair(context, ValueParseOutcome::invalid, el, attribute, text, formatInt(value)); + } + return value; +} + +Decimal parseDecimalValue(std::string_view text, const ParseContext &context, pugi::xml_node el, const char *attribute) +{ + Decimal value; + if (!Decimal::tryParse(text, value)) + { + reportValueRepair(context, ValueParseOutcome::invalid, el, attribute, text, value.toString()); + } + return value; +} + +} // namespace mx::core diff --git a/src/private/mx/core/ParseContext.h b/src/private/mx/core/ParseContext.h new file mode 100644 index 000000000..9d599ad1c --- /dev/null +++ b/src/private/mx/core/ParseContext.h @@ -0,0 +1,96 @@ +// MusicXML Class Library +// Copyright (c) by Matthew James Briggs +// Distributed under the MIT License + +// Hand-written runtime for the generated mx::core model; never regenerated. + +#pragma once + +#include "mx/core/Decimal.h" +#include "mx/core/Lexical.h" + +#include "pugixml.hpp" + +#include +#include +#include + +namespace mx::core +{ + +/// The repairs the lenient parsers make on import. The parsed document is +/// still valid; a diagnostic tells the caller what was changed. +enum class DiagnosticCode +{ + invalidValue, + valueAdjusted, + missingValueDefaulted, +}; + +struct Diagnostic +{ + DiagnosticCode code; + std::string path; // e.g. /score-partwise/part[1]/measure[3]/note[2] + std::string message; +}; + +using DiagnosticHandler = std::function; + +/// Passed down through the generated parsers so they can report repairs. A +/// default-constructed context discards every report. +class ParseContext +{ + public: + ParseContext() = default; + explicit ParseContext(DiagnosticHandler handler); + + void report(Diagnostic diagnostic) const; + + private: + DiagnosticHandler m_handler; +}; + +/// Reports that the text of `el`, or of its `attribute` when that is not +/// null, was replaced by `replacement`. +void reportValueRepair(const ParseContext &context, ValueParseOutcome outcome, pugi::xml_node el, const char *attribute, + std::string_view text, std::string_view replacement); + +/// Reports a repaired xs:ID attribute, warning that the repair may have made +/// it the same as another ID in the document. +void reportIdRepair(const ParseContext &context, ValueParseOutcome outcome, pugi::xml_node el, const char *attribute, + std::string_view text, std::string_view replacement); + +void reportAttributeDefaulted(const ParseContext &context, pugi::xml_node el, const char *attribute, + std::string_view value); + +/// The lenient parse of a value type, reporting any repair it made. +template +T parseValue(std::string_view text, const ParseContext &context, pugi::xml_node el, const char *attribute) +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + T value = T::parse(text, outcome); + if (outcome != ValueParseOutcome::valid) + { + reportValueRepair(context, outcome, el, attribute, text, std::string{value.toString()}); + } + return value; +} + +/// The lenient parse of an xs:ID attribute, reporting any repair it made. +template +T parseIdValue(std::string_view text, const ParseContext &context, pugi::xml_node el, const char *attribute) +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + T value = T::parse(text, outcome); + if (outcome != ValueParseOutcome::valid) + { + reportIdRepair(context, outcome, el, attribute, text, std::string{value.toString()}); + } + return value; +} + +int parseIntegerValue(std::string_view text, const ParseContext &context, pugi::xml_node el, const char *attribute); + +Decimal parseDecimalValue(std::string_view text, const ParseContext &context, pugi::xml_node el, const char *attribute); + +} // namespace mx::core diff --git a/src/private/mx/core/Token.cpp b/src/private/mx/core/Token.cpp index 641ffb32e..651c5842c 100644 --- a/src/private/mx/core/Token.cpp +++ b/src/private/mx/core/Token.cpp @@ -85,4 +85,11 @@ Token Token::parse(std::string_view text) return Token{std::string{text}}; } +Token Token::parse(std::string_view text, ValueParseOutcome &outcome) +{ + Token strict; + outcome = tryParse(trimWhitespace(text), strict) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; + return parse(text); +} + } // namespace mx::core diff --git a/src/private/mx/core/Token.h b/src/private/mx/core/Token.h index 3cd78db3c..8dcfe5771 100644 --- a/src/private/mx/core/Token.h +++ b/src/private/mx/core/Token.h @@ -6,6 +6,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -43,6 +45,10 @@ class Token final /// Lenient: repairs into the nearest valid NCName. static Token parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. Surrounding + /// XML whitespace is not a repair. + static Token parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const Token &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/AboveBelow.cpp b/src/private/mx/core/generated/AboveBelow.cpp index 9997528cc..227737071 100644 --- a/src/private/mx/core/generated/AboveBelow.cpp +++ b/src/private/mx/core/generated/AboveBelow.cpp @@ -42,9 +42,15 @@ bool AboveBelow::tryParse(std::string_view text, AboveBelow &out) noexcept } AboveBelow AboveBelow::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +AboveBelow AboveBelow::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { AboveBelow v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/AboveBelow.h b/src/private/mx/core/generated/AboveBelow.h index f963ddd02..4f2426ae9 100644 --- a/src/private/mx/core/generated/AboveBelow.h +++ b/src/private/mx/core/generated/AboveBelow.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -41,6 +43,9 @@ class AboveBelow final /// (the import leniency policy; never produces an invalid value). static AboveBelow parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static AboveBelow parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const AboveBelow &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Accidental.cpp b/src/private/mx/core/generated/Accidental.cpp index a02255477..b53b1d416 100644 --- a/src/private/mx/core/generated/Accidental.cpp +++ b/src/private/mx/core/generated/Accidental.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Accidental.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -170,7 +171,7 @@ void Accidental::setValue(AccidentalValue value) m_value = std::move(value); } -Accidental parseAccidental(pugi::xml_node el) +Accidental parseAccidental(pugi::xml_node el, const ParseContext &context) { Accidental out; for (pugi::xml_attribute a : el.attributes()) @@ -182,76 +183,76 @@ Accidental parseAccidental(pugi::xml_node el) } if (aname == "cautionary") { - out.setCautionary(YesNo::parse(a.value())); + out.setCautionary(parseValue(a.value(), context, el, "cautionary")); } else if (aname == "editorial") { - out.setEditorial(YesNo::parse(a.value())); + out.setEditorial(parseValue(a.value(), context, el, "editorial")); } else if (aname == "smufl") { - out.setSmufl(SmuflAccidentalGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else if (aname == "parentheses") { - out.setParentheses(YesNo::parse(a.value())); + out.setParentheses(parseValue(a.value(), context, el, "parentheses")); } else if (aname == "bracket") { - out.setBracket(YesNo::parse(a.value())); + out.setBracket(parseValue(a.value(), context, el, "bracket")); } else if (aname == "size") { - out.setSize(SymbolSize::parse(a.value())); + out.setSize(parseValue(a.value(), context, el, "size")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseAccidentalContent(out, el); + parseAccidentalContent(out, el, context); return out; } -void parseAccidentalContent(Accidental &out, pugi::xml_node el) +void parseAccidentalContent(Accidental &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(AccidentalValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Accidental.h b/src/private/mx/core/generated/Accidental.h index 46ba0411f..593a4c993 100644 --- a/src/private/mx/core/generated/Accidental.h +++ b/src/private/mx/core/generated/Accidental.h @@ -24,6 +24,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The accidental type represents actual notated accidentals. Editorial and cautionary indications /// are indicated by attributes. Values for these attributes are "no" if not present. Specific /// graphic display such as parentheses, brackets, and size are controlled by the level-display @@ -84,9 +86,9 @@ class Accidental final AccidentalValue m_value{}; }; -Accidental parseAccidental(pugi::xml_node el); +Accidental parseAccidental(pugi::xml_node el, const ParseContext &context); -void parseAccidentalContent(Accidental &out, pugi::xml_node el); +void parseAccidentalContent(Accidental &out, pugi::xml_node el, const ParseContext &context); void serializeAccidental(const Accidental &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/AccidentalMark.cpp b/src/private/mx/core/generated/AccidentalMark.cpp index f8e3a3d9d..93f5aa9d4 100644 --- a/src/private/mx/core/generated/AccidentalMark.cpp +++ b/src/private/mx/core/generated/AccidentalMark.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/AccidentalMark.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -170,7 +171,7 @@ void AccidentalMark::setValue(AccidentalValue value) m_value = std::move(value); } -AccidentalMark parseAccidentalMark(pugi::xml_node el) +AccidentalMark parseAccidentalMark(pugi::xml_node el, const ParseContext &context) { AccidentalMark out; for (pugi::xml_attribute a : el.attributes()) @@ -182,76 +183,76 @@ AccidentalMark parseAccidentalMark(pugi::xml_node el) } if (aname == "smufl") { - out.setSmufl(SmuflAccidentalGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else if (aname == "parentheses") { - out.setParentheses(YesNo::parse(a.value())); + out.setParentheses(parseValue(a.value(), context, el, "parentheses")); } else if (aname == "bracket") { - out.setBracket(YesNo::parse(a.value())); + out.setBracket(parseValue(a.value(), context, el, "bracket")); } else if (aname == "size") { - out.setSize(SymbolSize::parse(a.value())); + out.setSize(parseValue(a.value(), context, el, "size")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseAccidentalMarkContent(out, el); + parseAccidentalMarkContent(out, el, context); return out; } -void parseAccidentalMarkContent(AccidentalMark &out, pugi::xml_node el) +void parseAccidentalMarkContent(AccidentalMark &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(AccidentalValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/AccidentalMark.h b/src/private/mx/core/generated/AccidentalMark.h index 6c5338130..992d90bf3 100644 --- a/src/private/mx/core/generated/AccidentalMark.h +++ b/src/private/mx/core/generated/AccidentalMark.h @@ -27,6 +27,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// An accidental-mark can be used as a separate notation or as part of an ornament. When used in an /// ornament, position and placement are relative to the ornament, not relative to the note. class AccidentalMark final @@ -85,9 +87,9 @@ class AccidentalMark final AccidentalValue m_value{}; }; -AccidentalMark parseAccidentalMark(pugi::xml_node el); +AccidentalMark parseAccidentalMark(pugi::xml_node el, const ParseContext &context); -void parseAccidentalMarkContent(AccidentalMark &out, pugi::xml_node el); +void parseAccidentalMarkContent(AccidentalMark &out, pugi::xml_node el, const ParseContext &context); void serializeAccidentalMark(const AccidentalMark &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/AccidentalText.cpp b/src/private/mx/core/generated/AccidentalText.cpp index 44adff9a2..3cd3ef165 100644 --- a/src/private/mx/core/generated/AccidentalText.cpp +++ b/src/private/mx/core/generated/AccidentalText.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/AccidentalText.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -250,7 +251,7 @@ void AccidentalText::setValue(AccidentalValue value) m_value = std::move(value); } -AccidentalText parseAccidentalText(pugi::xml_node el) +AccidentalText parseAccidentalText(pugi::xml_node el, const ParseContext &context) { AccidentalText out; for (pugi::xml_attribute a : el.attributes()) @@ -262,7 +263,7 @@ AccidentalText parseAccidentalText(pugi::xml_node el) } if (aname == "smufl") { - out.setSmufl(SmuflAccidentalGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else if (aname == "xml:lang") { @@ -274,96 +275,96 @@ AccidentalText parseAccidentalText(pugi::xml_node el) } else if (aname == "justify") { - out.setJustify(LeftCenterRight::parse(a.value())); + out.setJustify(parseValue(a.value(), context, el, "justify")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "underline") { - out.setUnderline(NumberOfLines::parse(a.value())); + out.setUnderline(parseValue(a.value(), context, el, "underline")); } else if (aname == "overline") { - out.setOverline(NumberOfLines::parse(a.value())); + out.setOverline(parseValue(a.value(), context, el, "overline")); } else if (aname == "line-through") { - out.setLineThrough(NumberOfLines::parse(a.value())); + out.setLineThrough(parseValue(a.value(), context, el, "line-through")); } else if (aname == "rotation") { - out.setRotation(RotationDegrees::parse(a.value())); + out.setRotation(parseValue(a.value(), context, el, "rotation")); } else if (aname == "letter-spacing") { - out.setLetterSpacing(NumberOrNormal::parse(a.value())); + out.setLetterSpacing(parseValue(a.value(), context, el, "letter-spacing")); } else if (aname == "line-height") { - out.setLineHeight(NumberOrNormal::parse(a.value())); + out.setLineHeight(parseValue(a.value(), context, el, "line-height")); } else if (aname == "dir") { - out.setDir(TextDirection::parse(a.value())); + out.setDir(parseValue(a.value(), context, el, "dir")); } else if (aname == "enclosure") { - out.setEnclosure(EnclosureShape::parse(a.value())); + out.setEnclosure(parseValue(a.value(), context, el, "enclosure")); } else { throwUnknownAttribute(el, a.name()); } } - parseAccidentalTextContent(out, el); + parseAccidentalTextContent(out, el, context); return out; } -void parseAccidentalTextContent(AccidentalText &out, pugi::xml_node el) +void parseAccidentalTextContent(AccidentalText &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(AccidentalValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/AccidentalText.h b/src/private/mx/core/generated/AccidentalText.h index 19934a7db..041e1301d 100644 --- a/src/private/mx/core/generated/AccidentalText.h +++ b/src/private/mx/core/generated/AccidentalText.h @@ -29,6 +29,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The accidental-text type represents an element with an accidental value and text-formatting /// attributes. class AccidentalText final @@ -111,9 +113,9 @@ class AccidentalText final AccidentalValue m_value{}; }; -AccidentalText parseAccidentalText(pugi::xml_node el); +AccidentalText parseAccidentalText(pugi::xml_node el, const ParseContext &context); -void parseAccidentalTextContent(AccidentalText &out, pugi::xml_node el); +void parseAccidentalTextContent(AccidentalText &out, pugi::xml_node el, const ParseContext &context); void serializeAccidentalText(const AccidentalText &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/AccidentalValue.cpp b/src/private/mx/core/generated/AccidentalValue.cpp index 08eea51b2..713b461bc 100644 --- a/src/private/mx/core/generated/AccidentalValue.cpp +++ b/src/private/mx/core/generated/AccidentalValue.cpp @@ -276,9 +276,15 @@ bool AccidentalValue::tryParse(std::string_view text, AccidentalValue &out) noex } AccidentalValue AccidentalValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +AccidentalValue AccidentalValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { AccidentalValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/AccidentalValue.h b/src/private/mx/core/generated/AccidentalValue.h index a7b6c6748..e89d21f80 100644 --- a/src/private/mx/core/generated/AccidentalValue.h +++ b/src/private/mx/core/generated/AccidentalValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -128,6 +130,9 @@ class AccidentalValue final /// (the import leniency policy; never produces an invalid value). static AccidentalValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static AccidentalValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const AccidentalValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Accord.cpp b/src/private/mx/core/generated/Accord.cpp index 059974d57..8d7aff016 100644 --- a/src/private/mx/core/generated/Accord.cpp +++ b/src/private/mx/core/generated/Accord.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Accord.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Accord::setTuning(TuningGroup value) m_tuning = std::move(value); } -Accord parseAccord(pugi::xml_node el) +Accord parseAccord(pugi::xml_node el, const ParseContext &context) { Accord out; for (pugi::xml_attribute a : el.attributes()) @@ -42,23 +43,23 @@ Accord parseAccord(pugi::xml_node el) } if (aname == "string") { - out.setString(StringNumber::parse(a.value())); + out.setString(parseValue(a.value(), context, el, "string")); } else { throwUnknownAttribute(el, a.name()); } } - parseAccordContent(out, el); + parseAccordContent(out, el, context); return out; } -void parseAccordContent(Accord &out, pugi::xml_node el) +void parseAccordContent(Accord &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "tuning-step"))) { - out.setTuning(parseTuningGroup(el, cursor)); + out.setTuning(parseTuningGroup(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/Accord.h b/src/private/mx/core/generated/Accord.h index 0f13c4e14..b6a6696c5 100644 --- a/src/private/mx/core/generated/Accord.h +++ b/src/private/mx/core/generated/Accord.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The accord type represents the tuning of a single string in the scordatura element. It uses the /// same group of elements as the staff-tuning element. Strings are numbered from high to low. /// Content fields mirror the schema grammar in declaration order; the @@ -36,9 +38,9 @@ class Accord final TuningGroup m_tuning{}; }; -Accord parseAccord(pugi::xml_node el); +Accord parseAccord(pugi::xml_node el, const ParseContext &context); -void parseAccordContent(Accord &out, pugi::xml_node el); +void parseAccordContent(Accord &out, pugi::xml_node el, const ParseContext &context); void serializeAccord(const Accord &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/AccordionMiddle.cpp b/src/private/mx/core/generated/AccordionMiddle.cpp index 03c0dde6d..618d74275 100644 --- a/src/private/mx/core/generated/AccordionMiddle.cpp +++ b/src/private/mx/core/generated/AccordionMiddle.cpp @@ -42,20 +42,33 @@ std::string AccordionMiddle::toString() const bool AccordionMiddle::tryParse(std::string_view text, AccordionMiddle &out) { - int v{}; - if (!tryParseInt(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + AccordionMiddle parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = AccordionMiddle{v}; + out = std::move(parsed); return true; } AccordionMiddle AccordionMiddle::parse(std::string_view text) { - AccordionMiddle v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +AccordionMiddle AccordionMiddle::parse(std::string_view text, ValueParseOutcome &outcome) +{ + int v{}; + if (!tryParseInt(text, v)) + { + outcome = ValueParseOutcome::invalid; + return AccordionMiddle{}; + } + AccordionMiddle out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/AccordionMiddle.h b/src/private/mx/core/generated/AccordionMiddle.h index 83e0ae95e..87faf5d34 100644 --- a/src/private/mx/core/generated/AccordionMiddle.h +++ b/src/private/mx/core/generated/AccordionMiddle.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -37,6 +39,9 @@ class AccordionMiddle final /// Lenient: non-numeric text yields the clamped zero. static AccordionMiddle parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static AccordionMiddle parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const AccordionMiddle &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/AccordionRegistration.cpp b/src/private/mx/core/generated/AccordionRegistration.cpp index f3f25f5af..c1396f048 100644 --- a/src/private/mx/core/generated/AccordionRegistration.cpp +++ b/src/private/mx/core/generated/AccordionRegistration.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/AccordionRegistration.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -160,7 +161,7 @@ void AccordionRegistration::setAccordionLow(bool value) noexcept m_accordionLow = value; } -AccordionRegistration parseAccordionRegistration(pugi::xml_node el) +AccordionRegistration parseAccordionRegistration(pugi::xml_node el, const ParseContext &context) { AccordionRegistration out; for (pugi::xml_attribute a : el.attributes()) @@ -172,78 +173,78 @@ AccordionRegistration parseAccordionRegistration(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseAccordionRegistrationContent(out, el); + parseAccordionRegistrationContent(out, el, context); return out; } -void parseAccordionRegistrationContent(AccordionRegistration &out, pugi::xml_node el) +void parseAccordionRegistrationContent(AccordionRegistration &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "accordion-high")) { - parseEmpty(cursor); + parseEmpty(cursor, context); out.setAccordionHigh(true); cursor = nextElement(cursor); } if (cursorIs(cursor, "accordion-middle")) { - out.setAccordionMiddle(AccordionMiddle::parse(childText(cursor))); + out.setAccordionMiddle(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "accordion-low")) { - parseEmpty(cursor); + parseEmpty(cursor, context); out.setAccordionLow(true); cursor = nextElement(cursor); } diff --git a/src/private/mx/core/generated/AccordionRegistration.h b/src/private/mx/core/generated/AccordionRegistration.h index c026e6bf2..be151fd46 100644 --- a/src/private/mx/core/generated/AccordionRegistration.h +++ b/src/private/mx/core/generated/AccordionRegistration.h @@ -28,6 +28,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The accordion-registration type is used for accordion registration symbols. These are circular /// symbols divided horizontally into high, middle, and low sections that correspond to 4', 8', and /// 16' pipes. Each accordion-high, accordion-middle, and accordion-low element represents the @@ -88,9 +90,9 @@ class AccordionRegistration final bool m_accordionLow{false}; }; -AccordionRegistration parseAccordionRegistration(pugi::xml_node el); +AccordionRegistration parseAccordionRegistration(pugi::xml_node el, const ParseContext &context); -void parseAccordionRegistrationContent(AccordionRegistration &out, pugi::xml_node el); +void parseAccordionRegistrationContent(AccordionRegistration &out, pugi::xml_node el, const ParseContext &context); void serializeAccordionRegistration(const AccordionRegistration &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/AllMarginsGroup.cpp b/src/private/mx/core/generated/AllMarginsGroup.cpp index 56a0afa20..82919bddf 100644 --- a/src/private/mx/core/generated/AllMarginsGroup.cpp +++ b/src/private/mx/core/generated/AllMarginsGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/AllMarginsGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,12 +41,12 @@ void AllMarginsGroup::setBottomMargin(Tenths value) m_bottomMargin = std::move(value); } -AllMarginsGroup parseAllMarginsGroup(pugi::xml_node el, pugi::xml_node &cursor) +AllMarginsGroup parseAllMarginsGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { AllMarginsGroup out; if (cursor && (cursorIs(cursor, "left-margin"))) { - out.setLeftRightMargins(parseLeftRightMarginsGroup(el, cursor)); + out.setLeftRightMargins(parseLeftRightMarginsGroup(el, cursor, context)); } else { @@ -53,7 +54,7 @@ AllMarginsGroup parseAllMarginsGroup(pugi::xml_node el, pugi::xml_node &cursor) } if (cursorIs(cursor, "top-margin")) { - out.setTopMargin(Tenths::parse(childText(cursor))); + out.setTopMargin(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -62,7 +63,7 @@ AllMarginsGroup parseAllMarginsGroup(pugi::xml_node el, pugi::xml_node &cursor) } if (cursorIs(cursor, "bottom-margin")) { - out.setBottomMargin(Tenths::parse(childText(cursor))); + out.setBottomMargin(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/AllMarginsGroup.h b/src/private/mx/core/generated/AllMarginsGroup.h index 856e62ba8..7fb4ad86b 100644 --- a/src/private/mx/core/generated/AllMarginsGroup.h +++ b/src/private/mx/core/generated/AllMarginsGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The all-margins group specifies both horizontal and vertical margins in tenths. /// A shared content group: transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -40,7 +42,7 @@ class AllMarginsGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -AllMarginsGroup parseAllMarginsGroup(pugi::xml_node el, pugi::xml_node &cursor); +AllMarginsGroup parseAllMarginsGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeAllMarginsGroup(const AllMarginsGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Appearance.cpp b/src/private/mx/core/generated/Appearance.cpp index 3eaf8c7e8..f4eaf43cd 100644 --- a/src/private/mx/core/generated/Appearance.cpp +++ b/src/private/mx/core/generated/Appearance.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Appearance.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -85,7 +86,7 @@ void Appearance::setOtherAppearance(std::vector value) m_otherAppearance = std::move(value); } -Appearance parseAppearance(pugi::xml_node el) +Appearance parseAppearance(pugi::xml_node el, const ParseContext &context) { Appearance out; for (pugi::xml_attribute a : el.attributes()) @@ -97,36 +98,36 @@ Appearance parseAppearance(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseAppearanceContent(out, el); + parseAppearanceContent(out, el, context); return out; } -void parseAppearanceContent(Appearance &out, pugi::xml_node el) +void parseAppearanceContent(Appearance &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); while (cursorIs(cursor, "line-width")) { - out.addLineWidth(parseLineWidth(cursor)); + out.addLineWidth(parseLineWidth(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "note-size")) { - out.addNoteSize(parseNoteSize(cursor)); + out.addNoteSize(parseNoteSize(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "distance")) { - out.addDistance(parseDistance(cursor)); + out.addDistance(parseDistance(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "glyph")) { - out.addGlyph(parseGlyph(cursor)); + out.addGlyph(parseGlyph(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "other-appearance")) { - out.addOtherAppearance(parseOtherAppearance(cursor)); + out.addOtherAppearance(parseOtherAppearance(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Appearance.h b/src/private/mx/core/generated/Appearance.h index 746125608..bee9bb31a 100644 --- a/src/private/mx/core/generated/Appearance.h +++ b/src/private/mx/core/generated/Appearance.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The appearance type controls general graphical settings for the music's final form appearance on /// a printed page of display. This includes support for line widths, definitions for note sizes, and /// standard distances between notation elements, plus an extension element for other aspects of @@ -55,9 +57,9 @@ class Appearance final std::vector m_otherAppearance; }; -Appearance parseAppearance(pugi::xml_node el); +Appearance parseAppearance(pugi::xml_node el, const ParseContext &context); -void parseAppearanceContent(Appearance &out, pugi::xml_node el); +void parseAppearanceContent(Appearance &out, pugi::xml_node el, const ParseContext &context); void serializeAppearance(const Appearance &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Arpeggiate.cpp b/src/private/mx/core/generated/Arpeggiate.cpp index c75269602..0a0a82260 100644 --- a/src/private/mx/core/generated/Arpeggiate.cpp +++ b/src/private/mx/core/generated/Arpeggiate.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Arpeggiate.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -110,7 +111,7 @@ void Arpeggiate::setID(std::optional value) m_id = std::move(value); } -Arpeggiate parseArpeggiate(pugi::xml_node el) +Arpeggiate parseArpeggiate(pugi::xml_node el, const ParseContext &context) { Arpeggiate out; for (pugi::xml_attribute a : el.attributes()) @@ -122,56 +123,57 @@ Arpeggiate parseArpeggiate(pugi::xml_node el) } if (aname == "number") { - out.setNumber(NumberLevel::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "direction") { - out.setDirection(UpDown::parse(a.value())); + out.setDirection(parseValue(a.value(), context, el, "direction")); } else if (aname == "unbroken") { - out.setUnbroken(YesNo::parse(a.value())); + out.setUnbroken(parseValue(a.value(), context, el, "unbroken")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseArpeggiateContent(out, el); + parseArpeggiateContent(out, el, context); return out; } -void parseArpeggiateContent(Arpeggiate &out, pugi::xml_node el) +void parseArpeggiateContent(Arpeggiate &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Arpeggiate.h b/src/private/mx/core/generated/Arpeggiate.h index 745df1f11..c698a4f36 100644 --- a/src/private/mx/core/generated/Arpeggiate.h +++ b/src/private/mx/core/generated/Arpeggiate.h @@ -22,6 +22,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The arpeggiate type indicates that this note is part of an arpeggiated chord. The number /// attribute can be used to distinguish between two simultaneous chords arpeggiated separately /// (different numbers) or together (same number). The direction attribute is used if there is an @@ -68,9 +70,9 @@ class Arpeggiate final std::optional m_id; }; -Arpeggiate parseArpeggiate(pugi::xml_node el); +Arpeggiate parseArpeggiate(pugi::xml_node el, const ParseContext &context); -void parseArpeggiateContent(Arpeggiate &out, pugi::xml_node el); +void parseArpeggiateContent(Arpeggiate &out, pugi::xml_node el, const ParseContext &context); void serializeArpeggiate(const Arpeggiate &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Arrow.cpp b/src/private/mx/core/generated/Arrow.cpp index 8750566b3..19f6b44da 100644 --- a/src/private/mx/core/generated/Arrow.cpp +++ b/src/private/mx/core/generated/Arrow.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Arrow.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -130,7 +131,7 @@ void Arrow::setChoice(ArrowChoice value) m_choice = std::move(value); } -Arrow parseArrow(pugi::xml_node el) +Arrow parseArrow(pugi::xml_node el, const ParseContext &context) { Arrow out; for (pugi::xml_attribute a : el.attributes()) @@ -142,63 +143,63 @@ Arrow parseArrow(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "smufl") { - out.setSmufl(SmuflGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else { throwUnknownAttribute(el, a.name()); } } - parseArrowContent(out, el); + parseArrowContent(out, el, context); return out; } -void parseArrowContent(Arrow &out, pugi::xml_node el) +void parseArrowContent(Arrow &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "arrow-direction") || cursorIs(cursor, "circular-arrow"))) { - out.setChoice(parseArrowChoice(el, cursor)); + out.setChoice(parseArrowChoice(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/Arrow.h b/src/private/mx/core/generated/Arrow.h index a4281ee2e..23afb1c71 100644 --- a/src/private/mx/core/generated/Arrow.h +++ b/src/private/mx/core/generated/Arrow.h @@ -25,6 +25,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The arrow element represents an arrow used for a musical technical indication. It can represent /// both Unicode and SMuFL arrows. The presence of an arrowhead element indicates that only the /// arrowhead is displayed, not the arrow stem. The smufl attribute distinguishes different SMuFL @@ -76,9 +78,9 @@ class Arrow final ArrowChoice m_choice{}; }; -Arrow parseArrow(pugi::xml_node el); +Arrow parseArrow(pugi::xml_node el, const ParseContext &context); -void parseArrowContent(Arrow &out, pugi::xml_node el); +void parseArrowContent(Arrow &out, pugi::xml_node el, const ParseContext &context); void serializeArrow(const Arrow &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/ArrowChoice.cpp b/src/private/mx/core/generated/ArrowChoice.cpp index 87e13198f..061724740 100644 --- a/src/private/mx/core/generated/ArrowChoice.cpp +++ b/src/private/mx/core/generated/ArrowChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/ArrowChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,15 +26,15 @@ ArrowChoice ArrowChoice::circularArrow(CircularArrow value) return ArrowChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -ArrowChoice parseArrowChoice(pugi::xml_node el, pugi::xml_node &cursor) +ArrowChoice parseArrowChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "arrow-direction"))) { - return ArrowChoice::group(parseArrowChoiceGroup(el, cursor)); + return ArrowChoice::group(parseArrowChoiceGroup(el, cursor, context)); } if (cursor && (cursorIs(cursor, "circular-arrow"))) { - CircularArrow value = CircularArrow::parse(childText(cursor)); + CircularArrow value = parseValue(childText(cursor), context, cursor, nullptr); cursor = nextElement(cursor); return ArrowChoice::circularArrow(std::move(value)); } diff --git a/src/private/mx/core/generated/ArrowChoice.h b/src/private/mx/core/generated/ArrowChoice.h index bbc6b4c1d..7e0b17d39 100644 --- a/src/private/mx/core/generated/ArrowChoice.h +++ b/src/private/mx/core/generated/ArrowChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -80,7 +82,7 @@ class ArrowChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -ArrowChoice parseArrowChoice(pugi::xml_node el, pugi::xml_node &cursor); +ArrowChoice parseArrowChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeArrowChoice(const ArrowChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/ArrowChoiceGroup.cpp b/src/private/mx/core/generated/ArrowChoiceGroup.cpp index 22137a5ef..2fd6322ed 100644 --- a/src/private/mx/core/generated/ArrowChoiceGroup.cpp +++ b/src/private/mx/core/generated/ArrowChoiceGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/ArrowChoiceGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,12 +41,12 @@ void ArrowChoiceGroup::setArrowhead(bool value) noexcept m_arrowhead = value; } -ArrowChoiceGroup parseArrowChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor) +ArrowChoiceGroup parseArrowChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { ArrowChoiceGroup out; if (cursorIs(cursor, "arrow-direction")) { - out.setArrowDirection(ArrowDirection::parse(childText(cursor))); + out.setArrowDirection(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -54,12 +55,12 @@ ArrowChoiceGroup parseArrowChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor } if (cursorIs(cursor, "arrow-style")) { - out.setArrowStyle(ArrowStyle::parse(childText(cursor))); + out.setArrowStyle(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "arrowhead")) { - parseEmpty(cursor); + parseEmpty(cursor, context); out.setArrowhead(true); cursor = nextElement(cursor); } diff --git a/src/private/mx/core/generated/ArrowChoiceGroup.h b/src/private/mx/core/generated/ArrowChoiceGroup.h index a93629ff6..4718780b9 100644 --- a/src/private/mx/core/generated/ArrowChoiceGroup.h +++ b/src/private/mx/core/generated/ArrowChoiceGroup.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -41,7 +43,7 @@ class ArrowChoiceGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -ArrowChoiceGroup parseArrowChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor); +ArrowChoiceGroup parseArrowChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeArrowChoiceGroup(const ArrowChoiceGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/ArrowDirection.cpp b/src/private/mx/core/generated/ArrowDirection.cpp index 680f73192..478b33117 100644 --- a/src/private/mx/core/generated/ArrowDirection.cpp +++ b/src/private/mx/core/generated/ArrowDirection.cpp @@ -98,9 +98,15 @@ bool ArrowDirection::tryParse(std::string_view text, ArrowDirection &out) noexce } ArrowDirection ArrowDirection::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +ArrowDirection ArrowDirection::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { ArrowDirection v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/ArrowDirection.h b/src/private/mx/core/generated/ArrowDirection.h index 35aed9b42..d28b782ae 100644 --- a/src/private/mx/core/generated/ArrowDirection.h +++ b/src/private/mx/core/generated/ArrowDirection.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -63,6 +65,9 @@ class ArrowDirection final /// (the import leniency policy; never produces an invalid value). static ArrowDirection parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static ArrowDirection parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const ArrowDirection &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/ArrowStyle.cpp b/src/private/mx/core/generated/ArrowStyle.cpp index dbe9a8636..86bda74f3 100644 --- a/src/private/mx/core/generated/ArrowStyle.cpp +++ b/src/private/mx/core/generated/ArrowStyle.cpp @@ -66,9 +66,15 @@ bool ArrowStyle::tryParse(std::string_view text, ArrowStyle &out) noexcept } ArrowStyle ArrowStyle::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +ArrowStyle ArrowStyle::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { ArrowStyle v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/ArrowStyle.h b/src/private/mx/core/generated/ArrowStyle.h index 096f01015..26bb2fb0b 100644 --- a/src/private/mx/core/generated/ArrowStyle.h +++ b/src/private/mx/core/generated/ArrowStyle.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -53,6 +55,9 @@ class ArrowStyle final /// (the import leniency policy; never produces an invalid value). static ArrowStyle parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static ArrowStyle parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const ArrowStyle &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Articulations.cpp b/src/private/mx/core/generated/Articulations.cpp index b815920b9..3684e12fe 100644 --- a/src/private/mx/core/generated/Articulations.cpp +++ b/src/private/mx/core/generated/Articulations.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Articulations.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,7 +36,7 @@ void Articulations::setChoice(std::vector value) m_choice = std::move(value); } -Articulations parseArticulations(pugi::xml_node el) +Articulations parseArticulations(pugi::xml_node el, const ParseContext &context) { Articulations out; for (pugi::xml_attribute a : el.attributes()) @@ -47,18 +48,18 @@ Articulations parseArticulations(pugi::xml_node el) } if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseArticulationsContent(out, el); + parseArticulationsContent(out, el, context); return out; } -void parseArticulationsContent(Articulations &out, pugi::xml_node el) +void parseArticulationsContent(Articulations &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); while (cursor && @@ -69,7 +70,7 @@ void parseArticulationsContent(Articulations &out, pugi::xml_node el) cursorIs(cursor, "caesura") || cursorIs(cursor, "stress") || cursorIs(cursor, "unstress") || cursorIs(cursor, "soft-accent") || cursorIs(cursor, "other-articulation"))) { - out.addChoice(parseArticulationsChoice(el, cursor)); + out.addChoice(parseArticulationsChoice(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/Articulations.h b/src/private/mx/core/generated/Articulations.h index 66e38f44c..d05eae7f5 100644 --- a/src/private/mx/core/generated/Articulations.h +++ b/src/private/mx/core/generated/Articulations.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Articulations and accents are grouped together here. /// Content fields mirror the schema grammar in declaration order; the /// serializer walks them, so a wrong order is unrepresentable (plan §2.3). @@ -37,9 +39,9 @@ class Articulations final std::vector m_choice; }; -Articulations parseArticulations(pugi::xml_node el); +Articulations parseArticulations(pugi::xml_node el, const ParseContext &context); -void parseArticulationsContent(Articulations &out, pugi::xml_node el); +void parseArticulationsContent(Articulations &out, pugi::xml_node el, const ParseContext &context); void serializeArticulations(const Articulations &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/ArticulationsChoice.cpp b/src/private/mx/core/generated/ArticulationsChoice.cpp index 511088cf0..130f9f20f 100644 --- a/src/private/mx/core/generated/ArticulationsChoice.cpp +++ b/src/private/mx/core/generated/ArticulationsChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/ArticulationsChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -100,107 +101,107 @@ ArticulationsChoice ArticulationsChoice::otherArticulation(OtherPlacementText va return ArticulationsChoice{Storage{std::in_place_index<16>, std::move(value)}}; } -ArticulationsChoice parseArticulationsChoice(pugi::xml_node el, pugi::xml_node &cursor) +ArticulationsChoice parseArticulationsChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "accent"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return ArticulationsChoice::accent(std::move(value)); } if (cursor && (cursorIs(cursor, "strong-accent"))) { - StrongAccent value = parseStrongAccent(cursor); + StrongAccent value = parseStrongAccent(cursor, context); cursor = nextElement(cursor); return ArticulationsChoice::strongAccent(std::move(value)); } if (cursor && (cursorIs(cursor, "staccato"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return ArticulationsChoice::staccato(std::move(value)); } if (cursor && (cursorIs(cursor, "tenuto"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return ArticulationsChoice::tenuto(std::move(value)); } if (cursor && (cursorIs(cursor, "detached-legato"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return ArticulationsChoice::detachedLegato(std::move(value)); } if (cursor && (cursorIs(cursor, "staccatissimo"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return ArticulationsChoice::staccatissimo(std::move(value)); } if (cursor && (cursorIs(cursor, "spiccato"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return ArticulationsChoice::spiccato(std::move(value)); } if (cursor && (cursorIs(cursor, "scoop"))) { - EmptyLine value = parseEmptyLine(cursor); + EmptyLine value = parseEmptyLine(cursor, context); cursor = nextElement(cursor); return ArticulationsChoice::scoop(std::move(value)); } if (cursor && (cursorIs(cursor, "plop"))) { - EmptyLine value = parseEmptyLine(cursor); + EmptyLine value = parseEmptyLine(cursor, context); cursor = nextElement(cursor); return ArticulationsChoice::plop(std::move(value)); } if (cursor && (cursorIs(cursor, "doit"))) { - EmptyLine value = parseEmptyLine(cursor); + EmptyLine value = parseEmptyLine(cursor, context); cursor = nextElement(cursor); return ArticulationsChoice::doit(std::move(value)); } if (cursor && (cursorIs(cursor, "falloff"))) { - EmptyLine value = parseEmptyLine(cursor); + EmptyLine value = parseEmptyLine(cursor, context); cursor = nextElement(cursor); return ArticulationsChoice::falloff(std::move(value)); } if (cursor && (cursorIs(cursor, "breath-mark"))) { - BreathMark value = parseBreathMark(cursor); + BreathMark value = parseBreathMark(cursor, context); cursor = nextElement(cursor); return ArticulationsChoice::breathMark(std::move(value)); } if (cursor && (cursorIs(cursor, "caesura"))) { - Caesura value = parseCaesura(cursor); + Caesura value = parseCaesura(cursor, context); cursor = nextElement(cursor); return ArticulationsChoice::caesura(std::move(value)); } if (cursor && (cursorIs(cursor, "stress"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return ArticulationsChoice::stress(std::move(value)); } if (cursor && (cursorIs(cursor, "unstress"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return ArticulationsChoice::unstress(std::move(value)); } if (cursor && (cursorIs(cursor, "soft-accent"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return ArticulationsChoice::softAccent(std::move(value)); } if (cursor && (cursorIs(cursor, "other-articulation"))) { - OtherPlacementText value = parseOtherPlacementText(cursor); + OtherPlacementText value = parseOtherPlacementText(cursor, context); cursor = nextElement(cursor); return ArticulationsChoice::otherArticulation(std::move(value)); } diff --git a/src/private/mx/core/generated/ArticulationsChoice.h b/src/private/mx/core/generated/ArticulationsChoice.h index bd78abce8..4337fc225 100644 --- a/src/private/mx/core/generated/ArticulationsChoice.h +++ b/src/private/mx/core/generated/ArticulationsChoice.h @@ -22,6 +22,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -296,7 +298,7 @@ class ArticulationsChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -ArticulationsChoice parseArticulationsChoice(pugi::xml_node el, pugi::xml_node &cursor); +ArticulationsChoice parseArticulationsChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeArticulationsChoice(const ArticulationsChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Assess.cpp b/src/private/mx/core/generated/Assess.cpp index 797da221f..0610d696a 100644 --- a/src/private/mx/core/generated/Assess.cpp +++ b/src/private/mx/core/generated/Assess.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Assess.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,7 +41,7 @@ void Assess::setTimeOnly(std::optional value) m_timeOnly = std::move(value); } -Assess parseAssess(pugi::xml_node el) +Assess parseAssess(pugi::xml_node el, const ParseContext &context) { Assess out; bool seen_type = false; @@ -54,15 +55,15 @@ Assess parseAssess(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(YesNo::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "player") { - out.setPlayer(Token::parse(a.value())); + out.setPlayer(parseValue(a.value(), context, el, "player")); } else if (aname == "time-only") { - out.setTimeOnly(TimeOnly::parse(a.value())); + out.setTimeOnly(parseValue(a.value(), context, el, "time-only")); } else { @@ -73,13 +74,14 @@ Assess parseAssess(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseAssessContent(out, el); + parseAssessContent(out, el, context); return out; } -void parseAssessContent(Assess &out, pugi::xml_node el) +void parseAssessContent(Assess &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Assess.h b/src/private/mx/core/generated/Assess.h index 3a7eb0f3d..74d0b9448 100644 --- a/src/private/mx/core/generated/Assess.h +++ b/src/private/mx/core/generated/Assess.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// By default, an assessment application should assess all notes without a cue child element, and /// not assess any note with a cue child element. The assess type allows this default assessment to /// be overridden for individual notes. The optional player and time-only attributes restrict the @@ -42,9 +44,9 @@ class Assess final std::optional m_timeOnly; }; -Assess parseAssess(pugi::xml_node el); +Assess parseAssess(pugi::xml_node el, const ParseContext &context); -void parseAssessContent(Assess &out, pugi::xml_node el); +void parseAssessContent(Assess &out, pugi::xml_node el, const ParseContext &context); void serializeAssess(const Assess &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Attributes.cpp b/src/private/mx/core/generated/Attributes.cpp index df236f925..0a1619c6a 100644 --- a/src/private/mx/core/generated/Attributes.cpp +++ b/src/private/mx/core/generated/Attributes.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Attributes.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -160,7 +161,7 @@ void Attributes::setMeasureStyle(std::vector value) m_measureStyle = std::move(value); } -Attributes parseAttributes(pugi::xml_node el) +Attributes parseAttributes(pugi::xml_node el, const ParseContext &context) { Attributes out; for (pugi::xml_attribute a : el.attributes()) @@ -172,69 +173,69 @@ Attributes parseAttributes(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseAttributesContent(out, el); + parseAttributesContent(out, el, context); return out; } -void parseAttributesContent(Attributes &out, pugi::xml_node el) +void parseAttributesContent(Attributes &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "footnote") || cursorIs(cursor, "level"))) { - out.setEditorial(parseEditorialGroup(el, cursor)); + out.setEditorial(parseEditorialGroup(el, cursor, context)); } if (cursorIs(cursor, "divisions")) { - out.setDivisions(PositiveDivisions::parse(childText(cursor))); + out.setDivisions(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } while (cursorIs(cursor, "key")) { - out.addKey(parseKey(cursor)); + out.addKey(parseKey(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "time")) { - out.addTime(parseTime(cursor)); + out.addTime(parseTime(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "staves")) { - out.setStaves(parseInt(childText(cursor))); + out.setStaves(parseIntegerValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "part-symbol")) { - out.setPartSymbol(parsePartSymbol(cursor)); + out.setPartSymbol(parsePartSymbol(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "instruments")) { - out.setInstruments(parseInt(childText(cursor))); + out.setInstruments(parseIntegerValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } while (cursorIs(cursor, "clef")) { - out.addClef(parseClef(cursor)); + out.addClef(parseClef(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "staff-details")) { - out.addStaffDetails(parseStaffDetails(cursor)); + out.addStaffDetails(parseStaffDetails(cursor, context)); cursor = nextElement(cursor); } if (cursor && (cursorIs(cursor, "transpose") || cursorIs(cursor, "for-part"))) { - out.setChoice(parseAttributesChoice(el, cursor)); + out.setChoice(parseAttributesChoice(el, cursor, context)); } while (cursorIs(cursor, "directive")) { - out.addDirective(parseDirective(cursor)); + out.addDirective(parseDirective(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "measure-style")) { - out.addMeasureStyle(parseMeasureStyle(cursor)); + out.addMeasureStyle(parseMeasureStyle(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Attributes.h b/src/private/mx/core/generated/Attributes.h index d152a743e..f6ce152ae 100644 --- a/src/private/mx/core/generated/Attributes.h +++ b/src/private/mx/core/generated/Attributes.h @@ -26,6 +26,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The attributes element contains musical information that typically changes on measure boundaries. /// This includes key and time signatures, clefs, transpositions, and staving. When attributes are /// changed mid-measure, it affects the music in score order, not in MusicXML document order. @@ -81,9 +83,9 @@ class Attributes final std::vector m_measureStyle; }; -Attributes parseAttributes(pugi::xml_node el); +Attributes parseAttributes(pugi::xml_node el, const ParseContext &context); -void parseAttributesContent(Attributes &out, pugi::xml_node el); +void parseAttributesContent(Attributes &out, pugi::xml_node el, const ParseContext &context); void serializeAttributes(const Attributes &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/AttributesChoice.cpp b/src/private/mx/core/generated/AttributesChoice.cpp index 7f157d412..e43dbd8d1 100644 --- a/src/private/mx/core/generated/AttributesChoice.cpp +++ b/src/private/mx/core/generated/AttributesChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/AttributesChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,14 +26,14 @@ AttributesChoice AttributesChoice::forPart(std::vector value) return AttributesChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -AttributesChoice parseAttributesChoice(pugi::xml_node el, pugi::xml_node &cursor) +AttributesChoice parseAttributesChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "transpose"))) { std::vector items; while (cursor && (cursorIs(cursor, "transpose"))) { - items.push_back(parseTranspose(cursor)); + items.push_back(parseTranspose(cursor, context)); cursor = nextElement(cursor); } return AttributesChoice::transpose(std::move(items)); @@ -42,7 +43,7 @@ AttributesChoice parseAttributesChoice(pugi::xml_node el, pugi::xml_node &cursor std::vector items; while (cursor && (cursorIs(cursor, "for-part"))) { - items.push_back(parseForPart(cursor)); + items.push_back(parseForPart(cursor, context)); cursor = nextElement(cursor); } return AttributesChoice::forPart(std::move(items)); diff --git a/src/private/mx/core/generated/AttributesChoice.h b/src/private/mx/core/generated/AttributesChoice.h index 3634e367b..115545f8f 100644 --- a/src/private/mx/core/generated/AttributesChoice.h +++ b/src/private/mx/core/generated/AttributesChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -81,7 +83,7 @@ class AttributesChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -AttributesChoice parseAttributesChoice(pugi::xml_node el, pugi::xml_node &cursor); +AttributesChoice parseAttributesChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeAttributesChoice(const AttributesChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Backup.cpp b/src/private/mx/core/generated/Backup.cpp index 9f1066e94..f03df9153 100644 --- a/src/private/mx/core/generated/Backup.cpp +++ b/src/private/mx/core/generated/Backup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Backup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Backup::setEditorial(EditorialGroup value) m_editorial = std::move(value); } -Backup parseBackup(pugi::xml_node el) +Backup parseBackup(pugi::xml_node el, const ParseContext &context) { Backup out; for (pugi::xml_attribute a : el.attributes()) @@ -42,16 +43,16 @@ Backup parseBackup(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseBackupContent(out, el); + parseBackupContent(out, el, context); return out; } -void parseBackupContent(Backup &out, pugi::xml_node el) +void parseBackupContent(Backup &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "duration")) { - out.setDuration(PositiveDivisions::parse(childText(cursor))); + out.setDuration(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -60,7 +61,7 @@ void parseBackupContent(Backup &out, pugi::xml_node el) } if (cursor && (cursorIs(cursor, "footnote") || cursorIs(cursor, "level"))) { - out.setEditorial(parseEditorialGroup(el, cursor)); + out.setEditorial(parseEditorialGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/Backup.h b/src/private/mx/core/generated/Backup.h index 84919da0a..320a0362f 100644 --- a/src/private/mx/core/generated/Backup.h +++ b/src/private/mx/core/generated/Backup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The backup and forward elements are required to coordinate multiple voices in one part, including /// music on multiple staves. The backup type is generally used to move between voices and staves. /// Thus the backup element does not include voice or staff elements. Duration values should always @@ -39,9 +41,9 @@ class Backup final EditorialGroup m_editorial{}; }; -Backup parseBackup(pugi::xml_node el); +Backup parseBackup(pugi::xml_node el, const ParseContext &context); -void parseBackupContent(Backup &out, pugi::xml_node el); +void parseBackupContent(Backup &out, pugi::xml_node el, const ParseContext &context); void serializeBackup(const Backup &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/BackwardForward.cpp b/src/private/mx/core/generated/BackwardForward.cpp index 3fc85239f..fa73bb146 100644 --- a/src/private/mx/core/generated/BackwardForward.cpp +++ b/src/private/mx/core/generated/BackwardForward.cpp @@ -42,9 +42,15 @@ bool BackwardForward::tryParse(std::string_view text, BackwardForward &out) noex } BackwardForward BackwardForward::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +BackwardForward BackwardForward::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { BackwardForward v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/BackwardForward.h b/src/private/mx/core/generated/BackwardForward.h index ace778bda..a46adafa7 100644 --- a/src/private/mx/core/generated/BackwardForward.h +++ b/src/private/mx/core/generated/BackwardForward.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -41,6 +43,9 @@ class BackwardForward final /// (the import leniency policy; never produces an invalid value). static BackwardForward parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static BackwardForward parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const BackwardForward &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/BarStyle.cpp b/src/private/mx/core/generated/BarStyle.cpp index 91e0cdcda..89d462151 100644 --- a/src/private/mx/core/generated/BarStyle.cpp +++ b/src/private/mx/core/generated/BarStyle.cpp @@ -87,9 +87,15 @@ bool BarStyle::tryParse(std::string_view text, BarStyle &out) noexcept } BarStyle BarStyle::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +BarStyle BarStyle::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { BarStyle v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/BarStyle.h b/src/private/mx/core/generated/BarStyle.h index e4d226620..49879cd05 100644 --- a/src/private/mx/core/generated/BarStyle.h +++ b/src/private/mx/core/generated/BarStyle.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -60,6 +62,9 @@ class BarStyle final /// (the import leniency policy; never produces an invalid value). static BarStyle parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static BarStyle parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const BarStyle &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/BarStyleColor.cpp b/src/private/mx/core/generated/BarStyleColor.cpp index ba48d2758..f0e63fa2d 100644 --- a/src/private/mx/core/generated/BarStyleColor.cpp +++ b/src/private/mx/core/generated/BarStyleColor.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/BarStyleColor.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void BarStyleColor::setValue(BarStyle value) m_value = std::move(value); } -BarStyleColor parseBarStyleColor(pugi::xml_node el) +BarStyleColor parseBarStyleColor(pugi::xml_node el, const ParseContext &context) { BarStyleColor out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ BarStyleColor parseBarStyleColor(pugi::xml_node el) } if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseBarStyleColorContent(out, el); + parseBarStyleColorContent(out, el, context); return out; } -void parseBarStyleColorContent(BarStyleColor &out, pugi::xml_node el) +void parseBarStyleColorContent(BarStyleColor &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(BarStyle::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/BarStyleColor.h b/src/private/mx/core/generated/BarStyleColor.h index 1ade2024f..9743f706e 100644 --- a/src/private/mx/core/generated/BarStyleColor.h +++ b/src/private/mx/core/generated/BarStyleColor.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The bar-style-color type contains barline style and color information. class BarStyleColor final { @@ -31,9 +33,9 @@ class BarStyleColor final BarStyle m_value{}; }; -BarStyleColor parseBarStyleColor(pugi::xml_node el); +BarStyleColor parseBarStyleColor(pugi::xml_node el, const ParseContext &context); -void parseBarStyleColorContent(BarStyleColor &out, pugi::xml_node el); +void parseBarStyleColorContent(BarStyleColor &out, pugi::xml_node el, const ParseContext &context); void serializeBarStyleColor(const BarStyleColor &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Barline.cpp b/src/private/mx/core/generated/Barline.cpp index dec69309a..288d6b741 100644 --- a/src/private/mx/core/generated/Barline.cpp +++ b/src/private/mx/core/generated/Barline.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Barline.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -150,7 +151,7 @@ void Barline::setRepeat(std::optional value) m_repeat = std::move(value); } -Barline parseBarline(pugi::xml_node el) +Barline parseBarline(pugi::xml_node el, const ParseContext &context) { Barline out; for (pugi::xml_attribute a : el.attributes()) @@ -162,7 +163,7 @@ Barline parseBarline(pugi::xml_node el) } if (aname == "location") { - out.setLocation(RightLeftMiddle::parse(a.value())); + out.setLocation(parseValue(a.value(), context, el, "location")); } else if (aname == "segno") { @@ -174,51 +175,51 @@ Barline parseBarline(pugi::xml_node el) } else if (aname == "divisions") { - out.setDivisions(Divisions::parse(a.value())); + out.setDivisions(parseValue(a.value(), context, el, "divisions")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseBarlineContent(out, el); + parseBarlineContent(out, el, context); return out; } -void parseBarlineContent(Barline &out, pugi::xml_node el) +void parseBarlineContent(Barline &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "bar-style")) { - out.setBarStyle(parseBarStyleColor(cursor)); + out.setBarStyle(parseBarStyleColor(cursor, context)); cursor = nextElement(cursor); } if (cursor && (cursorIs(cursor, "footnote") || cursorIs(cursor, "level"))) { - out.setEditorial(parseEditorialGroup(el, cursor)); + out.setEditorial(parseEditorialGroup(el, cursor, context)); } if (cursorIs(cursor, "wavy-line")) { - out.setWavyLine(parseWavyLine(cursor)); + out.setWavyLine(parseWavyLine(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "segno")) { - out.setSegno(parseSegno(cursor)); + out.setSegno(parseSegno(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "coda")) { - out.setCoda(parseCoda(cursor)); + out.setCoda(parseCoda(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "fermata")) { - if (!out.addFermata(parseFermata(cursor))) + if (!out.addFermata(parseFermata(cursor, context))) { throwTooManyElements(cursor); } @@ -226,12 +227,12 @@ void parseBarlineContent(Barline &out, pugi::xml_node el) } if (cursorIs(cursor, "ending")) { - out.setEnding(parseEnding(cursor)); + out.setEnding(parseEnding(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "repeat")) { - out.setRepeat(parseRepeat(cursor)); + out.setRepeat(parseRepeat(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Barline.h b/src/private/mx/core/generated/Barline.h index d875910f2..ec2865294 100644 --- a/src/private/mx/core/generated/Barline.h +++ b/src/private/mx/core/generated/Barline.h @@ -29,6 +29,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// If a barline is other than a normal single barline, it should be represented by a barline type /// that describes it. This includes information about repeats and multiple endings, as well as line /// style. Barline data is on the same level as the other musical data in a score - a child of a @@ -96,9 +98,9 @@ class Barline final std::optional m_repeat; }; -Barline parseBarline(pugi::xml_node el); +Barline parseBarline(pugi::xml_node el, const ParseContext &context); -void parseBarlineContent(Barline &out, pugi::xml_node el); +void parseBarlineContent(Barline &out, pugi::xml_node el, const ParseContext &context); void serializeBarline(const Barline &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Barre.cpp b/src/private/mx/core/generated/Barre.cpp index 74cb9bdd4..4d0781d2a 100644 --- a/src/private/mx/core/generated/Barre.cpp +++ b/src/private/mx/core/generated/Barre.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Barre.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Barre::setColor(std::optional value) m_color = std::move(value); } -Barre parseBarre(pugi::xml_node el) +Barre parseBarre(pugi::xml_node el, const ParseContext &context) { Barre out; bool seen_type = false; @@ -44,11 +45,11 @@ Barre parseBarre(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStop::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { @@ -59,13 +60,14 @@ Barre parseBarre(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseBarreContent(out, el); + parseBarreContent(out, el, context); return out; } -void parseBarreContent(Barre &out, pugi::xml_node el) +void parseBarreContent(Barre &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Barre.h b/src/private/mx/core/generated/Barre.h index 14ad3f489..175e781c2 100644 --- a/src/private/mx/core/generated/Barre.h +++ b/src/private/mx/core/generated/Barre.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The barre element indicates placing a finger over multiple strings on a single fret. The type is /// "start" for the lowest pitched string (e.g., the string with the highest MusicXML number) and is /// "stop" for the highest pitched string. @@ -33,9 +35,9 @@ class Barre final std::optional m_color; }; -Barre parseBarre(pugi::xml_node el); +Barre parseBarre(pugi::xml_node el, const ParseContext &context); -void parseBarreContent(Barre &out, pugi::xml_node el); +void parseBarreContent(Barre &out, pugi::xml_node el, const ParseContext &context); void serializeBarre(const Barre &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Bass.cpp b/src/private/mx/core/generated/Bass.cpp index d9d9b1885..800dd1cac 100644 --- a/src/private/mx/core/generated/Bass.cpp +++ b/src/private/mx/core/generated/Bass.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Bass.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,7 +51,7 @@ void Bass::setBassAlter(std::optional value) m_bassAlter = std::move(value); } -Bass parseBass(pugi::xml_node el) +Bass parseBass(pugi::xml_node el, const ParseContext &context) { Bass out; for (pugi::xml_attribute a : el.attributes()) @@ -62,28 +63,28 @@ Bass parseBass(pugi::xml_node el) } if (aname == "arrangement") { - out.setArrangement(HarmonyArrangement::parse(a.value())); + out.setArrangement(parseValue(a.value(), context, el, "arrangement")); } else { throwUnknownAttribute(el, a.name()); } } - parseBassContent(out, el); + parseBassContent(out, el, context); return out; } -void parseBassContent(Bass &out, pugi::xml_node el) +void parseBassContent(Bass &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "bass-separator")) { - out.setBassSeparator(parseStyleText(cursor)); + out.setBassSeparator(parseStyleText(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "bass-step")) { - out.setBassStep(parseBassStep(cursor)); + out.setBassStep(parseBassStep(cursor, context)); cursor = nextElement(cursor); } else @@ -92,7 +93,7 @@ void parseBassContent(Bass &out, pugi::xml_node el) } if (cursorIs(cursor, "bass-alter")) { - out.setBassAlter(parseHarmonyAlter(cursor)); + out.setBassAlter(parseHarmonyAlter(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Bass.h b/src/private/mx/core/generated/Bass.h index 58193ed83..294315ff3 100644 --- a/src/private/mx/core/generated/Bass.h +++ b/src/private/mx/core/generated/Bass.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The bass type is used to indicate a bass note in popular music chord symbols, e.g. G/C. It is /// generally not used in functional harmony, as inversion is generally not used in pop chord /// symbols. As with root, it is divided into step and alter elements, similar to pitches. The @@ -46,9 +48,9 @@ class Bass final std::optional m_bassAlter; }; -Bass parseBass(pugi::xml_node el); +Bass parseBass(pugi::xml_node el, const ParseContext &context); -void parseBassContent(Bass &out, pugi::xml_node el); +void parseBassContent(Bass &out, pugi::xml_node el, const ParseContext &context); void serializeBass(const Bass &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/BassStep.cpp b/src/private/mx/core/generated/BassStep.cpp index fead3cfd3..2a3f2493a 100644 --- a/src/private/mx/core/generated/BassStep.cpp +++ b/src/private/mx/core/generated/BassStep.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/BassStep.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -120,7 +121,7 @@ void BassStep::setValue(Step value) m_value = std::move(value); } -BassStep parseBassStep(pugi::xml_node el) +BassStep parseBassStep(pugi::xml_node el, const ParseContext &context) { BassStep out; for (pugi::xml_attribute a : el.attributes()) @@ -136,52 +137,52 @@ BassStep parseBassStep(pugi::xml_node el) } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseBassStepContent(out, el); + parseBassStepContent(out, el, context); return out; } -void parseBassStepContent(BassStep &out, pugi::xml_node el) +void parseBassStepContent(BassStep &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(Step::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/BassStep.h b/src/private/mx/core/generated/BassStep.h index 14fae7540..05952b559 100644 --- a/src/private/mx/core/generated/BassStep.h +++ b/src/private/mx/core/generated/BassStep.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The bass-step type represents the pitch step of the bass of the current chord within the harmony /// element. The text attribute indicates how the bass should appear in a score if not using the /// element contents. @@ -65,9 +67,9 @@ class BassStep final Step m_value{}; }; -BassStep parseBassStep(pugi::xml_node el); +BassStep parseBassStep(pugi::xml_node el, const ParseContext &context); -void parseBassStepContent(BassStep &out, pugi::xml_node el); +void parseBassStepContent(BassStep &out, pugi::xml_node el, const ParseContext &context); void serializeBassStep(const BassStep &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Beam.cpp b/src/private/mx/core/generated/Beam.cpp index b3f5d3619..12237e9bf 100644 --- a/src/private/mx/core/generated/Beam.cpp +++ b/src/private/mx/core/generated/Beam.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Beam.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -70,7 +71,7 @@ void Beam::setValue(BeamValue value) m_value = std::move(value); } -Beam parseBeam(pugi::xml_node el) +Beam parseBeam(pugi::xml_node el, const ParseContext &context) { Beam out; for (pugi::xml_attribute a : el.attributes()) @@ -82,36 +83,36 @@ Beam parseBeam(pugi::xml_node el) } if (aname == "number") { - out.setNumber(BeamLevel::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "repeater") { - out.setRepeater(YesNo::parse(a.value())); + out.setRepeater(parseValue(a.value(), context, el, "repeater")); } else if (aname == "fan") { - out.setFan(Fan::parse(a.value())); + out.setFan(parseValue(a.value(), context, el, "fan")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseBeamContent(out, el); + parseBeamContent(out, el, context); return out; } -void parseBeamContent(Beam &out, pugi::xml_node el) +void parseBeamContent(Beam &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(BeamValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Beam.h b/src/private/mx/core/generated/Beam.h index 508173146..032c14949 100644 --- a/src/private/mx/core/generated/Beam.h +++ b/src/private/mx/core/generated/Beam.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Beam values include begin, continue, end, forward hook, and backward hook. Up to eight concurrent /// beams are available to cover up to 1024th notes. Each beam in a note is represented with a /// separate beam element, starting with the eighth note beam using a number attribute of 1. Note @@ -57,9 +59,9 @@ class Beam final BeamValue m_value{}; }; -Beam parseBeam(pugi::xml_node el); +Beam parseBeam(pugi::xml_node el, const ParseContext &context); -void parseBeamContent(Beam &out, pugi::xml_node el); +void parseBeamContent(Beam &out, pugi::xml_node el, const ParseContext &context); void serializeBeam(const Beam &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/BeamLevel.cpp b/src/private/mx/core/generated/BeamLevel.cpp index 21a0012e2..60b45f2e1 100644 --- a/src/private/mx/core/generated/BeamLevel.cpp +++ b/src/private/mx/core/generated/BeamLevel.cpp @@ -42,20 +42,33 @@ std::string BeamLevel::toString() const bool BeamLevel::tryParse(std::string_view text, BeamLevel &out) { - int v{}; - if (!tryParseInt(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + BeamLevel parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = BeamLevel{v}; + out = std::move(parsed); return true; } BeamLevel BeamLevel::parse(std::string_view text) { - BeamLevel v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +BeamLevel BeamLevel::parse(std::string_view text, ValueParseOutcome &outcome) +{ + int v{}; + if (!tryParseInt(text, v)) + { + outcome = ValueParseOutcome::invalid; + return BeamLevel{}; + } + BeamLevel out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/BeamLevel.h b/src/private/mx/core/generated/BeamLevel.h index 74a555fe4..afba0ccdb 100644 --- a/src/private/mx/core/generated/BeamLevel.h +++ b/src/private/mx/core/generated/BeamLevel.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -37,6 +39,9 @@ class BeamLevel final /// Lenient: non-numeric text yields the clamped zero. static BeamLevel parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static BeamLevel parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const BeamLevel &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/BeamValue.cpp b/src/private/mx/core/generated/BeamValue.cpp index 748b414ac..a868d5188 100644 --- a/src/private/mx/core/generated/BeamValue.cpp +++ b/src/private/mx/core/generated/BeamValue.cpp @@ -56,9 +56,15 @@ bool BeamValue::tryParse(std::string_view text, BeamValue &out) noexcept } BeamValue BeamValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +BeamValue BeamValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { BeamValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/BeamValue.h b/src/private/mx/core/generated/BeamValue.h index 824c8eb7e..eaffde993 100644 --- a/src/private/mx/core/generated/BeamValue.h +++ b/src/private/mx/core/generated/BeamValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -47,6 +49,9 @@ class BeamValue final /// (the import leniency policy; never produces an invalid value). static BeamValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static BeamValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const BeamValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/BeatRepeat.cpp b/src/private/mx/core/generated/BeatRepeat.cpp index dd68fefb4..9ac576e7e 100644 --- a/src/private/mx/core/generated/BeatRepeat.cpp +++ b/src/private/mx/core/generated/BeatRepeat.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/BeatRepeat.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,7 +51,7 @@ void BeatRepeat::setSlash(std::optional value) m_slash = std::move(value); } -BeatRepeat parseBeatRepeat(pugi::xml_node el) +BeatRepeat parseBeatRepeat(pugi::xml_node el, const ParseContext &context) { BeatRepeat out; bool seen_type = false; @@ -64,15 +65,15 @@ BeatRepeat parseBeatRepeat(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStop::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "slashes") { - out.setSlashes(parseInt(a.value())); + out.setSlashes(parseIntegerValue(a.value(), context, el, "slashes")); } else if (aname == "use-dots") { - out.setUseDots(YesNo::parse(a.value())); + out.setUseDots(parseValue(a.value(), context, el, "use-dots")); } else { @@ -83,16 +84,16 @@ BeatRepeat parseBeatRepeat(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseBeatRepeatContent(out, el); + parseBeatRepeatContent(out, el, context); return out; } -void parseBeatRepeatContent(BeatRepeat &out, pugi::xml_node el) +void parseBeatRepeatContent(BeatRepeat &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "slash-type") || cursorIs(cursor, "except-voice"))) { - out.setSlash(parseSlashGroup(el, cursor)); + out.setSlash(parseSlashGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/BeatRepeat.h b/src/private/mx/core/generated/BeatRepeat.h index a12c5aaf3..e65d7f303 100644 --- a/src/private/mx/core/generated/BeatRepeat.h +++ b/src/private/mx/core/generated/BeatRepeat.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The beat-repeat type is used to indicate that a single beat (but possibly many notes) is /// repeated. The slashes attribute specifies the number of slashes to use in the symbol. The /// use-dots attribute indicates whether or not to use dots as well (for instance, with mixed rhythm @@ -50,9 +52,9 @@ class BeatRepeat final std::optional m_slash; }; -BeatRepeat parseBeatRepeat(pugi::xml_node el); +BeatRepeat parseBeatRepeat(pugi::xml_node el, const ParseContext &context); -void parseBeatRepeatContent(BeatRepeat &out, pugi::xml_node el); +void parseBeatRepeatContent(BeatRepeat &out, pugi::xml_node el, const ParseContext &context); void serializeBeatRepeat(const BeatRepeat &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/BeatUnitGroup.cpp b/src/private/mx/core/generated/BeatUnitGroup.cpp index 07d6f4394..314d07a22 100644 --- a/src/private/mx/core/generated/BeatUnitGroup.cpp +++ b/src/private/mx/core/generated/BeatUnitGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/BeatUnitGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,12 +36,12 @@ void BeatUnitGroup::setBeatUnitDot(std::vector value) m_beatUnitDot = std::move(value); } -BeatUnitGroup parseBeatUnitGroup(pugi::xml_node el, pugi::xml_node &cursor) +BeatUnitGroup parseBeatUnitGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { BeatUnitGroup out; if (cursorIs(cursor, "beat-unit")) { - out.setBeatUnit(NoteTypeValue::parse(childText(cursor))); + out.setBeatUnit(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -49,7 +50,7 @@ BeatUnitGroup parseBeatUnitGroup(pugi::xml_node el, pugi::xml_node &cursor) } while (cursorIs(cursor, "beat-unit-dot")) { - out.addBeatUnitDot(parseEmpty(cursor)); + out.addBeatUnitDot(parseEmpty(cursor, context)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/BeatUnitGroup.h b/src/private/mx/core/generated/BeatUnitGroup.h index b58bea6f3..58b582db9 100644 --- a/src/private/mx/core/generated/BeatUnitGroup.h +++ b/src/private/mx/core/generated/BeatUnitGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The beat-unit group combines elements used repeatedly in the metronome element to specify a note /// within a metronome mark. /// A shared content group: transparent on the wire, its @@ -39,7 +41,7 @@ class BeatUnitGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -BeatUnitGroup parseBeatUnitGroup(pugi::xml_node el, pugi::xml_node &cursor); +BeatUnitGroup parseBeatUnitGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeBeatUnitGroup(const BeatUnitGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/BeatUnitTied.cpp b/src/private/mx/core/generated/BeatUnitTied.cpp index 0ca4eb2ff..01b3d189c 100644 --- a/src/private/mx/core/generated/BeatUnitTied.cpp +++ b/src/private/mx/core/generated/BeatUnitTied.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/BeatUnitTied.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -20,7 +21,7 @@ void BeatUnitTied::setBeatUnit(BeatUnitGroup value) m_beatUnit = std::move(value); } -BeatUnitTied parseBeatUnitTied(pugi::xml_node el) +BeatUnitTied parseBeatUnitTied(pugi::xml_node el, const ParseContext &context) { BeatUnitTied out; for (pugi::xml_attribute a : el.attributes()) @@ -32,16 +33,16 @@ BeatUnitTied parseBeatUnitTied(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseBeatUnitTiedContent(out, el); + parseBeatUnitTiedContent(out, el, context); return out; } -void parseBeatUnitTiedContent(BeatUnitTied &out, pugi::xml_node el) +void parseBeatUnitTiedContent(BeatUnitTied &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "beat-unit"))) { - out.setBeatUnit(parseBeatUnitGroup(el, cursor)); + out.setBeatUnit(parseBeatUnitGroup(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/BeatUnitTied.h b/src/private/mx/core/generated/BeatUnitTied.h index 2a2e755da..002cab66f 100644 --- a/src/private/mx/core/generated/BeatUnitTied.h +++ b/src/private/mx/core/generated/BeatUnitTied.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The beat-unit-tied type indicates a beat-unit within a metronome mark that is tied to the /// preceding beat-unit. This allows two or more tied notes to be associated with a per-minute value /// in a metronome mark, whereas the metronome-tied element is restricted to metric relationship @@ -34,9 +36,9 @@ class BeatUnitTied final BeatUnitGroup m_beatUnit{}; }; -BeatUnitTied parseBeatUnitTied(pugi::xml_node el); +BeatUnitTied parseBeatUnitTied(pugi::xml_node el, const ParseContext &context); -void parseBeatUnitTiedContent(BeatUnitTied &out, pugi::xml_node el); +void parseBeatUnitTiedContent(BeatUnitTied &out, pugi::xml_node el, const ParseContext &context); void serializeBeatUnitTied(const BeatUnitTied &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Beater.cpp b/src/private/mx/core/generated/Beater.cpp index 718609dd1..d7ef400fc 100644 --- a/src/private/mx/core/generated/Beater.cpp +++ b/src/private/mx/core/generated/Beater.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Beater.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Beater::setValue(BeaterValue value) m_value = std::move(value); } -Beater parseBeater(pugi::xml_node el) +Beater parseBeater(pugi::xml_node el, const ParseContext &context) { Beater out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ Beater parseBeater(pugi::xml_node el) } if (aname == "tip") { - out.setTip(TipDirection::parse(a.value())); + out.setTip(parseValue(a.value(), context, el, "tip")); } else { throwUnknownAttribute(el, a.name()); } } - parseBeaterContent(out, el); + parseBeaterContent(out, el, context); return out; } -void parseBeaterContent(Beater &out, pugi::xml_node el) +void parseBeaterContent(Beater &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(BeaterValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Beater.h b/src/private/mx/core/generated/Beater.h index 9c776b097..1c113e739 100644 --- a/src/private/mx/core/generated/Beater.h +++ b/src/private/mx/core/generated/Beater.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The beater type represents pictograms for beaters, mallets, and sticks that do not have different /// materials represented in the pictogram. class Beater final @@ -32,9 +34,9 @@ class Beater final BeaterValue m_value{}; }; -Beater parseBeater(pugi::xml_node el); +Beater parseBeater(pugi::xml_node el, const ParseContext &context); -void parseBeaterContent(Beater &out, pugi::xml_node el); +void parseBeaterContent(Beater &out, pugi::xml_node el, const ParseContext &context); void serializeBeater(const Beater &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/BeaterValue.cpp b/src/private/mx/core/generated/BeaterValue.cpp index 499931e7e..26587e455 100644 --- a/src/private/mx/core/generated/BeaterValue.cpp +++ b/src/private/mx/core/generated/BeaterValue.cpp @@ -150,9 +150,15 @@ bool BeaterValue::tryParse(std::string_view text, BeaterValue &out) noexcept } BeaterValue BeaterValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +BeaterValue BeaterValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { BeaterValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/BeaterValue.h b/src/private/mx/core/generated/BeaterValue.h index 7530d8790..01048ea06 100644 --- a/src/private/mx/core/generated/BeaterValue.h +++ b/src/private/mx/core/generated/BeaterValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -78,6 +80,9 @@ class BeaterValue final /// (the import leniency policy; never produces an invalid value). static BeaterValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static BeaterValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const BeaterValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Bend.cpp b/src/private/mx/core/generated/Bend.cpp index 227908031..04bf07b95 100644 --- a/src/private/mx/core/generated/Bend.cpp +++ b/src/private/mx/core/generated/Bend.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Bend.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -180,7 +181,7 @@ void Bend::setWithBar(std::optional value) m_withBar = std::move(value); } -Bend parseBend(pugi::xml_node el) +Bend parseBend(pugi::xml_node el, const ParseContext &context) { Bend out; for (pugi::xml_attribute a : el.attributes()) @@ -192,75 +193,75 @@ Bend parseBend(pugi::xml_node el) } if (aname == "shape") { - out.setShape(BendShape::parse(a.value())); + out.setShape(parseValue(a.value(), context, el, "shape")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "accelerate") { - out.setAccelerate(YesNo::parse(a.value())); + out.setAccelerate(parseValue(a.value(), context, el, "accelerate")); } else if (aname == "beats") { - out.setBeats(TrillBeats::parse(a.value())); + out.setBeats(parseValue(a.value(), context, el, "beats")); } else if (aname == "first-beat") { - out.setFirstBeat(Percent::parse(a.value())); + out.setFirstBeat(parseValue(a.value(), context, el, "first-beat")); } else if (aname == "last-beat") { - out.setLastBeat(Percent::parse(a.value())); + out.setLastBeat(parseValue(a.value(), context, el, "last-beat")); } else { throwUnknownAttribute(el, a.name()); } } - parseBendContent(out, el); + parseBendContent(out, el, context); return out; } -void parseBendContent(Bend &out, pugi::xml_node el) +void parseBendContent(Bend &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "bend-alter")) { - out.setBendAlter(Semitones::parse(childText(cursor))); + out.setBendAlter(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -269,11 +270,11 @@ void parseBendContent(Bend &out, pugi::xml_node el) } if (cursor && (cursorIs(cursor, "pre-bend") || cursorIs(cursor, "release"))) { - out.setChoice(parseBendChoice(el, cursor)); + out.setChoice(parseBendChoice(el, cursor, context)); } if (cursorIs(cursor, "with-bar")) { - out.setWithBar(parsePlacementText(cursor)); + out.setWithBar(parsePlacementText(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Bend.h b/src/private/mx/core/generated/Bend.h index 8aa6d8212..48c52eb08 100644 --- a/src/private/mx/core/generated/Bend.h +++ b/src/private/mx/core/generated/Bend.h @@ -29,6 +29,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The bend type is used in guitar notation and tablature. A single note with a bend and release /// will contain two bend elements: the first to represent the bend and the second to represent the /// release. The shape attribute distinguishes between the angled bend symbols commonly used in @@ -95,9 +97,9 @@ class Bend final std::optional m_withBar; }; -Bend parseBend(pugi::xml_node el); +Bend parseBend(pugi::xml_node el, const ParseContext &context); -void parseBendContent(Bend &out, pugi::xml_node el); +void parseBendContent(Bend &out, pugi::xml_node el, const ParseContext &context); void serializeBend(const Bend &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/BendChoice.cpp b/src/private/mx/core/generated/BendChoice.cpp index 1500c8d33..54d7f5488 100644 --- a/src/private/mx/core/generated/BendChoice.cpp +++ b/src/private/mx/core/generated/BendChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/BendChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,17 +26,17 @@ BendChoice BendChoice::release(Release value) return BendChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -BendChoice parseBendChoice(pugi::xml_node el, pugi::xml_node &cursor) +BendChoice parseBendChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "pre-bend"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return BendChoice::preBend(std::move(value)); } if (cursor && (cursorIs(cursor, "release"))) { - Release value = parseRelease(cursor); + Release value = parseRelease(cursor, context); cursor = nextElement(cursor); return BendChoice::release(std::move(value)); } diff --git a/src/private/mx/core/generated/BendChoice.h b/src/private/mx/core/generated/BendChoice.h index 6ce0d3ede..4a8deebf1 100644 --- a/src/private/mx/core/generated/BendChoice.h +++ b/src/private/mx/core/generated/BendChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -80,7 +82,7 @@ class BendChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -BendChoice parseBendChoice(pugi::xml_node el, pugi::xml_node &cursor); +BendChoice parseBendChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeBendChoice(const BendChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/BendShape.cpp b/src/private/mx/core/generated/BendShape.cpp index a3675a69c..e4380e56c 100644 --- a/src/private/mx/core/generated/BendShape.cpp +++ b/src/private/mx/core/generated/BendShape.cpp @@ -42,9 +42,15 @@ bool BendShape::tryParse(std::string_view text, BendShape &out) noexcept } BendShape BendShape::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +BendShape BendShape::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { BendShape v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/BendShape.h b/src/private/mx/core/generated/BendShape.h index 0cf7a4444..681c0b8a9 100644 --- a/src/private/mx/core/generated/BendShape.h +++ b/src/private/mx/core/generated/BendShape.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -41,6 +43,9 @@ class BendShape final /// (the import leniency policy; never produces an invalid value). static BendShape parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static BendShape parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const BendShape &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Bookmark.cpp b/src/private/mx/core/generated/Bookmark.cpp index a3ef099b6..641da9db6 100644 --- a/src/private/mx/core/generated/Bookmark.cpp +++ b/src/private/mx/core/generated/Bookmark.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Bookmark.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,7 +51,7 @@ void Bookmark::setPosition(std::optional value) m_position = std::move(value); } -Bookmark parseBookmark(pugi::xml_node el) +Bookmark parseBookmark(pugi::xml_node el, const ParseContext &context) { Bookmark out; bool seen_id = false; @@ -64,7 +65,7 @@ Bookmark parseBookmark(pugi::xml_node el) if (aname == "id") { seen_id = true; - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else if (aname == "name") { @@ -72,11 +73,11 @@ Bookmark parseBookmark(pugi::xml_node el) } else if (aname == "element") { - out.setElement(NameToken::parse(a.value())); + out.setElement(parseValue(a.value(), context, el, "element")); } else if (aname == "position") { - out.setPosition(parseInt(a.value())); + out.setPosition(parseIntegerValue(a.value(), context, el, "position")); } else { @@ -87,13 +88,14 @@ Bookmark parseBookmark(pugi::xml_node el) { throwMissingAttribute(el, "id"); } - parseBookmarkContent(out, el); + parseBookmarkContent(out, el, context); return out; } -void parseBookmarkContent(Bookmark &out, pugi::xml_node el) +void parseBookmarkContent(Bookmark &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Bookmark.h b/src/private/mx/core/generated/Bookmark.h index efef95d7c..1743fa627 100644 --- a/src/private/mx/core/generated/Bookmark.h +++ b/src/private/mx/core/generated/Bookmark.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The bookmark type serves as a well-defined target for an incoming simple XLink. class Bookmark final { @@ -37,9 +39,9 @@ class Bookmark final std::optional m_position; }; -Bookmark parseBookmark(pugi::xml_node el); +Bookmark parseBookmark(pugi::xml_node el, const ParseContext &context); -void parseBookmarkContent(Bookmark &out, pugi::xml_node el); +void parseBookmarkContent(Bookmark &out, pugi::xml_node el, const ParseContext &context); void serializeBookmark(const Bookmark &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Bracket.cpp b/src/private/mx/core/generated/Bracket.cpp index f3f4bdbef..0172206d8 100644 --- a/src/private/mx/core/generated/Bracket.cpp +++ b/src/private/mx/core/generated/Bracket.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Bracket.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -140,7 +141,7 @@ void Bracket::setID(std::optional value) m_id = std::move(value); } -Bracket parseBracket(pugi::xml_node el) +Bracket parseBracket(pugi::xml_node el, const ParseContext &context) { Bracket out; bool seen_type = false; @@ -155,56 +156,56 @@ Bracket parseBracket(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStopContinue::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "number") { - out.setNumber(NumberLevel::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "line-end") { seen_lineEnd = true; - out.setLineEnd(LineEnd::parse(a.value())); + out.setLineEnd(parseValue(a.value(), context, el, "line-end")); } else if (aname == "end-length") { - out.setEndLength(Tenths::parse(a.value())); + out.setEndLength(parseValue(a.value(), context, el, "end-length")); } else if (aname == "line-type") { - out.setLineType(LineType::parse(a.value())); + out.setLineType(parseValue(a.value(), context, el, "line-type")); } else if (aname == "dash-length") { - out.setDashLength(Tenths::parse(a.value())); + out.setDashLength(parseValue(a.value(), context, el, "dash-length")); } else if (aname == "space-length") { - out.setSpaceLength(Tenths::parse(a.value())); + out.setSpaceLength(parseValue(a.value(), context, el, "space-length")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -219,13 +220,14 @@ Bracket parseBracket(pugi::xml_node el) { throwMissingAttribute(el, "line-end"); } - parseBracketContent(out, el); + parseBracketContent(out, el, context); return out; } -void parseBracketContent(Bracket &out, pugi::xml_node el) +void parseBracketContent(Bracket &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Bracket.h b/src/private/mx/core/generated/Bracket.h index f05da8c26..f0babd13e 100644 --- a/src/private/mx/core/generated/Bracket.h +++ b/src/private/mx/core/generated/Bracket.h @@ -22,6 +22,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Brackets are combined with words in a variety of modern directions. The line-end attribute /// specifies if there is a jog up or down (or both), an arrow, or nothing at the start or end of the /// bracket. If the line-end is up or down, the length of the jog can be specified using the @@ -73,9 +75,9 @@ class Bracket final std::optional m_id; }; -Bracket parseBracket(pugi::xml_node el); +Bracket parseBracket(pugi::xml_node el, const ParseContext &context); -void parseBracketContent(Bracket &out, pugi::xml_node el); +void parseBracketContent(Bracket &out, pugi::xml_node el, const ParseContext &context); void serializeBracket(const Bracket &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/BreathMark.cpp b/src/private/mx/core/generated/BreathMark.cpp index de5bf33fb..f26fc425b 100644 --- a/src/private/mx/core/generated/BreathMark.cpp +++ b/src/private/mx/core/generated/BreathMark.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/BreathMark.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -120,7 +121,7 @@ void BreathMark::setValue(BreathMarkValue value) m_value = std::move(value); } -BreathMark parseBreathMark(pugi::xml_node el) +BreathMark parseBreathMark(pugi::xml_node el, const ParseContext &context) { BreathMark out; for (pugi::xml_attribute a : el.attributes()) @@ -132,56 +133,56 @@ BreathMark parseBreathMark(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else { throwUnknownAttribute(el, a.name()); } } - parseBreathMarkContent(out, el); + parseBreathMarkContent(out, el, context); return out; } -void parseBreathMarkContent(BreathMark &out, pugi::xml_node el) +void parseBreathMarkContent(BreathMark &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(BreathMarkValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/BreathMark.h b/src/private/mx/core/generated/BreathMark.h index 2cdb98007..bb008a0b4 100644 --- a/src/private/mx/core/generated/BreathMark.h +++ b/src/private/mx/core/generated/BreathMark.h @@ -22,6 +22,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The breath-mark element indicates a place to take a breath. class BreathMark final { @@ -64,9 +66,9 @@ class BreathMark final BreathMarkValue m_value{}; }; -BreathMark parseBreathMark(pugi::xml_node el); +BreathMark parseBreathMark(pugi::xml_node el, const ParseContext &context); -void parseBreathMarkContent(BreathMark &out, pugi::xml_node el); +void parseBreathMarkContent(BreathMark &out, pugi::xml_node el, const ParseContext &context); void serializeBreathMark(const BreathMark &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/BreathMarkValue.cpp b/src/private/mx/core/generated/BreathMarkValue.cpp index 8ececee9e..370ec4d11 100644 --- a/src/private/mx/core/generated/BreathMarkValue.cpp +++ b/src/private/mx/core/generated/BreathMarkValue.cpp @@ -56,9 +56,15 @@ bool BreathMarkValue::tryParse(std::string_view text, BreathMarkValue &out) noex } BreathMarkValue BreathMarkValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +BreathMarkValue BreathMarkValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { BreathMarkValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/BreathMarkValue.h b/src/private/mx/core/generated/BreathMarkValue.h index ae7db17c4..be919e21c 100644 --- a/src/private/mx/core/generated/BreathMarkValue.h +++ b/src/private/mx/core/generated/BreathMarkValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -46,6 +48,9 @@ class BreathMarkValue final /// (the import leniency policy; never produces an invalid value). static BreathMarkValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static BreathMarkValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const BreathMarkValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/CSSFontSize.cpp b/src/private/mx/core/generated/CSSFontSize.cpp index 1726eb021..3921e34d0 100644 --- a/src/private/mx/core/generated/CSSFontSize.cpp +++ b/src/private/mx/core/generated/CSSFontSize.cpp @@ -66,9 +66,15 @@ bool CSSFontSize::tryParse(std::string_view text, CSSFontSize &out) noexcept } CSSFontSize CSSFontSize::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +CSSFontSize CSSFontSize::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { CSSFontSize v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/CSSFontSize.h b/src/private/mx/core/generated/CSSFontSize.h index 0a7b7fa3a..492747386 100644 --- a/src/private/mx/core/generated/CSSFontSize.h +++ b/src/private/mx/core/generated/CSSFontSize.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -51,6 +53,9 @@ class CSSFontSize final /// (the import leniency policy; never produces an invalid value). static CSSFontSize parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static CSSFontSize parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const CSSFontSize &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Caesura.cpp b/src/private/mx/core/generated/Caesura.cpp index 26e7a450d..2b6363c1d 100644 --- a/src/private/mx/core/generated/Caesura.cpp +++ b/src/private/mx/core/generated/Caesura.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Caesura.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -120,7 +121,7 @@ void Caesura::setValue(CaesuraValue value) m_value = std::move(value); } -Caesura parseCaesura(pugi::xml_node el) +Caesura parseCaesura(pugi::xml_node el, const ParseContext &context) { Caesura out; for (pugi::xml_attribute a : el.attributes()) @@ -132,56 +133,56 @@ Caesura parseCaesura(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else { throwUnknownAttribute(el, a.name()); } } - parseCaesuraContent(out, el); + parseCaesuraContent(out, el, context); return out; } -void parseCaesuraContent(Caesura &out, pugi::xml_node el) +void parseCaesuraContent(Caesura &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(CaesuraValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Caesura.h b/src/private/mx/core/generated/Caesura.h index d28fb4b4f..f5f9e2013 100644 --- a/src/private/mx/core/generated/Caesura.h +++ b/src/private/mx/core/generated/Caesura.h @@ -22,6 +22,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The caesura element indicates a slight pause. It is notated using a "railroad tracks" symbol or /// other variations specified in the element content. class Caesura final @@ -65,9 +67,9 @@ class Caesura final CaesuraValue m_value{}; }; -Caesura parseCaesura(pugi::xml_node el); +Caesura parseCaesura(pugi::xml_node el, const ParseContext &context); -void parseCaesuraContent(Caesura &out, pugi::xml_node el); +void parseCaesuraContent(Caesura &out, pugi::xml_node el, const ParseContext &context); void serializeCaesura(const Caesura &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/CaesuraValue.cpp b/src/private/mx/core/generated/CaesuraValue.cpp index af5dc8564..a352740c4 100644 --- a/src/private/mx/core/generated/CaesuraValue.cpp +++ b/src/private/mx/core/generated/CaesuraValue.cpp @@ -61,9 +61,15 @@ bool CaesuraValue::tryParse(std::string_view text, CaesuraValue &out) noexcept } CaesuraValue CaesuraValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +CaesuraValue CaesuraValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { CaesuraValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/CaesuraValue.h b/src/private/mx/core/generated/CaesuraValue.h index a93c0d9ab..961b7027d 100644 --- a/src/private/mx/core/generated/CaesuraValue.h +++ b/src/private/mx/core/generated/CaesuraValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -48,6 +50,9 @@ class CaesuraValue final /// (the import leniency policy; never produces an invalid value). static CaesuraValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static CaesuraValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const CaesuraValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Cancel.cpp b/src/private/mx/core/generated/Cancel.cpp index 7e0b18960..78f805959 100644 --- a/src/private/mx/core/generated/Cancel.cpp +++ b/src/private/mx/core/generated/Cancel.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Cancel.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Cancel::setValue(Fifths value) m_value = std::move(value); } -Cancel parseCancel(pugi::xml_node el) +Cancel parseCancel(pugi::xml_node el, const ParseContext &context) { Cancel out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ Cancel parseCancel(pugi::xml_node el) } if (aname == "location") { - out.setLocation(CancelLocation::parse(a.value())); + out.setLocation(parseValue(a.value(), context, el, "location")); } else { throwUnknownAttribute(el, a.name()); } } - parseCancelContent(out, el); + parseCancelContent(out, el, context); return out; } -void parseCancelContent(Cancel &out, pugi::xml_node el) +void parseCancelContent(Cancel &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(Fifths::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Cancel.h b/src/private/mx/core/generated/Cancel.h index 3c88e65af..adc673c58 100644 --- a/src/private/mx/core/generated/Cancel.h +++ b/src/private/mx/core/generated/Cancel.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A cancel element indicates that the old key signature should be cancelled before the new one /// appears. This will always happen when changing to C major or A minor and need not be specified /// then. The cancel value matches the fifths value of the cancelled key signature (e.g., a cancel of @@ -35,9 +37,9 @@ class Cancel final Fifths m_value{}; }; -Cancel parseCancel(pugi::xml_node el); +Cancel parseCancel(pugi::xml_node el, const ParseContext &context); -void parseCancelContent(Cancel &out, pugi::xml_node el); +void parseCancelContent(Cancel &out, pugi::xml_node el, const ParseContext &context); void serializeCancel(const Cancel &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/CancelLocation.cpp b/src/private/mx/core/generated/CancelLocation.cpp index be8e5a88b..d4e894388 100644 --- a/src/private/mx/core/generated/CancelLocation.cpp +++ b/src/private/mx/core/generated/CancelLocation.cpp @@ -48,9 +48,15 @@ bool CancelLocation::tryParse(std::string_view text, CancelLocation &out) noexce } CancelLocation CancelLocation::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +CancelLocation CancelLocation::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { CancelLocation v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/CancelLocation.h b/src/private/mx/core/generated/CancelLocation.h index 19a5c8186..e172c74bb 100644 --- a/src/private/mx/core/generated/CancelLocation.h +++ b/src/private/mx/core/generated/CancelLocation.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -45,6 +47,9 @@ class CancelLocation final /// (the import leniency policy; never produces an invalid value). static CancelLocation parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static CancelLocation parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const CancelLocation &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/CircularArrow.cpp b/src/private/mx/core/generated/CircularArrow.cpp index 32e7aa641..f6af0e4fd 100644 --- a/src/private/mx/core/generated/CircularArrow.cpp +++ b/src/private/mx/core/generated/CircularArrow.cpp @@ -42,9 +42,15 @@ bool CircularArrow::tryParse(std::string_view text, CircularArrow &out) noexcept } CircularArrow CircularArrow::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +CircularArrow CircularArrow::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { CircularArrow v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/CircularArrow.h b/src/private/mx/core/generated/CircularArrow.h index c24fd3c6d..1e9305f16 100644 --- a/src/private/mx/core/generated/CircularArrow.h +++ b/src/private/mx/core/generated/CircularArrow.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -41,6 +43,9 @@ class CircularArrow final /// (the import leniency policy; never produces an invalid value). static CircularArrow parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static CircularArrow parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const CircularArrow &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Clef.cpp b/src/private/mx/core/generated/Clef.cpp index 1b1ca916b..1bc0c9b63 100644 --- a/src/private/mx/core/generated/Clef.cpp +++ b/src/private/mx/core/generated/Clef.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Clef.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -170,7 +171,7 @@ void Clef::setClef(ClefGroup value) m_clef = std::move(value); } -Clef parseClef(pugi::xml_node el) +Clef parseClef(pugi::xml_node el, const ParseContext &context) { Clef out; for (pugi::xml_attribute a : el.attributes()) @@ -182,79 +183,79 @@ Clef parseClef(pugi::xml_node el) } if (aname == "number") { - out.setNumber(StaffNumber::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "additional") { - out.setAdditional(YesNo::parse(a.value())); + out.setAdditional(parseValue(a.value(), context, el, "additional")); } else if (aname == "size") { - out.setSize(SymbolSize::parse(a.value())); + out.setSize(parseValue(a.value(), context, el, "size")); } else if (aname == "after-barline") { - out.setAfterBarline(YesNo::parse(a.value())); + out.setAfterBarline(parseValue(a.value(), context, el, "after-barline")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseClefContent(out, el); + parseClefContent(out, el, context); return out; } -void parseClefContent(Clef &out, pugi::xml_node el) +void parseClefContent(Clef &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "sign"))) { - out.setClef(parseClefGroup(el, cursor)); + out.setClef(parseClefGroup(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/Clef.h b/src/private/mx/core/generated/Clef.h index ccaafe4e6..f3b34bfc5 100644 --- a/src/private/mx/core/generated/Clef.h +++ b/src/private/mx/core/generated/Clef.h @@ -28,6 +28,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Clefs are represented by a combination of sign, line, and clef-octave-change elements. The /// optional number attribute refers to staff numbers within the part. A value of 1 is assumed if not /// present. Sometimes clefs are added to the staff in non-standard line positions, either to @@ -97,9 +99,9 @@ class Clef final ClefGroup m_clef{}; }; -Clef parseClef(pugi::xml_node el); +Clef parseClef(pugi::xml_node el, const ParseContext &context); -void parseClefContent(Clef &out, pugi::xml_node el); +void parseClefContent(Clef &out, pugi::xml_node el, const ParseContext &context); void serializeClef(const Clef &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/ClefGroup.cpp b/src/private/mx/core/generated/ClefGroup.cpp index fec9da9ca..b427bbc03 100644 --- a/src/private/mx/core/generated/ClefGroup.cpp +++ b/src/private/mx/core/generated/ClefGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/ClefGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,12 +41,12 @@ void ClefGroup::setClefOctaveChange(std::optional value) m_clefOctaveChange = std::move(value); } -ClefGroup parseClefGroup(pugi::xml_node el, pugi::xml_node &cursor) +ClefGroup parseClefGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { ClefGroup out; if (cursorIs(cursor, "sign")) { - out.setSign(ClefSign::parse(childText(cursor))); + out.setSign(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -54,12 +55,12 @@ ClefGroup parseClefGroup(pugi::xml_node el, pugi::xml_node &cursor) } if (cursorIs(cursor, "line")) { - out.setLine(StaffLinePosition::parse(childText(cursor))); + out.setLine(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "clef-octave-change")) { - out.setClefOctaveChange(parseInt(childText(cursor))); + out.setClefOctaveChange(parseIntegerValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/ClefGroup.h b/src/private/mx/core/generated/ClefGroup.h index a10d876d9..1670fae1b 100644 --- a/src/private/mx/core/generated/ClefGroup.h +++ b/src/private/mx/core/generated/ClefGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Clefs are represented by a combination of sign, line, and clef-octave-change elements. /// A shared content group: transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -40,7 +42,7 @@ class ClefGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -ClefGroup parseClefGroup(pugi::xml_node el, pugi::xml_node &cursor); +ClefGroup parseClefGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeClefGroup(const ClefGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/ClefSign.cpp b/src/private/mx/core/generated/ClefSign.cpp index 0bd6cf61e..5427fa242 100644 --- a/src/private/mx/core/generated/ClefSign.cpp +++ b/src/private/mx/core/generated/ClefSign.cpp @@ -66,9 +66,15 @@ bool ClefSign::tryParse(std::string_view text, ClefSign &out) noexcept } ClefSign ClefSign::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +ClefSign ClefSign::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { ClefSign v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/ClefSign.h b/src/private/mx/core/generated/ClefSign.h index 28c939099..86cfc9691 100644 --- a/src/private/mx/core/generated/ClefSign.h +++ b/src/private/mx/core/generated/ClefSign.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -55,6 +57,9 @@ class ClefSign final /// (the import leniency policy; never produces an invalid value). static ClefSign parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static ClefSign parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const ClefSign &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Coda.cpp b/src/private/mx/core/generated/Coda.cpp index 83310b363..836f971a2 100644 --- a/src/private/mx/core/generated/Coda.cpp +++ b/src/private/mx/core/generated/Coda.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Coda.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -140,7 +141,7 @@ void Coda::setID(std::optional value) m_id = std::move(value); } -Coda parseCoda(pugi::xml_node el) +Coda parseCoda(pugi::xml_node el, const ParseContext &context) { Coda out; for (pugi::xml_attribute a : el.attributes()) @@ -152,68 +153,69 @@ Coda parseCoda(pugi::xml_node el) } if (aname == "smufl") { - out.setSmufl(SmuflCodaGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseCodaContent(out, el); + parseCodaContent(out, el, context); return out; } -void parseCodaContent(Coda &out, pugi::xml_node el) +void parseCodaContent(Coda &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Coda.h b/src/private/mx/core/generated/Coda.h index 28482ec3d..ba0c056f5 100644 --- a/src/private/mx/core/generated/Coda.h +++ b/src/private/mx/core/generated/Coda.h @@ -25,6 +25,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The coda type is the visual indicator of a coda sign. The exact glyph can be specified with the /// smufl attribute. A sound element is also needed to guide playback applications reliably. class Coda final @@ -74,9 +76,9 @@ class Coda final std::optional m_id; }; -Coda parseCoda(pugi::xml_node el); +Coda parseCoda(pugi::xml_node el, const ParseContext &context); -void parseCodaContent(Coda &out, pugi::xml_node el); +void parseCodaContent(Coda &out, pugi::xml_node el, const ParseContext &context); void serializeCoda(const Coda &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Color.cpp b/src/private/mx/core/generated/Color.cpp index c80542ffb..2e73a9086 100644 --- a/src/private/mx/core/generated/Color.cpp +++ b/src/private/mx/core/generated/Color.cpp @@ -96,4 +96,16 @@ Color Color::parse(std::string_view text) noexcept return out; } +Color Color::parse(std::string_view text, ValueParseOutcome &outcome) noexcept +{ + Color out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/Color.h b/src/private/mx/core/generated/Color.h index a727a205f..b8771f0ae 100644 --- a/src/private/mx/core/generated/Color.h +++ b/src/private/mx/core/generated/Color.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include #include @@ -83,6 +85,9 @@ class Color final /// Lenient: malformed text yields opaque black. static Color parse(std::string_view text) noexcept; + /// Lenient, and says whether the text had to be repaired. + static Color parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const Color &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/CommaSeparatedText.cpp b/src/private/mx/core/generated/CommaSeparatedText.cpp index 5126c6589..0b6c8e0a4 100644 --- a/src/private/mx/core/generated/CommaSeparatedText.cpp +++ b/src/private/mx/core/generated/CommaSeparatedText.cpp @@ -120,4 +120,16 @@ CommaSeparatedText CommaSeparatedText::parse(std::string_view text) return out; } +CommaSeparatedText CommaSeparatedText::parse(std::string_view text, ValueParseOutcome &outcome) +{ + CommaSeparatedText out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/CommaSeparatedText.h b/src/private/mx/core/generated/CommaSeparatedText.h index fa11711ea..220f722af 100644 --- a/src/private/mx/core/generated/CommaSeparatedText.h +++ b/src/private/mx/core/generated/CommaSeparatedText.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include #include @@ -42,6 +44,9 @@ class CommaSeparatedText final /// Lenient: repairs into the nearest valid list. static CommaSeparatedText parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. + static CommaSeparatedText parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const CommaSeparatedText &other) const noexcept { return m_items == other.m_items; diff --git a/src/private/mx/core/generated/Credit.cpp b/src/private/mx/core/generated/Credit.cpp index 834ed45d1..993e5edcf 100644 --- a/src/private/mx/core/generated/Credit.cpp +++ b/src/private/mx/core/generated/Credit.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Credit.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -85,7 +86,7 @@ void Credit::setChoice(CreditChoice value) m_choice = std::move(value); } -Credit parseCredit(pugi::xml_node el) +Credit parseCredit(pugi::xml_node el, const ParseContext &context) { Credit out; for (pugi::xml_attribute a : el.attributes()) @@ -97,22 +98,22 @@ Credit parseCredit(pugi::xml_node el) } if (aname == "page") { - out.setPage(parseInt(a.value())); + out.setPage(parseIntegerValue(a.value(), context, el, "page")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseCreditContent(out, el); + parseCreditContent(out, el, context); return out; } -void parseCreditContent(Credit &out, pugi::xml_node el) +void parseCreditContent(Credit &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); while (cursorIs(cursor, "credit-type")) @@ -122,18 +123,18 @@ void parseCreditContent(Credit &out, pugi::xml_node el) } while (cursorIs(cursor, "link")) { - out.addLink(parseLink(cursor)); + out.addLink(parseLink(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "bookmark")) { - out.addBookmark(parseBookmark(cursor)); + out.addBookmark(parseBookmark(cursor, context)); cursor = nextElement(cursor); } if (cursor && (cursorIs(cursor, "credit-image") || cursorIs(cursor, "credit-words") || cursorIs(cursor, "credit-symbol"))) { - out.setChoice(parseCreditChoice(el, cursor)); + out.setChoice(parseCreditChoice(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/Credit.h b/src/private/mx/core/generated/Credit.h index 1a2f70a03..9eab4c77c 100644 --- a/src/private/mx/core/generated/Credit.h +++ b/src/private/mx/core/generated/Credit.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The credit type represents the appearance of the title, composer, arranger, lyricist, copyright, /// dedication, and other text, symbols, and graphics that commonly appear on the first page of a /// score. The credit-words, credit-symbol, and credit-image elements are similar to the words, @@ -67,9 +69,9 @@ class Credit final CreditChoice m_choice{}; }; -Credit parseCredit(pugi::xml_node el); +Credit parseCredit(pugi::xml_node el, const ParseContext &context); -void parseCreditContent(Credit &out, pugi::xml_node el); +void parseCreditContent(Credit &out, pugi::xml_node el, const ParseContext &context); void serializeCredit(const Credit &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/CreditChoice.cpp b/src/private/mx/core/generated/CreditChoice.cpp index 1c2300a86..a42ed18a4 100644 --- a/src/private/mx/core/generated/CreditChoice.cpp +++ b/src/private/mx/core/generated/CreditChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/CreditChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,17 +26,17 @@ CreditChoice CreditChoice::group(CreditChoiceGroup value) return CreditChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -CreditChoice parseCreditChoice(pugi::xml_node el, pugi::xml_node &cursor) +CreditChoice parseCreditChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "credit-image"))) { - Image value = parseImage(cursor); + Image value = parseImage(cursor, context); cursor = nextElement(cursor); return CreditChoice::creditImage(std::move(value)); } if (cursor && (cursorIs(cursor, "credit-words") || cursorIs(cursor, "credit-symbol"))) { - return CreditChoice::group(parseCreditChoiceGroup(el, cursor)); + return CreditChoice::group(parseCreditChoiceGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/CreditChoice.h b/src/private/mx/core/generated/CreditChoice.h index 94c649e21..c869b0296 100644 --- a/src/private/mx/core/generated/CreditChoice.h +++ b/src/private/mx/core/generated/CreditChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -80,7 +82,7 @@ class CreditChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -CreditChoice parseCreditChoice(pugi::xml_node el, pugi::xml_node &cursor); +CreditChoice parseCreditChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeCreditChoice(const CreditChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/CreditChoiceGroup.cpp b/src/private/mx/core/generated/CreditChoiceGroup.cpp index 7b5b55bca..0303dd934 100644 --- a/src/private/mx/core/generated/CreditChoiceGroup.cpp +++ b/src/private/mx/core/generated/CreditChoiceGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/CreditChoiceGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,12 +36,12 @@ void CreditChoiceGroup::setGroup(std::vector value) m_group = std::move(value); } -CreditChoiceGroup parseCreditChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor) +CreditChoiceGroup parseCreditChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { CreditChoiceGroup out; if (cursor && (cursorIs(cursor, "credit-words") || cursorIs(cursor, "credit-symbol"))) { - out.setChoice(parseCreditChoiceGroupChoice(el, cursor)); + out.setChoice(parseCreditChoiceGroupChoice(el, cursor, context)); } else { @@ -49,7 +50,7 @@ CreditChoiceGroup parseCreditChoiceGroup(pugi::xml_node el, pugi::xml_node &curs while (cursor && (cursorIs(cursor, "link") || cursorIs(cursor, "bookmark") || cursorIs(cursor, "credit-words") || cursorIs(cursor, "credit-symbol"))) { - out.addGroup(parseCreditChoiceGroupGroup(el, cursor)); + out.addGroup(parseCreditChoiceGroupGroup(el, cursor, context)); } return out; } diff --git a/src/private/mx/core/generated/CreditChoiceGroup.h b/src/private/mx/core/generated/CreditChoiceGroup.h index d00d0dde1..99a712c85 100644 --- a/src/private/mx/core/generated/CreditChoiceGroup.h +++ b/src/private/mx/core/generated/CreditChoiceGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -38,7 +40,7 @@ class CreditChoiceGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -CreditChoiceGroup parseCreditChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor); +CreditChoiceGroup parseCreditChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeCreditChoiceGroup(const CreditChoiceGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/CreditChoiceGroupChoice.cpp b/src/private/mx/core/generated/CreditChoiceGroupChoice.cpp index 4416f08b6..e134cd82b 100644 --- a/src/private/mx/core/generated/CreditChoiceGroupChoice.cpp +++ b/src/private/mx/core/generated/CreditChoiceGroupChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/CreditChoiceGroupChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,17 +26,18 @@ CreditChoiceGroupChoice CreditChoiceGroupChoice::creditSymbol(FormattedSymbolID return CreditChoiceGroupChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -CreditChoiceGroupChoice parseCreditChoiceGroupChoice(pugi::xml_node el, pugi::xml_node &cursor) +CreditChoiceGroupChoice parseCreditChoiceGroupChoice(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context) { if (cursor && (cursorIs(cursor, "credit-words"))) { - FormattedTextID value = parseFormattedTextID(cursor); + FormattedTextID value = parseFormattedTextID(cursor, context); cursor = nextElement(cursor); return CreditChoiceGroupChoice::creditWords(std::move(value)); } if (cursor && (cursorIs(cursor, "credit-symbol"))) { - FormattedSymbolID value = parseFormattedSymbolID(cursor); + FormattedSymbolID value = parseFormattedSymbolID(cursor, context); cursor = nextElement(cursor); return CreditChoiceGroupChoice::creditSymbol(std::move(value)); } diff --git a/src/private/mx/core/generated/CreditChoiceGroupChoice.h b/src/private/mx/core/generated/CreditChoiceGroupChoice.h index eb82acfc3..8be47776f 100644 --- a/src/private/mx/core/generated/CreditChoiceGroupChoice.h +++ b/src/private/mx/core/generated/CreditChoiceGroupChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -80,7 +82,8 @@ class CreditChoiceGroupChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -CreditChoiceGroupChoice parseCreditChoiceGroupChoice(pugi::xml_node el, pugi::xml_node &cursor); +CreditChoiceGroupChoice parseCreditChoiceGroupChoice(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context); void serializeCreditChoiceGroupChoice(const CreditChoiceGroupChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/CreditChoiceGroupGroup.cpp b/src/private/mx/core/generated/CreditChoiceGroupGroup.cpp index f829b8836..476ae59c5 100644 --- a/src/private/mx/core/generated/CreditChoiceGroupGroup.cpp +++ b/src/private/mx/core/generated/CreditChoiceGroupGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/CreditChoiceGroupGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,22 +51,23 @@ void CreditChoiceGroupGroup::setChoice(CreditChoiceGroupGroupChoice value) m_choice = std::move(value); } -CreditChoiceGroupGroup parseCreditChoiceGroupGroup(pugi::xml_node el, pugi::xml_node &cursor) +CreditChoiceGroupGroup parseCreditChoiceGroupGroup(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context) { CreditChoiceGroupGroup out; while (cursorIs(cursor, "link")) { - out.addLink(parseLink(cursor)); + out.addLink(parseLink(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "bookmark")) { - out.addBookmark(parseBookmark(cursor)); + out.addBookmark(parseBookmark(cursor, context)); cursor = nextElement(cursor); } if (cursor && (cursorIs(cursor, "credit-words") || cursorIs(cursor, "credit-symbol"))) { - out.setChoice(parseCreditChoiceGroupGroupChoice(el, cursor)); + out.setChoice(parseCreditChoiceGroupGroupChoice(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/CreditChoiceGroupGroup.h b/src/private/mx/core/generated/CreditChoiceGroupGroup.h index 5938aafae..d893ad34c 100644 --- a/src/private/mx/core/generated/CreditChoiceGroupGroup.h +++ b/src/private/mx/core/generated/CreditChoiceGroupGroup.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -43,7 +45,8 @@ class CreditChoiceGroupGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -CreditChoiceGroupGroup parseCreditChoiceGroupGroup(pugi::xml_node el, pugi::xml_node &cursor); +CreditChoiceGroupGroup parseCreditChoiceGroupGroup(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context); void serializeCreditChoiceGroupGroup(const CreditChoiceGroupGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/CreditChoiceGroupGroupChoice.cpp b/src/private/mx/core/generated/CreditChoiceGroupGroupChoice.cpp index 69332ddb3..efc53cff6 100644 --- a/src/private/mx/core/generated/CreditChoiceGroupGroupChoice.cpp +++ b/src/private/mx/core/generated/CreditChoiceGroupGroupChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/CreditChoiceGroupGroupChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,17 +26,18 @@ CreditChoiceGroupGroupChoice CreditChoiceGroupGroupChoice::creditSymbol(Formatte return CreditChoiceGroupGroupChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -CreditChoiceGroupGroupChoice parseCreditChoiceGroupGroupChoice(pugi::xml_node el, pugi::xml_node &cursor) +CreditChoiceGroupGroupChoice parseCreditChoiceGroupGroupChoice(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context) { if (cursor && (cursorIs(cursor, "credit-words"))) { - FormattedTextID value = parseFormattedTextID(cursor); + FormattedTextID value = parseFormattedTextID(cursor, context); cursor = nextElement(cursor); return CreditChoiceGroupGroupChoice::creditWords(std::move(value)); } if (cursor && (cursorIs(cursor, "credit-symbol"))) { - FormattedSymbolID value = parseFormattedSymbolID(cursor); + FormattedSymbolID value = parseFormattedSymbolID(cursor, context); cursor = nextElement(cursor); return CreditChoiceGroupGroupChoice::creditSymbol(std::move(value)); } diff --git a/src/private/mx/core/generated/CreditChoiceGroupGroupChoice.h b/src/private/mx/core/generated/CreditChoiceGroupGroupChoice.h index 3b895919d..45a88d74f 100644 --- a/src/private/mx/core/generated/CreditChoiceGroupGroupChoice.h +++ b/src/private/mx/core/generated/CreditChoiceGroupGroupChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -80,7 +82,8 @@ class CreditChoiceGroupGroupChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -CreditChoiceGroupGroupChoice parseCreditChoiceGroupGroupChoice(pugi::xml_node el, pugi::xml_node &cursor); +CreditChoiceGroupGroupChoice parseCreditChoiceGroupGroupChoice(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context); void serializeCreditChoiceGroupGroupChoice(const CreditChoiceGroupGroupChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/CueNoteGroup.cpp b/src/private/mx/core/generated/CueNoteGroup.cpp index b7aad8f9e..011c0f988 100644 --- a/src/private/mx/core/generated/CueNoteGroup.cpp +++ b/src/private/mx/core/generated/CueNoteGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/CueNoteGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,12 +41,12 @@ void CueNoteGroup::setDuration(PositiveDivisions value) m_duration = std::move(value); } -CueNoteGroup parseCueNoteGroup(pugi::xml_node el, pugi::xml_node &cursor) +CueNoteGroup parseCueNoteGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { CueNoteGroup out; if (cursorIs(cursor, "cue")) { - out.setCue(parseEmpty(cursor)); + out.setCue(parseEmpty(cursor, context)); cursor = nextElement(cursor); } else @@ -55,7 +56,7 @@ CueNoteGroup parseCueNoteGroup(pugi::xml_node el, pugi::xml_node &cursor) if (cursor && (cursorIs(cursor, "chord") || cursorIs(cursor, "pitch") || cursorIs(cursor, "unpitched") || cursorIs(cursor, "rest"))) { - out.setFullNote(parseFullNoteGroup(el, cursor)); + out.setFullNote(parseFullNoteGroup(el, cursor, context)); } else { @@ -63,7 +64,7 @@ CueNoteGroup parseCueNoteGroup(pugi::xml_node el, pugi::xml_node &cursor) } if (cursorIs(cursor, "duration")) { - out.setDuration(PositiveDivisions::parse(childText(cursor))); + out.setDuration(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/CueNoteGroup.h b/src/private/mx/core/generated/CueNoteGroup.h index 4ddbb3c81..b33c68703 100644 --- a/src/private/mx/core/generated/CueNoteGroup.h +++ b/src/private/mx/core/generated/CueNoteGroup.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -41,7 +43,7 @@ class CueNoteGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -CueNoteGroup parseCueNoteGroup(pugi::xml_node el, pugi::xml_node &cursor); +CueNoteGroup parseCueNoteGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeCueNoteGroup(const CueNoteGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Dashes.cpp b/src/private/mx/core/generated/Dashes.cpp index b43dac5d7..a3a973b9e 100644 --- a/src/private/mx/core/generated/Dashes.cpp +++ b/src/private/mx/core/generated/Dashes.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Dashes.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -110,7 +111,7 @@ void Dashes::setID(std::optional value) m_id = std::move(value); } -Dashes parseDashes(pugi::xml_node el) +Dashes parseDashes(pugi::xml_node el, const ParseContext &context) { Dashes out; bool seen_type = false; @@ -124,43 +125,43 @@ Dashes parseDashes(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStopContinue::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "number") { - out.setNumber(NumberLevel::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "dash-length") { - out.setDashLength(Tenths::parse(a.value())); + out.setDashLength(parseValue(a.value(), context, el, "dash-length")); } else if (aname == "space-length") { - out.setSpaceLength(Tenths::parse(a.value())); + out.setSpaceLength(parseValue(a.value(), context, el, "space-length")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -171,13 +172,14 @@ Dashes parseDashes(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseDashesContent(out, el); + parseDashesContent(out, el, context); return out; } -void parseDashesContent(Dashes &out, pugi::xml_node el) +void parseDashesContent(Dashes &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Dashes.h b/src/private/mx/core/generated/Dashes.h index 48f471374..60ac3806b 100644 --- a/src/private/mx/core/generated/Dashes.h +++ b/src/private/mx/core/generated/Dashes.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The dashes type represents dashes, used for instance with cresc. and dim. marks. class Dashes final { @@ -59,9 +61,9 @@ class Dashes final std::optional m_id; }; -Dashes parseDashes(pugi::xml_node el); +Dashes parseDashes(pugi::xml_node el, const ParseContext &context); -void parseDashesContent(Dashes &out, pugi::xml_node el); +void parseDashesContent(Dashes &out, pugi::xml_node el, const ParseContext &context); void serializeDashes(const Dashes &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Defaults.cpp b/src/private/mx/core/generated/Defaults.cpp index 45de3e5af..2ae847a40 100644 --- a/src/private/mx/core/generated/Defaults.cpp +++ b/src/private/mx/core/generated/Defaults.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Defaults.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -100,7 +101,7 @@ void Defaults::setLyricLanguage(std::vector value) m_lyricLanguage = std::move(value); } -Defaults parseDefaults(pugi::xml_node el) +Defaults parseDefaults(pugi::xml_node el, const ParseContext &context) { Defaults out; for (pugi::xml_attribute a : el.attributes()) @@ -112,52 +113,52 @@ Defaults parseDefaults(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseDefaultsContent(out, el); + parseDefaultsContent(out, el, context); return out; } -void parseDefaultsContent(Defaults &out, pugi::xml_node el) +void parseDefaultsContent(Defaults &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "scaling")) { - out.setScaling(parseScaling(cursor)); + out.setScaling(parseScaling(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "concert-score")) { - parseEmpty(cursor); + parseEmpty(cursor, context); out.setConcertScore(true); cursor = nextElement(cursor); } if (cursor && (cursorIs(cursor, "page-layout") || cursorIs(cursor, "system-layout") || cursorIs(cursor, "staff-layout"))) { - out.setLayout(parseLayoutGroup(el, cursor)); + out.setLayout(parseLayoutGroup(el, cursor, context)); } if (cursorIs(cursor, "appearance")) { - out.setAppearance(parseAppearance(cursor)); + out.setAppearance(parseAppearance(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "music-font")) { - out.setMusicFont(parseEmptyFont(cursor)); + out.setMusicFont(parseEmptyFont(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "word-font")) { - out.setWordFont(parseEmptyFont(cursor)); + out.setWordFont(parseEmptyFont(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "lyric-font")) { - out.addLyricFont(parseLyricFont(cursor)); + out.addLyricFont(parseLyricFont(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "lyric-language")) { - out.addLyricLanguage(parseLyricLanguage(cursor)); + out.addLyricLanguage(parseLyricLanguage(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Defaults.h b/src/private/mx/core/generated/Defaults.h index cbd9a034e..73163939b 100644 --- a/src/private/mx/core/generated/Defaults.h +++ b/src/private/mx/core/generated/Defaults.h @@ -23,6 +23,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The defaults type specifies score-wide defaults for scaling; whether or not the file is a concert /// score; layout; and default values for the music font, word font, lyric font, and lyric language. /// Except for the concert-score element, if any defaults are missing, the choice of what to use is @@ -63,9 +65,9 @@ class Defaults final std::vector m_lyricLanguage; }; -Defaults parseDefaults(pugi::xml_node el); +Defaults parseDefaults(pugi::xml_node el, const ParseContext &context); -void parseDefaultsContent(Defaults &out, pugi::xml_node el); +void parseDefaultsContent(Defaults &out, pugi::xml_node el, const ParseContext &context); void serializeDefaults(const Defaults &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/DefaultsProbe.cpp b/src/private/mx/core/generated/DefaultsProbe.cpp index 754659b3b..9f4b71aad 100644 --- a/src/private/mx/core/generated/DefaultsProbe.cpp +++ b/src/private/mx/core/generated/DefaultsProbe.cpp @@ -2,6 +2,7 @@ #include "mx/core/generated/DefaultsProbe.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include "mx/core/generated/Accidental.h" #include "mx/core/generated/AccidentalMark.h" @@ -319,1142 +320,1142 @@ void roundTripDefaults() { pugi::xml_document doc; serializeAccidental(Accidental{}, doc, "probe"); - parseAccidental(doc.document_element()); + parseAccidental(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeAccidentalMark(AccidentalMark{}, doc, "probe"); - parseAccidentalMark(doc.document_element()); + parseAccidentalMark(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeAccidentalText(AccidentalText{}, doc, "probe"); - parseAccidentalText(doc.document_element()); + parseAccidentalText(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeAccord(Accord{}, doc, "probe"); - parseAccord(doc.document_element()); + parseAccord(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeEmpty(Empty{}, doc, "probe"); - parseEmpty(doc.document_element()); + parseEmpty(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeAccordionRegistration(AccordionRegistration{}, doc, "probe"); - parseAccordionRegistration(doc.document_element()); + parseAccordionRegistration(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeDistance(Distance{}, doc, "probe"); - parseDistance(doc.document_element()); + parseDistance(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeGlyph(Glyph{}, doc, "probe"); - parseGlyph(doc.document_element()); + parseGlyph(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeLineWidth(LineWidth{}, doc, "probe"); - parseLineWidth(doc.document_element()); + parseLineWidth(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeNoteSize(NoteSize{}, doc, "probe"); - parseNoteSize(doc.document_element()); + parseNoteSize(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeOtherAppearance(OtherAppearance{}, doc, "probe"); - parseOtherAppearance(doc.document_element()); + parseOtherAppearance(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeAppearance(Appearance{}, doc, "probe"); - parseAppearance(doc.document_element()); + parseAppearance(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeArpeggiate(Arpeggiate{}, doc, "probe"); - parseArpeggiate(doc.document_element()); + parseArpeggiate(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeArrow(Arrow{}, doc, "probe"); - parseArrow(doc.document_element()); + parseArrow(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeBreathMark(BreathMark{}, doc, "probe"); - parseBreathMark(doc.document_element()); + parseBreathMark(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeCaesura(Caesura{}, doc, "probe"); - parseCaesura(doc.document_element()); + parseCaesura(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeEmptyLine(EmptyLine{}, doc, "probe"); - parseEmptyLine(doc.document_element()); + parseEmptyLine(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeEmptyPlacement(EmptyPlacement{}, doc, "probe"); - parseEmptyPlacement(doc.document_element()); + parseEmptyPlacement(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeOtherPlacementText(OtherPlacementText{}, doc, "probe"); - parseOtherPlacementText(doc.document_element()); + parseOtherPlacementText(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeStrongAccent(StrongAccent{}, doc, "probe"); - parseStrongAccent(doc.document_element()); + parseStrongAccent(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeArticulations(Articulations{}, doc, "probe"); - parseArticulations(doc.document_element()); + parseArticulations(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeAssess(Assess{}, doc, "probe"); - parseAssess(doc.document_element()); + parseAssess(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeClef(Clef{}, doc, "probe"); - parseClef(doc.document_element()); + parseClef(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeDirective(Directive{}, doc, "probe"); - parseDirective(doc.document_element()); + parseDirective(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePartClef(PartClef{}, doc, "probe"); - parsePartClef(doc.document_element()); + parsePartClef(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeDouble(Double{}, doc, "probe"); - parseDouble(doc.document_element()); + parseDouble(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePartTranspose(PartTranspose{}, doc, "probe"); - parsePartTranspose(doc.document_element()); + parsePartTranspose(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeForPart(ForPart{}, doc, "probe"); - parseForPart(doc.document_element()); + parseForPart(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeFormattedText(FormattedText{}, doc, "probe"); - parseFormattedText(doc.document_element()); + parseFormattedText(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeCancel(Cancel{}, doc, "probe"); - parseCancel(doc.document_element()); + parseCancel(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeKeyAccidental(KeyAccidental{}, doc, "probe"); - parseKeyAccidental(doc.document_element()); + parseKeyAccidental(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeKeyOctave(KeyOctave{}, doc, "probe"); - parseKeyOctave(doc.document_element()); + parseKeyOctave(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeKey(Key{}, doc, "probe"); - parseKey(doc.document_element()); + parseKey(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeLevel(Level{}, doc, "probe"); - parseLevel(doc.document_element()); + parseLevel(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeBeatRepeat(BeatRepeat{}, doc, "probe"); - parseBeatRepeat(doc.document_element()); + parseBeatRepeat(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeMeasureRepeat(MeasureRepeat{}, doc, "probe"); - parseMeasureRepeat(doc.document_element()); + parseMeasureRepeat(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeMultipleRest(MultipleRest{}, doc, "probe"); - parseMultipleRest(doc.document_element()); + parseMultipleRest(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeSlash(Slash{}, doc, "probe"); - parseSlash(doc.document_element()); + parseSlash(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeMeasureStyle(MeasureStyle{}, doc, "probe"); - parseMeasureStyle(doc.document_element()); + parseMeasureStyle(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePartSymbol(PartSymbol{}, doc, "probe"); - parsePartSymbol(doc.document_element()); + parsePartSymbol(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeLineDetail(LineDetail{}, doc, "probe"); - parseLineDetail(doc.document_element()); + parseLineDetail(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeStaffSize(StaffSize{}, doc, "probe"); - parseStaffSize(doc.document_element()); + parseStaffSize(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeStaffTuning(StaffTuning{}, doc, "probe"); - parseStaffTuning(doc.document_element()); + parseStaffTuning(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeStaffDetails(StaffDetails{}, doc, "probe"); - parseStaffDetails(doc.document_element()); + parseStaffDetails(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeInterchangeable(Interchangeable{}, doc, "probe"); - parseInterchangeable(doc.document_element()); + parseInterchangeable(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTime(Time{}, doc, "probe"); - parseTime(doc.document_element()); + parseTime(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTranspose(Transpose{}, doc, "probe"); - parseTranspose(doc.document_element()); + parseTranspose(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeAttributes(Attributes{}, doc, "probe"); - parseAttributes(doc.document_element()); + parseAttributes(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeBackup(Backup{}, doc, "probe"); - parseBackup(doc.document_element()); + parseBackup(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeBarStyleColor(BarStyleColor{}, doc, "probe"); - parseBarStyleColor(doc.document_element()); + parseBarStyleColor(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeCoda(Coda{}, doc, "probe"); - parseCoda(doc.document_element()); + parseCoda(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeEnding(Ending{}, doc, "probe"); - parseEnding(doc.document_element()); + parseEnding(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeFermata(Fermata{}, doc, "probe"); - parseFermata(doc.document_element()); + parseFermata(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeRepeat(Repeat{}, doc, "probe"); - parseRepeat(doc.document_element()); + parseRepeat(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeSegno(Segno{}, doc, "probe"); - parseSegno(doc.document_element()); + parseSegno(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeWavyLine(WavyLine{}, doc, "probe"); - parseWavyLine(doc.document_element()); + parseWavyLine(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeBarline(Barline{}, doc, "probe"); - parseBarline(doc.document_element()); + parseBarline(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeBarre(Barre{}, doc, "probe"); - parseBarre(doc.document_element()); + parseBarre(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeBassStep(BassStep{}, doc, "probe"); - parseBassStep(doc.document_element()); + parseBassStep(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeHarmonyAlter(HarmonyAlter{}, doc, "probe"); - parseHarmonyAlter(doc.document_element()); + parseHarmonyAlter(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeStyleText(StyleText{}, doc, "probe"); - parseStyleText(doc.document_element()); + parseStyleText(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeBass(Bass{}, doc, "probe"); - parseBass(doc.document_element()); + parseBass(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeBeam(Beam{}, doc, "probe"); - parseBeam(doc.document_element()); + parseBeam(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeBeatUnitTied(BeatUnitTied{}, doc, "probe"); - parseBeatUnitTied(doc.document_element()); + parseBeatUnitTied(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeBeater(Beater{}, doc, "probe"); - parseBeater(doc.document_element()); + parseBeater(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePlacementText(PlacementText{}, doc, "probe"); - parsePlacementText(doc.document_element()); + parsePlacementText(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeRelease(Release{}, doc, "probe"); - parseRelease(doc.document_element()); + parseRelease(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeBend(Bend{}, doc, "probe"); - parseBend(doc.document_element()); + parseBend(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeBookmark(Bookmark{}, doc, "probe"); - parseBookmark(doc.document_element()); + parseBookmark(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeBracket(Bracket{}, doc, "probe"); - parseBracket(doc.document_element()); + parseBracket(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeFormattedSymbolID(FormattedSymbolID{}, doc, "probe"); - parseFormattedSymbolID(doc.document_element()); + parseFormattedSymbolID(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeFormattedTextID(FormattedTextID{}, doc, "probe"); - parseFormattedTextID(doc.document_element()); + parseFormattedTextID(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeImage(Image{}, doc, "probe"); - parseImage(doc.document_element()); + parseImage(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeLink(Link{}, doc, "probe"); - parseLink(doc.document_element()); + parseLink(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeCredit(Credit{}, doc, "probe"); - parseCredit(doc.document_element()); + parseCredit(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeDashes(Dashes{}, doc, "probe"); - parseDashes(doc.document_element()); + parseDashes(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeEmptyFont(EmptyFont{}, doc, "probe"); - parseEmptyFont(doc.document_element()); + parseEmptyFont(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeLyricFont(LyricFont{}, doc, "probe"); - parseLyricFont(doc.document_element()); + parseLyricFont(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeLyricLanguage(LyricLanguage{}, doc, "probe"); - parseLyricLanguage(doc.document_element()); + parseLyricLanguage(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePageMargins(PageMargins{}, doc, "probe"); - parsePageMargins(doc.document_element()); + parsePageMargins(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePageLayout(PageLayout{}, doc, "probe"); - parsePageLayout(doc.document_element()); + parsePageLayout(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeScaling(Scaling{}, doc, "probe"); - parseScaling(doc.document_element()); + parseScaling(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeStaffLayout(StaffLayout{}, doc, "probe"); - parseStaffLayout(doc.document_element()); + parseStaffLayout(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeEmptyPrintObjectStyleAlign(EmptyPrintObjectStyleAlign{}, doc, "probe"); - parseEmptyPrintObjectStyleAlign(doc.document_element()); + parseEmptyPrintObjectStyleAlign(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeSystemDividers(SystemDividers{}, doc, "probe"); - parseSystemDividers(doc.document_element()); + parseSystemDividers(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeSystemMargins(SystemMargins{}, doc, "probe"); - parseSystemMargins(doc.document_element()); + parseSystemMargins(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeSystemLayout(SystemLayout{}, doc, "probe"); - parseSystemLayout(doc.document_element()); + parseSystemLayout(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeDefaults(Defaults{}, doc, "probe"); - parseDefaults(doc.document_element()); + parseDefaults(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeDegreeAlter(DegreeAlter{}, doc, "probe"); - parseDegreeAlter(doc.document_element()); + parseDegreeAlter(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeDegreeType(DegreeType{}, doc, "probe"); - parseDegreeType(doc.document_element()); + parseDegreeType(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeDegreeValue(DegreeValue{}, doc, "probe"); - parseDegreeValue(doc.document_element()); + parseDegreeValue(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeDegree(Degree{}, doc, "probe"); - parseDegree(doc.document_element()); + parseDegree(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeOtherText(OtherText{}, doc, "probe"); - parseOtherText(doc.document_element()); + parseOtherText(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeDynamics(Dynamics{}, doc, "probe"); - parseDynamics(doc.document_element()); + parseDynamics(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeEmptyPrintStyleAlignID(EmptyPrintStyleAlignID{}, doc, "probe"); - parseEmptyPrintStyleAlignID(doc.document_element()); + parseEmptyPrintStyleAlignID(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePedalTuning(PedalTuning{}, doc, "probe"); - parsePedalTuning(doc.document_element()); + parsePedalTuning(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeHarpPedals(HarpPedals{}, doc, "probe"); - parseHarpPedals(doc.document_element()); + parseHarpPedals(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeMetronomeBeam(MetronomeBeam{}, doc, "probe"); - parseMetronomeBeam(doc.document_element()); + parseMetronomeBeam(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeMetronomeTied(MetronomeTied{}, doc, "probe"); - parseMetronomeTied(doc.document_element()); + parseMetronomeTied(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTimeModification(TimeModification{}, doc, "probe"); - parseTimeModification(doc.document_element()); + parseTimeModification(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeMetronomeTuplet(MetronomeTuplet{}, doc, "probe"); - parseMetronomeTuplet(doc.document_element()); + parseMetronomeTuplet(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeMetronomeNote(MetronomeNote{}, doc, "probe"); - parseMetronomeNote(doc.document_element()); + parseMetronomeNote(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePerMinute(PerMinute{}, doc, "probe"); - parsePerMinute(doc.document_element()); + parsePerMinute(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeMetronome(Metronome{}, doc, "probe"); - parseMetronome(doc.document_element()); + parseMetronome(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeOctaveShift(OctaveShift{}, doc, "probe"); - parseOctaveShift(doc.document_element()); + parseOctaveShift(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeOtherDirection(OtherDirection{}, doc, "probe"); - parseOtherDirection(doc.document_element()); + parseOtherDirection(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePedal(Pedal{}, doc, "probe"); - parsePedal(doc.document_element()); + parsePedal(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeEffect(Effect{}, doc, "probe"); - parseEffect(doc.document_element()); + parseEffect(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeGlass(Glass{}, doc, "probe"); - parseGlass(doc.document_element()); + parseGlass(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeMembrane(Membrane{}, doc, "probe"); - parseMembrane(doc.document_element()); + parseMembrane(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeMetal(Metal{}, doc, "probe"); - parseMetal(doc.document_element()); + parseMetal(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePitched(Pitched{}, doc, "probe"); - parsePitched(doc.document_element()); + parsePitched(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeStick(Stick{}, doc, "probe"); - parseStick(doc.document_element()); + parseStick(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTimpani(Timpani{}, doc, "probe"); - parseTimpani(doc.document_element()); + parseTimpani(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeWood(Wood{}, doc, "probe"); - parseWood(doc.document_element()); + parseWood(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePercussion(Percussion{}, doc, "probe"); - parsePercussion(doc.document_element()); + parsePercussion(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePrincipalVoice(PrincipalVoice{}, doc, "probe"); - parsePrincipalVoice(doc.document_element()); + parsePrincipalVoice(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeScordatura(Scordatura{}, doc, "probe"); - parseScordatura(doc.document_element()); + parseScordatura(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeStaffDivide(StaffDivide{}, doc, "probe"); - parseStaffDivide(doc.document_element()); + parseStaffDivide(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeStringMute(StringMute{}, doc, "probe"); - parseStringMute(doc.document_element()); + parseStringMute(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeWedge(Wedge{}, doc, "probe"); - parseWedge(doc.document_element()); + parseWedge(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeDirectionType(DirectionType{}, doc, "probe"); - parseDirectionType(doc.document_element()); + parseDirectionType(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeOffset(Offset{}, doc, "probe"); - parseOffset(doc.document_element()); + parseOffset(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeOtherListening(OtherListening{}, doc, "probe"); - parseOtherListening(doc.document_element()); + parseOtherListening(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeSync(Sync{}, doc, "probe"); - parseSync(doc.document_element()); + parseSync(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeListening(Listening{}, doc, "probe"); - parseListening(doc.document_element()); + parseListening(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeVirtualInstrument(VirtualInstrument{}, doc, "probe"); - parseVirtualInstrument(doc.document_element()); + parseVirtualInstrument(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeInstrumentChange(InstrumentChange{}, doc, "probe"); - parseInstrumentChange(doc.document_element()); + parseInstrumentChange(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeMIDIDevice(MIDIDevice{}, doc, "probe"); - parseMIDIDevice(doc.document_element()); + parseMIDIDevice(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeMIDIInstrument(MIDIInstrument{}, doc, "probe"); - parseMIDIInstrument(doc.document_element()); + parseMIDIInstrument(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeOtherPlay(OtherPlay{}, doc, "probe"); - parseOtherPlay(doc.document_element()); + parseOtherPlay(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePlay(Play{}, doc, "probe"); - parsePlay(doc.document_element()); + parsePlay(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeSwing(Swing{}, doc, "probe"); - parseSwing(doc.document_element()); + parseSwing(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeSound(Sound{}, doc, "probe"); - parseSound(doc.document_element()); + parseSound(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeDirection(Direction{}, doc, "probe"); - parseDirection(doc.document_element()); + parseDirection(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeElision(Elision{}, doc, "probe"); - parseElision(doc.document_element()); + parseElision(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeEmptyPlacementSmufl(EmptyPlacementSmufl{}, doc, "probe"); - parseEmptyPlacementSmufl(doc.document_element()); + parseEmptyPlacementSmufl(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeEmptyTrillSound(EmptyTrillSound{}, doc, "probe"); - parseEmptyTrillSound(doc.document_element()); + parseEmptyTrillSound(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeSupports(Supports{}, doc, "probe"); - parseSupports(doc.document_element()); + parseSupports(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTypedText(TypedText{}, doc, "probe"); - parseTypedText(doc.document_element()); + parseTypedText(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeEncoding(Encoding{}, doc, "probe"); - parseEncoding(doc.document_element()); + parseEncoding(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeExtend(Extend{}, doc, "probe"); - parseExtend(doc.document_element()); + parseExtend(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeFeature(Feature{}, doc, "probe"); - parseFeature(doc.document_element()); + parseFeature(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeFigure(Figure{}, doc, "probe"); - parseFigure(doc.document_element()); + parseFigure(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeFiguredBass(FiguredBass{}, doc, "probe"); - parseFiguredBass(doc.document_element()); + parseFiguredBass(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeFingering(Fingering{}, doc, "probe"); - parseFingering(doc.document_element()); + parseFingering(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeFirstFret(FirstFret{}, doc, "probe"); - parseFirstFret(doc.document_element()); + parseFirstFret(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeForward(Forward{}, doc, "probe"); - parseForward(doc.document_element()); + parseForward(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeFret(Fret{}, doc, "probe"); - parseFret(doc.document_element()); + parseFret(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeString(String{}, doc, "probe"); - parseString(doc.document_element()); + parseString(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeFrameNote(FrameNote{}, doc, "probe"); - parseFrameNote(doc.document_element()); + parseFrameNote(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeFrame(Frame{}, doc, "probe"); - parseFrame(doc.document_element()); + parseFrame(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeGlissando(Glissando{}, doc, "probe"); - parseGlissando(doc.document_element()); + parseGlissando(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeGrace(Grace{}, doc, "probe"); - parseGrace(doc.document_element()); + parseGrace(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeGroupBarline(GroupBarline{}, doc, "probe"); - parseGroupBarline(doc.document_element()); + parseGroupBarline(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeGroupName(GroupName{}, doc, "probe"); - parseGroupName(doc.document_element()); + parseGroupName(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeGroupSymbol(GroupSymbol{}, doc, "probe"); - parseGroupSymbol(doc.document_element()); + parseGroupSymbol(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeGrouping(Grouping{}, doc, "probe"); - parseGrouping(doc.document_element()); + parseGrouping(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeHammerOnPullOff(HammerOnPullOff{}, doc, "probe"); - parseHammerOnPullOff(doc.document_element()); + parseHammerOnPullOff(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeHandbell(Handbell{}, doc, "probe"); - parseHandbell(doc.document_element()); + parseHandbell(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeHarmonClosed(HarmonClosed{}, doc, "probe"); - parseHarmonClosed(doc.document_element()); + parseHarmonClosed(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeHarmonMute(HarmonMute{}, doc, "probe"); - parseHarmonMute(doc.document_element()); + parseHarmonMute(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeHarmonic(Harmonic{}, doc, "probe"); - parseHarmonic(doc.document_element()); + parseHarmonic(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeInversion(Inversion{}, doc, "probe"); - parseInversion(doc.document_element()); + parseInversion(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeKind(Kind{}, doc, "probe"); - parseKind(doc.document_element()); + parseKind(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeNumeralKey(NumeralKey{}, doc, "probe"); - parseNumeralKey(doc.document_element()); + parseNumeralKey(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeNumeralRoot(NumeralRoot{}, doc, "probe"); - parseNumeralRoot(doc.document_element()); + parseNumeralRoot(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeNumeral(Numeral{}, doc, "probe"); - parseNumeral(doc.document_element()); + parseNumeral(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeRootStep(RootStep{}, doc, "probe"); - parseRootStep(doc.document_element()); + parseRootStep(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeRoot(Root{}, doc, "probe"); - parseRoot(doc.document_element()); + parseRoot(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeHarmony(Harmony{}, doc, "probe"); - parseHarmony(doc.document_element()); + parseHarmony(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeHeelToe(HeelToe{}, doc, "probe"); - parseHeelToe(doc.document_element()); + parseHeelToe(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeHoleClosed(HoleClosed{}, doc, "probe"); - parseHoleClosed(doc.document_element()); + parseHoleClosed(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeHole(Hole{}, doc, "probe"); - parseHole(doc.document_element()); + parseHole(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeHorizontalTurn(HorizontalTurn{}, doc, "probe"); - parseHorizontalTurn(doc.document_element()); + parseHorizontalTurn(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeMiscellaneousField(MiscellaneousField{}, doc, "probe"); - parseMiscellaneousField(doc.document_element()); + parseMiscellaneousField(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeMiscellaneous(Miscellaneous{}, doc, "probe"); - parseMiscellaneous(doc.document_element()); + parseMiscellaneous(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeIdentification(Identification{}, doc, "probe"); - parseIdentification(doc.document_element()); + parseIdentification(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeInstrument(Instrument{}, doc, "probe"); - parseInstrument(doc.document_element()); + parseInstrument(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeInstrumentLink(InstrumentLink{}, doc, "probe"); - parseInstrumentLink(doc.document_element()); + parseInstrumentLink(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeWait(Wait{}, doc, "probe"); - parseWait(doc.document_element()); + parseWait(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeListen(Listen{}, doc, "probe"); - parseListen(doc.document_element()); + parseListen(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTextElementData(TextElementData{}, doc, "probe"); - parseTextElementData(doc.document_element()); + parseTextElementData(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeLyric(Lyric{}, doc, "probe"); - parseLyric(doc.document_element()); + parseLyric(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeMeasureLayout(MeasureLayout{}, doc, "probe"); - parseMeasureLayout(doc.document_element()); + parseMeasureLayout(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeMeasureNumbering(MeasureNumbering{}, doc, "probe"); - parseMeasureNumbering(doc.document_element()); + parseMeasureNumbering(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeMordent(Mordent{}, doc, "probe"); - parseMordent(doc.document_element()); + parseMordent(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeNameDisplay(NameDisplay{}, doc, "probe"); - parseNameDisplay(doc.document_element()); + parseNameDisplay(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeNonArpeggiate(NonArpeggiate{}, doc, "probe"); - parseNonArpeggiate(doc.document_element()); + parseNonArpeggiate(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTremolo(Tremolo{}, doc, "probe"); - parseTremolo(doc.document_element()); + parseTremolo(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeOrnaments(Ornaments{}, doc, "probe"); - parseOrnaments(doc.document_element()); + parseOrnaments(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeOtherNotation(OtherNotation{}, doc, "probe"); - parseOtherNotation(doc.document_element()); + parseOtherNotation(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeSlide(Slide{}, doc, "probe"); - parseSlide(doc.document_element()); + parseSlide(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeSlur(Slur{}, doc, "probe"); - parseSlur(doc.document_element()); + parseSlur(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTap(Tap{}, doc, "probe"); - parseTap(doc.document_element()); + parseTap(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTechnical(Technical{}, doc, "probe"); - parseTechnical(doc.document_element()); + parseTechnical(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTied(Tied{}, doc, "probe"); - parseTied(doc.document_element()); + parseTied(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTupletDot(TupletDot{}, doc, "probe"); - parseTupletDot(doc.document_element()); + parseTupletDot(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTupletNumber(TupletNumber{}, doc, "probe"); - parseTupletNumber(doc.document_element()); + parseTupletNumber(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTupletType(TupletType{}, doc, "probe"); - parseTupletType(doc.document_element()); + parseTupletType(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTupletPortion(TupletPortion{}, doc, "probe"); - parseTupletPortion(doc.document_element()); + parseTupletPortion(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTuplet(Tuplet{}, doc, "probe"); - parseTuplet(doc.document_element()); + parseTuplet(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeNotations(Notations{}, doc, "probe"); - parseNotations(doc.document_element()); + parseNotations(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeNoteType(NoteType{}, doc, "probe"); - parseNoteType(doc.document_element()); + parseNoteType(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeNotehead(Notehead{}, doc, "probe"); - parseNotehead(doc.document_element()); + parseNotehead(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeNoteheadText(NoteheadText{}, doc, "probe"); - parseNoteheadText(doc.document_element()); + parseNoteheadText(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePitch(Pitch{}, doc, "probe"); - parsePitch(doc.document_element()); + parsePitch(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeRest(Rest{}, doc, "probe"); - parseRest(doc.document_element()); + parseRest(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeStem(Stem{}, doc, "probe"); - parseStem(doc.document_element()); + parseStem(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTie(Tie{}, doc, "probe"); - parseTie(doc.document_element()); + parseTie(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeUnpitched(Unpitched{}, doc, "probe"); - parseUnpitched(doc.document_element()); + parseUnpitched(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeNote(Note{}, doc, "probe"); - parseNote(doc.document_element()); + parseNote(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeOpus(Opus{}, doc, "probe"); - parseOpus(doc.document_element()); + parseOpus(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePartGroup(PartGroup{}, doc, "probe"); - parsePartGroup(doc.document_element()); + parsePartGroup(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePartLink(PartLink{}, doc, "probe"); - parsePartLink(doc.document_element()); + parsePartLink(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePartName(PartName{}, doc, "probe"); - parsePartName(doc.document_element()); + parsePartName(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePlayer(Player{}, doc, "probe"); - parsePlayer(doc.document_element()); + parsePlayer(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeScoreInstrument(ScoreInstrument{}, doc, "probe"); - parseScoreInstrument(doc.document_element()); + parseScoreInstrument(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeScorePart(ScorePart{}, doc, "probe"); - parseScorePart(doc.document_element()); + parseScorePart(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePartList(PartList{}, doc, "probe"); - parsePartList(doc.document_element()); + parsePartList(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePrint(Print{}, doc, "probe"); - parsePrint(doc.document_element()); + parsePrint(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePartwiseMeasure(PartwiseMeasure{}, doc, "probe"); - parsePartwiseMeasure(doc.document_element()); + parsePartwiseMeasure(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializePartwisePart(PartwisePart{}, doc, "probe"); - parsePartwisePart(doc.document_element()); + parsePartwisePart(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeWork(Work{}, doc, "probe"); - parseWork(doc.document_element()); + parseWork(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeScorePartwise(ScorePartwise{}, doc, "probe"); - parseScorePartwise(doc.document_element()); + parseScorePartwise(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTimewisePart(TimewisePart{}, doc, "probe"); - parseTimewisePart(doc.document_element()); + parseTimewisePart(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeTimewiseMeasure(TimewiseMeasure{}, doc, "probe"); - parseTimewiseMeasure(doc.document_element()); + parseTimewiseMeasure(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; serializeScoreTimewise(ScoreTimewise{}, doc, "probe"); - parseScoreTimewise(doc.document_element()); + parseScoreTimewise(doc.document_element(), ParseContext{}); } { pugi::xml_document doc; @@ -1462,7 +1463,7 @@ void roundTripDefaults() serializeTuningGroup(TuningGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseTuningGroup(el, cursor); + parseTuningGroup(el, cursor, ParseContext{}); } } { @@ -1471,7 +1472,7 @@ void roundTripDefaults() serializeClefGroup(ClefGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseClefGroup(el, cursor); + parseClefGroup(el, cursor, ParseContext{}); } } { @@ -1480,7 +1481,7 @@ void roundTripDefaults() serializeTransposeGroup(TransposeGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseTransposeGroup(el, cursor); + parseTransposeGroup(el, cursor, ParseContext{}); } } { @@ -1489,7 +1490,7 @@ void roundTripDefaults() serializeTraditionalKeyGroup(TraditionalKeyGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseTraditionalKeyGroup(el, cursor); + parseTraditionalKeyGroup(el, cursor, ParseContext{}); } } { @@ -1498,7 +1499,7 @@ void roundTripDefaults() serializeNonTraditionalKeyGroup(NonTraditionalKeyGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseNonTraditionalKeyGroup(el, cursor); + parseNonTraditionalKeyGroup(el, cursor, ParseContext{}); } } { @@ -1507,7 +1508,7 @@ void roundTripDefaults() serializeSlashGroup(SlashGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseSlashGroup(el, cursor); + parseSlashGroup(el, cursor, ParseContext{}); } } { @@ -1516,7 +1517,7 @@ void roundTripDefaults() serializeTimeSignatureGroup(TimeSignatureGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseTimeSignatureGroup(el, cursor); + parseTimeSignatureGroup(el, cursor, ParseContext{}); } } { @@ -1525,7 +1526,7 @@ void roundTripDefaults() serializeEditorialGroup(EditorialGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseEditorialGroup(el, cursor); + parseEditorialGroup(el, cursor, ParseContext{}); } } { @@ -1534,7 +1535,7 @@ void roundTripDefaults() serializeBeatUnitGroup(BeatUnitGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseBeatUnitGroup(el, cursor); + parseBeatUnitGroup(el, cursor, ParseContext{}); } } { @@ -1543,7 +1544,7 @@ void roundTripDefaults() serializeAllMarginsGroup(AllMarginsGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseAllMarginsGroup(el, cursor); + parseAllMarginsGroup(el, cursor, ParseContext{}); } } { @@ -1552,7 +1553,7 @@ void roundTripDefaults() serializeLeftRightMarginsGroup(LeftRightMarginsGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseLeftRightMarginsGroup(el, cursor); + parseLeftRightMarginsGroup(el, cursor, ParseContext{}); } } { @@ -1561,7 +1562,7 @@ void roundTripDefaults() serializeLayoutGroup(LayoutGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseLayoutGroup(el, cursor); + parseLayoutGroup(el, cursor, ParseContext{}); } } { @@ -1570,7 +1571,7 @@ void roundTripDefaults() serializeVirtualInstrumentDataGroup(VirtualInstrumentDataGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseVirtualInstrumentDataGroup(el, cursor); + parseVirtualInstrumentDataGroup(el, cursor, ParseContext{}); } } { @@ -1579,7 +1580,7 @@ void roundTripDefaults() serializeEditorialVoiceDirectionGroup(EditorialVoiceDirectionGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseEditorialVoiceDirectionGroup(el, cursor); + parseEditorialVoiceDirectionGroup(el, cursor, ParseContext{}); } } { @@ -1588,7 +1589,7 @@ void roundTripDefaults() serializeEditorialVoiceGroup(EditorialVoiceGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseEditorialVoiceGroup(el, cursor); + parseEditorialVoiceGroup(el, cursor, ParseContext{}); } } { @@ -1597,7 +1598,7 @@ void roundTripDefaults() serializeHarmonyChordGroup(HarmonyChordGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseHarmonyChordGroup(el, cursor); + parseHarmonyChordGroup(el, cursor, ParseContext{}); } } { @@ -1606,7 +1607,7 @@ void roundTripDefaults() serializeDisplayStepOctaveGroup(DisplayStepOctaveGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseDisplayStepOctaveGroup(el, cursor); + parseDisplayStepOctaveGroup(el, cursor, ParseContext{}); } } { @@ -1615,7 +1616,7 @@ void roundTripDefaults() serializeFullNoteGroup(FullNoteGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseFullNoteGroup(el, cursor); + parseFullNoteGroup(el, cursor, ParseContext{}); } } { @@ -1624,7 +1625,7 @@ void roundTripDefaults() serializeMusicDataChoice(MusicDataChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseMusicDataChoice(el, cursor); + parseMusicDataChoice(el, cursor, ParseContext{}); } } { @@ -1633,7 +1634,7 @@ void roundTripDefaults() serializeScoreHeaderGroup(ScoreHeaderGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseScoreHeaderGroup(el, cursor); + parseScoreHeaderGroup(el, cursor, ParseContext{}); } } { @@ -1642,7 +1643,7 @@ void roundTripDefaults() serializeArrowChoice(ArrowChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseArrowChoice(el, cursor); + parseArrowChoice(el, cursor, ParseContext{}); } } { @@ -1651,7 +1652,7 @@ void roundTripDefaults() serializeArrowChoiceGroup(ArrowChoiceGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseArrowChoiceGroup(el, cursor); + parseArrowChoiceGroup(el, cursor, ParseContext{}); } } { @@ -1660,7 +1661,7 @@ void roundTripDefaults() serializeArticulationsChoice(ArticulationsChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseArticulationsChoice(el, cursor); + parseArticulationsChoice(el, cursor, ParseContext{}); } } { @@ -1669,7 +1670,7 @@ void roundTripDefaults() serializeKeyChoice(KeyChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseKeyChoice(el, cursor); + parseKeyChoice(el, cursor, ParseContext{}); } } { @@ -1678,7 +1679,7 @@ void roundTripDefaults() serializeSlashGroupGroup(SlashGroupGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseSlashGroupGroup(el, cursor); + parseSlashGroupGroup(el, cursor, ParseContext{}); } } { @@ -1687,7 +1688,7 @@ void roundTripDefaults() serializeMeasureStyleChoice(MeasureStyleChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseMeasureStyleChoice(el, cursor); + parseMeasureStyleChoice(el, cursor, ParseContext{}); } } { @@ -1696,7 +1697,7 @@ void roundTripDefaults() serializeStaffDetailsGroup(StaffDetailsGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseStaffDetailsGroup(el, cursor); + parseStaffDetailsGroup(el, cursor, ParseContext{}); } } { @@ -1705,7 +1706,7 @@ void roundTripDefaults() serializeTimeChoice(TimeChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseTimeChoice(el, cursor); + parseTimeChoice(el, cursor, ParseContext{}); } } { @@ -1714,7 +1715,7 @@ void roundTripDefaults() serializeTimeChoiceGroup(TimeChoiceGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseTimeChoiceGroup(el, cursor); + parseTimeChoiceGroup(el, cursor, ParseContext{}); } } { @@ -1723,7 +1724,7 @@ void roundTripDefaults() serializeAttributesChoice(AttributesChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseAttributesChoice(el, cursor); + parseAttributesChoice(el, cursor, ParseContext{}); } } { @@ -1732,7 +1733,7 @@ void roundTripDefaults() serializeBendChoice(BendChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseBendChoice(el, cursor); + parseBendChoice(el, cursor, ParseContext{}); } } { @@ -1741,7 +1742,7 @@ void roundTripDefaults() serializeCreditChoice(CreditChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseCreditChoice(el, cursor); + parseCreditChoice(el, cursor, ParseContext{}); } } { @@ -1750,7 +1751,7 @@ void roundTripDefaults() serializeCreditChoiceGroup(CreditChoiceGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseCreditChoiceGroup(el, cursor); + parseCreditChoiceGroup(el, cursor, ParseContext{}); } } { @@ -1759,7 +1760,7 @@ void roundTripDefaults() serializeCreditChoiceGroupChoice(CreditChoiceGroupChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseCreditChoiceGroupChoice(el, cursor); + parseCreditChoiceGroupChoice(el, cursor, ParseContext{}); } } { @@ -1768,7 +1769,7 @@ void roundTripDefaults() serializeCreditChoiceGroupGroup(CreditChoiceGroupGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseCreditChoiceGroupGroup(el, cursor); + parseCreditChoiceGroupGroup(el, cursor, ParseContext{}); } } { @@ -1777,7 +1778,7 @@ void roundTripDefaults() serializeCreditChoiceGroupGroupChoice(CreditChoiceGroupGroupChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseCreditChoiceGroupGroupChoice(el, cursor); + parseCreditChoiceGroupGroupChoice(el, cursor, ParseContext{}); } } { @@ -1786,7 +1787,7 @@ void roundTripDefaults() serializePageLayoutGroup(PageLayoutGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parsePageLayoutGroup(el, cursor); + parsePageLayoutGroup(el, cursor, ParseContext{}); } } { @@ -1795,7 +1796,7 @@ void roundTripDefaults() serializeDynamicsChoice(DynamicsChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseDynamicsChoice(el, cursor); + parseDynamicsChoice(el, cursor, ParseContext{}); } } { @@ -1804,7 +1805,7 @@ void roundTripDefaults() serializeTimeModificationGroup(TimeModificationGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseTimeModificationGroup(el, cursor); + parseTimeModificationGroup(el, cursor, ParseContext{}); } } { @@ -1813,7 +1814,7 @@ void roundTripDefaults() serializeMetronomeChoice(MetronomeChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseMetronomeChoice(el, cursor); + parseMetronomeChoice(el, cursor, ParseContext{}); } } { @@ -1822,7 +1823,7 @@ void roundTripDefaults() serializeMetronomeChoiceGroup(MetronomeChoiceGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseMetronomeChoiceGroup(el, cursor); + parseMetronomeChoiceGroup(el, cursor, ParseContext{}); } } { @@ -1831,7 +1832,7 @@ void roundTripDefaults() serializeMetronomeChoiceGroupChoice(MetronomeChoiceGroupChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseMetronomeChoiceGroupChoice(el, cursor); + parseMetronomeChoiceGroupChoice(el, cursor, ParseContext{}); } } { @@ -1840,7 +1841,7 @@ void roundTripDefaults() serializeMetronomeChoiceGroupChoiceGroup(MetronomeChoiceGroupChoiceGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseMetronomeChoiceGroupChoiceGroup(el, cursor); + parseMetronomeChoiceGroupChoiceGroup(el, cursor, ParseContext{}); } } { @@ -1849,7 +1850,7 @@ void roundTripDefaults() serializeMetronomeChoiceGroup2(MetronomeChoiceGroup2{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseMetronomeChoiceGroup2(el, cursor); + parseMetronomeChoiceGroup2(el, cursor, ParseContext{}); } } { @@ -1858,7 +1859,7 @@ void roundTripDefaults() serializeMetronomeChoiceGroup2Group(MetronomeChoiceGroup2Group{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseMetronomeChoiceGroup2Group(el, cursor); + parseMetronomeChoiceGroup2Group(el, cursor, ParseContext{}); } } { @@ -1867,7 +1868,7 @@ void roundTripDefaults() serializePercussionChoice(PercussionChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parsePercussionChoice(el, cursor); + parsePercussionChoice(el, cursor, ParseContext{}); } } { @@ -1876,7 +1877,7 @@ void roundTripDefaults() serializeDirectionTypeChoice(DirectionTypeChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseDirectionTypeChoice(el, cursor); + parseDirectionTypeChoice(el, cursor, ParseContext{}); } } { @@ -1885,7 +1886,7 @@ void roundTripDefaults() serializeDirectionTypeChoiceChoice(DirectionTypeChoiceChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseDirectionTypeChoiceChoice(el, cursor); + parseDirectionTypeChoiceChoice(el, cursor, ParseContext{}); } } { @@ -1894,7 +1895,7 @@ void roundTripDefaults() serializeListeningChoice(ListeningChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseListeningChoice(el, cursor); + parseListeningChoice(el, cursor, ParseContext{}); } } { @@ -1903,7 +1904,7 @@ void roundTripDefaults() serializeVirtualInstrumentDataGroupChoice(VirtualInstrumentDataGroupChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseVirtualInstrumentDataGroupChoice(el, cursor); + parseVirtualInstrumentDataGroupChoice(el, cursor, ParseContext{}); } } { @@ -1912,7 +1913,7 @@ void roundTripDefaults() serializePlayChoice(PlayChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parsePlayChoice(el, cursor); + parsePlayChoice(el, cursor, ParseContext{}); } } { @@ -1921,7 +1922,7 @@ void roundTripDefaults() serializeSwingChoice(SwingChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseSwingChoice(el, cursor); + parseSwingChoice(el, cursor, ParseContext{}); } } { @@ -1930,7 +1931,7 @@ void roundTripDefaults() serializeSwingChoiceGroup(SwingChoiceGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseSwingChoiceGroup(el, cursor); + parseSwingChoiceGroup(el, cursor, ParseContext{}); } } { @@ -1939,7 +1940,7 @@ void roundTripDefaults() serializeSoundGroup(SoundGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseSoundGroup(el, cursor); + parseSoundGroup(el, cursor, ParseContext{}); } } { @@ -1948,7 +1949,7 @@ void roundTripDefaults() serializeEncodingChoice(EncodingChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseEncodingChoice(el, cursor); + parseEncodingChoice(el, cursor, ParseContext{}); } } { @@ -1957,7 +1958,7 @@ void roundTripDefaults() serializeHarmonicChoice(HarmonicChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseHarmonicChoice(el, cursor); + parseHarmonicChoice(el, cursor, ParseContext{}); } } { @@ -1966,7 +1967,7 @@ void roundTripDefaults() serializeHarmonicChoice2(HarmonicChoice2{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseHarmonicChoice2(el, cursor); + parseHarmonicChoice2(el, cursor, ParseContext{}); } } { @@ -1975,7 +1976,7 @@ void roundTripDefaults() serializeHarmonyChordGroupChoice(HarmonyChordGroupChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseHarmonyChordGroupChoice(el, cursor); + parseHarmonyChordGroupChoice(el, cursor, ParseContext{}); } } { @@ -1984,7 +1985,7 @@ void roundTripDefaults() serializeListenChoice(ListenChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseListenChoice(el, cursor); + parseListenChoice(el, cursor, ParseContext{}); } } { @@ -1993,7 +1994,7 @@ void roundTripDefaults() serializeLyricChoice(LyricChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseLyricChoice(el, cursor); + parseLyricChoice(el, cursor, ParseContext{}); } } { @@ -2002,7 +2003,7 @@ void roundTripDefaults() serializeLyricTextGroup(LyricTextGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseLyricTextGroup(el, cursor); + parseLyricTextGroup(el, cursor, ParseContext{}); } } { @@ -2011,7 +2012,7 @@ void roundTripDefaults() serializeLyricSyllableGroup(LyricSyllableGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseLyricSyllableGroup(el, cursor); + parseLyricSyllableGroup(el, cursor, ParseContext{}); } } { @@ -2020,7 +2021,7 @@ void roundTripDefaults() serializeElisionSyllabicGroup(ElisionSyllabicGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseElisionSyllabicGroup(el, cursor); + parseElisionSyllabicGroup(el, cursor, ParseContext{}); } } { @@ -2029,7 +2030,7 @@ void roundTripDefaults() serializeNameDisplayChoice(NameDisplayChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseNameDisplayChoice(el, cursor); + parseNameDisplayChoice(el, cursor, ParseContext{}); } } { @@ -2038,7 +2039,7 @@ void roundTripDefaults() serializeOrnamentsGroup(OrnamentsGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseOrnamentsGroup(el, cursor); + parseOrnamentsGroup(el, cursor, ParseContext{}); } } { @@ -2047,7 +2048,7 @@ void roundTripDefaults() serializeOrnamentsGroupChoice(OrnamentsGroupChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseOrnamentsGroupChoice(el, cursor); + parseOrnamentsGroupChoice(el, cursor, ParseContext{}); } } { @@ -2056,7 +2057,7 @@ void roundTripDefaults() serializeTechnicalChoice(TechnicalChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseTechnicalChoice(el, cursor); + parseTechnicalChoice(el, cursor, ParseContext{}); } } { @@ -2065,7 +2066,7 @@ void roundTripDefaults() serializeNotationsChoice(NotationsChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseNotationsChoice(el, cursor); + parseNotationsChoice(el, cursor, ParseContext{}); } } { @@ -2074,7 +2075,7 @@ void roundTripDefaults() serializeNoteheadTextChoice(NoteheadTextChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseNoteheadTextChoice(el, cursor); + parseNoteheadTextChoice(el, cursor, ParseContext{}); } } { @@ -2083,7 +2084,7 @@ void roundTripDefaults() serializeNoteChoice(NoteChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseNoteChoice(el, cursor); + parseNoteChoice(el, cursor, ParseContext{}); } } { @@ -2092,7 +2093,7 @@ void roundTripDefaults() serializeGraceNoteGroup(GraceNoteGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseGraceNoteGroup(el, cursor); + parseGraceNoteGroup(el, cursor, ParseContext{}); } } { @@ -2101,7 +2102,7 @@ void roundTripDefaults() serializeGraceNoteChoice(GraceNoteChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseGraceNoteChoice(el, cursor); + parseGraceNoteChoice(el, cursor, ParseContext{}); } } { @@ -2110,7 +2111,7 @@ void roundTripDefaults() serializeGraceNormalNoteGroup(GraceNormalNoteGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseGraceNormalNoteGroup(el, cursor); + parseGraceNormalNoteGroup(el, cursor, ParseContext{}); } } { @@ -2119,7 +2120,7 @@ void roundTripDefaults() serializeFullNoteGroupChoice(FullNoteGroupChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseFullNoteGroupChoice(el, cursor); + parseFullNoteGroupChoice(el, cursor, ParseContext{}); } } { @@ -2128,7 +2129,7 @@ void roundTripDefaults() serializeGraceCueNoteGroup(GraceCueNoteGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseGraceCueNoteGroup(el, cursor); + parseGraceCueNoteGroup(el, cursor, ParseContext{}); } } { @@ -2137,7 +2138,7 @@ void roundTripDefaults() serializeCueNoteGroup(CueNoteGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseCueNoteGroup(el, cursor); + parseCueNoteGroup(el, cursor, ParseContext{}); } } { @@ -2146,7 +2147,7 @@ void roundTripDefaults() serializeNormalNoteGroup(NormalNoteGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseNormalNoteGroup(el, cursor); + parseNormalNoteGroup(el, cursor, ParseContext{}); } } { @@ -2155,7 +2156,7 @@ void roundTripDefaults() serializeScorePartMIDIGroup(ScorePartMIDIGroup{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parseScorePartMIDIGroup(el, cursor); + parseScorePartMIDIGroup(el, cursor, ParseContext{}); } } { @@ -2164,7 +2165,7 @@ void roundTripDefaults() serializePartListChoice(PartListChoice{}, el); if (pugi::xml_node cursor = firstElement(el)) { - parsePartListChoice(el, cursor); + parsePartListChoice(el, cursor, ParseContext{}); } } } diff --git a/src/private/mx/core/generated/Degree.cpp b/src/private/mx/core/generated/Degree.cpp index 9fbad5ae6..6a8a98b5f 100644 --- a/src/private/mx/core/generated/Degree.cpp +++ b/src/private/mx/core/generated/Degree.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Degree.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,7 +51,7 @@ void Degree::setDegreeType(DegreeType value) m_degreeType = std::move(value); } -Degree parseDegree(pugi::xml_node el) +Degree parseDegree(pugi::xml_node el, const ParseContext &context) { Degree out; for (pugi::xml_attribute a : el.attributes()) @@ -62,23 +63,23 @@ Degree parseDegree(pugi::xml_node el) } if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else { throwUnknownAttribute(el, a.name()); } } - parseDegreeContent(out, el); + parseDegreeContent(out, el, context); return out; } -void parseDegreeContent(Degree &out, pugi::xml_node el) +void parseDegreeContent(Degree &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "degree-value")) { - out.setDegreeValue(parseDegreeValue(cursor)); + out.setDegreeValue(parseDegreeValue(cursor, context)); cursor = nextElement(cursor); } else @@ -87,7 +88,7 @@ void parseDegreeContent(Degree &out, pugi::xml_node el) } if (cursorIs(cursor, "degree-alter")) { - out.setDegreeAlter(parseDegreeAlter(cursor)); + out.setDegreeAlter(parseDegreeAlter(cursor, context)); cursor = nextElement(cursor); } else @@ -96,7 +97,7 @@ void parseDegreeContent(Degree &out, pugi::xml_node el) } if (cursorIs(cursor, "degree-type")) { - out.setDegreeType(parseDegreeType(cursor)); + out.setDegreeType(parseDegreeType(cursor, context)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/Degree.h b/src/private/mx/core/generated/Degree.h index ddc095c03..24cbe756c 100644 --- a/src/private/mx/core/generated/Degree.h +++ b/src/private/mx/core/generated/Degree.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The degree type is used to add, alter, or subtract individual notes in the chord. The /// print-object attribute can be used to keep the degree from printing separately when it has /// already taken into account in the text attribute of the kind element. The degree-value and @@ -48,9 +50,9 @@ class Degree final DegreeType m_degreeType{}; }; -Degree parseDegree(pugi::xml_node el); +Degree parseDegree(pugi::xml_node el, const ParseContext &context); -void parseDegreeContent(Degree &out, pugi::xml_node el); +void parseDegreeContent(Degree &out, pugi::xml_node el, const ParseContext &context); void serializeDegree(const Degree &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/DegreeAlter.cpp b/src/private/mx/core/generated/DegreeAlter.cpp index e5d5472d2..127557012 100644 --- a/src/private/mx/core/generated/DegreeAlter.cpp +++ b/src/private/mx/core/generated/DegreeAlter.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/DegreeAlter.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -120,7 +121,7 @@ void DegreeAlter::setValue(Semitones value) m_value = std::move(value); } -DegreeAlter parseDegreeAlter(pugi::xml_node el) +DegreeAlter parseDegreeAlter(pugi::xml_node el, const ParseContext &context) { DegreeAlter out; for (pugi::xml_attribute a : el.attributes()) @@ -132,56 +133,56 @@ DegreeAlter parseDegreeAlter(pugi::xml_node el) } if (aname == "plus-minus") { - out.setPlusMinus(YesNo::parse(a.value())); + out.setPlusMinus(parseValue(a.value(), context, el, "plus-minus")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseDegreeAlterContent(out, el); + parseDegreeAlterContent(out, el, context); return out; } -void parseDegreeAlterContent(DegreeAlter &out, pugi::xml_node el) +void parseDegreeAlterContent(DegreeAlter &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(Semitones::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/DegreeAlter.h b/src/private/mx/core/generated/DegreeAlter.h index 6e248fc3d..e76d72640 100644 --- a/src/private/mx/core/generated/DegreeAlter.h +++ b/src/private/mx/core/generated/DegreeAlter.h @@ -22,6 +22,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The degree-alter type represents the chromatic alteration for the current degree. If the /// degree-type value is alter or subtract, the degree-alter value is relative to the degree already /// in the chord based on its kind element. If the degree-type value is add, the degree-alter is @@ -69,9 +71,9 @@ class DegreeAlter final Semitones m_value{}; }; -DegreeAlter parseDegreeAlter(pugi::xml_node el); +DegreeAlter parseDegreeAlter(pugi::xml_node el, const ParseContext &context); -void parseDegreeAlterContent(DegreeAlter &out, pugi::xml_node el); +void parseDegreeAlterContent(DegreeAlter &out, pugi::xml_node el, const ParseContext &context); void serializeDegreeAlter(const DegreeAlter &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/DegreeSymbolValue.cpp b/src/private/mx/core/generated/DegreeSymbolValue.cpp index 0b9dde810..c61cf1b77 100644 --- a/src/private/mx/core/generated/DegreeSymbolValue.cpp +++ b/src/private/mx/core/generated/DegreeSymbolValue.cpp @@ -56,9 +56,15 @@ bool DegreeSymbolValue::tryParse(std::string_view text, DegreeSymbolValue &out) } DegreeSymbolValue DegreeSymbolValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +DegreeSymbolValue DegreeSymbolValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { DegreeSymbolValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/DegreeSymbolValue.h b/src/private/mx/core/generated/DegreeSymbolValue.h index 9a59fd104..3210eafd7 100644 --- a/src/private/mx/core/generated/DegreeSymbolValue.h +++ b/src/private/mx/core/generated/DegreeSymbolValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -46,6 +48,9 @@ class DegreeSymbolValue final /// (the import leniency policy; never produces an invalid value). static DegreeSymbolValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static DegreeSymbolValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const DegreeSymbolValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/DegreeType.cpp b/src/private/mx/core/generated/DegreeType.cpp index 4dbff0e34..668de3da2 100644 --- a/src/private/mx/core/generated/DegreeType.cpp +++ b/src/private/mx/core/generated/DegreeType.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/DegreeType.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -120,7 +121,7 @@ void DegreeType::setValue(DegreeTypeValue value) m_value = std::move(value); } -DegreeType parseDegreeType(pugi::xml_node el) +DegreeType parseDegreeType(pugi::xml_node el, const ParseContext &context) { DegreeType out; for (pugi::xml_attribute a : el.attributes()) @@ -136,52 +137,52 @@ DegreeType parseDegreeType(pugi::xml_node el) } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseDegreeTypeContent(out, el); + parseDegreeTypeContent(out, el, context); return out; } -void parseDegreeTypeContent(DegreeType &out, pugi::xml_node el) +void parseDegreeTypeContent(DegreeType &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(DegreeTypeValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/DegreeType.h b/src/private/mx/core/generated/DegreeType.h index c722c7b5e..a130afb07 100644 --- a/src/private/mx/core/generated/DegreeType.h +++ b/src/private/mx/core/generated/DegreeType.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The degree-type type indicates if this degree is an addition, alteration, or subtraction relative /// to the kind of the current chord. The value of the degree-type element affects the interpretation /// of the value of the degree-alter element. The text attribute specifies how the type of the degree @@ -66,9 +68,9 @@ class DegreeType final DegreeTypeValue m_value{}; }; -DegreeType parseDegreeType(pugi::xml_node el); +DegreeType parseDegreeType(pugi::xml_node el, const ParseContext &context); -void parseDegreeTypeContent(DegreeType &out, pugi::xml_node el); +void parseDegreeTypeContent(DegreeType &out, pugi::xml_node el, const ParseContext &context); void serializeDegreeType(const DegreeType &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/DegreeTypeValue.cpp b/src/private/mx/core/generated/DegreeTypeValue.cpp index 27f86f312..8966dd0bb 100644 --- a/src/private/mx/core/generated/DegreeTypeValue.cpp +++ b/src/private/mx/core/generated/DegreeTypeValue.cpp @@ -48,9 +48,15 @@ bool DegreeTypeValue::tryParse(std::string_view text, DegreeTypeValue &out) noex } DegreeTypeValue DegreeTypeValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +DegreeTypeValue DegreeTypeValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { DegreeTypeValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/DegreeTypeValue.h b/src/private/mx/core/generated/DegreeTypeValue.h index 1fe966d06..a1e3c651a 100644 --- a/src/private/mx/core/generated/DegreeTypeValue.h +++ b/src/private/mx/core/generated/DegreeTypeValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -43,6 +45,9 @@ class DegreeTypeValue final /// (the import leniency policy; never produces an invalid value). static DegreeTypeValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static DegreeTypeValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const DegreeTypeValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/DegreeValue.cpp b/src/private/mx/core/generated/DegreeValue.cpp index c99f2b6ed..b88ada1f7 100644 --- a/src/private/mx/core/generated/DegreeValue.cpp +++ b/src/private/mx/core/generated/DegreeValue.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/DegreeValue.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -130,7 +131,7 @@ void DegreeValue::setValue(int value) m_value = std::move(value); } -DegreeValue parseDegreeValue(pugi::xml_node el) +DegreeValue parseDegreeValue(pugi::xml_node el, const ParseContext &context) { DegreeValue out; for (pugi::xml_attribute a : el.attributes()) @@ -142,7 +143,7 @@ DegreeValue parseDegreeValue(pugi::xml_node el) } if (aname == "symbol") { - out.setSymbol(DegreeSymbolValue::parse(a.value())); + out.setSymbol(parseValue(a.value(), context, el, "symbol")); } else if (aname == "text") { @@ -150,52 +151,52 @@ DegreeValue parseDegreeValue(pugi::xml_node el) } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseDegreeValueContent(out, el); + parseDegreeValueContent(out, el, context); return out; } -void parseDegreeValueContent(DegreeValue &out, pugi::xml_node el) +void parseDegreeValueContent(DegreeValue &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(parseInt(childText(el))); + out.setValue(parseIntegerValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/DegreeValue.h b/src/private/mx/core/generated/DegreeValue.h index a19adf854..830a86648 100644 --- a/src/private/mx/core/generated/DegreeValue.h +++ b/src/private/mx/core/generated/DegreeValue.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The content of the degree-value type is a number indicating the degree of the chord (1 for the /// root, 3 for third, etc). The text attribute specifies how the value of the degree should be /// displayed. The symbol attribute indicates that a symbol should be used in specifying the degree. @@ -69,9 +71,9 @@ class DegreeValue final int m_value{}; }; -DegreeValue parseDegreeValue(pugi::xml_node el); +DegreeValue parseDegreeValue(pugi::xml_node el, const ParseContext &context); -void parseDegreeValueContent(DegreeValue &out, pugi::xml_node el); +void parseDegreeValueContent(DegreeValue &out, pugi::xml_node el, const ParseContext &context); void serializeDegreeValue(const DegreeValue &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Direction.cpp b/src/private/mx/core/generated/Direction.cpp index ef76c2a89..5f68b6714 100644 --- a/src/private/mx/core/generated/Direction.cpp +++ b/src/private/mx/core/generated/Direction.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Direction.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -115,7 +116,7 @@ void Direction::setListening(std::optional value) m_listening = std::move(value); } -Direction parseDirection(pugi::xml_node el) +Direction parseDirection(pugi::xml_node el, const ParseContext &context) { Direction out; for (pugi::xml_attribute a : el.attributes()) @@ -127,65 +128,65 @@ Direction parseDirection(pugi::xml_node el) } if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "directive") { - out.setDirective(YesNo::parse(a.value())); + out.setDirective(parseValue(a.value(), context, el, "directive")); } else if (aname == "system") { - out.setSystem(SystemRelation::parse(a.value())); + out.setSystem(parseValue(a.value(), context, el, "system")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseDirectionContent(out, el); + parseDirectionContent(out, el, context); return out; } -void parseDirectionContent(Direction &out, pugi::xml_node el) +void parseDirectionContent(Direction &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (!cursorIs(cursor, "direction-type")) { throwMissingOrMisplaced(el, cursor, "direction-type"); } - out.setDirectionType(OneOrMore{parseDirectionType(cursor)}); + out.setDirectionType(OneOrMore{parseDirectionType(cursor, context)}); cursor = nextElement(cursor); while (cursorIs(cursor, "direction-type")) { - out.addDirectionType(parseDirectionType(cursor)); + out.addDirectionType(parseDirectionType(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "offset")) { - out.setOffset(parseOffset(cursor)); + out.setOffset(parseOffset(cursor, context)); cursor = nextElement(cursor); } if (cursor && (cursorIs(cursor, "footnote") || cursorIs(cursor, "level") || cursorIs(cursor, "voice"))) { - out.setEditorialVoiceDirection(parseEditorialVoiceDirectionGroup(el, cursor)); + out.setEditorialVoiceDirection(parseEditorialVoiceDirectionGroup(el, cursor, context)); } if (cursorIs(cursor, "staff")) { - out.setStaff(parseInt(childText(cursor))); + out.setStaff(parseIntegerValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "sound")) { - out.setSound(parseSound(cursor)); + out.setSound(parseSound(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "listening")) { - out.setListening(parseListening(cursor)); + out.setListening(parseListening(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Direction.h b/src/private/mx/core/generated/Direction.h index 6bcd3f65f..ddc91eb4a 100644 --- a/src/private/mx/core/generated/Direction.h +++ b/src/private/mx/core/generated/Direction.h @@ -27,6 +27,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A direction is a musical indication that is not necessarily attached to a specific note. Two or /// more may be combined to indicate words followed by the start of a dashed line, the end of a wedge /// followed by dynamics, etc. For applications where a specific direction is indeed attached to a @@ -76,9 +78,9 @@ class Direction final std::optional m_listening; }; -Direction parseDirection(pugi::xml_node el); +Direction parseDirection(pugi::xml_node el, const ParseContext &context); -void parseDirectionContent(Direction &out, pugi::xml_node el); +void parseDirectionContent(Direction &out, pugi::xml_node el, const ParseContext &context); void serializeDirection(const Direction &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/DirectionType.cpp b/src/private/mx/core/generated/DirectionType.cpp index 135bed160..f40056e8f 100644 --- a/src/private/mx/core/generated/DirectionType.cpp +++ b/src/private/mx/core/generated/DirectionType.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/DirectionType.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void DirectionType::setChoice(DirectionTypeChoice value) m_choice = std::move(value); } -DirectionType parseDirectionType(pugi::xml_node el) +DirectionType parseDirectionType(pugi::xml_node el, const ParseContext &context) { DirectionType out; for (pugi::xml_attribute a : el.attributes()) @@ -42,18 +43,18 @@ DirectionType parseDirectionType(pugi::xml_node el) } if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseDirectionTypeContent(out, el); + parseDirectionTypeContent(out, el, context); return out; } -void parseDirectionTypeContent(DirectionType &out, pugi::xml_node el) +void parseDirectionTypeContent(DirectionType &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "rehearsal") || cursorIs(cursor, "segno") || cursorIs(cursor, "coda") || @@ -66,7 +67,7 @@ void parseDirectionTypeContent(DirectionType &out, pugi::xml_node el) cursorIs(cursor, "percussion") || cursorIs(cursor, "accordion-registration") || cursorIs(cursor, "staff-divide") || cursorIs(cursor, "other-direction"))) { - out.setChoice(parseDirectionTypeChoice(el, cursor)); + out.setChoice(parseDirectionTypeChoice(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/DirectionType.h b/src/private/mx/core/generated/DirectionType.h index 14d6d9c90..9e7035359 100644 --- a/src/private/mx/core/generated/DirectionType.h +++ b/src/private/mx/core/generated/DirectionType.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Textual direction types may have more than 1 component due to multiple fonts. The dynamics /// element may also be used in the notations element. Attribute groups related to print suggestions /// apply to the individual direction-type, not to the overall direction. @@ -38,9 +40,9 @@ class DirectionType final DirectionTypeChoice m_choice{}; }; -DirectionType parseDirectionType(pugi::xml_node el); +DirectionType parseDirectionType(pugi::xml_node el, const ParseContext &context); -void parseDirectionTypeContent(DirectionType &out, pugi::xml_node el); +void parseDirectionTypeContent(DirectionType &out, pugi::xml_node el, const ParseContext &context); void serializeDirectionType(const DirectionType &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/DirectionTypeChoice.cpp b/src/private/mx/core/generated/DirectionTypeChoice.cpp index ff9b8c4ac..61ca801a1 100644 --- a/src/private/mx/core/generated/DirectionTypeChoice.cpp +++ b/src/private/mx/core/generated/DirectionTypeChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/DirectionTypeChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -130,171 +131,171 @@ DirectionTypeChoice DirectionTypeChoice::otherDirection(OtherDirection value) return DirectionTypeChoice{Storage{std::in_place_index<22>, std::move(value)}}; } -DirectionTypeChoice parseDirectionTypeChoice(pugi::xml_node el, pugi::xml_node &cursor) +DirectionTypeChoice parseDirectionTypeChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "rehearsal"))) { - OneOrMore items{parseFormattedTextID(cursor)}; + OneOrMore items{parseFormattedTextID(cursor, context)}; cursor = nextElement(cursor); while (cursor && (cursorIs(cursor, "rehearsal"))) { - items.add(parseFormattedTextID(cursor)); + items.add(parseFormattedTextID(cursor, context)); cursor = nextElement(cursor); } return DirectionTypeChoice::rehearsal(std::move(items)); } if (cursor && (cursorIs(cursor, "segno"))) { - OneOrMore items{parseSegno(cursor)}; + OneOrMore items{parseSegno(cursor, context)}; cursor = nextElement(cursor); while (cursor && (cursorIs(cursor, "segno"))) { - items.add(parseSegno(cursor)); + items.add(parseSegno(cursor, context)); cursor = nextElement(cursor); } return DirectionTypeChoice::segno(std::move(items)); } if (cursor && (cursorIs(cursor, "coda"))) { - OneOrMore items{parseCoda(cursor)}; + OneOrMore items{parseCoda(cursor, context)}; cursor = nextElement(cursor); while (cursor && (cursorIs(cursor, "coda"))) { - items.add(parseCoda(cursor)); + items.add(parseCoda(cursor, context)); cursor = nextElement(cursor); } return DirectionTypeChoice::coda(std::move(items)); } if (cursor && (cursorIs(cursor, "words") || cursorIs(cursor, "symbol"))) { - OneOrMore items{parseDirectionTypeChoiceChoice(el, cursor)}; + OneOrMore items{parseDirectionTypeChoiceChoice(el, cursor, context)}; while (cursor && (cursorIs(cursor, "words") || cursorIs(cursor, "symbol"))) { - items.add(parseDirectionTypeChoiceChoice(el, cursor)); + items.add(parseDirectionTypeChoiceChoice(el, cursor, context)); } return DirectionTypeChoice::choice(std::move(items)); } if (cursor && (cursorIs(cursor, "wedge"))) { - Wedge value = parseWedge(cursor); + Wedge value = parseWedge(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoice::wedge(std::move(value)); } if (cursor && (cursorIs(cursor, "dynamics"))) { - OneOrMore items{parseDynamics(cursor)}; + OneOrMore items{parseDynamics(cursor, context)}; cursor = nextElement(cursor); while (cursor && (cursorIs(cursor, "dynamics"))) { - items.add(parseDynamics(cursor)); + items.add(parseDynamics(cursor, context)); cursor = nextElement(cursor); } return DirectionTypeChoice::dynamics(std::move(items)); } if (cursor && (cursorIs(cursor, "dashes"))) { - Dashes value = parseDashes(cursor); + Dashes value = parseDashes(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoice::dashes(std::move(value)); } if (cursor && (cursorIs(cursor, "bracket"))) { - Bracket value = parseBracket(cursor); + Bracket value = parseBracket(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoice::bracket(std::move(value)); } if (cursor && (cursorIs(cursor, "pedal"))) { - Pedal value = parsePedal(cursor); + Pedal value = parsePedal(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoice::pedal(std::move(value)); } if (cursor && (cursorIs(cursor, "metronome"))) { - Metronome value = parseMetronome(cursor); + Metronome value = parseMetronome(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoice::metronome(std::move(value)); } if (cursor && (cursorIs(cursor, "octave-shift"))) { - OctaveShift value = parseOctaveShift(cursor); + OctaveShift value = parseOctaveShift(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoice::octaveShift(std::move(value)); } if (cursor && (cursorIs(cursor, "harp-pedals"))) { - HarpPedals value = parseHarpPedals(cursor); + HarpPedals value = parseHarpPedals(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoice::harpPedals(std::move(value)); } if (cursor && (cursorIs(cursor, "damp"))) { - EmptyPrintStyleAlignID value = parseEmptyPrintStyleAlignID(cursor); + EmptyPrintStyleAlignID value = parseEmptyPrintStyleAlignID(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoice::damp(std::move(value)); } if (cursor && (cursorIs(cursor, "damp-all"))) { - EmptyPrintStyleAlignID value = parseEmptyPrintStyleAlignID(cursor); + EmptyPrintStyleAlignID value = parseEmptyPrintStyleAlignID(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoice::dampAll(std::move(value)); } if (cursor && (cursorIs(cursor, "eyeglasses"))) { - EmptyPrintStyleAlignID value = parseEmptyPrintStyleAlignID(cursor); + EmptyPrintStyleAlignID value = parseEmptyPrintStyleAlignID(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoice::eyeglasses(std::move(value)); } if (cursor && (cursorIs(cursor, "string-mute"))) { - StringMute value = parseStringMute(cursor); + StringMute value = parseStringMute(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoice::stringMute(std::move(value)); } if (cursor && (cursorIs(cursor, "scordatura"))) { - Scordatura value = parseScordatura(cursor); + Scordatura value = parseScordatura(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoice::scordatura(std::move(value)); } if (cursor && (cursorIs(cursor, "image"))) { - Image value = parseImage(cursor); + Image value = parseImage(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoice::image(std::move(value)); } if (cursor && (cursorIs(cursor, "principal-voice"))) { - PrincipalVoice value = parsePrincipalVoice(cursor); + PrincipalVoice value = parsePrincipalVoice(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoice::principalVoice(std::move(value)); } if (cursor && (cursorIs(cursor, "percussion"))) { - OneOrMore items{parsePercussion(cursor)}; + OneOrMore items{parsePercussion(cursor, context)}; cursor = nextElement(cursor); while (cursor && (cursorIs(cursor, "percussion"))) { - items.add(parsePercussion(cursor)); + items.add(parsePercussion(cursor, context)); cursor = nextElement(cursor); } return DirectionTypeChoice::percussion(std::move(items)); } if (cursor && (cursorIs(cursor, "accordion-registration"))) { - AccordionRegistration value = parseAccordionRegistration(cursor); + AccordionRegistration value = parseAccordionRegistration(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoice::accordionRegistration(std::move(value)); } if (cursor && (cursorIs(cursor, "staff-divide"))) { - StaffDivide value = parseStaffDivide(cursor); + StaffDivide value = parseStaffDivide(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoice::staffDivide(std::move(value)); } if (cursor && (cursorIs(cursor, "other-direction"))) { - OtherDirection value = parseOtherDirection(cursor); + OtherDirection value = parseOtherDirection(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoice::otherDirection(std::move(value)); } diff --git a/src/private/mx/core/generated/DirectionTypeChoice.h b/src/private/mx/core/generated/DirectionTypeChoice.h index 2042d0908..12cc82486 100644 --- a/src/private/mx/core/generated/DirectionTypeChoice.h +++ b/src/private/mx/core/generated/DirectionTypeChoice.h @@ -38,6 +38,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -399,7 +401,7 @@ class DirectionTypeChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -DirectionTypeChoice parseDirectionTypeChoice(pugi::xml_node el, pugi::xml_node &cursor); +DirectionTypeChoice parseDirectionTypeChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeDirectionTypeChoice(const DirectionTypeChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/DirectionTypeChoiceChoice.cpp b/src/private/mx/core/generated/DirectionTypeChoiceChoice.cpp index d6db71b7e..55989c376 100644 --- a/src/private/mx/core/generated/DirectionTypeChoiceChoice.cpp +++ b/src/private/mx/core/generated/DirectionTypeChoiceChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/DirectionTypeChoiceChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,17 +26,18 @@ DirectionTypeChoiceChoice DirectionTypeChoiceChoice::symbol(FormattedSymbolID va return DirectionTypeChoiceChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -DirectionTypeChoiceChoice parseDirectionTypeChoiceChoice(pugi::xml_node el, pugi::xml_node &cursor) +DirectionTypeChoiceChoice parseDirectionTypeChoiceChoice(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context) { if (cursor && (cursorIs(cursor, "words"))) { - FormattedTextID value = parseFormattedTextID(cursor); + FormattedTextID value = parseFormattedTextID(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoiceChoice::words(std::move(value)); } if (cursor && (cursorIs(cursor, "symbol"))) { - FormattedSymbolID value = parseFormattedSymbolID(cursor); + FormattedSymbolID value = parseFormattedSymbolID(cursor, context); cursor = nextElement(cursor); return DirectionTypeChoiceChoice::symbol(std::move(value)); } diff --git a/src/private/mx/core/generated/DirectionTypeChoiceChoice.h b/src/private/mx/core/generated/DirectionTypeChoiceChoice.h index ab3d1c02f..c6cbfe1f8 100644 --- a/src/private/mx/core/generated/DirectionTypeChoiceChoice.h +++ b/src/private/mx/core/generated/DirectionTypeChoiceChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -80,7 +82,8 @@ class DirectionTypeChoiceChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -DirectionTypeChoiceChoice parseDirectionTypeChoiceChoice(pugi::xml_node el, pugi::xml_node &cursor); +DirectionTypeChoiceChoice parseDirectionTypeChoiceChoice(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context); void serializeDirectionTypeChoiceChoice(const DirectionTypeChoiceChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Directive.cpp b/src/private/mx/core/generated/Directive.cpp index d13894542..0f2da367d 100644 --- a/src/private/mx/core/generated/Directive.cpp +++ b/src/private/mx/core/generated/Directive.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Directive.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -120,7 +121,7 @@ void Directive::setValue(std::string value) m_value = std::move(value); } -Directive parseDirective(pugi::xml_node el) +Directive parseDirective(pugi::xml_node el, const ParseContext &context) { Directive out; for (pugi::xml_attribute a : el.attributes()) @@ -136,50 +137,50 @@ Directive parseDirective(pugi::xml_node el) } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseDirectiveContent(out, el); + parseDirectiveContent(out, el, context); return out; } -void parseDirectiveContent(Directive &out, pugi::xml_node el) +void parseDirectiveContent(Directive &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/Directive.h b/src/private/mx/core/generated/Directive.h index 6249adeb4..ad1da1ef7 100644 --- a/src/private/mx/core/generated/Directive.h +++ b/src/private/mx/core/generated/Directive.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + class Directive final { public: @@ -61,9 +63,9 @@ class Directive final std::string m_value{}; }; -Directive parseDirective(pugi::xml_node el); +Directive parseDirective(pugi::xml_node el, const ParseContext &context); -void parseDirectiveContent(Directive &out, pugi::xml_node el); +void parseDirectiveContent(Directive &out, pugi::xml_node el, const ParseContext &context); void serializeDirective(const Directive &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/DisplayStepOctaveGroup.cpp b/src/private/mx/core/generated/DisplayStepOctaveGroup.cpp index 981245331..02b6bdd69 100644 --- a/src/private/mx/core/generated/DisplayStepOctaveGroup.cpp +++ b/src/private/mx/core/generated/DisplayStepOctaveGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/DisplayStepOctaveGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,12 +31,13 @@ void DisplayStepOctaveGroup::setDisplayOctave(Octave value) m_displayOctave = std::move(value); } -DisplayStepOctaveGroup parseDisplayStepOctaveGroup(pugi::xml_node el, pugi::xml_node &cursor) +DisplayStepOctaveGroup parseDisplayStepOctaveGroup(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context) { DisplayStepOctaveGroup out; if (cursorIs(cursor, "display-step")) { - out.setDisplayStep(Step::parse(childText(cursor))); + out.setDisplayStep(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -44,7 +46,7 @@ DisplayStepOctaveGroup parseDisplayStepOctaveGroup(pugi::xml_node el, pugi::xml_ } if (cursorIs(cursor, "display-octave")) { - out.setDisplayOctave(Octave::parse(childText(cursor))); + out.setDisplayOctave(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/DisplayStepOctaveGroup.h b/src/private/mx/core/generated/DisplayStepOctaveGroup.h index 18a879bef..339bdc246 100644 --- a/src/private/mx/core/generated/DisplayStepOctaveGroup.h +++ b/src/private/mx/core/generated/DisplayStepOctaveGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The display-step-octave group contains the sequence of elements used by both the rest and /// unpitched elements. This group is used to place rests and unpitched elements on the staff without /// implying that these elements have pitch. Positioning follows the current clef. If percussion clef @@ -41,7 +43,8 @@ class DisplayStepOctaveGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -DisplayStepOctaveGroup parseDisplayStepOctaveGroup(pugi::xml_node el, pugi::xml_node &cursor); +DisplayStepOctaveGroup parseDisplayStepOctaveGroup(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context); void serializeDisplayStepOctaveGroup(const DisplayStepOctaveGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Distance.cpp b/src/private/mx/core/generated/Distance.cpp index f2a1ea9cd..6923aba10 100644 --- a/src/private/mx/core/generated/Distance.cpp +++ b/src/private/mx/core/generated/Distance.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Distance.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Distance::setValue(Tenths value) m_value = std::move(value); } -Distance parseDistance(pugi::xml_node el) +Distance parseDistance(pugi::xml_node el, const ParseContext &context) { Distance out; bool seen_type = false; @@ -44,7 +45,7 @@ Distance parseDistance(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(DistanceType::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else { @@ -55,13 +56,13 @@ Distance parseDistance(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseDistanceContent(out, el); + parseDistanceContent(out, el, context); return out; } -void parseDistanceContent(Distance &out, pugi::xml_node el) +void parseDistanceContent(Distance &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(Tenths::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Distance.h b/src/private/mx/core/generated/Distance.h index bcf5847bc..264451b73 100644 --- a/src/private/mx/core/generated/Distance.h +++ b/src/private/mx/core/generated/Distance.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The distance element represents standard distances between notation elements in tenths. The type /// attribute defines what type of distance is being defined. Valid values include hyphen (for /// hyphens in lyrics) and beam. @@ -33,9 +35,9 @@ class Distance final Tenths m_value{}; }; -Distance parseDistance(pugi::xml_node el); +Distance parseDistance(pugi::xml_node el, const ParseContext &context); -void parseDistanceContent(Distance &out, pugi::xml_node el); +void parseDistanceContent(Distance &out, pugi::xml_node el, const ParseContext &context); void serializeDistance(const Distance &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/DistanceType.cpp b/src/private/mx/core/generated/DistanceType.cpp index b46f430b6..c06825c90 100644 --- a/src/private/mx/core/generated/DistanceType.cpp +++ b/src/private/mx/core/generated/DistanceType.cpp @@ -32,4 +32,11 @@ DistanceType DistanceType::parse(std::string_view text) return DistanceType{std::string{text}}; } +DistanceType DistanceType::parse(std::string_view text, ValueParseOutcome &outcome) +{ + DistanceType out{std::string{text}}; + outcome = out.value() == text ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/DistanceType.h b/src/private/mx/core/generated/DistanceType.h index 610900824..ce39c4302 100644 --- a/src/private/mx/core/generated/DistanceType.h +++ b/src/private/mx/core/generated/DistanceType.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -36,6 +38,9 @@ class DistanceType final static DistanceType parse(std::string_view text); + /// Says whether the text had to be repaired. + static DistanceType parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const DistanceType &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Divisions.cpp b/src/private/mx/core/generated/Divisions.cpp index 91a724764..a0769c4d2 100644 --- a/src/private/mx/core/generated/Divisions.cpp +++ b/src/private/mx/core/generated/Divisions.cpp @@ -34,20 +34,33 @@ std::string Divisions::toString() const bool Divisions::tryParse(std::string_view text, Divisions &out) { - Decimal v; - if (!Decimal::tryParse(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + Divisions parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = Divisions{std::move(v)}; + out = std::move(parsed); return true; } Divisions Divisions::parse(std::string_view text) { - Divisions v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +Divisions Divisions::parse(std::string_view text, ValueParseOutcome &outcome) +{ + Decimal v; + if (!Decimal::tryParse(text, v)) + { + outcome = ValueParseOutcome::invalid; + return Divisions{}; + } + Divisions out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/Divisions.h b/src/private/mx/core/generated/Divisions.h index 535b0c36f..2580b5efb 100644 --- a/src/private/mx/core/generated/Divisions.h +++ b/src/private/mx/core/generated/Divisions.h @@ -3,6 +3,7 @@ #pragma once #include "mx/core/Decimal.h" +#include "mx/core/Lexical.h" #include #include @@ -39,6 +40,9 @@ class Divisions final /// Lenient: non-numeric text yields the clamped zero. static Divisions parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static Divisions parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const Divisions &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Document.cpp b/src/private/mx/core/generated/Document.cpp index a54b6c258..38f30621d 100644 --- a/src/private/mx/core/generated/Document.cpp +++ b/src/private/mx/core/generated/Document.cpp @@ -4,6 +4,7 @@ #include "mx/core/Error.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include "mx/core/generated/Version.h" @@ -13,7 +14,7 @@ namespace mx::core { -Result parse(const pugi::xml_document &doc) +Result parse(const pugi::xml_document &doc, const ParseContext &context) { try { @@ -41,11 +42,11 @@ Result parse(const pugi::xml_document &doc) Document d; if (tag == "score-partwise") { - d = Document{parseScorePartwise(root)}; + d = Document{parseScorePartwise(root, context)}; } else if (tag == "score-timewise") { - d = Document{parseScoreTimewise(root)}; + d = Document{parseScoreTimewise(root, context)}; } else { diff --git a/src/private/mx/core/generated/Document.h b/src/private/mx/core/generated/Document.h index f8fc590fe..0ffa10891 100644 --- a/src/private/mx/core/generated/Document.h +++ b/src/private/mx/core/generated/Document.h @@ -19,6 +19,8 @@ class xml_document; namespace mx::core { +class ParseContext; + /// A namespace declaration preserved verbatim from the parsed root element /// (xmlns, xmlns:xlink, ...): document plumbing, not schema data, but /// round-trip parity requires writing it back. @@ -101,8 +103,9 @@ class Document final /// The parse error boundary (plan §2.5): strict on names and structure, /// lenient on values; a root declaring a version newer than /// SupportedMusicXMLVersion is rejected with unsupportedVersion. -Result parse(const pugi::xml_document &doc); +Result parse(const pugi::xml_document &doc, const ParseContext &context); +// TODO: document the unique ID exception /// Total for every Document (§1.1 guarantees validity): writes the XML /// declaration, the matching DOCTYPE, the preserved root namespace /// declarations, and the typed tree. diff --git a/src/private/mx/core/generated/Double.cpp b/src/private/mx/core/generated/Double.cpp index 128082dd8..2fa145997 100644 --- a/src/private/mx/core/generated/Double.cpp +++ b/src/private/mx/core/generated/Double.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Double.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -20,7 +21,7 @@ void Double::setAbove(std::optional value) m_above = std::move(value); } -Double parseDouble(pugi::xml_node el) +Double parseDouble(pugi::xml_node el, const ParseContext &context) { Double out; for (pugi::xml_attribute a : el.attributes()) @@ -32,20 +33,21 @@ Double parseDouble(pugi::xml_node el) } if (aname == "above") { - out.setAbove(YesNo::parse(a.value())); + out.setAbove(parseValue(a.value(), context, el, "above")); } else { throwUnknownAttribute(el, a.name()); } } - parseDoubleContent(out, el); + parseDoubleContent(out, el, context); return out; } -void parseDoubleContent(Double &out, pugi::xml_node el) +void parseDoubleContent(Double &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Double.h b/src/private/mx/core/generated/Double.h index 106eb0939..fbb4f02ad 100644 --- a/src/private/mx/core/generated/Double.h +++ b/src/private/mx/core/generated/Double.h @@ -15,6 +15,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The double type indicates that the music is doubled one octave from what is currently written. If /// the above attribute is set to yes, the doubling is one octave above what is written, as for mixed /// flute / piccolo parts in band literature. Otherwise the doubling is one octave below what is @@ -30,9 +32,9 @@ class Double final std::optional m_above; }; -Double parseDouble(pugi::xml_node el); +Double parseDouble(pugi::xml_node el, const ParseContext &context); -void parseDoubleContent(Double &out, pugi::xml_node el); +void parseDoubleContent(Double &out, pugi::xml_node el, const ParseContext &context); void serializeDouble(const Double &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Dynamics.cpp b/src/private/mx/core/generated/Dynamics.cpp index 2f78e9641..5d0ce9100 100644 --- a/src/private/mx/core/generated/Dynamics.cpp +++ b/src/private/mx/core/generated/Dynamics.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Dynamics.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -195,7 +196,7 @@ void Dynamics::setChoice(std::vector value) m_choice = std::move(value); } -Dynamics parseDynamics(pugi::xml_node el) +Dynamics parseDynamics(pugi::xml_node el, const ParseContext &context) { Dynamics out; for (pugi::xml_attribute a : el.attributes()) @@ -207,82 +208,82 @@ Dynamics parseDynamics(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "underline") { - out.setUnderline(NumberOfLines::parse(a.value())); + out.setUnderline(parseValue(a.value(), context, el, "underline")); } else if (aname == "overline") { - out.setOverline(NumberOfLines::parse(a.value())); + out.setOverline(parseValue(a.value(), context, el, "overline")); } else if (aname == "line-through") { - out.setLineThrough(NumberOfLines::parse(a.value())); + out.setLineThrough(parseValue(a.value(), context, el, "line-through")); } else if (aname == "enclosure") { - out.setEnclosure(EnclosureShape::parse(a.value())); + out.setEnclosure(parseValue(a.value(), context, el, "enclosure")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseDynamicsContent(out, el); + parseDynamicsContent(out, el, context); return out; } -void parseDynamicsContent(Dynamics &out, pugi::xml_node el) +void parseDynamicsContent(Dynamics &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); while (cursor && @@ -295,7 +296,7 @@ void parseDynamicsContent(Dynamics &out, pugi::xml_node el) cursorIs(cursor, "sffz") || cursorIs(cursor, "fz") || cursorIs(cursor, "n") || cursorIs(cursor, "pf") || cursorIs(cursor, "sfzp") || cursorIs(cursor, "other-dynamics"))) { - out.addChoice(parseDynamicsChoice(el, cursor)); + out.addChoice(parseDynamicsChoice(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/Dynamics.h b/src/private/mx/core/generated/Dynamics.h index 9fc71785e..bf7e1762d 100644 --- a/src/private/mx/core/generated/Dynamics.h +++ b/src/private/mx/core/generated/Dynamics.h @@ -30,6 +30,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Dynamics can be associated either with a note or a general musical direction. To avoid /// inconsistencies between and amongst the letter abbreviations for dynamics (what is sf vs. sfz, /// standing alone or with a trailing dynamic that is not always piano), we use the actual letters as @@ -107,9 +109,9 @@ class Dynamics final std::vector m_choice; }; -Dynamics parseDynamics(pugi::xml_node el); +Dynamics parseDynamics(pugi::xml_node el, const ParseContext &context); -void parseDynamicsContent(Dynamics &out, pugi::xml_node el); +void parseDynamicsContent(Dynamics &out, pugi::xml_node el, const ParseContext &context); void serializeDynamics(const Dynamics &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/DynamicsChoice.cpp b/src/private/mx/core/generated/DynamicsChoice.cpp index 98be29622..069f8a04a 100644 --- a/src/private/mx/core/generated/DynamicsChoice.cpp +++ b/src/private/mx/core/generated/DynamicsChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/DynamicsChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -150,167 +151,167 @@ DynamicsChoice DynamicsChoice::otherDynamics(OtherText value) return DynamicsChoice{Storage{std::in_place_index<26>, std::move(value)}}; } -DynamicsChoice parseDynamicsChoice(pugi::xml_node el, pugi::xml_node &cursor) +DynamicsChoice parseDynamicsChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "p"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::p(std::move(value)); } if (cursor && (cursorIs(cursor, "pp"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::pp(std::move(value)); } if (cursor && (cursorIs(cursor, "ppp"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::ppp(std::move(value)); } if (cursor && (cursorIs(cursor, "pppp"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::pppp(std::move(value)); } if (cursor && (cursorIs(cursor, "ppppp"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::ppppp(std::move(value)); } if (cursor && (cursorIs(cursor, "pppppp"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::pppppp(std::move(value)); } if (cursor && (cursorIs(cursor, "f"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::f(std::move(value)); } if (cursor && (cursorIs(cursor, "ff"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::ff(std::move(value)); } if (cursor && (cursorIs(cursor, "fff"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::fff(std::move(value)); } if (cursor && (cursorIs(cursor, "ffff"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::ffff(std::move(value)); } if (cursor && (cursorIs(cursor, "fffff"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::fffff(std::move(value)); } if (cursor && (cursorIs(cursor, "ffffff"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::ffffff(std::move(value)); } if (cursor && (cursorIs(cursor, "mp"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::mp(std::move(value)); } if (cursor && (cursorIs(cursor, "mf"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::mf(std::move(value)); } if (cursor && (cursorIs(cursor, "sf"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::sf(std::move(value)); } if (cursor && (cursorIs(cursor, "sfp"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::sfp(std::move(value)); } if (cursor && (cursorIs(cursor, "sfpp"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::sfpp(std::move(value)); } if (cursor && (cursorIs(cursor, "fp"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::fp(std::move(value)); } if (cursor && (cursorIs(cursor, "rf"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::rf(std::move(value)); } if (cursor && (cursorIs(cursor, "rfz"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::rfz(std::move(value)); } if (cursor && (cursorIs(cursor, "sfz"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::sfz(std::move(value)); } if (cursor && (cursorIs(cursor, "sffz"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::sffz(std::move(value)); } if (cursor && (cursorIs(cursor, "fz"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::fz(std::move(value)); } if (cursor && (cursorIs(cursor, "n"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::n(std::move(value)); } if (cursor && (cursorIs(cursor, "pf"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::pf(std::move(value)); } if (cursor && (cursorIs(cursor, "sfzp"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::sfzp(std::move(value)); } if (cursor && (cursorIs(cursor, "other-dynamics"))) { - OtherText value = parseOtherText(cursor); + OtherText value = parseOtherText(cursor, context); cursor = nextElement(cursor); return DynamicsChoice::otherDynamics(std::move(value)); } diff --git a/src/private/mx/core/generated/DynamicsChoice.h b/src/private/mx/core/generated/DynamicsChoice.h index 52ccf721f..bf28ceac7 100644 --- a/src/private/mx/core/generated/DynamicsChoice.h +++ b/src/private/mx/core/generated/DynamicsChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -432,7 +434,7 @@ class DynamicsChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -DynamicsChoice parseDynamicsChoice(pugi::xml_node el, pugi::xml_node &cursor); +DynamicsChoice parseDynamicsChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeDynamicsChoice(const DynamicsChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/EditorialGroup.cpp b/src/private/mx/core/generated/EditorialGroup.cpp index 4296ec847..0ae7149a4 100644 --- a/src/private/mx/core/generated/EditorialGroup.cpp +++ b/src/private/mx/core/generated/EditorialGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/EditorialGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,17 +31,17 @@ void EditorialGroup::setLevel(std::optional value) m_level = std::move(value); } -EditorialGroup parseEditorialGroup(pugi::xml_node el, pugi::xml_node &cursor) +EditorialGroup parseEditorialGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { EditorialGroup out; if (cursorIs(cursor, "footnote")) { - out.setFootnote(parseFormattedText(cursor)); + out.setFootnote(parseFormattedText(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "level")) { - out.setLevel(parseLevel(cursor)); + out.setLevel(parseLevel(cursor, context)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/EditorialGroup.h b/src/private/mx/core/generated/EditorialGroup.h index 4025ee96e..fb0122d88 100644 --- a/src/private/mx/core/generated/EditorialGroup.h +++ b/src/private/mx/core/generated/EditorialGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The editorial group specifies editorial information for a musical element. /// A shared content group: transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -37,7 +39,7 @@ class EditorialGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -EditorialGroup parseEditorialGroup(pugi::xml_node el, pugi::xml_node &cursor); +EditorialGroup parseEditorialGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeEditorialGroup(const EditorialGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/EditorialVoiceDirectionGroup.cpp b/src/private/mx/core/generated/EditorialVoiceDirectionGroup.cpp index c530438ce..0a5acd666 100644 --- a/src/private/mx/core/generated/EditorialVoiceDirectionGroup.cpp +++ b/src/private/mx/core/generated/EditorialVoiceDirectionGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/EditorialVoiceDirectionGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,17 +41,18 @@ void EditorialVoiceDirectionGroup::setVoice(std::optional value) m_voice = std::move(value); } -EditorialVoiceDirectionGroup parseEditorialVoiceDirectionGroup(pugi::xml_node el, pugi::xml_node &cursor) +EditorialVoiceDirectionGroup parseEditorialVoiceDirectionGroup(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context) { EditorialVoiceDirectionGroup out; if (cursorIs(cursor, "footnote")) { - out.setFootnote(parseFormattedText(cursor)); + out.setFootnote(parseFormattedText(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "level")) { - out.setLevel(parseLevel(cursor)); + out.setLevel(parseLevel(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "voice")) diff --git a/src/private/mx/core/generated/EditorialVoiceDirectionGroup.h b/src/private/mx/core/generated/EditorialVoiceDirectionGroup.h index aa1cf51d7..390e14033 100644 --- a/src/private/mx/core/generated/EditorialVoiceDirectionGroup.h +++ b/src/private/mx/core/generated/EditorialVoiceDirectionGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The editorial-voice-direction group supports the common combination of editorial and voice /// information for a direction element. It is separate from the editorial-voice element because /// extensions and restrictions might be different for directions than for the note and forward @@ -43,7 +45,8 @@ class EditorialVoiceDirectionGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -EditorialVoiceDirectionGroup parseEditorialVoiceDirectionGroup(pugi::xml_node el, pugi::xml_node &cursor); +EditorialVoiceDirectionGroup parseEditorialVoiceDirectionGroup(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context); void serializeEditorialVoiceDirectionGroup(const EditorialVoiceDirectionGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/EditorialVoiceGroup.cpp b/src/private/mx/core/generated/EditorialVoiceGroup.cpp index e9aa79ddd..51b9f560e 100644 --- a/src/private/mx/core/generated/EditorialVoiceGroup.cpp +++ b/src/private/mx/core/generated/EditorialVoiceGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/EditorialVoiceGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,17 +41,17 @@ void EditorialVoiceGroup::setVoice(std::optional value) m_voice = std::move(value); } -EditorialVoiceGroup parseEditorialVoiceGroup(pugi::xml_node el, pugi::xml_node &cursor) +EditorialVoiceGroup parseEditorialVoiceGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { EditorialVoiceGroup out; if (cursorIs(cursor, "footnote")) { - out.setFootnote(parseFormattedText(cursor)); + out.setFootnote(parseFormattedText(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "level")) { - out.setLevel(parseLevel(cursor)); + out.setLevel(parseLevel(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "voice")) diff --git a/src/private/mx/core/generated/EditorialVoiceGroup.h b/src/private/mx/core/generated/EditorialVoiceGroup.h index fbd4a5364..123934d0f 100644 --- a/src/private/mx/core/generated/EditorialVoiceGroup.h +++ b/src/private/mx/core/generated/EditorialVoiceGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The editorial-voice group supports the common combination of editorial and voice information for /// a musical element. /// A shared content group: transparent on the wire, its @@ -41,7 +43,7 @@ class EditorialVoiceGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -EditorialVoiceGroup parseEditorialVoiceGroup(pugi::xml_node el, pugi::xml_node &cursor); +EditorialVoiceGroup parseEditorialVoiceGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeEditorialVoiceGroup(const EditorialVoiceGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Effect.cpp b/src/private/mx/core/generated/Effect.cpp index 22dc1926c..f2c82aba6 100644 --- a/src/private/mx/core/generated/Effect.cpp +++ b/src/private/mx/core/generated/Effect.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Effect.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Effect::setValue(EffectValue value) m_value = std::move(value); } -Effect parseEffect(pugi::xml_node el) +Effect parseEffect(pugi::xml_node el, const ParseContext &context) { Effect out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ Effect parseEffect(pugi::xml_node el) } if (aname == "smufl") { - out.setSmufl(SmuflPictogramGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else { throwUnknownAttribute(el, a.name()); } } - parseEffectContent(out, el); + parseEffectContent(out, el, context); return out; } -void parseEffectContent(Effect &out, pugi::xml_node el) +void parseEffectContent(Effect &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(EffectValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Effect.h b/src/private/mx/core/generated/Effect.h index c79739371..2968f6726 100644 --- a/src/private/mx/core/generated/Effect.h +++ b/src/private/mx/core/generated/Effect.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The effect type represents pictograms for sound effect percussion instruments. The smufl /// attribute is used to distinguish different SMuFL stylistic alternates. class Effect final @@ -32,9 +34,9 @@ class Effect final EffectValue m_value{}; }; -Effect parseEffect(pugi::xml_node el); +Effect parseEffect(pugi::xml_node el, const ParseContext &context); -void parseEffectContent(Effect &out, pugi::xml_node el); +void parseEffectContent(Effect &out, pugi::xml_node el, const ParseContext &context); void serializeEffect(const Effect &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/EffectValue.cpp b/src/private/mx/core/generated/EffectValue.cpp index aa2db4651..662f736da 100644 --- a/src/private/mx/core/generated/EffectValue.cpp +++ b/src/private/mx/core/generated/EffectValue.cpp @@ -113,9 +113,15 @@ bool EffectValue::tryParse(std::string_view text, EffectValue &out) noexcept } EffectValue EffectValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +EffectValue EffectValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { EffectValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/EffectValue.h b/src/private/mx/core/generated/EffectValue.h index 5a3e6bab4..58caf75e3 100644 --- a/src/private/mx/core/generated/EffectValue.h +++ b/src/private/mx/core/generated/EffectValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -69,6 +71,9 @@ class EffectValue final /// (the import leniency policy; never produces an invalid value). static EffectValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static EffectValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const EffectValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Elision.cpp b/src/private/mx/core/generated/Elision.cpp index f5faa0d43..f24ac39a1 100644 --- a/src/private/mx/core/generated/Elision.cpp +++ b/src/private/mx/core/generated/Elision.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Elision.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -80,7 +81,7 @@ void Elision::setValue(std::string value) m_value = std::move(value); } -Elision parseElision(pugi::xml_node el) +Elision parseElision(pugi::xml_node el, const ParseContext &context) { Elision out; for (pugi::xml_attribute a : el.attributes()) @@ -92,38 +93,38 @@ Elision parseElision(pugi::xml_node el) } if (aname == "smufl") { - out.setSmufl(SmuflLyricsGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseElisionContent(out, el); + parseElisionContent(out, el, context); return out; } -void parseElisionContent(Elision &out, pugi::xml_node el) +void parseElisionContent(Elision &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/Elision.h b/src/private/mx/core/generated/Elision.h index 1e43cbcbc..5022a9ce5 100644 --- a/src/private/mx/core/generated/Elision.h +++ b/src/private/mx/core/generated/Elision.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The elision type represents an elision between lyric syllables. The text content specifies the /// symbol used to display the elision. Common values are a no-break space (Unicode 00A0), an /// underscore (Unicode 005F), or an undertie (Unicode 203F). If the text content is empty, the smufl @@ -56,9 +58,9 @@ class Elision final std::string m_value{}; }; -Elision parseElision(pugi::xml_node el); +Elision parseElision(pugi::xml_node el, const ParseContext &context); -void parseElisionContent(Elision &out, pugi::xml_node el); +void parseElisionContent(Elision &out, pugi::xml_node el, const ParseContext &context); void serializeElision(const Elision &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/ElisionSyllabicGroup.cpp b/src/private/mx/core/generated/ElisionSyllabicGroup.cpp index f41f8793f..4a703bbed 100644 --- a/src/private/mx/core/generated/ElisionSyllabicGroup.cpp +++ b/src/private/mx/core/generated/ElisionSyllabicGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/ElisionSyllabicGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,12 +31,12 @@ void ElisionSyllabicGroup::setSyllabic(std::optional value) m_syllabic = std::move(value); } -ElisionSyllabicGroup parseElisionSyllabicGroup(pugi::xml_node el, pugi::xml_node &cursor) +ElisionSyllabicGroup parseElisionSyllabicGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { ElisionSyllabicGroup out; if (cursorIs(cursor, "elision")) { - out.setElision(parseElision(cursor)); + out.setElision(parseElision(cursor, context)); cursor = nextElement(cursor); } else @@ -44,7 +45,7 @@ ElisionSyllabicGroup parseElisionSyllabicGroup(pugi::xml_node el, pugi::xml_node } if (cursorIs(cursor, "syllabic")) { - out.setSyllabic(Syllabic::parse(childText(cursor))); + out.setSyllabic(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/ElisionSyllabicGroup.h b/src/private/mx/core/generated/ElisionSyllabicGroup.h index 0b063a5ae..dc5307a8f 100644 --- a/src/private/mx/core/generated/ElisionSyllabicGroup.h +++ b/src/private/mx/core/generated/ElisionSyllabicGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -37,7 +39,7 @@ class ElisionSyllabicGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -ElisionSyllabicGroup parseElisionSyllabicGroup(pugi::xml_node el, pugi::xml_node &cursor); +ElisionSyllabicGroup parseElisionSyllabicGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeElisionSyllabicGroup(const ElisionSyllabicGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Empty.cpp b/src/private/mx/core/generated/Empty.cpp index 990b6fbb5..ef4278afe 100644 --- a/src/private/mx/core/generated/Empty.cpp +++ b/src/private/mx/core/generated/Empty.cpp @@ -2,12 +2,13 @@ #include "mx/core/generated/Empty.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" namespace mx::core { -Empty parseEmpty(pugi::xml_node el) +Empty parseEmpty(pugi::xml_node el, const ParseContext &context) { Empty out; for (pugi::xml_attribute a : el.attributes()) @@ -19,13 +20,14 @@ Empty parseEmpty(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseEmptyContent(out, el); + parseEmptyContent(out, el, context); return out; } -void parseEmptyContent(Empty &out, pugi::xml_node el) +void parseEmptyContent(Empty &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Empty.h b/src/private/mx/core/generated/Empty.h index 2766300c7..550f036b6 100644 --- a/src/private/mx/core/generated/Empty.h +++ b/src/private/mx/core/generated/Empty.h @@ -10,6 +10,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The empty type represents an empty element with no attributes. /// Presence-only marker: the element's only information is whether it /// appears. Optional occurrences project to bool fields; choice @@ -20,9 +22,9 @@ class Empty bool operator==(const Empty &) const noexcept = default; }; -Empty parseEmpty(pugi::xml_node el); +Empty parseEmpty(pugi::xml_node el, const ParseContext &context); -void parseEmptyContent(Empty &out, pugi::xml_node el); +void parseEmptyContent(Empty &out, pugi::xml_node el, const ParseContext &context); void serializeEmpty(const Empty &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/EmptyFont.cpp b/src/private/mx/core/generated/EmptyFont.cpp index 11a8f344d..772ea13ad 100644 --- a/src/private/mx/core/generated/EmptyFont.cpp +++ b/src/private/mx/core/generated/EmptyFont.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/EmptyFont.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,7 +51,7 @@ void EmptyFont::setFontWeight(std::optional value) m_fontWeight = std::move(value); } -EmptyFont parseEmptyFont(pugi::xml_node el) +EmptyFont parseEmptyFont(pugi::xml_node el, const ParseContext &context) { EmptyFont out; for (pugi::xml_attribute a : el.attributes()) @@ -62,32 +63,33 @@ EmptyFont parseEmptyFont(pugi::xml_node el) } if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else { throwUnknownAttribute(el, a.name()); } } - parseEmptyFontContent(out, el); + parseEmptyFontContent(out, el, context); return out; } -void parseEmptyFontContent(EmptyFont &out, pugi::xml_node el) +void parseEmptyFontContent(EmptyFont &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/EmptyFont.h b/src/private/mx/core/generated/EmptyFont.h index 592220cc5..92371040c 100644 --- a/src/private/mx/core/generated/EmptyFont.h +++ b/src/private/mx/core/generated/EmptyFont.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The empty-font type represents an empty element with font attributes. class EmptyFont final { @@ -39,9 +41,9 @@ class EmptyFont final std::optional m_fontWeight; }; -EmptyFont parseEmptyFont(pugi::xml_node el); +EmptyFont parseEmptyFont(pugi::xml_node el, const ParseContext &context); -void parseEmptyFontContent(EmptyFont &out, pugi::xml_node el); +void parseEmptyFontContent(EmptyFont &out, pugi::xml_node el, const ParseContext &context); void serializeEmptyFont(const EmptyFont &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/EmptyLine.cpp b/src/private/mx/core/generated/EmptyLine.cpp index 68b8ff05e..a92531a25 100644 --- a/src/private/mx/core/generated/EmptyLine.cpp +++ b/src/private/mx/core/generated/EmptyLine.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/EmptyLine.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -160,7 +161,7 @@ void EmptyLine::setPlacement(std::optional value) m_placement = std::move(value); } -EmptyLine parseEmptyLine(pugi::xml_node el) +EmptyLine parseEmptyLine(pugi::xml_node el, const ParseContext &context) { EmptyLine out; for (pugi::xml_attribute a : el.attributes()) @@ -172,76 +173,77 @@ EmptyLine parseEmptyLine(pugi::xml_node el) } if (aname == "line-shape") { - out.setLineShape(LineShape::parse(a.value())); + out.setLineShape(parseValue(a.value(), context, el, "line-shape")); } else if (aname == "line-type") { - out.setLineType(LineType::parse(a.value())); + out.setLineType(parseValue(a.value(), context, el, "line-type")); } else if (aname == "line-length") { - out.setLineLength(LineLength::parse(a.value())); + out.setLineLength(parseValue(a.value(), context, el, "line-length")); } else if (aname == "dash-length") { - out.setDashLength(Tenths::parse(a.value())); + out.setDashLength(parseValue(a.value(), context, el, "dash-length")); } else if (aname == "space-length") { - out.setSpaceLength(Tenths::parse(a.value())); + out.setSpaceLength(parseValue(a.value(), context, el, "space-length")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else { throwUnknownAttribute(el, a.name()); } } - parseEmptyLineContent(out, el); + parseEmptyLineContent(out, el, context); return out; } -void parseEmptyLineContent(EmptyLine &out, pugi::xml_node el) +void parseEmptyLineContent(EmptyLine &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/EmptyLine.h b/src/private/mx/core/generated/EmptyLine.h index 73f8590b4..9dd599cdb 100644 --- a/src/private/mx/core/generated/EmptyLine.h +++ b/src/private/mx/core/generated/EmptyLine.h @@ -24,6 +24,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The empty-line type represents an empty element with line-shape, line-type, line-length, /// dashed-formatting, print-style and placement attributes. class EmptyLine final @@ -79,9 +81,9 @@ class EmptyLine final std::optional m_placement; }; -EmptyLine parseEmptyLine(pugi::xml_node el); +EmptyLine parseEmptyLine(pugi::xml_node el, const ParseContext &context); -void parseEmptyLineContent(EmptyLine &out, pugi::xml_node el); +void parseEmptyLineContent(EmptyLine &out, pugi::xml_node el, const ParseContext &context); void serializeEmptyLine(const EmptyLine &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/EmptyPlacement.cpp b/src/private/mx/core/generated/EmptyPlacement.cpp index 9ea49e65c..c829ef8a4 100644 --- a/src/private/mx/core/generated/EmptyPlacement.cpp +++ b/src/private/mx/core/generated/EmptyPlacement.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/EmptyPlacement.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -110,7 +111,7 @@ void EmptyPlacement::setPlacement(std::optional value) m_placement = std::move(value); } -EmptyPlacement parseEmptyPlacement(pugi::xml_node el) +EmptyPlacement parseEmptyPlacement(pugi::xml_node el, const ParseContext &context) { EmptyPlacement out; for (pugi::xml_attribute a : el.attributes()) @@ -122,56 +123,57 @@ EmptyPlacement parseEmptyPlacement(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else { throwUnknownAttribute(el, a.name()); } } - parseEmptyPlacementContent(out, el); + parseEmptyPlacementContent(out, el, context); return out; } -void parseEmptyPlacementContent(EmptyPlacement &out, pugi::xml_node el) +void parseEmptyPlacementContent(EmptyPlacement &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/EmptyPlacement.h b/src/private/mx/core/generated/EmptyPlacement.h index ca00d40fe..d1b3f45ed 100644 --- a/src/private/mx/core/generated/EmptyPlacement.h +++ b/src/private/mx/core/generated/EmptyPlacement.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The empty-placement type represents an empty element with print-style and placement attributes. class EmptyPlacement { @@ -60,9 +62,9 @@ class EmptyPlacement std::optional m_placement; }; -EmptyPlacement parseEmptyPlacement(pugi::xml_node el); +EmptyPlacement parseEmptyPlacement(pugi::xml_node el, const ParseContext &context); -void parseEmptyPlacementContent(EmptyPlacement &out, pugi::xml_node el); +void parseEmptyPlacementContent(EmptyPlacement &out, pugi::xml_node el, const ParseContext &context); void serializeEmptyPlacement(const EmptyPlacement &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/EmptyPlacementSmufl.cpp b/src/private/mx/core/generated/EmptyPlacementSmufl.cpp index 7253e6d37..597b66dd3 100644 --- a/src/private/mx/core/generated/EmptyPlacementSmufl.cpp +++ b/src/private/mx/core/generated/EmptyPlacementSmufl.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/EmptyPlacementSmufl.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -120,7 +121,7 @@ void EmptyPlacementSmufl::setSmufl(std::optional value) m_smufl = std::move(value); } -EmptyPlacementSmufl parseEmptyPlacementSmufl(pugi::xml_node el) +EmptyPlacementSmufl parseEmptyPlacementSmufl(pugi::xml_node el, const ParseContext &context) { EmptyPlacementSmufl out; for (pugi::xml_attribute a : el.attributes()) @@ -132,60 +133,61 @@ EmptyPlacementSmufl parseEmptyPlacementSmufl(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "smufl") { - out.setSmufl(SmuflGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else { throwUnknownAttribute(el, a.name()); } } - parseEmptyPlacementSmuflContent(out, el); + parseEmptyPlacementSmuflContent(out, el, context); return out; } -void parseEmptyPlacementSmuflContent(EmptyPlacementSmufl &out, pugi::xml_node el) +void parseEmptyPlacementSmuflContent(EmptyPlacementSmufl &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/EmptyPlacementSmufl.h b/src/private/mx/core/generated/EmptyPlacementSmufl.h index 0a8c28466..b0acd3265 100644 --- a/src/private/mx/core/generated/EmptyPlacementSmufl.h +++ b/src/private/mx/core/generated/EmptyPlacementSmufl.h @@ -22,6 +22,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The empty-placement-smufl type represents an empty element with print-style, placement, and smufl /// attributes. class EmptyPlacementSmufl final @@ -65,9 +67,9 @@ class EmptyPlacementSmufl final std::optional m_smufl; }; -EmptyPlacementSmufl parseEmptyPlacementSmufl(pugi::xml_node el); +EmptyPlacementSmufl parseEmptyPlacementSmufl(pugi::xml_node el, const ParseContext &context); -void parseEmptyPlacementSmuflContent(EmptyPlacementSmufl &out, pugi::xml_node el); +void parseEmptyPlacementSmuflContent(EmptyPlacementSmufl &out, pugi::xml_node el, const ParseContext &context); void serializeEmptyPlacementSmufl(const EmptyPlacementSmufl &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/EmptyPrintObjectStyleAlign.cpp b/src/private/mx/core/generated/EmptyPrintObjectStyleAlign.cpp index dddc2c188..673e90ea3 100644 --- a/src/private/mx/core/generated/EmptyPrintObjectStyleAlign.cpp +++ b/src/private/mx/core/generated/EmptyPrintObjectStyleAlign.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/EmptyPrintObjectStyleAlign.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -130,7 +131,7 @@ void EmptyPrintObjectStyleAlign::setValign(std::optional value) m_valign = std::move(value); } -EmptyPrintObjectStyleAlign parseEmptyPrintObjectStyleAlign(pugi::xml_node el) +EmptyPrintObjectStyleAlign parseEmptyPrintObjectStyleAlign(pugi::xml_node el, const ParseContext &context) { EmptyPrintObjectStyleAlign out; for (pugi::xml_attribute a : el.attributes()) @@ -142,64 +143,66 @@ EmptyPrintObjectStyleAlign parseEmptyPrintObjectStyleAlign(pugi::xml_node el) } if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else { throwUnknownAttribute(el, a.name()); } } - parseEmptyPrintObjectStyleAlignContent(out, el); + parseEmptyPrintObjectStyleAlignContent(out, el, context); return out; } -void parseEmptyPrintObjectStyleAlignContent(EmptyPrintObjectStyleAlign &out, pugi::xml_node el) +void parseEmptyPrintObjectStyleAlignContent(EmptyPrintObjectStyleAlign &out, pugi::xml_node el, + const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/EmptyPrintObjectStyleAlign.h b/src/private/mx/core/generated/EmptyPrintObjectStyleAlign.h index 47b4f48e3..03d875f8a 100644 --- a/src/private/mx/core/generated/EmptyPrintObjectStyleAlign.h +++ b/src/private/mx/core/generated/EmptyPrintObjectStyleAlign.h @@ -23,6 +23,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The empty-print-style-align-object type represents an empty element with print-object and /// print-style-align attribute groups. class EmptyPrintObjectStyleAlign final @@ -69,9 +71,10 @@ class EmptyPrintObjectStyleAlign final std::optional m_valign; }; -EmptyPrintObjectStyleAlign parseEmptyPrintObjectStyleAlign(pugi::xml_node el); +EmptyPrintObjectStyleAlign parseEmptyPrintObjectStyleAlign(pugi::xml_node el, const ParseContext &context); -void parseEmptyPrintObjectStyleAlignContent(EmptyPrintObjectStyleAlign &out, pugi::xml_node el); +void parseEmptyPrintObjectStyleAlignContent(EmptyPrintObjectStyleAlign &out, pugi::xml_node el, + const ParseContext &context); void serializeEmptyPrintObjectStyleAlign(const EmptyPrintObjectStyleAlign &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/EmptyPrintStyleAlignID.cpp b/src/private/mx/core/generated/EmptyPrintStyleAlignID.cpp index 1c1c75512..e131f6f40 100644 --- a/src/private/mx/core/generated/EmptyPrintStyleAlignID.cpp +++ b/src/private/mx/core/generated/EmptyPrintStyleAlignID.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/EmptyPrintStyleAlignID.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -130,7 +131,7 @@ void EmptyPrintStyleAlignID::setID(std::optional value) m_id = std::move(value); } -EmptyPrintStyleAlignID parseEmptyPrintStyleAlignID(pugi::xml_node el) +EmptyPrintStyleAlignID parseEmptyPrintStyleAlignID(pugi::xml_node el, const ParseContext &context) { EmptyPrintStyleAlignID out; for (pugi::xml_attribute a : el.attributes()) @@ -142,64 +143,65 @@ EmptyPrintStyleAlignID parseEmptyPrintStyleAlignID(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseEmptyPrintStyleAlignIDContent(out, el); + parseEmptyPrintStyleAlignIDContent(out, el, context); return out; } -void parseEmptyPrintStyleAlignIDContent(EmptyPrintStyleAlignID &out, pugi::xml_node el) +void parseEmptyPrintStyleAlignIDContent(EmptyPrintStyleAlignID &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/EmptyPrintStyleAlignID.h b/src/private/mx/core/generated/EmptyPrintStyleAlignID.h index 2dafbd197..c9166209f 100644 --- a/src/private/mx/core/generated/EmptyPrintStyleAlignID.h +++ b/src/private/mx/core/generated/EmptyPrintStyleAlignID.h @@ -24,6 +24,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The empty-print-style-align-id type represents an empty element with print-style-align and /// optional-unique-id attribute groups. class EmptyPrintStyleAlignID final @@ -70,9 +72,9 @@ class EmptyPrintStyleAlignID final std::optional m_id; }; -EmptyPrintStyleAlignID parseEmptyPrintStyleAlignID(pugi::xml_node el); +EmptyPrintStyleAlignID parseEmptyPrintStyleAlignID(pugi::xml_node el, const ParseContext &context); -void parseEmptyPrintStyleAlignIDContent(EmptyPrintStyleAlignID &out, pugi::xml_node el); +void parseEmptyPrintStyleAlignIDContent(EmptyPrintStyleAlignID &out, pugi::xml_node el, const ParseContext &context); void serializeEmptyPrintStyleAlignID(const EmptyPrintStyleAlignID &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/EmptyTrillSound.cpp b/src/private/mx/core/generated/EmptyTrillSound.cpp index da7fb3280..5c0538e53 100644 --- a/src/private/mx/core/generated/EmptyTrillSound.cpp +++ b/src/private/mx/core/generated/EmptyTrillSound.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/EmptyTrillSound.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -180,7 +181,7 @@ void EmptyTrillSound::setLastBeat(std::optional value) m_lastBeat = std::move(value); } -EmptyTrillSound parseEmptyTrillSound(pugi::xml_node el) +EmptyTrillSound parseEmptyTrillSound(pugi::xml_node el, const ParseContext &context) { EmptyTrillSound out; for (pugi::xml_attribute a : el.attributes()) @@ -192,84 +193,85 @@ EmptyTrillSound parseEmptyTrillSound(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "start-note") { - out.setStartNote(StartNote::parse(a.value())); + out.setStartNote(parseValue(a.value(), context, el, "start-note")); } else if (aname == "trill-step") { - out.setTrillStep(TrillStep::parse(a.value())); + out.setTrillStep(parseValue(a.value(), context, el, "trill-step")); } else if (aname == "two-note-turn") { - out.setTwoNoteTurn(TwoNoteTurn::parse(a.value())); + out.setTwoNoteTurn(parseValue(a.value(), context, el, "two-note-turn")); } else if (aname == "accelerate") { - out.setAccelerate(YesNo::parse(a.value())); + out.setAccelerate(parseValue(a.value(), context, el, "accelerate")); } else if (aname == "beats") { - out.setBeats(TrillBeats::parse(a.value())); + out.setBeats(parseValue(a.value(), context, el, "beats")); } else if (aname == "second-beat") { - out.setSecondBeat(Percent::parse(a.value())); + out.setSecondBeat(parseValue(a.value(), context, el, "second-beat")); } else if (aname == "last-beat") { - out.setLastBeat(Percent::parse(a.value())); + out.setLastBeat(parseValue(a.value(), context, el, "last-beat")); } else { throwUnknownAttribute(el, a.name()); } } - parseEmptyTrillSoundContent(out, el); + parseEmptyTrillSoundContent(out, el, context); return out; } -void parseEmptyTrillSoundContent(EmptyTrillSound &out, pugi::xml_node el) +void parseEmptyTrillSoundContent(EmptyTrillSound &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/EmptyTrillSound.h b/src/private/mx/core/generated/EmptyTrillSound.h index c241ce5e4..01f529c8c 100644 --- a/src/private/mx/core/generated/EmptyTrillSound.h +++ b/src/private/mx/core/generated/EmptyTrillSound.h @@ -27,6 +27,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The empty-trill-sound type represents an empty element with print-style, placement, and /// trill-sound attributes. class EmptyTrillSound @@ -88,9 +90,9 @@ class EmptyTrillSound std::optional m_lastBeat; }; -EmptyTrillSound parseEmptyTrillSound(pugi::xml_node el); +EmptyTrillSound parseEmptyTrillSound(pugi::xml_node el, const ParseContext &context); -void parseEmptyTrillSoundContent(EmptyTrillSound &out, pugi::xml_node el); +void parseEmptyTrillSoundContent(EmptyTrillSound &out, pugi::xml_node el, const ParseContext &context); void serializeEmptyTrillSound(const EmptyTrillSound &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/EnclosureShape.cpp b/src/private/mx/core/generated/EnclosureShape.cpp index 29beac6ad..64a12a37e 100644 --- a/src/private/mx/core/generated/EnclosureShape.cpp +++ b/src/private/mx/core/generated/EnclosureShape.cpp @@ -107,9 +107,15 @@ bool EnclosureShape::tryParse(std::string_view text, EnclosureShape &out) noexce } EnclosureShape EnclosureShape::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +EnclosureShape EnclosureShape::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { EnclosureShape v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/EnclosureShape.h b/src/private/mx/core/generated/EnclosureShape.h index b762b6217..2feab76f7 100644 --- a/src/private/mx/core/generated/EnclosureShape.h +++ b/src/private/mx/core/generated/EnclosureShape.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -69,6 +71,9 @@ class EnclosureShape final /// (the import leniency policy; never produces an invalid value). static EnclosureShape parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static EnclosureShape parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const EnclosureShape &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Encoding.cpp b/src/private/mx/core/generated/Encoding.cpp index 044c92455..16402d437 100644 --- a/src/private/mx/core/generated/Encoding.cpp +++ b/src/private/mx/core/generated/Encoding.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Encoding.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,7 +26,7 @@ void Encoding::setChoice(std::vector value) m_choice = std::move(value); } -Encoding parseEncoding(pugi::xml_node el) +Encoding parseEncoding(pugi::xml_node el, const ParseContext &context) { Encoding out; for (pugi::xml_attribute a : el.attributes()) @@ -37,18 +38,18 @@ Encoding parseEncoding(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseEncodingContent(out, el); + parseEncodingContent(out, el, context); return out; } -void parseEncodingContent(Encoding &out, pugi::xml_node el) +void parseEncodingContent(Encoding &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); while (cursor && (cursorIs(cursor, "encoding-date") || cursorIs(cursor, "encoder") || cursorIs(cursor, "software") || cursorIs(cursor, "encoding-description") || cursorIs(cursor, "supports"))) { - out.addChoice(parseEncodingChoice(el, cursor)); + out.addChoice(parseEncodingChoice(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/Encoding.h b/src/private/mx/core/generated/Encoding.h index 320c61e98..f8d2a8416 100644 --- a/src/private/mx/core/generated/Encoding.h +++ b/src/private/mx/core/generated/Encoding.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The encoding element contains information about who did the digital encoding, when, with what /// software, and in what aspects. Standard type values for the encoder element are music, words, and /// arrangement, but other types may be used. The type attribute is only needed when there are @@ -35,9 +37,9 @@ class Encoding final std::vector m_choice; }; -Encoding parseEncoding(pugi::xml_node el); +Encoding parseEncoding(pugi::xml_node el, const ParseContext &context); -void parseEncodingContent(Encoding &out, pugi::xml_node el); +void parseEncodingContent(Encoding &out, pugi::xml_node el, const ParseContext &context); void serializeEncoding(const Encoding &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/EncodingChoice.cpp b/src/private/mx/core/generated/EncodingChoice.cpp index 9e13a10aa..046cf63b4 100644 --- a/src/private/mx/core/generated/EncodingChoice.cpp +++ b/src/private/mx/core/generated/EncodingChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/EncodingChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,17 +41,17 @@ EncodingChoice EncodingChoice::supports(Supports value) return EncodingChoice{Storage{std::in_place_index<4>, std::move(value)}}; } -EncodingChoice parseEncodingChoice(pugi::xml_node el, pugi::xml_node &cursor) +EncodingChoice parseEncodingChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "encoding-date"))) { - YyyyMmDd value = YyyyMmDd::parse(childText(cursor)); + YyyyMmDd value = parseValue(childText(cursor), context, cursor, nullptr); cursor = nextElement(cursor); return EncodingChoice::encodingDate(std::move(value)); } if (cursor && (cursorIs(cursor, "encoder"))) { - TypedText value = parseTypedText(cursor); + TypedText value = parseTypedText(cursor, context); cursor = nextElement(cursor); return EncodingChoice::encoder(std::move(value)); } @@ -68,7 +69,7 @@ EncodingChoice parseEncodingChoice(pugi::xml_node el, pugi::xml_node &cursor) } if (cursor && (cursorIs(cursor, "supports"))) { - Supports value = parseSupports(cursor); + Supports value = parseSupports(cursor, context); cursor = nextElement(cursor); return EncodingChoice::supports(std::move(value)); } diff --git a/src/private/mx/core/generated/EncodingChoice.h b/src/private/mx/core/generated/EncodingChoice.h index 24e6c2fc4..dda15de8e 100644 --- a/src/private/mx/core/generated/EncodingChoice.h +++ b/src/private/mx/core/generated/EncodingChoice.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -123,7 +125,7 @@ class EncodingChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -EncodingChoice parseEncodingChoice(pugi::xml_node el, pugi::xml_node &cursor); +EncodingChoice parseEncodingChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeEncodingChoice(const EncodingChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Ending.cpp b/src/private/mx/core/generated/Ending.cpp index 888c925e7..57daf629a 100644 --- a/src/private/mx/core/generated/Ending.cpp +++ b/src/private/mx/core/generated/Ending.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Ending.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -180,7 +181,7 @@ void Ending::setValue(std::string value) m_value = std::move(value); } -Ending parseEnding(pugi::xml_node el) +Ending parseEnding(pugi::xml_node el, const ParseContext &context) { Ending out; bool seen_number = false; @@ -195,68 +196,68 @@ Ending parseEnding(pugi::xml_node el) if (aname == "number") { seen_number = true; - out.setNumber(EndingNumber::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "type") { seen_type = true; - out.setType(StartStopDiscontinue::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "end-length") { - out.setEndLength(Tenths::parse(a.value())); + out.setEndLength(parseValue(a.value(), context, el, "end-length")); } else if (aname == "text-x") { - out.setTextX(Tenths::parse(a.value())); + out.setTextX(parseValue(a.value(), context, el, "text-x")); } else if (aname == "text-y") { - out.setTextY(Tenths::parse(a.value())); + out.setTextY(parseValue(a.value(), context, el, "text-y")); } else if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "system") { - out.setSystem(SystemRelation::parse(a.value())); + out.setSystem(parseValue(a.value(), context, el, "system")); } else { @@ -271,11 +272,11 @@ Ending parseEnding(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseEndingContent(out, el); + parseEndingContent(out, el, context); return out; } -void parseEndingContent(Ending &out, pugi::xml_node el) +void parseEndingContent(Ending &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/Ending.h b/src/private/mx/core/generated/Ending.h index 298dca1ae..0ec39556a 100644 --- a/src/private/mx/core/generated/Ending.h +++ b/src/private/mx/core/generated/Ending.h @@ -24,6 +24,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The ending type represents multiple (e.g. first and second) endings. Typically, the start type is /// associated with the left barline of the first measure in an ending. The stop and discontinue /// types are associated with the right barline of the last measure in an ending. Stop is used when @@ -98,9 +100,9 @@ class Ending final std::string m_value{}; }; -Ending parseEnding(pugi::xml_node el); +Ending parseEnding(pugi::xml_node el, const ParseContext &context); -void parseEndingContent(Ending &out, pugi::xml_node el); +void parseEndingContent(Ending &out, pugi::xml_node el, const ParseContext &context); void serializeEnding(const Ending &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/EndingNumber.cpp b/src/private/mx/core/generated/EndingNumber.cpp index a849bcb3f..0489a6332 100644 --- a/src/private/mx/core/generated/EndingNumber.cpp +++ b/src/private/mx/core/generated/EndingNumber.cpp @@ -101,4 +101,16 @@ EndingNumber EndingNumber::parse(std::string_view text) return out; } +EndingNumber EndingNumber::parse(std::string_view text, ValueParseOutcome &outcome) +{ + EndingNumber out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/EndingNumber.h b/src/private/mx/core/generated/EndingNumber.h index 9dc703149..beda365aa 100644 --- a/src/private/mx/core/generated/EndingNumber.h +++ b/src/private/mx/core/generated/EndingNumber.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include #include @@ -44,6 +46,9 @@ class EndingNumber final /// Lenient: repairs into the nearest valid value. static EndingNumber parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. + static EndingNumber parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const EndingNumber &other) const noexcept { return m_values == other.m_values; diff --git a/src/private/mx/core/generated/Extend.cpp b/src/private/mx/core/generated/Extend.cpp index 91ed69cd4..ca40f7d1e 100644 --- a/src/private/mx/core/generated/Extend.cpp +++ b/src/private/mx/core/generated/Extend.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Extend.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -70,7 +71,7 @@ void Extend::setColor(std::optional value) m_color = std::move(value); } -Extend parseExtend(pugi::xml_node el) +Extend parseExtend(pugi::xml_node el, const ParseContext &context) { Extend out; for (pugi::xml_attribute a : el.attributes()) @@ -82,40 +83,41 @@ Extend parseExtend(pugi::xml_node el) } if (aname == "type") { - out.setType(StartStopContinue::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseExtendContent(out, el); + parseExtendContent(out, el, context); return out; } -void parseExtendContent(Extend &out, pugi::xml_node el) +void parseExtendContent(Extend &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Extend.h b/src/private/mx/core/generated/Extend.h index 223fa1db6..dec4cfa81 100644 --- a/src/private/mx/core/generated/Extend.h +++ b/src/private/mx/core/generated/Extend.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The extend type represents lyric word extension / melisma lines as well as figured bass /// extensions. The optional type and position attributes are added in Version 3.0 to provide better /// formatting control. @@ -46,9 +48,9 @@ class Extend final std::optional m_color; }; -Extend parseExtend(pugi::xml_node el); +Extend parseExtend(pugi::xml_node el, const ParseContext &context); -void parseExtendContent(Extend &out, pugi::xml_node el); +void parseExtendContent(Extend &out, pugi::xml_node el, const ParseContext &context); void serializeExtend(const Extend &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Fan.cpp b/src/private/mx/core/generated/Fan.cpp index 6aea0f1fe..85c5d3c6f 100644 --- a/src/private/mx/core/generated/Fan.cpp +++ b/src/private/mx/core/generated/Fan.cpp @@ -48,9 +48,15 @@ bool Fan::tryParse(std::string_view text, Fan &out) noexcept } Fan Fan::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +Fan Fan::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { Fan v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/Fan.h b/src/private/mx/core/generated/Fan.h index 93362f6d0..513afa7da 100644 --- a/src/private/mx/core/generated/Fan.h +++ b/src/private/mx/core/generated/Fan.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -43,6 +45,9 @@ class Fan final /// (the import leniency policy; never produces an invalid value). static Fan parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static Fan parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const Fan &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Feature.cpp b/src/private/mx/core/generated/Feature.cpp index 9b459933c..ed88564e0 100644 --- a/src/private/mx/core/generated/Feature.cpp +++ b/src/private/mx/core/generated/Feature.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Feature.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Feature::setValue(std::string value) m_value = std::move(value); } -Feature parseFeature(pugi::xml_node el) +Feature parseFeature(pugi::xml_node el, const ParseContext &context) { Feature out; for (pugi::xml_attribute a : el.attributes()) @@ -49,11 +50,11 @@ Feature parseFeature(pugi::xml_node el) throwUnknownAttribute(el, a.name()); } } - parseFeatureContent(out, el); + parseFeatureContent(out, el, context); return out; } -void parseFeatureContent(Feature &out, pugi::xml_node el) +void parseFeatureContent(Feature &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/Feature.h b/src/private/mx/core/generated/Feature.h index b8fdb75ae..6c977be34 100644 --- a/src/private/mx/core/generated/Feature.h +++ b/src/private/mx/core/generated/Feature.h @@ -13,6 +13,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The feature type is a part of the grouping element used for musical analysis. The type attribute /// represents the type of the feature and the element content represents its value. This type is /// flexible to allow for different analyses. @@ -30,9 +32,9 @@ class Feature final std::string m_value{}; }; -Feature parseFeature(pugi::xml_node el); +Feature parseFeature(pugi::xml_node el, const ParseContext &context); -void parseFeatureContent(Feature &out, pugi::xml_node el); +void parseFeatureContent(Feature &out, pugi::xml_node el, const ParseContext &context); void serializeFeature(const Feature &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Fermata.cpp b/src/private/mx/core/generated/Fermata.cpp index 583ea5479..f06f200ae 100644 --- a/src/private/mx/core/generated/Fermata.cpp +++ b/src/private/mx/core/generated/Fermata.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Fermata.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -130,7 +131,7 @@ void Fermata::setValue(FermataShape value) m_value = std::move(value); } -Fermata parseFermata(pugi::xml_node el) +Fermata parseFermata(pugi::xml_node el, const ParseContext &context) { Fermata out; for (pugi::xml_attribute a : el.attributes()) @@ -142,60 +143,60 @@ Fermata parseFermata(pugi::xml_node el) } if (aname == "type") { - out.setType(UprightInverted::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseFermataContent(out, el); + parseFermataContent(out, el, context); return out; } -void parseFermataContent(Fermata &out, pugi::xml_node el) +void parseFermataContent(Fermata &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(FermataShape::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Fermata.h b/src/private/mx/core/generated/Fermata.h index 8819db610..9b8969e82 100644 --- a/src/private/mx/core/generated/Fermata.h +++ b/src/private/mx/core/generated/Fermata.h @@ -24,6 +24,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The fermata text content represents the shape of the fermata sign. An empty fermata element /// represents a normal fermata. The fermata type is upright if not specified. class Fermata final @@ -70,9 +72,9 @@ class Fermata final FermataShape m_value{}; }; -Fermata parseFermata(pugi::xml_node el); +Fermata parseFermata(pugi::xml_node el, const ParseContext &context); -void parseFermataContent(Fermata &out, pugi::xml_node el); +void parseFermataContent(Fermata &out, pugi::xml_node el, const ParseContext &context); void serializeFermata(const Fermata &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/FermataShape.cpp b/src/private/mx/core/generated/FermataShape.cpp index b095d1b1b..4fa602c7d 100644 --- a/src/private/mx/core/generated/FermataShape.cpp +++ b/src/private/mx/core/generated/FermataShape.cpp @@ -76,9 +76,15 @@ bool FermataShape::tryParse(std::string_view text, FermataShape &out) noexcept } FermataShape FermataShape::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +FermataShape FermataShape::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { FermataShape v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/FermataShape.h b/src/private/mx/core/generated/FermataShape.h index 5957b944d..28fe3c553 100644 --- a/src/private/mx/core/generated/FermataShape.h +++ b/src/private/mx/core/generated/FermataShape.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -55,6 +57,9 @@ class FermataShape final /// (the import leniency policy; never produces an invalid value). static FermataShape parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static FermataShape parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const FermataShape &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Fifths.cpp b/src/private/mx/core/generated/Fifths.cpp index 2f30ff2bd..45dd30685 100644 --- a/src/private/mx/core/generated/Fifths.cpp +++ b/src/private/mx/core/generated/Fifths.cpp @@ -34,20 +34,33 @@ std::string Fifths::toString() const bool Fifths::tryParse(std::string_view text, Fifths &out) { - int v{}; - if (!tryParseInt(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + Fifths parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = Fifths{v}; + out = std::move(parsed); return true; } Fifths Fifths::parse(std::string_view text) { - Fifths v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +Fifths Fifths::parse(std::string_view text, ValueParseOutcome &outcome) +{ + int v{}; + if (!tryParseInt(text, v)) + { + outcome = ValueParseOutcome::invalid; + return Fifths{}; + } + Fifths out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/Fifths.h b/src/private/mx/core/generated/Fifths.h index 1b4cbf847..2c97cf7ad 100644 --- a/src/private/mx/core/generated/Fifths.h +++ b/src/private/mx/core/generated/Fifths.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -37,6 +39,9 @@ class Fifths final /// Lenient: non-numeric text yields the clamped zero. static Fifths parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static Fifths parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const Fifths &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Figure.cpp b/src/private/mx/core/generated/Figure.cpp index 9c03f88ac..108b0d35e 100644 --- a/src/private/mx/core/generated/Figure.cpp +++ b/src/private/mx/core/generated/Figure.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Figure.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -60,7 +61,7 @@ void Figure::setEditorial(EditorialGroup value) m_editorial = std::move(value); } -Figure parseFigure(pugi::xml_node el) +Figure parseFigure(pugi::xml_node el, const ParseContext &context) { Figure out; for (pugi::xml_attribute a : el.attributes()) @@ -72,36 +73,36 @@ Figure parseFigure(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseFigureContent(out, el); + parseFigureContent(out, el, context); return out; } -void parseFigureContent(Figure &out, pugi::xml_node el) +void parseFigureContent(Figure &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "prefix")) { - out.setPrefix(parseStyleText(cursor)); + out.setPrefix(parseStyleText(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "figure-number")) { - out.setFigureNumber(parseStyleText(cursor)); + out.setFigureNumber(parseStyleText(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "suffix")) { - out.setSuffix(parseStyleText(cursor)); + out.setSuffix(parseStyleText(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "extend")) { - out.setExtend(parseExtend(cursor)); + out.setExtend(parseExtend(cursor, context)); cursor = nextElement(cursor); } if (cursor && (cursorIs(cursor, "footnote") || cursorIs(cursor, "level"))) { - out.setEditorial(parseEditorialGroup(el, cursor)); + out.setEditorial(parseEditorialGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/Figure.h b/src/private/mx/core/generated/Figure.h index 82279fad6..476f33c98 100644 --- a/src/private/mx/core/generated/Figure.h +++ b/src/private/mx/core/generated/Figure.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The figure type represents a single figure within a figured-bass element. /// Content fields mirror the schema grammar in declaration order; the /// serializer walks them, so a wrong order is unrepresentable (plan §2.3). @@ -45,9 +47,9 @@ class Figure final EditorialGroup m_editorial{}; }; -Figure parseFigure(pugi::xml_node el); +Figure parseFigure(pugi::xml_node el, const ParseContext &context); -void parseFigureContent(Figure &out, pugi::xml_node el); +void parseFigureContent(Figure &out, pugi::xml_node el, const ParseContext &context); void serializeFigure(const Figure &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/FiguredBass.cpp b/src/private/mx/core/generated/FiguredBass.cpp index 1062a7e32..e8b75e67b 100644 --- a/src/private/mx/core/generated/FiguredBass.cpp +++ b/src/private/mx/core/generated/FiguredBass.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/FiguredBass.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -225,7 +226,7 @@ void FiguredBass::setEditorial(EditorialGroup value) m_editorial = std::move(value); } -FiguredBass parseFiguredBass(pugi::xml_node el) +FiguredBass parseFiguredBass(pugi::xml_node el, const ParseContext &context) { FiguredBass out; for (pugi::xml_attribute a : el.attributes()) @@ -237,107 +238,107 @@ FiguredBass parseFiguredBass(pugi::xml_node el) } if (aname == "parentheses") { - out.setParentheses(YesNo::parse(a.value())); + out.setParentheses(parseValue(a.value(), context, el, "parentheses")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "print-dot") { - out.setPrintDot(YesNo::parse(a.value())); + out.setPrintDot(parseValue(a.value(), context, el, "print-dot")); } else if (aname == "print-lyric") { - out.setPrintLyric(YesNo::parse(a.value())); + out.setPrintLyric(parseValue(a.value(), context, el, "print-lyric")); } else if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else if (aname == "print-spacing") { - out.setPrintSpacing(YesNo::parse(a.value())); + out.setPrintSpacing(parseValue(a.value(), context, el, "print-spacing")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseFiguredBassContent(out, el); + parseFiguredBassContent(out, el, context); return out; } -void parseFiguredBassContent(FiguredBass &out, pugi::xml_node el) +void parseFiguredBassContent(FiguredBass &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (!cursorIs(cursor, "figure")) { throwMissingOrMisplaced(el, cursor, "figure"); } - out.setFigure(OneOrMore
{parseFigure(cursor)}); + out.setFigure(OneOrMore
{parseFigure(cursor, context)}); cursor = nextElement(cursor); while (cursorIs(cursor, "figure")) { - out.addFigure(parseFigure(cursor)); + out.addFigure(parseFigure(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "duration")) { - out.setDuration(PositiveDivisions::parse(childText(cursor))); + out.setDuration(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursor && (cursorIs(cursor, "footnote") || cursorIs(cursor, "level"))) { - out.setEditorial(parseEditorialGroup(el, cursor)); + out.setEditorial(parseEditorialGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/FiguredBass.h b/src/private/mx/core/generated/FiguredBass.h index 71b74c730..d5674e8d3 100644 --- a/src/private/mx/core/generated/FiguredBass.h +++ b/src/private/mx/core/generated/FiguredBass.h @@ -32,6 +32,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The figured-bass element represents figured bass notation. Figured bass elements take their /// position from the first regular note (not a grace note or chord note) that follows in score /// order. The optional duration element is used to indicate changes of figures under a note. Figures @@ -110,9 +112,9 @@ class FiguredBass final EditorialGroup m_editorial{}; }; -FiguredBass parseFiguredBass(pugi::xml_node el); +FiguredBass parseFiguredBass(pugi::xml_node el, const ParseContext &context); -void parseFiguredBassContent(FiguredBass &out, pugi::xml_node el); +void parseFiguredBassContent(FiguredBass &out, pugi::xml_node el, const ParseContext &context); void serializeFiguredBass(const FiguredBass &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Fingering.cpp b/src/private/mx/core/generated/Fingering.cpp index d998a1d2a..6525398ed 100644 --- a/src/private/mx/core/generated/Fingering.cpp +++ b/src/private/mx/core/generated/Fingering.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Fingering.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -140,7 +141,7 @@ void Fingering::setValue(std::string value) m_value = std::move(value); } -Fingering parseFingering(pugi::xml_node el) +Fingering parseFingering(pugi::xml_node el, const ParseContext &context) { Fingering out; for (pugi::xml_attribute a : el.attributes()) @@ -152,62 +153,62 @@ Fingering parseFingering(pugi::xml_node el) } if (aname == "substitution") { - out.setSubstitution(YesNo::parse(a.value())); + out.setSubstitution(parseValue(a.value(), context, el, "substitution")); } else if (aname == "alternate") { - out.setAlternate(YesNo::parse(a.value())); + out.setAlternate(parseValue(a.value(), context, el, "alternate")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else { throwUnknownAttribute(el, a.name()); } } - parseFingeringContent(out, el); + parseFingeringContent(out, el, context); return out; } -void parseFingeringContent(Fingering &out, pugi::xml_node el) +void parseFingeringContent(Fingering &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/Fingering.h b/src/private/mx/core/generated/Fingering.h index 3ddac33e8..e88fe44be 100644 --- a/src/private/mx/core/generated/Fingering.h +++ b/src/private/mx/core/generated/Fingering.h @@ -22,6 +22,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Fingering is typically indicated 1,2,3,4,5. Multiple fingerings may be given, typically to /// substitute fingerings in the middle of a note. The substitution and alternate values are "no" if /// the attribute is not present. For guitar and other fretted instruments, the fingering element @@ -73,9 +75,9 @@ class Fingering final std::string m_value{}; }; -Fingering parseFingering(pugi::xml_node el); +Fingering parseFingering(pugi::xml_node el, const ParseContext &context); -void parseFingeringContent(Fingering &out, pugi::xml_node el); +void parseFingeringContent(Fingering &out, pugi::xml_node el, const ParseContext &context); void serializeFingering(const Fingering &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/FirstFret.cpp b/src/private/mx/core/generated/FirstFret.cpp index 0963321ca..5aa96db09 100644 --- a/src/private/mx/core/generated/FirstFret.cpp +++ b/src/private/mx/core/generated/FirstFret.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/FirstFret.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,7 +41,7 @@ void FirstFret::setValue(int value) m_value = std::move(value); } -FirstFret parseFirstFret(pugi::xml_node el) +FirstFret parseFirstFret(pugi::xml_node el, const ParseContext &context) { FirstFret out; for (pugi::xml_attribute a : el.attributes()) @@ -56,20 +57,20 @@ FirstFret parseFirstFret(pugi::xml_node el) } else if (aname == "location") { - out.setLocation(LeftRight::parse(a.value())); + out.setLocation(parseValue(a.value(), context, el, "location")); } else { throwUnknownAttribute(el, a.name()); } } - parseFirstFretContent(out, el); + parseFirstFretContent(out, el, context); return out; } -void parseFirstFretContent(FirstFret &out, pugi::xml_node el) +void parseFirstFretContent(FirstFret &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(parseInt(childText(el))); + out.setValue(parseIntegerValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/FirstFret.h b/src/private/mx/core/generated/FirstFret.h index 321351d2d..42a1e0952 100644 --- a/src/private/mx/core/generated/FirstFret.h +++ b/src/private/mx/core/generated/FirstFret.h @@ -15,6 +15,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The first-fret type indicates which fret is shown in the top space of the frame; it is fret 1 if /// the element is not present. The optional text attribute indicates how this is represented in the /// fret diagram, while the location attribute indicates whether the text appears to the left or @@ -36,9 +38,9 @@ class FirstFret final int m_value{}; }; -FirstFret parseFirstFret(pugi::xml_node el); +FirstFret parseFirstFret(pugi::xml_node el, const ParseContext &context); -void parseFirstFretContent(FirstFret &out, pugi::xml_node el); +void parseFirstFretContent(FirstFret &out, pugi::xml_node el, const ParseContext &context); void serializeFirstFret(const FirstFret &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/FontFamily.cpp b/src/private/mx/core/generated/FontFamily.cpp index 1bb0a1aa8..12c16e841 100644 --- a/src/private/mx/core/generated/FontFamily.cpp +++ b/src/private/mx/core/generated/FontFamily.cpp @@ -120,4 +120,16 @@ FontFamily FontFamily::parse(std::string_view text) return out; } +FontFamily FontFamily::parse(std::string_view text, ValueParseOutcome &outcome) +{ + FontFamily out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/FontFamily.h b/src/private/mx/core/generated/FontFamily.h index 68eea2b34..09948c99d 100644 --- a/src/private/mx/core/generated/FontFamily.h +++ b/src/private/mx/core/generated/FontFamily.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include #include @@ -45,6 +47,9 @@ class FontFamily final /// Lenient: repairs into the nearest valid list. static FontFamily parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. + static FontFamily parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const FontFamily &other) const noexcept { return m_items == other.m_items; diff --git a/src/private/mx/core/generated/FontSize.cpp b/src/private/mx/core/generated/FontSize.cpp index 0630465ad..c28f24e26 100644 --- a/src/private/mx/core/generated/FontSize.cpp +++ b/src/private/mx/core/generated/FontSize.cpp @@ -59,12 +59,20 @@ bool FontSize::tryParse(std::string_view text, FontSize &out) } FontSize FontSize::parse(std::string_view text) +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +FontSize FontSize::parse(std::string_view text, ValueParseOutcome &outcome) { FontSize out; if (tryParse(text, out)) { + outcome = ValueParseOutcome::valid; return out; } + outcome = ValueParseOutcome::invalid; return FontSize::decimal(Decimal::parse(text)); } diff --git a/src/private/mx/core/generated/FontSize.h b/src/private/mx/core/generated/FontSize.h index 7a90cdabb..4784d8924 100644 --- a/src/private/mx/core/generated/FontSize.h +++ b/src/private/mx/core/generated/FontSize.h @@ -3,6 +3,7 @@ #pragma once #include "mx/core/Decimal.h" +#include "mx/core/Lexical.h" #include "mx/core/generated/CSSFontSize.h" #include @@ -70,6 +71,10 @@ class FontSize final /// parse (never produces an invalid value). static FontSize parse(std::string_view text); + /// Lenient, and says whether no member matched or a matched number was + /// clamped. + static FontSize parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const FontSize &other) const = default; private: diff --git a/src/private/mx/core/generated/FontStyle.cpp b/src/private/mx/core/generated/FontStyle.cpp index 807a94f3c..dd79ef948 100644 --- a/src/private/mx/core/generated/FontStyle.cpp +++ b/src/private/mx/core/generated/FontStyle.cpp @@ -42,9 +42,15 @@ bool FontStyle::tryParse(std::string_view text, FontStyle &out) noexcept } FontStyle FontStyle::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +FontStyle FontStyle::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { FontStyle v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/FontStyle.h b/src/private/mx/core/generated/FontStyle.h index a40d9263f..b41c91066 100644 --- a/src/private/mx/core/generated/FontStyle.h +++ b/src/private/mx/core/generated/FontStyle.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -40,6 +42,9 @@ class FontStyle final /// (the import leniency policy; never produces an invalid value). static FontStyle parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static FontStyle parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const FontStyle &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/FontWeight.cpp b/src/private/mx/core/generated/FontWeight.cpp index 7494bbbe7..1521c9fb7 100644 --- a/src/private/mx/core/generated/FontWeight.cpp +++ b/src/private/mx/core/generated/FontWeight.cpp @@ -42,9 +42,15 @@ bool FontWeight::tryParse(std::string_view text, FontWeight &out) noexcept } FontWeight FontWeight::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +FontWeight FontWeight::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { FontWeight v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/FontWeight.h b/src/private/mx/core/generated/FontWeight.h index bfcd14f23..4abee5846 100644 --- a/src/private/mx/core/generated/FontWeight.h +++ b/src/private/mx/core/generated/FontWeight.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -40,6 +42,9 @@ class FontWeight final /// (the import leniency policy; never produces an invalid value). static FontWeight parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static FontWeight parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const FontWeight &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/ForPart.cpp b/src/private/mx/core/generated/ForPart.cpp index 51211180f..1afb91847 100644 --- a/src/private/mx/core/generated/ForPart.cpp +++ b/src/private/mx/core/generated/ForPart.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/ForPart.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,7 +51,7 @@ void ForPart::setPartTranspose(PartTranspose value) m_partTranspose = std::move(value); } -ForPart parseForPart(pugi::xml_node el) +ForPart parseForPart(pugi::xml_node el, const ParseContext &context) { ForPart out; for (pugi::xml_attribute a : el.attributes()) @@ -62,32 +63,32 @@ ForPart parseForPart(pugi::xml_node el) } if (aname == "number") { - out.setNumber(StaffNumber::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseForPartContent(out, el); + parseForPartContent(out, el, context); return out; } -void parseForPartContent(ForPart &out, pugi::xml_node el) +void parseForPartContent(ForPart &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "part-clef")) { - out.setPartClef(parsePartClef(cursor)); + out.setPartClef(parsePartClef(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "part-transpose")) { - out.setPartTranspose(parsePartTranspose(cursor)); + out.setPartTranspose(parsePartTranspose(cursor, context)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/ForPart.h b/src/private/mx/core/generated/ForPart.h index 8f0292633..0e63083a5 100644 --- a/src/private/mx/core/generated/ForPart.h +++ b/src/private/mx/core/generated/ForPart.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The for-part type is used in a concert score to indicate the transposition for a transposed part /// created from that score. It is only used in score files that contain a concert-score element in /// the defaults. This allows concert scores with transposed parts to be represented in a single @@ -48,9 +50,9 @@ class ForPart final PartTranspose m_partTranspose{}; }; -ForPart parseForPart(pugi::xml_node el); +ForPart parseForPart(pugi::xml_node el, const ParseContext &context); -void parseForPartContent(ForPart &out, pugi::xml_node el); +void parseForPartContent(ForPart &out, pugi::xml_node el, const ParseContext &context); void serializeForPart(const ForPart &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/FormattedSymbolID.cpp b/src/private/mx/core/generated/FormattedSymbolID.cpp index d61544871..436b4e360 100644 --- a/src/private/mx/core/generated/FormattedSymbolID.cpp +++ b/src/private/mx/core/generated/FormattedSymbolID.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/FormattedSymbolID.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -230,7 +231,7 @@ void FormattedSymbolID::setValue(SmuflGlyphName value) m_value = std::move(value); } -FormattedSymbolID parseFormattedSymbolID(pugi::xml_node el) +FormattedSymbolID parseFormattedSymbolID(pugi::xml_node el, const ParseContext &context) { FormattedSymbolID out; for (pugi::xml_attribute a : el.attributes()) @@ -242,100 +243,100 @@ FormattedSymbolID parseFormattedSymbolID(pugi::xml_node el) } if (aname == "justify") { - out.setJustify(LeftCenterRight::parse(a.value())); + out.setJustify(parseValue(a.value(), context, el, "justify")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "underline") { - out.setUnderline(NumberOfLines::parse(a.value())); + out.setUnderline(parseValue(a.value(), context, el, "underline")); } else if (aname == "overline") { - out.setOverline(NumberOfLines::parse(a.value())); + out.setOverline(parseValue(a.value(), context, el, "overline")); } else if (aname == "line-through") { - out.setLineThrough(NumberOfLines::parse(a.value())); + out.setLineThrough(parseValue(a.value(), context, el, "line-through")); } else if (aname == "rotation") { - out.setRotation(RotationDegrees::parse(a.value())); + out.setRotation(parseValue(a.value(), context, el, "rotation")); } else if (aname == "letter-spacing") { - out.setLetterSpacing(NumberOrNormal::parse(a.value())); + out.setLetterSpacing(parseValue(a.value(), context, el, "letter-spacing")); } else if (aname == "line-height") { - out.setLineHeight(NumberOrNormal::parse(a.value())); + out.setLineHeight(parseValue(a.value(), context, el, "line-height")); } else if (aname == "dir") { - out.setDir(TextDirection::parse(a.value())); + out.setDir(parseValue(a.value(), context, el, "dir")); } else if (aname == "enclosure") { - out.setEnclosure(EnclosureShape::parse(a.value())); + out.setEnclosure(parseValue(a.value(), context, el, "enclosure")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseFormattedSymbolIDContent(out, el); + parseFormattedSymbolIDContent(out, el, context); return out; } -void parseFormattedSymbolIDContent(FormattedSymbolID &out, pugi::xml_node el) +void parseFormattedSymbolIDContent(FormattedSymbolID &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(SmuflGlyphName::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/FormattedSymbolID.h b/src/private/mx/core/generated/FormattedSymbolID.h index c38a99fe4..73d58fdde 100644 --- a/src/private/mx/core/generated/FormattedSymbolID.h +++ b/src/private/mx/core/generated/FormattedSymbolID.h @@ -30,6 +30,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The formatted-symbol-id type represents a SMuFL musical symbol element with formatting and id /// attributes. class FormattedSymbolID final @@ -106,9 +108,9 @@ class FormattedSymbolID final SmuflGlyphName m_value{}; }; -FormattedSymbolID parseFormattedSymbolID(pugi::xml_node el); +FormattedSymbolID parseFormattedSymbolID(pugi::xml_node el, const ParseContext &context); -void parseFormattedSymbolIDContent(FormattedSymbolID &out, pugi::xml_node el); +void parseFormattedSymbolIDContent(FormattedSymbolID &out, pugi::xml_node el, const ParseContext &context); void serializeFormattedSymbolID(const FormattedSymbolID &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/FormattedText.cpp b/src/private/mx/core/generated/FormattedText.cpp index 99fa081dc..68898015c 100644 --- a/src/private/mx/core/generated/FormattedText.cpp +++ b/src/private/mx/core/generated/FormattedText.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/FormattedText.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -240,7 +241,7 @@ void FormattedText::setValue(std::string value) m_value = std::move(value); } -FormattedText parseFormattedText(pugi::xml_node el) +FormattedText parseFormattedText(pugi::xml_node el, const ParseContext &context) { FormattedText out; for (pugi::xml_attribute a : el.attributes()) @@ -260,94 +261,94 @@ FormattedText parseFormattedText(pugi::xml_node el) } else if (aname == "justify") { - out.setJustify(LeftCenterRight::parse(a.value())); + out.setJustify(parseValue(a.value(), context, el, "justify")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "underline") { - out.setUnderline(NumberOfLines::parse(a.value())); + out.setUnderline(parseValue(a.value(), context, el, "underline")); } else if (aname == "overline") { - out.setOverline(NumberOfLines::parse(a.value())); + out.setOverline(parseValue(a.value(), context, el, "overline")); } else if (aname == "line-through") { - out.setLineThrough(NumberOfLines::parse(a.value())); + out.setLineThrough(parseValue(a.value(), context, el, "line-through")); } else if (aname == "rotation") { - out.setRotation(RotationDegrees::parse(a.value())); + out.setRotation(parseValue(a.value(), context, el, "rotation")); } else if (aname == "letter-spacing") { - out.setLetterSpacing(NumberOrNormal::parse(a.value())); + out.setLetterSpacing(parseValue(a.value(), context, el, "letter-spacing")); } else if (aname == "line-height") { - out.setLineHeight(NumberOrNormal::parse(a.value())); + out.setLineHeight(parseValue(a.value(), context, el, "line-height")); } else if (aname == "dir") { - out.setDir(TextDirection::parse(a.value())); + out.setDir(parseValue(a.value(), context, el, "dir")); } else if (aname == "enclosure") { - out.setEnclosure(EnclosureShape::parse(a.value())); + out.setEnclosure(parseValue(a.value(), context, el, "enclosure")); } else { throwUnknownAttribute(el, a.name()); } } - parseFormattedTextContent(out, el); + parseFormattedTextContent(out, el, context); return out; } -void parseFormattedTextContent(FormattedText &out, pugi::xml_node el) +void parseFormattedTextContent(FormattedText &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/FormattedText.h b/src/private/mx/core/generated/FormattedText.h index 04905e01c..576513849 100644 --- a/src/private/mx/core/generated/FormattedText.h +++ b/src/private/mx/core/generated/FormattedText.h @@ -27,6 +27,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The formatted-text type represents a text element with text-formatting attributes. class FormattedText final { @@ -105,9 +107,9 @@ class FormattedText final std::string m_value{}; }; -FormattedText parseFormattedText(pugi::xml_node el); +FormattedText parseFormattedText(pugi::xml_node el, const ParseContext &context); -void parseFormattedTextContent(FormattedText &out, pugi::xml_node el); +void parseFormattedTextContent(FormattedText &out, pugi::xml_node el, const ParseContext &context); void serializeFormattedText(const FormattedText &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/FormattedTextID.cpp b/src/private/mx/core/generated/FormattedTextID.cpp index 82efb5156..e9427bc93 100644 --- a/src/private/mx/core/generated/FormattedTextID.cpp +++ b/src/private/mx/core/generated/FormattedTextID.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/FormattedTextID.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -250,7 +251,7 @@ void FormattedTextID::setValue(std::string value) m_value = std::move(value); } -FormattedTextID parseFormattedTextID(pugi::xml_node el) +FormattedTextID parseFormattedTextID(pugi::xml_node el, const ParseContext &context) { FormattedTextID out; for (pugi::xml_attribute a : el.attributes()) @@ -270,98 +271,98 @@ FormattedTextID parseFormattedTextID(pugi::xml_node el) } else if (aname == "justify") { - out.setJustify(LeftCenterRight::parse(a.value())); + out.setJustify(parseValue(a.value(), context, el, "justify")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "underline") { - out.setUnderline(NumberOfLines::parse(a.value())); + out.setUnderline(parseValue(a.value(), context, el, "underline")); } else if (aname == "overline") { - out.setOverline(NumberOfLines::parse(a.value())); + out.setOverline(parseValue(a.value(), context, el, "overline")); } else if (aname == "line-through") { - out.setLineThrough(NumberOfLines::parse(a.value())); + out.setLineThrough(parseValue(a.value(), context, el, "line-through")); } else if (aname == "rotation") { - out.setRotation(RotationDegrees::parse(a.value())); + out.setRotation(parseValue(a.value(), context, el, "rotation")); } else if (aname == "letter-spacing") { - out.setLetterSpacing(NumberOrNormal::parse(a.value())); + out.setLetterSpacing(parseValue(a.value(), context, el, "letter-spacing")); } else if (aname == "line-height") { - out.setLineHeight(NumberOrNormal::parse(a.value())); + out.setLineHeight(parseValue(a.value(), context, el, "line-height")); } else if (aname == "dir") { - out.setDir(TextDirection::parse(a.value())); + out.setDir(parseValue(a.value(), context, el, "dir")); } else if (aname == "enclosure") { - out.setEnclosure(EnclosureShape::parse(a.value())); + out.setEnclosure(parseValue(a.value(), context, el, "enclosure")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseFormattedTextIDContent(out, el); + parseFormattedTextIDContent(out, el, context); return out; } -void parseFormattedTextIDContent(FormattedTextID &out, pugi::xml_node el) +void parseFormattedTextIDContent(FormattedTextID &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/FormattedTextID.h b/src/private/mx/core/generated/FormattedTextID.h index efc1d2926..4cb107b6c 100644 --- a/src/private/mx/core/generated/FormattedTextID.h +++ b/src/private/mx/core/generated/FormattedTextID.h @@ -29,6 +29,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The formatted-text-id type represents a text element with text-formatting and id attributes. class FormattedTextID final { @@ -110,9 +112,9 @@ class FormattedTextID final std::string m_value{}; }; -FormattedTextID parseFormattedTextID(pugi::xml_node el); +FormattedTextID parseFormattedTextID(pugi::xml_node el, const ParseContext &context); -void parseFormattedTextIDContent(FormattedTextID &out, pugi::xml_node el); +void parseFormattedTextIDContent(FormattedTextID &out, pugi::xml_node el, const ParseContext &context); void serializeFormattedTextID(const FormattedTextID &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Forward.cpp b/src/private/mx/core/generated/Forward.cpp index 8d1f1f07e..263c5cbeb 100644 --- a/src/private/mx/core/generated/Forward.cpp +++ b/src/private/mx/core/generated/Forward.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Forward.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,7 +41,7 @@ void Forward::setStaff(std::optional value) m_staff = std::move(value); } -Forward parseForward(pugi::xml_node el) +Forward parseForward(pugi::xml_node el, const ParseContext &context) { Forward out; for (pugi::xml_attribute a : el.attributes()) @@ -52,16 +53,16 @@ Forward parseForward(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseForwardContent(out, el); + parseForwardContent(out, el, context); return out; } -void parseForwardContent(Forward &out, pugi::xml_node el) +void parseForwardContent(Forward &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "duration")) { - out.setDuration(PositiveDivisions::parse(childText(cursor))); + out.setDuration(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -70,11 +71,11 @@ void parseForwardContent(Forward &out, pugi::xml_node el) } if (cursor && (cursorIs(cursor, "footnote") || cursorIs(cursor, "level") || cursorIs(cursor, "voice"))) { - out.setEditorialVoice(parseEditorialVoiceGroup(el, cursor)); + out.setEditorialVoice(parseEditorialVoiceGroup(el, cursor, context)); } if (cursorIs(cursor, "staff")) { - out.setStaff(parseInt(childText(cursor))); + out.setStaff(parseIntegerValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Forward.h b/src/private/mx/core/generated/Forward.h index fc2684157..1893e4bdc 100644 --- a/src/private/mx/core/generated/Forward.h +++ b/src/private/mx/core/generated/Forward.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The backup and forward elements are required to coordinate multiple voices in one part, including /// music on multiple staves. The forward element is generally used within voices and staves. /// Duration values should always be positive, and should not cross measure boundaries or mid-measure @@ -41,9 +43,9 @@ class Forward final std::optional m_staff; }; -Forward parseForward(pugi::xml_node el); +Forward parseForward(pugi::xml_node el, const ParseContext &context); -void parseForwardContent(Forward &out, pugi::xml_node el); +void parseForwardContent(Forward &out, pugi::xml_node el, const ParseContext &context); void serializeForward(const Forward &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Frame.cpp b/src/private/mx/core/generated/Frame.cpp index 7a82f9323..c32059651 100644 --- a/src/private/mx/core/generated/Frame.cpp +++ b/src/private/mx/core/generated/Frame.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Frame.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -165,7 +166,7 @@ void Frame::setFrameNote(OneOrMore value) m_frameNote = std::move(value); } -Frame parseFrame(pugi::xml_node el) +Frame parseFrame(pugi::xml_node el, const ParseContext &context) { Frame out; for (pugi::xml_attribute a : el.attributes()) @@ -177,11 +178,11 @@ Frame parseFrame(pugi::xml_node el) } if (aname == "height") { - out.setHeight(Tenths::parse(a.value())); + out.setHeight(parseValue(a.value(), context, el, "height")); } else if (aname == "width") { - out.setWidth(Tenths::parse(a.value())); + out.setWidth(parseValue(a.value(), context, el, "width")); } else if (aname == "unplayed") { @@ -189,51 +190,51 @@ Frame parseFrame(pugi::xml_node el) } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(ValignImage::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseFrameContent(out, el); + parseFrameContent(out, el, context); return out; } -void parseFrameContent(Frame &out, pugi::xml_node el) +void parseFrameContent(Frame &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "frame-strings")) { - out.setFrameStrings(parseInt(childText(cursor))); + out.setFrameStrings(parseIntegerValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -242,7 +243,7 @@ void parseFrameContent(Frame &out, pugi::xml_node el) } if (cursorIs(cursor, "frame-frets")) { - out.setFrameFrets(parseInt(childText(cursor))); + out.setFrameFrets(parseIntegerValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -251,18 +252,18 @@ void parseFrameContent(Frame &out, pugi::xml_node el) } if (cursorIs(cursor, "first-fret")) { - out.setFirstFret(parseFirstFret(cursor)); + out.setFirstFret(parseFirstFret(cursor, context)); cursor = nextElement(cursor); } if (!cursorIs(cursor, "frame-note")) { throwMissingOrMisplaced(el, cursor, "frame-note"); } - out.setFrameNote(OneOrMore{parseFrameNote(cursor)}); + out.setFrameNote(OneOrMore{parseFrameNote(cursor, context)}); cursor = nextElement(cursor); while (cursorIs(cursor, "frame-note")) { - out.addFrameNote(parseFrameNote(cursor)); + out.addFrameNote(parseFrameNote(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Frame.h b/src/private/mx/core/generated/Frame.h index 61c2daa33..9ca10c848 100644 --- a/src/private/mx/core/generated/Frame.h +++ b/src/private/mx/core/generated/Frame.h @@ -25,6 +25,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The frame type represents a frame or fretboard diagram used together with a chord symbol. The /// representation is based on the NIFF guitar grid with additional information. The frame type's /// unplayed attribute indicates what to display above a string that has no associated frame-note @@ -86,9 +88,9 @@ class Frame final OneOrMore m_frameNote; }; -Frame parseFrame(pugi::xml_node el); +Frame parseFrame(pugi::xml_node el, const ParseContext &context); -void parseFrameContent(Frame &out, pugi::xml_node el); +void parseFrameContent(Frame &out, pugi::xml_node el, const ParseContext &context); void serializeFrame(const Frame &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/FrameNote.cpp b/src/private/mx/core/generated/FrameNote.cpp index 96c1a1c08..c991a6b92 100644 --- a/src/private/mx/core/generated/FrameNote.cpp +++ b/src/private/mx/core/generated/FrameNote.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/FrameNote.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,7 +51,7 @@ void FrameNote::setBarre(std::optional value) m_barre = std::move(value); } -FrameNote parseFrameNote(pugi::xml_node el) +FrameNote parseFrameNote(pugi::xml_node el, const ParseContext &context) { FrameNote out; for (pugi::xml_attribute a : el.attributes()) @@ -62,16 +63,16 @@ FrameNote parseFrameNote(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseFrameNoteContent(out, el); + parseFrameNoteContent(out, el, context); return out; } -void parseFrameNoteContent(FrameNote &out, pugi::xml_node el) +void parseFrameNoteContent(FrameNote &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "string")) { - out.setString(parseString(cursor)); + out.setString(parseString(cursor, context)); cursor = nextElement(cursor); } else @@ -80,7 +81,7 @@ void parseFrameNoteContent(FrameNote &out, pugi::xml_node el) } if (cursorIs(cursor, "fret")) { - out.setFret(parseFret(cursor)); + out.setFret(parseFret(cursor, context)); cursor = nextElement(cursor); } else @@ -89,12 +90,12 @@ void parseFrameNoteContent(FrameNote &out, pugi::xml_node el) } if (cursorIs(cursor, "fingering")) { - out.setFingering(parseFingering(cursor)); + out.setFingering(parseFingering(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "barre")) { - out.setBarre(parseBarre(cursor)); + out.setBarre(parseBarre(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/FrameNote.h b/src/private/mx/core/generated/FrameNote.h index 7bc0191a4..5696672a1 100644 --- a/src/private/mx/core/generated/FrameNote.h +++ b/src/private/mx/core/generated/FrameNote.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The frame-note type represents each note included in the frame. An open string will have a fret /// value of 0, while a muted string will not be associated with a frame-note element. /// Content fields mirror the schema grammar in declaration order; the @@ -44,9 +46,9 @@ class FrameNote final std::optional m_barre; }; -FrameNote parseFrameNote(pugi::xml_node el); +FrameNote parseFrameNote(pugi::xml_node el, const ParseContext &context); -void parseFrameNoteContent(FrameNote &out, pugi::xml_node el); +void parseFrameNoteContent(FrameNote &out, pugi::xml_node el, const ParseContext &context); void serializeFrameNote(const FrameNote &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Fret.cpp b/src/private/mx/core/generated/Fret.cpp index a68173f94..633b4b7ef 100644 --- a/src/private/mx/core/generated/Fret.cpp +++ b/src/private/mx/core/generated/Fret.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Fret.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -70,7 +71,7 @@ void Fret::setValue(int value) m_value = std::move(value); } -Fret parseFret(pugi::xml_node el) +Fret parseFret(pugi::xml_node el, const ParseContext &context) { Fret out; for (pugi::xml_attribute a : el.attributes()) @@ -82,36 +83,36 @@ Fret parseFret(pugi::xml_node el) } if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseFretContent(out, el); + parseFretContent(out, el, context); return out; } -void parseFretContent(Fret &out, pugi::xml_node el) +void parseFretContent(Fret &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(parseInt(childText(el))); + out.setValue(parseIntegerValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Fret.h b/src/private/mx/core/generated/Fret.h index 002176cbb..81438825c 100644 --- a/src/private/mx/core/generated/Fret.h +++ b/src/private/mx/core/generated/Fret.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The fret element is used with tablature notation and chord diagrams. Fret numbers start with 0 /// for an open string and 1 for the first fret. class Fret final @@ -47,9 +49,9 @@ class Fret final int m_value{}; }; -Fret parseFret(pugi::xml_node el); +Fret parseFret(pugi::xml_node el, const ParseContext &context); -void parseFretContent(Fret &out, pugi::xml_node el); +void parseFretContent(Fret &out, pugi::xml_node el, const ParseContext &context); void serializeFret(const Fret &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/FullNoteGroup.cpp b/src/private/mx/core/generated/FullNoteGroup.cpp index 0fe1b5681..99d83ac00 100644 --- a/src/private/mx/core/generated/FullNoteGroup.cpp +++ b/src/private/mx/core/generated/FullNoteGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/FullNoteGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,18 +31,18 @@ void FullNoteGroup::setChoice(FullNoteGroupChoice value) m_choice = std::move(value); } -FullNoteGroup parseFullNoteGroup(pugi::xml_node el, pugi::xml_node &cursor) +FullNoteGroup parseFullNoteGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { FullNoteGroup out; if (cursorIs(cursor, "chord")) { - parseEmpty(cursor); + parseEmpty(cursor, context); out.setChord(true); cursor = nextElement(cursor); } if (cursor && (cursorIs(cursor, "pitch") || cursorIs(cursor, "unpitched") || cursorIs(cursor, "rest"))) { - out.setChoice(parseFullNoteGroupChoice(el, cursor)); + out.setChoice(parseFullNoteGroupChoice(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/FullNoteGroup.h b/src/private/mx/core/generated/FullNoteGroup.h index e350ba824..351ff3b72 100644 --- a/src/private/mx/core/generated/FullNoteGroup.h +++ b/src/private/mx/core/generated/FullNoteGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The full-note group is a sequence of the common note elements between cue/grace notes and regular /// (full) notes: pitch, chord, and rest information, but not duration (cue and grace notes do not /// have duration encoded). Unpitched elements are used for unpitched percussion, speaking voice, and @@ -40,7 +42,7 @@ class FullNoteGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -FullNoteGroup parseFullNoteGroup(pugi::xml_node el, pugi::xml_node &cursor); +FullNoteGroup parseFullNoteGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeFullNoteGroup(const FullNoteGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/FullNoteGroupChoice.cpp b/src/private/mx/core/generated/FullNoteGroupChoice.cpp index ae1b84705..764997421 100644 --- a/src/private/mx/core/generated/FullNoteGroupChoice.cpp +++ b/src/private/mx/core/generated/FullNoteGroupChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/FullNoteGroupChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,23 +31,23 @@ FullNoteGroupChoice FullNoteGroupChoice::rest(Rest value) return FullNoteGroupChoice{Storage{std::in_place_index<2>, std::move(value)}}; } -FullNoteGroupChoice parseFullNoteGroupChoice(pugi::xml_node el, pugi::xml_node &cursor) +FullNoteGroupChoice parseFullNoteGroupChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "pitch"))) { - Pitch value = parsePitch(cursor); + Pitch value = parsePitch(cursor, context); cursor = nextElement(cursor); return FullNoteGroupChoice::pitch(std::move(value)); } if (cursor && (cursorIs(cursor, "unpitched"))) { - Unpitched value = parseUnpitched(cursor); + Unpitched value = parseUnpitched(cursor, context); cursor = nextElement(cursor); return FullNoteGroupChoice::unpitched(std::move(value)); } if (cursor && (cursorIs(cursor, "rest"))) { - Rest value = parseRest(cursor); + Rest value = parseRest(cursor, context); cursor = nextElement(cursor); return FullNoteGroupChoice::rest(std::move(value)); } diff --git a/src/private/mx/core/generated/FullNoteGroupChoice.h b/src/private/mx/core/generated/FullNoteGroupChoice.h index 1c9a110b4..cceafe445 100644 --- a/src/private/mx/core/generated/FullNoteGroupChoice.h +++ b/src/private/mx/core/generated/FullNoteGroupChoice.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -95,7 +97,7 @@ class FullNoteGroupChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -FullNoteGroupChoice parseFullNoteGroupChoice(pugi::xml_node el, pugi::xml_node &cursor); +FullNoteGroupChoice parseFullNoteGroupChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeFullNoteGroupChoice(const FullNoteGroupChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Glass.cpp b/src/private/mx/core/generated/Glass.cpp index dede2fbd7..811b5fb21 100644 --- a/src/private/mx/core/generated/Glass.cpp +++ b/src/private/mx/core/generated/Glass.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Glass.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Glass::setValue(GlassValue value) m_value = std::move(value); } -Glass parseGlass(pugi::xml_node el) +Glass parseGlass(pugi::xml_node el, const ParseContext &context) { Glass out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ Glass parseGlass(pugi::xml_node el) } if (aname == "smufl") { - out.setSmufl(SmuflPictogramGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else { throwUnknownAttribute(el, a.name()); } } - parseGlassContent(out, el); + parseGlassContent(out, el, context); return out; } -void parseGlassContent(Glass &out, pugi::xml_node el) +void parseGlassContent(Glass &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(GlassValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Glass.h b/src/private/mx/core/generated/Glass.h index 70999b28f..2c401794b 100644 --- a/src/private/mx/core/generated/Glass.h +++ b/src/private/mx/core/generated/Glass.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The glass type represents pictograms for glass percussion instruments. The smufl attribute is /// used to distinguish different SMuFL glyphs for wind chimes in the Chimes pictograms range, /// including those made of materials other than glass. @@ -33,9 +35,9 @@ class Glass final GlassValue m_value{}; }; -Glass parseGlass(pugi::xml_node el); +Glass parseGlass(pugi::xml_node el, const ParseContext &context); -void parseGlassContent(Glass &out, pugi::xml_node el); +void parseGlassContent(Glass &out, pugi::xml_node el, const ParseContext &context); void serializeGlass(const Glass &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/GlassValue.cpp b/src/private/mx/core/generated/GlassValue.cpp index 766e25ab6..540946d72 100644 --- a/src/private/mx/core/generated/GlassValue.cpp +++ b/src/private/mx/core/generated/GlassValue.cpp @@ -48,9 +48,15 @@ bool GlassValue::tryParse(std::string_view text, GlassValue &out) noexcept } GlassValue GlassValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +GlassValue GlassValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { GlassValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/GlassValue.h b/src/private/mx/core/generated/GlassValue.h index a5159252e..52453dfb2 100644 --- a/src/private/mx/core/generated/GlassValue.h +++ b/src/private/mx/core/generated/GlassValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -42,6 +44,9 @@ class GlassValue final /// (the import leniency policy; never produces an invalid value). static GlassValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static GlassValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const GlassValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Glissando.cpp b/src/private/mx/core/generated/Glissando.cpp index 820050166..5ca7b4689 100644 --- a/src/private/mx/core/generated/Glissando.cpp +++ b/src/private/mx/core/generated/Glissando.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Glissando.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -170,7 +171,7 @@ void Glissando::setValue(std::string value) m_value = std::move(value); } -Glissando parseGlissando(pugi::xml_node el) +Glissando parseGlissando(pugi::xml_node el, const ParseContext &context) { Glissando out; bool seen_type = false; @@ -184,63 +185,63 @@ Glissando parseGlissando(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStop::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "number") { - out.setNumber(NumberLevel::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "line-type") { - out.setLineType(LineType::parse(a.value())); + out.setLineType(parseValue(a.value(), context, el, "line-type")); } else if (aname == "dash-length") { - out.setDashLength(Tenths::parse(a.value())); + out.setDashLength(parseValue(a.value(), context, el, "dash-length")); } else if (aname == "space-length") { - out.setSpaceLength(Tenths::parse(a.value())); + out.setSpaceLength(parseValue(a.value(), context, el, "space-length")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -251,11 +252,11 @@ Glissando parseGlissando(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseGlissandoContent(out, el); + parseGlissandoContent(out, el, context); return out; } -void parseGlissandoContent(Glissando &out, pugi::xml_node el) +void parseGlissandoContent(Glissando &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/Glissando.h b/src/private/mx/core/generated/Glissando.h index 99bf6398e..a779eabfb 100644 --- a/src/private/mx/core/generated/Glissando.h +++ b/src/private/mx/core/generated/Glissando.h @@ -25,6 +25,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Glissando and slide types both indicate rapidly moving from one pitch to the other so that /// individual notes are not discerned. A glissando sounds the distinct notes in between the two /// pitches and defaults to a wavy line. The optional text is printed alongside the line. @@ -84,9 +86,9 @@ class Glissando final std::string m_value{}; }; -Glissando parseGlissando(pugi::xml_node el); +Glissando parseGlissando(pugi::xml_node el, const ParseContext &context); -void parseGlissandoContent(Glissando &out, pugi::xml_node el); +void parseGlissandoContent(Glissando &out, pugi::xml_node el, const ParseContext &context); void serializeGlissando(const Glissando &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Glyph.cpp b/src/private/mx/core/generated/Glyph.cpp index b868a3833..f9662bbc2 100644 --- a/src/private/mx/core/generated/Glyph.cpp +++ b/src/private/mx/core/generated/Glyph.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Glyph.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Glyph::setValue(SmuflGlyphName value) m_value = std::move(value); } -Glyph parseGlyph(pugi::xml_node el) +Glyph parseGlyph(pugi::xml_node el, const ParseContext &context) { Glyph out; bool seen_type = false; @@ -44,7 +45,7 @@ Glyph parseGlyph(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(GlyphType::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else { @@ -55,13 +56,13 @@ Glyph parseGlyph(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseGlyphContent(out, el); + parseGlyphContent(out, el, context); return out; } -void parseGlyphContent(Glyph &out, pugi::xml_node el) +void parseGlyphContent(Glyph &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(SmuflGlyphName::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Glyph.h b/src/private/mx/core/generated/Glyph.h index 226b3a9f5..0b2d6645e 100644 --- a/src/private/mx/core/generated/Glyph.h +++ b/src/private/mx/core/generated/Glyph.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The glyph element represents what SMuFL glyph should be used for different variations of symbols /// that are semantically identical. The type attribute specifies what type of glyph is being /// defined. The element value specifies what SMuFL glyph to use, including recommended stylistic @@ -37,9 +39,9 @@ class Glyph final SmuflGlyphName m_value{}; }; -Glyph parseGlyph(pugi::xml_node el); +Glyph parseGlyph(pugi::xml_node el, const ParseContext &context); -void parseGlyphContent(Glyph &out, pugi::xml_node el); +void parseGlyphContent(Glyph &out, pugi::xml_node el, const ParseContext &context); void serializeGlyph(const Glyph &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/GlyphType.cpp b/src/private/mx/core/generated/GlyphType.cpp index 5451037f3..0d8f08c52 100644 --- a/src/private/mx/core/generated/GlyphType.cpp +++ b/src/private/mx/core/generated/GlyphType.cpp @@ -32,4 +32,11 @@ GlyphType GlyphType::parse(std::string_view text) return GlyphType{std::string{text}}; } +GlyphType GlyphType::parse(std::string_view text, ValueParseOutcome &outcome) +{ + GlyphType out{std::string{text}}; + outcome = out.value() == text ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/GlyphType.h b/src/private/mx/core/generated/GlyphType.h index c23a559f9..0abf73a40 100644 --- a/src/private/mx/core/generated/GlyphType.h +++ b/src/private/mx/core/generated/GlyphType.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -45,6 +47,9 @@ class GlyphType final static GlyphType parse(std::string_view text); + /// Says whether the text had to be repaired. + static GlyphType parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const GlyphType &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Grace.cpp b/src/private/mx/core/generated/Grace.cpp index 612a3f3f4..e256da580 100644 --- a/src/private/mx/core/generated/Grace.cpp +++ b/src/private/mx/core/generated/Grace.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Grace.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,7 +51,7 @@ void Grace::setSlash(std::optional value) m_slash = std::move(value); } -Grace parseGrace(pugi::xml_node el) +Grace parseGrace(pugi::xml_node el, const ParseContext &context) { Grace out; for (pugi::xml_attribute a : el.attributes()) @@ -62,32 +63,33 @@ Grace parseGrace(pugi::xml_node el) } if (aname == "steal-time-previous") { - out.setStealTimePrevious(Percent::parse(a.value())); + out.setStealTimePrevious(parseValue(a.value(), context, el, "steal-time-previous")); } else if (aname == "steal-time-following") { - out.setStealTimeFollowing(Percent::parse(a.value())); + out.setStealTimeFollowing(parseValue(a.value(), context, el, "steal-time-following")); } else if (aname == "make-time") { - out.setMakeTime(Divisions::parse(a.value())); + out.setMakeTime(parseValue(a.value(), context, el, "make-time")); } else if (aname == "slash") { - out.setSlash(YesNo::parse(a.value())); + out.setSlash(parseValue(a.value(), context, el, "slash")); } else { throwUnknownAttribute(el, a.name()); } } - parseGraceContent(out, el); + parseGraceContent(out, el, context); return out; } -void parseGraceContent(Grace &out, pugi::xml_node el) +void parseGraceContent(Grace &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Grace.h b/src/private/mx/core/generated/Grace.h index 7215102bb..ca82e6014 100644 --- a/src/private/mx/core/generated/Grace.h +++ b/src/private/mx/core/generated/Grace.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The grace type indicates the presence of a grace note. The slash attribute for a grace note is /// yes for slashed grace notes. The steal-time-previous attribute indicates the percentage of time /// to steal from the previous note for the grace note. The steal-time-following attribute indicates @@ -43,9 +45,9 @@ class Grace final std::optional m_slash; }; -Grace parseGrace(pugi::xml_node el); +Grace parseGrace(pugi::xml_node el, const ParseContext &context); -void parseGraceContent(Grace &out, pugi::xml_node el); +void parseGraceContent(Grace &out, pugi::xml_node el, const ParseContext &context); void serializeGrace(const Grace &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/GraceCueNoteGroup.cpp b/src/private/mx/core/generated/GraceCueNoteGroup.cpp index 4bf75ae69..0b71339a5 100644 --- a/src/private/mx/core/generated/GraceCueNoteGroup.cpp +++ b/src/private/mx/core/generated/GraceCueNoteGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/GraceCueNoteGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,12 +31,12 @@ void GraceCueNoteGroup::setFullNote(FullNoteGroup value) m_fullNote = std::move(value); } -GraceCueNoteGroup parseGraceCueNoteGroup(pugi::xml_node el, pugi::xml_node &cursor) +GraceCueNoteGroup parseGraceCueNoteGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { GraceCueNoteGroup out; if (cursorIs(cursor, "cue")) { - out.setCue(parseEmpty(cursor)); + out.setCue(parseEmpty(cursor, context)); cursor = nextElement(cursor); } else @@ -45,7 +46,7 @@ GraceCueNoteGroup parseGraceCueNoteGroup(pugi::xml_node el, pugi::xml_node &curs if (cursor && (cursorIs(cursor, "chord") || cursorIs(cursor, "pitch") || cursorIs(cursor, "unpitched") || cursorIs(cursor, "rest"))) { - out.setFullNote(parseFullNoteGroup(el, cursor)); + out.setFullNote(parseFullNoteGroup(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/GraceCueNoteGroup.h b/src/private/mx/core/generated/GraceCueNoteGroup.h index 24b50793b..646a84ed2 100644 --- a/src/private/mx/core/generated/GraceCueNoteGroup.h +++ b/src/private/mx/core/generated/GraceCueNoteGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -37,7 +39,7 @@ class GraceCueNoteGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -GraceCueNoteGroup parseGraceCueNoteGroup(pugi::xml_node el, pugi::xml_node &cursor); +GraceCueNoteGroup parseGraceCueNoteGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeGraceCueNoteGroup(const GraceCueNoteGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/GraceNormalNoteGroup.cpp b/src/private/mx/core/generated/GraceNormalNoteGroup.cpp index a60951a82..05a7aa2ac 100644 --- a/src/private/mx/core/generated/GraceNormalNoteGroup.cpp +++ b/src/private/mx/core/generated/GraceNormalNoteGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/GraceNormalNoteGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,13 +41,13 @@ void GraceNormalNoteGroup::clearTie() noexcept m_tie.clear(); } -GraceNormalNoteGroup parseGraceNormalNoteGroup(pugi::xml_node el, pugi::xml_node &cursor) +GraceNormalNoteGroup parseGraceNormalNoteGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { GraceNormalNoteGroup out; if (cursor && (cursorIs(cursor, "chord") || cursorIs(cursor, "pitch") || cursorIs(cursor, "unpitched") || cursorIs(cursor, "rest"))) { - out.setFullNote(parseFullNoteGroup(el, cursor)); + out.setFullNote(parseFullNoteGroup(el, cursor, context)); } else { @@ -54,7 +55,7 @@ GraceNormalNoteGroup parseGraceNormalNoteGroup(pugi::xml_node el, pugi::xml_node } while (cursorIs(cursor, "tie")) { - if (!out.addTie(parseTie(cursor))) + if (!out.addTie(parseTie(cursor, context))) { throwTooManyElements(cursor); } diff --git a/src/private/mx/core/generated/GraceNormalNoteGroup.h b/src/private/mx/core/generated/GraceNormalNoteGroup.h index b3cf5ac8b..c13d77531 100644 --- a/src/private/mx/core/generated/GraceNormalNoteGroup.h +++ b/src/private/mx/core/generated/GraceNormalNoteGroup.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -41,7 +43,7 @@ class GraceNormalNoteGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -GraceNormalNoteGroup parseGraceNormalNoteGroup(pugi::xml_node el, pugi::xml_node &cursor); +GraceNormalNoteGroup parseGraceNormalNoteGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeGraceNormalNoteGroup(const GraceNormalNoteGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/GraceNoteChoice.cpp b/src/private/mx/core/generated/GraceNoteChoice.cpp index e3deb3c75..185b734ce 100644 --- a/src/private/mx/core/generated/GraceNoteChoice.cpp +++ b/src/private/mx/core/generated/GraceNoteChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/GraceNoteChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,16 +26,16 @@ GraceNoteChoice GraceNoteChoice::graceCueNoteGroup(GraceCueNoteGroup value) return GraceNoteChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -GraceNoteChoice parseGraceNoteChoice(pugi::xml_node el, pugi::xml_node &cursor) +GraceNoteChoice parseGraceNoteChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "chord") || cursorIs(cursor, "pitch") || cursorIs(cursor, "unpitched") || cursorIs(cursor, "rest"))) { - return GraceNoteChoice::graceNormalNoteGroup(parseGraceNormalNoteGroup(el, cursor)); + return GraceNoteChoice::graceNormalNoteGroup(parseGraceNormalNoteGroup(el, cursor, context)); } if (cursor && (cursorIs(cursor, "cue"))) { - return GraceNoteChoice::graceCueNoteGroup(parseGraceCueNoteGroup(el, cursor)); + return GraceNoteChoice::graceCueNoteGroup(parseGraceCueNoteGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/GraceNoteChoice.h b/src/private/mx/core/generated/GraceNoteChoice.h index 2cb493e1a..44bd2381e 100644 --- a/src/private/mx/core/generated/GraceNoteChoice.h +++ b/src/private/mx/core/generated/GraceNoteChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -80,7 +82,7 @@ class GraceNoteChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -GraceNoteChoice parseGraceNoteChoice(pugi::xml_node el, pugi::xml_node &cursor); +GraceNoteChoice parseGraceNoteChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeGraceNoteChoice(const GraceNoteChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/GraceNoteGroup.cpp b/src/private/mx/core/generated/GraceNoteGroup.cpp index 89e532f6b..ea7941e75 100644 --- a/src/private/mx/core/generated/GraceNoteGroup.cpp +++ b/src/private/mx/core/generated/GraceNoteGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/GraceNoteGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,12 +31,12 @@ void GraceNoteGroup::setGraceNoteChoice(GraceNoteChoice value) m_graceNoteChoice = std::move(value); } -GraceNoteGroup parseGraceNoteGroup(pugi::xml_node el, pugi::xml_node &cursor) +GraceNoteGroup parseGraceNoteGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { GraceNoteGroup out; if (cursorIs(cursor, "grace")) { - out.setGrace(parseGrace(cursor)); + out.setGrace(parseGrace(cursor, context)); cursor = nextElement(cursor); } else @@ -45,7 +46,7 @@ GraceNoteGroup parseGraceNoteGroup(pugi::xml_node el, pugi::xml_node &cursor) if (cursor && (cursorIs(cursor, "chord") || cursorIs(cursor, "pitch") || cursorIs(cursor, "unpitched") || cursorIs(cursor, "rest") || cursorIs(cursor, "cue"))) { - out.setGraceNoteChoice(parseGraceNoteChoice(el, cursor)); + out.setGraceNoteChoice(parseGraceNoteChoice(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/GraceNoteGroup.h b/src/private/mx/core/generated/GraceNoteGroup.h index d81c5661d..64a802afb 100644 --- a/src/private/mx/core/generated/GraceNoteGroup.h +++ b/src/private/mx/core/generated/GraceNoteGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -37,7 +39,7 @@ class GraceNoteGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -GraceNoteGroup parseGraceNoteGroup(pugi::xml_node el, pugi::xml_node &cursor); +GraceNoteGroup parseGraceNoteGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeGraceNoteGroup(const GraceNoteGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/GroupBarline.cpp b/src/private/mx/core/generated/GroupBarline.cpp index f0ff32d1e..c1b58e3f9 100644 --- a/src/private/mx/core/generated/GroupBarline.cpp +++ b/src/private/mx/core/generated/GroupBarline.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/GroupBarline.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void GroupBarline::setValue(GroupBarlineValue value) m_value = std::move(value); } -GroupBarline parseGroupBarline(pugi::xml_node el) +GroupBarline parseGroupBarline(pugi::xml_node el, const ParseContext &context) { GroupBarline out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ GroupBarline parseGroupBarline(pugi::xml_node el) } if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseGroupBarlineContent(out, el); + parseGroupBarlineContent(out, el, context); return out; } -void parseGroupBarlineContent(GroupBarline &out, pugi::xml_node el) +void parseGroupBarlineContent(GroupBarline &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(GroupBarlineValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/GroupBarline.h b/src/private/mx/core/generated/GroupBarline.h index d154306b8..0d0bfff8f 100644 --- a/src/private/mx/core/generated/GroupBarline.h +++ b/src/private/mx/core/generated/GroupBarline.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The group-barline type indicates if the group should have common barlines. class GroupBarline final { @@ -31,9 +33,9 @@ class GroupBarline final GroupBarlineValue m_value{}; }; -GroupBarline parseGroupBarline(pugi::xml_node el); +GroupBarline parseGroupBarline(pugi::xml_node el, const ParseContext &context); -void parseGroupBarlineContent(GroupBarline &out, pugi::xml_node el); +void parseGroupBarlineContent(GroupBarline &out, pugi::xml_node el, const ParseContext &context); void serializeGroupBarline(const GroupBarline &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/GroupBarlineValue.cpp b/src/private/mx/core/generated/GroupBarlineValue.cpp index f729cf41c..0b606c42a 100644 --- a/src/private/mx/core/generated/GroupBarlineValue.cpp +++ b/src/private/mx/core/generated/GroupBarlineValue.cpp @@ -48,9 +48,15 @@ bool GroupBarlineValue::tryParse(std::string_view text, GroupBarlineValue &out) } GroupBarlineValue GroupBarlineValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +GroupBarlineValue GroupBarlineValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { GroupBarlineValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/GroupBarlineValue.h b/src/private/mx/core/generated/GroupBarlineValue.h index 1a2db6ebf..916ce9449 100644 --- a/src/private/mx/core/generated/GroupBarlineValue.h +++ b/src/private/mx/core/generated/GroupBarlineValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -42,6 +44,9 @@ class GroupBarlineValue final /// (the import leniency policy; never produces an invalid value). static GroupBarlineValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static GroupBarlineValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const GroupBarlineValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/GroupName.cpp b/src/private/mx/core/generated/GroupName.cpp index adfb2857e..380d150bc 100644 --- a/src/private/mx/core/generated/GroupName.cpp +++ b/src/private/mx/core/generated/GroupName.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/GroupName.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -120,7 +121,7 @@ void GroupName::setValue(std::string value) m_value = std::move(value); } -GroupName parseGroupName(pugi::xml_node el) +GroupName parseGroupName(pugi::xml_node el, const ParseContext &context) { GroupName out; for (pugi::xml_attribute a : el.attributes()) @@ -132,54 +133,54 @@ GroupName parseGroupName(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "justify") { - out.setJustify(LeftCenterRight::parse(a.value())); + out.setJustify(parseValue(a.value(), context, el, "justify")); } else { throwUnknownAttribute(el, a.name()); } } - parseGroupNameContent(out, el); + parseGroupNameContent(out, el, context); return out; } -void parseGroupNameContent(GroupName &out, pugi::xml_node el) +void parseGroupNameContent(GroupName &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/GroupName.h b/src/private/mx/core/generated/GroupName.h index acaa9599a..8eff5d9cb 100644 --- a/src/private/mx/core/generated/GroupName.h +++ b/src/private/mx/core/generated/GroupName.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The group-name type describes the name or abbreviation of a part-group element. Formatting /// attributes in the group-name type are deprecated in Version 2.0 in favor of the new /// group-name-display and group-abbreviation-display elements. @@ -65,9 +67,9 @@ class GroupName final std::string m_value{}; }; -GroupName parseGroupName(pugi::xml_node el); +GroupName parseGroupName(pugi::xml_node el, const ParseContext &context); -void parseGroupNameContent(GroupName &out, pugi::xml_node el); +void parseGroupNameContent(GroupName &out, pugi::xml_node el, const ParseContext &context); void serializeGroupName(const GroupName &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/GroupSymbol.cpp b/src/private/mx/core/generated/GroupSymbol.cpp index 6e8d65d77..b46d40869 100644 --- a/src/private/mx/core/generated/GroupSymbol.cpp +++ b/src/private/mx/core/generated/GroupSymbol.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/GroupSymbol.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -70,7 +71,7 @@ void GroupSymbol::setValue(GroupSymbolValue value) m_value = std::move(value); } -GroupSymbol parseGroupSymbol(pugi::xml_node el) +GroupSymbol parseGroupSymbol(pugi::xml_node el, const ParseContext &context) { GroupSymbol out; for (pugi::xml_attribute a : el.attributes()) @@ -82,36 +83,36 @@ GroupSymbol parseGroupSymbol(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseGroupSymbolContent(out, el); + parseGroupSymbolContent(out, el, context); return out; } -void parseGroupSymbolContent(GroupSymbol &out, pugi::xml_node el) +void parseGroupSymbolContent(GroupSymbol &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(GroupSymbolValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/GroupSymbol.h b/src/private/mx/core/generated/GroupSymbol.h index 1de49702d..f463ddf9c 100644 --- a/src/private/mx/core/generated/GroupSymbol.h +++ b/src/private/mx/core/generated/GroupSymbol.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The group-symbol type indicates how the symbol for a group is indicated in the score. It is none /// if not specified. class GroupSymbol final @@ -45,9 +47,9 @@ class GroupSymbol final GroupSymbolValue m_value{}; }; -GroupSymbol parseGroupSymbol(pugi::xml_node el); +GroupSymbol parseGroupSymbol(pugi::xml_node el, const ParseContext &context); -void parseGroupSymbolContent(GroupSymbol &out, pugi::xml_node el); +void parseGroupSymbolContent(GroupSymbol &out, pugi::xml_node el, const ParseContext &context); void serializeGroupSymbol(const GroupSymbol &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/GroupSymbolValue.cpp b/src/private/mx/core/generated/GroupSymbolValue.cpp index c3c5d4b6d..e0be8a243 100644 --- a/src/private/mx/core/generated/GroupSymbolValue.cpp +++ b/src/private/mx/core/generated/GroupSymbolValue.cpp @@ -56,9 +56,15 @@ bool GroupSymbolValue::tryParse(std::string_view text, GroupSymbolValue &out) no } GroupSymbolValue GroupSymbolValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +GroupSymbolValue GroupSymbolValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { GroupSymbolValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/GroupSymbolValue.h b/src/private/mx/core/generated/GroupSymbolValue.h index 2b2d3bc0f..4080cacb4 100644 --- a/src/private/mx/core/generated/GroupSymbolValue.h +++ b/src/private/mx/core/generated/GroupSymbolValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -47,6 +49,9 @@ class GroupSymbolValue final /// (the import leniency policy; never produces an invalid value). static GroupSymbolValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static GroupSymbolValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const GroupSymbolValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Grouping.cpp b/src/private/mx/core/generated/Grouping.cpp index 50c7f4b38..3a76ecb02 100644 --- a/src/private/mx/core/generated/Grouping.cpp +++ b/src/private/mx/core/generated/Grouping.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Grouping.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -65,7 +66,7 @@ void Grouping::setFeature(std::vector value) m_feature = std::move(value); } -Grouping parseGrouping(pugi::xml_node el) +Grouping parseGrouping(pugi::xml_node el, const ParseContext &context) { Grouping out; bool seen_type = false; @@ -79,7 +80,7 @@ Grouping parseGrouping(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStopSingle::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "number") { @@ -91,7 +92,7 @@ Grouping parseGrouping(pugi::xml_node el) } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -102,16 +103,16 @@ Grouping parseGrouping(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseGroupingContent(out, el); + parseGroupingContent(out, el, context); return out; } -void parseGroupingContent(Grouping &out, pugi::xml_node el) +void parseGroupingContent(Grouping &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); while (cursorIs(cursor, "feature")) { - out.addFeature(parseFeature(cursor)); + out.addFeature(parseFeature(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Grouping.h b/src/private/mx/core/generated/Grouping.h index 70851e360..3047801fc 100644 --- a/src/private/mx/core/generated/Grouping.h +++ b/src/private/mx/core/generated/Grouping.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The grouping type is used for musical analysis. When the type attribute is "start" or "single", /// it usually contains one or more feature elements. The number attribute is used for distinguishing /// between overlapping and hierarchical groupings. The member-of attribute allows for easy @@ -53,9 +55,9 @@ class Grouping final std::vector m_feature; }; -Grouping parseGrouping(pugi::xml_node el); +Grouping parseGrouping(pugi::xml_node el, const ParseContext &context); -void parseGroupingContent(Grouping &out, pugi::xml_node el); +void parseGroupingContent(Grouping &out, pugi::xml_node el, const ParseContext &context); void serializeGrouping(const Grouping &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/HammerOnPullOff.cpp b/src/private/mx/core/generated/HammerOnPullOff.cpp index b2642275f..7b1140880 100644 --- a/src/private/mx/core/generated/HammerOnPullOff.cpp +++ b/src/private/mx/core/generated/HammerOnPullOff.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/HammerOnPullOff.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -140,7 +141,7 @@ void HammerOnPullOff::setValue(std::string value) m_value = std::move(value); } -HammerOnPullOff parseHammerOnPullOff(pugi::xml_node el) +HammerOnPullOff parseHammerOnPullOff(pugi::xml_node el, const ParseContext &context) { HammerOnPullOff out; bool seen_type = false; @@ -154,51 +155,51 @@ HammerOnPullOff parseHammerOnPullOff(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStop::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "number") { - out.setNumber(NumberLevel::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else { @@ -209,11 +210,11 @@ HammerOnPullOff parseHammerOnPullOff(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseHammerOnPullOffContent(out, el); + parseHammerOnPullOffContent(out, el, context); return out; } -void parseHammerOnPullOffContent(HammerOnPullOff &out, pugi::xml_node el) +void parseHammerOnPullOffContent(HammerOnPullOff &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/HammerOnPullOff.h b/src/private/mx/core/generated/HammerOnPullOff.h index e1f1e8ae1..07c304ddf 100644 --- a/src/private/mx/core/generated/HammerOnPullOff.h +++ b/src/private/mx/core/generated/HammerOnPullOff.h @@ -23,6 +23,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The hammer-on and pull-off elements are used in guitar and fretted instrument notation. Since a /// single slur can be marked over many notes, the hammer-on and pull-off elements are separate so /// the individual pair of notes can be specified. The element content can be used to specify how the @@ -75,9 +77,9 @@ class HammerOnPullOff final std::string m_value{}; }; -HammerOnPullOff parseHammerOnPullOff(pugi::xml_node el); +HammerOnPullOff parseHammerOnPullOff(pugi::xml_node el, const ParseContext &context); -void parseHammerOnPullOffContent(HammerOnPullOff &out, pugi::xml_node el); +void parseHammerOnPullOffContent(HammerOnPullOff &out, pugi::xml_node el, const ParseContext &context); void serializeHammerOnPullOff(const HammerOnPullOff &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Handbell.cpp b/src/private/mx/core/generated/Handbell.cpp index 2f2cf003d..43db326c2 100644 --- a/src/private/mx/core/generated/Handbell.cpp +++ b/src/private/mx/core/generated/Handbell.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Handbell.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -120,7 +121,7 @@ void Handbell::setValue(HandbellValue value) m_value = std::move(value); } -Handbell parseHandbell(pugi::xml_node el) +Handbell parseHandbell(pugi::xml_node el, const ParseContext &context) { Handbell out; for (pugi::xml_attribute a : el.attributes()) @@ -132,56 +133,56 @@ Handbell parseHandbell(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else { throwUnknownAttribute(el, a.name()); } } - parseHandbellContent(out, el); + parseHandbellContent(out, el, context); return out; } -void parseHandbellContent(Handbell &out, pugi::xml_node el) +void parseHandbellContent(Handbell &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(HandbellValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Handbell.h b/src/private/mx/core/generated/Handbell.h index ffc1386f9..9335b1d80 100644 --- a/src/private/mx/core/generated/Handbell.h +++ b/src/private/mx/core/generated/Handbell.h @@ -22,6 +22,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The handbell element represents notation for various techniques used in handbell and handchime /// music. class Handbell final @@ -65,9 +67,9 @@ class Handbell final HandbellValue m_value{}; }; -Handbell parseHandbell(pugi::xml_node el); +Handbell parseHandbell(pugi::xml_node el, const ParseContext &context); -void parseHandbellContent(Handbell &out, pugi::xml_node el); +void parseHandbellContent(Handbell &out, pugi::xml_node el, const ParseContext &context); void serializeHandbell(const Handbell &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/HandbellValue.cpp b/src/private/mx/core/generated/HandbellValue.cpp index d8b4051aa..04b7fa4b8 100644 --- a/src/private/mx/core/generated/HandbellValue.cpp +++ b/src/private/mx/core/generated/HandbellValue.cpp @@ -102,9 +102,15 @@ bool HandbellValue::tryParse(std::string_view text, HandbellValue &out) noexcept } HandbellValue HandbellValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +HandbellValue HandbellValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { HandbellValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/HandbellValue.h b/src/private/mx/core/generated/HandbellValue.h index d66f84585..621b83fbf 100644 --- a/src/private/mx/core/generated/HandbellValue.h +++ b/src/private/mx/core/generated/HandbellValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -60,6 +62,9 @@ class HandbellValue final /// (the import leniency policy; never produces an invalid value). static HandbellValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static HandbellValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const HandbellValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/HarmonClosed.cpp b/src/private/mx/core/generated/HarmonClosed.cpp index bfa8864b9..659e579cf 100644 --- a/src/private/mx/core/generated/HarmonClosed.cpp +++ b/src/private/mx/core/generated/HarmonClosed.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/HarmonClosed.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void HarmonClosed::setValue(HarmonClosedValue value) m_value = std::move(value); } -HarmonClosed parseHarmonClosed(pugi::xml_node el) +HarmonClosed parseHarmonClosed(pugi::xml_node el, const ParseContext &context) { HarmonClosed out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ HarmonClosed parseHarmonClosed(pugi::xml_node el) } if (aname == "location") { - out.setLocation(HarmonClosedLocation::parse(a.value())); + out.setLocation(parseValue(a.value(), context, el, "location")); } else { throwUnknownAttribute(el, a.name()); } } - parseHarmonClosedContent(out, el); + parseHarmonClosedContent(out, el, context); return out; } -void parseHarmonClosedContent(HarmonClosed &out, pugi::xml_node el) +void parseHarmonClosedContent(HarmonClosed &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(HarmonClosedValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/HarmonClosed.h b/src/private/mx/core/generated/HarmonClosed.h index c37689d27..4848383ac 100644 --- a/src/private/mx/core/generated/HarmonClosed.h +++ b/src/private/mx/core/generated/HarmonClosed.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The harmon-closed type represents whether the harmon mute is closed, open, or half-open. The /// optional location attribute indicates which portion of the symbol is filled in when the element /// value is half. @@ -33,9 +35,9 @@ class HarmonClosed final HarmonClosedValue m_value{}; }; -HarmonClosed parseHarmonClosed(pugi::xml_node el); +HarmonClosed parseHarmonClosed(pugi::xml_node el, const ParseContext &context); -void parseHarmonClosedContent(HarmonClosed &out, pugi::xml_node el); +void parseHarmonClosedContent(HarmonClosed &out, pugi::xml_node el, const ParseContext &context); void serializeHarmonClosed(const HarmonClosed &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/HarmonClosedLocation.cpp b/src/private/mx/core/generated/HarmonClosedLocation.cpp index 45de7e0f8..a3967f41c 100644 --- a/src/private/mx/core/generated/HarmonClosedLocation.cpp +++ b/src/private/mx/core/generated/HarmonClosedLocation.cpp @@ -54,9 +54,15 @@ bool HarmonClosedLocation::tryParse(std::string_view text, HarmonClosedLocation } HarmonClosedLocation HarmonClosedLocation::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +HarmonClosedLocation HarmonClosedLocation::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { HarmonClosedLocation v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/HarmonClosedLocation.h b/src/private/mx/core/generated/HarmonClosedLocation.h index 2a5d71d6b..b9bdc9cb8 100644 --- a/src/private/mx/core/generated/HarmonClosedLocation.h +++ b/src/private/mx/core/generated/HarmonClosedLocation.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -45,6 +47,9 @@ class HarmonClosedLocation final /// (the import leniency policy; never produces an invalid value). static HarmonClosedLocation parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static HarmonClosedLocation parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const HarmonClosedLocation &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/HarmonClosedValue.cpp b/src/private/mx/core/generated/HarmonClosedValue.cpp index c6f7afac6..2fcff6ed8 100644 --- a/src/private/mx/core/generated/HarmonClosedValue.cpp +++ b/src/private/mx/core/generated/HarmonClosedValue.cpp @@ -48,9 +48,15 @@ bool HarmonClosedValue::tryParse(std::string_view text, HarmonClosedValue &out) } HarmonClosedValue HarmonClosedValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +HarmonClosedValue HarmonClosedValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { HarmonClosedValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/HarmonClosedValue.h b/src/private/mx/core/generated/HarmonClosedValue.h index 2bd938620..41d4b29f6 100644 --- a/src/private/mx/core/generated/HarmonClosedValue.h +++ b/src/private/mx/core/generated/HarmonClosedValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -42,6 +44,9 @@ class HarmonClosedValue final /// (the import leniency policy; never produces an invalid value). static HarmonClosedValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static HarmonClosedValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const HarmonClosedValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/HarmonMute.cpp b/src/private/mx/core/generated/HarmonMute.cpp index 5f793f23b..c5c1edc4f 100644 --- a/src/private/mx/core/generated/HarmonMute.cpp +++ b/src/private/mx/core/generated/HarmonMute.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/HarmonMute.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -120,7 +121,7 @@ void HarmonMute::setHarmonClosed(HarmonClosed value) m_harmonClosed = std::move(value); } -HarmonMute parseHarmonMute(pugi::xml_node el) +HarmonMute parseHarmonMute(pugi::xml_node el, const ParseContext &context) { HarmonMute out; for (pugi::xml_attribute a : el.attributes()) @@ -132,59 +133,59 @@ HarmonMute parseHarmonMute(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else { throwUnknownAttribute(el, a.name()); } } - parseHarmonMuteContent(out, el); + parseHarmonMuteContent(out, el, context); return out; } -void parseHarmonMuteContent(HarmonMute &out, pugi::xml_node el) +void parseHarmonMuteContent(HarmonMute &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "harmon-closed")) { - out.setHarmonClosed(parseHarmonClosed(cursor)); + out.setHarmonClosed(parseHarmonClosed(cursor, context)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/HarmonMute.h b/src/private/mx/core/generated/HarmonMute.h index 89c7794ce..1d9ea1e86 100644 --- a/src/private/mx/core/generated/HarmonMute.h +++ b/src/private/mx/core/generated/HarmonMute.h @@ -24,6 +24,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The harmon-mute type represents the symbols used for harmon mutes in brass notation. /// Content fields mirror the schema grammar in declaration order; the /// serializer walks them, so a wrong order is unrepresentable (plan §2.3). @@ -68,9 +70,9 @@ class HarmonMute final HarmonClosed m_harmonClosed{}; }; -HarmonMute parseHarmonMute(pugi::xml_node el); +HarmonMute parseHarmonMute(pugi::xml_node el, const ParseContext &context); -void parseHarmonMuteContent(HarmonMute &out, pugi::xml_node el); +void parseHarmonMuteContent(HarmonMute &out, pugi::xml_node el, const ParseContext &context); void serializeHarmonMute(const HarmonMute &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Harmonic.cpp b/src/private/mx/core/generated/Harmonic.cpp index 8673d41f0..b1a0289ce 100644 --- a/src/private/mx/core/generated/Harmonic.cpp +++ b/src/private/mx/core/generated/Harmonic.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Harmonic.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -140,7 +141,7 @@ void Harmonic::setChoice2(std::optional value) m_choice2 = std::move(value); } -Harmonic parseHarmonic(pugi::xml_node el) +Harmonic parseHarmonic(pugi::xml_node el, const ParseContext &context) { Harmonic out; for (pugi::xml_attribute a : el.attributes()) @@ -152,68 +153,68 @@ Harmonic parseHarmonic(pugi::xml_node el) } if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else { throwUnknownAttribute(el, a.name()); } } - parseHarmonicContent(out, el); + parseHarmonicContent(out, el, context); return out; } -void parseHarmonicContent(Harmonic &out, pugi::xml_node el) +void parseHarmonicContent(Harmonic &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "natural") || cursorIs(cursor, "artificial"))) { - out.setChoice(parseHarmonicChoice(el, cursor)); + out.setChoice(parseHarmonicChoice(el, cursor, context)); } if (cursor && (cursorIs(cursor, "base-pitch") || cursorIs(cursor, "touching-pitch") || cursorIs(cursor, "sounding-pitch"))) { - out.setChoice2(parseHarmonicChoice2(el, cursor)); + out.setChoice2(parseHarmonicChoice2(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/Harmonic.h b/src/private/mx/core/generated/Harmonic.h index d256331d7..630f55756 100644 --- a/src/private/mx/core/generated/Harmonic.h +++ b/src/private/mx/core/generated/Harmonic.h @@ -26,6 +26,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The harmonic type indicates natural and artificial harmonics. Allowing the type of pitch to be /// specified, combined with controls for appearance/playback differences, allows both the notation /// and the sound to be represented. Artificial harmonics can add a notated touching pitch; @@ -81,9 +83,9 @@ class Harmonic final std::optional m_choice2; }; -Harmonic parseHarmonic(pugi::xml_node el); +Harmonic parseHarmonic(pugi::xml_node el, const ParseContext &context); -void parseHarmonicContent(Harmonic &out, pugi::xml_node el); +void parseHarmonicContent(Harmonic &out, pugi::xml_node el, const ParseContext &context); void serializeHarmonic(const Harmonic &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/HarmonicChoice.cpp b/src/private/mx/core/generated/HarmonicChoice.cpp index 17ce02f2a..4df858688 100644 --- a/src/private/mx/core/generated/HarmonicChoice.cpp +++ b/src/private/mx/core/generated/HarmonicChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/HarmonicChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,17 +26,17 @@ HarmonicChoice HarmonicChoice::artificial(Empty value) return HarmonicChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -HarmonicChoice parseHarmonicChoice(pugi::xml_node el, pugi::xml_node &cursor) +HarmonicChoice parseHarmonicChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "natural"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return HarmonicChoice::natural(std::move(value)); } if (cursor && (cursorIs(cursor, "artificial"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return HarmonicChoice::artificial(std::move(value)); } diff --git a/src/private/mx/core/generated/HarmonicChoice.h b/src/private/mx/core/generated/HarmonicChoice.h index 81690b955..7250cc369 100644 --- a/src/private/mx/core/generated/HarmonicChoice.h +++ b/src/private/mx/core/generated/HarmonicChoice.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -79,7 +81,7 @@ class HarmonicChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -HarmonicChoice parseHarmonicChoice(pugi::xml_node el, pugi::xml_node &cursor); +HarmonicChoice parseHarmonicChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeHarmonicChoice(const HarmonicChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/HarmonicChoice2.cpp b/src/private/mx/core/generated/HarmonicChoice2.cpp index 6119401e6..12e7ca443 100644 --- a/src/private/mx/core/generated/HarmonicChoice2.cpp +++ b/src/private/mx/core/generated/HarmonicChoice2.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/HarmonicChoice2.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,23 +31,23 @@ HarmonicChoice2 HarmonicChoice2::soundingPitch(Empty value) return HarmonicChoice2{Storage{std::in_place_index<2>, std::move(value)}}; } -HarmonicChoice2 parseHarmonicChoice2(pugi::xml_node el, pugi::xml_node &cursor) +HarmonicChoice2 parseHarmonicChoice2(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "base-pitch"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return HarmonicChoice2::basePitch(std::move(value)); } if (cursor && (cursorIs(cursor, "touching-pitch"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return HarmonicChoice2::touchingPitch(std::move(value)); } if (cursor && (cursorIs(cursor, "sounding-pitch"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return HarmonicChoice2::soundingPitch(std::move(value)); } diff --git a/src/private/mx/core/generated/HarmonicChoice2.h b/src/private/mx/core/generated/HarmonicChoice2.h index 87dace623..0faba368d 100644 --- a/src/private/mx/core/generated/HarmonicChoice2.h +++ b/src/private/mx/core/generated/HarmonicChoice2.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -93,7 +95,7 @@ class HarmonicChoice2 final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -HarmonicChoice2 parseHarmonicChoice2(pugi::xml_node el, pugi::xml_node &cursor); +HarmonicChoice2 parseHarmonicChoice2(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeHarmonicChoice2(const HarmonicChoice2 &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Harmony.cpp b/src/private/mx/core/generated/Harmony.cpp index 7a6b977d3..742f64004 100644 --- a/src/private/mx/core/generated/Harmony.cpp +++ b/src/private/mx/core/generated/Harmony.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Harmony.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -225,7 +226,7 @@ void Harmony::setStaff(std::optional value) m_staff = std::move(value); } -Harmony parseHarmony(pugi::xml_node el) +Harmony parseHarmony(pugi::xml_node el, const ParseContext &context) { Harmony out; for (pugi::xml_attribute a : el.attributes()) @@ -237,106 +238,106 @@ Harmony parseHarmony(pugi::xml_node el) } if (aname == "type") { - out.setType(HarmonyType::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "print-frame") { - out.setPrintFrame(YesNo::parse(a.value())); + out.setPrintFrame(parseValue(a.value(), context, el, "print-frame")); } else if (aname == "arrangement") { - out.setArrangement(HarmonyArrangement::parse(a.value())); + out.setArrangement(parseValue(a.value(), context, el, "arrangement")); } else if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "system") { - out.setSystem(SystemRelation::parse(a.value())); + out.setSystem(parseValue(a.value(), context, el, "system")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseHarmonyContent(out, el); + parseHarmonyContent(out, el, context); return out; } -void parseHarmonyContent(Harmony &out, pugi::xml_node el) +void parseHarmonyContent(Harmony &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (!(cursor && (cursorIs(cursor, "root") || cursorIs(cursor, "numeral") || cursorIs(cursor, "function")))) { throwMissingElement(el, "root"); } - out.setHarmonyChord(OneOrMore{parseHarmonyChordGroup(el, cursor)}); + out.setHarmonyChord(OneOrMore{parseHarmonyChordGroup(el, cursor, context)}); while (cursor && (cursorIs(cursor, "root") || cursorIs(cursor, "numeral") || cursorIs(cursor, "function"))) { - out.addHarmonyChord(parseHarmonyChordGroup(el, cursor)); + out.addHarmonyChord(parseHarmonyChordGroup(el, cursor, context)); } if (cursorIs(cursor, "frame")) { - out.setFrame(parseFrame(cursor)); + out.setFrame(parseFrame(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "offset")) { - out.setOffset(parseOffset(cursor)); + out.setOffset(parseOffset(cursor, context)); cursor = nextElement(cursor); } if (cursor && (cursorIs(cursor, "footnote") || cursorIs(cursor, "level"))) { - out.setEditorial(parseEditorialGroup(el, cursor)); + out.setEditorial(parseEditorialGroup(el, cursor, context)); } if (cursorIs(cursor, "staff")) { - out.setStaff(parseInt(childText(cursor))); + out.setStaff(parseIntegerValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Harmony.h b/src/private/mx/core/generated/Harmony.h index 00549a563..abc4862f6 100644 --- a/src/private/mx/core/generated/Harmony.h +++ b/src/private/mx/core/generated/Harmony.h @@ -34,6 +34,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The harmony type represents harmony analysis, including chord symbols in popular music as well as /// functional harmony analysis in classical music. If there are alternate harmonies possible, this /// can be specified using multiple harmony elements differentiated by type. Explicit harmonies have @@ -119,9 +121,9 @@ class Harmony final std::optional m_staff; }; -Harmony parseHarmony(pugi::xml_node el); +Harmony parseHarmony(pugi::xml_node el, const ParseContext &context); -void parseHarmonyContent(Harmony &out, pugi::xml_node el); +void parseHarmonyContent(Harmony &out, pugi::xml_node el, const ParseContext &context); void serializeHarmony(const Harmony &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/HarmonyAlter.cpp b/src/private/mx/core/generated/HarmonyAlter.cpp index 6221dbda2..aa08b5f59 100644 --- a/src/private/mx/core/generated/HarmonyAlter.cpp +++ b/src/private/mx/core/generated/HarmonyAlter.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/HarmonyAlter.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -130,7 +131,7 @@ void HarmonyAlter::setValue(Semitones value) m_value = std::move(value); } -HarmonyAlter parseHarmonyAlter(pugi::xml_node el) +HarmonyAlter parseHarmonyAlter(pugi::xml_node el, const ParseContext &context) { HarmonyAlter out; for (pugi::xml_attribute a : el.attributes()) @@ -142,60 +143,60 @@ HarmonyAlter parseHarmonyAlter(pugi::xml_node el) } if (aname == "location") { - out.setLocation(LeftRight::parse(a.value())); + out.setLocation(parseValue(a.value(), context, el, "location")); } else if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseHarmonyAlterContent(out, el); + parseHarmonyAlterContent(out, el, context); return out; } -void parseHarmonyAlterContent(HarmonyAlter &out, pugi::xml_node el) +void parseHarmonyAlterContent(HarmonyAlter &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(Semitones::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/HarmonyAlter.h b/src/private/mx/core/generated/HarmonyAlter.h index 8d96b1e43..09ca22c09 100644 --- a/src/private/mx/core/generated/HarmonyAlter.h +++ b/src/private/mx/core/generated/HarmonyAlter.h @@ -23,6 +23,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The harmony-alter type represents the chromatic alteration of the root, numeral, or bass of the /// current harmony-chord group within the harmony element. In some chord styles, the text of the /// preceding element may include alteration information. In that case, the print-object attribute of @@ -72,9 +74,9 @@ class HarmonyAlter final Semitones m_value{}; }; -HarmonyAlter parseHarmonyAlter(pugi::xml_node el); +HarmonyAlter parseHarmonyAlter(pugi::xml_node el, const ParseContext &context); -void parseHarmonyAlterContent(HarmonyAlter &out, pugi::xml_node el); +void parseHarmonyAlterContent(HarmonyAlter &out, pugi::xml_node el, const ParseContext &context); void serializeHarmonyAlter(const HarmonyAlter &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/HarmonyArrangement.cpp b/src/private/mx/core/generated/HarmonyArrangement.cpp index 20aae3fc0..a38d6fb50 100644 --- a/src/private/mx/core/generated/HarmonyArrangement.cpp +++ b/src/private/mx/core/generated/HarmonyArrangement.cpp @@ -48,9 +48,15 @@ bool HarmonyArrangement::tryParse(std::string_view text, HarmonyArrangement &out } HarmonyArrangement HarmonyArrangement::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +HarmonyArrangement HarmonyArrangement::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { HarmonyArrangement v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/HarmonyArrangement.h b/src/private/mx/core/generated/HarmonyArrangement.h index dbf9801c5..5caaf2d73 100644 --- a/src/private/mx/core/generated/HarmonyArrangement.h +++ b/src/private/mx/core/generated/HarmonyArrangement.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -46,6 +48,9 @@ class HarmonyArrangement final /// (the import leniency policy; never produces an invalid value). static HarmonyArrangement parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static HarmonyArrangement parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const HarmonyArrangement &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/HarmonyChordGroup.cpp b/src/private/mx/core/generated/HarmonyChordGroup.cpp index e00d04a69..a945865f6 100644 --- a/src/private/mx/core/generated/HarmonyChordGroup.cpp +++ b/src/private/mx/core/generated/HarmonyChordGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/HarmonyChordGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -65,12 +66,12 @@ void HarmonyChordGroup::setDegree(std::vector value) m_degree = std::move(value); } -HarmonyChordGroup parseHarmonyChordGroup(pugi::xml_node el, pugi::xml_node &cursor) +HarmonyChordGroup parseHarmonyChordGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { HarmonyChordGroup out; if (cursor && (cursorIs(cursor, "root") || cursorIs(cursor, "numeral") || cursorIs(cursor, "function"))) { - out.setChoice(parseHarmonyChordGroupChoice(el, cursor)); + out.setChoice(parseHarmonyChordGroupChoice(el, cursor, context)); } else { @@ -78,7 +79,7 @@ HarmonyChordGroup parseHarmonyChordGroup(pugi::xml_node el, pugi::xml_node &curs } if (cursorIs(cursor, "kind")) { - out.setKind(parseKind(cursor)); + out.setKind(parseKind(cursor, context)); cursor = nextElement(cursor); } else @@ -87,17 +88,17 @@ HarmonyChordGroup parseHarmonyChordGroup(pugi::xml_node el, pugi::xml_node &curs } if (cursorIs(cursor, "inversion")) { - out.setInversion(parseInversion(cursor)); + out.setInversion(parseInversion(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "bass")) { - out.setBass(parseBass(cursor)); + out.setBass(parseBass(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "degree")) { - out.addDegree(parseDegree(cursor)); + out.addDegree(parseDegree(cursor, context)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/HarmonyChordGroup.h b/src/private/mx/core/generated/HarmonyChordGroup.h index 984587219..18c2fa33b 100644 --- a/src/private/mx/core/generated/HarmonyChordGroup.h +++ b/src/private/mx/core/generated/HarmonyChordGroup.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A harmony element can contain many stacked chords (e.g. V of II). A sequence of harmony-chord /// groups is used for this type of secondary function, where V of II would be represented by a /// harmony-chord with a 5 numeral followed by a harmony-chord with a 2 numeral. A root is a pitch @@ -57,7 +59,7 @@ class HarmonyChordGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -HarmonyChordGroup parseHarmonyChordGroup(pugi::xml_node el, pugi::xml_node &cursor); +HarmonyChordGroup parseHarmonyChordGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeHarmonyChordGroup(const HarmonyChordGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/HarmonyChordGroupChoice.cpp b/src/private/mx/core/generated/HarmonyChordGroupChoice.cpp index 237044ca5..2a5030c64 100644 --- a/src/private/mx/core/generated/HarmonyChordGroupChoice.cpp +++ b/src/private/mx/core/generated/HarmonyChordGroupChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/HarmonyChordGroupChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,23 +31,24 @@ HarmonyChordGroupChoice HarmonyChordGroupChoice::function(StyleText value) return HarmonyChordGroupChoice{Storage{std::in_place_index<2>, std::move(value)}}; } -HarmonyChordGroupChoice parseHarmonyChordGroupChoice(pugi::xml_node el, pugi::xml_node &cursor) +HarmonyChordGroupChoice parseHarmonyChordGroupChoice(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context) { if (cursor && (cursorIs(cursor, "root"))) { - Root value = parseRoot(cursor); + Root value = parseRoot(cursor, context); cursor = nextElement(cursor); return HarmonyChordGroupChoice::root(std::move(value)); } if (cursor && (cursorIs(cursor, "numeral"))) { - Numeral value = parseNumeral(cursor); + Numeral value = parseNumeral(cursor, context); cursor = nextElement(cursor); return HarmonyChordGroupChoice::numeral(std::move(value)); } if (cursor && (cursorIs(cursor, "function"))) { - StyleText value = parseStyleText(cursor); + StyleText value = parseStyleText(cursor, context); cursor = nextElement(cursor); return HarmonyChordGroupChoice::function(std::move(value)); } diff --git a/src/private/mx/core/generated/HarmonyChordGroupChoice.h b/src/private/mx/core/generated/HarmonyChordGroupChoice.h index 86adac49f..60ce4b004 100644 --- a/src/private/mx/core/generated/HarmonyChordGroupChoice.h +++ b/src/private/mx/core/generated/HarmonyChordGroupChoice.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -95,7 +97,8 @@ class HarmonyChordGroupChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -HarmonyChordGroupChoice parseHarmonyChordGroupChoice(pugi::xml_node el, pugi::xml_node &cursor); +HarmonyChordGroupChoice parseHarmonyChordGroupChoice(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context); void serializeHarmonyChordGroupChoice(const HarmonyChordGroupChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/HarmonyType.cpp b/src/private/mx/core/generated/HarmonyType.cpp index cc18287cc..3ffcf7648 100644 --- a/src/private/mx/core/generated/HarmonyType.cpp +++ b/src/private/mx/core/generated/HarmonyType.cpp @@ -48,9 +48,15 @@ bool HarmonyType::tryParse(std::string_view text, HarmonyType &out) noexcept } HarmonyType HarmonyType::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +HarmonyType HarmonyType::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { HarmonyType v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/HarmonyType.h b/src/private/mx/core/generated/HarmonyType.h index f79f44c42..2962630f3 100644 --- a/src/private/mx/core/generated/HarmonyType.h +++ b/src/private/mx/core/generated/HarmonyType.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -44,6 +46,9 @@ class HarmonyType final /// (the import leniency policy; never produces an invalid value). static HarmonyType parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static HarmonyType parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const HarmonyType &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/HarpPedals.cpp b/src/private/mx/core/generated/HarpPedals.cpp index a9e8b971a..0a7b910e0 100644 --- a/src/private/mx/core/generated/HarpPedals.cpp +++ b/src/private/mx/core/generated/HarpPedals.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/HarpPedals.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -145,7 +146,7 @@ void HarpPedals::setPedalTuning(OneOrMore value) m_pedalTuning = std::move(value); } -HarpPedals parseHarpPedals(pugi::xml_node el) +HarpPedals parseHarpPedals(pugi::xml_node el, const ParseContext &context) { HarpPedals out; for (pugi::xml_attribute a : el.attributes()) @@ -157,73 +158,73 @@ HarpPedals parseHarpPedals(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseHarpPedalsContent(out, el); + parseHarpPedalsContent(out, el, context); return out; } -void parseHarpPedalsContent(HarpPedals &out, pugi::xml_node el) +void parseHarpPedalsContent(HarpPedals &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (!cursorIs(cursor, "pedal-tuning")) { throwMissingOrMisplaced(el, cursor, "pedal-tuning"); } - out.setPedalTuning(OneOrMore{parsePedalTuning(cursor)}); + out.setPedalTuning(OneOrMore{parsePedalTuning(cursor, context)}); cursor = nextElement(cursor); while (cursorIs(cursor, "pedal-tuning")) { - out.addPedalTuning(parsePedalTuning(cursor)); + out.addPedalTuning(parsePedalTuning(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/HarpPedals.h b/src/private/mx/core/generated/HarpPedals.h index 08792a3ba..59d65e917 100644 --- a/src/private/mx/core/generated/HarpPedals.h +++ b/src/private/mx/core/generated/HarpPedals.h @@ -28,6 +28,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The harp-pedals type is used to create harp pedal diagrams. The pedal-step and pedal-alter /// elements use the same values as the step and alter elements. For easiest reading, the /// pedal-tuning elements should follow standard harp pedal order, with pedal-step values of D, C, B, @@ -82,9 +84,9 @@ class HarpPedals final OneOrMore m_pedalTuning; }; -HarpPedals parseHarpPedals(pugi::xml_node el); +HarpPedals parseHarpPedals(pugi::xml_node el, const ParseContext &context); -void parseHarpPedalsContent(HarpPedals &out, pugi::xml_node el); +void parseHarpPedalsContent(HarpPedals &out, pugi::xml_node el, const ParseContext &context); void serializeHarpPedals(const HarpPedals &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/HeelToe.cpp b/src/private/mx/core/generated/HeelToe.cpp index a38182115..be2966f68 100644 --- a/src/private/mx/core/generated/HeelToe.cpp +++ b/src/private/mx/core/generated/HeelToe.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/HeelToe.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -20,7 +21,7 @@ void HeelToe::setSubstitution(std::optional value) m_substitution = std::move(value); } -HeelToe parseHeelToe(pugi::xml_node el) +HeelToe parseHeelToe(pugi::xml_node el, const ParseContext &context) { HeelToe out; for (pugi::xml_attribute a : el.attributes()) @@ -32,54 +33,54 @@ HeelToe parseHeelToe(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "substitution") { - out.setSubstitution(YesNo::parse(a.value())); + out.setSubstitution(parseValue(a.value(), context, el, "substitution")); } else { throwUnknownAttribute(el, a.name()); } } - parseEmptyPlacementContent(out, el); + parseEmptyPlacementContent(out, el, context); return out; } diff --git a/src/private/mx/core/generated/HeelToe.h b/src/private/mx/core/generated/HeelToe.h index 58e3b0b6d..c2325488c 100644 --- a/src/private/mx/core/generated/HeelToe.h +++ b/src/private/mx/core/generated/HeelToe.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The heel and toe elements are used with organ pedals. The substitution value is "no" if the /// attribute is not present. /// The schema's complexContent extension: non-polymorphic public @@ -31,7 +33,7 @@ class HeelToe : public EmptyPlacement std::optional m_substitution; }; -HeelToe parseHeelToe(pugi::xml_node el); +HeelToe parseHeelToe(pugi::xml_node el, const ParseContext &context); void serializeHeelToe(const HeelToe &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Hole.cpp b/src/private/mx/core/generated/Hole.cpp index b0670004d..927c3d9ef 100644 --- a/src/private/mx/core/generated/Hole.cpp +++ b/src/private/mx/core/generated/Hole.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Hole.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -140,7 +141,7 @@ void Hole::setHoleShape(std::optional value) m_holeShape = std::move(value); } -Hole parseHole(pugi::xml_node el) +Hole parseHole(pugi::xml_node el, const ParseContext &context) { Hole out; for (pugi::xml_attribute a : el.attributes()) @@ -152,54 +153,54 @@ Hole parseHole(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else { throwUnknownAttribute(el, a.name()); } } - parseHoleContent(out, el); + parseHoleContent(out, el, context); return out; } -void parseHoleContent(Hole &out, pugi::xml_node el) +void parseHoleContent(Hole &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "hole-type")) @@ -209,7 +210,7 @@ void parseHoleContent(Hole &out, pugi::xml_node el) } if (cursorIs(cursor, "hole-closed")) { - out.setHoleClosed(parseHoleClosed(cursor)); + out.setHoleClosed(parseHoleClosed(cursor, context)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/Hole.h b/src/private/mx/core/generated/Hole.h index 2b11c296f..61d103c28 100644 --- a/src/private/mx/core/generated/Hole.h +++ b/src/private/mx/core/generated/Hole.h @@ -24,6 +24,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The hole type represents the symbols used for woodwind and brass fingerings as well as other /// notations. /// Content fields mirror the schema grammar in declaration order; the @@ -75,9 +77,9 @@ class Hole final std::optional m_holeShape; }; -Hole parseHole(pugi::xml_node el); +Hole parseHole(pugi::xml_node el, const ParseContext &context); -void parseHoleContent(Hole &out, pugi::xml_node el); +void parseHoleContent(Hole &out, pugi::xml_node el, const ParseContext &context); void serializeHole(const Hole &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/HoleClosed.cpp b/src/private/mx/core/generated/HoleClosed.cpp index 8c8b8df06..ffd0de243 100644 --- a/src/private/mx/core/generated/HoleClosed.cpp +++ b/src/private/mx/core/generated/HoleClosed.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/HoleClosed.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void HoleClosed::setValue(HoleClosedValue value) m_value = std::move(value); } -HoleClosed parseHoleClosed(pugi::xml_node el) +HoleClosed parseHoleClosed(pugi::xml_node el, const ParseContext &context) { HoleClosed out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ HoleClosed parseHoleClosed(pugi::xml_node el) } if (aname == "location") { - out.setLocation(HoleClosedLocation::parse(a.value())); + out.setLocation(parseValue(a.value(), context, el, "location")); } else { throwUnknownAttribute(el, a.name()); } } - parseHoleClosedContent(out, el); + parseHoleClosedContent(out, el, context); return out; } -void parseHoleClosedContent(HoleClosed &out, pugi::xml_node el) +void parseHoleClosedContent(HoleClosed &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(HoleClosedValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/HoleClosed.h b/src/private/mx/core/generated/HoleClosed.h index e12c478de..ae9fac286 100644 --- a/src/private/mx/core/generated/HoleClosed.h +++ b/src/private/mx/core/generated/HoleClosed.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The hole-closed type represents whether the hole is closed, open, or half-open. The optional /// location attribute indicates which portion of the hole is filled in when the element value is /// half. @@ -33,9 +35,9 @@ class HoleClosed final HoleClosedValue m_value{}; }; -HoleClosed parseHoleClosed(pugi::xml_node el); +HoleClosed parseHoleClosed(pugi::xml_node el, const ParseContext &context); -void parseHoleClosedContent(HoleClosed &out, pugi::xml_node el); +void parseHoleClosedContent(HoleClosed &out, pugi::xml_node el, const ParseContext &context); void serializeHoleClosed(const HoleClosed &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/HoleClosedLocation.cpp b/src/private/mx/core/generated/HoleClosedLocation.cpp index 41383ca0d..a6e56f8eb 100644 --- a/src/private/mx/core/generated/HoleClosedLocation.cpp +++ b/src/private/mx/core/generated/HoleClosedLocation.cpp @@ -54,9 +54,15 @@ bool HoleClosedLocation::tryParse(std::string_view text, HoleClosedLocation &out } HoleClosedLocation HoleClosedLocation::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +HoleClosedLocation HoleClosedLocation::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { HoleClosedLocation v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/HoleClosedLocation.h b/src/private/mx/core/generated/HoleClosedLocation.h index e9ba43d58..8d3b82a4d 100644 --- a/src/private/mx/core/generated/HoleClosedLocation.h +++ b/src/private/mx/core/generated/HoleClosedLocation.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -45,6 +47,9 @@ class HoleClosedLocation final /// (the import leniency policy; never produces an invalid value). static HoleClosedLocation parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static HoleClosedLocation parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const HoleClosedLocation &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/HoleClosedValue.cpp b/src/private/mx/core/generated/HoleClosedValue.cpp index 6d86cef50..170ef6d85 100644 --- a/src/private/mx/core/generated/HoleClosedValue.cpp +++ b/src/private/mx/core/generated/HoleClosedValue.cpp @@ -48,9 +48,15 @@ bool HoleClosedValue::tryParse(std::string_view text, HoleClosedValue &out) noex } HoleClosedValue HoleClosedValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +HoleClosedValue HoleClosedValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { HoleClosedValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/HoleClosedValue.h b/src/private/mx/core/generated/HoleClosedValue.h index 6c015a95c..31973bb5f 100644 --- a/src/private/mx/core/generated/HoleClosedValue.h +++ b/src/private/mx/core/generated/HoleClosedValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -42,6 +44,9 @@ class HoleClosedValue final /// (the import leniency policy; never produces an invalid value). static HoleClosedValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static HoleClosedValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const HoleClosedValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/HorizontalTurn.cpp b/src/private/mx/core/generated/HorizontalTurn.cpp index 2efb4e60b..e9347b14c 100644 --- a/src/private/mx/core/generated/HorizontalTurn.cpp +++ b/src/private/mx/core/generated/HorizontalTurn.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/HorizontalTurn.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -190,7 +191,7 @@ void HorizontalTurn::setLastBeat(std::optional value) m_lastBeat = std::move(value); } -HorizontalTurn parseHorizontalTurn(pugi::xml_node el) +HorizontalTurn parseHorizontalTurn(pugi::xml_node el, const ParseContext &context) { HorizontalTurn out; for (pugi::xml_attribute a : el.attributes()) @@ -202,88 +203,89 @@ HorizontalTurn parseHorizontalTurn(pugi::xml_node el) } if (aname == "slash") { - out.setSlash(YesNo::parse(a.value())); + out.setSlash(parseValue(a.value(), context, el, "slash")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "start-note") { - out.setStartNote(StartNote::parse(a.value())); + out.setStartNote(parseValue(a.value(), context, el, "start-note")); } else if (aname == "trill-step") { - out.setTrillStep(TrillStep::parse(a.value())); + out.setTrillStep(parseValue(a.value(), context, el, "trill-step")); } else if (aname == "two-note-turn") { - out.setTwoNoteTurn(TwoNoteTurn::parse(a.value())); + out.setTwoNoteTurn(parseValue(a.value(), context, el, "two-note-turn")); } else if (aname == "accelerate") { - out.setAccelerate(YesNo::parse(a.value())); + out.setAccelerate(parseValue(a.value(), context, el, "accelerate")); } else if (aname == "beats") { - out.setBeats(TrillBeats::parse(a.value())); + out.setBeats(parseValue(a.value(), context, el, "beats")); } else if (aname == "second-beat") { - out.setSecondBeat(Percent::parse(a.value())); + out.setSecondBeat(parseValue(a.value(), context, el, "second-beat")); } else if (aname == "last-beat") { - out.setLastBeat(Percent::parse(a.value())); + out.setLastBeat(parseValue(a.value(), context, el, "last-beat")); } else { throwUnknownAttribute(el, a.name()); } } - parseHorizontalTurnContent(out, el); + parseHorizontalTurnContent(out, el, context); return out; } -void parseHorizontalTurnContent(HorizontalTurn &out, pugi::xml_node el) +void parseHorizontalTurnContent(HorizontalTurn &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/HorizontalTurn.h b/src/private/mx/core/generated/HorizontalTurn.h index 2a58360b5..ccb35817e 100644 --- a/src/private/mx/core/generated/HorizontalTurn.h +++ b/src/private/mx/core/generated/HorizontalTurn.h @@ -27,6 +27,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The horizontal-turn type represents turn elements that are horizontal rather than vertical. These /// are empty elements with print-style, placement, trill-sound, and slash attributes. If the slash /// attribute is yes, then a vertical line is used to slash the turn. It is no if not specified. @@ -92,9 +94,9 @@ class HorizontalTurn final std::optional m_lastBeat; }; -HorizontalTurn parseHorizontalTurn(pugi::xml_node el); +HorizontalTurn parseHorizontalTurn(pugi::xml_node el, const ParseContext &context); -void parseHorizontalTurnContent(HorizontalTurn &out, pugi::xml_node el); +void parseHorizontalTurnContent(HorizontalTurn &out, pugi::xml_node el, const ParseContext &context); void serializeHorizontalTurn(const HorizontalTurn &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Identification.cpp b/src/private/mx/core/generated/Identification.cpp index 01b3aba09..c67ea3602 100644 --- a/src/private/mx/core/generated/Identification.cpp +++ b/src/private/mx/core/generated/Identification.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Identification.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -85,7 +86,7 @@ void Identification::setMiscellaneous(std::optional value) m_miscellaneous = std::move(value); } -Identification parseIdentification(pugi::xml_node el) +Identification parseIdentification(pugi::xml_node el, const ParseContext &context) { Identification out; for (pugi::xml_attribute a : el.attributes()) @@ -97,26 +98,26 @@ Identification parseIdentification(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseIdentificationContent(out, el); + parseIdentificationContent(out, el, context); return out; } -void parseIdentificationContent(Identification &out, pugi::xml_node el) +void parseIdentificationContent(Identification &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); while (cursorIs(cursor, "creator")) { - out.addCreator(parseTypedText(cursor)); + out.addCreator(parseTypedText(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "rights")) { - out.addRights(parseTypedText(cursor)); + out.addRights(parseTypedText(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "encoding")) { - out.setEncoding(parseEncoding(cursor)); + out.setEncoding(parseEncoding(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "source")) @@ -126,12 +127,12 @@ void parseIdentificationContent(Identification &out, pugi::xml_node el) } while (cursorIs(cursor, "relation")) { - out.addRelation(parseTypedText(cursor)); + out.addRelation(parseTypedText(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "miscellaneous")) { - out.setMiscellaneous(parseMiscellaneous(cursor)); + out.setMiscellaneous(parseMiscellaneous(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Identification.h b/src/private/mx/core/generated/Identification.h index 198699082..1ce7bcbe7 100644 --- a/src/private/mx/core/generated/Identification.h +++ b/src/private/mx/core/generated/Identification.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Identification contains basic metadata about the score. It includes information that may apply at /// a score-wide, movement-wide, or part-wide level. The creator, rights, source, and relation /// elements are based on Dublin Core. @@ -53,9 +55,9 @@ class Identification final std::optional m_miscellaneous; }; -Identification parseIdentification(pugi::xml_node el); +Identification parseIdentification(pugi::xml_node el, const ParseContext &context); -void parseIdentificationContent(Identification &out, pugi::xml_node el); +void parseIdentificationContent(Identification &out, pugi::xml_node el, const ParseContext &context); void serializeIdentification(const Identification &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Image.cpp b/src/private/mx/core/generated/Image.cpp index f19babe16..2ba06f193 100644 --- a/src/private/mx/core/generated/Image.cpp +++ b/src/private/mx/core/generated/Image.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Image.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -120,7 +121,7 @@ void Image::setID(std::optional value) m_id = std::move(value); } -Image parseImage(pugi::xml_node el) +Image parseImage(pugi::xml_node el, const ParseContext &context) { Image out; bool seen_source = false; @@ -144,39 +145,39 @@ Image parseImage(pugi::xml_node el) } else if (aname == "height") { - out.setHeight(Tenths::parse(a.value())); + out.setHeight(parseValue(a.value(), context, el, "height")); } else if (aname == "width") { - out.setWidth(Tenths::parse(a.value())); + out.setWidth(parseValue(a.value(), context, el, "width")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(ValignImage::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -191,13 +192,14 @@ Image parseImage(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseImageContent(out, el); + parseImageContent(out, el, context); return out; } -void parseImageContent(Image &out, pugi::xml_node el) +void parseImageContent(Image &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Image.h b/src/private/mx/core/generated/Image.h index 7cd1bc684..ee6d1a7bb 100644 --- a/src/private/mx/core/generated/Image.h +++ b/src/private/mx/core/generated/Image.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The image type is used to include graphical images in a score. class Image final { @@ -61,9 +63,9 @@ class Image final std::optional m_id; }; -Image parseImage(pugi::xml_node el); +Image parseImage(pugi::xml_node el, const ParseContext &context); -void parseImageContent(Image &out, pugi::xml_node el); +void parseImageContent(Image &out, pugi::xml_node el, const ParseContext &context); void serializeImage(const Image &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Instrument.cpp b/src/private/mx/core/generated/Instrument.cpp index ec72a97a6..5628eeaf6 100644 --- a/src/private/mx/core/generated/Instrument.cpp +++ b/src/private/mx/core/generated/Instrument.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Instrument.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -20,7 +21,7 @@ void Instrument::setID(Token value) m_id = std::move(value); } -Instrument parseInstrument(pugi::xml_node el) +Instrument parseInstrument(pugi::xml_node el, const ParseContext &context) { Instrument out; bool seen_id = false; @@ -34,7 +35,7 @@ Instrument parseInstrument(pugi::xml_node el) if (aname == "id") { seen_id = true; - out.setID(Token::parse(a.value())); + out.setID(parseValue(a.value(), context, el, "id")); } else { @@ -45,13 +46,14 @@ Instrument parseInstrument(pugi::xml_node el) { throwMissingAttribute(el, "id"); } - parseInstrumentContent(out, el); + parseInstrumentContent(out, el, context); return out; } -void parseInstrumentContent(Instrument &out, pugi::xml_node el) +void parseInstrumentContent(Instrument &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Instrument.h b/src/private/mx/core/generated/Instrument.h index 201ac77b2..3b5d0a12b 100644 --- a/src/private/mx/core/generated/Instrument.h +++ b/src/private/mx/core/generated/Instrument.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The instrument type distinguishes between score-instrument elements in a score-part. The id /// attribute is an IDREF back to the score-instrument ID. If multiple score-instruments are /// specified in a score-part, there should be an instrument element for each note in the part. Notes @@ -31,9 +33,9 @@ class Instrument final Token m_id{}; }; -Instrument parseInstrument(pugi::xml_node el); +Instrument parseInstrument(pugi::xml_node el, const ParseContext &context); -void parseInstrumentContent(Instrument &out, pugi::xml_node el); +void parseInstrumentContent(Instrument &out, pugi::xml_node el, const ParseContext &context); void serializeInstrument(const Instrument &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/InstrumentChange.cpp b/src/private/mx/core/generated/InstrumentChange.cpp index 034b01976..c61973bec 100644 --- a/src/private/mx/core/generated/InstrumentChange.cpp +++ b/src/private/mx/core/generated/InstrumentChange.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/InstrumentChange.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void InstrumentChange::setVirtualInstrumentData(VirtualInstrumentDataGroup value m_virtualInstrumentData = std::move(value); } -InstrumentChange parseInstrumentChange(pugi::xml_node el) +InstrumentChange parseInstrumentChange(pugi::xml_node el, const ParseContext &context) { InstrumentChange out; bool seen_id = false; @@ -44,7 +45,7 @@ InstrumentChange parseInstrumentChange(pugi::xml_node el) if (aname == "id") { seen_id = true; - out.setID(Token::parse(a.value())); + out.setID(parseValue(a.value(), context, el, "id")); } else { @@ -55,17 +56,17 @@ InstrumentChange parseInstrumentChange(pugi::xml_node el) { throwMissingAttribute(el, "id"); } - parseInstrumentChangeContent(out, el); + parseInstrumentChangeContent(out, el, context); return out; } -void parseInstrumentChangeContent(InstrumentChange &out, pugi::xml_node el) +void parseInstrumentChangeContent(InstrumentChange &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "instrument-sound") || cursorIs(cursor, "solo") || cursorIs(cursor, "ensemble") || cursorIs(cursor, "virtual-instrument"))) { - out.setVirtualInstrumentData(parseVirtualInstrumentDataGroup(el, cursor)); + out.setVirtualInstrumentData(parseVirtualInstrumentDataGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/InstrumentChange.h b/src/private/mx/core/generated/InstrumentChange.h index cdb7ee51e..557001783 100644 --- a/src/private/mx/core/generated/InstrumentChange.h +++ b/src/private/mx/core/generated/InstrumentChange.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The instrument-change element type represents a change to the virtual instrument sound for a /// given score-instrument. The id attribute refers to the score-instrument affected by the change. /// All instrument-change child elements can also be initially specified within the score-instrument @@ -39,9 +41,9 @@ class InstrumentChange final VirtualInstrumentDataGroup m_virtualInstrumentData{}; }; -InstrumentChange parseInstrumentChange(pugi::xml_node el); +InstrumentChange parseInstrumentChange(pugi::xml_node el, const ParseContext &context); -void parseInstrumentChangeContent(InstrumentChange &out, pugi::xml_node el); +void parseInstrumentChangeContent(InstrumentChange &out, pugi::xml_node el, const ParseContext &context); void serializeInstrumentChange(const InstrumentChange &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/InstrumentLink.cpp b/src/private/mx/core/generated/InstrumentLink.cpp index b6cee8096..37351c2a1 100644 --- a/src/private/mx/core/generated/InstrumentLink.cpp +++ b/src/private/mx/core/generated/InstrumentLink.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/InstrumentLink.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -20,7 +21,7 @@ void InstrumentLink::setID(Token value) m_id = std::move(value); } -InstrumentLink parseInstrumentLink(pugi::xml_node el) +InstrumentLink parseInstrumentLink(pugi::xml_node el, const ParseContext &context) { InstrumentLink out; bool seen_id = false; @@ -34,7 +35,7 @@ InstrumentLink parseInstrumentLink(pugi::xml_node el) if (aname == "id") { seen_id = true; - out.setID(Token::parse(a.value())); + out.setID(parseValue(a.value(), context, el, "id")); } else { @@ -45,13 +46,14 @@ InstrumentLink parseInstrumentLink(pugi::xml_node el) { throwMissingAttribute(el, "id"); } - parseInstrumentLinkContent(out, el); + parseInstrumentLinkContent(out, el, context); return out; } -void parseInstrumentLinkContent(InstrumentLink &out, pugi::xml_node el) +void parseInstrumentLinkContent(InstrumentLink &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/InstrumentLink.h b/src/private/mx/core/generated/InstrumentLink.h index a7549666a..d2ffa29af 100644 --- a/src/private/mx/core/generated/InstrumentLink.h +++ b/src/private/mx/core/generated/InstrumentLink.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Multiple part-link elements can link a condensed part within a score file to multiple MusicXML /// parts files. For example, a "Clarinet 1 and 2" part in a score file could link to separate /// "Clarinet 1" and "Clarinet 2" part files. The instrument-link type distinguish which of the @@ -32,9 +34,9 @@ class InstrumentLink final Token m_id{}; }; -InstrumentLink parseInstrumentLink(pugi::xml_node el); +InstrumentLink parseInstrumentLink(pugi::xml_node el, const ParseContext &context); -void parseInstrumentLinkContent(InstrumentLink &out, pugi::xml_node el); +void parseInstrumentLinkContent(InstrumentLink &out, pugi::xml_node el, const ParseContext &context); void serializeInstrumentLink(const InstrumentLink &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/InstrumentSound.cpp b/src/private/mx/core/generated/InstrumentSound.cpp index d30cd30d9..a77e0be71 100644 --- a/src/private/mx/core/generated/InstrumentSound.cpp +++ b/src/private/mx/core/generated/InstrumentSound.cpp @@ -53,12 +53,20 @@ bool InstrumentSound::tryParse(std::string_view text, InstrumentSound &out) } InstrumentSound InstrumentSound::parse(std::string_view text) +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +InstrumentSound InstrumentSound::parse(std::string_view text, ValueParseOutcome &outcome) { InstrumentSound out; if (tryParse(text, out)) { + outcome = ValueParseOutcome::valid; return out; } + outcome = ValueParseOutcome::invalid; return InstrumentSound::soundID(SoundID::parse(text)); } diff --git a/src/private/mx/core/generated/InstrumentSound.h b/src/private/mx/core/generated/InstrumentSound.h index a471abb9a..3c1f355d8 100644 --- a/src/private/mx/core/generated/InstrumentSound.h +++ b/src/private/mx/core/generated/InstrumentSound.h @@ -2,6 +2,7 @@ #pragma once +#include "mx/core/Lexical.h" #include "mx/core/generated/SoundID.h" #include @@ -69,6 +70,10 @@ class InstrumentSound final /// parse (never produces an invalid value). static InstrumentSound parse(std::string_view text); + /// Lenient, and says whether no member matched or a matched number was + /// clamped. + static InstrumentSound parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const InstrumentSound &other) const = default; private: diff --git a/src/private/mx/core/generated/Interchangeable.cpp b/src/private/mx/core/generated/Interchangeable.cpp index 903e36be3..88dea540e 100644 --- a/src/private/mx/core/generated/Interchangeable.cpp +++ b/src/private/mx/core/generated/Interchangeable.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Interchangeable.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -55,7 +56,7 @@ void Interchangeable::setTimeSignature(OneOrMore value) m_timeSignature = std::move(value); } -Interchangeable parseInterchangeable(pugi::xml_node el) +Interchangeable parseInterchangeable(pugi::xml_node el, const ParseContext &context) { Interchangeable out; for (pugi::xml_attribute a : el.attributes()) @@ -67,37 +68,37 @@ Interchangeable parseInterchangeable(pugi::xml_node el) } if (aname == "symbol") { - out.setSymbol(TimeSymbol::parse(a.value())); + out.setSymbol(parseValue(a.value(), context, el, "symbol")); } else if (aname == "separator") { - out.setSeparator(TimeSeparator::parse(a.value())); + out.setSeparator(parseValue(a.value(), context, el, "separator")); } else { throwUnknownAttribute(el, a.name()); } } - parseInterchangeableContent(out, el); + parseInterchangeableContent(out, el, context); return out; } -void parseInterchangeableContent(Interchangeable &out, pugi::xml_node el) +void parseInterchangeableContent(Interchangeable &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "time-relation")) { - out.setTimeRelation(TimeRelation::parse(childText(cursor))); + out.setTimeRelation(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (!(cursor && (cursorIs(cursor, "beats")))) { throwMissingElement(el, "beats"); } - out.setTimeSignature(OneOrMore{parseTimeSignatureGroup(el, cursor)}); + out.setTimeSignature(OneOrMore{parseTimeSignatureGroup(el, cursor, context)}); while (cursor && (cursorIs(cursor, "beats"))) { - out.addTimeSignature(parseTimeSignatureGroup(el, cursor)); + out.addTimeSignature(parseTimeSignatureGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/Interchangeable.h b/src/private/mx/core/generated/Interchangeable.h index c9c0494d4..db98ca10e 100644 --- a/src/private/mx/core/generated/Interchangeable.h +++ b/src/private/mx/core/generated/Interchangeable.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The interchangeable type is used to represent the second in a pair of interchangeable dual time /// signatures, such as the 6/8 in 3/4 (6/8). A separate symbol attribute value is available compared /// to the time element's symbol attribute, which applies to the first of the dual time signatures. @@ -47,9 +49,9 @@ class Interchangeable final OneOrMore m_timeSignature; }; -Interchangeable parseInterchangeable(pugi::xml_node el); +Interchangeable parseInterchangeable(pugi::xml_node el, const ParseContext &context); -void parseInterchangeableContent(Interchangeable &out, pugi::xml_node el); +void parseInterchangeableContent(Interchangeable &out, pugi::xml_node el, const ParseContext &context); void serializeInterchangeable(const Interchangeable &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Inversion.cpp b/src/private/mx/core/generated/Inversion.cpp index 150c89c9a..cb6d98f09 100644 --- a/src/private/mx/core/generated/Inversion.cpp +++ b/src/private/mx/core/generated/Inversion.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Inversion.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -120,7 +121,7 @@ void Inversion::setValue(int value) m_value = std::move(value); } -Inversion parseInversion(pugi::xml_node el) +Inversion parseInversion(pugi::xml_node el, const ParseContext &context) { Inversion out; for (pugi::xml_attribute a : el.attributes()) @@ -136,52 +137,52 @@ Inversion parseInversion(pugi::xml_node el) } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseInversionContent(out, el); + parseInversionContent(out, el, context); return out; } -void parseInversionContent(Inversion &out, pugi::xml_node el) +void parseInversionContent(Inversion &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(parseInt(childText(el))); + out.setValue(parseIntegerValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Inversion.h b/src/private/mx/core/generated/Inversion.h index 341c719a7..f6333b3c4 100644 --- a/src/private/mx/core/generated/Inversion.h +++ b/src/private/mx/core/generated/Inversion.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The inversion type represents harmony inversions. The value is a number indicating which /// inversion is used: 0 for root position, 1 for first inversion, etc. The text attribute indicates /// how the inversion should be displayed in a score. @@ -64,9 +66,9 @@ class Inversion final int m_value{}; }; -Inversion parseInversion(pugi::xml_node el); +Inversion parseInversion(pugi::xml_node el, const ParseContext &context); -void parseInversionContent(Inversion &out, pugi::xml_node el); +void parseInversionContent(Inversion &out, pugi::xml_node el, const ParseContext &context); void serializeInversion(const Inversion &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Key.cpp b/src/private/mx/core/generated/Key.cpp index 56cb8cb1f..4fe8df5d5 100644 --- a/src/private/mx/core/generated/Key.cpp +++ b/src/private/mx/core/generated/Key.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Key.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -155,7 +156,7 @@ void Key::setKeyOctave(std::vector value) m_keyOctave = std::move(value); } -Key parseKey(pugi::xml_node el) +Key parseKey(pugi::xml_node el, const ParseContext &context) { Key out; for (pugi::xml_attribute a : el.attributes()) @@ -167,71 +168,71 @@ Key parseKey(pugi::xml_node el) } if (aname == "number") { - out.setNumber(StaffNumber::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseKeyContent(out, el); + parseKeyContent(out, el, context); return out; } -void parseKeyContent(Key &out, pugi::xml_node el) +void parseKeyContent(Key &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "cancel") || cursorIs(cursor, "fifths") || cursorIs(cursor, "key-step"))) { - out.setChoice(parseKeyChoice(el, cursor)); + out.setChoice(parseKeyChoice(el, cursor, context)); } while (cursorIs(cursor, "key-octave")) { - out.addKeyOctave(parseKeyOctave(cursor)); + out.addKeyOctave(parseKeyOctave(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Key.h b/src/private/mx/core/generated/Key.h index f1bda620d..40ec9f6ed 100644 --- a/src/private/mx/core/generated/Key.h +++ b/src/private/mx/core/generated/Key.h @@ -28,6 +28,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The key type represents a key signature. Both traditional and non-traditional key signatures are /// supported. The optional number attribute refers to staff numbers. If absent, the key signature /// applies to all staves in the part. Key signatures appear at the start of each system unless the @@ -85,9 +87,9 @@ class Key final std::vector m_keyOctave; }; -Key parseKey(pugi::xml_node el); +Key parseKey(pugi::xml_node el, const ParseContext &context); -void parseKeyContent(Key &out, pugi::xml_node el); +void parseKeyContent(Key &out, pugi::xml_node el, const ParseContext &context); void serializeKey(const Key &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/KeyAccidental.cpp b/src/private/mx/core/generated/KeyAccidental.cpp index 092bad9f9..0d7a228d5 100644 --- a/src/private/mx/core/generated/KeyAccidental.cpp +++ b/src/private/mx/core/generated/KeyAccidental.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/KeyAccidental.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void KeyAccidental::setValue(AccidentalValue value) m_value = std::move(value); } -KeyAccidental parseKeyAccidental(pugi::xml_node el) +KeyAccidental parseKeyAccidental(pugi::xml_node el, const ParseContext &context) { KeyAccidental out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ KeyAccidental parseKeyAccidental(pugi::xml_node el) } if (aname == "smufl") { - out.setSmufl(SmuflAccidentalGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else { throwUnknownAttribute(el, a.name()); } } - parseKeyAccidentalContent(out, el); + parseKeyAccidentalContent(out, el, context); return out; } -void parseKeyAccidentalContent(KeyAccidental &out, pugi::xml_node el) +void parseKeyAccidentalContent(KeyAccidental &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(AccidentalValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/KeyAccidental.h b/src/private/mx/core/generated/KeyAccidental.h index e36711d48..65837f9d0 100644 --- a/src/private/mx/core/generated/KeyAccidental.h +++ b/src/private/mx/core/generated/KeyAccidental.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The key-accidental type indicates the accidental to be displayed in a non-traditional key /// signature, represented in the same manner as the accidental type without the formatting /// attributes. @@ -33,9 +35,9 @@ class KeyAccidental final AccidentalValue m_value{}; }; -KeyAccidental parseKeyAccidental(pugi::xml_node el); +KeyAccidental parseKeyAccidental(pugi::xml_node el, const ParseContext &context); -void parseKeyAccidentalContent(KeyAccidental &out, pugi::xml_node el); +void parseKeyAccidentalContent(KeyAccidental &out, pugi::xml_node el, const ParseContext &context); void serializeKeyAccidental(const KeyAccidental &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/KeyChoice.cpp b/src/private/mx/core/generated/KeyChoice.cpp index ef89e7ac3..9c156c9ab 100644 --- a/src/private/mx/core/generated/KeyChoice.cpp +++ b/src/private/mx/core/generated/KeyChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/KeyChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,18 +26,18 @@ KeyChoice KeyChoice::nonTraditionalKey(std::vector value return KeyChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -KeyChoice parseKeyChoice(pugi::xml_node el, pugi::xml_node &cursor) +KeyChoice parseKeyChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "cancel") || cursorIs(cursor, "fifths"))) { - return KeyChoice::traditionalKey(parseTraditionalKeyGroup(el, cursor)); + return KeyChoice::traditionalKey(parseTraditionalKeyGroup(el, cursor, context)); } if (cursor && (cursorIs(cursor, "key-step"))) { std::vector items; while (cursor && (cursorIs(cursor, "key-step"))) { - items.push_back(parseNonTraditionalKeyGroup(el, cursor)); + items.push_back(parseNonTraditionalKeyGroup(el, cursor, context)); } return KeyChoice::nonTraditionalKey(std::move(items)); } diff --git a/src/private/mx/core/generated/KeyChoice.h b/src/private/mx/core/generated/KeyChoice.h index 0c970b174..9f9a63e71 100644 --- a/src/private/mx/core/generated/KeyChoice.h +++ b/src/private/mx/core/generated/KeyChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -81,7 +83,7 @@ class KeyChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -KeyChoice parseKeyChoice(pugi::xml_node el, pugi::xml_node &cursor); +KeyChoice parseKeyChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeKeyChoice(const KeyChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/KeyOctave.cpp b/src/private/mx/core/generated/KeyOctave.cpp index a9d6d4d59..9d66fbf59 100644 --- a/src/private/mx/core/generated/KeyOctave.cpp +++ b/src/private/mx/core/generated/KeyOctave.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/KeyOctave.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,7 +41,7 @@ void KeyOctave::setValue(Octave value) m_value = std::move(value); } -KeyOctave parseKeyOctave(pugi::xml_node el) +KeyOctave parseKeyOctave(pugi::xml_node el, const ParseContext &context) { KeyOctave out; bool seen_number = false; @@ -54,11 +55,11 @@ KeyOctave parseKeyOctave(pugi::xml_node el) if (aname == "number") { seen_number = true; - out.setNumber(parseInt(a.value())); + out.setNumber(parseIntegerValue(a.value(), context, el, "number")); } else if (aname == "cancel") { - out.setCancel(YesNo::parse(a.value())); + out.setCancel(parseValue(a.value(), context, el, "cancel")); } else { @@ -69,13 +70,13 @@ KeyOctave parseKeyOctave(pugi::xml_node el) { throwMissingAttribute(el, "number"); } - parseKeyOctaveContent(out, el); + parseKeyOctaveContent(out, el, context); return out; } -void parseKeyOctaveContent(KeyOctave &out, pugi::xml_node el) +void parseKeyOctaveContent(KeyOctave &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(Octave::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/KeyOctave.h b/src/private/mx/core/generated/KeyOctave.h index 830f6cb10..0c6f2d111 100644 --- a/src/private/mx/core/generated/KeyOctave.h +++ b/src/private/mx/core/generated/KeyOctave.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The key-octave type specifies in which octave an element of a key signature appears. The content /// specifies the octave value using the same values as the display-octave element. The number /// attribute is a positive integer that refers to the key signature element in left-to-right order. @@ -40,9 +42,9 @@ class KeyOctave final Octave m_value{}; }; -KeyOctave parseKeyOctave(pugi::xml_node el); +KeyOctave parseKeyOctave(pugi::xml_node el, const ParseContext &context); -void parseKeyOctaveContent(KeyOctave &out, pugi::xml_node el); +void parseKeyOctaveContent(KeyOctave &out, pugi::xml_node el, const ParseContext &context); void serializeKeyOctave(const KeyOctave &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Kind.cpp b/src/private/mx/core/generated/Kind.cpp index 17154de25..47e3eec07 100644 --- a/src/private/mx/core/generated/Kind.cpp +++ b/src/private/mx/core/generated/Kind.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Kind.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -180,7 +181,7 @@ void Kind::setValue(KindValue value) m_value = std::move(value); } -Kind parseKind(pugi::xml_node el) +Kind parseKind(pugi::xml_node el, const ParseContext &context) { Kind out; for (pugi::xml_attribute a : el.attributes()) @@ -192,7 +193,7 @@ Kind parseKind(pugi::xml_node el) } if (aname == "use-symbols") { - out.setUseSymbols(YesNo::parse(a.value())); + out.setUseSymbols(parseValue(a.value(), context, el, "use-symbols")); } else if (aname == "text") { @@ -200,72 +201,72 @@ Kind parseKind(pugi::xml_node el) } else if (aname == "stack-degrees") { - out.setStackDegrees(YesNo::parse(a.value())); + out.setStackDegrees(parseValue(a.value(), context, el, "stack-degrees")); } else if (aname == "parentheses-degrees") { - out.setParenthesesDegrees(YesNo::parse(a.value())); + out.setParenthesesDegrees(parseValue(a.value(), context, el, "parentheses-degrees")); } else if (aname == "bracket-degrees") { - out.setBracketDegrees(YesNo::parse(a.value())); + out.setBracketDegrees(parseValue(a.value(), context, el, "bracket-degrees")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else { throwUnknownAttribute(el, a.name()); } } - parseKindContent(out, el); + parseKindContent(out, el, context); return out; } -void parseKindContent(Kind &out, pugi::xml_node el) +void parseKindContent(Kind &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(KindValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Kind.h b/src/private/mx/core/generated/Kind.h index a9c18cf83..c598a03fc 100644 --- a/src/private/mx/core/generated/Kind.h +++ b/src/private/mx/core/generated/Kind.h @@ -24,6 +24,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Kind indicates the type of chord. Degree elements can then add, subtract, or alter from these /// starting points The attributes are used to indicate the formatting of the symbol. Since the kind /// element is the constant in all the harmony-chord groups that can make up a polychord, many @@ -102,9 +104,9 @@ class Kind final KindValue m_value{}; }; -Kind parseKind(pugi::xml_node el); +Kind parseKind(pugi::xml_node el, const ParseContext &context); -void parseKindContent(Kind &out, pugi::xml_node el); +void parseKindContent(Kind &out, pugi::xml_node el, const ParseContext &context); void serializeKind(const Kind &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/KindValue.cpp b/src/private/mx/core/generated/KindValue.cpp index a679ded82..6a7a83b7e 100644 --- a/src/private/mx/core/generated/KindValue.cpp +++ b/src/private/mx/core/generated/KindValue.cpp @@ -228,9 +228,15 @@ bool KindValue::tryParse(std::string_view text, KindValue &out) noexcept } KindValue KindValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +KindValue KindValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { KindValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/KindValue.h b/src/private/mx/core/generated/KindValue.h index b2fb72f81..33c53b844 100644 --- a/src/private/mx/core/generated/KindValue.h +++ b/src/private/mx/core/generated/KindValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -121,6 +123,9 @@ class KindValue final /// (the import leniency policy; never produces an invalid value). static KindValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static KindValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const KindValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/LayoutGroup.cpp b/src/private/mx/core/generated/LayoutGroup.cpp index e2d9faf62..b230ed9b1 100644 --- a/src/private/mx/core/generated/LayoutGroup.cpp +++ b/src/private/mx/core/generated/LayoutGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/LayoutGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -45,22 +46,22 @@ void LayoutGroup::setStaffLayout(std::vector value) m_staffLayout = std::move(value); } -LayoutGroup parseLayoutGroup(pugi::xml_node el, pugi::xml_node &cursor) +LayoutGroup parseLayoutGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { LayoutGroup out; if (cursorIs(cursor, "page-layout")) { - out.setPageLayout(parsePageLayout(cursor)); + out.setPageLayout(parsePageLayout(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "system-layout")) { - out.setSystemLayout(parseSystemLayout(cursor)); + out.setSystemLayout(parseSystemLayout(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "staff-layout")) { - out.addStaffLayout(parseStaffLayout(cursor)); + out.addStaffLayout(parseStaffLayout(cursor, context)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/LayoutGroup.h b/src/private/mx/core/generated/LayoutGroup.h index 7b9a67b8d..f616dbbac 100644 --- a/src/private/mx/core/generated/LayoutGroup.h +++ b/src/private/mx/core/generated/LayoutGroup.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The layout group specifies the sequence of page, system, and staff layout elements that is common /// to both the defaults and print elements. /// A shared content group: transparent on the wire, its @@ -43,7 +45,7 @@ class LayoutGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -LayoutGroup parseLayoutGroup(pugi::xml_node el, pugi::xml_node &cursor); +LayoutGroup parseLayoutGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeLayoutGroup(const LayoutGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/LeftCenterRight.cpp b/src/private/mx/core/generated/LeftCenterRight.cpp index b171598e7..ce877b777 100644 --- a/src/private/mx/core/generated/LeftCenterRight.cpp +++ b/src/private/mx/core/generated/LeftCenterRight.cpp @@ -48,9 +48,15 @@ bool LeftCenterRight::tryParse(std::string_view text, LeftCenterRight &out) noex } LeftCenterRight LeftCenterRight::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +LeftCenterRight LeftCenterRight::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { LeftCenterRight v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/LeftCenterRight.h b/src/private/mx/core/generated/LeftCenterRight.h index 6e18240d6..663ef67d2 100644 --- a/src/private/mx/core/generated/LeftCenterRight.h +++ b/src/private/mx/core/generated/LeftCenterRight.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -42,6 +44,9 @@ class LeftCenterRight final /// (the import leniency policy; never produces an invalid value). static LeftCenterRight parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static LeftCenterRight parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const LeftCenterRight &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/LeftRight.cpp b/src/private/mx/core/generated/LeftRight.cpp index 341a3c5db..1f361c57b 100644 --- a/src/private/mx/core/generated/LeftRight.cpp +++ b/src/private/mx/core/generated/LeftRight.cpp @@ -42,9 +42,15 @@ bool LeftRight::tryParse(std::string_view text, LeftRight &out) noexcept } LeftRight LeftRight::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +LeftRight LeftRight::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { LeftRight v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/LeftRight.h b/src/private/mx/core/generated/LeftRight.h index 0826331fe..4a672722d 100644 --- a/src/private/mx/core/generated/LeftRight.h +++ b/src/private/mx/core/generated/LeftRight.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -41,6 +43,9 @@ class LeftRight final /// (the import leniency policy; never produces an invalid value). static LeftRight parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static LeftRight parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const LeftRight &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/LeftRightMarginsGroup.cpp b/src/private/mx/core/generated/LeftRightMarginsGroup.cpp index 1d6881a4f..dac357df2 100644 --- a/src/private/mx/core/generated/LeftRightMarginsGroup.cpp +++ b/src/private/mx/core/generated/LeftRightMarginsGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/LeftRightMarginsGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,12 +31,12 @@ void LeftRightMarginsGroup::setRightMargin(Tenths value) m_rightMargin = std::move(value); } -LeftRightMarginsGroup parseLeftRightMarginsGroup(pugi::xml_node el, pugi::xml_node &cursor) +LeftRightMarginsGroup parseLeftRightMarginsGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { LeftRightMarginsGroup out; if (cursorIs(cursor, "left-margin")) { - out.setLeftMargin(Tenths::parse(childText(cursor))); + out.setLeftMargin(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -44,7 +45,7 @@ LeftRightMarginsGroup parseLeftRightMarginsGroup(pugi::xml_node el, pugi::xml_no } if (cursorIs(cursor, "right-margin")) { - out.setRightMargin(Tenths::parse(childText(cursor))); + out.setRightMargin(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/LeftRightMarginsGroup.h b/src/private/mx/core/generated/LeftRightMarginsGroup.h index 16b6197f2..63a222b95 100644 --- a/src/private/mx/core/generated/LeftRightMarginsGroup.h +++ b/src/private/mx/core/generated/LeftRightMarginsGroup.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The left-right-margins group specifies horizontal margins in tenths. /// A shared content group: transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -36,7 +38,8 @@ class LeftRightMarginsGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -LeftRightMarginsGroup parseLeftRightMarginsGroup(pugi::xml_node el, pugi::xml_node &cursor); +LeftRightMarginsGroup parseLeftRightMarginsGroup(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context); void serializeLeftRightMarginsGroup(const LeftRightMarginsGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Level.cpp b/src/private/mx/core/generated/Level.cpp index 2e868fe24..f60ffcd6d 100644 --- a/src/private/mx/core/generated/Level.cpp +++ b/src/private/mx/core/generated/Level.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Level.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -70,7 +71,7 @@ void Level::setValue(std::string value) m_value = std::move(value); } -Level parseLevel(pugi::xml_node el) +Level parseLevel(pugi::xml_node el, const ParseContext &context) { Level out; for (pugi::xml_attribute a : el.attributes()) @@ -82,34 +83,34 @@ Level parseLevel(pugi::xml_node el) } if (aname == "reference") { - out.setReference(YesNo::parse(a.value())); + out.setReference(parseValue(a.value(), context, el, "reference")); } else if (aname == "type") { - out.setType(StartStopSingle::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "parentheses") { - out.setParentheses(YesNo::parse(a.value())); + out.setParentheses(parseValue(a.value(), context, el, "parentheses")); } else if (aname == "bracket") { - out.setBracket(YesNo::parse(a.value())); + out.setBracket(parseValue(a.value(), context, el, "bracket")); } else if (aname == "size") { - out.setSize(SymbolSize::parse(a.value())); + out.setSize(parseValue(a.value(), context, el, "size")); } else { throwUnknownAttribute(el, a.name()); } } - parseLevelContent(out, el); + parseLevelContent(out, el, context); return out; } -void parseLevelContent(Level &out, pugi::xml_node el) +void parseLevelContent(Level &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/Level.h b/src/private/mx/core/generated/Level.h index 3cf4c9090..977a3e8a3 100644 --- a/src/private/mx/core/generated/Level.h +++ b/src/private/mx/core/generated/Level.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The level type is used to specify editorial information for different MusicXML elements. The /// content contains identifying and/or descriptive text about the editorial status of the parent /// element. If the reference attribute is yes, this indicates editorial information that is for @@ -51,9 +53,9 @@ class Level final std::string m_value{}; }; -Level parseLevel(pugi::xml_node el); +Level parseLevel(pugi::xml_node el, const ParseContext &context); -void parseLevelContent(Level &out, pugi::xml_node el); +void parseLevelContent(Level &out, pugi::xml_node el, const ParseContext &context); void serializeLevel(const Level &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/LineDetail.cpp b/src/private/mx/core/generated/LineDetail.cpp index 5f55796a2..bdc625512 100644 --- a/src/private/mx/core/generated/LineDetail.cpp +++ b/src/private/mx/core/generated/LineDetail.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/LineDetail.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -60,7 +61,7 @@ void LineDetail::setPrintObject(std::optional value) m_printObject = std::move(value); } -LineDetail parseLineDetail(pugi::xml_node el) +LineDetail parseLineDetail(pugi::xml_node el, const ParseContext &context) { LineDetail out; bool seen_line = false; @@ -74,23 +75,23 @@ LineDetail parseLineDetail(pugi::xml_node el) if (aname == "line") { seen_line = true; - out.setLine(StaffLine::parse(a.value())); + out.setLine(parseValue(a.value(), context, el, "line")); } else if (aname == "width") { - out.setWidth(Tenths::parse(a.value())); + out.setWidth(parseValue(a.value(), context, el, "width")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "line-type") { - out.setLineType(LineType::parse(a.value())); + out.setLineType(parseValue(a.value(), context, el, "line-type")); } else if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else { @@ -101,13 +102,14 @@ LineDetail parseLineDetail(pugi::xml_node el) { throwMissingAttribute(el, "line"); } - parseLineDetailContent(out, el); + parseLineDetailContent(out, el, context); return out; } -void parseLineDetailContent(LineDetail &out, pugi::xml_node el) +void parseLineDetailContent(LineDetail &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/LineDetail.h b/src/private/mx/core/generated/LineDetail.h index de27d3105..661b5fafb 100644 --- a/src/private/mx/core/generated/LineDetail.h +++ b/src/private/mx/core/generated/LineDetail.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// If the staff-lines element is present, the appearance of each line may be individually specified /// with a line-detail type. Staff lines are numbered from bottom to top. The print-object attribute /// allows lines to be hidden within a staff. This is used in special situations such as a @@ -49,9 +51,9 @@ class LineDetail final std::optional m_printObject; }; -LineDetail parseLineDetail(pugi::xml_node el); +LineDetail parseLineDetail(pugi::xml_node el, const ParseContext &context); -void parseLineDetailContent(LineDetail &out, pugi::xml_node el); +void parseLineDetailContent(LineDetail &out, pugi::xml_node el, const ParseContext &context); void serializeLineDetail(const LineDetail &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/LineEnd.cpp b/src/private/mx/core/generated/LineEnd.cpp index 89a16b59b..078c87467 100644 --- a/src/private/mx/core/generated/LineEnd.cpp +++ b/src/private/mx/core/generated/LineEnd.cpp @@ -56,9 +56,15 @@ bool LineEnd::tryParse(std::string_view text, LineEnd &out) noexcept } LineEnd LineEnd::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +LineEnd LineEnd::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { LineEnd v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/LineEnd.h b/src/private/mx/core/generated/LineEnd.h index a383333ed..a66a95d0d 100644 --- a/src/private/mx/core/generated/LineEnd.h +++ b/src/private/mx/core/generated/LineEnd.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -47,6 +49,9 @@ class LineEnd final /// (the import leniency policy; never produces an invalid value). static LineEnd parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static LineEnd parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const LineEnd &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/LineLength.cpp b/src/private/mx/core/generated/LineLength.cpp index 3367d1f0b..e691b1dde 100644 --- a/src/private/mx/core/generated/LineLength.cpp +++ b/src/private/mx/core/generated/LineLength.cpp @@ -48,9 +48,15 @@ bool LineLength::tryParse(std::string_view text, LineLength &out) noexcept } LineLength LineLength::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +LineLength LineLength::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { LineLength v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/LineLength.h b/src/private/mx/core/generated/LineLength.h index 479e090f1..da2e7050c 100644 --- a/src/private/mx/core/generated/LineLength.h +++ b/src/private/mx/core/generated/LineLength.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -43,6 +45,9 @@ class LineLength final /// (the import leniency policy; never produces an invalid value). static LineLength parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static LineLength parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const LineLength &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/LineShape.cpp b/src/private/mx/core/generated/LineShape.cpp index 9b202405b..80844191e 100644 --- a/src/private/mx/core/generated/LineShape.cpp +++ b/src/private/mx/core/generated/LineShape.cpp @@ -42,9 +42,15 @@ bool LineShape::tryParse(std::string_view text, LineShape &out) noexcept } LineShape LineShape::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +LineShape LineShape::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { LineShape v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/LineShape.h b/src/private/mx/core/generated/LineShape.h index c87596ae2..7f262468d 100644 --- a/src/private/mx/core/generated/LineShape.h +++ b/src/private/mx/core/generated/LineShape.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -40,6 +42,9 @@ class LineShape final /// (the import leniency policy; never produces an invalid value). static LineShape parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static LineShape parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const LineShape &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/LineType.cpp b/src/private/mx/core/generated/LineType.cpp index cd878b70b..cbc6e2a68 100644 --- a/src/private/mx/core/generated/LineType.cpp +++ b/src/private/mx/core/generated/LineType.cpp @@ -54,9 +54,15 @@ bool LineType::tryParse(std::string_view text, LineType &out) noexcept } LineType LineType::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +LineType LineType::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { LineType v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/LineType.h b/src/private/mx/core/generated/LineType.h index b26bd7b6f..bc6687f30 100644 --- a/src/private/mx/core/generated/LineType.h +++ b/src/private/mx/core/generated/LineType.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -44,6 +46,9 @@ class LineType final /// (the import leniency policy; never produces an invalid value). static LineType parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static LineType parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const LineType &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/LineWidth.cpp b/src/private/mx/core/generated/LineWidth.cpp index 372546138..0f7f2a09d 100644 --- a/src/private/mx/core/generated/LineWidth.cpp +++ b/src/private/mx/core/generated/LineWidth.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/LineWidth.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void LineWidth::setValue(Tenths value) m_value = std::move(value); } -LineWidth parseLineWidth(pugi::xml_node el) +LineWidth parseLineWidth(pugi::xml_node el, const ParseContext &context) { LineWidth out; bool seen_type = false; @@ -44,7 +45,7 @@ LineWidth parseLineWidth(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(LineWidthType::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else { @@ -55,13 +56,13 @@ LineWidth parseLineWidth(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseLineWidthContent(out, el); + parseLineWidthContent(out, el, context); return out; } -void parseLineWidthContent(LineWidth &out, pugi::xml_node el) +void parseLineWidthContent(LineWidth &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(Tenths::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/LineWidth.h b/src/private/mx/core/generated/LineWidth.h index 8a3ee1157..c8a6cb616 100644 --- a/src/private/mx/core/generated/LineWidth.h +++ b/src/private/mx/core/generated/LineWidth.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The line-width type indicates the width of a line type in tenths. The type attribute defines what /// type of line is being defined. Values include beam, bracket, dashes, enclosure, ending, extend, /// heavy barline, leger, light barline, octave shift, pedal, slur middle, slur tip, staff, stem, tie @@ -34,9 +36,9 @@ class LineWidth final Tenths m_value{}; }; -LineWidth parseLineWidth(pugi::xml_node el); +LineWidth parseLineWidth(pugi::xml_node el, const ParseContext &context); -void parseLineWidthContent(LineWidth &out, pugi::xml_node el); +void parseLineWidthContent(LineWidth &out, pugi::xml_node el, const ParseContext &context); void serializeLineWidth(const LineWidth &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/LineWidthType.cpp b/src/private/mx/core/generated/LineWidthType.cpp index 3804343ae..4f31ab1cd 100644 --- a/src/private/mx/core/generated/LineWidthType.cpp +++ b/src/private/mx/core/generated/LineWidthType.cpp @@ -32,4 +32,11 @@ LineWidthType LineWidthType::parse(std::string_view text) return LineWidthType{std::string{text}}; } +LineWidthType LineWidthType::parse(std::string_view text, ValueParseOutcome &outcome) +{ + LineWidthType out{std::string{text}}; + outcome = out.value() == text ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/LineWidthType.h b/src/private/mx/core/generated/LineWidthType.h index 13cb46ba1..06784226f 100644 --- a/src/private/mx/core/generated/LineWidthType.h +++ b/src/private/mx/core/generated/LineWidthType.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -38,6 +40,9 @@ class LineWidthType final static LineWidthType parse(std::string_view text); + /// Says whether the text had to be repaired. + static LineWidthType parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const LineWidthType &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Link.cpp b/src/private/mx/core/generated/Link.cpp index defca5d1a..f3788a04c 100644 --- a/src/private/mx/core/generated/Link.cpp +++ b/src/private/mx/core/generated/Link.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Link.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -140,7 +141,7 @@ void Link::setRelativeY(std::optional value) m_relativeY = std::move(value); } -Link parseLink(pugi::xml_node el) +Link parseLink(pugi::xml_node el, const ParseContext &context) { Link out; bool seen_xlinkHref = false; @@ -182,27 +183,27 @@ Link parseLink(pugi::xml_node el) } else if (aname == "element") { - out.setElement(NameToken::parse(a.value())); + out.setElement(parseValue(a.value(), context, el, "element")); } else if (aname == "position") { - out.setPosition(parseInt(a.value())); + out.setPosition(parseIntegerValue(a.value(), context, el, "position")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else { @@ -213,13 +214,14 @@ Link parseLink(pugi::xml_node el) { throwMissingAttribute(el, "xlink:href"); } - parseLinkContent(out, el); + parseLinkContent(out, el, context); return out; } -void parseLinkContent(Link &out, pugi::xml_node el) +void parseLinkContent(Link &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Link.h b/src/private/mx/core/generated/Link.h index 5adf7f6ca..94db05a76 100644 --- a/src/private/mx/core/generated/Link.h +++ b/src/private/mx/core/generated/Link.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The link type serves as an outgoing simple XLink. If a relative link is used within a document /// that is part of a compressed MusicXML file, the link is relative to the root folder of the zip /// file. @@ -67,9 +69,9 @@ class Link final std::optional m_relativeY; }; -Link parseLink(pugi::xml_node el); +Link parseLink(pugi::xml_node el, const ParseContext &context); -void parseLinkContent(Link &out, pugi::xml_node el); +void parseLinkContent(Link &out, pugi::xml_node el, const ParseContext &context); void serializeLink(const Link &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Listen.cpp b/src/private/mx/core/generated/Listen.cpp index d56bcffd5..53dc307e5 100644 --- a/src/private/mx/core/generated/Listen.cpp +++ b/src/private/mx/core/generated/Listen.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Listen.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,7 +26,7 @@ void Listen::setChoice(OneOrMore value) m_choice = std::move(value); } -Listen parseListen(pugi::xml_node el) +Listen parseListen(pugi::xml_node el, const ParseContext &context) { Listen out; for (pugi::xml_attribute a : el.attributes()) @@ -37,21 +38,21 @@ Listen parseListen(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseListenContent(out, el); + parseListenContent(out, el, context); return out; } -void parseListenContent(Listen &out, pugi::xml_node el) +void parseListenContent(Listen &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (!(cursor && (cursorIs(cursor, "assess") || cursorIs(cursor, "wait") || cursorIs(cursor, "other-listen")))) { throwMissingElement(el, "assess"); } - out.setChoice(OneOrMore{parseListenChoice(el, cursor)}); + out.setChoice(OneOrMore{parseListenChoice(el, cursor, context)}); while (cursor && (cursorIs(cursor, "assess") || cursorIs(cursor, "wait") || cursorIs(cursor, "other-listen"))) { - out.addChoice(parseListenChoice(el, cursor)); + out.addChoice(parseListenChoice(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/Listen.h b/src/private/mx/core/generated/Listen.h index c390b3c6b..8fba504c4 100644 --- a/src/private/mx/core/generated/Listen.h +++ b/src/private/mx/core/generated/Listen.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The listen and listening types, new in Version 4.0, specify different ways that a score following /// or machine listening application can interact with a performer. The listen type handles /// interactions that are specific to a note. If multiple child elements of the same type are @@ -36,9 +38,9 @@ class Listen final OneOrMore m_choice; }; -Listen parseListen(pugi::xml_node el); +Listen parseListen(pugi::xml_node el, const ParseContext &context); -void parseListenContent(Listen &out, pugi::xml_node el); +void parseListenContent(Listen &out, pugi::xml_node el, const ParseContext &context); void serializeListen(const Listen &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/ListenChoice.cpp b/src/private/mx/core/generated/ListenChoice.cpp index 266555008..8b35c1288 100644 --- a/src/private/mx/core/generated/ListenChoice.cpp +++ b/src/private/mx/core/generated/ListenChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/ListenChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,23 +31,23 @@ ListenChoice ListenChoice::otherListen(OtherListening value) return ListenChoice{Storage{std::in_place_index<2>, std::move(value)}}; } -ListenChoice parseListenChoice(pugi::xml_node el, pugi::xml_node &cursor) +ListenChoice parseListenChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "assess"))) { - Assess value = parseAssess(cursor); + Assess value = parseAssess(cursor, context); cursor = nextElement(cursor); return ListenChoice::assess(std::move(value)); } if (cursor && (cursorIs(cursor, "wait"))) { - Wait value = parseWait(cursor); + Wait value = parseWait(cursor, context); cursor = nextElement(cursor); return ListenChoice::wait(std::move(value)); } if (cursor && (cursorIs(cursor, "other-listen"))) { - OtherListening value = parseOtherListening(cursor); + OtherListening value = parseOtherListening(cursor, context); cursor = nextElement(cursor); return ListenChoice::otherListen(std::move(value)); } diff --git a/src/private/mx/core/generated/ListenChoice.h b/src/private/mx/core/generated/ListenChoice.h index ba51d47e0..2e7e87cf6 100644 --- a/src/private/mx/core/generated/ListenChoice.h +++ b/src/private/mx/core/generated/ListenChoice.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -95,7 +97,7 @@ class ListenChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -ListenChoice parseListenChoice(pugi::xml_node el, pugi::xml_node &cursor); +ListenChoice parseListenChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeListenChoice(const ListenChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Listening.cpp b/src/private/mx/core/generated/Listening.cpp index 8703b1a2d..979a570c7 100644 --- a/src/private/mx/core/generated/Listening.cpp +++ b/src/private/mx/core/generated/Listening.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Listening.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,7 +36,7 @@ void Listening::setOffset(std::optional value) m_offset = std::move(value); } -Listening parseListening(pugi::xml_node el) +Listening parseListening(pugi::xml_node el, const ParseContext &context) { Listening out; for (pugi::xml_attribute a : el.attributes()) @@ -47,25 +48,25 @@ Listening parseListening(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseListeningContent(out, el); + parseListeningContent(out, el, context); return out; } -void parseListeningContent(Listening &out, pugi::xml_node el) +void parseListeningContent(Listening &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (!(cursor && (cursorIs(cursor, "sync") || cursorIs(cursor, "other-listening")))) { throwMissingElement(el, "sync"); } - out.setChoice(OneOrMore{parseListeningChoice(el, cursor)}); + out.setChoice(OneOrMore{parseListeningChoice(el, cursor, context)}); while (cursor && (cursorIs(cursor, "sync") || cursorIs(cursor, "other-listening"))) { - out.addChoice(parseListeningChoice(el, cursor)); + out.addChoice(parseListeningChoice(el, cursor, context)); } if (cursorIs(cursor, "offset")) { - out.setOffset(parseOffset(cursor)); + out.setOffset(parseOffset(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Listening.h b/src/private/mx/core/generated/Listening.h index 5043d007b..422038b54 100644 --- a/src/private/mx/core/generated/Listening.h +++ b/src/private/mx/core/generated/Listening.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The listen and listening types, new in Version 4.0, specify different ways that a score following /// or machine listening application can interact with a performer. The listening type handles /// interactions that change the state of the listening application from the specified point in the @@ -46,9 +48,9 @@ class Listening final std::optional m_offset; }; -Listening parseListening(pugi::xml_node el); +Listening parseListening(pugi::xml_node el, const ParseContext &context); -void parseListeningContent(Listening &out, pugi::xml_node el); +void parseListeningContent(Listening &out, pugi::xml_node el, const ParseContext &context); void serializeListening(const Listening &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/ListeningChoice.cpp b/src/private/mx/core/generated/ListeningChoice.cpp index 48c80e406..c0f126136 100644 --- a/src/private/mx/core/generated/ListeningChoice.cpp +++ b/src/private/mx/core/generated/ListeningChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/ListeningChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,17 +26,17 @@ ListeningChoice ListeningChoice::otherListening(OtherListening value) return ListeningChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -ListeningChoice parseListeningChoice(pugi::xml_node el, pugi::xml_node &cursor) +ListeningChoice parseListeningChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "sync"))) { - Sync value = parseSync(cursor); + Sync value = parseSync(cursor, context); cursor = nextElement(cursor); return ListeningChoice::sync(std::move(value)); } if (cursor && (cursorIs(cursor, "other-listening"))) { - OtherListening value = parseOtherListening(cursor); + OtherListening value = parseOtherListening(cursor, context); cursor = nextElement(cursor); return ListeningChoice::otherListening(std::move(value)); } diff --git a/src/private/mx/core/generated/ListeningChoice.h b/src/private/mx/core/generated/ListeningChoice.h index ec68b2db6..683a20d72 100644 --- a/src/private/mx/core/generated/ListeningChoice.h +++ b/src/private/mx/core/generated/ListeningChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -80,7 +82,7 @@ class ListeningChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -ListeningChoice parseListeningChoice(pugi::xml_node el, pugi::xml_node &cursor); +ListeningChoice parseListeningChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeListeningChoice(const ListeningChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Lyric.cpp b/src/private/mx/core/generated/Lyric.cpp index 5eabb022d..774bfc9cf 100644 --- a/src/private/mx/core/generated/Lyric.cpp +++ b/src/private/mx/core/generated/Lyric.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Lyric.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -170,7 +171,7 @@ void Lyric::setEditorial(EditorialGroup value) m_editorial = std::move(value); } -Lyric parseLyric(pugi::xml_node el) +Lyric parseLyric(pugi::xml_node el, const ParseContext &context) { Lyric out; for (pugi::xml_attribute a : el.attributes()) @@ -182,7 +183,7 @@ Lyric parseLyric(pugi::xml_node el) } if (aname == "number") { - out.setNumber(NameToken::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "name") { @@ -190,60 +191,60 @@ Lyric parseLyric(pugi::xml_node el) } else if (aname == "time-only") { - out.setTimeOnly(TimeOnly::parse(a.value())); + out.setTimeOnly(parseValue(a.value(), context, el, "time-only")); } else if (aname == "justify") { - out.setJustify(LeftCenterRight::parse(a.value())); + out.setJustify(parseValue(a.value(), context, el, "justify")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseLyricContent(out, el); + parseLyricContent(out, el, context); return out; } -void parseLyricContent(Lyric &out, pugi::xml_node el) +void parseLyricContent(Lyric &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "syllabic") || cursorIs(cursor, "text") || cursorIs(cursor, "extend") || cursorIs(cursor, "laughing") || cursorIs(cursor, "humming"))) { - out.setChoice(parseLyricChoice(el, cursor)); + out.setChoice(parseLyricChoice(el, cursor, context)); } else { @@ -251,19 +252,19 @@ void parseLyricContent(Lyric &out, pugi::xml_node el) } if (cursorIs(cursor, "end-line")) { - parseEmpty(cursor); + parseEmpty(cursor, context); out.setEndLine(true); cursor = nextElement(cursor); } if (cursorIs(cursor, "end-paragraph")) { - parseEmpty(cursor); + parseEmpty(cursor, context); out.setEndParagraph(true); cursor = nextElement(cursor); } if (cursor && (cursorIs(cursor, "footnote") || cursorIs(cursor, "level"))) { - out.setEditorial(parseEditorialGroup(el, cursor)); + out.setEditorial(parseEditorialGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/Lyric.h b/src/private/mx/core/generated/Lyric.h index 7d9b00c00..5d1716c85 100644 --- a/src/private/mx/core/generated/Lyric.h +++ b/src/private/mx/core/generated/Lyric.h @@ -27,6 +27,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The lyric type represents text underlays for lyrics. Two text elements that are not separated by /// an elision element are part of the same syllable, but may have different text formatting. The /// MusicXML XSD is more strict than the DTD in enforcing this by disallowing a second syllabic @@ -95,9 +97,9 @@ class Lyric final EditorialGroup m_editorial{}; }; -Lyric parseLyric(pugi::xml_node el); +Lyric parseLyric(pugi::xml_node el, const ParseContext &context); -void parseLyricContent(Lyric &out, pugi::xml_node el); +void parseLyricContent(Lyric &out, pugi::xml_node el, const ParseContext &context); void serializeLyric(const Lyric &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/LyricChoice.cpp b/src/private/mx/core/generated/LyricChoice.cpp index f80d6db44..28a7eb120 100644 --- a/src/private/mx/core/generated/LyricChoice.cpp +++ b/src/private/mx/core/generated/LyricChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/LyricChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,27 +36,27 @@ LyricChoice LyricChoice::humming(Empty value) return LyricChoice{Storage{std::in_place_index<3>, std::move(value)}}; } -LyricChoice parseLyricChoice(pugi::xml_node el, pugi::xml_node &cursor) +LyricChoice parseLyricChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "syllabic") || cursorIs(cursor, "text"))) { - return LyricChoice::lyricTextGroup(parseLyricTextGroup(el, cursor)); + return LyricChoice::lyricTextGroup(parseLyricTextGroup(el, cursor, context)); } if (cursor && (cursorIs(cursor, "extend"))) { - Extend value = parseExtend(cursor); + Extend value = parseExtend(cursor, context); cursor = nextElement(cursor); return LyricChoice::extend(std::move(value)); } if (cursor && (cursorIs(cursor, "laughing"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return LyricChoice::laughing(std::move(value)); } if (cursor && (cursorIs(cursor, "humming"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return LyricChoice::humming(std::move(value)); } diff --git a/src/private/mx/core/generated/LyricChoice.h b/src/private/mx/core/generated/LyricChoice.h index 4d1480f01..03f25c7c7 100644 --- a/src/private/mx/core/generated/LyricChoice.h +++ b/src/private/mx/core/generated/LyricChoice.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -109,7 +111,7 @@ class LyricChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -LyricChoice parseLyricChoice(pugi::xml_node el, pugi::xml_node &cursor); +LyricChoice parseLyricChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeLyricChoice(const LyricChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/LyricFont.cpp b/src/private/mx/core/generated/LyricFont.cpp index 407391eb7..29bb74a0d 100644 --- a/src/private/mx/core/generated/LyricFont.cpp +++ b/src/private/mx/core/generated/LyricFont.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/LyricFont.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -70,7 +71,7 @@ void LyricFont::setFontWeight(std::optional value) m_fontWeight = std::move(value); } -LyricFont parseLyricFont(pugi::xml_node el) +LyricFont parseLyricFont(pugi::xml_node el, const ParseContext &context) { LyricFont out; for (pugi::xml_attribute a : el.attributes()) @@ -82,7 +83,7 @@ LyricFont parseLyricFont(pugi::xml_node el) } if (aname == "number") { - out.setNumber(NameToken::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "name") { @@ -90,32 +91,33 @@ LyricFont parseLyricFont(pugi::xml_node el) } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else { throwUnknownAttribute(el, a.name()); } } - parseLyricFontContent(out, el); + parseLyricFontContent(out, el, context); return out; } -void parseLyricFontContent(LyricFont &out, pugi::xml_node el) +void parseLyricFontContent(LyricFont &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/LyricFont.h b/src/private/mx/core/generated/LyricFont.h index efc6f6969..800226f79 100644 --- a/src/private/mx/core/generated/LyricFont.h +++ b/src/private/mx/core/generated/LyricFont.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The lyric-font type specifies the default font for a particular name and number of lyric. class LyricFont final { @@ -47,9 +49,9 @@ class LyricFont final std::optional m_fontWeight; }; -LyricFont parseLyricFont(pugi::xml_node el); +LyricFont parseLyricFont(pugi::xml_node el, const ParseContext &context); -void parseLyricFontContent(LyricFont &out, pugi::xml_node el); +void parseLyricFontContent(LyricFont &out, pugi::xml_node el, const ParseContext &context); void serializeLyricFont(const LyricFont &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/LyricLanguage.cpp b/src/private/mx/core/generated/LyricLanguage.cpp index be8169849..1707a5131 100644 --- a/src/private/mx/core/generated/LyricLanguage.cpp +++ b/src/private/mx/core/generated/LyricLanguage.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/LyricLanguage.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,7 +41,7 @@ void LyricLanguage::setXMLLang(std::string value) m_xmlLang = std::move(value); } -LyricLanguage parseLyricLanguage(pugi::xml_node el) +LyricLanguage parseLyricLanguage(pugi::xml_node el, const ParseContext &context) { LyricLanguage out; bool seen_xmlLang = false; @@ -53,7 +54,7 @@ LyricLanguage parseLyricLanguage(pugi::xml_node el) } if (aname == "number") { - out.setNumber(NameToken::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "name") { @@ -73,14 +74,16 @@ LyricLanguage parseLyricLanguage(pugi::xml_node el) { // Configured import repair (plan §2.4): inject the default. out.setXMLLang(std::string("und")); + reportAttributeDefaulted(context, el, "xml:lang", "und"); } - parseLyricLanguageContent(out, el); + parseLyricLanguageContent(out, el, context); return out; } -void parseLyricLanguageContent(LyricLanguage &out, pugi::xml_node el) +void parseLyricLanguageContent(LyricLanguage &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/LyricLanguage.h b/src/private/mx/core/generated/LyricLanguage.h index d6330379a..1fd1f968d 100644 --- a/src/private/mx/core/generated/LyricLanguage.h +++ b/src/private/mx/core/generated/LyricLanguage.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The lyric-language type specifies the default language for a particular name and number of lyric. class LyricLanguage final { @@ -34,9 +36,9 @@ class LyricLanguage final std::string m_xmlLang{}; }; -LyricLanguage parseLyricLanguage(pugi::xml_node el); +LyricLanguage parseLyricLanguage(pugi::xml_node el, const ParseContext &context); -void parseLyricLanguageContent(LyricLanguage &out, pugi::xml_node el); +void parseLyricLanguageContent(LyricLanguage &out, pugi::xml_node el, const ParseContext &context); void serializeLyricLanguage(const LyricLanguage &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/LyricSyllableGroup.cpp b/src/private/mx/core/generated/LyricSyllableGroup.cpp index 28ee6a5ae..cadaeabbc 100644 --- a/src/private/mx/core/generated/LyricSyllableGroup.cpp +++ b/src/private/mx/core/generated/LyricSyllableGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/LyricSyllableGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,16 +31,16 @@ void LyricSyllableGroup::setText(TextElementData value) m_text = std::move(value); } -LyricSyllableGroup parseLyricSyllableGroup(pugi::xml_node el, pugi::xml_node &cursor) +LyricSyllableGroup parseLyricSyllableGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { LyricSyllableGroup out; if (cursor && (cursorIs(cursor, "elision"))) { - out.setElisionSyllabicGroup(parseElisionSyllabicGroup(el, cursor)); + out.setElisionSyllabicGroup(parseElisionSyllabicGroup(el, cursor, context)); } if (cursorIs(cursor, "text")) { - out.setText(parseTextElementData(cursor)); + out.setText(parseTextElementData(cursor, context)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/LyricSyllableGroup.h b/src/private/mx/core/generated/LyricSyllableGroup.h index 2f758b90d..24985c980 100644 --- a/src/private/mx/core/generated/LyricSyllableGroup.h +++ b/src/private/mx/core/generated/LyricSyllableGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -37,7 +39,7 @@ class LyricSyllableGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -LyricSyllableGroup parseLyricSyllableGroup(pugi::xml_node el, pugi::xml_node &cursor); +LyricSyllableGroup parseLyricSyllableGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeLyricSyllableGroup(const LyricSyllableGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/LyricTextGroup.cpp b/src/private/mx/core/generated/LyricTextGroup.cpp index 02a5b736d..246df6377 100644 --- a/src/private/mx/core/generated/LyricTextGroup.cpp +++ b/src/private/mx/core/generated/LyricTextGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/LyricTextGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -55,17 +56,17 @@ void LyricTextGroup::setExtend(std::optional value) m_extend = std::move(value); } -LyricTextGroup parseLyricTextGroup(pugi::xml_node el, pugi::xml_node &cursor) +LyricTextGroup parseLyricTextGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { LyricTextGroup out; if (cursorIs(cursor, "syllabic")) { - out.setSyllabic(Syllabic::parse(childText(cursor))); + out.setSyllabic(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "text")) { - out.setText(parseTextElementData(cursor)); + out.setText(parseTextElementData(cursor, context)); cursor = nextElement(cursor); } else @@ -74,11 +75,11 @@ LyricTextGroup parseLyricTextGroup(pugi::xml_node el, pugi::xml_node &cursor) } while (cursor && (cursorIs(cursor, "elision") || cursorIs(cursor, "text"))) { - out.addLyricSyllableGroup(parseLyricSyllableGroup(el, cursor)); + out.addLyricSyllableGroup(parseLyricSyllableGroup(el, cursor, context)); } if (cursorIs(cursor, "extend")) { - out.setExtend(parseExtend(cursor)); + out.setExtend(parseExtend(cursor, context)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/LyricTextGroup.h b/src/private/mx/core/generated/LyricTextGroup.h index 6655343c6..f8a8b8b70 100644 --- a/src/private/mx/core/generated/LyricTextGroup.h +++ b/src/private/mx/core/generated/LyricTextGroup.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -46,7 +48,7 @@ class LyricTextGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -LyricTextGroup parseLyricTextGroup(pugi::xml_node el, pugi::xml_node &cursor); +LyricTextGroup parseLyricTextGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeLyricTextGroup(const LyricTextGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/MIDI128.cpp b/src/private/mx/core/generated/MIDI128.cpp index 358e9fb7a..900f161a6 100644 --- a/src/private/mx/core/generated/MIDI128.cpp +++ b/src/private/mx/core/generated/MIDI128.cpp @@ -42,20 +42,33 @@ std::string MIDI128::toString() const bool MIDI128::tryParse(std::string_view text, MIDI128 &out) { - int v{}; - if (!tryParseInt(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + MIDI128 parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = MIDI128{v}; + out = std::move(parsed); return true; } MIDI128 MIDI128::parse(std::string_view text) { - MIDI128 v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +MIDI128 MIDI128::parse(std::string_view text, ValueParseOutcome &outcome) +{ + int v{}; + if (!tryParseInt(text, v)) + { + outcome = ValueParseOutcome::invalid; + return MIDI128{}; + } + MIDI128 out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/MIDI128.h b/src/private/mx/core/generated/MIDI128.h index 89510bc81..dcf806952 100644 --- a/src/private/mx/core/generated/MIDI128.h +++ b/src/private/mx/core/generated/MIDI128.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -35,6 +37,9 @@ class MIDI128 final /// Lenient: non-numeric text yields the clamped zero. static MIDI128 parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static MIDI128 parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const MIDI128 &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/MIDI16.cpp b/src/private/mx/core/generated/MIDI16.cpp index 74920b4a9..c7b4cbd79 100644 --- a/src/private/mx/core/generated/MIDI16.cpp +++ b/src/private/mx/core/generated/MIDI16.cpp @@ -42,20 +42,33 @@ std::string MIDI16::toString() const bool MIDI16::tryParse(std::string_view text, MIDI16 &out) { - int v{}; - if (!tryParseInt(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + MIDI16 parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = MIDI16{v}; + out = std::move(parsed); return true; } MIDI16 MIDI16::parse(std::string_view text) { - MIDI16 v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +MIDI16 MIDI16::parse(std::string_view text, ValueParseOutcome &outcome) +{ + int v{}; + if (!tryParseInt(text, v)) + { + outcome = ValueParseOutcome::invalid; + return MIDI16{}; + } + MIDI16 out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/MIDI16.h b/src/private/mx/core/generated/MIDI16.h index 7777c8f5a..78ac9cd85 100644 --- a/src/private/mx/core/generated/MIDI16.h +++ b/src/private/mx/core/generated/MIDI16.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -35,6 +37,9 @@ class MIDI16 final /// Lenient: non-numeric text yields the clamped zero. static MIDI16 parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static MIDI16 parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const MIDI16 &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/MIDI16384.cpp b/src/private/mx/core/generated/MIDI16384.cpp index 269a088c8..d32543233 100644 --- a/src/private/mx/core/generated/MIDI16384.cpp +++ b/src/private/mx/core/generated/MIDI16384.cpp @@ -42,20 +42,33 @@ std::string MIDI16384::toString() const bool MIDI16384::tryParse(std::string_view text, MIDI16384 &out) { - int v{}; - if (!tryParseInt(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + MIDI16384 parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = MIDI16384{v}; + out = std::move(parsed); return true; } MIDI16384 MIDI16384::parse(std::string_view text) { - MIDI16384 v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +MIDI16384 MIDI16384::parse(std::string_view text, ValueParseOutcome &outcome) +{ + int v{}; + if (!tryParseInt(text, v)) + { + outcome = ValueParseOutcome::invalid; + return MIDI16384{}; + } + MIDI16384 out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/MIDI16384.h b/src/private/mx/core/generated/MIDI16384.h index 567ecc234..adec05596 100644 --- a/src/private/mx/core/generated/MIDI16384.h +++ b/src/private/mx/core/generated/MIDI16384.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -35,6 +37,9 @@ class MIDI16384 final /// Lenient: non-numeric text yields the clamped zero. static MIDI16384 parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static MIDI16384 parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const MIDI16384 &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/MIDIDevice.cpp b/src/private/mx/core/generated/MIDIDevice.cpp index 8c764d179..84ab156d0 100644 --- a/src/private/mx/core/generated/MIDIDevice.cpp +++ b/src/private/mx/core/generated/MIDIDevice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MIDIDevice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,7 +41,7 @@ void MIDIDevice::setValue(std::string value) m_value = std::move(value); } -MIDIDevice parseMIDIDevice(pugi::xml_node el) +MIDIDevice parseMIDIDevice(pugi::xml_node el, const ParseContext &context) { MIDIDevice out; for (pugi::xml_attribute a : el.attributes()) @@ -52,22 +53,22 @@ MIDIDevice parseMIDIDevice(pugi::xml_node el) } if (aname == "port") { - out.setPort(MIDI16::parse(a.value())); + out.setPort(parseValue(a.value(), context, el, "port")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseMIDIDeviceContent(out, el); + parseMIDIDeviceContent(out, el, context); return out; } -void parseMIDIDeviceContent(MIDIDevice &out, pugi::xml_node el) +void parseMIDIDeviceContent(MIDIDevice &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/MIDIDevice.h b/src/private/mx/core/generated/MIDIDevice.h index 3531a000e..b4999bd4a 100644 --- a/src/private/mx/core/generated/MIDIDevice.h +++ b/src/private/mx/core/generated/MIDIDevice.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The midi-device type corresponds to the DeviceName meta event in Standard MIDI Files. The /// optional port attribute is a number from 1 to 16 that can be used with the unofficial MIDI 1.0 /// port (or cable) meta event. Unlike the DeviceName meta event, there can be multiple midi-device @@ -40,9 +42,9 @@ class MIDIDevice final std::string m_value{}; }; -MIDIDevice parseMIDIDevice(pugi::xml_node el); +MIDIDevice parseMIDIDevice(pugi::xml_node el, const ParseContext &context); -void parseMIDIDeviceContent(MIDIDevice &out, pugi::xml_node el); +void parseMIDIDeviceContent(MIDIDevice &out, pugi::xml_node el, const ParseContext &context); void serializeMIDIDevice(const MIDIDevice &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/MIDIInstrument.cpp b/src/private/mx/core/generated/MIDIInstrument.cpp index e6b6e44ed..605055011 100644 --- a/src/private/mx/core/generated/MIDIInstrument.cpp +++ b/src/private/mx/core/generated/MIDIInstrument.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MIDIInstrument.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -100,7 +101,7 @@ void MIDIInstrument::setElevation(std::optional value) m_elevation = std::move(value); } -MIDIInstrument parseMIDIInstrument(pugi::xml_node el) +MIDIInstrument parseMIDIInstrument(pugi::xml_node el, const ParseContext &context) { MIDIInstrument out; bool seen_id = false; @@ -114,7 +115,7 @@ MIDIInstrument parseMIDIInstrument(pugi::xml_node el) if (aname == "id") { seen_id = true; - out.setID(Token::parse(a.value())); + out.setID(parseValue(a.value(), context, el, "id")); } else { @@ -125,16 +126,16 @@ MIDIInstrument parseMIDIInstrument(pugi::xml_node el) { throwMissingAttribute(el, "id"); } - parseMIDIInstrumentContent(out, el); + parseMIDIInstrumentContent(out, el, context); return out; } -void parseMIDIInstrumentContent(MIDIInstrument &out, pugi::xml_node el) +void parseMIDIInstrumentContent(MIDIInstrument &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "midi-channel")) { - out.setMIDIChannel(MIDI16::parse(childText(cursor))); + out.setMIDIChannel(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "midi-name")) @@ -144,32 +145,32 @@ void parseMIDIInstrumentContent(MIDIInstrument &out, pugi::xml_node el) } if (cursorIs(cursor, "midi-bank")) { - out.setMIDIBank(MIDI16384::parse(childText(cursor))); + out.setMIDIBank(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "midi-program")) { - out.setMIDIProgram(MIDI128::parse(childText(cursor))); + out.setMIDIProgram(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "midi-unpitched")) { - out.setMIDIUnpitched(MIDI128::parse(childText(cursor))); + out.setMIDIUnpitched(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "volume")) { - out.setVolume(Percent::parse(childText(cursor))); + out.setVolume(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "pan")) { - out.setPan(RotationDegrees::parse(childText(cursor))); + out.setPan(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "elevation")) { - out.setElevation(RotationDegrees::parse(childText(cursor))); + out.setElevation(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/MIDIInstrument.h b/src/private/mx/core/generated/MIDIInstrument.h index a950d0e85..4ff8d7089 100644 --- a/src/private/mx/core/generated/MIDIInstrument.h +++ b/src/private/mx/core/generated/MIDIInstrument.h @@ -23,6 +23,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The midi-instrument type defines MIDI 1.0 instrument playback. The midi-instrument element can be /// a part of either the score-instrument element at the start of a part, or the sound element within /// a part. The id attribute refers to the score-instrument affected by the change. @@ -63,9 +65,9 @@ class MIDIInstrument final std::optional m_elevation; }; -MIDIInstrument parseMIDIInstrument(pugi::xml_node el); +MIDIInstrument parseMIDIInstrument(pugi::xml_node el, const ParseContext &context); -void parseMIDIInstrumentContent(MIDIInstrument &out, pugi::xml_node el); +void parseMIDIInstrumentContent(MIDIInstrument &out, pugi::xml_node el, const ParseContext &context); void serializeMIDIInstrument(const MIDIInstrument &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/MarginType.cpp b/src/private/mx/core/generated/MarginType.cpp index 5ad2f1386..cb2a0939b 100644 --- a/src/private/mx/core/generated/MarginType.cpp +++ b/src/private/mx/core/generated/MarginType.cpp @@ -48,9 +48,15 @@ bool MarginType::tryParse(std::string_view text, MarginType &out) noexcept } MarginType MarginType::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +MarginType MarginType::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { MarginType v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/MarginType.h b/src/private/mx/core/generated/MarginType.h index 40ad8e109..92c2c2af5 100644 --- a/src/private/mx/core/generated/MarginType.h +++ b/src/private/mx/core/generated/MarginType.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -42,6 +44,9 @@ class MarginType final /// (the import leniency policy; never produces an invalid value). static MarginType parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static MarginType parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const MarginType &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/MeasureLayout.cpp b/src/private/mx/core/generated/MeasureLayout.cpp index e330e93ab..afff0f444 100644 --- a/src/private/mx/core/generated/MeasureLayout.cpp +++ b/src/private/mx/core/generated/MeasureLayout.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MeasureLayout.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -20,7 +21,7 @@ void MeasureLayout::setMeasureDistance(std::optional value) m_measureDistance = std::move(value); } -MeasureLayout parseMeasureLayout(pugi::xml_node el) +MeasureLayout parseMeasureLayout(pugi::xml_node el, const ParseContext &context) { MeasureLayout out; for (pugi::xml_attribute a : el.attributes()) @@ -32,16 +33,16 @@ MeasureLayout parseMeasureLayout(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseMeasureLayoutContent(out, el); + parseMeasureLayoutContent(out, el, context); return out; } -void parseMeasureLayoutContent(MeasureLayout &out, pugi::xml_node el) +void parseMeasureLayoutContent(MeasureLayout &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "measure-distance")) { - out.setMeasureDistance(Tenths::parse(childText(cursor))); + out.setMeasureDistance(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/MeasureLayout.h b/src/private/mx/core/generated/MeasureLayout.h index f6c8da1eb..41d0d30d5 100644 --- a/src/private/mx/core/generated/MeasureLayout.h +++ b/src/private/mx/core/generated/MeasureLayout.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The measure-layout type includes the horizontal distance from the previous measure. It applies to /// the current measure only. /// Content fields mirror the schema grammar in declaration order; the @@ -32,9 +34,9 @@ class MeasureLayout final std::optional m_measureDistance; }; -MeasureLayout parseMeasureLayout(pugi::xml_node el); +MeasureLayout parseMeasureLayout(pugi::xml_node el, const ParseContext &context); -void parseMeasureLayoutContent(MeasureLayout &out, pugi::xml_node el); +void parseMeasureLayoutContent(MeasureLayout &out, pugi::xml_node el, const ParseContext &context); void serializeMeasureLayout(const MeasureLayout &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/MeasureNumbering.cpp b/src/private/mx/core/generated/MeasureNumbering.cpp index 8faf032bf..1e3af7d5c 100644 --- a/src/private/mx/core/generated/MeasureNumbering.cpp +++ b/src/private/mx/core/generated/MeasureNumbering.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MeasureNumbering.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -170,7 +171,7 @@ void MeasureNumbering::setValue(MeasureNumberingValue value) m_value = std::move(value); } -MeasureNumbering parseMeasureNumbering(pugi::xml_node el) +MeasureNumbering parseMeasureNumbering(pugi::xml_node el, const ParseContext &context) { MeasureNumbering out; for (pugi::xml_attribute a : el.attributes()) @@ -182,76 +183,76 @@ MeasureNumbering parseMeasureNumbering(pugi::xml_node el) } if (aname == "system") { - out.setSystem(SystemRelationNumber::parse(a.value())); + out.setSystem(parseValue(a.value(), context, el, "system")); } else if (aname == "staff") { - out.setStaff(StaffNumber::parse(a.value())); + out.setStaff(parseValue(a.value(), context, el, "staff")); } else if (aname == "multiple-rest-always") { - out.setMultipleRestAlways(YesNo::parse(a.value())); + out.setMultipleRestAlways(parseValue(a.value(), context, el, "multiple-rest-always")); } else if (aname == "multiple-rest-range") { - out.setMultipleRestRange(YesNo::parse(a.value())); + out.setMultipleRestRange(parseValue(a.value(), context, el, "multiple-rest-range")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else { throwUnknownAttribute(el, a.name()); } } - parseMeasureNumberingContent(out, el); + parseMeasureNumberingContent(out, el, context); return out; } -void parseMeasureNumberingContent(MeasureNumbering &out, pugi::xml_node el) +void parseMeasureNumberingContent(MeasureNumbering &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(MeasureNumberingValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/MeasureNumbering.h b/src/private/mx/core/generated/MeasureNumbering.h index 7025a1729..55d8390a7 100644 --- a/src/private/mx/core/generated/MeasureNumbering.h +++ b/src/private/mx/core/generated/MeasureNumbering.h @@ -26,6 +26,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The measure-numbering type describes how frequently measure numbers are displayed on this part. /// The text attribute from the measure element is used for display, or the number attribute if the /// text attribute is not present. Measures with an implicit attribute set to "yes" never display a @@ -95,9 +97,9 @@ class MeasureNumbering final MeasureNumberingValue m_value{}; }; -MeasureNumbering parseMeasureNumbering(pugi::xml_node el); +MeasureNumbering parseMeasureNumbering(pugi::xml_node el, const ParseContext &context); -void parseMeasureNumberingContent(MeasureNumbering &out, pugi::xml_node el); +void parseMeasureNumberingContent(MeasureNumbering &out, pugi::xml_node el, const ParseContext &context); void serializeMeasureNumbering(const MeasureNumbering &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/MeasureNumberingValue.cpp b/src/private/mx/core/generated/MeasureNumberingValue.cpp index 5d8b47b59..56adbe966 100644 --- a/src/private/mx/core/generated/MeasureNumberingValue.cpp +++ b/src/private/mx/core/generated/MeasureNumberingValue.cpp @@ -48,9 +48,15 @@ bool MeasureNumberingValue::tryParse(std::string_view text, MeasureNumberingValu } MeasureNumberingValue MeasureNumberingValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +MeasureNumberingValue MeasureNumberingValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { MeasureNumberingValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/MeasureNumberingValue.h b/src/private/mx/core/generated/MeasureNumberingValue.h index 96664d633..939055eb2 100644 --- a/src/private/mx/core/generated/MeasureNumberingValue.h +++ b/src/private/mx/core/generated/MeasureNumberingValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -43,6 +45,9 @@ class MeasureNumberingValue final /// (the import leniency policy; never produces an invalid value). static MeasureNumberingValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static MeasureNumberingValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const MeasureNumberingValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/MeasureRepeat.cpp b/src/private/mx/core/generated/MeasureRepeat.cpp index c23e4fb45..83d3cd986 100644 --- a/src/private/mx/core/generated/MeasureRepeat.cpp +++ b/src/private/mx/core/generated/MeasureRepeat.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MeasureRepeat.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,7 +41,7 @@ void MeasureRepeat::setValue(PositiveIntegerOrEmpty value) m_value = std::move(value); } -MeasureRepeat parseMeasureRepeat(pugi::xml_node el) +MeasureRepeat parseMeasureRepeat(pugi::xml_node el, const ParseContext &context) { MeasureRepeat out; bool seen_type = false; @@ -54,11 +55,11 @@ MeasureRepeat parseMeasureRepeat(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStop::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "slashes") { - out.setSlashes(parseInt(a.value())); + out.setSlashes(parseIntegerValue(a.value(), context, el, "slashes")); } else { @@ -69,13 +70,13 @@ MeasureRepeat parseMeasureRepeat(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseMeasureRepeatContent(out, el); + parseMeasureRepeatContent(out, el, context); return out; } -void parseMeasureRepeatContent(MeasureRepeat &out, pugi::xml_node el) +void parseMeasureRepeatContent(MeasureRepeat &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(PositiveIntegerOrEmpty::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/MeasureRepeat.h b/src/private/mx/core/generated/MeasureRepeat.h index 8585d0798..82ab24151 100644 --- a/src/private/mx/core/generated/MeasureRepeat.h +++ b/src/private/mx/core/generated/MeasureRepeat.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The measure-repeat type is used for both single and multiple measure repeats. The text of the /// element indicates the number of measures to be repeated in a single pattern. The slashes /// attribute specifies the number of slashes to use in the repeat sign. It is 1 if not specified. @@ -42,9 +44,9 @@ class MeasureRepeat final PositiveIntegerOrEmpty m_value{}; }; -MeasureRepeat parseMeasureRepeat(pugi::xml_node el); +MeasureRepeat parseMeasureRepeat(pugi::xml_node el, const ParseContext &context); -void parseMeasureRepeatContent(MeasureRepeat &out, pugi::xml_node el); +void parseMeasureRepeatContent(MeasureRepeat &out, pugi::xml_node el, const ParseContext &context); void serializeMeasureRepeat(const MeasureRepeat &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/MeasureStyle.cpp b/src/private/mx/core/generated/MeasureStyle.cpp index af8306a35..4a83c6de6 100644 --- a/src/private/mx/core/generated/MeasureStyle.cpp +++ b/src/private/mx/core/generated/MeasureStyle.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MeasureStyle.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -90,7 +91,7 @@ void MeasureStyle::setChoice(MeasureStyleChoice value) m_choice = std::move(value); } -MeasureStyle parseMeasureStyle(pugi::xml_node el) +MeasureStyle parseMeasureStyle(pugi::xml_node el, const ParseContext &context) { MeasureStyle out; for (pugi::xml_attribute a : el.attributes()) @@ -102,48 +103,48 @@ MeasureStyle parseMeasureStyle(pugi::xml_node el) } if (aname == "number") { - out.setNumber(StaffNumber::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseMeasureStyleContent(out, el); + parseMeasureStyleContent(out, el, context); return out; } -void parseMeasureStyleContent(MeasureStyle &out, pugi::xml_node el) +void parseMeasureStyleContent(MeasureStyle &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "multiple-rest") || cursorIs(cursor, "measure-repeat") || cursorIs(cursor, "beat-repeat") || cursorIs(cursor, "slash"))) { - out.setChoice(parseMeasureStyleChoice(el, cursor)); + out.setChoice(parseMeasureStyleChoice(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/MeasureStyle.h b/src/private/mx/core/generated/MeasureStyle.h index 98f54a3d1..8c00ef9b1 100644 --- a/src/private/mx/core/generated/MeasureStyle.h +++ b/src/private/mx/core/generated/MeasureStyle.h @@ -25,6 +25,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A measure-style indicates a special way to print partial to multiple measures within a part. This /// includes multiple rests over several measures, repeats of beats, single, or multiple measures, /// and use of slash notation. The multiple-rest and measure-repeat elements indicate the number of @@ -66,9 +68,9 @@ class MeasureStyle final MeasureStyleChoice m_choice{}; }; -MeasureStyle parseMeasureStyle(pugi::xml_node el); +MeasureStyle parseMeasureStyle(pugi::xml_node el, const ParseContext &context); -void parseMeasureStyleContent(MeasureStyle &out, pugi::xml_node el); +void parseMeasureStyleContent(MeasureStyle &out, pugi::xml_node el, const ParseContext &context); void serializeMeasureStyle(const MeasureStyle &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/MeasureStyleChoice.cpp b/src/private/mx/core/generated/MeasureStyleChoice.cpp index b492b2ab5..b7354aecd 100644 --- a/src/private/mx/core/generated/MeasureStyleChoice.cpp +++ b/src/private/mx/core/generated/MeasureStyleChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MeasureStyleChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,29 +36,29 @@ MeasureStyleChoice MeasureStyleChoice::slash(Slash value) return MeasureStyleChoice{Storage{std::in_place_index<3>, std::move(value)}}; } -MeasureStyleChoice parseMeasureStyleChoice(pugi::xml_node el, pugi::xml_node &cursor) +MeasureStyleChoice parseMeasureStyleChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "multiple-rest"))) { - MultipleRest value = parseMultipleRest(cursor); + MultipleRest value = parseMultipleRest(cursor, context); cursor = nextElement(cursor); return MeasureStyleChoice::multipleRest(std::move(value)); } if (cursor && (cursorIs(cursor, "measure-repeat"))) { - MeasureRepeat value = parseMeasureRepeat(cursor); + MeasureRepeat value = parseMeasureRepeat(cursor, context); cursor = nextElement(cursor); return MeasureStyleChoice::measureRepeat(std::move(value)); } if (cursor && (cursorIs(cursor, "beat-repeat"))) { - BeatRepeat value = parseBeatRepeat(cursor); + BeatRepeat value = parseBeatRepeat(cursor, context); cursor = nextElement(cursor); return MeasureStyleChoice::beatRepeat(std::move(value)); } if (cursor && (cursorIs(cursor, "slash"))) { - Slash value = parseSlash(cursor); + Slash value = parseSlash(cursor, context); cursor = nextElement(cursor); return MeasureStyleChoice::slash(std::move(value)); } diff --git a/src/private/mx/core/generated/MeasureStyleChoice.h b/src/private/mx/core/generated/MeasureStyleChoice.h index 57987a1a8..d33d3da19 100644 --- a/src/private/mx/core/generated/MeasureStyleChoice.h +++ b/src/private/mx/core/generated/MeasureStyleChoice.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -110,7 +112,7 @@ class MeasureStyleChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -MeasureStyleChoice parseMeasureStyleChoice(pugi::xml_node el, pugi::xml_node &cursor); +MeasureStyleChoice parseMeasureStyleChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeMeasureStyleChoice(const MeasureStyleChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/MeasureText.cpp b/src/private/mx/core/generated/MeasureText.cpp index 65b80f007..9a188a80e 100644 --- a/src/private/mx/core/generated/MeasureText.cpp +++ b/src/private/mx/core/generated/MeasureText.cpp @@ -42,4 +42,11 @@ MeasureText MeasureText::parse(std::string_view text) return MeasureText{std::string{text}}; } +MeasureText MeasureText::parse(std::string_view text, ValueParseOutcome &outcome) +{ + MeasureText out{std::string{text}}; + outcome = out.value() == text ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/MeasureText.h b/src/private/mx/core/generated/MeasureText.h index b27dfe10a..652ce4318 100644 --- a/src/private/mx/core/generated/MeasureText.h +++ b/src/private/mx/core/generated/MeasureText.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -37,6 +39,9 @@ class MeasureText final static MeasureText parse(std::string_view text); + /// Says whether the text had to be repaired. + static MeasureText parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const MeasureText &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Membrane.cpp b/src/private/mx/core/generated/Membrane.cpp index e42ec6826..314f24559 100644 --- a/src/private/mx/core/generated/Membrane.cpp +++ b/src/private/mx/core/generated/Membrane.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Membrane.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Membrane::setValue(MembraneValue value) m_value = std::move(value); } -Membrane parseMembrane(pugi::xml_node el) +Membrane parseMembrane(pugi::xml_node el, const ParseContext &context) { Membrane out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ Membrane parseMembrane(pugi::xml_node el) } if (aname == "smufl") { - out.setSmufl(SmuflPictogramGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else { throwUnknownAttribute(el, a.name()); } } - parseMembraneContent(out, el); + parseMembraneContent(out, el, context); return out; } -void parseMembraneContent(Membrane &out, pugi::xml_node el) +void parseMembraneContent(Membrane &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(MembraneValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Membrane.h b/src/private/mx/core/generated/Membrane.h index 4c75a6130..682bb6492 100644 --- a/src/private/mx/core/generated/Membrane.h +++ b/src/private/mx/core/generated/Membrane.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The membrane type represents pictograms for membrane percussion instruments. The smufl attribute /// is used to distinguish different SMuFL stylistic alternates. class Membrane final @@ -32,9 +34,9 @@ class Membrane final MembraneValue m_value{}; }; -Membrane parseMembrane(pugi::xml_node el); +Membrane parseMembrane(pugi::xml_node el, const ParseContext &context); -void parseMembraneContent(Membrane &out, pugi::xml_node el); +void parseMembraneContent(Membrane &out, pugi::xml_node el, const ParseContext &context); void serializeMembrane(const Membrane &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/MembraneValue.cpp b/src/private/mx/core/generated/MembraneValue.cpp index 8440bd772..6796e23c0 100644 --- a/src/private/mx/core/generated/MembraneValue.cpp +++ b/src/private/mx/core/generated/MembraneValue.cpp @@ -118,9 +118,15 @@ bool MembraneValue::tryParse(std::string_view text, MembraneValue &out) noexcept } MembraneValue MembraneValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +MembraneValue MembraneValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { MembraneValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/MembraneValue.h b/src/private/mx/core/generated/MembraneValue.h index dd5246102..34bd86e7f 100644 --- a/src/private/mx/core/generated/MembraneValue.h +++ b/src/private/mx/core/generated/MembraneValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -70,6 +72,9 @@ class MembraneValue final /// (the import leniency policy; never produces an invalid value). static MembraneValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static MembraneValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const MembraneValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Metal.cpp b/src/private/mx/core/generated/Metal.cpp index 39d9da1b1..99089671d 100644 --- a/src/private/mx/core/generated/Metal.cpp +++ b/src/private/mx/core/generated/Metal.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Metal.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Metal::setValue(MetalValue value) m_value = std::move(value); } -Metal parseMetal(pugi::xml_node el) +Metal parseMetal(pugi::xml_node el, const ParseContext &context) { Metal out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ Metal parseMetal(pugi::xml_node el) } if (aname == "smufl") { - out.setSmufl(SmuflPictogramGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else { throwUnknownAttribute(el, a.name()); } } - parseMetalContent(out, el); + parseMetalContent(out, el, context); return out; } -void parseMetalContent(Metal &out, pugi::xml_node el) +void parseMetalContent(Metal &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(MetalValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Metal.h b/src/private/mx/core/generated/Metal.h index ad429fe82..9d253dbed 100644 --- a/src/private/mx/core/generated/Metal.h +++ b/src/private/mx/core/generated/Metal.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The metal type represents pictograms for metal percussion instruments. The smufl attribute is /// used to distinguish different SMuFL stylistic alternates. class Metal final @@ -32,9 +34,9 @@ class Metal final MetalValue m_value{}; }; -Metal parseMetal(pugi::xml_node el); +Metal parseMetal(pugi::xml_node el, const ParseContext &context); -void parseMetalContent(Metal &out, pugi::xml_node el); +void parseMetalContent(Metal &out, pugi::xml_node el, const ParseContext &context); void serializeMetal(const Metal &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/MetalValue.cpp b/src/private/mx/core/generated/MetalValue.cpp index 3f1ee2b04..2bf45eb43 100644 --- a/src/private/mx/core/generated/MetalValue.cpp +++ b/src/private/mx/core/generated/MetalValue.cpp @@ -222,9 +222,15 @@ bool MetalValue::tryParse(std::string_view text, MetalValue &out) noexcept } MetalValue MetalValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +MetalValue MetalValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { MetalValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/MetalValue.h b/src/private/mx/core/generated/MetalValue.h index 8d008e670..758919a10 100644 --- a/src/private/mx/core/generated/MetalValue.h +++ b/src/private/mx/core/generated/MetalValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -102,6 +104,9 @@ class MetalValue final /// (the import leniency policy; never produces an invalid value). static MetalValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static MetalValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const MetalValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Metronome.cpp b/src/private/mx/core/generated/Metronome.cpp index db355f8eb..78738bf36 100644 --- a/src/private/mx/core/generated/Metronome.cpp +++ b/src/private/mx/core/generated/Metronome.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Metronome.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -170,7 +171,7 @@ void Metronome::setChoice(MetronomeChoice value) m_choice = std::move(value); } -Metronome parseMetronome(pugi::xml_node el) +Metronome parseMetronome(pugi::xml_node el, const ParseContext &context) { Metronome out; for (pugi::xml_attribute a : el.attributes()) @@ -182,80 +183,80 @@ Metronome parseMetronome(pugi::xml_node el) } if (aname == "parentheses") { - out.setParentheses(YesNo::parse(a.value())); + out.setParentheses(parseValue(a.value(), context, el, "parentheses")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else if (aname == "justify") { - out.setJustify(LeftCenterRight::parse(a.value())); + out.setJustify(parseValue(a.value(), context, el, "justify")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseMetronomeContent(out, el); + parseMetronomeContent(out, el, context); return out; } -void parseMetronomeContent(Metronome &out, pugi::xml_node el) +void parseMetronomeContent(Metronome &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "beat-unit") || cursorIs(cursor, "metronome-arrows") || cursorIs(cursor, "metronome-note"))) { - out.setChoice(parseMetronomeChoice(el, cursor)); + out.setChoice(parseMetronomeChoice(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/Metronome.h b/src/private/mx/core/generated/Metronome.h index 1b2114531..0515ae20f 100644 --- a/src/private/mx/core/generated/Metronome.h +++ b/src/private/mx/core/generated/Metronome.h @@ -28,6 +28,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The metronome type represents metronome marks and other metric relationships. The beat-unit group /// and per-minute element specify regular metronome marks. The metronome-note and metronome-relation /// elements allow for the specification of metric modulations and other metric relationships, such @@ -95,9 +97,9 @@ class Metronome final MetronomeChoice m_choice{}; }; -Metronome parseMetronome(pugi::xml_node el); +Metronome parseMetronome(pugi::xml_node el, const ParseContext &context); -void parseMetronomeContent(Metronome &out, pugi::xml_node el); +void parseMetronomeContent(Metronome &out, pugi::xml_node el, const ParseContext &context); void serializeMetronome(const Metronome &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/MetronomeBeam.cpp b/src/private/mx/core/generated/MetronomeBeam.cpp index 231377fe8..4ef6c6996 100644 --- a/src/private/mx/core/generated/MetronomeBeam.cpp +++ b/src/private/mx/core/generated/MetronomeBeam.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MetronomeBeam.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void MetronomeBeam::setValue(BeamValue value) m_value = std::move(value); } -MetronomeBeam parseMetronomeBeam(pugi::xml_node el) +MetronomeBeam parseMetronomeBeam(pugi::xml_node el, const ParseContext &context) { MetronomeBeam out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ MetronomeBeam parseMetronomeBeam(pugi::xml_node el) } if (aname == "number") { - out.setNumber(BeamLevel::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else { throwUnknownAttribute(el, a.name()); } } - parseMetronomeBeamContent(out, el); + parseMetronomeBeamContent(out, el, context); return out; } -void parseMetronomeBeamContent(MetronomeBeam &out, pugi::xml_node el) +void parseMetronomeBeamContent(MetronomeBeam &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(BeamValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/MetronomeBeam.h b/src/private/mx/core/generated/MetronomeBeam.h index da7bd01a9..7c7cdcc88 100644 --- a/src/private/mx/core/generated/MetronomeBeam.h +++ b/src/private/mx/core/generated/MetronomeBeam.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The metronome-beam type works like the beam type in defining metric relationships, but does not /// include all the attributes available in the beam type. class MetronomeBeam final @@ -32,9 +34,9 @@ class MetronomeBeam final BeamValue m_value{}; }; -MetronomeBeam parseMetronomeBeam(pugi::xml_node el); +MetronomeBeam parseMetronomeBeam(pugi::xml_node el, const ParseContext &context); -void parseMetronomeBeamContent(MetronomeBeam &out, pugi::xml_node el); +void parseMetronomeBeamContent(MetronomeBeam &out, pugi::xml_node el, const ParseContext &context); void serializeMetronomeBeam(const MetronomeBeam &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/MetronomeChoice.cpp b/src/private/mx/core/generated/MetronomeChoice.cpp index 75425ec78..09c89a8b1 100644 --- a/src/private/mx/core/generated/MetronomeChoice.cpp +++ b/src/private/mx/core/generated/MetronomeChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MetronomeChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,15 +26,15 @@ MetronomeChoice MetronomeChoice::group2(MetronomeChoiceGroup2 value) return MetronomeChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -MetronomeChoice parseMetronomeChoice(pugi::xml_node el, pugi::xml_node &cursor) +MetronomeChoice parseMetronomeChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "beat-unit"))) { - return MetronomeChoice::group(parseMetronomeChoiceGroup(el, cursor)); + return MetronomeChoice::group(parseMetronomeChoiceGroup(el, cursor, context)); } if (cursor && (cursorIs(cursor, "metronome-arrows") || cursorIs(cursor, "metronome-note"))) { - return MetronomeChoice::group2(parseMetronomeChoiceGroup2(el, cursor)); + return MetronomeChoice::group2(parseMetronomeChoiceGroup2(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/MetronomeChoice.h b/src/private/mx/core/generated/MetronomeChoice.h index c249bfedc..699fe0c3b 100644 --- a/src/private/mx/core/generated/MetronomeChoice.h +++ b/src/private/mx/core/generated/MetronomeChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -80,7 +82,7 @@ class MetronomeChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -MetronomeChoice parseMetronomeChoice(pugi::xml_node el, pugi::xml_node &cursor); +MetronomeChoice parseMetronomeChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeMetronomeChoice(const MetronomeChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/MetronomeChoiceGroup.cpp b/src/private/mx/core/generated/MetronomeChoiceGroup.cpp index 7a54bf11f..0a4c2d3c6 100644 --- a/src/private/mx/core/generated/MetronomeChoiceGroup.cpp +++ b/src/private/mx/core/generated/MetronomeChoiceGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MetronomeChoiceGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -45,12 +46,12 @@ void MetronomeChoiceGroup::setChoice(MetronomeChoiceGroupChoice value) m_choice = std::move(value); } -MetronomeChoiceGroup parseMetronomeChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor) +MetronomeChoiceGroup parseMetronomeChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { MetronomeChoiceGroup out; if (cursor && (cursorIs(cursor, "beat-unit"))) { - out.setBeatUnit(parseBeatUnitGroup(el, cursor)); + out.setBeatUnit(parseBeatUnitGroup(el, cursor, context)); } else { @@ -58,12 +59,12 @@ MetronomeChoiceGroup parseMetronomeChoiceGroup(pugi::xml_node el, pugi::xml_node } while (cursorIs(cursor, "beat-unit-tied")) { - out.addBeatUnitTied(parseBeatUnitTied(cursor)); + out.addBeatUnitTied(parseBeatUnitTied(cursor, context)); cursor = nextElement(cursor); } if (cursor && (cursorIs(cursor, "per-minute") || cursorIs(cursor, "beat-unit"))) { - out.setChoice(parseMetronomeChoiceGroupChoice(el, cursor)); + out.setChoice(parseMetronomeChoiceGroupChoice(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/MetronomeChoiceGroup.h b/src/private/mx/core/generated/MetronomeChoiceGroup.h index 76457a185..a60db3a14 100644 --- a/src/private/mx/core/generated/MetronomeChoiceGroup.h +++ b/src/private/mx/core/generated/MetronomeChoiceGroup.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -42,7 +44,7 @@ class MetronomeChoiceGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -MetronomeChoiceGroup parseMetronomeChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor); +MetronomeChoiceGroup parseMetronomeChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeMetronomeChoiceGroup(const MetronomeChoiceGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/MetronomeChoiceGroup2.cpp b/src/private/mx/core/generated/MetronomeChoiceGroup2.cpp index 4db5e1c25..4ac698f88 100644 --- a/src/private/mx/core/generated/MetronomeChoiceGroup2.cpp +++ b/src/private/mx/core/generated/MetronomeChoiceGroup2.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MetronomeChoiceGroup2.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -45,12 +46,12 @@ void MetronomeChoiceGroup2::setGroup(std::optional v m_group = std::move(value); } -MetronomeChoiceGroup2 parseMetronomeChoiceGroup2(pugi::xml_node el, pugi::xml_node &cursor) +MetronomeChoiceGroup2 parseMetronomeChoiceGroup2(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { MetronomeChoiceGroup2 out; if (cursorIs(cursor, "metronome-arrows")) { - parseEmpty(cursor); + parseEmpty(cursor, context); out.setMetronomeArrows(true); cursor = nextElement(cursor); } @@ -58,16 +59,16 @@ MetronomeChoiceGroup2 parseMetronomeChoiceGroup2(pugi::xml_node el, pugi::xml_no { throwMissingOrMisplaced(el, cursor, "metronome-note"); } - out.setMetronomeNote(OneOrMore{parseMetronomeNote(cursor)}); + out.setMetronomeNote(OneOrMore{parseMetronomeNote(cursor, context)}); cursor = nextElement(cursor); while (cursorIs(cursor, "metronome-note")) { - out.addMetronomeNote(parseMetronomeNote(cursor)); + out.addMetronomeNote(parseMetronomeNote(cursor, context)); cursor = nextElement(cursor); } if (cursor && (cursorIs(cursor, "metronome-relation"))) { - out.setGroup(parseMetronomeChoiceGroup2Group(el, cursor)); + out.setGroup(parseMetronomeChoiceGroup2Group(el, cursor, context)); } return out; } diff --git a/src/private/mx/core/generated/MetronomeChoiceGroup2.h b/src/private/mx/core/generated/MetronomeChoiceGroup2.h index 0cd762c50..4805815a5 100644 --- a/src/private/mx/core/generated/MetronomeChoiceGroup2.h +++ b/src/private/mx/core/generated/MetronomeChoiceGroup2.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -43,7 +45,8 @@ class MetronomeChoiceGroup2 final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -MetronomeChoiceGroup2 parseMetronomeChoiceGroup2(pugi::xml_node el, pugi::xml_node &cursor); +MetronomeChoiceGroup2 parseMetronomeChoiceGroup2(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context); void serializeMetronomeChoiceGroup2(const MetronomeChoiceGroup2 &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/MetronomeChoiceGroup2Group.cpp b/src/private/mx/core/generated/MetronomeChoiceGroup2Group.cpp index 465462887..76fa1b894 100644 --- a/src/private/mx/core/generated/MetronomeChoiceGroup2Group.cpp +++ b/src/private/mx/core/generated/MetronomeChoiceGroup2Group.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MetronomeChoiceGroup2Group.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,7 +36,8 @@ void MetronomeChoiceGroup2Group::setMetronomeNote(OneOrMore value m_metronomeNote = std::move(value); } -MetronomeChoiceGroup2Group parseMetronomeChoiceGroup2Group(pugi::xml_node el, pugi::xml_node &cursor) +MetronomeChoiceGroup2Group parseMetronomeChoiceGroup2Group(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context) { MetronomeChoiceGroup2Group out; if (cursorIs(cursor, "metronome-relation")) @@ -51,11 +53,11 @@ MetronomeChoiceGroup2Group parseMetronomeChoiceGroup2Group(pugi::xml_node el, pu { throwMissingOrMisplaced(el, cursor, "metronome-note"); } - out.setMetronomeNote(OneOrMore{parseMetronomeNote(cursor)}); + out.setMetronomeNote(OneOrMore{parseMetronomeNote(cursor, context)}); cursor = nextElement(cursor); while (cursorIs(cursor, "metronome-note")) { - out.addMetronomeNote(parseMetronomeNote(cursor)); + out.addMetronomeNote(parseMetronomeNote(cursor, context)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/MetronomeChoiceGroup2Group.h b/src/private/mx/core/generated/MetronomeChoiceGroup2Group.h index 1f6b5b406..e0aea6d92 100644 --- a/src/private/mx/core/generated/MetronomeChoiceGroup2Group.h +++ b/src/private/mx/core/generated/MetronomeChoiceGroup2Group.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -38,7 +40,8 @@ class MetronomeChoiceGroup2Group final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -MetronomeChoiceGroup2Group parseMetronomeChoiceGroup2Group(pugi::xml_node el, pugi::xml_node &cursor); +MetronomeChoiceGroup2Group parseMetronomeChoiceGroup2Group(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context); void serializeMetronomeChoiceGroup2Group(const MetronomeChoiceGroup2Group &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/MetronomeChoiceGroupChoice.cpp b/src/private/mx/core/generated/MetronomeChoiceGroupChoice.cpp index ae10bd9d3..2976d5975 100644 --- a/src/private/mx/core/generated/MetronomeChoiceGroupChoice.cpp +++ b/src/private/mx/core/generated/MetronomeChoiceGroupChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MetronomeChoiceGroupChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,17 +26,18 @@ MetronomeChoiceGroupChoice MetronomeChoiceGroupChoice::group(MetronomeChoiceGrou return MetronomeChoiceGroupChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -MetronomeChoiceGroupChoice parseMetronomeChoiceGroupChoice(pugi::xml_node el, pugi::xml_node &cursor) +MetronomeChoiceGroupChoice parseMetronomeChoiceGroupChoice(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context) { if (cursor && (cursorIs(cursor, "per-minute"))) { - PerMinute value = parsePerMinute(cursor); + PerMinute value = parsePerMinute(cursor, context); cursor = nextElement(cursor); return MetronomeChoiceGroupChoice::perMinute(std::move(value)); } if (cursor && (cursorIs(cursor, "beat-unit"))) { - return MetronomeChoiceGroupChoice::group(parseMetronomeChoiceGroupChoiceGroup(el, cursor)); + return MetronomeChoiceGroupChoice::group(parseMetronomeChoiceGroupChoiceGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/MetronomeChoiceGroupChoice.h b/src/private/mx/core/generated/MetronomeChoiceGroupChoice.h index 0723d330e..b8cdb7d3b 100644 --- a/src/private/mx/core/generated/MetronomeChoiceGroupChoice.h +++ b/src/private/mx/core/generated/MetronomeChoiceGroupChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -80,7 +82,8 @@ class MetronomeChoiceGroupChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -MetronomeChoiceGroupChoice parseMetronomeChoiceGroupChoice(pugi::xml_node el, pugi::xml_node &cursor); +MetronomeChoiceGroupChoice parseMetronomeChoiceGroupChoice(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context); void serializeMetronomeChoiceGroupChoice(const MetronomeChoiceGroupChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/MetronomeChoiceGroupChoiceGroup.cpp b/src/private/mx/core/generated/MetronomeChoiceGroupChoiceGroup.cpp index accaa60a9..7f748aae9 100644 --- a/src/private/mx/core/generated/MetronomeChoiceGroupChoiceGroup.cpp +++ b/src/private/mx/core/generated/MetronomeChoiceGroupChoiceGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MetronomeChoiceGroupChoiceGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,12 +36,13 @@ void MetronomeChoiceGroupChoiceGroup::setBeatUnitTied(std::vector m_beatUnitTied = std::move(value); } -MetronomeChoiceGroupChoiceGroup parseMetronomeChoiceGroupChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor) +MetronomeChoiceGroupChoiceGroup parseMetronomeChoiceGroupChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context) { MetronomeChoiceGroupChoiceGroup out; if (cursor && (cursorIs(cursor, "beat-unit"))) { - out.setBeatUnit(parseBeatUnitGroup(el, cursor)); + out.setBeatUnit(parseBeatUnitGroup(el, cursor, context)); } else { @@ -48,7 +50,7 @@ MetronomeChoiceGroupChoiceGroup parseMetronomeChoiceGroupChoiceGroup(pugi::xml_n } while (cursorIs(cursor, "beat-unit-tied")) { - out.addBeatUnitTied(parseBeatUnitTied(cursor)); + out.addBeatUnitTied(parseBeatUnitTied(cursor, context)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/MetronomeChoiceGroupChoiceGroup.h b/src/private/mx/core/generated/MetronomeChoiceGroupChoiceGroup.h index 3cd45ab8a..36f201d18 100644 --- a/src/private/mx/core/generated/MetronomeChoiceGroupChoiceGroup.h +++ b/src/private/mx/core/generated/MetronomeChoiceGroupChoiceGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -38,7 +40,8 @@ class MetronomeChoiceGroupChoiceGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -MetronomeChoiceGroupChoiceGroup parseMetronomeChoiceGroupChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor); +MetronomeChoiceGroupChoiceGroup parseMetronomeChoiceGroupChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context); void serializeMetronomeChoiceGroupChoiceGroup(const MetronomeChoiceGroupChoiceGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/MetronomeNote.cpp b/src/private/mx/core/generated/MetronomeNote.cpp index 0c700221e..a1d53e174 100644 --- a/src/private/mx/core/generated/MetronomeNote.cpp +++ b/src/private/mx/core/generated/MetronomeNote.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MetronomeNote.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -70,7 +71,7 @@ void MetronomeNote::setMetronomeTuplet(std::optional value) m_metronomeTuplet = std::move(value); } -MetronomeNote parseMetronomeNote(pugi::xml_node el) +MetronomeNote parseMetronomeNote(pugi::xml_node el, const ParseContext &context) { MetronomeNote out; for (pugi::xml_attribute a : el.attributes()) @@ -82,16 +83,16 @@ MetronomeNote parseMetronomeNote(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseMetronomeNoteContent(out, el); + parseMetronomeNoteContent(out, el, context); return out; } -void parseMetronomeNoteContent(MetronomeNote &out, pugi::xml_node el) +void parseMetronomeNoteContent(MetronomeNote &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "metronome-type")) { - out.setMetronomeType(NoteTypeValue::parse(childText(cursor))); + out.setMetronomeType(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -100,22 +101,22 @@ void parseMetronomeNoteContent(MetronomeNote &out, pugi::xml_node el) } while (cursorIs(cursor, "metronome-dot")) { - out.addMetronomeDot(parseEmpty(cursor)); + out.addMetronomeDot(parseEmpty(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "metronome-beam")) { - out.addMetronomeBeam(parseMetronomeBeam(cursor)); + out.addMetronomeBeam(parseMetronomeBeam(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "metronome-tied")) { - out.setMetronomeTied(parseMetronomeTied(cursor)); + out.setMetronomeTied(parseMetronomeTied(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "metronome-tuplet")) { - out.setMetronomeTuplet(parseMetronomeTuplet(cursor)); + out.setMetronomeTuplet(parseMetronomeTuplet(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/MetronomeNote.h b/src/private/mx/core/generated/MetronomeNote.h index 44735bad9..344dc6a7c 100644 --- a/src/private/mx/core/generated/MetronomeNote.h +++ b/src/private/mx/core/generated/MetronomeNote.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The metronome-note type defines the appearance of a note within a metric relationship mark. /// Content fields mirror the schema grammar in declaration order; the /// serializer walks them, so a wrong order is unrepresentable (plan §2.3). @@ -49,9 +51,9 @@ class MetronomeNote final std::optional m_metronomeTuplet; }; -MetronomeNote parseMetronomeNote(pugi::xml_node el); +MetronomeNote parseMetronomeNote(pugi::xml_node el, const ParseContext &context); -void parseMetronomeNoteContent(MetronomeNote &out, pugi::xml_node el); +void parseMetronomeNoteContent(MetronomeNote &out, pugi::xml_node el, const ParseContext &context); void serializeMetronomeNote(const MetronomeNote &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/MetronomeTied.cpp b/src/private/mx/core/generated/MetronomeTied.cpp index 33d3705c1..8cb7458a0 100644 --- a/src/private/mx/core/generated/MetronomeTied.cpp +++ b/src/private/mx/core/generated/MetronomeTied.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MetronomeTied.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -20,7 +21,7 @@ void MetronomeTied::setType(StartStop value) m_type = std::move(value); } -MetronomeTied parseMetronomeTied(pugi::xml_node el) +MetronomeTied parseMetronomeTied(pugi::xml_node el, const ParseContext &context) { MetronomeTied out; bool seen_type = false; @@ -34,7 +35,7 @@ MetronomeTied parseMetronomeTied(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStop::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else { @@ -45,13 +46,14 @@ MetronomeTied parseMetronomeTied(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseMetronomeTiedContent(out, el); + parseMetronomeTiedContent(out, el, context); return out; } -void parseMetronomeTiedContent(MetronomeTied &out, pugi::xml_node el) +void parseMetronomeTiedContent(MetronomeTied &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/MetronomeTied.h b/src/private/mx/core/generated/MetronomeTied.h index 86419414c..bafb2e79b 100644 --- a/src/private/mx/core/generated/MetronomeTied.h +++ b/src/private/mx/core/generated/MetronomeTied.h @@ -15,6 +15,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The metronome-tied indicates the presence of a tie within a metric relationship mark. As with the /// tied element, both the start and stop of the tie should be specified, in this case within /// separate metronome-note elements. @@ -29,9 +31,9 @@ class MetronomeTied final StartStop m_type{}; }; -MetronomeTied parseMetronomeTied(pugi::xml_node el); +MetronomeTied parseMetronomeTied(pugi::xml_node el, const ParseContext &context); -void parseMetronomeTiedContent(MetronomeTied &out, pugi::xml_node el); +void parseMetronomeTiedContent(MetronomeTied &out, pugi::xml_node el, const ParseContext &context); void serializeMetronomeTied(const MetronomeTied &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/MetronomeTuplet.cpp b/src/private/mx/core/generated/MetronomeTuplet.cpp index dc03df05e..d99e0caca 100644 --- a/src/private/mx/core/generated/MetronomeTuplet.cpp +++ b/src/private/mx/core/generated/MetronomeTuplet.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MetronomeTuplet.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,7 +41,7 @@ void MetronomeTuplet::setShowNumber(std::optional value) m_showNumber = std::move(value); } -MetronomeTuplet parseMetronomeTuplet(pugi::xml_node el) +MetronomeTuplet parseMetronomeTuplet(pugi::xml_node el, const ParseContext &context) { MetronomeTuplet out; bool seen_type = false; @@ -54,15 +55,15 @@ MetronomeTuplet parseMetronomeTuplet(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStop::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "bracket") { - out.setBracket(YesNo::parse(a.value())); + out.setBracket(parseValue(a.value(), context, el, "bracket")); } else if (aname == "show-number") { - out.setShowNumber(ShowTuplet::parse(a.value())); + out.setShowNumber(parseValue(a.value(), context, el, "show-number")); } else { @@ -73,7 +74,7 @@ MetronomeTuplet parseMetronomeTuplet(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseTimeModificationContent(out, el); + parseTimeModificationContent(out, el, context); return out; } diff --git a/src/private/mx/core/generated/MetronomeTuplet.h b/src/private/mx/core/generated/MetronomeTuplet.h index 5c4a28ddf..c5ebcf6ff 100644 --- a/src/private/mx/core/generated/MetronomeTuplet.h +++ b/src/private/mx/core/generated/MetronomeTuplet.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The metronome-tuplet type uses the same element structure as the time-modification element along /// with some attributes from the tuplet element. /// The schema's complexContent extension: non-polymorphic public @@ -39,7 +41,7 @@ class MetronomeTuplet : public TimeModification std::optional m_showNumber; }; -MetronomeTuplet parseMetronomeTuplet(pugi::xml_node el); +MetronomeTuplet parseMetronomeTuplet(pugi::xml_node el, const ParseContext &context); void serializeMetronomeTuplet(const MetronomeTuplet &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Millimeters.cpp b/src/private/mx/core/generated/Millimeters.cpp index 11edc688a..93ee55e58 100644 --- a/src/private/mx/core/generated/Millimeters.cpp +++ b/src/private/mx/core/generated/Millimeters.cpp @@ -34,20 +34,33 @@ std::string Millimeters::toString() const bool Millimeters::tryParse(std::string_view text, Millimeters &out) { - Decimal v; - if (!Decimal::tryParse(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + Millimeters parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = Millimeters{std::move(v)}; + out = std::move(parsed); return true; } Millimeters Millimeters::parse(std::string_view text) { - Millimeters v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +Millimeters Millimeters::parse(std::string_view text, ValueParseOutcome &outcome) +{ + Decimal v; + if (!Decimal::tryParse(text, v)) + { + outcome = ValueParseOutcome::invalid; + return Millimeters{}; + } + Millimeters out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/Millimeters.h b/src/private/mx/core/generated/Millimeters.h index 806dc180d..411c7398e 100644 --- a/src/private/mx/core/generated/Millimeters.h +++ b/src/private/mx/core/generated/Millimeters.h @@ -3,6 +3,7 @@ #pragma once #include "mx/core/Decimal.h" +#include "mx/core/Lexical.h" #include #include @@ -38,6 +39,9 @@ class Millimeters final /// Lenient: non-numeric text yields the clamped zero. static Millimeters parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static Millimeters parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const Millimeters &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Milliseconds.cpp b/src/private/mx/core/generated/Milliseconds.cpp index 10d7e9e60..2b5e117fe 100644 --- a/src/private/mx/core/generated/Milliseconds.cpp +++ b/src/private/mx/core/generated/Milliseconds.cpp @@ -38,20 +38,33 @@ std::string Milliseconds::toString() const bool Milliseconds::tryParse(std::string_view text, Milliseconds &out) { - int v{}; - if (!tryParseInt(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + Milliseconds parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = Milliseconds{v}; + out = std::move(parsed); return true; } Milliseconds Milliseconds::parse(std::string_view text) { - Milliseconds v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +Milliseconds Milliseconds::parse(std::string_view text, ValueParseOutcome &outcome) +{ + int v{}; + if (!tryParseInt(text, v)) + { + outcome = ValueParseOutcome::invalid; + return Milliseconds{}; + } + Milliseconds out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/Milliseconds.h b/src/private/mx/core/generated/Milliseconds.h index f23cb66d2..198882f46 100644 --- a/src/private/mx/core/generated/Milliseconds.h +++ b/src/private/mx/core/generated/Milliseconds.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -35,6 +37,9 @@ class Milliseconds final /// Lenient: non-numeric text yields the clamped zero. static Milliseconds parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static Milliseconds parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const Milliseconds &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Miscellaneous.cpp b/src/private/mx/core/generated/Miscellaneous.cpp index c78033d28..f11673b92 100644 --- a/src/private/mx/core/generated/Miscellaneous.cpp +++ b/src/private/mx/core/generated/Miscellaneous.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Miscellaneous.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,7 +26,7 @@ void Miscellaneous::setMiscellaneousField(std::vector value) m_miscellaneousField = std::move(value); } -Miscellaneous parseMiscellaneous(pugi::xml_node el) +Miscellaneous parseMiscellaneous(pugi::xml_node el, const ParseContext &context) { Miscellaneous out; for (pugi::xml_attribute a : el.attributes()) @@ -37,16 +38,16 @@ Miscellaneous parseMiscellaneous(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseMiscellaneousContent(out, el); + parseMiscellaneousContent(out, el, context); return out; } -void parseMiscellaneousContent(Miscellaneous &out, pugi::xml_node el) +void parseMiscellaneousContent(Miscellaneous &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); while (cursorIs(cursor, "miscellaneous-field")) { - out.addMiscellaneousField(parseMiscellaneousField(cursor)); + out.addMiscellaneousField(parseMiscellaneousField(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Miscellaneous.h b/src/private/mx/core/generated/Miscellaneous.h index 01d0d1c46..d7bc41756 100644 --- a/src/private/mx/core/generated/Miscellaneous.h +++ b/src/private/mx/core/generated/Miscellaneous.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// If a program has other metadata not yet supported in the MusicXML format, it can go in the /// miscellaneous element. The miscellaneous type puts each separate part of metadata into its own /// miscellaneous-field type. @@ -34,9 +36,9 @@ class Miscellaneous final std::vector m_miscellaneousField; }; -Miscellaneous parseMiscellaneous(pugi::xml_node el); +Miscellaneous parseMiscellaneous(pugi::xml_node el, const ParseContext &context); -void parseMiscellaneousContent(Miscellaneous &out, pugi::xml_node el); +void parseMiscellaneousContent(Miscellaneous &out, pugi::xml_node el, const ParseContext &context); void serializeMiscellaneous(const Miscellaneous &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/MiscellaneousField.cpp b/src/private/mx/core/generated/MiscellaneousField.cpp index 083502967..7c648e710 100644 --- a/src/private/mx/core/generated/MiscellaneousField.cpp +++ b/src/private/mx/core/generated/MiscellaneousField.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MiscellaneousField.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void MiscellaneousField::setValue(std::string value) m_value = std::move(value); } -MiscellaneousField parseMiscellaneousField(pugi::xml_node el) +MiscellaneousField parseMiscellaneousField(pugi::xml_node el, const ParseContext &context) { MiscellaneousField out; bool seen_name = false; @@ -55,11 +56,11 @@ MiscellaneousField parseMiscellaneousField(pugi::xml_node el) { throwMissingAttribute(el, "name"); } - parseMiscellaneousFieldContent(out, el); + parseMiscellaneousFieldContent(out, el, context); return out; } -void parseMiscellaneousFieldContent(MiscellaneousField &out, pugi::xml_node el) +void parseMiscellaneousFieldContent(MiscellaneousField &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/MiscellaneousField.h b/src/private/mx/core/generated/MiscellaneousField.h index 5951dd8b3..78dc6aee2 100644 --- a/src/private/mx/core/generated/MiscellaneousField.h +++ b/src/private/mx/core/generated/MiscellaneousField.h @@ -13,6 +13,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// If a program has other metadata not yet supported in the MusicXML format, each type of metadata /// can go in a miscellaneous-field element. The required name attribute indicates the type of /// metadata the element content represents. @@ -30,9 +32,9 @@ class MiscellaneousField final std::string m_value{}; }; -MiscellaneousField parseMiscellaneousField(pugi::xml_node el); +MiscellaneousField parseMiscellaneousField(pugi::xml_node el, const ParseContext &context); -void parseMiscellaneousFieldContent(MiscellaneousField &out, pugi::xml_node el); +void parseMiscellaneousFieldContent(MiscellaneousField &out, pugi::xml_node el, const ParseContext &context); void serializeMiscellaneousField(const MiscellaneousField &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Mode.cpp b/src/private/mx/core/generated/Mode.cpp index 937150740..b5ac25710 100644 --- a/src/private/mx/core/generated/Mode.cpp +++ b/src/private/mx/core/generated/Mode.cpp @@ -32,4 +32,11 @@ Mode Mode::parse(std::string_view text) return Mode{std::string{text}}; } +Mode Mode::parse(std::string_view text, ValueParseOutcome &outcome) +{ + Mode out{std::string{text}}; + outcome = out.value() == text ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/Mode.h b/src/private/mx/core/generated/Mode.h index f45cf7880..6efeb5a06 100644 --- a/src/private/mx/core/generated/Mode.h +++ b/src/private/mx/core/generated/Mode.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -35,6 +37,9 @@ class Mode final static Mode parse(std::string_view text); + /// Says whether the text had to be repaired. + static Mode parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const Mode &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Mordent.cpp b/src/private/mx/core/generated/Mordent.cpp index 8ad6701c0..d9f7322e0 100644 --- a/src/private/mx/core/generated/Mordent.cpp +++ b/src/private/mx/core/generated/Mordent.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Mordent.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,7 +41,7 @@ void Mordent::setDeparture(std::optional value) m_departure = std::move(value); } -Mordent parseMordent(pugi::xml_node el) +Mordent parseMordent(pugi::xml_node el, const ParseContext &context) { Mordent out; for (pugi::xml_attribute a : el.attributes()) @@ -52,90 +53,90 @@ Mordent parseMordent(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "start-note") { - out.setStartNote(StartNote::parse(a.value())); + out.setStartNote(parseValue(a.value(), context, el, "start-note")); } else if (aname == "trill-step") { - out.setTrillStep(TrillStep::parse(a.value())); + out.setTrillStep(parseValue(a.value(), context, el, "trill-step")); } else if (aname == "two-note-turn") { - out.setTwoNoteTurn(TwoNoteTurn::parse(a.value())); + out.setTwoNoteTurn(parseValue(a.value(), context, el, "two-note-turn")); } else if (aname == "accelerate") { - out.setAccelerate(YesNo::parse(a.value())); + out.setAccelerate(parseValue(a.value(), context, el, "accelerate")); } else if (aname == "beats") { - out.setBeats(TrillBeats::parse(a.value())); + out.setBeats(parseValue(a.value(), context, el, "beats")); } else if (aname == "second-beat") { - out.setSecondBeat(Percent::parse(a.value())); + out.setSecondBeat(parseValue(a.value(), context, el, "second-beat")); } else if (aname == "last-beat") { - out.setLastBeat(Percent::parse(a.value())); + out.setLastBeat(parseValue(a.value(), context, el, "last-beat")); } else if (aname == "long") { - out.setLong(YesNo::parse(a.value())); + out.setLong(parseValue(a.value(), context, el, "long")); } else if (aname == "approach") { - out.setApproach(AboveBelow::parse(a.value())); + out.setApproach(parseValue(a.value(), context, el, "approach")); } else if (aname == "departure") { - out.setDeparture(AboveBelow::parse(a.value())); + out.setDeparture(parseValue(a.value(), context, el, "departure")); } else { throwUnknownAttribute(el, a.name()); } } - parseEmptyTrillSoundContent(out, el); + parseEmptyTrillSoundContent(out, el, context); return out; } diff --git a/src/private/mx/core/generated/Mordent.h b/src/private/mx/core/generated/Mordent.h index 27980f313..6b5b501fb 100644 --- a/src/private/mx/core/generated/Mordent.h +++ b/src/private/mx/core/generated/Mordent.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The mordent type is used for both represents the mordent sign with the vertical line and the /// inverted-mordent sign without the line. The long attribute is "no" by default. The approach and /// departure attributes are used for compound ornaments, indicating how the beginning and ending of @@ -40,7 +42,7 @@ class Mordent : public EmptyTrillSound std::optional m_departure; }; -Mordent parseMordent(pugi::xml_node el); +Mordent parseMordent(pugi::xml_node el, const ParseContext &context); void serializeMordent(const Mordent &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/MultipleRest.cpp b/src/private/mx/core/generated/MultipleRest.cpp index 4da046413..ad542e8c9 100644 --- a/src/private/mx/core/generated/MultipleRest.cpp +++ b/src/private/mx/core/generated/MultipleRest.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/MultipleRest.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void MultipleRest::setValue(int value) m_value = std::move(value); } -MultipleRest parseMultipleRest(pugi::xml_node el) +MultipleRest parseMultipleRest(pugi::xml_node el, const ParseContext &context) { MultipleRest out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ MultipleRest parseMultipleRest(pugi::xml_node el) } if (aname == "use-symbols") { - out.setUseSymbols(YesNo::parse(a.value())); + out.setUseSymbols(parseValue(a.value(), context, el, "use-symbols")); } else { throwUnknownAttribute(el, a.name()); } } - parseMultipleRestContent(out, el); + parseMultipleRestContent(out, el, context); return out; } -void parseMultipleRestContent(MultipleRest &out, pugi::xml_node el) +void parseMultipleRestContent(MultipleRest &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(parseInt(childText(el))); + out.setValue(parseIntegerValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/MultipleRest.h b/src/private/mx/core/generated/MultipleRest.h index 1940adfb6..bc7016ef4 100644 --- a/src/private/mx/core/generated/MultipleRest.h +++ b/src/private/mx/core/generated/MultipleRest.h @@ -15,6 +15,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The text of the multiple-rest type indicates the number of measures in the multiple rest. /// Multiple rests may use the 1-bar / 2-bar / 4-bar rest symbols, or a single shape. The use-symbols /// attribute indicates which to use; it is no if not specified. @@ -32,9 +34,9 @@ class MultipleRest final int m_value{}; }; -MultipleRest parseMultipleRest(pugi::xml_node el); +MultipleRest parseMultipleRest(pugi::xml_node el, const ParseContext &context); -void parseMultipleRestContent(MultipleRest &out, pugi::xml_node el); +void parseMultipleRestContent(MultipleRest &out, pugi::xml_node el, const ParseContext &context); void serializeMultipleRest(const MultipleRest &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/MusicDataChoice.cpp b/src/private/mx/core/generated/MusicDataChoice.cpp index 4a51a2fe4..53cfedcaf 100644 --- a/src/private/mx/core/generated/MusicDataChoice.cpp +++ b/src/private/mx/core/generated/MusicDataChoice.cpp @@ -2,6 +2,7 @@ #include "mx/core/generated/MusicDataChoice.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -178,89 +179,89 @@ MusicDataChoice MusicDataChoice::bookmark(Bookmark value) return MusicDataChoice{Storage{std::in_place_index<13>, std::make_unique(std::move(value))}}; } -MusicDataChoice parseMusicDataChoice(pugi::xml_node el, pugi::xml_node &cursor) +MusicDataChoice parseMusicDataChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursorIs(cursor, "note")) { - Note value = parseNote(cursor); + Note value = parseNote(cursor, context); cursor = nextElement(cursor); return MusicDataChoice::note(std::move(value)); } if (cursorIs(cursor, "backup")) { - Backup value = parseBackup(cursor); + Backup value = parseBackup(cursor, context); cursor = nextElement(cursor); return MusicDataChoice::backup(std::move(value)); } if (cursorIs(cursor, "forward")) { - Forward value = parseForward(cursor); + Forward value = parseForward(cursor, context); cursor = nextElement(cursor); return MusicDataChoice::forward(std::move(value)); } if (cursorIs(cursor, "direction")) { - Direction value = parseDirection(cursor); + Direction value = parseDirection(cursor, context); cursor = nextElement(cursor); return MusicDataChoice::direction(std::move(value)); } if (cursorIs(cursor, "attributes")) { - Attributes value = parseAttributes(cursor); + Attributes value = parseAttributes(cursor, context); cursor = nextElement(cursor); return MusicDataChoice::attributes(std::move(value)); } if (cursorIs(cursor, "harmony")) { - Harmony value = parseHarmony(cursor); + Harmony value = parseHarmony(cursor, context); cursor = nextElement(cursor); return MusicDataChoice::harmony(std::move(value)); } if (cursorIs(cursor, "figured-bass")) { - FiguredBass value = parseFiguredBass(cursor); + FiguredBass value = parseFiguredBass(cursor, context); cursor = nextElement(cursor); return MusicDataChoice::figuredBass(std::move(value)); } if (cursorIs(cursor, "print")) { - Print value = parsePrint(cursor); + Print value = parsePrint(cursor, context); cursor = nextElement(cursor); return MusicDataChoice::print(std::move(value)); } if (cursorIs(cursor, "sound")) { - Sound value = parseSound(cursor); + Sound value = parseSound(cursor, context); cursor = nextElement(cursor); return MusicDataChoice::sound(std::move(value)); } if (cursorIs(cursor, "listening")) { - Listening value = parseListening(cursor); + Listening value = parseListening(cursor, context); cursor = nextElement(cursor); return MusicDataChoice::listening(std::move(value)); } if (cursorIs(cursor, "barline")) { - Barline value = parseBarline(cursor); + Barline value = parseBarline(cursor, context); cursor = nextElement(cursor); return MusicDataChoice::barline(std::move(value)); } if (cursorIs(cursor, "grouping")) { - Grouping value = parseGrouping(cursor); + Grouping value = parseGrouping(cursor, context); cursor = nextElement(cursor); return MusicDataChoice::grouping(std::move(value)); } if (cursorIs(cursor, "link")) { - Link value = parseLink(cursor); + Link value = parseLink(cursor, context); cursor = nextElement(cursor); return MusicDataChoice::link(std::move(value)); } if (cursorIs(cursor, "bookmark")) { - Bookmark value = parseBookmark(cursor); + Bookmark value = parseBookmark(cursor, context); cursor = nextElement(cursor); return MusicDataChoice::bookmark(std::move(value)); } diff --git a/src/private/mx/core/generated/MusicDataChoice.h b/src/private/mx/core/generated/MusicDataChoice.h index 230220b41..8d709ab23 100644 --- a/src/private/mx/core/generated/MusicDataChoice.h +++ b/src/private/mx/core/generated/MusicDataChoice.h @@ -29,6 +29,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The music-data group contains the basic musical data that is either associated with a part or a /// measure, depending on whether the partwise or timewise hierarchy is used. /// A repeated heterogeneous choice with large alternatives: each is boxed @@ -275,7 +277,7 @@ class MusicDataChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -MusicDataChoice parseMusicDataChoice(pugi::xml_node el, pugi::xml_node &cursor); +MusicDataChoice parseMusicDataChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeMusicDataChoice(const MusicDataChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Mute.cpp b/src/private/mx/core/generated/Mute.cpp index e49dac800..c9b8c8f04 100644 --- a/src/private/mx/core/generated/Mute.cpp +++ b/src/private/mx/core/generated/Mute.cpp @@ -107,9 +107,15 @@ bool Mute::tryParse(std::string_view text, Mute &out) noexcept } Mute Mute::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +Mute Mute::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { Mute v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/Mute.h b/src/private/mx/core/generated/Mute.h index 4b7bff496..42c9af7da 100644 --- a/src/private/mx/core/generated/Mute.h +++ b/src/private/mx/core/generated/Mute.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -68,6 +70,9 @@ class Mute final /// (the import leniency policy; never produces an invalid value). static Mute parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static Mute parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const Mute &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/NameDisplay.cpp b/src/private/mx/core/generated/NameDisplay.cpp index e7e000e4d..31d612127 100644 --- a/src/private/mx/core/generated/NameDisplay.cpp +++ b/src/private/mx/core/generated/NameDisplay.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/NameDisplay.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,7 +36,7 @@ void NameDisplay::setChoice(std::vector value) m_choice = std::move(value); } -NameDisplay parseNameDisplay(pugi::xml_node el) +NameDisplay parseNameDisplay(pugi::xml_node el, const ParseContext &context) { NameDisplay out; for (pugi::xml_attribute a : el.attributes()) @@ -47,23 +48,23 @@ NameDisplay parseNameDisplay(pugi::xml_node el) } if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else { throwUnknownAttribute(el, a.name()); } } - parseNameDisplayContent(out, el); + parseNameDisplayContent(out, el, context); return out; } -void parseNameDisplayContent(NameDisplay &out, pugi::xml_node el) +void parseNameDisplayContent(NameDisplay &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); while (cursor && (cursorIs(cursor, "display-text") || cursorIs(cursor, "accidental-text"))) { - out.addChoice(parseNameDisplayChoice(el, cursor)); + out.addChoice(parseNameDisplayChoice(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/NameDisplay.h b/src/private/mx/core/generated/NameDisplay.h index 9a9179522..d495b9dd8 100644 --- a/src/private/mx/core/generated/NameDisplay.h +++ b/src/private/mx/core/generated/NameDisplay.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The name-display type is used for exact formatting of multi-font text in part and group names to /// the left of the system. The print-object attribute can be used to determine what, if anything, is /// printed at the start of each system. Enclosure for the display-text element is none by default. @@ -39,9 +41,9 @@ class NameDisplay final std::vector m_choice; }; -NameDisplay parseNameDisplay(pugi::xml_node el); +NameDisplay parseNameDisplay(pugi::xml_node el, const ParseContext &context); -void parseNameDisplayContent(NameDisplay &out, pugi::xml_node el); +void parseNameDisplayContent(NameDisplay &out, pugi::xml_node el, const ParseContext &context); void serializeNameDisplay(const NameDisplay &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/NameDisplayChoice.cpp b/src/private/mx/core/generated/NameDisplayChoice.cpp index 81e9480f8..a779859f5 100644 --- a/src/private/mx/core/generated/NameDisplayChoice.cpp +++ b/src/private/mx/core/generated/NameDisplayChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/NameDisplayChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,17 +26,17 @@ NameDisplayChoice NameDisplayChoice::accidentalText(AccidentalText value) return NameDisplayChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -NameDisplayChoice parseNameDisplayChoice(pugi::xml_node el, pugi::xml_node &cursor) +NameDisplayChoice parseNameDisplayChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "display-text"))) { - FormattedText value = parseFormattedText(cursor); + FormattedText value = parseFormattedText(cursor, context); cursor = nextElement(cursor); return NameDisplayChoice::displayText(std::move(value)); } if (cursor && (cursorIs(cursor, "accidental-text"))) { - AccidentalText value = parseAccidentalText(cursor); + AccidentalText value = parseAccidentalText(cursor, context); cursor = nextElement(cursor); return NameDisplayChoice::accidentalText(std::move(value)); } diff --git a/src/private/mx/core/generated/NameDisplayChoice.h b/src/private/mx/core/generated/NameDisplayChoice.h index e50909193..006273fa2 100644 --- a/src/private/mx/core/generated/NameDisplayChoice.h +++ b/src/private/mx/core/generated/NameDisplayChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -80,7 +82,7 @@ class NameDisplayChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -NameDisplayChoice parseNameDisplayChoice(pugi::xml_node el, pugi::xml_node &cursor); +NameDisplayChoice parseNameDisplayChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeNameDisplayChoice(const NameDisplayChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/NonArpeggiate.cpp b/src/private/mx/core/generated/NonArpeggiate.cpp index 9c3cc3c6d..21bd0d124 100644 --- a/src/private/mx/core/generated/NonArpeggiate.cpp +++ b/src/private/mx/core/generated/NonArpeggiate.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/NonArpeggiate.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -100,7 +101,7 @@ void NonArpeggiate::setID(std::optional value) m_id = std::move(value); } -NonArpeggiate parseNonArpeggiate(pugi::xml_node el) +NonArpeggiate parseNonArpeggiate(pugi::xml_node el, const ParseContext &context) { NonArpeggiate out; bool seen_type = false; @@ -114,39 +115,39 @@ NonArpeggiate parseNonArpeggiate(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(TopBottom::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "number") { - out.setNumber(NumberLevel::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -157,13 +158,14 @@ NonArpeggiate parseNonArpeggiate(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseNonArpeggiateContent(out, el); + parseNonArpeggiateContent(out, el, context); return out; } -void parseNonArpeggiateContent(NonArpeggiate &out, pugi::xml_node el) +void parseNonArpeggiateContent(NonArpeggiate &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/NonArpeggiate.h b/src/private/mx/core/generated/NonArpeggiate.h index c24ef7f87..535e12c52 100644 --- a/src/private/mx/core/generated/NonArpeggiate.h +++ b/src/private/mx/core/generated/NonArpeggiate.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The non-arpeggiate type indicates that this note is at the top or bottom of a bracket indicating /// to not arpeggiate these notes. Since this does not involve playback, it is only used on the top /// or bottom notes, not on each note as for the arpeggiate type. @@ -59,9 +61,9 @@ class NonArpeggiate final std::optional m_id; }; -NonArpeggiate parseNonArpeggiate(pugi::xml_node el); +NonArpeggiate parseNonArpeggiate(pugi::xml_node el, const ParseContext &context); -void parseNonArpeggiateContent(NonArpeggiate &out, pugi::xml_node el); +void parseNonArpeggiateContent(NonArpeggiate &out, pugi::xml_node el, const ParseContext &context); void serializeNonArpeggiate(const NonArpeggiate &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/NonNegativeDecimal.cpp b/src/private/mx/core/generated/NonNegativeDecimal.cpp index c5e0374af..1944e573e 100644 --- a/src/private/mx/core/generated/NonNegativeDecimal.cpp +++ b/src/private/mx/core/generated/NonNegativeDecimal.cpp @@ -38,20 +38,33 @@ std::string NonNegativeDecimal::toString() const bool NonNegativeDecimal::tryParse(std::string_view text, NonNegativeDecimal &out) { - Decimal v; - if (!Decimal::tryParse(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + NonNegativeDecimal parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = NonNegativeDecimal{std::move(v)}; + out = std::move(parsed); return true; } NonNegativeDecimal NonNegativeDecimal::parse(std::string_view text) { - NonNegativeDecimal v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +NonNegativeDecimal NonNegativeDecimal::parse(std::string_view text, ValueParseOutcome &outcome) +{ + Decimal v; + if (!Decimal::tryParse(text, v)) + { + outcome = ValueParseOutcome::invalid; + return NonNegativeDecimal{}; + } + NonNegativeDecimal out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/NonNegativeDecimal.h b/src/private/mx/core/generated/NonNegativeDecimal.h index c0746d2ef..5b605f67b 100644 --- a/src/private/mx/core/generated/NonNegativeDecimal.h +++ b/src/private/mx/core/generated/NonNegativeDecimal.h @@ -3,6 +3,7 @@ #pragma once #include "mx/core/Decimal.h" +#include "mx/core/Lexical.h" #include #include @@ -37,6 +38,9 @@ class NonNegativeDecimal final /// Lenient: non-numeric text yields the clamped zero. static NonNegativeDecimal parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static NonNegativeDecimal parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const NonNegativeDecimal &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/NonTraditionalKeyGroup.cpp b/src/private/mx/core/generated/NonTraditionalKeyGroup.cpp index 9ad686ea0..3c27407ee 100644 --- a/src/private/mx/core/generated/NonTraditionalKeyGroup.cpp +++ b/src/private/mx/core/generated/NonTraditionalKeyGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/NonTraditionalKeyGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,12 +41,13 @@ void NonTraditionalKeyGroup::setKeyAccidental(std::optional value m_keyAccidental = std::move(value); } -NonTraditionalKeyGroup parseNonTraditionalKeyGroup(pugi::xml_node el, pugi::xml_node &cursor) +NonTraditionalKeyGroup parseNonTraditionalKeyGroup(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context) { NonTraditionalKeyGroup out; if (cursorIs(cursor, "key-step")) { - out.setKeyStep(Step::parse(childText(cursor))); + out.setKeyStep(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -54,7 +56,7 @@ NonTraditionalKeyGroup parseNonTraditionalKeyGroup(pugi::xml_node el, pugi::xml_ } if (cursorIs(cursor, "key-alter")) { - out.setKeyAlter(Semitones::parse(childText(cursor))); + out.setKeyAlter(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -63,7 +65,7 @@ NonTraditionalKeyGroup parseNonTraditionalKeyGroup(pugi::xml_node el, pugi::xml_ } if (cursorIs(cursor, "key-accidental")) { - out.setKeyAccidental(parseKeyAccidental(cursor)); + out.setKeyAccidental(parseKeyAccidental(cursor, context)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/NonTraditionalKeyGroup.h b/src/private/mx/core/generated/NonTraditionalKeyGroup.h index 3ddb6a894..520923771 100644 --- a/src/private/mx/core/generated/NonTraditionalKeyGroup.h +++ b/src/private/mx/core/generated/NonTraditionalKeyGroup.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The non-traditional-key group represents a single alteration within a non-traditional key /// signature. A sequence of these groups makes up a non-traditional key signature /// A shared content group: transparent on the wire, its @@ -42,7 +44,8 @@ class NonTraditionalKeyGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -NonTraditionalKeyGroup parseNonTraditionalKeyGroup(pugi::xml_node el, pugi::xml_node &cursor); +NonTraditionalKeyGroup parseNonTraditionalKeyGroup(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context); void serializeNonTraditionalKeyGroup(const NonTraditionalKeyGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/NormalNoteGroup.cpp b/src/private/mx/core/generated/NormalNoteGroup.cpp index 3f51a183f..7d25040d5 100644 --- a/src/private/mx/core/generated/NormalNoteGroup.cpp +++ b/src/private/mx/core/generated/NormalNoteGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/NormalNoteGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,13 +51,13 @@ void NormalNoteGroup::clearTie() noexcept m_tie.clear(); } -NormalNoteGroup parseNormalNoteGroup(pugi::xml_node el, pugi::xml_node &cursor) +NormalNoteGroup parseNormalNoteGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { NormalNoteGroup out; if (cursor && (cursorIs(cursor, "chord") || cursorIs(cursor, "pitch") || cursorIs(cursor, "unpitched") || cursorIs(cursor, "rest"))) { - out.setFullNote(parseFullNoteGroup(el, cursor)); + out.setFullNote(parseFullNoteGroup(el, cursor, context)); } else { @@ -64,7 +65,7 @@ NormalNoteGroup parseNormalNoteGroup(pugi::xml_node el, pugi::xml_node &cursor) } if (cursorIs(cursor, "duration")) { - out.setDuration(PositiveDivisions::parse(childText(cursor))); + out.setDuration(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -73,7 +74,7 @@ NormalNoteGroup parseNormalNoteGroup(pugi::xml_node el, pugi::xml_node &cursor) } while (cursorIs(cursor, "tie")) { - if (!out.addTie(parseTie(cursor))) + if (!out.addTie(parseTie(cursor, context))) { throwTooManyElements(cursor); } diff --git a/src/private/mx/core/generated/NormalNoteGroup.h b/src/private/mx/core/generated/NormalNoteGroup.h index 3fe87af62..8f8a98a44 100644 --- a/src/private/mx/core/generated/NormalNoteGroup.h +++ b/src/private/mx/core/generated/NormalNoteGroup.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -45,7 +47,7 @@ class NormalNoteGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -NormalNoteGroup parseNormalNoteGroup(pugi::xml_node el, pugi::xml_node &cursor); +NormalNoteGroup parseNormalNoteGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeNormalNoteGroup(const NormalNoteGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Notations.cpp b/src/private/mx/core/generated/Notations.cpp index e8c219a50..cf6a25877 100644 --- a/src/private/mx/core/generated/Notations.cpp +++ b/src/private/mx/core/generated/Notations.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Notations.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -55,7 +56,7 @@ void Notations::setChoice(std::vector value) m_choice = std::move(value); } -Notations parseNotations(pugi::xml_node el) +Notations parseNotations(pugi::xml_node el, const ParseContext &context) { Notations out; for (pugi::xml_attribute a : el.attributes()) @@ -67,27 +68,27 @@ Notations parseNotations(pugi::xml_node el) } if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseNotationsContent(out, el); + parseNotationsContent(out, el, context); return out; } -void parseNotationsContent(Notations &out, pugi::xml_node el) +void parseNotationsContent(Notations &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "footnote") || cursorIs(cursor, "level"))) { - out.setEditorial(parseEditorialGroup(el, cursor)); + out.setEditorial(parseEditorialGroup(el, cursor, context)); } while (cursor && (cursorIs(cursor, "tied") || cursorIs(cursor, "slur") || cursorIs(cursor, "tuplet") || @@ -96,7 +97,7 @@ void parseNotationsContent(Notations &out, pugi::xml_node el) cursorIs(cursor, "fermata") || cursorIs(cursor, "arpeggiate") || cursorIs(cursor, "non-arpeggiate") || cursorIs(cursor, "accidental-mark") || cursorIs(cursor, "other-notation"))) { - out.addChoice(parseNotationsChoice(el, cursor)); + out.addChoice(parseNotationsChoice(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/Notations.h b/src/private/mx/core/generated/Notations.h index 360ea1fa1..3d3405fd3 100644 --- a/src/private/mx/core/generated/Notations.h +++ b/src/private/mx/core/generated/Notations.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Notations refer to musical notations, not XML notations. Multiple notations are allowed in order /// to represent multiple editorial levels. The print-object attribute, added in Version 3.0, allows /// notations to represent details of performance technique, such as fingerings, without having them @@ -48,9 +50,9 @@ class Notations final std::vector m_choice; }; -Notations parseNotations(pugi::xml_node el); +Notations parseNotations(pugi::xml_node el, const ParseContext &context); -void parseNotationsContent(Notations &out, pugi::xml_node el); +void parseNotationsContent(Notations &out, pugi::xml_node el, const ParseContext &context); void serializeNotations(const Notations &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/NotationsChoice.cpp b/src/private/mx/core/generated/NotationsChoice.cpp index dfb5b0b79..5a17727a4 100644 --- a/src/private/mx/core/generated/NotationsChoice.cpp +++ b/src/private/mx/core/generated/NotationsChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/NotationsChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -85,89 +86,89 @@ NotationsChoice NotationsChoice::otherNotation(OtherNotation value) return NotationsChoice{Storage{std::in_place_index<13>, std::move(value)}}; } -NotationsChoice parseNotationsChoice(pugi::xml_node el, pugi::xml_node &cursor) +NotationsChoice parseNotationsChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "tied"))) { - Tied value = parseTied(cursor); + Tied value = parseTied(cursor, context); cursor = nextElement(cursor); return NotationsChoice::tied(std::move(value)); } if (cursor && (cursorIs(cursor, "slur"))) { - Slur value = parseSlur(cursor); + Slur value = parseSlur(cursor, context); cursor = nextElement(cursor); return NotationsChoice::slur(std::move(value)); } if (cursor && (cursorIs(cursor, "tuplet"))) { - Tuplet value = parseTuplet(cursor); + Tuplet value = parseTuplet(cursor, context); cursor = nextElement(cursor); return NotationsChoice::tuplet(std::move(value)); } if (cursor && (cursorIs(cursor, "glissando"))) { - Glissando value = parseGlissando(cursor); + Glissando value = parseGlissando(cursor, context); cursor = nextElement(cursor); return NotationsChoice::glissando(std::move(value)); } if (cursor && (cursorIs(cursor, "slide"))) { - Slide value = parseSlide(cursor); + Slide value = parseSlide(cursor, context); cursor = nextElement(cursor); return NotationsChoice::slide(std::move(value)); } if (cursor && (cursorIs(cursor, "ornaments"))) { - Ornaments value = parseOrnaments(cursor); + Ornaments value = parseOrnaments(cursor, context); cursor = nextElement(cursor); return NotationsChoice::ornaments(std::move(value)); } if (cursor && (cursorIs(cursor, "technical"))) { - Technical value = parseTechnical(cursor); + Technical value = parseTechnical(cursor, context); cursor = nextElement(cursor); return NotationsChoice::technical(std::move(value)); } if (cursor && (cursorIs(cursor, "articulations"))) { - Articulations value = parseArticulations(cursor); + Articulations value = parseArticulations(cursor, context); cursor = nextElement(cursor); return NotationsChoice::articulations(std::move(value)); } if (cursor && (cursorIs(cursor, "dynamics"))) { - Dynamics value = parseDynamics(cursor); + Dynamics value = parseDynamics(cursor, context); cursor = nextElement(cursor); return NotationsChoice::dynamics(std::move(value)); } if (cursor && (cursorIs(cursor, "fermata"))) { - Fermata value = parseFermata(cursor); + Fermata value = parseFermata(cursor, context); cursor = nextElement(cursor); return NotationsChoice::fermata(std::move(value)); } if (cursor && (cursorIs(cursor, "arpeggiate"))) { - Arpeggiate value = parseArpeggiate(cursor); + Arpeggiate value = parseArpeggiate(cursor, context); cursor = nextElement(cursor); return NotationsChoice::arpeggiate(std::move(value)); } if (cursor && (cursorIs(cursor, "non-arpeggiate"))) { - NonArpeggiate value = parseNonArpeggiate(cursor); + NonArpeggiate value = parseNonArpeggiate(cursor, context); cursor = nextElement(cursor); return NotationsChoice::nonArpeggiate(std::move(value)); } if (cursor && (cursorIs(cursor, "accidental-mark"))) { - AccidentalMark value = parseAccidentalMark(cursor); + AccidentalMark value = parseAccidentalMark(cursor, context); cursor = nextElement(cursor); return NotationsChoice::accidentalMark(std::move(value)); } if (cursor && (cursorIs(cursor, "other-notation"))) { - OtherNotation value = parseOtherNotation(cursor); + OtherNotation value = parseOtherNotation(cursor, context); cursor = nextElement(cursor); return NotationsChoice::otherNotation(std::move(value)); } diff --git a/src/private/mx/core/generated/NotationsChoice.h b/src/private/mx/core/generated/NotationsChoice.h index 88dc08d18..b5a24e8d9 100644 --- a/src/private/mx/core/generated/NotationsChoice.h +++ b/src/private/mx/core/generated/NotationsChoice.h @@ -30,6 +30,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -261,7 +263,7 @@ class NotationsChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -NotationsChoice parseNotationsChoice(pugi::xml_node el, pugi::xml_node &cursor); +NotationsChoice parseNotationsChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeNotationsChoice(const NotationsChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Note.cpp b/src/private/mx/core/generated/Note.cpp index e767fb695..d1f7b71ef 100644 --- a/src/private/mx/core/generated/Note.cpp +++ b/src/private/mx/core/generated/Note.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Note.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -410,7 +411,7 @@ void Note::setListen(std::optional value) m_listen = std::move(value); } -Note parseNote(pugi::xml_node el) +Note parseNote(pugi::xml_node el, const ParseContext &context) { Note out; for (pugi::xml_attribute a : el.attributes()) @@ -422,104 +423,104 @@ Note parseNote(pugi::xml_node el) } if (aname == "print-leger") { - out.setPrintLeger(YesNo::parse(a.value())); + out.setPrintLeger(parseValue(a.value(), context, el, "print-leger")); } else if (aname == "dynamics") { - out.setDynamics(NonNegativeDecimal::parse(a.value())); + out.setDynamics(parseValue(a.value(), context, el, "dynamics")); } else if (aname == "end-dynamics") { - out.setEndDynamics(NonNegativeDecimal::parse(a.value())); + out.setEndDynamics(parseValue(a.value(), context, el, "end-dynamics")); } else if (aname == "attack") { - out.setAttack(Divisions::parse(a.value())); + out.setAttack(parseValue(a.value(), context, el, "attack")); } else if (aname == "release") { - out.setRelease(Divisions::parse(a.value())); + out.setRelease(parseValue(a.value(), context, el, "release")); } else if (aname == "time-only") { - out.setTimeOnly(TimeOnly::parse(a.value())); + out.setTimeOnly(parseValue(a.value(), context, el, "time-only")); } else if (aname == "pizzicato") { - out.setPizzicato(YesNo::parse(a.value())); + out.setPizzicato(parseValue(a.value(), context, el, "pizzicato")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "print-dot") { - out.setPrintDot(YesNo::parse(a.value())); + out.setPrintDot(parseValue(a.value(), context, el, "print-dot")); } else if (aname == "print-lyric") { - out.setPrintLyric(YesNo::parse(a.value())); + out.setPrintLyric(parseValue(a.value(), context, el, "print-lyric")); } else if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else if (aname == "print-spacing") { - out.setPrintSpacing(YesNo::parse(a.value())); + out.setPrintSpacing(parseValue(a.value(), context, el, "print-spacing")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseNoteContent(out, el); + parseNoteContent(out, el, context); return out; } -void parseNoteContent(Note &out, pugi::xml_node el) +void parseNoteContent(Note &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "grace") || cursorIs(cursor, "cue") || cursorIs(cursor, "chord") || cursorIs(cursor, "pitch") || cursorIs(cursor, "unpitched") || cursorIs(cursor, "rest"))) { - out.setChoice(parseNoteChoice(el, cursor)); + out.setChoice(parseNoteChoice(el, cursor, context)); } else { @@ -527,56 +528,56 @@ void parseNoteContent(Note &out, pugi::xml_node el) } while (cursorIs(cursor, "instrument")) { - out.addInstrument(parseInstrument(cursor)); + out.addInstrument(parseInstrument(cursor, context)); cursor = nextElement(cursor); } if (cursor && (cursorIs(cursor, "footnote") || cursorIs(cursor, "level") || cursorIs(cursor, "voice"))) { - out.setEditorialVoice(parseEditorialVoiceGroup(el, cursor)); + out.setEditorialVoice(parseEditorialVoiceGroup(el, cursor, context)); } if (cursorIs(cursor, "type")) { - out.setType(parseNoteType(cursor)); + out.setType(parseNoteType(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "dot")) { - out.addDot(parseEmptyPlacement(cursor)); + out.addDot(parseEmptyPlacement(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "accidental")) { - out.setAccidental(parseAccidental(cursor)); + out.setAccidental(parseAccidental(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "time-modification")) { - out.setTimeModification(parseTimeModification(cursor)); + out.setTimeModification(parseTimeModification(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "stem")) { - out.setStem(parseStem(cursor)); + out.setStem(parseStem(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "notehead")) { - out.setNotehead(parseNotehead(cursor)); + out.setNotehead(parseNotehead(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "notehead-text")) { - out.setNoteheadText(parseNoteheadText(cursor)); + out.setNoteheadText(parseNoteheadText(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "staff")) { - out.setStaff(parseInt(childText(cursor))); + out.setStaff(parseIntegerValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } while (cursorIs(cursor, "beam")) { - if (!out.addBeam(parseBeam(cursor))) + if (!out.addBeam(parseBeam(cursor, context))) { throwTooManyElements(cursor); } @@ -584,22 +585,22 @@ void parseNoteContent(Note &out, pugi::xml_node el) } while (cursorIs(cursor, "notations")) { - out.addNotations(parseNotations(cursor)); + out.addNotations(parseNotations(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "lyric")) { - out.addLyric(parseLyric(cursor)); + out.addLyric(parseLyric(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "play")) { - out.setPlay(parsePlay(cursor)); + out.setPlay(parsePlay(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "listen")) { - out.setListen(parseListen(cursor)); + out.setListen(parseListen(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Note.h b/src/private/mx/core/generated/Note.h index 595cca438..dd080c6f8 100644 --- a/src/private/mx/core/generated/Note.h +++ b/src/private/mx/core/generated/Note.h @@ -44,6 +44,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Notes are the most common type of MusicXML data. The MusicXML format distinguishes between /// elements used for sound information and elements used for notation information (e.g., tie is used /// for sound, tied for notation). Thus grace notes do not have a duration element. Cue notes have a @@ -193,9 +195,9 @@ class Note final std::optional m_listen; }; -Note parseNote(pugi::xml_node el); +Note parseNote(pugi::xml_node el, const ParseContext &context); -void parseNoteContent(Note &out, pugi::xml_node el); +void parseNoteContent(Note &out, pugi::xml_node el, const ParseContext &context); void serializeNote(const Note &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/NoteChoice.cpp b/src/private/mx/core/generated/NoteChoice.cpp index 9df834fdc..468c6513a 100644 --- a/src/private/mx/core/generated/NoteChoice.cpp +++ b/src/private/mx/core/generated/NoteChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/NoteChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,20 +31,20 @@ NoteChoice NoteChoice::normalNoteGroup(NormalNoteGroup value) return NoteChoice{Storage{std::in_place_index<2>, std::move(value)}}; } -NoteChoice parseNoteChoice(pugi::xml_node el, pugi::xml_node &cursor) +NoteChoice parseNoteChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "grace"))) { - return NoteChoice::graceNoteGroup(parseGraceNoteGroup(el, cursor)); + return NoteChoice::graceNoteGroup(parseGraceNoteGroup(el, cursor, context)); } if (cursor && (cursorIs(cursor, "cue"))) { - return NoteChoice::cueNoteGroup(parseCueNoteGroup(el, cursor)); + return NoteChoice::cueNoteGroup(parseCueNoteGroup(el, cursor, context)); } if (cursor && (cursorIs(cursor, "chord") || cursorIs(cursor, "pitch") || cursorIs(cursor, "unpitched") || cursorIs(cursor, "rest"))) { - return NoteChoice::normalNoteGroup(parseNormalNoteGroup(el, cursor)); + return NoteChoice::normalNoteGroup(parseNormalNoteGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/NoteChoice.h b/src/private/mx/core/generated/NoteChoice.h index 9b691a501..98d824ccd 100644 --- a/src/private/mx/core/generated/NoteChoice.h +++ b/src/private/mx/core/generated/NoteChoice.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -95,7 +97,7 @@ class NoteChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -NoteChoice parseNoteChoice(pugi::xml_node el, pugi::xml_node &cursor); +NoteChoice parseNoteChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeNoteChoice(const NoteChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/NoteSize.cpp b/src/private/mx/core/generated/NoteSize.cpp index 8f6217a87..7488a507f 100644 --- a/src/private/mx/core/generated/NoteSize.cpp +++ b/src/private/mx/core/generated/NoteSize.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/NoteSize.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void NoteSize::setValue(NonNegativeDecimal value) m_value = std::move(value); } -NoteSize parseNoteSize(pugi::xml_node el) +NoteSize parseNoteSize(pugi::xml_node el, const ParseContext &context) { NoteSize out; bool seen_type = false; @@ -44,7 +45,7 @@ NoteSize parseNoteSize(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(NoteSizeType::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else { @@ -55,13 +56,13 @@ NoteSize parseNoteSize(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseNoteSizeContent(out, el); + parseNoteSizeContent(out, el, context); return out; } -void parseNoteSizeContent(NoteSize &out, pugi::xml_node el) +void parseNoteSizeContent(NoteSize &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(NonNegativeDecimal::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/NoteSize.h b/src/private/mx/core/generated/NoteSize.h index 98a64c497..542b1d9df 100644 --- a/src/private/mx/core/generated/NoteSize.h +++ b/src/private/mx/core/generated/NoteSize.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The note-size type indicates the percentage of the regular note size to use for notes with a cue /// and large size as defined in the type element. The grace type is used for notes of cue size that /// that include a grace element. The cue type is used for all other notes with cue size, whether @@ -36,9 +38,9 @@ class NoteSize final NonNegativeDecimal m_value{}; }; -NoteSize parseNoteSize(pugi::xml_node el); +NoteSize parseNoteSize(pugi::xml_node el, const ParseContext &context); -void parseNoteSizeContent(NoteSize &out, pugi::xml_node el); +void parseNoteSizeContent(NoteSize &out, pugi::xml_node el, const ParseContext &context); void serializeNoteSize(const NoteSize &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/NoteSizeType.cpp b/src/private/mx/core/generated/NoteSizeType.cpp index 76201ab6a..e3e007793 100644 --- a/src/private/mx/core/generated/NoteSizeType.cpp +++ b/src/private/mx/core/generated/NoteSizeType.cpp @@ -54,9 +54,15 @@ bool NoteSizeType::tryParse(std::string_view text, NoteSizeType &out) noexcept } NoteSizeType NoteSizeType::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +NoteSizeType NoteSizeType::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { NoteSizeType v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/NoteSizeType.h b/src/private/mx/core/generated/NoteSizeType.h index 8c9fd8b53..0881973f7 100644 --- a/src/private/mx/core/generated/NoteSizeType.h +++ b/src/private/mx/core/generated/NoteSizeType.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -48,6 +50,9 @@ class NoteSizeType final /// (the import leniency policy; never produces an invalid value). static NoteSizeType parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static NoteSizeType parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const NoteSizeType &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/NoteType.cpp b/src/private/mx/core/generated/NoteType.cpp index 8cca5228a..342e737d5 100644 --- a/src/private/mx/core/generated/NoteType.cpp +++ b/src/private/mx/core/generated/NoteType.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/NoteType.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void NoteType::setValue(NoteTypeValue value) m_value = std::move(value); } -NoteType parseNoteType(pugi::xml_node el) +NoteType parseNoteType(pugi::xml_node el, const ParseContext &context) { NoteType out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ NoteType parseNoteType(pugi::xml_node el) } if (aname == "size") { - out.setSize(SymbolSize::parse(a.value())); + out.setSize(parseValue(a.value(), context, el, "size")); } else { throwUnknownAttribute(el, a.name()); } } - parseNoteTypeContent(out, el); + parseNoteTypeContent(out, el, context); return out; } -void parseNoteTypeContent(NoteType &out, pugi::xml_node el) +void parseNoteTypeContent(NoteType &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(NoteTypeValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/NoteType.h b/src/private/mx/core/generated/NoteType.h index c169cc106..065f4452b 100644 --- a/src/private/mx/core/generated/NoteType.h +++ b/src/private/mx/core/generated/NoteType.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The note-type type indicates the graphic note type. Values range from 1024th to maxima. The size /// attribute indicates full, cue, grace-cue, or large size. The default is full for regular notes, /// grace-cue for notes that contain both grace and cue elements, and cue for notes that contain @@ -34,9 +36,9 @@ class NoteType final NoteTypeValue m_value{}; }; -NoteType parseNoteType(pugi::xml_node el); +NoteType parseNoteType(pugi::xml_node el, const ParseContext &context); -void parseNoteTypeContent(NoteType &out, pugi::xml_node el); +void parseNoteTypeContent(NoteType &out, pugi::xml_node el, const ParseContext &context); void serializeNoteType(const NoteType &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/NoteTypeValue.cpp b/src/private/mx/core/generated/NoteTypeValue.cpp index a38615d1a..0dbcb9455 100644 --- a/src/private/mx/core/generated/NoteTypeValue.cpp +++ b/src/private/mx/core/generated/NoteTypeValue.cpp @@ -102,9 +102,15 @@ bool NoteTypeValue::tryParse(std::string_view text, NoteTypeValue &out) noexcept } NoteTypeValue NoteTypeValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +NoteTypeValue NoteTypeValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { NoteTypeValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/NoteTypeValue.h b/src/private/mx/core/generated/NoteTypeValue.h index 0bc2b815e..58a08f3cf 100644 --- a/src/private/mx/core/generated/NoteTypeValue.h +++ b/src/private/mx/core/generated/NoteTypeValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -65,6 +67,9 @@ class NoteTypeValue final /// (the import leniency policy; never produces an invalid value). static NoteTypeValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static NoteTypeValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const NoteTypeValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Notehead.cpp b/src/private/mx/core/generated/Notehead.cpp index 696687d67..67bd12a65 100644 --- a/src/private/mx/core/generated/Notehead.cpp +++ b/src/private/mx/core/generated/Notehead.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Notehead.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -100,7 +101,7 @@ void Notehead::setValue(NoteheadValue value) m_value = std::move(value); } -Notehead parseNotehead(pugi::xml_node el) +Notehead parseNotehead(pugi::xml_node el, const ParseContext &context) { Notehead out; for (pugi::xml_attribute a : el.attributes()) @@ -112,48 +113,48 @@ Notehead parseNotehead(pugi::xml_node el) } if (aname == "filled") { - out.setFilled(YesNo::parse(a.value())); + out.setFilled(parseValue(a.value(), context, el, "filled")); } else if (aname == "parentheses") { - out.setParentheses(YesNo::parse(a.value())); + out.setParentheses(parseValue(a.value(), context, el, "parentheses")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "smufl") { - out.setSmufl(SmuflGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else { throwUnknownAttribute(el, a.name()); } } - parseNoteheadContent(out, el); + parseNoteheadContent(out, el, context); return out; } -void parseNoteheadContent(Notehead &out, pugi::xml_node el) +void parseNoteheadContent(Notehead &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(NoteheadValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Notehead.h b/src/private/mx/core/generated/Notehead.h index 535cdc42b..ce748b29d 100644 --- a/src/private/mx/core/generated/Notehead.h +++ b/src/private/mx/core/generated/Notehead.h @@ -22,6 +22,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The notehead type indicates shapes other than the open and closed ovals associated with note /// durations. The smufl attribute can be used to specify a particular notehead, allowing application /// interoperability without requiring every SMuFL glyph to have a MusicXML element equivalent. This @@ -67,9 +69,9 @@ class Notehead final NoteheadValue m_value{}; }; -Notehead parseNotehead(pugi::xml_node el); +Notehead parseNotehead(pugi::xml_node el, const ParseContext &context); -void parseNoteheadContent(Notehead &out, pugi::xml_node el); +void parseNoteheadContent(Notehead &out, pugi::xml_node el, const ParseContext &context); void serializeNotehead(const Notehead &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/NoteheadText.cpp b/src/private/mx/core/generated/NoteheadText.cpp index fbe8aef3f..6e585d89e 100644 --- a/src/private/mx/core/generated/NoteheadText.cpp +++ b/src/private/mx/core/generated/NoteheadText.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/NoteheadText.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,7 +26,7 @@ void NoteheadText::setChoice(OneOrMore value) m_choice = std::move(value); } -NoteheadText parseNoteheadText(pugi::xml_node el) +NoteheadText parseNoteheadText(pugi::xml_node el, const ParseContext &context) { NoteheadText out; for (pugi::xml_attribute a : el.attributes()) @@ -37,21 +38,21 @@ NoteheadText parseNoteheadText(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseNoteheadTextContent(out, el); + parseNoteheadTextContent(out, el, context); return out; } -void parseNoteheadTextContent(NoteheadText &out, pugi::xml_node el) +void parseNoteheadTextContent(NoteheadText &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (!(cursor && (cursorIs(cursor, "display-text") || cursorIs(cursor, "accidental-text")))) { throwMissingElement(el, "display-text"); } - out.setChoice(OneOrMore{parseNoteheadTextChoice(el, cursor)}); + out.setChoice(OneOrMore{parseNoteheadTextChoice(el, cursor, context)}); while (cursor && (cursorIs(cursor, "display-text") || cursorIs(cursor, "accidental-text"))) { - out.addChoice(parseNoteheadTextChoice(el, cursor)); + out.addChoice(parseNoteheadTextChoice(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/NoteheadText.h b/src/private/mx/core/generated/NoteheadText.h index e7e36ddc0..da763dc61 100644 --- a/src/private/mx/core/generated/NoteheadText.h +++ b/src/private/mx/core/generated/NoteheadText.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The notehead-text type represents text that is displayed inside a notehead, as is done in some /// educational music. It is not needed for the numbers used in tablature or jianpu notation. The /// presence of a TAB or jianpu clefs is sufficient to indicate that numbers are used. The @@ -36,9 +38,9 @@ class NoteheadText final OneOrMore m_choice; }; -NoteheadText parseNoteheadText(pugi::xml_node el); +NoteheadText parseNoteheadText(pugi::xml_node el, const ParseContext &context); -void parseNoteheadTextContent(NoteheadText &out, pugi::xml_node el); +void parseNoteheadTextContent(NoteheadText &out, pugi::xml_node el, const ParseContext &context); void serializeNoteheadText(const NoteheadText &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/NoteheadTextChoice.cpp b/src/private/mx/core/generated/NoteheadTextChoice.cpp index 4949e4a3f..41860403a 100644 --- a/src/private/mx/core/generated/NoteheadTextChoice.cpp +++ b/src/private/mx/core/generated/NoteheadTextChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/NoteheadTextChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,17 +26,17 @@ NoteheadTextChoice NoteheadTextChoice::accidentalText(AccidentalText value) return NoteheadTextChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -NoteheadTextChoice parseNoteheadTextChoice(pugi::xml_node el, pugi::xml_node &cursor) +NoteheadTextChoice parseNoteheadTextChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "display-text"))) { - FormattedText value = parseFormattedText(cursor); + FormattedText value = parseFormattedText(cursor, context); cursor = nextElement(cursor); return NoteheadTextChoice::displayText(std::move(value)); } if (cursor && (cursorIs(cursor, "accidental-text"))) { - AccidentalText value = parseAccidentalText(cursor); + AccidentalText value = parseAccidentalText(cursor, context); cursor = nextElement(cursor); return NoteheadTextChoice::accidentalText(std::move(value)); } diff --git a/src/private/mx/core/generated/NoteheadTextChoice.h b/src/private/mx/core/generated/NoteheadTextChoice.h index 2572c5d93..d7e44e641 100644 --- a/src/private/mx/core/generated/NoteheadTextChoice.h +++ b/src/private/mx/core/generated/NoteheadTextChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -80,7 +82,7 @@ class NoteheadTextChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -NoteheadTextChoice parseNoteheadTextChoice(pugi::xml_node el, pugi::xml_node &cursor); +NoteheadTextChoice parseNoteheadTextChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeNoteheadTextChoice(const NoteheadTextChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/NoteheadValue.cpp b/src/private/mx/core/generated/NoteheadValue.cpp index e46e5e08f..9489caafd 100644 --- a/src/private/mx/core/generated/NoteheadValue.cpp +++ b/src/private/mx/core/generated/NoteheadValue.cpp @@ -198,9 +198,15 @@ bool NoteheadValue::tryParse(std::string_view text, NoteheadValue &out) noexcept } NoteheadValue NoteheadValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +NoteheadValue NoteheadValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { NoteheadValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/NoteheadValue.h b/src/private/mx/core/generated/NoteheadValue.h index a339915c8..10bf12036 100644 --- a/src/private/mx/core/generated/NoteheadValue.h +++ b/src/private/mx/core/generated/NoteheadValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -104,6 +106,9 @@ class NoteheadValue final /// (the import leniency policy; never produces an invalid value). static NoteheadValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static NoteheadValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const NoteheadValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/NumberLevel.cpp b/src/private/mx/core/generated/NumberLevel.cpp index 8318801f6..0f3b3af3d 100644 --- a/src/private/mx/core/generated/NumberLevel.cpp +++ b/src/private/mx/core/generated/NumberLevel.cpp @@ -42,20 +42,33 @@ std::string NumberLevel::toString() const bool NumberLevel::tryParse(std::string_view text, NumberLevel &out) { - int v{}; - if (!tryParseInt(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + NumberLevel parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = NumberLevel{v}; + out = std::move(parsed); return true; } NumberLevel NumberLevel::parse(std::string_view text) { - NumberLevel v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +NumberLevel NumberLevel::parse(std::string_view text, ValueParseOutcome &outcome) +{ + int v{}; + if (!tryParseInt(text, v)) + { + outcome = ValueParseOutcome::invalid; + return NumberLevel{}; + } + NumberLevel out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/NumberLevel.h b/src/private/mx/core/generated/NumberLevel.h index e8cbd3def..591146bb2 100644 --- a/src/private/mx/core/generated/NumberLevel.h +++ b/src/private/mx/core/generated/NumberLevel.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -58,6 +60,9 @@ class NumberLevel final /// Lenient: non-numeric text yields the clamped zero. static NumberLevel parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static NumberLevel parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const NumberLevel &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/NumberOfLines.cpp b/src/private/mx/core/generated/NumberOfLines.cpp index a270f127a..f755cd608 100644 --- a/src/private/mx/core/generated/NumberOfLines.cpp +++ b/src/private/mx/core/generated/NumberOfLines.cpp @@ -42,20 +42,33 @@ std::string NumberOfLines::toString() const bool NumberOfLines::tryParse(std::string_view text, NumberOfLines &out) { - int v{}; - if (!tryParseInt(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + NumberOfLines parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = NumberOfLines{v}; + out = std::move(parsed); return true; } NumberOfLines NumberOfLines::parse(std::string_view text) { - NumberOfLines v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +NumberOfLines NumberOfLines::parse(std::string_view text, ValueParseOutcome &outcome) +{ + int v{}; + if (!tryParseInt(text, v)) + { + outcome = ValueParseOutcome::invalid; + return NumberOfLines{}; + } + NumberOfLines out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/NumberOfLines.h b/src/private/mx/core/generated/NumberOfLines.h index 76eb02a19..7e2b3e288 100644 --- a/src/private/mx/core/generated/NumberOfLines.h +++ b/src/private/mx/core/generated/NumberOfLines.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -35,6 +37,9 @@ class NumberOfLines final /// Lenient: non-numeric text yields the clamped zero. static NumberOfLines parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static NumberOfLines parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const NumberOfLines &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/NumberOrNormal.cpp b/src/private/mx/core/generated/NumberOrNormal.cpp index 76898e6d1..2b1e75bc1 100644 --- a/src/private/mx/core/generated/NumberOrNormal.cpp +++ b/src/private/mx/core/generated/NumberOrNormal.cpp @@ -56,12 +56,20 @@ bool NumberOrNormal::tryParse(std::string_view text, NumberOrNormal &out) } NumberOrNormal NumberOrNormal::parse(std::string_view text) +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +NumberOrNormal NumberOrNormal::parse(std::string_view text, ValueParseOutcome &outcome) { NumberOrNormal out; if (tryParse(text, out)) { + outcome = ValueParseOutcome::valid; return out; } + outcome = ValueParseOutcome::invalid; return NumberOrNormal::decimal(Decimal::parse(text)); } diff --git a/src/private/mx/core/generated/NumberOrNormal.h b/src/private/mx/core/generated/NumberOrNormal.h index 9688922c6..c733792a4 100644 --- a/src/private/mx/core/generated/NumberOrNormal.h +++ b/src/private/mx/core/generated/NumberOrNormal.h @@ -3,6 +3,7 @@ #pragma once #include "mx/core/Decimal.h" +#include "mx/core/Lexical.h" #include #include @@ -68,6 +69,10 @@ class NumberOrNormal final /// parse (never produces an invalid value). static NumberOrNormal parse(std::string_view text); + /// Lenient, and says whether no member matched or a matched number was + /// clamped. + static NumberOrNormal parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const NumberOrNormal &other) const = default; private: diff --git a/src/private/mx/core/generated/Numeral.cpp b/src/private/mx/core/generated/Numeral.cpp index 2ad954235..3d9fb5df0 100644 --- a/src/private/mx/core/generated/Numeral.cpp +++ b/src/private/mx/core/generated/Numeral.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Numeral.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,7 +41,7 @@ void Numeral::setNumeralKey(std::optional value) m_numeralKey = std::move(value); } -Numeral parseNumeral(pugi::xml_node el) +Numeral parseNumeral(pugi::xml_node el, const ParseContext &context) { Numeral out; for (pugi::xml_attribute a : el.attributes()) @@ -52,16 +53,16 @@ Numeral parseNumeral(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseNumeralContent(out, el); + parseNumeralContent(out, el, context); return out; } -void parseNumeralContent(Numeral &out, pugi::xml_node el) +void parseNumeralContent(Numeral &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "numeral-root")) { - out.setNumeralRoot(parseNumeralRoot(cursor)); + out.setNumeralRoot(parseNumeralRoot(cursor, context)); cursor = nextElement(cursor); } else @@ -70,12 +71,12 @@ void parseNumeralContent(Numeral &out, pugi::xml_node el) } if (cursorIs(cursor, "numeral-alter")) { - out.setNumeralAlter(parseHarmonyAlter(cursor)); + out.setNumeralAlter(parseHarmonyAlter(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "numeral-key")) { - out.setNumeralKey(parseNumeralKey(cursor)); + out.setNumeralKey(parseNumeralKey(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Numeral.h b/src/private/mx/core/generated/Numeral.h index c13334ab2..27895a0a1 100644 --- a/src/private/mx/core/generated/Numeral.h +++ b/src/private/mx/core/generated/Numeral.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The numeral type represents the Roman numeral or Nashville number part of a harmony. It requires /// that the key be specified in the encoding, either with a key or numeral-key element. /// Content fields mirror the schema grammar in declaration order; the @@ -40,9 +42,9 @@ class Numeral final std::optional m_numeralKey; }; -Numeral parseNumeral(pugi::xml_node el); +Numeral parseNumeral(pugi::xml_node el, const ParseContext &context); -void parseNumeralContent(Numeral &out, pugi::xml_node el); +void parseNumeralContent(Numeral &out, pugi::xml_node el, const ParseContext &context); void serializeNumeral(const Numeral &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/NumeralKey.cpp b/src/private/mx/core/generated/NumeralKey.cpp index 99a680947..1d00d400c 100644 --- a/src/private/mx/core/generated/NumeralKey.cpp +++ b/src/private/mx/core/generated/NumeralKey.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/NumeralKey.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,7 +41,7 @@ void NumeralKey::setNumeralMode(NumeralMode value) m_numeralMode = std::move(value); } -NumeralKey parseNumeralKey(pugi::xml_node el) +NumeralKey parseNumeralKey(pugi::xml_node el, const ParseContext &context) { NumeralKey out; for (pugi::xml_attribute a : el.attributes()) @@ -52,23 +53,23 @@ NumeralKey parseNumeralKey(pugi::xml_node el) } if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else { throwUnknownAttribute(el, a.name()); } } - parseNumeralKeyContent(out, el); + parseNumeralKeyContent(out, el, context); return out; } -void parseNumeralKeyContent(NumeralKey &out, pugi::xml_node el) +void parseNumeralKeyContent(NumeralKey &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "numeral-fifths")) { - out.setNumeralFifths(Fifths::parse(childText(cursor))); + out.setNumeralFifths(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -77,7 +78,7 @@ void parseNumeralKeyContent(NumeralKey &out, pugi::xml_node el) } if (cursorIs(cursor, "numeral-mode")) { - out.setNumeralMode(NumeralMode::parse(childText(cursor))); + out.setNumeralMode(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/NumeralKey.h b/src/private/mx/core/generated/NumeralKey.h index 758bd6076..8751da2da 100644 --- a/src/private/mx/core/generated/NumeralKey.h +++ b/src/private/mx/core/generated/NumeralKey.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The numeral-key type is used when the key for the numeral is different than the key specified by /// the key signature. The numeral-fifths element specifies the key in the same way as the fifths /// element. The numeral-mode element specifies the mode similar to the mode element, but with a @@ -42,9 +44,9 @@ class NumeralKey final NumeralMode m_numeralMode{}; }; -NumeralKey parseNumeralKey(pugi::xml_node el); +NumeralKey parseNumeralKey(pugi::xml_node el, const ParseContext &context); -void parseNumeralKeyContent(NumeralKey &out, pugi::xml_node el); +void parseNumeralKeyContent(NumeralKey &out, pugi::xml_node el, const ParseContext &context); void serializeNumeralKey(const NumeralKey &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/NumeralMode.cpp b/src/private/mx/core/generated/NumeralMode.cpp index be73dabda..ed5c508f2 100644 --- a/src/private/mx/core/generated/NumeralMode.cpp +++ b/src/private/mx/core/generated/NumeralMode.cpp @@ -56,9 +56,15 @@ bool NumeralMode::tryParse(std::string_view text, NumeralMode &out) noexcept } NumeralMode NumeralMode::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +NumeralMode NumeralMode::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { NumeralMode v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/NumeralMode.h b/src/private/mx/core/generated/NumeralMode.h index 7d2179736..ad7e3b7d5 100644 --- a/src/private/mx/core/generated/NumeralMode.h +++ b/src/private/mx/core/generated/NumeralMode.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -50,6 +52,9 @@ class NumeralMode final /// (the import leniency policy; never produces an invalid value). static NumeralMode parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static NumeralMode parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const NumeralMode &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/NumeralRoot.cpp b/src/private/mx/core/generated/NumeralRoot.cpp index d95605add..66a291176 100644 --- a/src/private/mx/core/generated/NumeralRoot.cpp +++ b/src/private/mx/core/generated/NumeralRoot.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/NumeralRoot.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -120,7 +121,7 @@ void NumeralRoot::setValue(NumeralValue value) m_value = std::move(value); } -NumeralRoot parseNumeralRoot(pugi::xml_node el) +NumeralRoot parseNumeralRoot(pugi::xml_node el, const ParseContext &context) { NumeralRoot out; for (pugi::xml_attribute a : el.attributes()) @@ -136,52 +137,52 @@ NumeralRoot parseNumeralRoot(pugi::xml_node el) } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseNumeralRootContent(out, el); + parseNumeralRootContent(out, el, context); return out; } -void parseNumeralRootContent(NumeralRoot &out, pugi::xml_node el) +void parseNumeralRootContent(NumeralRoot &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(NumeralValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/NumeralRoot.h b/src/private/mx/core/generated/NumeralRoot.h index 517860baf..15ae787fb 100644 --- a/src/private/mx/core/generated/NumeralRoot.h +++ b/src/private/mx/core/generated/NumeralRoot.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The numeral-root type represents the Roman numeral or Nashville number as a positive integer from /// 1 to 7. The text attribute indicates how the numeral should appear in the score. A numeral-root /// value of 5 with a kind of major would have a text attribute of "V" if displayed as a Roman @@ -67,9 +69,9 @@ class NumeralRoot final NumeralValue m_value{}; }; -NumeralRoot parseNumeralRoot(pugi::xml_node el); +NumeralRoot parseNumeralRoot(pugi::xml_node el, const ParseContext &context); -void parseNumeralRootContent(NumeralRoot &out, pugi::xml_node el); +void parseNumeralRootContent(NumeralRoot &out, pugi::xml_node el, const ParseContext &context); void serializeNumeralRoot(const NumeralRoot &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/NumeralValue.cpp b/src/private/mx/core/generated/NumeralValue.cpp index 2355ff18c..0e53f0ff1 100644 --- a/src/private/mx/core/generated/NumeralValue.cpp +++ b/src/private/mx/core/generated/NumeralValue.cpp @@ -42,20 +42,33 @@ std::string NumeralValue::toString() const bool NumeralValue::tryParse(std::string_view text, NumeralValue &out) { - int v{}; - if (!tryParseInt(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + NumeralValue parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = NumeralValue{v}; + out = std::move(parsed); return true; } NumeralValue NumeralValue::parse(std::string_view text) { - NumeralValue v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +NumeralValue NumeralValue::parse(std::string_view text, ValueParseOutcome &outcome) +{ + int v{}; + if (!tryParseInt(text, v)) + { + outcome = ValueParseOutcome::invalid; + return NumeralValue{}; + } + NumeralValue out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/NumeralValue.h b/src/private/mx/core/generated/NumeralValue.h index 177e12142..cb50396e8 100644 --- a/src/private/mx/core/generated/NumeralValue.h +++ b/src/private/mx/core/generated/NumeralValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -36,6 +38,9 @@ class NumeralValue final /// Lenient: non-numeric text yields the clamped zero. static NumeralValue parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static NumeralValue parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const NumeralValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Octave.cpp b/src/private/mx/core/generated/Octave.cpp index 2a6b34fd5..e7ed36e31 100644 --- a/src/private/mx/core/generated/Octave.cpp +++ b/src/private/mx/core/generated/Octave.cpp @@ -42,20 +42,33 @@ std::string Octave::toString() const bool Octave::tryParse(std::string_view text, Octave &out) { - int v{}; - if (!tryParseInt(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + Octave parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = Octave{v}; + out = std::move(parsed); return true; } Octave Octave::parse(std::string_view text) { - Octave v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +Octave Octave::parse(std::string_view text, ValueParseOutcome &outcome) +{ + int v{}; + if (!tryParseInt(text, v)) + { + outcome = ValueParseOutcome::invalid; + return Octave{}; + } + Octave out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/Octave.h b/src/private/mx/core/generated/Octave.h index 6606a1388..e4cca6fcd 100644 --- a/src/private/mx/core/generated/Octave.h +++ b/src/private/mx/core/generated/Octave.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -35,6 +37,9 @@ class Octave final /// Lenient: non-numeric text yields the clamped zero. static Octave parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static Octave parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const Octave &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/OctaveShift.cpp b/src/private/mx/core/generated/OctaveShift.cpp index 30328f9c6..3fe716707 100644 --- a/src/private/mx/core/generated/OctaveShift.cpp +++ b/src/private/mx/core/generated/OctaveShift.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/OctaveShift.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -160,7 +161,7 @@ void OctaveShift::setID(std::optional value) m_id = std::move(value); } -OctaveShift parseOctaveShift(pugi::xml_node el) +OctaveShift parseOctaveShift(pugi::xml_node el, const ParseContext &context) { OctaveShift out; bool seen_type = false; @@ -174,63 +175,63 @@ OctaveShift parseOctaveShift(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(UpDownStopContinue::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "number") { - out.setNumber(NumberLevel::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "size") { - out.setSize(parseInt(a.value())); + out.setSize(parseIntegerValue(a.value(), context, el, "size")); } else if (aname == "dash-length") { - out.setDashLength(Tenths::parse(a.value())); + out.setDashLength(parseValue(a.value(), context, el, "dash-length")); } else if (aname == "space-length") { - out.setSpaceLength(Tenths::parse(a.value())); + out.setSpaceLength(parseValue(a.value(), context, el, "space-length")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -241,13 +242,14 @@ OctaveShift parseOctaveShift(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseOctaveShiftContent(out, el); + parseOctaveShiftContent(out, el, context); return out; } -void parseOctaveShiftContent(OctaveShift &out, pugi::xml_node el) +void parseOctaveShiftContent(OctaveShift &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/OctaveShift.h b/src/private/mx/core/generated/OctaveShift.h index ad513dae9..ab876f5e0 100644 --- a/src/private/mx/core/generated/OctaveShift.h +++ b/src/private/mx/core/generated/OctaveShift.h @@ -24,6 +24,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The octave shift type indicates where notes are shifted up or down from their true pitched values /// because of printing difficulty. Thus a treble clef line noted with 8va will be indicated with an /// octave-shift down from the pitch data indicated in the notes. A size of 8 indicates one octave; a @@ -81,9 +83,9 @@ class OctaveShift final std::optional m_id; }; -OctaveShift parseOctaveShift(pugi::xml_node el); +OctaveShift parseOctaveShift(pugi::xml_node el, const ParseContext &context); -void parseOctaveShiftContent(OctaveShift &out, pugi::xml_node el); +void parseOctaveShiftContent(OctaveShift &out, pugi::xml_node el, const ParseContext &context); void serializeOctaveShift(const OctaveShift &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Offset.cpp b/src/private/mx/core/generated/Offset.cpp index 1293993c5..3c6652bf7 100644 --- a/src/private/mx/core/generated/Offset.cpp +++ b/src/private/mx/core/generated/Offset.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Offset.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Offset::setValue(Divisions value) m_value = std::move(value); } -Offset parseOffset(pugi::xml_node el) +Offset parseOffset(pugi::xml_node el, const ParseContext &context) { Offset out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ Offset parseOffset(pugi::xml_node el) } if (aname == "sound") { - out.setSound(YesNo::parse(a.value())); + out.setSound(parseValue(a.value(), context, el, "sound")); } else { throwUnknownAttribute(el, a.name()); } } - parseOffsetContent(out, el); + parseOffsetContent(out, el, context); return out; } -void parseOffsetContent(Offset &out, pugi::xml_node el) +void parseOffsetContent(Offset &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(Divisions::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Offset.h b/src/private/mx/core/generated/Offset.h index 9e2ae68b7..d7714e79b 100644 --- a/src/private/mx/core/generated/Offset.h +++ b/src/private/mx/core/generated/Offset.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// An offset is represented in terms of divisions, and indicates where the direction will appear /// relative to the current musical location. The current musical location is always within the /// current measure, even at the end of a measure. The offset affects the visual appearance of the @@ -38,9 +40,9 @@ class Offset final Divisions m_value{}; }; -Offset parseOffset(pugi::xml_node el); +Offset parseOffset(pugi::xml_node el, const ParseContext &context); -void parseOffsetContent(Offset &out, pugi::xml_node el); +void parseOffsetContent(Offset &out, pugi::xml_node el, const ParseContext &context); void serializeOffset(const Offset &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/OnOff.cpp b/src/private/mx/core/generated/OnOff.cpp index ba5d4f34a..43715da20 100644 --- a/src/private/mx/core/generated/OnOff.cpp +++ b/src/private/mx/core/generated/OnOff.cpp @@ -42,9 +42,15 @@ bool OnOff::tryParse(std::string_view text, OnOff &out) noexcept } OnOff OnOff::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +OnOff OnOff::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { OnOff v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/OnOff.h b/src/private/mx/core/generated/OnOff.h index 0e3703fbd..e9ddcdf29 100644 --- a/src/private/mx/core/generated/OnOff.h +++ b/src/private/mx/core/generated/OnOff.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -40,6 +42,9 @@ class OnOff final /// (the import leniency policy; never produces an invalid value). static OnOff parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static OnOff parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const OnOff &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Opus.cpp b/src/private/mx/core/generated/Opus.cpp index 93223f5e5..2f331312e 100644 --- a/src/private/mx/core/generated/Opus.cpp +++ b/src/private/mx/core/generated/Opus.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Opus.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -70,7 +71,7 @@ void Opus::setXlinkActuate(std::optional value) m_xlinkActuate = std::move(value); } -Opus parseOpus(pugi::xml_node el) +Opus parseOpus(pugi::xml_node el, const ParseContext &context) { Opus out; bool seen_xlinkHref = false; @@ -115,13 +116,14 @@ Opus parseOpus(pugi::xml_node el) { throwMissingAttribute(el, "xlink:href"); } - parseOpusContent(out, el); + parseOpusContent(out, el, context); return out; } -void parseOpusContent(Opus &out, pugi::xml_node el) +void parseOpusContent(Opus &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Opus.h b/src/private/mx/core/generated/Opus.h index 56810dde6..f0d909290 100644 --- a/src/private/mx/core/generated/Opus.h +++ b/src/private/mx/core/generated/Opus.h @@ -13,6 +13,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The opus type represents a link to a MusicXML opus document that composes multiple MusicXML /// scores into a collection. class Opus final @@ -41,9 +43,9 @@ class Opus final std::optional m_xlinkActuate; }; -Opus parseOpus(pugi::xml_node el); +Opus parseOpus(pugi::xml_node el, const ParseContext &context); -void parseOpusContent(Opus &out, pugi::xml_node el); +void parseOpusContent(Opus &out, pugi::xml_node el, const ParseContext &context); void serializeOpus(const Opus &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Ornaments.cpp b/src/private/mx/core/generated/Ornaments.cpp index 7c9626be7..133a7f00e 100644 --- a/src/private/mx/core/generated/Ornaments.cpp +++ b/src/private/mx/core/generated/Ornaments.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Ornaments.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,7 +36,7 @@ void Ornaments::setGroup(std::vector value) m_group = std::move(value); } -Ornaments parseOrnaments(pugi::xml_node el) +Ornaments parseOrnaments(pugi::xml_node el, const ParseContext &context) { Ornaments out; for (pugi::xml_attribute a : el.attributes()) @@ -47,18 +48,18 @@ Ornaments parseOrnaments(pugi::xml_node el) } if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseOrnamentsContent(out, el); + parseOrnamentsContent(out, el, context); return out; } -void parseOrnamentsContent(Ornaments &out, pugi::xml_node el) +void parseOrnamentsContent(Ornaments &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); while (cursor && (cursorIs(cursor, "trill-mark") || cursorIs(cursor, "turn") || cursorIs(cursor, "delayed-turn") || @@ -68,7 +69,7 @@ void parseOrnamentsContent(Ornaments &out, pugi::xml_node el) cursorIs(cursor, "inverted-mordent") || cursorIs(cursor, "schleifer") || cursorIs(cursor, "tremolo") || cursorIs(cursor, "haydn") || cursorIs(cursor, "other-ornament"))) { - out.addGroup(parseOrnamentsGroup(el, cursor)); + out.addGroup(parseOrnamentsGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/Ornaments.h b/src/private/mx/core/generated/Ornaments.h index 759df1242..2dc2aad7d 100644 --- a/src/private/mx/core/generated/Ornaments.h +++ b/src/private/mx/core/generated/Ornaments.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Ornaments can be any of several types, followed optionally by accidentals. The accidental-mark /// element's content is represented the same as an accidental element, but with a different name to /// reflect the different musical meaning. @@ -39,9 +41,9 @@ class Ornaments final std::vector m_group; }; -Ornaments parseOrnaments(pugi::xml_node el); +Ornaments parseOrnaments(pugi::xml_node el, const ParseContext &context); -void parseOrnamentsContent(Ornaments &out, pugi::xml_node el); +void parseOrnamentsContent(Ornaments &out, pugi::xml_node el, const ParseContext &context); void serializeOrnaments(const Ornaments &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/OrnamentsGroup.cpp b/src/private/mx/core/generated/OrnamentsGroup.cpp index 28dc41701..4109d63c1 100644 --- a/src/private/mx/core/generated/OrnamentsGroup.cpp +++ b/src/private/mx/core/generated/OrnamentsGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/OrnamentsGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,7 +36,7 @@ void OrnamentsGroup::setAccidentalMark(std::vector value) m_accidentalMark = std::move(value); } -OrnamentsGroup parseOrnamentsGroup(pugi::xml_node el, pugi::xml_node &cursor) +OrnamentsGroup parseOrnamentsGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { OrnamentsGroup out; if (cursor && (cursorIs(cursor, "trill-mark") || cursorIs(cursor, "turn") || cursorIs(cursor, "delayed-turn") || @@ -45,7 +46,7 @@ OrnamentsGroup parseOrnamentsGroup(pugi::xml_node el, pugi::xml_node &cursor) cursorIs(cursor, "inverted-mordent") || cursorIs(cursor, "schleifer") || cursorIs(cursor, "tremolo") || cursorIs(cursor, "haydn") || cursorIs(cursor, "other-ornament"))) { - out.setChoice(parseOrnamentsGroupChoice(el, cursor)); + out.setChoice(parseOrnamentsGroupChoice(el, cursor, context)); } else { @@ -53,7 +54,7 @@ OrnamentsGroup parseOrnamentsGroup(pugi::xml_node el, pugi::xml_node &cursor) } while (cursorIs(cursor, "accidental-mark")) { - out.addAccidentalMark(parseAccidentalMark(cursor)); + out.addAccidentalMark(parseAccidentalMark(cursor, context)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/OrnamentsGroup.h b/src/private/mx/core/generated/OrnamentsGroup.h index d1b3dc2dd..143cd90c7 100644 --- a/src/private/mx/core/generated/OrnamentsGroup.h +++ b/src/private/mx/core/generated/OrnamentsGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -38,7 +40,7 @@ class OrnamentsGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -OrnamentsGroup parseOrnamentsGroup(pugi::xml_node el, pugi::xml_node &cursor); +OrnamentsGroup parseOrnamentsGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeOrnamentsGroup(const OrnamentsGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/OrnamentsGroupChoice.cpp b/src/private/mx/core/generated/OrnamentsGroupChoice.cpp index 4a2729824..0aad7046e 100644 --- a/src/private/mx/core/generated/OrnamentsGroupChoice.cpp +++ b/src/private/mx/core/generated/OrnamentsGroupChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/OrnamentsGroupChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -90,95 +91,95 @@ OrnamentsGroupChoice OrnamentsGroupChoice::otherOrnament(OtherPlacementText valu return OrnamentsGroupChoice{Storage{std::in_place_index<14>, std::move(value)}}; } -OrnamentsGroupChoice parseOrnamentsGroupChoice(pugi::xml_node el, pugi::xml_node &cursor) +OrnamentsGroupChoice parseOrnamentsGroupChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "trill-mark"))) { - EmptyTrillSound value = parseEmptyTrillSound(cursor); + EmptyTrillSound value = parseEmptyTrillSound(cursor, context); cursor = nextElement(cursor); return OrnamentsGroupChoice::trillMark(std::move(value)); } if (cursor && (cursorIs(cursor, "turn"))) { - HorizontalTurn value = parseHorizontalTurn(cursor); + HorizontalTurn value = parseHorizontalTurn(cursor, context); cursor = nextElement(cursor); return OrnamentsGroupChoice::turn(std::move(value)); } if (cursor && (cursorIs(cursor, "delayed-turn"))) { - HorizontalTurn value = parseHorizontalTurn(cursor); + HorizontalTurn value = parseHorizontalTurn(cursor, context); cursor = nextElement(cursor); return OrnamentsGroupChoice::delayedTurn(std::move(value)); } if (cursor && (cursorIs(cursor, "inverted-turn"))) { - HorizontalTurn value = parseHorizontalTurn(cursor); + HorizontalTurn value = parseHorizontalTurn(cursor, context); cursor = nextElement(cursor); return OrnamentsGroupChoice::invertedTurn(std::move(value)); } if (cursor && (cursorIs(cursor, "delayed-inverted-turn"))) { - HorizontalTurn value = parseHorizontalTurn(cursor); + HorizontalTurn value = parseHorizontalTurn(cursor, context); cursor = nextElement(cursor); return OrnamentsGroupChoice::delayedInvertedTurn(std::move(value)); } if (cursor && (cursorIs(cursor, "vertical-turn"))) { - EmptyTrillSound value = parseEmptyTrillSound(cursor); + EmptyTrillSound value = parseEmptyTrillSound(cursor, context); cursor = nextElement(cursor); return OrnamentsGroupChoice::verticalTurn(std::move(value)); } if (cursor && (cursorIs(cursor, "inverted-vertical-turn"))) { - EmptyTrillSound value = parseEmptyTrillSound(cursor); + EmptyTrillSound value = parseEmptyTrillSound(cursor, context); cursor = nextElement(cursor); return OrnamentsGroupChoice::invertedVerticalTurn(std::move(value)); } if (cursor && (cursorIs(cursor, "shake"))) { - EmptyTrillSound value = parseEmptyTrillSound(cursor); + EmptyTrillSound value = parseEmptyTrillSound(cursor, context); cursor = nextElement(cursor); return OrnamentsGroupChoice::shake(std::move(value)); } if (cursor && (cursorIs(cursor, "wavy-line"))) { - WavyLine value = parseWavyLine(cursor); + WavyLine value = parseWavyLine(cursor, context); cursor = nextElement(cursor); return OrnamentsGroupChoice::wavyLine(std::move(value)); } if (cursor && (cursorIs(cursor, "mordent"))) { - Mordent value = parseMordent(cursor); + Mordent value = parseMordent(cursor, context); cursor = nextElement(cursor); return OrnamentsGroupChoice::mordent(std::move(value)); } if (cursor && (cursorIs(cursor, "inverted-mordent"))) { - Mordent value = parseMordent(cursor); + Mordent value = parseMordent(cursor, context); cursor = nextElement(cursor); return OrnamentsGroupChoice::invertedMordent(std::move(value)); } if (cursor && (cursorIs(cursor, "schleifer"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return OrnamentsGroupChoice::schleifer(std::move(value)); } if (cursor && (cursorIs(cursor, "tremolo"))) { - Tremolo value = parseTremolo(cursor); + Tremolo value = parseTremolo(cursor, context); cursor = nextElement(cursor); return OrnamentsGroupChoice::tremolo(std::move(value)); } if (cursor && (cursorIs(cursor, "haydn"))) { - EmptyTrillSound value = parseEmptyTrillSound(cursor); + EmptyTrillSound value = parseEmptyTrillSound(cursor, context); cursor = nextElement(cursor); return OrnamentsGroupChoice::haydn(std::move(value)); } if (cursor && (cursorIs(cursor, "other-ornament"))) { - OtherPlacementText value = parseOtherPlacementText(cursor); + OtherPlacementText value = parseOtherPlacementText(cursor, context); cursor = nextElement(cursor); return OrnamentsGroupChoice::otherOrnament(std::move(value)); } diff --git a/src/private/mx/core/generated/OrnamentsGroupChoice.h b/src/private/mx/core/generated/OrnamentsGroupChoice.h index 20ef16e94..a88570880 100644 --- a/src/private/mx/core/generated/OrnamentsGroupChoice.h +++ b/src/private/mx/core/generated/OrnamentsGroupChoice.h @@ -23,6 +23,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -269,7 +271,7 @@ class OrnamentsGroupChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -OrnamentsGroupChoice parseOrnamentsGroupChoice(pugi::xml_node el, pugi::xml_node &cursor); +OrnamentsGroupChoice parseOrnamentsGroupChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeOrnamentsGroupChoice(const OrnamentsGroupChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/OtherAppearance.cpp b/src/private/mx/core/generated/OtherAppearance.cpp index 8eb6e9e55..0768ac546 100644 --- a/src/private/mx/core/generated/OtherAppearance.cpp +++ b/src/private/mx/core/generated/OtherAppearance.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/OtherAppearance.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void OtherAppearance::setValue(std::string value) m_value = std::move(value); } -OtherAppearance parseOtherAppearance(pugi::xml_node el) +OtherAppearance parseOtherAppearance(pugi::xml_node el, const ParseContext &context) { OtherAppearance out; bool seen_type = false; @@ -55,11 +56,11 @@ OtherAppearance parseOtherAppearance(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseOtherAppearanceContent(out, el); + parseOtherAppearanceContent(out, el, context); return out; } -void parseOtherAppearanceContent(OtherAppearance &out, pugi::xml_node el) +void parseOtherAppearanceContent(OtherAppearance &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/OtherAppearance.h b/src/private/mx/core/generated/OtherAppearance.h index 0b1a245e1..d29ce5a36 100644 --- a/src/private/mx/core/generated/OtherAppearance.h +++ b/src/private/mx/core/generated/OtherAppearance.h @@ -13,6 +13,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The other-appearance type is used to define any graphical settings not yet in the current version /// of the MusicXML format. This allows extended representation, though without application /// interoperability. @@ -30,9 +32,9 @@ class OtherAppearance final std::string m_value{}; }; -OtherAppearance parseOtherAppearance(pugi::xml_node el); +OtherAppearance parseOtherAppearance(pugi::xml_node el, const ParseContext &context); -void parseOtherAppearanceContent(OtherAppearance &out, pugi::xml_node el); +void parseOtherAppearanceContent(OtherAppearance &out, pugi::xml_node el, const ParseContext &context); void serializeOtherAppearance(const OtherAppearance &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/OtherDirection.cpp b/src/private/mx/core/generated/OtherDirection.cpp index 387eefd1b..cfb4a459a 100644 --- a/src/private/mx/core/generated/OtherDirection.cpp +++ b/src/private/mx/core/generated/OtherDirection.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/OtherDirection.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -160,7 +161,7 @@ void OtherDirection::setValue(std::string value) m_value = std::move(value); } -OtherDirection parseOtherDirection(pugi::xml_node el) +OtherDirection parseOtherDirection(pugi::xml_node el, const ParseContext &context) { OtherDirection out; for (pugi::xml_attribute a : el.attributes()) @@ -172,70 +173,70 @@ OtherDirection parseOtherDirection(pugi::xml_node el) } if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "smufl") { - out.setSmufl(SmuflGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseOtherDirectionContent(out, el); + parseOtherDirectionContent(out, el, context); return out; } -void parseOtherDirectionContent(OtherDirection &out, pugi::xml_node el) +void parseOtherDirectionContent(OtherDirection &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/OtherDirection.h b/src/private/mx/core/generated/OtherDirection.h index 78d9aea9a..94cad53a2 100644 --- a/src/private/mx/core/generated/OtherDirection.h +++ b/src/private/mx/core/generated/OtherDirection.h @@ -26,6 +26,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The other-direction type is used to define any direction symbols not yet in the MusicXML format. /// The smufl attribute can be used to specify a particular direction symbol, allowing application /// interoperability without requiring every SMuFL glyph to have a MusicXML element equivalent. Using @@ -84,9 +86,9 @@ class OtherDirection final std::string m_value{}; }; -OtherDirection parseOtherDirection(pugi::xml_node el); +OtherDirection parseOtherDirection(pugi::xml_node el, const ParseContext &context); -void parseOtherDirectionContent(OtherDirection &out, pugi::xml_node el); +void parseOtherDirectionContent(OtherDirection &out, pugi::xml_node el, const ParseContext &context); void serializeOtherDirection(const OtherDirection &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/OtherListening.cpp b/src/private/mx/core/generated/OtherListening.cpp index b48d53092..e6b941bb9 100644 --- a/src/private/mx/core/generated/OtherListening.cpp +++ b/src/private/mx/core/generated/OtherListening.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/OtherListening.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,7 +51,7 @@ void OtherListening::setValue(std::string value) m_value = std::move(value); } -OtherListening parseOtherListening(pugi::xml_node el) +OtherListening parseOtherListening(pugi::xml_node el, const ParseContext &context) { OtherListening out; bool seen_type = false; @@ -68,11 +69,11 @@ OtherListening parseOtherListening(pugi::xml_node el) } else if (aname == "player") { - out.setPlayer(Token::parse(a.value())); + out.setPlayer(parseValue(a.value(), context, el, "player")); } else if (aname == "time-only") { - out.setTimeOnly(TimeOnly::parse(a.value())); + out.setTimeOnly(parseValue(a.value(), context, el, "time-only")); } else { @@ -83,11 +84,11 @@ OtherListening parseOtherListening(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseOtherListeningContent(out, el); + parseOtherListeningContent(out, el, context); return out; } -void parseOtherListeningContent(OtherListening &out, pugi::xml_node el) +void parseOtherListeningContent(OtherListening &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/OtherListening.h b/src/private/mx/core/generated/OtherListening.h index 844b1763e..7756bd516 100644 --- a/src/private/mx/core/generated/OtherListening.h +++ b/src/private/mx/core/generated/OtherListening.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The other-listening type represents other types of listening control and interaction. The /// required type attribute indicates the type of listening to which the element content applies. The /// optional player and time-only attributes restrict the element to apply to a single player or set @@ -41,9 +43,9 @@ class OtherListening final std::string m_value{}; }; -OtherListening parseOtherListening(pugi::xml_node el); +OtherListening parseOtherListening(pugi::xml_node el, const ParseContext &context); -void parseOtherListeningContent(OtherListening &out, pugi::xml_node el); +void parseOtherListeningContent(OtherListening &out, pugi::xml_node el, const ParseContext &context); void serializeOtherListening(const OtherListening &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/OtherNotation.cpp b/src/private/mx/core/generated/OtherNotation.cpp index d34f7893b..cf34e36e0 100644 --- a/src/private/mx/core/generated/OtherNotation.cpp +++ b/src/private/mx/core/generated/OtherNotation.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/OtherNotation.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -170,7 +171,7 @@ void OtherNotation::setValue(std::string value) m_value = std::move(value); } -OtherNotation parseOtherNotation(pugi::xml_node el) +OtherNotation parseOtherNotation(pugi::xml_node el, const ParseContext &context) { OtherNotation out; bool seen_type = false; @@ -184,63 +185,63 @@ OtherNotation parseOtherNotation(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStopSingle::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "number") { - out.setNumber(NumberLevel::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "smufl") { - out.setSmufl(SmuflGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -251,11 +252,11 @@ OtherNotation parseOtherNotation(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseOtherNotationContent(out, el); + parseOtherNotationContent(out, el, context); return out; } -void parseOtherNotationContent(OtherNotation &out, pugi::xml_node el) +void parseOtherNotationContent(OtherNotation &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/OtherNotation.h b/src/private/mx/core/generated/OtherNotation.h index c7a5741c6..a81d26f1a 100644 --- a/src/private/mx/core/generated/OtherNotation.h +++ b/src/private/mx/core/generated/OtherNotation.h @@ -27,6 +27,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The other-notation type is used to define any notations not yet in the MusicXML format. It /// handles notations where more specific extension elements such as other-dynamics and /// other-technical are not appropriate. The smufl attribute can be used to specify a particular @@ -89,9 +91,9 @@ class OtherNotation final std::string m_value{}; }; -OtherNotation parseOtherNotation(pugi::xml_node el); +OtherNotation parseOtherNotation(pugi::xml_node el, const ParseContext &context); -void parseOtherNotationContent(OtherNotation &out, pugi::xml_node el); +void parseOtherNotationContent(OtherNotation &out, pugi::xml_node el, const ParseContext &context); void serializeOtherNotation(const OtherNotation &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/OtherPlacementText.cpp b/src/private/mx/core/generated/OtherPlacementText.cpp index 0297aaba0..2d9a3b383 100644 --- a/src/private/mx/core/generated/OtherPlacementText.cpp +++ b/src/private/mx/core/generated/OtherPlacementText.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/OtherPlacementText.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -130,7 +131,7 @@ void OtherPlacementText::setValue(std::string value) m_value = std::move(value); } -OtherPlacementText parseOtherPlacementText(pugi::xml_node el) +OtherPlacementText parseOtherPlacementText(pugi::xml_node el, const ParseContext &context) { OtherPlacementText out; for (pugi::xml_attribute a : el.attributes()) @@ -142,58 +143,58 @@ OtherPlacementText parseOtherPlacementText(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "smufl") { - out.setSmufl(SmuflGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else { throwUnknownAttribute(el, a.name()); } } - parseOtherPlacementTextContent(out, el); + parseOtherPlacementTextContent(out, el, context); return out; } -void parseOtherPlacementTextContent(OtherPlacementText &out, pugi::xml_node el) +void parseOtherPlacementTextContent(OtherPlacementText &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/OtherPlacementText.h b/src/private/mx/core/generated/OtherPlacementText.h index 720356a8f..6ade71f62 100644 --- a/src/private/mx/core/generated/OtherPlacementText.h +++ b/src/private/mx/core/generated/OtherPlacementText.h @@ -22,6 +22,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The other-placement-text type represents a text element with print-style, placement, and smufl /// attribute groups. This type is used by MusicXML notation extension elements to allow /// specification of specific SMuFL glyphs without needed to add every glyph as a MusicXML element. @@ -69,9 +71,9 @@ class OtherPlacementText final std::string m_value{}; }; -OtherPlacementText parseOtherPlacementText(pugi::xml_node el); +OtherPlacementText parseOtherPlacementText(pugi::xml_node el, const ParseContext &context); -void parseOtherPlacementTextContent(OtherPlacementText &out, pugi::xml_node el); +void parseOtherPlacementTextContent(OtherPlacementText &out, pugi::xml_node el, const ParseContext &context); void serializeOtherPlacementText(const OtherPlacementText &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/OtherPlay.cpp b/src/private/mx/core/generated/OtherPlay.cpp index 306130333..09170e3b2 100644 --- a/src/private/mx/core/generated/OtherPlay.cpp +++ b/src/private/mx/core/generated/OtherPlay.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/OtherPlay.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void OtherPlay::setValue(std::string value) m_value = std::move(value); } -OtherPlay parseOtherPlay(pugi::xml_node el) +OtherPlay parseOtherPlay(pugi::xml_node el, const ParseContext &context) { OtherPlay out; bool seen_type = false; @@ -55,11 +56,11 @@ OtherPlay parseOtherPlay(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseOtherPlayContent(out, el); + parseOtherPlayContent(out, el, context); return out; } -void parseOtherPlayContent(OtherPlay &out, pugi::xml_node el) +void parseOtherPlayContent(OtherPlay &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/OtherPlay.h b/src/private/mx/core/generated/OtherPlay.h index 4c2a6ebfc..2d6dfb796 100644 --- a/src/private/mx/core/generated/OtherPlay.h +++ b/src/private/mx/core/generated/OtherPlay.h @@ -13,6 +13,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The other-play element represents other types of playback. The required type attribute indicates /// the type of playback to which the element content applies. class OtherPlay final @@ -29,9 +31,9 @@ class OtherPlay final std::string m_value{}; }; -OtherPlay parseOtherPlay(pugi::xml_node el); +OtherPlay parseOtherPlay(pugi::xml_node el, const ParseContext &context); -void parseOtherPlayContent(OtherPlay &out, pugi::xml_node el); +void parseOtherPlayContent(OtherPlay &out, pugi::xml_node el, const ParseContext &context); void serializeOtherPlay(const OtherPlay &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/OtherText.cpp b/src/private/mx/core/generated/OtherText.cpp index 2b75935fd..b4a23f24a 100644 --- a/src/private/mx/core/generated/OtherText.cpp +++ b/src/private/mx/core/generated/OtherText.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/OtherText.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void OtherText::setValue(std::string value) m_value = std::move(value); } -OtherText parseOtherText(pugi::xml_node el) +OtherText parseOtherText(pugi::xml_node el, const ParseContext &context) { OtherText out; for (pugi::xml_attribute a : el.attributes()) @@ -42,18 +43,18 @@ OtherText parseOtherText(pugi::xml_node el) } if (aname == "smufl") { - out.setSmufl(SmuflGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else { throwUnknownAttribute(el, a.name()); } } - parseOtherTextContent(out, el); + parseOtherTextContent(out, el, context); return out; } -void parseOtherTextContent(OtherText &out, pugi::xml_node el) +void parseOtherTextContent(OtherText &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/OtherText.h b/src/private/mx/core/generated/OtherText.h index 464478e25..801638e79 100644 --- a/src/private/mx/core/generated/OtherText.h +++ b/src/private/mx/core/generated/OtherText.h @@ -15,6 +15,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The other-text type represents a text element with a smufl attribute group. This type is used by /// MusicXML direction extension elements to allow specification of specific SMuFL glyphs without /// needed to add every glyph as a MusicXML element. @@ -32,9 +34,9 @@ class OtherText final std::string m_value{}; }; -OtherText parseOtherText(pugi::xml_node el); +OtherText parseOtherText(pugi::xml_node el, const ParseContext &context); -void parseOtherTextContent(OtherText &out, pugi::xml_node el); +void parseOtherTextContent(OtherText &out, pugi::xml_node el, const ParseContext &context); void serializeOtherText(const OtherText &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/OverUnder.cpp b/src/private/mx/core/generated/OverUnder.cpp index 9f37cc03e..f846ab579 100644 --- a/src/private/mx/core/generated/OverUnder.cpp +++ b/src/private/mx/core/generated/OverUnder.cpp @@ -42,9 +42,15 @@ bool OverUnder::tryParse(std::string_view text, OverUnder &out) noexcept } OverUnder OverUnder::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +OverUnder OverUnder::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { OverUnder v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/OverUnder.h b/src/private/mx/core/generated/OverUnder.h index 4972fc1be..a66b80d52 100644 --- a/src/private/mx/core/generated/OverUnder.h +++ b/src/private/mx/core/generated/OverUnder.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -41,6 +43,9 @@ class OverUnder final /// (the import leniency policy; never produces an invalid value). static OverUnder parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static OverUnder parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const OverUnder &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/PageLayout.cpp b/src/private/mx/core/generated/PageLayout.cpp index d62f99c8a..de9037b83 100644 --- a/src/private/mx/core/generated/PageLayout.cpp +++ b/src/private/mx/core/generated/PageLayout.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PageLayout.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,7 +41,7 @@ void PageLayout::clearPageMargins() noexcept m_pageMargins.clear(); } -PageLayout parsePageLayout(pugi::xml_node el) +PageLayout parsePageLayout(pugi::xml_node el, const ParseContext &context) { PageLayout out; for (pugi::xml_attribute a : el.attributes()) @@ -52,20 +53,20 @@ PageLayout parsePageLayout(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parsePageLayoutContent(out, el); + parsePageLayoutContent(out, el, context); return out; } -void parsePageLayoutContent(PageLayout &out, pugi::xml_node el) +void parsePageLayoutContent(PageLayout &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "page-height"))) { - out.setGroup(parsePageLayoutGroup(el, cursor)); + out.setGroup(parsePageLayoutGroup(el, cursor, context)); } while (cursorIs(cursor, "page-margins")) { - if (!out.addPageMargins(parsePageMargins(cursor))) + if (!out.addPageMargins(parsePageMargins(cursor, context))) { throwTooManyElements(cursor); } diff --git a/src/private/mx/core/generated/PageLayout.h b/src/private/mx/core/generated/PageLayout.h index 35e029982..152a04bbf 100644 --- a/src/private/mx/core/generated/PageLayout.h +++ b/src/private/mx/core/generated/PageLayout.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Page layout can be defined both in score-wide defaults and in the print element. Page margins are /// specified either for both even and odd pages, or via separate odd and even page number values. /// The type is not needed when used as part of a print element. If omitted when used in the defaults @@ -47,9 +49,9 @@ class PageLayout final std::vector m_pageMargins; }; -PageLayout parsePageLayout(pugi::xml_node el); +PageLayout parsePageLayout(pugi::xml_node el, const ParseContext &context); -void parsePageLayoutContent(PageLayout &out, pugi::xml_node el); +void parsePageLayoutContent(PageLayout &out, pugi::xml_node el, const ParseContext &context); void serializePageLayout(const PageLayout &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/PageLayoutGroup.cpp b/src/private/mx/core/generated/PageLayoutGroup.cpp index 862dfdb1b..e5d6639dd 100644 --- a/src/private/mx/core/generated/PageLayoutGroup.cpp +++ b/src/private/mx/core/generated/PageLayoutGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PageLayoutGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,12 +31,12 @@ void PageLayoutGroup::setPageWidth(Tenths value) m_pageWidth = std::move(value); } -PageLayoutGroup parsePageLayoutGroup(pugi::xml_node el, pugi::xml_node &cursor) +PageLayoutGroup parsePageLayoutGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { PageLayoutGroup out; if (cursorIs(cursor, "page-height")) { - out.setPageHeight(Tenths::parse(childText(cursor))); + out.setPageHeight(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -44,7 +45,7 @@ PageLayoutGroup parsePageLayoutGroup(pugi::xml_node el, pugi::xml_node &cursor) } if (cursorIs(cursor, "page-width")) { - out.setPageWidth(Tenths::parse(childText(cursor))); + out.setPageWidth(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/PageLayoutGroup.h b/src/private/mx/core/generated/PageLayoutGroup.h index e2da2d30b..ac19e680e 100644 --- a/src/private/mx/core/generated/PageLayoutGroup.h +++ b/src/private/mx/core/generated/PageLayoutGroup.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -36,7 +38,7 @@ class PageLayoutGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -PageLayoutGroup parsePageLayoutGroup(pugi::xml_node el, pugi::xml_node &cursor); +PageLayoutGroup parsePageLayoutGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializePageLayoutGroup(const PageLayoutGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/PageMargins.cpp b/src/private/mx/core/generated/PageMargins.cpp index 64671b7fb..0990ab028 100644 --- a/src/private/mx/core/generated/PageMargins.cpp +++ b/src/private/mx/core/generated/PageMargins.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PageMargins.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void PageMargins::setAllMargins(AllMarginsGroup value) m_allMargins = std::move(value); } -PageMargins parsePageMargins(pugi::xml_node el) +PageMargins parsePageMargins(pugi::xml_node el, const ParseContext &context) { PageMargins out; for (pugi::xml_attribute a : el.attributes()) @@ -42,23 +43,23 @@ PageMargins parsePageMargins(pugi::xml_node el) } if (aname == "type") { - out.setType(MarginType::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else { throwUnknownAttribute(el, a.name()); } } - parsePageMarginsContent(out, el); + parsePageMarginsContent(out, el, context); return out; } -void parsePageMarginsContent(PageMargins &out, pugi::xml_node el) +void parsePageMarginsContent(PageMargins &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "left-margin"))) { - out.setAllMargins(parseAllMarginsGroup(el, cursor)); + out.setAllMargins(parseAllMarginsGroup(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/PageMargins.h b/src/private/mx/core/generated/PageMargins.h index 86cdb5493..d5ab1da36 100644 --- a/src/private/mx/core/generated/PageMargins.h +++ b/src/private/mx/core/generated/PageMargins.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Page margins are specified either for both even and odd pages, or via separate odd and even page /// number values. The type attribute is not needed when used as part of a print element. If omitted /// when the page-margins type is used in the defaults element, "both" is the default value. @@ -37,9 +39,9 @@ class PageMargins final AllMarginsGroup m_allMargins{}; }; -PageMargins parsePageMargins(pugi::xml_node el); +PageMargins parsePageMargins(pugi::xml_node el, const ParseContext &context); -void parsePageMarginsContent(PageMargins &out, pugi::xml_node el); +void parsePageMarginsContent(PageMargins &out, pugi::xml_node el, const ParseContext &context); void serializePageMargins(const PageMargins &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/PartClef.cpp b/src/private/mx/core/generated/PartClef.cpp index 9d0632cac..99aa74e4c 100644 --- a/src/private/mx/core/generated/PartClef.cpp +++ b/src/private/mx/core/generated/PartClef.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PartClef.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -20,7 +21,7 @@ void PartClef::setClef(ClefGroup value) m_clef = std::move(value); } -PartClef parsePartClef(pugi::xml_node el) +PartClef parsePartClef(pugi::xml_node el, const ParseContext &context) { PartClef out; for (pugi::xml_attribute a : el.attributes()) @@ -32,16 +33,16 @@ PartClef parsePartClef(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parsePartClefContent(out, el); + parsePartClefContent(out, el, context); return out; } -void parsePartClefContent(PartClef &out, pugi::xml_node el) +void parsePartClefContent(PartClef &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "sign"))) { - out.setClef(parseClefGroup(el, cursor)); + out.setClef(parseClefGroup(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/PartClef.h b/src/private/mx/core/generated/PartClef.h index 06b10ddd1..854fbdd45 100644 --- a/src/private/mx/core/generated/PartClef.h +++ b/src/private/mx/core/generated/PartClef.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The child elements of the part-clef type have the same meaning as for the clef type. However that /// meaning applies to a transposed part created from the existing score file. /// Content fields mirror the schema grammar in declaration order; the @@ -32,9 +34,9 @@ class PartClef final ClefGroup m_clef{}; }; -PartClef parsePartClef(pugi::xml_node el); +PartClef parsePartClef(pugi::xml_node el, const ParseContext &context); -void parsePartClefContent(PartClef &out, pugi::xml_node el); +void parsePartClefContent(PartClef &out, pugi::xml_node el, const ParseContext &context); void serializePartClef(const PartClef &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/PartGroup.cpp b/src/private/mx/core/generated/PartGroup.cpp index 3e2bb9428..1b847372d 100644 --- a/src/private/mx/core/generated/PartGroup.cpp +++ b/src/private/mx/core/generated/PartGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PartGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -110,7 +111,7 @@ void PartGroup::setEditorial(EditorialGroup value) m_editorial = std::move(value); } -PartGroup parsePartGroup(pugi::xml_node el) +PartGroup parsePartGroup(pugi::xml_node el, const ParseContext &context) { PartGroup out; bool seen_type = false; @@ -124,7 +125,7 @@ PartGroup parsePartGroup(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStop::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "number") { @@ -139,52 +140,52 @@ PartGroup parsePartGroup(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parsePartGroupContent(out, el); + parsePartGroupContent(out, el, context); return out; } -void parsePartGroupContent(PartGroup &out, pugi::xml_node el) +void parsePartGroupContent(PartGroup &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "group-name")) { - out.setGroupName(parseGroupName(cursor)); + out.setGroupName(parseGroupName(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "group-name-display")) { - out.setGroupNameDisplay(parseNameDisplay(cursor)); + out.setGroupNameDisplay(parseNameDisplay(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "group-abbreviation")) { - out.setGroupAbbreviation(parseGroupName(cursor)); + out.setGroupAbbreviation(parseGroupName(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "group-abbreviation-display")) { - out.setGroupAbbreviationDisplay(parseNameDisplay(cursor)); + out.setGroupAbbreviationDisplay(parseNameDisplay(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "group-symbol")) { - out.setGroupSymbol(parseGroupSymbol(cursor)); + out.setGroupSymbol(parseGroupSymbol(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "group-barline")) { - out.setGroupBarline(parseGroupBarline(cursor)); + out.setGroupBarline(parseGroupBarline(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "group-time")) { - parseEmpty(cursor); + parseEmpty(cursor, context); out.setGroupTime(true); cursor = nextElement(cursor); } if (cursor && (cursorIs(cursor, "footnote") || cursorIs(cursor, "level"))) { - out.setEditorial(parseEditorialGroup(el, cursor)); + out.setEditorial(parseEditorialGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/PartGroup.h b/src/private/mx/core/generated/PartGroup.h index 6d7515265..66415983d 100644 --- a/src/private/mx/core/generated/PartGroup.h +++ b/src/private/mx/core/generated/PartGroup.h @@ -23,6 +23,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The part-group element indicates groupings of parts in the score, usually indicated by braces and /// brackets. Braces that are used for multi-staff parts should be defined in the attributes element /// for that part. The part-group start element appears before the first score-part in the group. The @@ -73,9 +75,9 @@ class PartGroup final EditorialGroup m_editorial{}; }; -PartGroup parsePartGroup(pugi::xml_node el); +PartGroup parsePartGroup(pugi::xml_node el, const ParseContext &context); -void parsePartGroupContent(PartGroup &out, pugi::xml_node el); +void parsePartGroupContent(PartGroup &out, pugi::xml_node el, const ParseContext &context); void serializePartGroup(const PartGroup &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/PartLink.cpp b/src/private/mx/core/generated/PartLink.cpp index b93d41033..be001b290 100644 --- a/src/private/mx/core/generated/PartLink.cpp +++ b/src/private/mx/core/generated/PartLink.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PartLink.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -100,7 +101,7 @@ void PartLink::setGroupLink(std::vector value) m_groupLink = std::move(value); } -PartLink parsePartLink(pugi::xml_node el) +PartLink parsePartLink(pugi::xml_node el, const ParseContext &context) { PartLink out; bool seen_xlinkHref = false; @@ -145,16 +146,16 @@ PartLink parsePartLink(pugi::xml_node el) { throwMissingAttribute(el, "xlink:href"); } - parsePartLinkContent(out, el); + parsePartLinkContent(out, el, context); return out; } -void parsePartLinkContent(PartLink &out, pugi::xml_node el) +void parsePartLinkContent(PartLink &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); while (cursorIs(cursor, "instrument-link")) { - out.addInstrumentLink(parseInstrumentLink(cursor)); + out.addInstrumentLink(parseInstrumentLink(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "group-link")) diff --git a/src/private/mx/core/generated/PartLink.h b/src/private/mx/core/generated/PartLink.h index 05a81fdd4..3b87682db 100644 --- a/src/private/mx/core/generated/PartLink.h +++ b/src/private/mx/core/generated/PartLink.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The part-link type allows MusicXML data for both score and parts to be contained within a single /// compressed MusicXML file. It links a score-part from a score document to MusicXML documents that /// contain parts data. In the case of a single compressed MusicXML file, the link href values are @@ -57,9 +59,9 @@ class PartLink final std::vector m_groupLink; }; -PartLink parsePartLink(pugi::xml_node el); +PartLink parsePartLink(pugi::xml_node el, const ParseContext &context); -void parsePartLinkContent(PartLink &out, pugi::xml_node el); +void parsePartLinkContent(PartLink &out, pugi::xml_node el, const ParseContext &context); void serializePartLink(const PartLink &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/PartList.cpp b/src/private/mx/core/generated/PartList.cpp index 3af0b1f82..d783dcd3f 100644 --- a/src/private/mx/core/generated/PartList.cpp +++ b/src/private/mx/core/generated/PartList.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PartList.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,7 +51,7 @@ void PartList::setChoice(std::vector value) m_choice = std::move(value); } -PartList parsePartList(pugi::xml_node el) +PartList parsePartList(pugi::xml_node el, const ParseContext &context) { PartList out; for (pugi::xml_attribute a : el.attributes()) @@ -62,21 +63,21 @@ PartList parsePartList(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parsePartListContent(out, el); + parsePartListContent(out, el, context); return out; } -void parsePartListContent(PartList &out, pugi::xml_node el) +void parsePartListContent(PartList &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); while (cursorIs(cursor, "part-group")) { - out.addPartGroup(parsePartGroup(cursor)); + out.addPartGroup(parsePartGroup(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "score-part")) { - out.setScorePart(parseScorePart(cursor)); + out.setScorePart(parseScorePart(cursor, context)); cursor = nextElement(cursor); } else @@ -85,7 +86,7 @@ void parsePartListContent(PartList &out, pugi::xml_node el) } while (cursor && (cursorIs(cursor, "part-group") || cursorIs(cursor, "score-part"))) { - out.addChoice(parsePartListChoice(el, cursor)); + out.addChoice(parsePartListChoice(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/PartList.h b/src/private/mx/core/generated/PartList.h index 79e77f976..4276e9936 100644 --- a/src/private/mx/core/generated/PartList.h +++ b/src/private/mx/core/generated/PartList.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The part-list identifies the different musical parts in this document. Each part has an ID that /// is used later within the musical data. Since parts may be encoded separately and combined later, /// identification elements are present at both the score and score-part levels. There must be at @@ -46,9 +48,9 @@ class PartList final std::vector m_choice; }; -PartList parsePartList(pugi::xml_node el); +PartList parsePartList(pugi::xml_node el, const ParseContext &context); -void parsePartListContent(PartList &out, pugi::xml_node el); +void parsePartListContent(PartList &out, pugi::xml_node el, const ParseContext &context); void serializePartList(const PartList &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/PartListChoice.cpp b/src/private/mx/core/generated/PartListChoice.cpp index 6d17c7829..d2776c30c 100644 --- a/src/private/mx/core/generated/PartListChoice.cpp +++ b/src/private/mx/core/generated/PartListChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PartListChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,17 +26,17 @@ PartListChoice PartListChoice::scorePart(ScorePart value) return PartListChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -PartListChoice parsePartListChoice(pugi::xml_node el, pugi::xml_node &cursor) +PartListChoice parsePartListChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "part-group"))) { - PartGroup value = parsePartGroup(cursor); + PartGroup value = parsePartGroup(cursor, context); cursor = nextElement(cursor); return PartListChoice::partGroup(std::move(value)); } if (cursor && (cursorIs(cursor, "score-part"))) { - ScorePart value = parseScorePart(cursor); + ScorePart value = parseScorePart(cursor, context); cursor = nextElement(cursor); return PartListChoice::scorePart(std::move(value)); } diff --git a/src/private/mx/core/generated/PartListChoice.h b/src/private/mx/core/generated/PartListChoice.h index d11ecf977..3132a6e66 100644 --- a/src/private/mx/core/generated/PartListChoice.h +++ b/src/private/mx/core/generated/PartListChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -80,7 +82,7 @@ class PartListChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -PartListChoice parsePartListChoice(pugi::xml_node el, pugi::xml_node &cursor); +PartListChoice parsePartListChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializePartListChoice(const PartListChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/PartName.cpp b/src/private/mx/core/generated/PartName.cpp index c2832bf2e..1ef2ccde8 100644 --- a/src/private/mx/core/generated/PartName.cpp +++ b/src/private/mx/core/generated/PartName.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PartName.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -130,7 +131,7 @@ void PartName::setValue(std::string value) m_value = std::move(value); } -PartName parsePartName(pugi::xml_node el) +PartName parsePartName(pugi::xml_node el, const ParseContext &context) { PartName out; for (pugi::xml_attribute a : el.attributes()) @@ -142,58 +143,58 @@ PartName parsePartName(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else if (aname == "justify") { - out.setJustify(LeftCenterRight::parse(a.value())); + out.setJustify(parseValue(a.value(), context, el, "justify")); } else { throwUnknownAttribute(el, a.name()); } } - parsePartNameContent(out, el); + parsePartNameContent(out, el, context); return out; } -void parsePartNameContent(PartName &out, pugi::xml_node el) +void parsePartNameContent(PartName &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/PartName.h b/src/private/mx/core/generated/PartName.h index 6eaca6824..84f4be279 100644 --- a/src/private/mx/core/generated/PartName.h +++ b/src/private/mx/core/generated/PartName.h @@ -22,6 +22,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The part-name type describes the name or abbreviation of a score-part element. Formatting /// attributes for the part-name element are deprecated in Version 2.0 in favor of the new /// part-name-display and part-abbreviation-display elements. @@ -69,9 +71,9 @@ class PartName final std::string m_value{}; }; -PartName parsePartName(pugi::xml_node el); +PartName parsePartName(pugi::xml_node el, const ParseContext &context); -void parsePartNameContent(PartName &out, pugi::xml_node el); +void parsePartNameContent(PartName &out, pugi::xml_node el, const ParseContext &context); void serializePartName(const PartName &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/PartSymbol.cpp b/src/private/mx/core/generated/PartSymbol.cpp index e0940d609..dd3fed80f 100644 --- a/src/private/mx/core/generated/PartSymbol.cpp +++ b/src/private/mx/core/generated/PartSymbol.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PartSymbol.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -90,7 +91,7 @@ void PartSymbol::setValue(GroupSymbolValue value) m_value = std::move(value); } -PartSymbol parsePartSymbol(pugi::xml_node el) +PartSymbol parsePartSymbol(pugi::xml_node el, const ParseContext &context) { PartSymbol out; for (pugi::xml_attribute a : el.attributes()) @@ -102,44 +103,44 @@ PartSymbol parsePartSymbol(pugi::xml_node el) } if (aname == "top-staff") { - out.setTopStaff(StaffNumber::parse(a.value())); + out.setTopStaff(parseValue(a.value(), context, el, "top-staff")); } else if (aname == "bottom-staff") { - out.setBottomStaff(StaffNumber::parse(a.value())); + out.setBottomStaff(parseValue(a.value(), context, el, "bottom-staff")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parsePartSymbolContent(out, el); + parsePartSymbolContent(out, el, context); return out; } -void parsePartSymbolContent(PartSymbol &out, pugi::xml_node el) +void parsePartSymbolContent(PartSymbol &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(GroupSymbolValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/PartSymbol.h b/src/private/mx/core/generated/PartSymbol.h index e6bdf48b2..177c5dac6 100644 --- a/src/private/mx/core/generated/PartSymbol.h +++ b/src/private/mx/core/generated/PartSymbol.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The part-symbol type indicates how a symbol for a multi-staff part is indicated in the score; /// brace is the default value. The top-staff and bottom-staff attributes are used when the brace /// does not extend across the entire part. For example, in a 3-staff organ part, the top-staff will @@ -57,9 +59,9 @@ class PartSymbol final GroupSymbolValue m_value{}; }; -PartSymbol parsePartSymbol(pugi::xml_node el); +PartSymbol parsePartSymbol(pugi::xml_node el, const ParseContext &context); -void parsePartSymbolContent(PartSymbol &out, pugi::xml_node el); +void parsePartSymbolContent(PartSymbol &out, pugi::xml_node el, const ParseContext &context); void serializePartSymbol(const PartSymbol &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/PartTranspose.cpp b/src/private/mx/core/generated/PartTranspose.cpp index e3e8733de..0d2c3fdba 100644 --- a/src/private/mx/core/generated/PartTranspose.cpp +++ b/src/private/mx/core/generated/PartTranspose.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PartTranspose.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -20,7 +21,7 @@ void PartTranspose::setTranspose(TransposeGroup value) m_transpose = std::move(value); } -PartTranspose parsePartTranspose(pugi::xml_node el) +PartTranspose parsePartTranspose(pugi::xml_node el, const ParseContext &context) { PartTranspose out; for (pugi::xml_attribute a : el.attributes()) @@ -32,16 +33,16 @@ PartTranspose parsePartTranspose(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parsePartTransposeContent(out, el); + parsePartTransposeContent(out, el, context); return out; } -void parsePartTransposeContent(PartTranspose &out, pugi::xml_node el) +void parsePartTransposeContent(PartTranspose &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "diatonic") || cursorIs(cursor, "chromatic"))) { - out.setTranspose(parseTransposeGroup(el, cursor)); + out.setTranspose(parseTransposeGroup(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/PartTranspose.h b/src/private/mx/core/generated/PartTranspose.h index 9d6feb989..d7af3fb8d 100644 --- a/src/private/mx/core/generated/PartTranspose.h +++ b/src/private/mx/core/generated/PartTranspose.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The child elements of the part-transpose type have the same meaning as for the transpose type. /// However that meaning applies to a transposed part created from the existing score file. /// Content fields mirror the schema grammar in declaration order; the @@ -32,9 +34,9 @@ class PartTranspose final TransposeGroup m_transpose{}; }; -PartTranspose parsePartTranspose(pugi::xml_node el); +PartTranspose parsePartTranspose(pugi::xml_node el, const ParseContext &context); -void parsePartTransposeContent(PartTranspose &out, pugi::xml_node el); +void parsePartTransposeContent(PartTranspose &out, pugi::xml_node el, const ParseContext &context); void serializePartTranspose(const PartTranspose &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/PartwiseMeasure.cpp b/src/private/mx/core/generated/PartwiseMeasure.cpp index 97ed0a17b..70ae7a1d1 100644 --- a/src/private/mx/core/generated/PartwiseMeasure.cpp +++ b/src/private/mx/core/generated/PartwiseMeasure.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PartwiseMeasure.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -85,7 +86,7 @@ void PartwiseMeasure::setMusicData(std::vector value) m_musicData = std::move(value); } -PartwiseMeasure parsePartwiseMeasure(pugi::xml_node el) +PartwiseMeasure parsePartwiseMeasure(pugi::xml_node el, const ParseContext &context) { PartwiseMeasure out; bool seen_number = false; @@ -103,23 +104,23 @@ PartwiseMeasure parsePartwiseMeasure(pugi::xml_node el) } else if (aname == "text") { - out.setText(MeasureText::parse(a.value())); + out.setText(parseValue(a.value(), context, el, "text")); } else if (aname == "implicit") { - out.setImplicit(YesNo::parse(a.value())); + out.setImplicit(parseValue(a.value(), context, el, "implicit")); } else if (aname == "non-controlling") { - out.setNonControlling(YesNo::parse(a.value())); + out.setNonControlling(parseValue(a.value(), context, el, "non-controlling")); } else if (aname == "width") { - out.setWidth(Tenths::parse(a.value())); + out.setWidth(parseValue(a.value(), context, el, "width")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -130,11 +131,11 @@ PartwiseMeasure parsePartwiseMeasure(pugi::xml_node el) { throwMissingAttribute(el, "number"); } - parsePartwiseMeasureContent(out, el); + parsePartwiseMeasureContent(out, el, context); return out; } -void parsePartwiseMeasureContent(PartwiseMeasure &out, pugi::xml_node el) +void parsePartwiseMeasureContent(PartwiseMeasure &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); while (cursor && (cursorIs(cursor, "note") || cursorIs(cursor, "backup") || cursorIs(cursor, "forward") || @@ -143,7 +144,7 @@ void parsePartwiseMeasureContent(PartwiseMeasure &out, pugi::xml_node el) cursorIs(cursor, "listening") || cursorIs(cursor, "barline") || cursorIs(cursor, "grouping") || cursorIs(cursor, "link") || cursorIs(cursor, "bookmark"))) { - out.addMusicData(parseMusicDataChoice(el, cursor)); + out.addMusicData(parseMusicDataChoice(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/PartwiseMeasure.h b/src/private/mx/core/generated/PartwiseMeasure.h index 6868a76de..151b593f6 100644 --- a/src/private/mx/core/generated/PartwiseMeasure.h +++ b/src/private/mx/core/generated/PartwiseMeasure.h @@ -22,6 +22,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Content fields mirror the schema grammar in declaration order; the /// serializer walks them, so a wrong order is unrepresentable (plan §2.3). class PartwiseMeasure final @@ -54,9 +56,9 @@ class PartwiseMeasure final std::vector m_musicData; }; -PartwiseMeasure parsePartwiseMeasure(pugi::xml_node el); +PartwiseMeasure parsePartwiseMeasure(pugi::xml_node el, const ParseContext &context); -void parsePartwiseMeasureContent(PartwiseMeasure &out, pugi::xml_node el); +void parsePartwiseMeasureContent(PartwiseMeasure &out, pugi::xml_node el, const ParseContext &context); void serializePartwiseMeasure(const PartwiseMeasure &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/PartwisePart.cpp b/src/private/mx/core/generated/PartwisePart.cpp index 6bf7988e3..7902aa4eb 100644 --- a/src/private/mx/core/generated/PartwisePart.cpp +++ b/src/private/mx/core/generated/PartwisePart.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PartwisePart.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,7 +36,7 @@ void PartwisePart::setMeasure(OneOrMore value) m_measure = std::move(value); } -PartwisePart parsePartwisePart(pugi::xml_node el) +PartwisePart parsePartwisePart(pugi::xml_node el, const ParseContext &context) { PartwisePart out; bool seen_id = false; @@ -49,7 +50,7 @@ PartwisePart parsePartwisePart(pugi::xml_node el) if (aname == "id") { seen_id = true; - out.setID(Token::parse(a.value())); + out.setID(parseValue(a.value(), context, el, "id")); } else { @@ -60,22 +61,22 @@ PartwisePart parsePartwisePart(pugi::xml_node el) { throwMissingAttribute(el, "id"); } - parsePartwisePartContent(out, el); + parsePartwisePartContent(out, el, context); return out; } -void parsePartwisePartContent(PartwisePart &out, pugi::xml_node el) +void parsePartwisePartContent(PartwisePart &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (!cursorIs(cursor, "measure")) { throwMissingOrMisplaced(el, cursor, "measure"); } - out.setMeasure(OneOrMore{parsePartwiseMeasure(cursor)}); + out.setMeasure(OneOrMore{parsePartwiseMeasure(cursor, context)}); cursor = nextElement(cursor); while (cursorIs(cursor, "measure")) { - out.addMeasure(parsePartwiseMeasure(cursor)); + out.addMeasure(parsePartwiseMeasure(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/PartwisePart.h b/src/private/mx/core/generated/PartwisePart.h index c7f1fcb83..ec9ad4d5d 100644 --- a/src/private/mx/core/generated/PartwisePart.h +++ b/src/private/mx/core/generated/PartwisePart.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Content fields mirror the schema grammar in declaration order; the /// serializer walks them, so a wrong order is unrepresentable (plan §2.3). class PartwisePart final @@ -37,9 +39,9 @@ class PartwisePart final OneOrMore m_measure; }; -PartwisePart parsePartwisePart(pugi::xml_node el); +PartwisePart parsePartwisePart(pugi::xml_node el, const ParseContext &context); -void parsePartwisePartContent(PartwisePart &out, pugi::xml_node el); +void parsePartwisePartContent(PartwisePart &out, pugi::xml_node el, const ParseContext &context); void serializePartwisePart(const PartwisePart &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Pedal.cpp b/src/private/mx/core/generated/Pedal.cpp index 2b624a0f1..9b19435d6 100644 --- a/src/private/mx/core/generated/Pedal.cpp +++ b/src/private/mx/core/generated/Pedal.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Pedal.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -180,7 +181,7 @@ void Pedal::setID(std::optional value) m_id = std::move(value); } -Pedal parsePedal(pugi::xml_node el) +Pedal parsePedal(pugi::xml_node el, const ParseContext &context) { Pedal out; bool seen_type = false; @@ -194,71 +195,71 @@ Pedal parsePedal(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(PedalType::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "number") { - out.setNumber(NumberLevel::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "line") { - out.setLine(YesNo::parse(a.value())); + out.setLine(parseValue(a.value(), context, el, "line")); } else if (aname == "sign") { - out.setSign(YesNo::parse(a.value())); + out.setSign(parseValue(a.value(), context, el, "sign")); } else if (aname == "abbreviated") { - out.setAbbreviated(YesNo::parse(a.value())); + out.setAbbreviated(parseValue(a.value(), context, el, "abbreviated")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -269,13 +270,14 @@ Pedal parsePedal(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parsePedalContent(out, el); + parsePedalContent(out, el, context); return out; } -void parsePedalContent(Pedal &out, pugi::xml_node el) +void parsePedalContent(Pedal &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Pedal.h b/src/private/mx/core/generated/Pedal.h index 82ec81877..4444fb490 100644 --- a/src/private/mx/core/generated/Pedal.h +++ b/src/private/mx/core/generated/Pedal.h @@ -27,6 +27,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The pedal type represents piano pedal marks, including damper and sostenuto pedal marks. The line /// attribute is yes if pedal lines are used. The sign attribute is yes if Ped, Sost, and * signs are /// used. For compatibility with older versions, the sign attribute is yes by default if the line @@ -94,9 +96,9 @@ class Pedal final std::optional m_id; }; -Pedal parsePedal(pugi::xml_node el); +Pedal parsePedal(pugi::xml_node el, const ParseContext &context); -void parsePedalContent(Pedal &out, pugi::xml_node el); +void parsePedalContent(Pedal &out, pugi::xml_node el, const ParseContext &context); void serializePedal(const Pedal &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/PedalTuning.cpp b/src/private/mx/core/generated/PedalTuning.cpp index 4ed2fb7d5..216981f08 100644 --- a/src/private/mx/core/generated/PedalTuning.cpp +++ b/src/private/mx/core/generated/PedalTuning.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PedalTuning.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void PedalTuning::setPedalAlter(Semitones value) m_pedalAlter = std::move(value); } -PedalTuning parsePedalTuning(pugi::xml_node el) +PedalTuning parsePedalTuning(pugi::xml_node el, const ParseContext &context) { PedalTuning out; for (pugi::xml_attribute a : el.attributes()) @@ -42,16 +43,16 @@ PedalTuning parsePedalTuning(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parsePedalTuningContent(out, el); + parsePedalTuningContent(out, el, context); return out; } -void parsePedalTuningContent(PedalTuning &out, pugi::xml_node el) +void parsePedalTuningContent(PedalTuning &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "pedal-step")) { - out.setPedalStep(Step::parse(childText(cursor))); + out.setPedalStep(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -60,7 +61,7 @@ void parsePedalTuningContent(PedalTuning &out, pugi::xml_node el) } if (cursorIs(cursor, "pedal-alter")) { - out.setPedalAlter(Semitones::parse(childText(cursor))); + out.setPedalAlter(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/PedalTuning.h b/src/private/mx/core/generated/PedalTuning.h index 91e3cdec9..9f2e07d83 100644 --- a/src/private/mx/core/generated/PedalTuning.h +++ b/src/private/mx/core/generated/PedalTuning.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The pedal-tuning type specifies the tuning of a single harp pedal. /// Content fields mirror the schema grammar in declaration order; the /// serializer walks them, so a wrong order is unrepresentable (plan §2.3). @@ -35,9 +37,9 @@ class PedalTuning final Semitones m_pedalAlter{}; }; -PedalTuning parsePedalTuning(pugi::xml_node el); +PedalTuning parsePedalTuning(pugi::xml_node el, const ParseContext &context); -void parsePedalTuningContent(PedalTuning &out, pugi::xml_node el); +void parsePedalTuningContent(PedalTuning &out, pugi::xml_node el, const ParseContext &context); void serializePedalTuning(const PedalTuning &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/PedalType.cpp b/src/private/mx/core/generated/PedalType.cpp index 00a5b5823..a7731c07b 100644 --- a/src/private/mx/core/generated/PedalType.cpp +++ b/src/private/mx/core/generated/PedalType.cpp @@ -66,9 +66,15 @@ bool PedalType::tryParse(std::string_view text, PedalType &out) noexcept } PedalType PedalType::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +PedalType PedalType::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { PedalType v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/PedalType.h b/src/private/mx/core/generated/PedalType.h index 180c910b4..1259d2e68 100644 --- a/src/private/mx/core/generated/PedalType.h +++ b/src/private/mx/core/generated/PedalType.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -61,6 +63,9 @@ class PedalType final /// (the import leniency policy; never produces an invalid value). static PedalType parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static PedalType parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const PedalType &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/PerMinute.cpp b/src/private/mx/core/generated/PerMinute.cpp index 09fc93e99..1a677bc1f 100644 --- a/src/private/mx/core/generated/PerMinute.cpp +++ b/src/private/mx/core/generated/PerMinute.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PerMinute.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -60,7 +61,7 @@ void PerMinute::setValue(std::string value) m_value = std::move(value); } -PerMinute parsePerMinute(pugi::xml_node el) +PerMinute parsePerMinute(pugi::xml_node el, const ParseContext &context) { PerMinute out; for (pugi::xml_attribute a : el.attributes()) @@ -72,30 +73,30 @@ PerMinute parsePerMinute(pugi::xml_node el) } if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else { throwUnknownAttribute(el, a.name()); } } - parsePerMinuteContent(out, el); + parsePerMinuteContent(out, el, context); return out; } -void parsePerMinuteContent(PerMinute &out, pugi::xml_node el) +void parsePerMinuteContent(PerMinute &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/PerMinute.h b/src/private/mx/core/generated/PerMinute.h index a0d94a226..9efda68e9 100644 --- a/src/private/mx/core/generated/PerMinute.h +++ b/src/private/mx/core/generated/PerMinute.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The per-minute type can be a number, or a text description including numbers. If a font is /// specified, it overrides the font specified for the overall metronome element. This allows /// separate specification of a music font for the beat-unit and a text font for the numeric value, @@ -45,9 +47,9 @@ class PerMinute final std::string m_value{}; }; -PerMinute parsePerMinute(pugi::xml_node el); +PerMinute parsePerMinute(pugi::xml_node el, const ParseContext &context); -void parsePerMinuteContent(PerMinute &out, pugi::xml_node el); +void parsePerMinuteContent(PerMinute &out, pugi::xml_node el, const ParseContext &context); void serializePerMinute(const PerMinute &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Percent.cpp b/src/private/mx/core/generated/Percent.cpp index 524abe4fc..f08a19140 100644 --- a/src/private/mx/core/generated/Percent.cpp +++ b/src/private/mx/core/generated/Percent.cpp @@ -42,20 +42,33 @@ std::string Percent::toString() const bool Percent::tryParse(std::string_view text, Percent &out) { - Decimal v; - if (!Decimal::tryParse(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + Percent parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = Percent{std::move(v)}; + out = std::move(parsed); return true; } Percent Percent::parse(std::string_view text) { - Percent v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +Percent Percent::parse(std::string_view text, ValueParseOutcome &outcome) +{ + Decimal v; + if (!Decimal::tryParse(text, v)) + { + outcome = ValueParseOutcome::invalid; + return Percent{}; + } + Percent out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/Percent.h b/src/private/mx/core/generated/Percent.h index 109b5bd03..c3c7cca0d 100644 --- a/src/private/mx/core/generated/Percent.h +++ b/src/private/mx/core/generated/Percent.h @@ -3,6 +3,7 @@ #pragma once #include "mx/core/Decimal.h" +#include "mx/core/Lexical.h" #include #include @@ -37,6 +38,9 @@ class Percent final /// Lenient: non-numeric text yields the clamped zero. static Percent parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static Percent parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const Percent &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Percussion.cpp b/src/private/mx/core/generated/Percussion.cpp index e2adc3992..70f2ff798 100644 --- a/src/private/mx/core/generated/Percussion.cpp +++ b/src/private/mx/core/generated/Percussion.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Percussion.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -150,7 +151,7 @@ void Percussion::setChoice(PercussionChoice value) m_choice = std::move(value); } -Percussion parsePercussion(pugi::xml_node el) +Percussion parsePercussion(pugi::xml_node el, const ParseContext &context) { Percussion out; for (pugi::xml_attribute a : el.attributes()) @@ -162,66 +163,66 @@ Percussion parsePercussion(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "enclosure") { - out.setEnclosure(EnclosureShape::parse(a.value())); + out.setEnclosure(parseValue(a.value(), context, el, "enclosure")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parsePercussionContent(out, el); + parsePercussionContent(out, el, context); return out; } -void parsePercussionContent(Percussion &out, pugi::xml_node el) +void parsePercussionContent(Percussion &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "glass") || cursorIs(cursor, "metal") || cursorIs(cursor, "wood") || @@ -229,7 +230,7 @@ void parsePercussionContent(Percussion &out, pugi::xml_node el) cursorIs(cursor, "timpani") || cursorIs(cursor, "beater") || cursorIs(cursor, "stick") || cursorIs(cursor, "stick-location") || cursorIs(cursor, "other-percussion"))) { - out.setChoice(parsePercussionChoice(el, cursor)); + out.setChoice(parsePercussionChoice(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/Percussion.h b/src/private/mx/core/generated/Percussion.h index 00ebf1715..833b6f0ba 100644 --- a/src/private/mx/core/generated/Percussion.h +++ b/src/private/mx/core/generated/Percussion.h @@ -28,6 +28,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The percussion element is used to define percussion pictogram symbols. Definitions for these /// symbols can be found in Kurt Stone's "Music Notation in the Twentieth Century" on pages 206-212 /// and 223. Some values are added to these based on how usage has evolved in the 30 years since @@ -84,9 +86,9 @@ class Percussion final PercussionChoice m_choice{}; }; -Percussion parsePercussion(pugi::xml_node el); +Percussion parsePercussion(pugi::xml_node el, const ParseContext &context); -void parsePercussionContent(Percussion &out, pugi::xml_node el); +void parsePercussionContent(Percussion &out, pugi::xml_node el, const ParseContext &context); void serializePercussion(const Percussion &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/PercussionChoice.cpp b/src/private/mx/core/generated/PercussionChoice.cpp index 53a0b6fcd..c1b47379e 100644 --- a/src/private/mx/core/generated/PercussionChoice.cpp +++ b/src/private/mx/core/generated/PercussionChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PercussionChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -70,71 +71,71 @@ PercussionChoice PercussionChoice::otherPercussion(OtherText value) return PercussionChoice{Storage{std::in_place_index<10>, std::move(value)}}; } -PercussionChoice parsePercussionChoice(pugi::xml_node el, pugi::xml_node &cursor) +PercussionChoice parsePercussionChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "glass"))) { - Glass value = parseGlass(cursor); + Glass value = parseGlass(cursor, context); cursor = nextElement(cursor); return PercussionChoice::glass(std::move(value)); } if (cursor && (cursorIs(cursor, "metal"))) { - Metal value = parseMetal(cursor); + Metal value = parseMetal(cursor, context); cursor = nextElement(cursor); return PercussionChoice::metal(std::move(value)); } if (cursor && (cursorIs(cursor, "wood"))) { - Wood value = parseWood(cursor); + Wood value = parseWood(cursor, context); cursor = nextElement(cursor); return PercussionChoice::wood(std::move(value)); } if (cursor && (cursorIs(cursor, "pitched"))) { - Pitched value = parsePitched(cursor); + Pitched value = parsePitched(cursor, context); cursor = nextElement(cursor); return PercussionChoice::pitched(std::move(value)); } if (cursor && (cursorIs(cursor, "membrane"))) { - Membrane value = parseMembrane(cursor); + Membrane value = parseMembrane(cursor, context); cursor = nextElement(cursor); return PercussionChoice::membrane(std::move(value)); } if (cursor && (cursorIs(cursor, "effect"))) { - Effect value = parseEffect(cursor); + Effect value = parseEffect(cursor, context); cursor = nextElement(cursor); return PercussionChoice::effect(std::move(value)); } if (cursor && (cursorIs(cursor, "timpani"))) { - Timpani value = parseTimpani(cursor); + Timpani value = parseTimpani(cursor, context); cursor = nextElement(cursor); return PercussionChoice::timpani(std::move(value)); } if (cursor && (cursorIs(cursor, "beater"))) { - Beater value = parseBeater(cursor); + Beater value = parseBeater(cursor, context); cursor = nextElement(cursor); return PercussionChoice::beater(std::move(value)); } if (cursor && (cursorIs(cursor, "stick"))) { - Stick value = parseStick(cursor); + Stick value = parseStick(cursor, context); cursor = nextElement(cursor); return PercussionChoice::stick(std::move(value)); } if (cursor && (cursorIs(cursor, "stick-location"))) { - StickLocation value = StickLocation::parse(childText(cursor)); + StickLocation value = parseValue(childText(cursor), context, cursor, nullptr); cursor = nextElement(cursor); return PercussionChoice::stickLocation(std::move(value)); } if (cursor && (cursorIs(cursor, "other-percussion"))) { - OtherText value = parseOtherText(cursor); + OtherText value = parseOtherText(cursor, context); cursor = nextElement(cursor); return PercussionChoice::otherPercussion(std::move(value)); } diff --git a/src/private/mx/core/generated/PercussionChoice.h b/src/private/mx/core/generated/PercussionChoice.h index f06cdbccd..d05797253 100644 --- a/src/private/mx/core/generated/PercussionChoice.h +++ b/src/private/mx/core/generated/PercussionChoice.h @@ -27,6 +27,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -216,7 +218,7 @@ class PercussionChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -PercussionChoice parsePercussionChoice(pugi::xml_node el, pugi::xml_node &cursor); +PercussionChoice parsePercussionChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializePercussionChoice(const PercussionChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Pitch.cpp b/src/private/mx/core/generated/Pitch.cpp index 82cd46e18..bc92fe254 100644 --- a/src/private/mx/core/generated/Pitch.cpp +++ b/src/private/mx/core/generated/Pitch.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Pitch.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,7 +41,7 @@ void Pitch::setOctave(Octave value) m_octave = std::move(value); } -Pitch parsePitch(pugi::xml_node el) +Pitch parsePitch(pugi::xml_node el, const ParseContext &context) { Pitch out; for (pugi::xml_attribute a : el.attributes()) @@ -52,16 +53,16 @@ Pitch parsePitch(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parsePitchContent(out, el); + parsePitchContent(out, el, context); return out; } -void parsePitchContent(Pitch &out, pugi::xml_node el) +void parsePitchContent(Pitch &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "step")) { - out.setStep(Step::parse(childText(cursor))); + out.setStep(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -70,12 +71,12 @@ void parsePitchContent(Pitch &out, pugi::xml_node el) } if (cursorIs(cursor, "alter")) { - out.setAlter(Semitones::parse(childText(cursor))); + out.setAlter(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "octave")) { - out.setOctave(Octave::parse(childText(cursor))); + out.setOctave(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/Pitch.h b/src/private/mx/core/generated/Pitch.h index 04c153ed7..85282e037 100644 --- a/src/private/mx/core/generated/Pitch.h +++ b/src/private/mx/core/generated/Pitch.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Pitch is represented as a combination of the step of the diatonic scale, the chromatic /// alteration, and the octave. /// Content fields mirror the schema grammar in declaration order; the @@ -40,9 +42,9 @@ class Pitch final Octave m_octave{}; }; -Pitch parsePitch(pugi::xml_node el); +Pitch parsePitch(pugi::xml_node el, const ParseContext &context); -void parsePitchContent(Pitch &out, pugi::xml_node el); +void parsePitchContent(Pitch &out, pugi::xml_node el, const ParseContext &context); void serializePitch(const Pitch &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Pitched.cpp b/src/private/mx/core/generated/Pitched.cpp index 548ab8298..7bf231f2f 100644 --- a/src/private/mx/core/generated/Pitched.cpp +++ b/src/private/mx/core/generated/Pitched.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Pitched.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Pitched::setValue(PitchedValue value) m_value = std::move(value); } -Pitched parsePitched(pugi::xml_node el) +Pitched parsePitched(pugi::xml_node el, const ParseContext &context) { Pitched out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ Pitched parsePitched(pugi::xml_node el) } if (aname == "smufl") { - out.setSmufl(SmuflPictogramGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else { throwUnknownAttribute(el, a.name()); } } - parsePitchedContent(out, el); + parsePitchedContent(out, el, context); return out; } -void parsePitchedContent(Pitched &out, pugi::xml_node el) +void parsePitchedContent(Pitched &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(PitchedValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Pitched.h b/src/private/mx/core/generated/Pitched.h index a8d577826..0ad68baa2 100644 --- a/src/private/mx/core/generated/Pitched.h +++ b/src/private/mx/core/generated/Pitched.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The pitched-value type represents pictograms for pitched percussion instruments. The smufl /// attribute is used to distinguish different SMuFL glyphs for a particular pictogram within the /// Tuned mallet percussion pictograms range. @@ -33,9 +35,9 @@ class Pitched final PitchedValue m_value{}; }; -Pitched parsePitched(pugi::xml_node el); +Pitched parsePitched(pugi::xml_node el, const ParseContext &context); -void parsePitchedContent(Pitched &out, pugi::xml_node el); +void parsePitchedContent(Pitched &out, pugi::xml_node el, const ParseContext &context); void serializePitched(const Pitched &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/PitchedValue.cpp b/src/private/mx/core/generated/PitchedValue.cpp index 929c0b528..bc37f12dc 100644 --- a/src/private/mx/core/generated/PitchedValue.cpp +++ b/src/private/mx/core/generated/PitchedValue.cpp @@ -87,9 +87,15 @@ bool PitchedValue::tryParse(std::string_view text, PitchedValue &out) noexcept } PitchedValue PitchedValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +PitchedValue PitchedValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { PitchedValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/PitchedValue.h b/src/private/mx/core/generated/PitchedValue.h index d5b0e6397..3fd94975a 100644 --- a/src/private/mx/core/generated/PitchedValue.h +++ b/src/private/mx/core/generated/PitchedValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -59,6 +61,9 @@ class PitchedValue final /// (the import leniency policy; never produces an invalid value). static PitchedValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static PitchedValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const PitchedValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/PlacementText.cpp b/src/private/mx/core/generated/PlacementText.cpp index 65d79f1b5..4c9c3cb04 100644 --- a/src/private/mx/core/generated/PlacementText.cpp +++ b/src/private/mx/core/generated/PlacementText.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PlacementText.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -120,7 +121,7 @@ void PlacementText::setValue(std::string value) m_value = std::move(value); } -PlacementText parsePlacementText(pugi::xml_node el) +PlacementText parsePlacementText(pugi::xml_node el, const ParseContext &context) { PlacementText out; for (pugi::xml_attribute a : el.attributes()) @@ -132,54 +133,54 @@ PlacementText parsePlacementText(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else { throwUnknownAttribute(el, a.name()); } } - parsePlacementTextContent(out, el); + parsePlacementTextContent(out, el, context); return out; } -void parsePlacementTextContent(PlacementText &out, pugi::xml_node el) +void parsePlacementTextContent(PlacementText &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/PlacementText.h b/src/private/mx/core/generated/PlacementText.h index d72111cb2..f85dbb8be 100644 --- a/src/private/mx/core/generated/PlacementText.h +++ b/src/private/mx/core/generated/PlacementText.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The placement-text type represents a text element with print-style and placement attribute /// groups. class PlacementText final @@ -64,9 +66,9 @@ class PlacementText final std::string m_value{}; }; -PlacementText parsePlacementText(pugi::xml_node el); +PlacementText parsePlacementText(pugi::xml_node el, const ParseContext &context); -void parsePlacementTextContent(PlacementText &out, pugi::xml_node el); +void parsePlacementTextContent(PlacementText &out, pugi::xml_node el, const ParseContext &context); void serializePlacementText(const PlacementText &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Play.cpp b/src/private/mx/core/generated/Play.cpp index 6745eb48c..3eeeb4747 100644 --- a/src/private/mx/core/generated/Play.cpp +++ b/src/private/mx/core/generated/Play.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Play.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,7 +36,7 @@ void Play::setChoice(std::vector value) m_choice = std::move(value); } -Play parsePlay(pugi::xml_node el) +Play parsePlay(pugi::xml_node el, const ParseContext &context) { Play out; for (pugi::xml_attribute a : el.attributes()) @@ -47,24 +48,24 @@ Play parsePlay(pugi::xml_node el) } if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parsePlayContent(out, el); + parsePlayContent(out, el, context); return out; } -void parsePlayContent(Play &out, pugi::xml_node el) +void parsePlayContent(Play &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); while (cursor && (cursorIs(cursor, "ipa") || cursorIs(cursor, "mute") || cursorIs(cursor, "semi-pitched") || cursorIs(cursor, "other-play"))) { - out.addChoice(parsePlayChoice(el, cursor)); + out.addChoice(parsePlayChoice(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/Play.h b/src/private/mx/core/generated/Play.h index 51737531f..50e941e31 100644 --- a/src/private/mx/core/generated/Play.h +++ b/src/private/mx/core/generated/Play.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The play type specifies playback techniques to be used in conjunction with the instrument-sound /// element. When used as part of a sound element, it applies to all notes going forward in score /// order. In multi-instrument parts, the affected instrument should be specified using the id @@ -40,9 +42,9 @@ class Play final std::vector m_choice; }; -Play parsePlay(pugi::xml_node el); +Play parsePlay(pugi::xml_node el, const ParseContext &context); -void parsePlayContent(Play &out, pugi::xml_node el); +void parsePlayContent(Play &out, pugi::xml_node el, const ParseContext &context); void serializePlay(const Play &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/PlayChoice.cpp b/src/private/mx/core/generated/PlayChoice.cpp index 3b4cc2d83..c07ad4f2b 100644 --- a/src/private/mx/core/generated/PlayChoice.cpp +++ b/src/private/mx/core/generated/PlayChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PlayChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,7 +36,7 @@ PlayChoice PlayChoice::otherPlay(OtherPlay value) return PlayChoice{Storage{std::in_place_index<3>, std::move(value)}}; } -PlayChoice parsePlayChoice(pugi::xml_node el, pugi::xml_node &cursor) +PlayChoice parsePlayChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "ipa"))) { @@ -45,19 +46,19 @@ PlayChoice parsePlayChoice(pugi::xml_node el, pugi::xml_node &cursor) } if (cursor && (cursorIs(cursor, "mute"))) { - Mute value = Mute::parse(childText(cursor)); + Mute value = parseValue(childText(cursor), context, cursor, nullptr); cursor = nextElement(cursor); return PlayChoice::mute(std::move(value)); } if (cursor && (cursorIs(cursor, "semi-pitched"))) { - SemiPitched value = SemiPitched::parse(childText(cursor)); + SemiPitched value = parseValue(childText(cursor), context, cursor, nullptr); cursor = nextElement(cursor); return PlayChoice::semiPitched(std::move(value)); } if (cursor && (cursorIs(cursor, "other-play"))) { - OtherPlay value = parseOtherPlay(cursor); + OtherPlay value = parseOtherPlay(cursor, context); cursor = nextElement(cursor); return PlayChoice::otherPlay(std::move(value)); } diff --git a/src/private/mx/core/generated/PlayChoice.h b/src/private/mx/core/generated/PlayChoice.h index 8c167cd65..a713ef622 100644 --- a/src/private/mx/core/generated/PlayChoice.h +++ b/src/private/mx/core/generated/PlayChoice.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -109,7 +111,7 @@ class PlayChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -PlayChoice parsePlayChoice(pugi::xml_node el, pugi::xml_node &cursor); +PlayChoice parsePlayChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializePlayChoice(const PlayChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Player.cpp b/src/private/mx/core/generated/Player.cpp index f408054d1..1c8d6bf7b 100644 --- a/src/private/mx/core/generated/Player.cpp +++ b/src/private/mx/core/generated/Player.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Player.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Player::setPlayerName(std::string value) m_playerName = std::move(value); } -Player parsePlayer(pugi::xml_node el) +Player parsePlayer(pugi::xml_node el, const ParseContext &context) { Player out; bool seen_id = false; @@ -44,7 +45,7 @@ Player parsePlayer(pugi::xml_node el) if (aname == "id") { seen_id = true; - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -55,11 +56,11 @@ Player parsePlayer(pugi::xml_node el) { throwMissingAttribute(el, "id"); } - parsePlayerContent(out, el); + parsePlayerContent(out, el, context); return out; } -void parsePlayerContent(Player &out, pugi::xml_node el) +void parsePlayerContent(Player &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "player-name")) diff --git a/src/private/mx/core/generated/Player.h b/src/private/mx/core/generated/Player.h index 851b3317d..ba447b70e 100644 --- a/src/private/mx/core/generated/Player.h +++ b/src/private/mx/core/generated/Player.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The player type allows for multiple players per score-part for use in listening applications. One /// player may play multiple instruments, while a single instrument may include multiple players in /// divisi sections. @@ -37,9 +39,9 @@ class Player final std::string m_playerName{}; }; -Player parsePlayer(pugi::xml_node el); +Player parsePlayer(pugi::xml_node el, const ParseContext &context); -void parsePlayerContent(Player &out, pugi::xml_node el); +void parsePlayerContent(Player &out, pugi::xml_node el, const ParseContext &context); void serializePlayer(const Player &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/PositiveDivisions.cpp b/src/private/mx/core/generated/PositiveDivisions.cpp index 73d5cfa66..fd7ef10b8 100644 --- a/src/private/mx/core/generated/PositiveDivisions.cpp +++ b/src/private/mx/core/generated/PositiveDivisions.cpp @@ -38,20 +38,33 @@ std::string PositiveDivisions::toString() const bool PositiveDivisions::tryParse(std::string_view text, PositiveDivisions &out) { - Decimal v; - if (!Decimal::tryParse(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + PositiveDivisions parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = PositiveDivisions{std::move(v)}; + out = std::move(parsed); return true; } PositiveDivisions PositiveDivisions::parse(std::string_view text) { - PositiveDivisions v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +PositiveDivisions PositiveDivisions::parse(std::string_view text, ValueParseOutcome &outcome) +{ + Decimal v; + if (!Decimal::tryParse(text, v)) + { + outcome = ValueParseOutcome::invalid; + return PositiveDivisions{}; + } + PositiveDivisions out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/PositiveDivisions.h b/src/private/mx/core/generated/PositiveDivisions.h index bf23bbdcd..324c9e043 100644 --- a/src/private/mx/core/generated/PositiveDivisions.h +++ b/src/private/mx/core/generated/PositiveDivisions.h @@ -3,6 +3,7 @@ #pragma once #include "mx/core/Decimal.h" +#include "mx/core/Lexical.h" #include #include @@ -37,6 +38,9 @@ class PositiveDivisions final /// Lenient: non-numeric text yields the clamped zero. static PositiveDivisions parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static PositiveDivisions parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const PositiveDivisions &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/PositiveIntegerOrEmpty.cpp b/src/private/mx/core/generated/PositiveIntegerOrEmpty.cpp index 6b4543d82..353f4e2aa 100644 --- a/src/private/mx/core/generated/PositiveIntegerOrEmpty.cpp +++ b/src/private/mx/core/generated/PositiveIntegerOrEmpty.cpp @@ -60,12 +60,24 @@ bool PositiveIntegerOrEmpty::tryParse(std::string_view text, PositiveIntegerOrEm } PositiveIntegerOrEmpty PositiveIntegerOrEmpty::parse(std::string_view text) +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +PositiveIntegerOrEmpty PositiveIntegerOrEmpty::parse(std::string_view text, ValueParseOutcome &outcome) { PositiveIntegerOrEmpty out; if (tryParse(text, out)) { + outcome = ValueParseOutcome::valid; + if (int raw{}; out.isPositiveInteger() && tryParseInt(text, raw) && out.asPositiveInteger() != raw) + { + outcome = ValueParseOutcome::adjusted; + } return out; } + outcome = ValueParseOutcome::invalid; return PositiveIntegerOrEmpty::positiveInteger(parseInt(text)); } diff --git a/src/private/mx/core/generated/PositiveIntegerOrEmpty.h b/src/private/mx/core/generated/PositiveIntegerOrEmpty.h index 791729511..e68813ef1 100644 --- a/src/private/mx/core/generated/PositiveIntegerOrEmpty.h +++ b/src/private/mx/core/generated/PositiveIntegerOrEmpty.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include #include @@ -65,6 +67,10 @@ class PositiveIntegerOrEmpty final /// parse (never produces an invalid value). static PositiveIntegerOrEmpty parse(std::string_view text); + /// Lenient, and says whether no member matched or a matched number was + /// clamped. + static PositiveIntegerOrEmpty parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const PositiveIntegerOrEmpty &other) const = default; private: diff --git a/src/private/mx/core/generated/PrincipalVoice.cpp b/src/private/mx/core/generated/PrincipalVoice.cpp index 463652755..65099afe3 100644 --- a/src/private/mx/core/generated/PrincipalVoice.cpp +++ b/src/private/mx/core/generated/PrincipalVoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/PrincipalVoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -160,7 +161,7 @@ void PrincipalVoice::setValue(std::string value) m_value = std::move(value); } -PrincipalVoice parsePrincipalVoice(pugi::xml_node el) +PrincipalVoice parsePrincipalVoice(pugi::xml_node el, const ParseContext &context) { PrincipalVoice out; bool seen_type = false; @@ -175,60 +176,60 @@ PrincipalVoice parsePrincipalVoice(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStop::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "symbol") { seen_symbol = true; - out.setSymbol(PrincipalVoiceSymbol::parse(a.value())); + out.setSymbol(parseValue(a.value(), context, el, "symbol")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -243,11 +244,11 @@ PrincipalVoice parsePrincipalVoice(pugi::xml_node el) { throwMissingAttribute(el, "symbol"); } - parsePrincipalVoiceContent(out, el); + parsePrincipalVoiceContent(out, el, context); return out; } -void parsePrincipalVoiceContent(PrincipalVoice &out, pugi::xml_node el) +void parsePrincipalVoiceContent(PrincipalVoice &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/PrincipalVoice.h b/src/private/mx/core/generated/PrincipalVoice.h index 7d6ed9f49..46b3c72ea 100644 --- a/src/private/mx/core/generated/PrincipalVoice.h +++ b/src/private/mx/core/generated/PrincipalVoice.h @@ -26,6 +26,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The principal-voice type represents principal and secondary voices in a score, either for /// analysis or for square bracket symbols that appear in a score. The element content is used for /// analysis and may be any text value. The symbol attribute indicates the type of symbol used. When @@ -84,9 +86,9 @@ class PrincipalVoice final std::string m_value{}; }; -PrincipalVoice parsePrincipalVoice(pugi::xml_node el); +PrincipalVoice parsePrincipalVoice(pugi::xml_node el, const ParseContext &context); -void parsePrincipalVoiceContent(PrincipalVoice &out, pugi::xml_node el); +void parsePrincipalVoiceContent(PrincipalVoice &out, pugi::xml_node el, const ParseContext &context); void serializePrincipalVoice(const PrincipalVoice &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/PrincipalVoiceSymbol.cpp b/src/private/mx/core/generated/PrincipalVoiceSymbol.cpp index f360ac000..9d24ba0ac 100644 --- a/src/private/mx/core/generated/PrincipalVoiceSymbol.cpp +++ b/src/private/mx/core/generated/PrincipalVoiceSymbol.cpp @@ -54,9 +54,15 @@ bool PrincipalVoiceSymbol::tryParse(std::string_view text, PrincipalVoiceSymbol } PrincipalVoiceSymbol PrincipalVoiceSymbol::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +PrincipalVoiceSymbol PrincipalVoiceSymbol::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { PrincipalVoiceSymbol v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/PrincipalVoiceSymbol.h b/src/private/mx/core/generated/PrincipalVoiceSymbol.h index 0eb3481b1..e739b1faa 100644 --- a/src/private/mx/core/generated/PrincipalVoiceSymbol.h +++ b/src/private/mx/core/generated/PrincipalVoiceSymbol.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -47,6 +49,9 @@ class PrincipalVoiceSymbol final /// (the import leniency policy; never produces an invalid value). static PrincipalVoiceSymbol parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static PrincipalVoiceSymbol parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const PrincipalVoiceSymbol &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Print.cpp b/src/private/mx/core/generated/Print.cpp index fd28a4991..bbbddbe0c 100644 --- a/src/private/mx/core/generated/Print.cpp +++ b/src/private/mx/core/generated/Print.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Print.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -120,7 +121,7 @@ void Print::setPartAbbreviationDisplay(std::optional value) m_partAbbreviationDisplay = std::move(value); } -Print parsePrint(pugi::xml_node el) +Print parsePrint(pugi::xml_node el, const ParseContext &context) { Print out; for (pugi::xml_attribute a : el.attributes()) @@ -132,19 +133,19 @@ Print parsePrint(pugi::xml_node el) } if (aname == "staff-spacing") { - out.setStaffSpacing(Tenths::parse(a.value())); + out.setStaffSpacing(parseValue(a.value(), context, el, "staff-spacing")); } else if (aname == "new-system") { - out.setNewSystem(YesNo::parse(a.value())); + out.setNewSystem(parseValue(a.value(), context, el, "new-system")); } else if (aname == "new-page") { - out.setNewPage(YesNo::parse(a.value())); + out.setNewPage(parseValue(a.value(), context, el, "new-page")); } else if (aname == "blank-page") { - out.setBlankPage(parseInt(a.value())); + out.setBlankPage(parseIntegerValue(a.value(), context, el, "blank-page")); } else if (aname == "page-number") { @@ -152,43 +153,43 @@ Print parsePrint(pugi::xml_node el) } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parsePrintContent(out, el); + parsePrintContent(out, el, context); return out; } -void parsePrintContent(Print &out, pugi::xml_node el) +void parsePrintContent(Print &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "page-layout") || cursorIs(cursor, "system-layout") || cursorIs(cursor, "staff-layout"))) { - out.setLayout(parseLayoutGroup(el, cursor)); + out.setLayout(parseLayoutGroup(el, cursor, context)); } if (cursorIs(cursor, "measure-layout")) { - out.setMeasureLayout(parseMeasureLayout(cursor)); + out.setMeasureLayout(parseMeasureLayout(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "measure-numbering")) { - out.setMeasureNumbering(parseMeasureNumbering(cursor)); + out.setMeasureNumbering(parseMeasureNumbering(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "part-name-display")) { - out.setPartNameDisplay(parseNameDisplay(cursor)); + out.setPartNameDisplay(parseNameDisplay(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "part-abbreviation-display")) { - out.setPartAbbreviationDisplay(parseNameDisplay(cursor)); + out.setPartAbbreviationDisplay(parseNameDisplay(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Print.h b/src/private/mx/core/generated/Print.h index a94e7c8f8..f076d5f3e 100644 --- a/src/private/mx/core/generated/Print.h +++ b/src/private/mx/core/generated/Print.h @@ -24,6 +24,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The print type contains general printing parameters, including layout elements. The /// part-name-display and part-abbreviation-display elements may also be used here to change how a /// part name or abbreviation is displayed over the course of a piece. They take effect when the @@ -73,9 +75,9 @@ class Print final std::optional m_partAbbreviationDisplay; }; -Print parsePrint(pugi::xml_node el); +Print parsePrint(pugi::xml_node el, const ParseContext &context); -void parsePrintContent(Print &out, pugi::xml_node el); +void parsePrintContent(Print &out, pugi::xml_node el, const ParseContext &context); void serializePrint(const Print &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Release.cpp b/src/private/mx/core/generated/Release.cpp index d0e63f7dc..e86256650 100644 --- a/src/private/mx/core/generated/Release.cpp +++ b/src/private/mx/core/generated/Release.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Release.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -20,7 +21,7 @@ void Release::setOffset(std::optional value) m_offset = std::move(value); } -Release parseRelease(pugi::xml_node el) +Release parseRelease(pugi::xml_node el, const ParseContext &context) { Release out; for (pugi::xml_attribute a : el.attributes()) @@ -32,14 +33,14 @@ Release parseRelease(pugi::xml_node el) } if (aname == "offset") { - out.setOffset(Divisions::parse(a.value())); + out.setOffset(parseValue(a.value(), context, el, "offset")); } else { throwUnknownAttribute(el, a.name()); } } - parseEmptyContent(out, el); + parseEmptyContent(out, el, context); return out; } diff --git a/src/private/mx/core/generated/Release.h b/src/private/mx/core/generated/Release.h index d9a9aef64..5e53a6670 100644 --- a/src/private/mx/core/generated/Release.h +++ b/src/private/mx/core/generated/Release.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The release type indicates that a bend is a release rather than a normal bend or pre-bend. The /// offset attribute specifies where the release starts in terms of divisions relative to the current /// note. The first-beat and last-beat attributes of the parent bend element are relative to the @@ -33,7 +35,7 @@ class Release : public Empty std::optional m_offset; }; -Release parseRelease(pugi::xml_node el); +Release parseRelease(pugi::xml_node el, const ParseContext &context); void serializeRelease(const Release &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Repeat.cpp b/src/private/mx/core/generated/Repeat.cpp index 7a3e7c557..d8a188ab8 100644 --- a/src/private/mx/core/generated/Repeat.cpp +++ b/src/private/mx/core/generated/Repeat.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Repeat.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,7 +51,7 @@ void Repeat::setWinged(std::optional value) m_winged = std::move(value); } -Repeat parseRepeat(pugi::xml_node el) +Repeat parseRepeat(pugi::xml_node el, const ParseContext &context) { Repeat out; bool seen_direction = false; @@ -64,19 +65,19 @@ Repeat parseRepeat(pugi::xml_node el) if (aname == "direction") { seen_direction = true; - out.setDirection(BackwardForward::parse(a.value())); + out.setDirection(parseValue(a.value(), context, el, "direction")); } else if (aname == "times") { - out.setTimes(parseInt(a.value())); + out.setTimes(parseIntegerValue(a.value(), context, el, "times")); } else if (aname == "after-jump") { - out.setAfterJump(YesNo::parse(a.value())); + out.setAfterJump(parseValue(a.value(), context, el, "after-jump")); } else if (aname == "winged") { - out.setWinged(Winged::parse(a.value())); + out.setWinged(parseValue(a.value(), context, el, "winged")); } else { @@ -87,13 +88,14 @@ Repeat parseRepeat(pugi::xml_node el) { throwMissingAttribute(el, "direction"); } - parseRepeatContent(out, el); + parseRepeatContent(out, el, context); return out; } -void parseRepeatContent(Repeat &out, pugi::xml_node el) +void parseRepeatContent(Repeat &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Repeat.h b/src/private/mx/core/generated/Repeat.h index 3818602c7..d20c14124 100644 --- a/src/private/mx/core/generated/Repeat.h +++ b/src/private/mx/core/generated/Repeat.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The repeat type represents repeat marks. The start of the repeat has a forward direction while /// the end of the repeat has a backward direction. The times and after-jump attributes are only used /// with backward repeats that are not part of an ending. The times attribute indicates the number of @@ -42,9 +44,9 @@ class Repeat final std::optional m_winged; }; -Repeat parseRepeat(pugi::xml_node el); +Repeat parseRepeat(pugi::xml_node el, const ParseContext &context); -void parseRepeatContent(Repeat &out, pugi::xml_node el); +void parseRepeatContent(Repeat &out, pugi::xml_node el, const ParseContext &context); void serializeRepeat(const Repeat &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Rest.cpp b/src/private/mx/core/generated/Rest.cpp index c74da8625..3db1d4786 100644 --- a/src/private/mx/core/generated/Rest.cpp +++ b/src/private/mx/core/generated/Rest.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Rest.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Rest::setDisplayStepOctave(std::optional value) m_displayStepOctave = std::move(value); } -Rest parseRest(pugi::xml_node el) +Rest parseRest(pugi::xml_node el, const ParseContext &context) { Rest out; for (pugi::xml_attribute a : el.attributes()) @@ -42,23 +43,23 @@ Rest parseRest(pugi::xml_node el) } if (aname == "measure") { - out.setMeasure(YesNo::parse(a.value())); + out.setMeasure(parseValue(a.value(), context, el, "measure")); } else { throwUnknownAttribute(el, a.name()); } } - parseRestContent(out, el); + parseRestContent(out, el, context); return out; } -void parseRestContent(Rest &out, pugi::xml_node el) +void parseRestContent(Rest &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "display-step"))) { - out.setDisplayStepOctave(parseDisplayStepOctaveGroup(el, cursor)); + out.setDisplayStepOctave(parseDisplayStepOctaveGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/Rest.h b/src/private/mx/core/generated/Rest.h index a5f41f733..ab49ec4d3 100644 --- a/src/private/mx/core/generated/Rest.h +++ b/src/private/mx/core/generated/Rest.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The rest element indicates notated rests or silences. Rest elements are usually empty, but /// placement on the staff can be specified using display-step and display-octave elements. If the /// measure attribute is set to yes, this indicates this is a complete measure rest. @@ -37,9 +39,9 @@ class Rest final std::optional m_displayStepOctave; }; -Rest parseRest(pugi::xml_node el); +Rest parseRest(pugi::xml_node el, const ParseContext &context); -void parseRestContent(Rest &out, pugi::xml_node el); +void parseRestContent(Rest &out, pugi::xml_node el, const ParseContext &context); void serializeRest(const Rest &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/RightLeftMiddle.cpp b/src/private/mx/core/generated/RightLeftMiddle.cpp index a73f7ffc1..cc2dd4a37 100644 --- a/src/private/mx/core/generated/RightLeftMiddle.cpp +++ b/src/private/mx/core/generated/RightLeftMiddle.cpp @@ -48,9 +48,15 @@ bool RightLeftMiddle::tryParse(std::string_view text, RightLeftMiddle &out) noex } RightLeftMiddle RightLeftMiddle::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +RightLeftMiddle RightLeftMiddle::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { RightLeftMiddle v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/RightLeftMiddle.h b/src/private/mx/core/generated/RightLeftMiddle.h index 2347b4276..190dca905 100644 --- a/src/private/mx/core/generated/RightLeftMiddle.h +++ b/src/private/mx/core/generated/RightLeftMiddle.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -42,6 +44,9 @@ class RightLeftMiddle final /// (the import leniency policy; never produces an invalid value). static RightLeftMiddle parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static RightLeftMiddle parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const RightLeftMiddle &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Root.cpp b/src/private/mx/core/generated/Root.cpp index ad53df3f8..a5a67bf72 100644 --- a/src/private/mx/core/generated/Root.cpp +++ b/src/private/mx/core/generated/Root.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Root.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Root::setRootAlter(std::optional value) m_rootAlter = std::move(value); } -Root parseRoot(pugi::xml_node el) +Root parseRoot(pugi::xml_node el, const ParseContext &context) { Root out; for (pugi::xml_attribute a : el.attributes()) @@ -42,16 +43,16 @@ Root parseRoot(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseRootContent(out, el); + parseRootContent(out, el, context); return out; } -void parseRootContent(Root &out, pugi::xml_node el) +void parseRootContent(Root &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "root-step")) { - out.setRootStep(parseRootStep(cursor)); + out.setRootStep(parseRootStep(cursor, context)); cursor = nextElement(cursor); } else @@ -60,7 +61,7 @@ void parseRootContent(Root &out, pugi::xml_node el) } if (cursorIs(cursor, "root-alter")) { - out.setRootAlter(parseHarmonyAlter(cursor)); + out.setRootAlter(parseHarmonyAlter(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Root.h b/src/private/mx/core/generated/Root.h index 003ccdc02..3d9cd849d 100644 --- a/src/private/mx/core/generated/Root.h +++ b/src/private/mx/core/generated/Root.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The root type indicates a pitch like C, D, E vs. a scale degree like 1, 2, 3. It is used with /// chord symbols in popular music. The root element has a root-step and optional root-alter element /// similar to the step and alter elements, but renamed to distinguish the different musical @@ -38,9 +40,9 @@ class Root final std::optional m_rootAlter; }; -Root parseRoot(pugi::xml_node el); +Root parseRoot(pugi::xml_node el, const ParseContext &context); -void parseRootContent(Root &out, pugi::xml_node el); +void parseRootContent(Root &out, pugi::xml_node el, const ParseContext &context); void serializeRoot(const Root &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/RootStep.cpp b/src/private/mx/core/generated/RootStep.cpp index 9c64d1ebb..59e52d1d2 100644 --- a/src/private/mx/core/generated/RootStep.cpp +++ b/src/private/mx/core/generated/RootStep.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/RootStep.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -120,7 +121,7 @@ void RootStep::setValue(Step value) m_value = std::move(value); } -RootStep parseRootStep(pugi::xml_node el) +RootStep parseRootStep(pugi::xml_node el, const ParseContext &context) { RootStep out; for (pugi::xml_attribute a : el.attributes()) @@ -136,52 +137,52 @@ RootStep parseRootStep(pugi::xml_node el) } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseRootStepContent(out, el); + parseRootStepContent(out, el, context); return out; } -void parseRootStepContent(RootStep &out, pugi::xml_node el) +void parseRootStepContent(RootStep &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(Step::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/RootStep.h b/src/private/mx/core/generated/RootStep.h index c83b570bc..508bf630f 100644 --- a/src/private/mx/core/generated/RootStep.h +++ b/src/private/mx/core/generated/RootStep.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The root-step type represents the pitch step of the root of the current chord within the harmony /// element. The text attribute indicates how the root should appear in a score if not using the /// element contents. @@ -65,9 +67,9 @@ class RootStep final Step m_value{}; }; -RootStep parseRootStep(pugi::xml_node el); +RootStep parseRootStep(pugi::xml_node el, const ParseContext &context); -void parseRootStepContent(RootStep &out, pugi::xml_node el); +void parseRootStepContent(RootStep &out, pugi::xml_node el, const ParseContext &context); void serializeRootStep(const RootStep &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/RotationDegrees.cpp b/src/private/mx/core/generated/RotationDegrees.cpp index a7f4dbd87..c4cfde395 100644 --- a/src/private/mx/core/generated/RotationDegrees.cpp +++ b/src/private/mx/core/generated/RotationDegrees.cpp @@ -42,20 +42,33 @@ std::string RotationDegrees::toString() const bool RotationDegrees::tryParse(std::string_view text, RotationDegrees &out) { - Decimal v; - if (!Decimal::tryParse(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + RotationDegrees parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = RotationDegrees{std::move(v)}; + out = std::move(parsed); return true; } RotationDegrees RotationDegrees::parse(std::string_view text) { - RotationDegrees v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +RotationDegrees RotationDegrees::parse(std::string_view text, ValueParseOutcome &outcome) +{ + Decimal v; + if (!Decimal::tryParse(text, v)) + { + outcome = ValueParseOutcome::invalid; + return RotationDegrees{}; + } + RotationDegrees out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/RotationDegrees.h b/src/private/mx/core/generated/RotationDegrees.h index 224c98e60..bd4485c63 100644 --- a/src/private/mx/core/generated/RotationDegrees.h +++ b/src/private/mx/core/generated/RotationDegrees.h @@ -3,6 +3,7 @@ #pragma once #include "mx/core/Decimal.h" +#include "mx/core/Lexical.h" #include #include @@ -38,6 +39,9 @@ class RotationDegrees final /// Lenient: non-numeric text yields the clamped zero. static RotationDegrees parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static RotationDegrees parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const RotationDegrees &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Scaling.cpp b/src/private/mx/core/generated/Scaling.cpp index 22fa1d682..e1285000c 100644 --- a/src/private/mx/core/generated/Scaling.cpp +++ b/src/private/mx/core/generated/Scaling.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Scaling.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Scaling::setTenths(Tenths value) m_tenths = std::move(value); } -Scaling parseScaling(pugi::xml_node el) +Scaling parseScaling(pugi::xml_node el, const ParseContext &context) { Scaling out; for (pugi::xml_attribute a : el.attributes()) @@ -42,16 +43,16 @@ Scaling parseScaling(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseScalingContent(out, el); + parseScalingContent(out, el, context); return out; } -void parseScalingContent(Scaling &out, pugi::xml_node el) +void parseScalingContent(Scaling &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "millimeters")) { - out.setMillimeters(Millimeters::parse(childText(cursor))); + out.setMillimeters(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -60,7 +61,7 @@ void parseScalingContent(Scaling &out, pugi::xml_node el) } if (cursorIs(cursor, "tenths")) { - out.setTenths(Tenths::parse(childText(cursor))); + out.setTenths(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/Scaling.h b/src/private/mx/core/generated/Scaling.h index 851a2aa8b..7fe193445 100644 --- a/src/private/mx/core/generated/Scaling.h +++ b/src/private/mx/core/generated/Scaling.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Margins, page sizes, and distances are all measured in tenths to keep MusicXML data in a /// consistent coordinate system as much as possible. The translation to absolute units is done with /// the scaling type, which specifies how many millimeters are equal to how many tenths. For a staff @@ -39,9 +41,9 @@ class Scaling final Tenths m_tenths{}; }; -Scaling parseScaling(pugi::xml_node el); +Scaling parseScaling(pugi::xml_node el, const ParseContext &context); -void parseScalingContent(Scaling &out, pugi::xml_node el); +void parseScalingContent(Scaling &out, pugi::xml_node el, const ParseContext &context); void serializeScaling(const Scaling &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Scordatura.cpp b/src/private/mx/core/generated/Scordatura.cpp index c8bd60b56..0016c9675 100644 --- a/src/private/mx/core/generated/Scordatura.cpp +++ b/src/private/mx/core/generated/Scordatura.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Scordatura.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,7 +36,7 @@ void Scordatura::setAccord(OneOrMore value) m_accord = std::move(value); } -Scordatura parseScordatura(pugi::xml_node el) +Scordatura parseScordatura(pugi::xml_node el, const ParseContext &context) { Scordatura out; for (pugi::xml_attribute a : el.attributes()) @@ -47,29 +48,29 @@ Scordatura parseScordatura(pugi::xml_node el) } if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseScordaturaContent(out, el); + parseScordaturaContent(out, el, context); return out; } -void parseScordaturaContent(Scordatura &out, pugi::xml_node el) +void parseScordaturaContent(Scordatura &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (!cursorIs(cursor, "accord")) { throwMissingOrMisplaced(el, cursor, "accord"); } - out.setAccord(OneOrMore{parseAccord(cursor)}); + out.setAccord(OneOrMore{parseAccord(cursor, context)}); cursor = nextElement(cursor); while (cursorIs(cursor, "accord")) { - out.addAccord(parseAccord(cursor)); + out.addAccord(parseAccord(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Scordatura.h b/src/private/mx/core/generated/Scordatura.h index 86b30fca0..15cb28c09 100644 --- a/src/private/mx/core/generated/Scordatura.h +++ b/src/private/mx/core/generated/Scordatura.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Scordatura string tunings are represented by a series of accord elements, similar to the /// staff-tuning elements. Strings are numbered from high to low. /// Content fields mirror the schema grammar in declaration order; the @@ -39,9 +41,9 @@ class Scordatura final OneOrMore m_accord; }; -Scordatura parseScordatura(pugi::xml_node el); +Scordatura parseScordatura(pugi::xml_node el, const ParseContext &context); -void parseScordaturaContent(Scordatura &out, pugi::xml_node el); +void parseScordaturaContent(Scordatura &out, pugi::xml_node el, const ParseContext &context); void serializeScordatura(const Scordatura &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/ScoreHeaderGroup.cpp b/src/private/mx/core/generated/ScoreHeaderGroup.cpp index ab7f5cca6..01533bfbf 100644 --- a/src/private/mx/core/generated/ScoreHeaderGroup.cpp +++ b/src/private/mx/core/generated/ScoreHeaderGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/ScoreHeaderGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -85,12 +86,12 @@ void ScoreHeaderGroup::setPartList(PartList value) m_partList = std::move(value); } -ScoreHeaderGroup parseScoreHeaderGroup(pugi::xml_node el, pugi::xml_node &cursor) +ScoreHeaderGroup parseScoreHeaderGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { ScoreHeaderGroup out; if (cursorIs(cursor, "work")) { - out.setWork(parseWork(cursor)); + out.setWork(parseWork(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "movement-number")) @@ -105,22 +106,22 @@ ScoreHeaderGroup parseScoreHeaderGroup(pugi::xml_node el, pugi::xml_node &cursor } if (cursorIs(cursor, "identification")) { - out.setIdentification(parseIdentification(cursor)); + out.setIdentification(parseIdentification(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "defaults")) { - out.setDefaults(parseDefaults(cursor)); + out.setDefaults(parseDefaults(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "credit")) { - out.addCredit(parseCredit(cursor)); + out.addCredit(parseCredit(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "part-list")) { - out.setPartList(parsePartList(cursor)); + out.setPartList(parsePartList(cursor, context)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/ScoreHeaderGroup.h b/src/private/mx/core/generated/ScoreHeaderGroup.h index ec055eac9..4a0cae8d1 100644 --- a/src/private/mx/core/generated/ScoreHeaderGroup.h +++ b/src/private/mx/core/generated/ScoreHeaderGroup.h @@ -21,6 +21,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The score-header group contains basic score metadata about the work and movement, score-wide /// defaults for layout and fonts, credits that appear on the first or following pages, and the part /// list. @@ -58,7 +60,7 @@ class ScoreHeaderGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -ScoreHeaderGroup parseScoreHeaderGroup(pugi::xml_node el, pugi::xml_node &cursor); +ScoreHeaderGroup parseScoreHeaderGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeScoreHeaderGroup(const ScoreHeaderGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/ScoreInstrument.cpp b/src/private/mx/core/generated/ScoreInstrument.cpp index 8b806ccbc..98aa4dd0b 100644 --- a/src/private/mx/core/generated/ScoreInstrument.cpp +++ b/src/private/mx/core/generated/ScoreInstrument.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/ScoreInstrument.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,7 +51,7 @@ void ScoreInstrument::setVirtualInstrumentData(VirtualInstrumentDataGroup value) m_virtualInstrumentData = std::move(value); } -ScoreInstrument parseScoreInstrument(pugi::xml_node el) +ScoreInstrument parseScoreInstrument(pugi::xml_node el, const ParseContext &context) { ScoreInstrument out; bool seen_id = false; @@ -64,7 +65,7 @@ ScoreInstrument parseScoreInstrument(pugi::xml_node el) if (aname == "id") { seen_id = true; - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -75,11 +76,11 @@ ScoreInstrument parseScoreInstrument(pugi::xml_node el) { throwMissingAttribute(el, "id"); } - parseScoreInstrumentContent(out, el); + parseScoreInstrumentContent(out, el, context); return out; } -void parseScoreInstrumentContent(ScoreInstrument &out, pugi::xml_node el) +void parseScoreInstrumentContent(ScoreInstrument &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "instrument-name")) @@ -99,7 +100,7 @@ void parseScoreInstrumentContent(ScoreInstrument &out, pugi::xml_node el) if (cursor && (cursorIs(cursor, "instrument-sound") || cursorIs(cursor, "solo") || cursorIs(cursor, "ensemble") || cursorIs(cursor, "virtual-instrument"))) { - out.setVirtualInstrumentData(parseVirtualInstrumentDataGroup(el, cursor)); + out.setVirtualInstrumentData(parseVirtualInstrumentDataGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/ScoreInstrument.h b/src/private/mx/core/generated/ScoreInstrument.h index c99512201..dc3c72466 100644 --- a/src/private/mx/core/generated/ScoreInstrument.h +++ b/src/private/mx/core/generated/ScoreInstrument.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The score-instrument type represents a single instrument within a score-part. As with the /// score-part type, each score-instrument has a required ID attribute, a name, and an optional /// abbreviation. A score-instrument type is also required if the score specifies MIDI 1.0 channels, @@ -49,9 +51,9 @@ class ScoreInstrument final VirtualInstrumentDataGroup m_virtualInstrumentData{}; }; -ScoreInstrument parseScoreInstrument(pugi::xml_node el); +ScoreInstrument parseScoreInstrument(pugi::xml_node el, const ParseContext &context); -void parseScoreInstrumentContent(ScoreInstrument &out, pugi::xml_node el); +void parseScoreInstrumentContent(ScoreInstrument &out, pugi::xml_node el, const ParseContext &context); void serializeScoreInstrument(const ScoreInstrument &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/ScorePart.cpp b/src/private/mx/core/generated/ScorePart.cpp index e538e9811..26a6aaec7 100644 --- a/src/private/mx/core/generated/ScorePart.cpp +++ b/src/private/mx/core/generated/ScorePart.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/ScorePart.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -145,7 +146,7 @@ void ScorePart::setMIDIGroup(std::vector value) m_midiGroup = std::move(value); } -ScorePart parseScorePart(pugi::xml_node el) +ScorePart parseScorePart(pugi::xml_node el, const ParseContext &context) { ScorePart out; bool seen_id = false; @@ -159,7 +160,7 @@ ScorePart parseScorePart(pugi::xml_node el) if (aname == "id") { seen_id = true; - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -170,26 +171,26 @@ ScorePart parseScorePart(pugi::xml_node el) { throwMissingAttribute(el, "id"); } - parseScorePartContent(out, el); + parseScorePartContent(out, el, context); return out; } -void parseScorePartContent(ScorePart &out, pugi::xml_node el) +void parseScorePartContent(ScorePart &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "identification")) { - out.setIdentification(parseIdentification(cursor)); + out.setIdentification(parseIdentification(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "part-link")) { - out.addPartLink(parsePartLink(cursor)); + out.addPartLink(parsePartLink(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "part-name")) { - out.setPartName(parsePartName(cursor)); + out.setPartName(parsePartName(cursor, context)); cursor = nextElement(cursor); } else @@ -198,17 +199,17 @@ void parseScorePartContent(ScorePart &out, pugi::xml_node el) } if (cursorIs(cursor, "part-name-display")) { - out.setPartNameDisplay(parseNameDisplay(cursor)); + out.setPartNameDisplay(parseNameDisplay(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "part-abbreviation")) { - out.setPartAbbreviation(parsePartName(cursor)); + out.setPartAbbreviation(parsePartName(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "part-abbreviation-display")) { - out.setPartAbbreviationDisplay(parseNameDisplay(cursor)); + out.setPartAbbreviationDisplay(parseNameDisplay(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "group")) @@ -218,17 +219,17 @@ void parseScorePartContent(ScorePart &out, pugi::xml_node el) } while (cursorIs(cursor, "score-instrument")) { - out.addScoreInstrument(parseScoreInstrument(cursor)); + out.addScoreInstrument(parseScoreInstrument(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "player")) { - out.addPlayer(parsePlayer(cursor)); + out.addPlayer(parsePlayer(cursor, context)); cursor = nextElement(cursor); } while (cursor && (cursorIs(cursor, "midi-device") || cursorIs(cursor, "midi-instrument"))) { - out.addMIDIGroup(parseScorePartMIDIGroup(el, cursor)); + out.addMIDIGroup(parseScorePartMIDIGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/ScorePart.h b/src/private/mx/core/generated/ScorePart.h index f4ec1e793..56685db30 100644 --- a/src/private/mx/core/generated/ScorePart.h +++ b/src/private/mx/core/generated/ScorePart.h @@ -25,6 +25,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The score-part type collects part-wide information for each part in a score. Often, each MusicXML /// part corresponds to a track in a Standard MIDI Format 1 file. In this case, the midi-device /// element is used to make a MIDI device or port assignment for the given track or specific MIDI @@ -78,9 +80,9 @@ class ScorePart final std::vector m_midiGroup; }; -ScorePart parseScorePart(pugi::xml_node el); +ScorePart parseScorePart(pugi::xml_node el, const ParseContext &context); -void parseScorePartContent(ScorePart &out, pugi::xml_node el); +void parseScorePartContent(ScorePart &out, pugi::xml_node el, const ParseContext &context); void serializeScorePart(const ScorePart &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/ScorePartMIDIGroup.cpp b/src/private/mx/core/generated/ScorePartMIDIGroup.cpp index ff58619c2..df45569ca 100644 --- a/src/private/mx/core/generated/ScorePartMIDIGroup.cpp +++ b/src/private/mx/core/generated/ScorePartMIDIGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/ScorePartMIDIGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,17 +31,17 @@ void ScorePartMIDIGroup::setMIDIInstrument(std::optional value) m_midiInstrument = std::move(value); } -ScorePartMIDIGroup parseScorePartMIDIGroup(pugi::xml_node el, pugi::xml_node &cursor) +ScorePartMIDIGroup parseScorePartMIDIGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { ScorePartMIDIGroup out; if (cursorIs(cursor, "midi-device")) { - out.setMIDIDevice(parseMIDIDevice(cursor)); + out.setMIDIDevice(parseMIDIDevice(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "midi-instrument")) { - out.setMIDIInstrument(parseMIDIInstrument(cursor)); + out.setMIDIInstrument(parseMIDIInstrument(cursor, context)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/ScorePartMIDIGroup.h b/src/private/mx/core/generated/ScorePartMIDIGroup.h index 4f1f512e9..0f1822547 100644 --- a/src/private/mx/core/generated/ScorePartMIDIGroup.h +++ b/src/private/mx/core/generated/ScorePartMIDIGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -37,7 +39,7 @@ class ScorePartMIDIGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -ScorePartMIDIGroup parseScorePartMIDIGroup(pugi::xml_node el, pugi::xml_node &cursor); +ScorePartMIDIGroup parseScorePartMIDIGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeScorePartMIDIGroup(const ScorePartMIDIGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/ScorePartwise.cpp b/src/private/mx/core/generated/ScorePartwise.cpp index c13b023f7..5e50307a1 100644 --- a/src/private/mx/core/generated/ScorePartwise.cpp +++ b/src/private/mx/core/generated/ScorePartwise.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/ScorePartwise.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -45,7 +46,7 @@ void ScorePartwise::setPart(OneOrMore value) m_part = std::move(value); } -ScorePartwise parseScorePartwise(pugi::xml_node el) +ScorePartwise parseScorePartwise(pugi::xml_node el, const ParseContext &context) { ScorePartwise out; for (pugi::xml_attribute a : el.attributes()) @@ -64,18 +65,18 @@ ScorePartwise parseScorePartwise(pugi::xml_node el) throwUnknownAttribute(el, a.name()); } } - parseScorePartwiseContent(out, el); + parseScorePartwiseContent(out, el, context); return out; } -void parseScorePartwiseContent(ScorePartwise &out, pugi::xml_node el) +void parseScorePartwiseContent(ScorePartwise &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "work") || cursorIs(cursor, "movement-number") || cursorIs(cursor, "movement-title") || cursorIs(cursor, "identification") || cursorIs(cursor, "defaults") || cursorIs(cursor, "credit") || cursorIs(cursor, "part-list"))) { - out.setScoreHeader(parseScoreHeaderGroup(el, cursor)); + out.setScoreHeader(parseScoreHeaderGroup(el, cursor, context)); } else { @@ -85,11 +86,11 @@ void parseScorePartwiseContent(ScorePartwise &out, pugi::xml_node el) { throwMissingOrMisplaced(el, cursor, "part"); } - out.setPart(OneOrMore{parsePartwisePart(cursor)}); + out.setPart(OneOrMore{parsePartwisePart(cursor, context)}); cursor = nextElement(cursor); while (cursorIs(cursor, "part")) { - out.addPart(parsePartwisePart(cursor)); + out.addPart(parsePartwisePart(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/ScorePartwise.h b/src/private/mx/core/generated/ScorePartwise.h index f88a481e4..e26be3f0f 100644 --- a/src/private/mx/core/generated/ScorePartwise.h +++ b/src/private/mx/core/generated/ScorePartwise.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Content fields mirror the schema grammar in declaration order; the /// serializer walks them, so a wrong order is unrepresentable (plan §2.3). class ScorePartwise final @@ -39,9 +41,9 @@ class ScorePartwise final OneOrMore m_part; }; -ScorePartwise parseScorePartwise(pugi::xml_node el); +ScorePartwise parseScorePartwise(pugi::xml_node el, const ParseContext &context); -void parseScorePartwiseContent(ScorePartwise &out, pugi::xml_node el); +void parseScorePartwiseContent(ScorePartwise &out, pugi::xml_node el, const ParseContext &context); void serializeScorePartwise(const ScorePartwise &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/ScoreTimewise.cpp b/src/private/mx/core/generated/ScoreTimewise.cpp index 2447a3f01..3c58c5bca 100644 --- a/src/private/mx/core/generated/ScoreTimewise.cpp +++ b/src/private/mx/core/generated/ScoreTimewise.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/ScoreTimewise.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -45,7 +46,7 @@ void ScoreTimewise::setMeasure(OneOrMore value) m_measure = std::move(value); } -ScoreTimewise parseScoreTimewise(pugi::xml_node el) +ScoreTimewise parseScoreTimewise(pugi::xml_node el, const ParseContext &context) { ScoreTimewise out; for (pugi::xml_attribute a : el.attributes()) @@ -64,18 +65,18 @@ ScoreTimewise parseScoreTimewise(pugi::xml_node el) throwUnknownAttribute(el, a.name()); } } - parseScoreTimewiseContent(out, el); + parseScoreTimewiseContent(out, el, context); return out; } -void parseScoreTimewiseContent(ScoreTimewise &out, pugi::xml_node el) +void parseScoreTimewiseContent(ScoreTimewise &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "work") || cursorIs(cursor, "movement-number") || cursorIs(cursor, "movement-title") || cursorIs(cursor, "identification") || cursorIs(cursor, "defaults") || cursorIs(cursor, "credit") || cursorIs(cursor, "part-list"))) { - out.setScoreHeader(parseScoreHeaderGroup(el, cursor)); + out.setScoreHeader(parseScoreHeaderGroup(el, cursor, context)); } else { @@ -85,11 +86,11 @@ void parseScoreTimewiseContent(ScoreTimewise &out, pugi::xml_node el) { throwMissingOrMisplaced(el, cursor, "measure"); } - out.setMeasure(OneOrMore{parseTimewiseMeasure(cursor)}); + out.setMeasure(OneOrMore{parseTimewiseMeasure(cursor, context)}); cursor = nextElement(cursor); while (cursorIs(cursor, "measure")) { - out.addMeasure(parseTimewiseMeasure(cursor)); + out.addMeasure(parseTimewiseMeasure(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/ScoreTimewise.h b/src/private/mx/core/generated/ScoreTimewise.h index c0d0b9fe8..647cecfa6 100644 --- a/src/private/mx/core/generated/ScoreTimewise.h +++ b/src/private/mx/core/generated/ScoreTimewise.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Content fields mirror the schema grammar in declaration order; the /// serializer walks them, so a wrong order is unrepresentable (plan §2.3). class ScoreTimewise final @@ -39,9 +41,9 @@ class ScoreTimewise final OneOrMore m_measure; }; -ScoreTimewise parseScoreTimewise(pugi::xml_node el); +ScoreTimewise parseScoreTimewise(pugi::xml_node el, const ParseContext &context); -void parseScoreTimewiseContent(ScoreTimewise &out, pugi::xml_node el); +void parseScoreTimewiseContent(ScoreTimewise &out, pugi::xml_node el, const ParseContext &context); void serializeScoreTimewise(const ScoreTimewise &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Segno.cpp b/src/private/mx/core/generated/Segno.cpp index 2d25e3058..56406ea74 100644 --- a/src/private/mx/core/generated/Segno.cpp +++ b/src/private/mx/core/generated/Segno.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Segno.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -140,7 +141,7 @@ void Segno::setID(std::optional value) m_id = std::move(value); } -Segno parseSegno(pugi::xml_node el) +Segno parseSegno(pugi::xml_node el, const ParseContext &context) { Segno out; for (pugi::xml_attribute a : el.attributes()) @@ -152,68 +153,69 @@ Segno parseSegno(pugi::xml_node el) } if (aname == "smufl") { - out.setSmufl(SmuflSegnoGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseSegnoContent(out, el); + parseSegnoContent(out, el, context); return out; } -void parseSegnoContent(Segno &out, pugi::xml_node el) +void parseSegnoContent(Segno &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Segno.h b/src/private/mx/core/generated/Segno.h index fdb31d8b1..99c4d4953 100644 --- a/src/private/mx/core/generated/Segno.h +++ b/src/private/mx/core/generated/Segno.h @@ -25,6 +25,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The segno type is the visual indicator of a segno sign. The exact glyph can be specified with the /// smufl attribute. A sound element is also needed to guide playback applications reliably. class Segno final @@ -74,9 +76,9 @@ class Segno final std::optional m_id; }; -Segno parseSegno(pugi::xml_node el); +Segno parseSegno(pugi::xml_node el, const ParseContext &context); -void parseSegnoContent(Segno &out, pugi::xml_node el); +void parseSegnoContent(Segno &out, pugi::xml_node el, const ParseContext &context); void serializeSegno(const Segno &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/SemiPitched.cpp b/src/private/mx/core/generated/SemiPitched.cpp index 2a71be7aa..7f94173da 100644 --- a/src/private/mx/core/generated/SemiPitched.cpp +++ b/src/private/mx/core/generated/SemiPitched.cpp @@ -61,9 +61,15 @@ bool SemiPitched::tryParse(std::string_view text, SemiPitched &out) noexcept } SemiPitched SemiPitched::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +SemiPitched SemiPitched::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { SemiPitched v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/SemiPitched.h b/src/private/mx/core/generated/SemiPitched.h index ceb8ffdfc..731ae9f3f 100644 --- a/src/private/mx/core/generated/SemiPitched.h +++ b/src/private/mx/core/generated/SemiPitched.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -48,6 +50,9 @@ class SemiPitched final /// (the import leniency policy; never produces an invalid value). static SemiPitched parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static SemiPitched parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const SemiPitched &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Semitones.cpp b/src/private/mx/core/generated/Semitones.cpp index bb24a41af..1ed8c5024 100644 --- a/src/private/mx/core/generated/Semitones.cpp +++ b/src/private/mx/core/generated/Semitones.cpp @@ -34,20 +34,33 @@ std::string Semitones::toString() const bool Semitones::tryParse(std::string_view text, Semitones &out) { - Decimal v; - if (!Decimal::tryParse(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + Semitones parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = Semitones{std::move(v)}; + out = std::move(parsed); return true; } Semitones Semitones::parse(std::string_view text) { - Semitones v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +Semitones Semitones::parse(std::string_view text, ValueParseOutcome &outcome) +{ + Decimal v; + if (!Decimal::tryParse(text, v)) + { + outcome = ValueParseOutcome::invalid; + return Semitones{}; + } + Semitones out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/Semitones.h b/src/private/mx/core/generated/Semitones.h index d09d8a45c..e109fdd3d 100644 --- a/src/private/mx/core/generated/Semitones.h +++ b/src/private/mx/core/generated/Semitones.h @@ -3,6 +3,7 @@ #pragma once #include "mx/core/Decimal.h" +#include "mx/core/Lexical.h" #include #include @@ -39,6 +40,9 @@ class Semitones final /// Lenient: non-numeric text yields the clamped zero. static Semitones parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static Semitones parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const Semitones &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/ShowFrets.cpp b/src/private/mx/core/generated/ShowFrets.cpp index 73771b48d..ae7d7e596 100644 --- a/src/private/mx/core/generated/ShowFrets.cpp +++ b/src/private/mx/core/generated/ShowFrets.cpp @@ -42,9 +42,15 @@ bool ShowFrets::tryParse(std::string_view text, ShowFrets &out) noexcept } ShowFrets ShowFrets::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +ShowFrets ShowFrets::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { ShowFrets v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/ShowFrets.h b/src/private/mx/core/generated/ShowFrets.h index 55e38ee9f..f148609df 100644 --- a/src/private/mx/core/generated/ShowFrets.h +++ b/src/private/mx/core/generated/ShowFrets.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -41,6 +43,9 @@ class ShowFrets final /// (the import leniency policy; never produces an invalid value). static ShowFrets parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static ShowFrets parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const ShowFrets &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/ShowTuplet.cpp b/src/private/mx/core/generated/ShowTuplet.cpp index 489a7d07b..a6793087e 100644 --- a/src/private/mx/core/generated/ShowTuplet.cpp +++ b/src/private/mx/core/generated/ShowTuplet.cpp @@ -48,9 +48,15 @@ bool ShowTuplet::tryParse(std::string_view text, ShowTuplet &out) noexcept } ShowTuplet ShowTuplet::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +ShowTuplet ShowTuplet::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { ShowTuplet v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/ShowTuplet.h b/src/private/mx/core/generated/ShowTuplet.h index 5b132e7c8..1b56b13b6 100644 --- a/src/private/mx/core/generated/ShowTuplet.h +++ b/src/private/mx/core/generated/ShowTuplet.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -43,6 +45,9 @@ class ShowTuplet final /// (the import leniency policy; never produces an invalid value). static ShowTuplet parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static ShowTuplet parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const ShowTuplet &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Slash.cpp b/src/private/mx/core/generated/Slash.cpp index d8c62afd1..bc511fe1c 100644 --- a/src/private/mx/core/generated/Slash.cpp +++ b/src/private/mx/core/generated/Slash.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Slash.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,7 +51,7 @@ void Slash::setSlash(std::optional value) m_slash = std::move(value); } -Slash parseSlash(pugi::xml_node el) +Slash parseSlash(pugi::xml_node el, const ParseContext &context) { Slash out; bool seen_type = false; @@ -64,15 +65,15 @@ Slash parseSlash(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStop::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "use-dots") { - out.setUseDots(YesNo::parse(a.value())); + out.setUseDots(parseValue(a.value(), context, el, "use-dots")); } else if (aname == "use-stems") { - out.setUseStems(YesNo::parse(a.value())); + out.setUseStems(parseValue(a.value(), context, el, "use-stems")); } else { @@ -83,16 +84,16 @@ Slash parseSlash(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseSlashContent(out, el); + parseSlashContent(out, el, context); return out; } -void parseSlashContent(Slash &out, pugi::xml_node el) +void parseSlashContent(Slash &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "slash-type") || cursorIs(cursor, "except-voice"))) { - out.setSlash(parseSlashGroup(el, cursor)); + out.setSlash(parseSlashGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/Slash.h b/src/private/mx/core/generated/Slash.h index f52c2ca1b..927a15493 100644 --- a/src/private/mx/core/generated/Slash.h +++ b/src/private/mx/core/generated/Slash.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The slash type is used to indicate that slash notation is to be used. If the slash is on every /// beat, use-stems is no (the default). To indicate rhythms but not pitches, use-stems is set to /// yes. The type attribute indicates whether this is the start or stop of a slash notation style. @@ -46,9 +48,9 @@ class Slash final std::optional m_slash; }; -Slash parseSlash(pugi::xml_node el); +Slash parseSlash(pugi::xml_node el, const ParseContext &context); -void parseSlashContent(Slash &out, pugi::xml_node el); +void parseSlashContent(Slash &out, pugi::xml_node el, const ParseContext &context); void serializeSlash(const Slash &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/SlashGroup.cpp b/src/private/mx/core/generated/SlashGroup.cpp index c6386c09d..f21e60b23 100644 --- a/src/private/mx/core/generated/SlashGroup.cpp +++ b/src/private/mx/core/generated/SlashGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/SlashGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,12 +36,12 @@ void SlashGroup::setExceptVoice(std::vector value) m_exceptVoice = std::move(value); } -SlashGroup parseSlashGroup(pugi::xml_node el, pugi::xml_node &cursor) +SlashGroup parseSlashGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { SlashGroup out; if (cursor && (cursorIs(cursor, "slash-type"))) { - out.setGroup(parseSlashGroupGroup(el, cursor)); + out.setGroup(parseSlashGroupGroup(el, cursor, context)); } while (cursorIs(cursor, "except-voice")) { diff --git a/src/private/mx/core/generated/SlashGroup.h b/src/private/mx/core/generated/SlashGroup.h index 0cf878865..cb1882e6d 100644 --- a/src/private/mx/core/generated/SlashGroup.h +++ b/src/private/mx/core/generated/SlashGroup.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The slash group combines elements used for more complete specification of the slash and /// beat-repeat measure-style elements. They have the same values as the type and dot elements, and /// define what the beat is for the display of repetition marks. If not present, the beat is based on @@ -40,7 +42,7 @@ class SlashGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -SlashGroup parseSlashGroup(pugi::xml_node el, pugi::xml_node &cursor); +SlashGroup parseSlashGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeSlashGroup(const SlashGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/SlashGroupGroup.cpp b/src/private/mx/core/generated/SlashGroupGroup.cpp index 5b9b61f45..26876b24a 100644 --- a/src/private/mx/core/generated/SlashGroupGroup.cpp +++ b/src/private/mx/core/generated/SlashGroupGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/SlashGroupGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,12 +36,12 @@ void SlashGroupGroup::setSlashDot(std::vector value) m_slashDot = std::move(value); } -SlashGroupGroup parseSlashGroupGroup(pugi::xml_node el, pugi::xml_node &cursor) +SlashGroupGroup parseSlashGroupGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { SlashGroupGroup out; if (cursorIs(cursor, "slash-type")) { - out.setSlashType(NoteTypeValue::parse(childText(cursor))); + out.setSlashType(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -49,7 +50,7 @@ SlashGroupGroup parseSlashGroupGroup(pugi::xml_node el, pugi::xml_node &cursor) } while (cursorIs(cursor, "slash-dot")) { - out.addSlashDot(parseEmpty(cursor)); + out.addSlashDot(parseEmpty(cursor, context)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/SlashGroupGroup.h b/src/private/mx/core/generated/SlashGroupGroup.h index 91e7a9c08..a500a3600 100644 --- a/src/private/mx/core/generated/SlashGroupGroup.h +++ b/src/private/mx/core/generated/SlashGroupGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -38,7 +40,7 @@ class SlashGroupGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -SlashGroupGroup parseSlashGroupGroup(pugi::xml_node el, pugi::xml_node &cursor); +SlashGroupGroup parseSlashGroupGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeSlashGroupGroup(const SlashGroupGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Slide.cpp b/src/private/mx/core/generated/Slide.cpp index 1cddb4502..0188b0fac 100644 --- a/src/private/mx/core/generated/Slide.cpp +++ b/src/private/mx/core/generated/Slide.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Slide.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -210,7 +211,7 @@ void Slide::setValue(std::string value) m_value = std::move(value); } -Slide parseSlide(pugi::xml_node el) +Slide parseSlide(pugi::xml_node el, const ParseContext &context) { Slide out; bool seen_type = false; @@ -224,79 +225,79 @@ Slide parseSlide(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStop::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "number") { - out.setNumber(NumberLevel::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "line-type") { - out.setLineType(LineType::parse(a.value())); + out.setLineType(parseValue(a.value(), context, el, "line-type")); } else if (aname == "dash-length") { - out.setDashLength(Tenths::parse(a.value())); + out.setDashLength(parseValue(a.value(), context, el, "dash-length")); } else if (aname == "space-length") { - out.setSpaceLength(Tenths::parse(a.value())); + out.setSpaceLength(parseValue(a.value(), context, el, "space-length")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "accelerate") { - out.setAccelerate(YesNo::parse(a.value())); + out.setAccelerate(parseValue(a.value(), context, el, "accelerate")); } else if (aname == "beats") { - out.setBeats(TrillBeats::parse(a.value())); + out.setBeats(parseValue(a.value(), context, el, "beats")); } else if (aname == "first-beat") { - out.setFirstBeat(Percent::parse(a.value())); + out.setFirstBeat(parseValue(a.value(), context, el, "first-beat")); } else if (aname == "last-beat") { - out.setLastBeat(Percent::parse(a.value())); + out.setLastBeat(parseValue(a.value(), context, el, "last-beat")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -307,11 +308,11 @@ Slide parseSlide(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseSlideContent(out, el); + parseSlideContent(out, el, context); return out; } -void parseSlideContent(Slide &out, pugi::xml_node el) +void parseSlideContent(Slide &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/Slide.h b/src/private/mx/core/generated/Slide.h index 5b83d8a37..8c5398480 100644 --- a/src/private/mx/core/generated/Slide.h +++ b/src/private/mx/core/generated/Slide.h @@ -28,6 +28,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Glissando and slide types both indicate rapidly moving from one pitch to the other so that /// individual notes are not discerned. A slide is continuous between the two pitches and defaults to /// a solid line. The optional text for a is printed alongside the line. @@ -99,9 +101,9 @@ class Slide final std::string m_value{}; }; -Slide parseSlide(pugi::xml_node el); +Slide parseSlide(pugi::xml_node el, const ParseContext &context); -void parseSlideContent(Slide &out, pugi::xml_node el); +void parseSlideContent(Slide &out, pugi::xml_node el, const ParseContext &context); void serializeSlide(const Slide &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Slur.cpp b/src/private/mx/core/generated/Slur.cpp index a35c9bf40..343b84097 100644 --- a/src/private/mx/core/generated/Slur.cpp +++ b/src/private/mx/core/generated/Slur.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Slur.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -200,7 +201,7 @@ void Slur::setID(std::optional value) m_id = std::move(value); } -Slur parseSlur(pugi::xml_node el) +Slur parseSlur(pugi::xml_node el, const ParseContext &context) { Slur out; bool seen_type = false; @@ -214,79 +215,79 @@ Slur parseSlur(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStopContinue::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "number") { - out.setNumber(NumberLevel::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "line-type") { - out.setLineType(LineType::parse(a.value())); + out.setLineType(parseValue(a.value(), context, el, "line-type")); } else if (aname == "dash-length") { - out.setDashLength(Tenths::parse(a.value())); + out.setDashLength(parseValue(a.value(), context, el, "dash-length")); } else if (aname == "space-length") { - out.setSpaceLength(Tenths::parse(a.value())); + out.setSpaceLength(parseValue(a.value(), context, el, "space-length")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "orientation") { - out.setOrientation(OverUnder::parse(a.value())); + out.setOrientation(parseValue(a.value(), context, el, "orientation")); } else if (aname == "bezier-x") { - out.setBezierX(Tenths::parse(a.value())); + out.setBezierX(parseValue(a.value(), context, el, "bezier-x")); } else if (aname == "bezier-y") { - out.setBezierY(Tenths::parse(a.value())); + out.setBezierY(parseValue(a.value(), context, el, "bezier-y")); } else if (aname == "bezier-x2") { - out.setBezierX2(Tenths::parse(a.value())); + out.setBezierX2(parseValue(a.value(), context, el, "bezier-x2")); } else if (aname == "bezier-y2") { - out.setBezierY2(Tenths::parse(a.value())); + out.setBezierY2(parseValue(a.value(), context, el, "bezier-y2")); } else if (aname == "bezier-offset") { - out.setBezierOffset(Divisions::parse(a.value())); + out.setBezierOffset(parseValue(a.value(), context, el, "bezier-offset")); } else if (aname == "bezier-offset2") { - out.setBezierOffset2(Divisions::parse(a.value())); + out.setBezierOffset2(parseValue(a.value(), context, el, "bezier-offset2")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -297,13 +298,14 @@ Slur parseSlur(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseSlurContent(out, el); + parseSlurContent(out, el, context); return out; } -void parseSlurContent(Slur &out, pugi::xml_node el) +void parseSlurContent(Slur &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Slur.h b/src/private/mx/core/generated/Slur.h index 6347c3778..193489040 100644 --- a/src/private/mx/core/generated/Slur.h +++ b/src/private/mx/core/generated/Slur.h @@ -24,6 +24,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Slur types are empty. Most slurs are represented with two elements: one with a start type, and /// one with a stop type. Slurs can add more elements using a continue type. This is typically used /// to specify the formatting of cross-system slurs, or to specify the shape of very complex slurs. @@ -92,9 +94,9 @@ class Slur final std::optional m_id; }; -Slur parseSlur(pugi::xml_node el); +Slur parseSlur(pugi::xml_node el, const ParseContext &context); -void parseSlurContent(Slur &out, pugi::xml_node el); +void parseSlurContent(Slur &out, pugi::xml_node el, const ParseContext &context); void serializeSlur(const Slur &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/SmuflAccidentalGlyphName.cpp b/src/private/mx/core/generated/SmuflAccidentalGlyphName.cpp index b9d527615..aa7186017 100644 --- a/src/private/mx/core/generated/SmuflAccidentalGlyphName.cpp +++ b/src/private/mx/core/generated/SmuflAccidentalGlyphName.cpp @@ -124,4 +124,16 @@ SmuflAccidentalGlyphName SmuflAccidentalGlyphName::parse(std::string_view text) return SmuflAccidentalGlyphName{std::string{text}}; } +SmuflAccidentalGlyphName SmuflAccidentalGlyphName::parse(std::string_view text, ValueParseOutcome &outcome) +{ + SmuflAccidentalGlyphName out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/SmuflAccidentalGlyphName.h b/src/private/mx/core/generated/SmuflAccidentalGlyphName.h index e4087ed1c..705030f18 100644 --- a/src/private/mx/core/generated/SmuflAccidentalGlyphName.h +++ b/src/private/mx/core/generated/SmuflAccidentalGlyphName.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -57,6 +59,9 @@ class SmuflAccidentalGlyphName final /// Lenient: repairs into the nearest valid glyph name. static SmuflAccidentalGlyphName parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. + static SmuflAccidentalGlyphName parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const SmuflAccidentalGlyphName &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/SmuflCodaGlyphName.cpp b/src/private/mx/core/generated/SmuflCodaGlyphName.cpp index d3bd6e67b..ce99d1875 100644 --- a/src/private/mx/core/generated/SmuflCodaGlyphName.cpp +++ b/src/private/mx/core/generated/SmuflCodaGlyphName.cpp @@ -97,4 +97,16 @@ SmuflCodaGlyphName SmuflCodaGlyphName::parse(std::string_view text) return SmuflCodaGlyphName{std::string{text}}; } +SmuflCodaGlyphName SmuflCodaGlyphName::parse(std::string_view text, ValueParseOutcome &outcome) +{ + SmuflCodaGlyphName out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/SmuflCodaGlyphName.h b/src/private/mx/core/generated/SmuflCodaGlyphName.h index 953fe532e..aff3f4326 100644 --- a/src/private/mx/core/generated/SmuflCodaGlyphName.h +++ b/src/private/mx/core/generated/SmuflCodaGlyphName.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -37,6 +39,9 @@ class SmuflCodaGlyphName final /// Lenient: repairs into the nearest valid glyph name. static SmuflCodaGlyphName parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. + static SmuflCodaGlyphName parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const SmuflCodaGlyphName &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/SmuflGlyphName.cpp b/src/private/mx/core/generated/SmuflGlyphName.cpp index 61e16cd06..4a48f3ebe 100644 --- a/src/private/mx/core/generated/SmuflGlyphName.cpp +++ b/src/private/mx/core/generated/SmuflGlyphName.cpp @@ -70,4 +70,16 @@ SmuflGlyphName SmuflGlyphName::parse(std::string_view text) return SmuflGlyphName{std::string{text}}; } +SmuflGlyphName SmuflGlyphName::parse(std::string_view text, ValueParseOutcome &outcome) +{ + SmuflGlyphName out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/SmuflGlyphName.h b/src/private/mx/core/generated/SmuflGlyphName.h index 6ae20c1a1..05066b5fb 100644 --- a/src/private/mx/core/generated/SmuflGlyphName.h +++ b/src/private/mx/core/generated/SmuflGlyphName.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -40,6 +42,9 @@ class SmuflGlyphName final /// Lenient: repairs into the nearest valid token. static SmuflGlyphName parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. + static SmuflGlyphName parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const SmuflGlyphName &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/SmuflLyricsGlyphName.cpp b/src/private/mx/core/generated/SmuflLyricsGlyphName.cpp index 913e9fef6..f52c18c89 100644 --- a/src/private/mx/core/generated/SmuflLyricsGlyphName.cpp +++ b/src/private/mx/core/generated/SmuflLyricsGlyphName.cpp @@ -105,4 +105,16 @@ SmuflLyricsGlyphName SmuflLyricsGlyphName::parse(std::string_view text) return SmuflLyricsGlyphName{std::string{text}}; } +SmuflLyricsGlyphName SmuflLyricsGlyphName::parse(std::string_view text, ValueParseOutcome &outcome) +{ + SmuflLyricsGlyphName out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/SmuflLyricsGlyphName.h b/src/private/mx/core/generated/SmuflLyricsGlyphName.h index 970cc9e24..099b5eb42 100644 --- a/src/private/mx/core/generated/SmuflLyricsGlyphName.h +++ b/src/private/mx/core/generated/SmuflLyricsGlyphName.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -39,6 +41,9 @@ class SmuflLyricsGlyphName final /// Lenient: repairs into the nearest valid glyph name. static SmuflLyricsGlyphName parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. + static SmuflLyricsGlyphName parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const SmuflLyricsGlyphName &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/SmuflPictogramGlyphName.cpp b/src/private/mx/core/generated/SmuflPictogramGlyphName.cpp index a71955a29..3d2ea7f89 100644 --- a/src/private/mx/core/generated/SmuflPictogramGlyphName.cpp +++ b/src/private/mx/core/generated/SmuflPictogramGlyphName.cpp @@ -105,4 +105,16 @@ SmuflPictogramGlyphName SmuflPictogramGlyphName::parse(std::string_view text) return SmuflPictogramGlyphName{std::string{text}}; } +SmuflPictogramGlyphName SmuflPictogramGlyphName::parse(std::string_view text, ValueParseOutcome &outcome) +{ + SmuflPictogramGlyphName out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/SmuflPictogramGlyphName.h b/src/private/mx/core/generated/SmuflPictogramGlyphName.h index 5edf8dfae..a5627475c 100644 --- a/src/private/mx/core/generated/SmuflPictogramGlyphName.h +++ b/src/private/mx/core/generated/SmuflPictogramGlyphName.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -39,6 +41,9 @@ class SmuflPictogramGlyphName final /// Lenient: repairs into the nearest valid glyph name. static SmuflPictogramGlyphName parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. + static SmuflPictogramGlyphName parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const SmuflPictogramGlyphName &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/SmuflSegnoGlyphName.cpp b/src/private/mx/core/generated/SmuflSegnoGlyphName.cpp index 88db3505e..8bea76b35 100644 --- a/src/private/mx/core/generated/SmuflSegnoGlyphName.cpp +++ b/src/private/mx/core/generated/SmuflSegnoGlyphName.cpp @@ -97,4 +97,16 @@ SmuflSegnoGlyphName SmuflSegnoGlyphName::parse(std::string_view text) return SmuflSegnoGlyphName{std::string{text}}; } +SmuflSegnoGlyphName SmuflSegnoGlyphName::parse(std::string_view text, ValueParseOutcome &outcome) +{ + SmuflSegnoGlyphName out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/SmuflSegnoGlyphName.h b/src/private/mx/core/generated/SmuflSegnoGlyphName.h index 7767ffa96..5dc255898 100644 --- a/src/private/mx/core/generated/SmuflSegnoGlyphName.h +++ b/src/private/mx/core/generated/SmuflSegnoGlyphName.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -37,6 +39,9 @@ class SmuflSegnoGlyphName final /// Lenient: repairs into the nearest valid glyph name. static SmuflSegnoGlyphName parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. + static SmuflSegnoGlyphName parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const SmuflSegnoGlyphName &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/SmuflWavyLineGlyphName.cpp b/src/private/mx/core/generated/SmuflWavyLineGlyphName.cpp index bdaebb54f..0b45c3d60 100644 --- a/src/private/mx/core/generated/SmuflWavyLineGlyphName.cpp +++ b/src/private/mx/core/generated/SmuflWavyLineGlyphName.cpp @@ -119,4 +119,16 @@ SmuflWavyLineGlyphName SmuflWavyLineGlyphName::parse(std::string_view text) return SmuflWavyLineGlyphName{Kind::wiggle, std::string{text}}; } +SmuflWavyLineGlyphName SmuflWavyLineGlyphName::parse(std::string_view text, ValueParseOutcome &outcome) +{ + SmuflWavyLineGlyphName out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/SmuflWavyLineGlyphName.h b/src/private/mx/core/generated/SmuflWavyLineGlyphName.h index e0b381630..7829c5d7d 100644 --- a/src/private/mx/core/generated/SmuflWavyLineGlyphName.h +++ b/src/private/mx/core/generated/SmuflWavyLineGlyphName.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -53,6 +55,9 @@ class SmuflWavyLineGlyphName final /// Lenient: repairs into the nearest valid glyph name. static SmuflWavyLineGlyphName parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. + static SmuflWavyLineGlyphName parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const SmuflWavyLineGlyphName &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Sound.cpp b/src/private/mx/core/generated/Sound.cpp index ed1ec7ef5..015c5d814 100644 --- a/src/private/mx/core/generated/Sound.cpp +++ b/src/private/mx/core/generated/Sound.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Sound.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -225,7 +226,7 @@ void Sound::setOffset(std::optional value) m_offset = std::move(value); } -Sound parseSound(pugi::xml_node el) +Sound parseSound(pugi::xml_node el, const ParseContext &context) { Sound out; for (pugi::xml_attribute a : el.attributes()) @@ -237,15 +238,15 @@ Sound parseSound(pugi::xml_node el) } if (aname == "tempo") { - out.setTempo(NonNegativeDecimal::parse(a.value())); + out.setTempo(parseValue(a.value(), context, el, "tempo")); } else if (aname == "dynamics") { - out.setDynamics(NonNegativeDecimal::parse(a.value())); + out.setDynamics(parseValue(a.value(), context, el, "dynamics")); } else if (aname == "dacapo") { - out.setDacapo(YesNo::parse(a.value())); + out.setDacapo(parseValue(a.value(), context, el, "dacapo")); } else if (aname == "segno") { @@ -265,11 +266,11 @@ Sound parseSound(pugi::xml_node el) } else if (aname == "divisions") { - out.setDivisions(Divisions::parse(a.value())); + out.setDivisions(parseValue(a.value(), context, el, "divisions")); } else if (aname == "forward-repeat") { - out.setForwardRepeat(YesNo::parse(a.value())); + out.setForwardRepeat(parseValue(a.value(), context, el, "forward-repeat")); } else if (aname == "fine") { @@ -277,61 +278,61 @@ Sound parseSound(pugi::xml_node el) } else if (aname == "time-only") { - out.setTimeOnly(TimeOnly::parse(a.value())); + out.setTimeOnly(parseValue(a.value(), context, el, "time-only")); } else if (aname == "pizzicato") { - out.setPizzicato(YesNo::parse(a.value())); + out.setPizzicato(parseValue(a.value(), context, el, "pizzicato")); } else if (aname == "pan") { - out.setPan(RotationDegrees::parse(a.value())); + out.setPan(parseValue(a.value(), context, el, "pan")); } else if (aname == "elevation") { - out.setElevation(RotationDegrees::parse(a.value())); + out.setElevation(parseValue(a.value(), context, el, "elevation")); } else if (aname == "damper-pedal") { - out.setDamperPedal(YesNoNumber::parse(a.value())); + out.setDamperPedal(parseValue(a.value(), context, el, "damper-pedal")); } else if (aname == "soft-pedal") { - out.setSoftPedal(YesNoNumber::parse(a.value())); + out.setSoftPedal(parseValue(a.value(), context, el, "soft-pedal")); } else if (aname == "sostenuto-pedal") { - out.setSostenutoPedal(YesNoNumber::parse(a.value())); + out.setSostenutoPedal(parseValue(a.value(), context, el, "sostenuto-pedal")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseSoundContent(out, el); + parseSoundContent(out, el, context); return out; } -void parseSoundContent(Sound &out, pugi::xml_node el) +void parseSoundContent(Sound &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); while (cursor && (cursorIs(cursor, "instrument-change") || cursorIs(cursor, "midi-device") || cursorIs(cursor, "midi-instrument") || cursorIs(cursor, "play"))) { - out.addGroup(parseSoundGroup(el, cursor)); + out.addGroup(parseSoundGroup(el, cursor, context)); } if (cursorIs(cursor, "swing")) { - out.setSwing(parseSwing(cursor)); + out.setSwing(parseSwing(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "offset")) { - out.setOffset(parseOffset(cursor)); + out.setOffset(parseOffset(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Sound.h b/src/private/mx/core/generated/Sound.h index 5aff35ba2..e09321dc6 100644 --- a/src/private/mx/core/generated/Sound.h +++ b/src/private/mx/core/generated/Sound.h @@ -27,6 +27,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The sound element contains general playback parameters. They can stand alone within a /// part/measure, or be a component element within a direction. Tempo is expressed in quarter notes /// per minute. If 0, the sound-generating program should prompt the user at the time of compiling a @@ -139,9 +141,9 @@ class Sound final std::optional m_offset; }; -Sound parseSound(pugi::xml_node el); +Sound parseSound(pugi::xml_node el, const ParseContext &context); -void parseSoundContent(Sound &out, pugi::xml_node el); +void parseSoundContent(Sound &out, pugi::xml_node el, const ParseContext &context); void serializeSound(const Sound &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/SoundGroup.cpp b/src/private/mx/core/generated/SoundGroup.cpp index 7e68a20d7..91881a5c9 100644 --- a/src/private/mx/core/generated/SoundGroup.cpp +++ b/src/private/mx/core/generated/SoundGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/SoundGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,27 +51,27 @@ void SoundGroup::setPlay(std::optional value) m_play = std::move(value); } -SoundGroup parseSoundGroup(pugi::xml_node el, pugi::xml_node &cursor) +SoundGroup parseSoundGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { SoundGroup out; if (cursorIs(cursor, "instrument-change")) { - out.setInstrumentChange(parseInstrumentChange(cursor)); + out.setInstrumentChange(parseInstrumentChange(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "midi-device")) { - out.setMIDIDevice(parseMIDIDevice(cursor)); + out.setMIDIDevice(parseMIDIDevice(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "midi-instrument")) { - out.setMIDIInstrument(parseMIDIInstrument(cursor)); + out.setMIDIInstrument(parseMIDIInstrument(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "play")) { - out.setPlay(parsePlay(cursor)); + out.setPlay(parsePlay(cursor, context)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/SoundGroup.h b/src/private/mx/core/generated/SoundGroup.h index 33ed6860a..9f345397e 100644 --- a/src/private/mx/core/generated/SoundGroup.h +++ b/src/private/mx/core/generated/SoundGroup.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -45,7 +47,7 @@ class SoundGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -SoundGroup parseSoundGroup(pugi::xml_node el, pugi::xml_node &cursor); +SoundGroup parseSoundGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeSoundGroup(const SoundGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/SoundID.cpp b/src/private/mx/core/generated/SoundID.cpp index 9bc2d2dda..5775e9138 100644 --- a/src/private/mx/core/generated/SoundID.cpp +++ b/src/private/mx/core/generated/SoundID.cpp @@ -5394,9 +5394,15 @@ bool SoundID::tryParse(std::string_view text, SoundID &out) noexcept } SoundID SoundID::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +SoundID SoundID::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { SoundID v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/SoundID.h b/src/private/mx/core/generated/SoundID.h index 6ccef581f..23b1cff14 100644 --- a/src/private/mx/core/generated/SoundID.h +++ b/src/private/mx/core/generated/SoundID.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -1825,6 +1827,9 @@ class SoundID final /// (the import leniency policy; never produces an invalid value). static SoundID parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static SoundID parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const SoundID &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/StaffDetails.cpp b/src/private/mx/core/generated/StaffDetails.cpp index 47dc1558f..0b4541f56 100644 --- a/src/private/mx/core/generated/StaffDetails.cpp +++ b/src/private/mx/core/generated/StaffDetails.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/StaffDetails.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -105,7 +106,7 @@ void StaffDetails::setStaffSize(std::optional value) m_staffSize = std::move(value); } -StaffDetails parseStaffDetails(pugi::xml_node el) +StaffDetails parseStaffDetails(pugi::xml_node el, const ParseContext &context) { StaffDetails out; for (pugi::xml_attribute a : el.attributes()) @@ -117,54 +118,54 @@ StaffDetails parseStaffDetails(pugi::xml_node el) } if (aname == "number") { - out.setNumber(StaffNumber::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "show-frets") { - out.setShowFrets(ShowFrets::parse(a.value())); + out.setShowFrets(parseValue(a.value(), context, el, "show-frets")); } else if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else if (aname == "print-spacing") { - out.setPrintSpacing(YesNo::parse(a.value())); + out.setPrintSpacing(parseValue(a.value(), context, el, "print-spacing")); } else { throwUnknownAttribute(el, a.name()); } } - parseStaffDetailsContent(out, el); + parseStaffDetailsContent(out, el, context); return out; } -void parseStaffDetailsContent(StaffDetails &out, pugi::xml_node el) +void parseStaffDetailsContent(StaffDetails &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "staff-type")) { - out.setStaffType(StaffType::parse(childText(cursor))); + out.setStaffType(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursor && (cursorIs(cursor, "staff-lines"))) { - out.setGroup(parseStaffDetailsGroup(el, cursor)); + out.setGroup(parseStaffDetailsGroup(el, cursor, context)); } while (cursorIs(cursor, "staff-tuning")) { - out.addStaffTuning(parseStaffTuning(cursor)); + out.addStaffTuning(parseStaffTuning(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "capo")) { - out.setCapo(parseInt(childText(cursor))); + out.setCapo(parseIntegerValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "staff-size")) { - out.setStaffSize(parseStaffSize(cursor)); + out.setStaffSize(parseStaffSize(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/StaffDetails.h b/src/private/mx/core/generated/StaffDetails.h index dc2fcd09e..e03a42cb4 100644 --- a/src/private/mx/core/generated/StaffDetails.h +++ b/src/private/mx/core/generated/StaffDetails.h @@ -23,6 +23,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The staff-details element is used to indicate different types of staves. The optional number /// attribute specifies the staff number from top to bottom on the system, as with clef. The /// print-object attribute is used to indicate when a staff is not printed in a part, usually in @@ -67,9 +69,9 @@ class StaffDetails final std::optional m_staffSize; }; -StaffDetails parseStaffDetails(pugi::xml_node el); +StaffDetails parseStaffDetails(pugi::xml_node el, const ParseContext &context); -void parseStaffDetailsContent(StaffDetails &out, pugi::xml_node el); +void parseStaffDetailsContent(StaffDetails &out, pugi::xml_node el, const ParseContext &context); void serializeStaffDetails(const StaffDetails &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/StaffDetailsGroup.cpp b/src/private/mx/core/generated/StaffDetailsGroup.cpp index 12a0d8117..49852e90f 100644 --- a/src/private/mx/core/generated/StaffDetailsGroup.cpp +++ b/src/private/mx/core/generated/StaffDetailsGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/StaffDetailsGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,12 +36,12 @@ void StaffDetailsGroup::setLineDetail(std::vector value) m_lineDetail = std::move(value); } -StaffDetailsGroup parseStaffDetailsGroup(pugi::xml_node el, pugi::xml_node &cursor) +StaffDetailsGroup parseStaffDetailsGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { StaffDetailsGroup out; if (cursorIs(cursor, "staff-lines")) { - out.setStaffLines(parseInt(childText(cursor))); + out.setStaffLines(parseIntegerValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -49,7 +50,7 @@ StaffDetailsGroup parseStaffDetailsGroup(pugi::xml_node el, pugi::xml_node &curs } while (cursorIs(cursor, "line-detail")) { - out.addLineDetail(parseLineDetail(cursor)); + out.addLineDetail(parseLineDetail(cursor, context)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/StaffDetailsGroup.h b/src/private/mx/core/generated/StaffDetailsGroup.h index 4229dca35..cbf3fd08e 100644 --- a/src/private/mx/core/generated/StaffDetailsGroup.h +++ b/src/private/mx/core/generated/StaffDetailsGroup.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -37,7 +39,7 @@ class StaffDetailsGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -StaffDetailsGroup parseStaffDetailsGroup(pugi::xml_node el, pugi::xml_node &cursor); +StaffDetailsGroup parseStaffDetailsGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeStaffDetailsGroup(const StaffDetailsGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/StaffDivide.cpp b/src/private/mx/core/generated/StaffDivide.cpp index cba472496..1659b5c01 100644 --- a/src/private/mx/core/generated/StaffDivide.cpp +++ b/src/private/mx/core/generated/StaffDivide.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/StaffDivide.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -140,7 +141,7 @@ void StaffDivide::setID(std::optional value) m_id = std::move(value); } -StaffDivide parseStaffDivide(pugi::xml_node el) +StaffDivide parseStaffDivide(pugi::xml_node el, const ParseContext &context) { StaffDivide out; bool seen_type = false; @@ -154,55 +155,55 @@ StaffDivide parseStaffDivide(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StaffDivideSymbol::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -213,13 +214,14 @@ StaffDivide parseStaffDivide(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseStaffDivideContent(out, el); + parseStaffDivideContent(out, el, context); return out; } -void parseStaffDivideContent(StaffDivide &out, pugi::xml_node el) +void parseStaffDivideContent(StaffDivide &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/StaffDivide.h b/src/private/mx/core/generated/StaffDivide.h index c851b5122..988ed2e95 100644 --- a/src/private/mx/core/generated/StaffDivide.h +++ b/src/private/mx/core/generated/StaffDivide.h @@ -25,6 +25,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The staff-divide element represents the staff division arrow symbols found at SMuFL code points /// U+E00B, U+E00C, and U+E00D. class StaffDivide final @@ -74,9 +76,9 @@ class StaffDivide final std::optional m_id; }; -StaffDivide parseStaffDivide(pugi::xml_node el); +StaffDivide parseStaffDivide(pugi::xml_node el, const ParseContext &context); -void parseStaffDivideContent(StaffDivide &out, pugi::xml_node el); +void parseStaffDivideContent(StaffDivide &out, pugi::xml_node el, const ParseContext &context); void serializeStaffDivide(const StaffDivide &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/StaffDivideSymbol.cpp b/src/private/mx/core/generated/StaffDivideSymbol.cpp index 89ca86e9d..7fb9e2bc8 100644 --- a/src/private/mx/core/generated/StaffDivideSymbol.cpp +++ b/src/private/mx/core/generated/StaffDivideSymbol.cpp @@ -48,9 +48,15 @@ bool StaffDivideSymbol::tryParse(std::string_view text, StaffDivideSymbol &out) } StaffDivideSymbol StaffDivideSymbol::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +StaffDivideSymbol StaffDivideSymbol::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { StaffDivideSymbol v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/StaffDivideSymbol.h b/src/private/mx/core/generated/StaffDivideSymbol.h index 3fdd7ae85..76dbdcace 100644 --- a/src/private/mx/core/generated/StaffDivideSymbol.h +++ b/src/private/mx/core/generated/StaffDivideSymbol.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -43,6 +45,9 @@ class StaffDivideSymbol final /// (the import leniency policy; never produces an invalid value). static StaffDivideSymbol parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static StaffDivideSymbol parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const StaffDivideSymbol &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/StaffLayout.cpp b/src/private/mx/core/generated/StaffLayout.cpp index c6ff09891..021968aad 100644 --- a/src/private/mx/core/generated/StaffLayout.cpp +++ b/src/private/mx/core/generated/StaffLayout.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/StaffLayout.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void StaffLayout::setStaffDistance(std::optional value) m_staffDistance = std::move(value); } -StaffLayout parseStaffLayout(pugi::xml_node el) +StaffLayout parseStaffLayout(pugi::xml_node el, const ParseContext &context) { StaffLayout out; for (pugi::xml_attribute a : el.attributes()) @@ -42,23 +43,23 @@ StaffLayout parseStaffLayout(pugi::xml_node el) } if (aname == "number") { - out.setNumber(StaffNumber::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else { throwUnknownAttribute(el, a.name()); } } - parseStaffLayoutContent(out, el); + parseStaffLayoutContent(out, el, context); return out; } -void parseStaffLayoutContent(StaffLayout &out, pugi::xml_node el) +void parseStaffLayoutContent(StaffLayout &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "staff-distance")) { - out.setStaffDistance(Tenths::parse(childText(cursor))); + out.setStaffDistance(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/StaffLayout.h b/src/private/mx/core/generated/StaffLayout.h index 2818adf6e..963b4e6c7 100644 --- a/src/private/mx/core/generated/StaffLayout.h +++ b/src/private/mx/core/generated/StaffLayout.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Staff layout includes the vertical distance from the bottom line of the previous staff in this /// system to the top line of the staff specified by the number attribute. The optional number /// attribute refers to staff numbers within the part, from top to bottom on the system. A value of 1 @@ -40,9 +42,9 @@ class StaffLayout final std::optional m_staffDistance; }; -StaffLayout parseStaffLayout(pugi::xml_node el); +StaffLayout parseStaffLayout(pugi::xml_node el, const ParseContext &context); -void parseStaffLayoutContent(StaffLayout &out, pugi::xml_node el); +void parseStaffLayoutContent(StaffLayout &out, pugi::xml_node el, const ParseContext &context); void serializeStaffLayout(const StaffLayout &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/StaffLine.cpp b/src/private/mx/core/generated/StaffLine.cpp index c17ac0c51..f0c5775f3 100644 --- a/src/private/mx/core/generated/StaffLine.cpp +++ b/src/private/mx/core/generated/StaffLine.cpp @@ -38,20 +38,33 @@ std::string StaffLine::toString() const bool StaffLine::tryParse(std::string_view text, StaffLine &out) { - int v{}; - if (!tryParseInt(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + StaffLine parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = StaffLine{v}; + out = std::move(parsed); return true; } StaffLine StaffLine::parse(std::string_view text) { - StaffLine v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +StaffLine StaffLine::parse(std::string_view text, ValueParseOutcome &outcome) +{ + int v{}; + if (!tryParseInt(text, v)) + { + outcome = ValueParseOutcome::invalid; + return StaffLine{}; + } + StaffLine out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/StaffLine.h b/src/private/mx/core/generated/StaffLine.h index cbfd1f22c..5da64b2aa 100644 --- a/src/private/mx/core/generated/StaffLine.h +++ b/src/private/mx/core/generated/StaffLine.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -36,6 +38,9 @@ class StaffLine final /// Lenient: non-numeric text yields the clamped zero. static StaffLine parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static StaffLine parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const StaffLine &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/StaffLinePosition.cpp b/src/private/mx/core/generated/StaffLinePosition.cpp index 82b39c163..a932473a0 100644 --- a/src/private/mx/core/generated/StaffLinePosition.cpp +++ b/src/private/mx/core/generated/StaffLinePosition.cpp @@ -34,20 +34,33 @@ std::string StaffLinePosition::toString() const bool StaffLinePosition::tryParse(std::string_view text, StaffLinePosition &out) { - int v{}; - if (!tryParseInt(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + StaffLinePosition parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = StaffLinePosition{v}; + out = std::move(parsed); return true; } StaffLinePosition StaffLinePosition::parse(std::string_view text) { - StaffLinePosition v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +StaffLinePosition StaffLinePosition::parse(std::string_view text, ValueParseOutcome &outcome) +{ + int v{}; + if (!tryParseInt(text, v)) + { + outcome = ValueParseOutcome::invalid; + return StaffLinePosition{}; + } + StaffLinePosition out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/StaffLinePosition.h b/src/private/mx/core/generated/StaffLinePosition.h index e4ebc798d..96ce77627 100644 --- a/src/private/mx/core/generated/StaffLinePosition.h +++ b/src/private/mx/core/generated/StaffLinePosition.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -37,6 +39,9 @@ class StaffLinePosition final /// Lenient: non-numeric text yields the clamped zero. static StaffLinePosition parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static StaffLinePosition parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const StaffLinePosition &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/StaffNumber.cpp b/src/private/mx/core/generated/StaffNumber.cpp index 5d2d57915..41d01a50e 100644 --- a/src/private/mx/core/generated/StaffNumber.cpp +++ b/src/private/mx/core/generated/StaffNumber.cpp @@ -38,20 +38,33 @@ std::string StaffNumber::toString() const bool StaffNumber::tryParse(std::string_view text, StaffNumber &out) { - int v{}; - if (!tryParseInt(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + StaffNumber parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = StaffNumber{v}; + out = std::move(parsed); return true; } StaffNumber StaffNumber::parse(std::string_view text) { - StaffNumber v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +StaffNumber StaffNumber::parse(std::string_view text, ValueParseOutcome &outcome) +{ + int v{}; + if (!tryParseInt(text, v)) + { + outcome = ValueParseOutcome::invalid; + return StaffNumber{}; + } + StaffNumber out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/StaffNumber.h b/src/private/mx/core/generated/StaffNumber.h index cde886f68..b92b6f4c8 100644 --- a/src/private/mx/core/generated/StaffNumber.h +++ b/src/private/mx/core/generated/StaffNumber.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -36,6 +38,9 @@ class StaffNumber final /// Lenient: non-numeric text yields the clamped zero. static StaffNumber parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static StaffNumber parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const StaffNumber &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/StaffSize.cpp b/src/private/mx/core/generated/StaffSize.cpp index bdce369a7..701df9641 100644 --- a/src/private/mx/core/generated/StaffSize.cpp +++ b/src/private/mx/core/generated/StaffSize.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/StaffSize.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void StaffSize::setValue(NonNegativeDecimal value) m_value = std::move(value); } -StaffSize parseStaffSize(pugi::xml_node el) +StaffSize parseStaffSize(pugi::xml_node el, const ParseContext &context) { StaffSize out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ StaffSize parseStaffSize(pugi::xml_node el) } if (aname == "scaling") { - out.setScaling(NonNegativeDecimal::parse(a.value())); + out.setScaling(parseValue(a.value(), context, el, "scaling")); } else { throwUnknownAttribute(el, a.name()); } } - parseStaffSizeContent(out, el); + parseStaffSizeContent(out, el, context); return out; } -void parseStaffSizeContent(StaffSize &out, pugi::xml_node el) +void parseStaffSizeContent(StaffSize &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(NonNegativeDecimal::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/StaffSize.h b/src/private/mx/core/generated/StaffSize.h index 73d568e12..2a4b1accc 100644 --- a/src/private/mx/core/generated/StaffSize.h +++ b/src/private/mx/core/generated/StaffSize.h @@ -15,6 +15,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The staff-size element indicates how large a staff space is on this staff, expressed as a /// percentage of the work's default scaling. Values less than 100 make the staff space smaller while /// values over 100 make the staff space larger. A staff-type of cue, ossia, or editorial implies a @@ -40,9 +42,9 @@ class StaffSize final NonNegativeDecimal m_value{}; }; -StaffSize parseStaffSize(pugi::xml_node el); +StaffSize parseStaffSize(pugi::xml_node el, const ParseContext &context); -void parseStaffSizeContent(StaffSize &out, pugi::xml_node el); +void parseStaffSizeContent(StaffSize &out, pugi::xml_node el, const ParseContext &context); void serializeStaffSize(const StaffSize &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/StaffTuning.cpp b/src/private/mx/core/generated/StaffTuning.cpp index add34a7c1..7accd9cc8 100644 --- a/src/private/mx/core/generated/StaffTuning.cpp +++ b/src/private/mx/core/generated/StaffTuning.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/StaffTuning.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void StaffTuning::setTuning(TuningGroup value) m_tuning = std::move(value); } -StaffTuning parseStaffTuning(pugi::xml_node el) +StaffTuning parseStaffTuning(pugi::xml_node el, const ParseContext &context) { StaffTuning out; bool seen_line = false; @@ -44,7 +45,7 @@ StaffTuning parseStaffTuning(pugi::xml_node el) if (aname == "line") { seen_line = true; - out.setLine(StaffLine::parse(a.value())); + out.setLine(parseValue(a.value(), context, el, "line")); } else { @@ -55,16 +56,16 @@ StaffTuning parseStaffTuning(pugi::xml_node el) { throwMissingAttribute(el, "line"); } - parseStaffTuningContent(out, el); + parseStaffTuningContent(out, el, context); return out; } -void parseStaffTuningContent(StaffTuning &out, pugi::xml_node el) +void parseStaffTuningContent(StaffTuning &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "tuning-step"))) { - out.setTuning(parseTuningGroup(el, cursor)); + out.setTuning(parseTuningGroup(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/StaffTuning.h b/src/private/mx/core/generated/StaffTuning.h index 8b3b4c539..3bb1ba431 100644 --- a/src/private/mx/core/generated/StaffTuning.h +++ b/src/private/mx/core/generated/StaffTuning.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The staff-tuning type specifies the open, non-capo tuning of the lines on a tablature staff. /// Content fields mirror the schema grammar in declaration order; the /// serializer walks them, so a wrong order is unrepresentable (plan §2.3). @@ -35,9 +37,9 @@ class StaffTuning final TuningGroup m_tuning{}; }; -StaffTuning parseStaffTuning(pugi::xml_node el); +StaffTuning parseStaffTuning(pugi::xml_node el, const ParseContext &context); -void parseStaffTuningContent(StaffTuning &out, pugi::xml_node el); +void parseStaffTuningContent(StaffTuning &out, pugi::xml_node el, const ParseContext &context); void serializeStaffTuning(const StaffTuning &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/StaffType.cpp b/src/private/mx/core/generated/StaffType.cpp index da8283bdd..1ffe2fe8a 100644 --- a/src/private/mx/core/generated/StaffType.cpp +++ b/src/private/mx/core/generated/StaffType.cpp @@ -56,9 +56,15 @@ bool StaffType::tryParse(std::string_view text, StaffType &out) noexcept } StaffType StaffType::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +StaffType StaffType::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { StaffType v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/StaffType.h b/src/private/mx/core/generated/StaffType.h index 9528c706c..c43ee47a4 100644 --- a/src/private/mx/core/generated/StaffType.h +++ b/src/private/mx/core/generated/StaffType.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -54,6 +56,9 @@ class StaffType final /// (the import leniency policy; never produces an invalid value). static StaffType parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static StaffType parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const StaffType &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/StartNote.cpp b/src/private/mx/core/generated/StartNote.cpp index fd7048699..aa1d0c2e8 100644 --- a/src/private/mx/core/generated/StartNote.cpp +++ b/src/private/mx/core/generated/StartNote.cpp @@ -48,9 +48,15 @@ bool StartNote::tryParse(std::string_view text, StartNote &out) noexcept } StartNote StartNote::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +StartNote StartNote::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { StartNote v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/StartNote.h b/src/private/mx/core/generated/StartNote.h index 7083347f9..9307d67b3 100644 --- a/src/private/mx/core/generated/StartNote.h +++ b/src/private/mx/core/generated/StartNote.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -43,6 +45,9 @@ class StartNote final /// (the import leniency policy; never produces an invalid value). static StartNote parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static StartNote parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const StartNote &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/StartStop.cpp b/src/private/mx/core/generated/StartStop.cpp index 90d747e7e..7ee3fd1de 100644 --- a/src/private/mx/core/generated/StartStop.cpp +++ b/src/private/mx/core/generated/StartStop.cpp @@ -42,9 +42,15 @@ bool StartStop::tryParse(std::string_view text, StartStop &out) noexcept } StartStop StartStop::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +StartStop StartStop::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { StartStop v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/StartStop.h b/src/private/mx/core/generated/StartStop.h index b87a5934e..ec8360b00 100644 --- a/src/private/mx/core/generated/StartStop.h +++ b/src/private/mx/core/generated/StartStop.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -47,6 +49,9 @@ class StartStop final /// (the import leniency policy; never produces an invalid value). static StartStop parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static StartStop parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const StartStop &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/StartStopContinue.cpp b/src/private/mx/core/generated/StartStopContinue.cpp index c07bf1b16..35a5901f5 100644 --- a/src/private/mx/core/generated/StartStopContinue.cpp +++ b/src/private/mx/core/generated/StartStopContinue.cpp @@ -48,9 +48,15 @@ bool StartStopContinue::tryParse(std::string_view text, StartStopContinue &out) } StartStopContinue StartStopContinue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +StartStopContinue StartStopContinue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { StartStopContinue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/StartStopContinue.h b/src/private/mx/core/generated/StartStopContinue.h index 718ef41f3..f30f9f362 100644 --- a/src/private/mx/core/generated/StartStopContinue.h +++ b/src/private/mx/core/generated/StartStopContinue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -52,6 +54,9 @@ class StartStopContinue final /// (the import leniency policy; never produces an invalid value). static StartStopContinue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static StartStopContinue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const StartStopContinue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/StartStopDiscontinue.cpp b/src/private/mx/core/generated/StartStopDiscontinue.cpp index b45e6beb2..a7ba6c574 100644 --- a/src/private/mx/core/generated/StartStopDiscontinue.cpp +++ b/src/private/mx/core/generated/StartStopDiscontinue.cpp @@ -48,9 +48,15 @@ bool StartStopDiscontinue::tryParse(std::string_view text, StartStopDiscontinue } StartStopDiscontinue StartStopDiscontinue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +StartStopDiscontinue StartStopDiscontinue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { StartStopDiscontinue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/StartStopDiscontinue.h b/src/private/mx/core/generated/StartStopDiscontinue.h index a12ffb9f1..6baa17ec8 100644 --- a/src/private/mx/core/generated/StartStopDiscontinue.h +++ b/src/private/mx/core/generated/StartStopDiscontinue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -47,6 +49,9 @@ class StartStopDiscontinue final /// (the import leniency policy; never produces an invalid value). static StartStopDiscontinue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static StartStopDiscontinue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const StartStopDiscontinue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/StartStopSingle.cpp b/src/private/mx/core/generated/StartStopSingle.cpp index 751a864bc..be0329917 100644 --- a/src/private/mx/core/generated/StartStopSingle.cpp +++ b/src/private/mx/core/generated/StartStopSingle.cpp @@ -48,9 +48,15 @@ bool StartStopSingle::tryParse(std::string_view text, StartStopSingle &out) noex } StartStopSingle StartStopSingle::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +StartStopSingle StartStopSingle::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { StartStopSingle v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/StartStopSingle.h b/src/private/mx/core/generated/StartStopSingle.h index 99e5c2d17..9835aebaf 100644 --- a/src/private/mx/core/generated/StartStopSingle.h +++ b/src/private/mx/core/generated/StartStopSingle.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -45,6 +47,9 @@ class StartStopSingle final /// (the import leniency policy; never produces an invalid value). static StartStopSingle parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static StartStopSingle parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const StartStopSingle &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Stem.cpp b/src/private/mx/core/generated/Stem.cpp index 09591e938..d7042fb00 100644 --- a/src/private/mx/core/generated/Stem.cpp +++ b/src/private/mx/core/generated/Stem.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Stem.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -70,7 +71,7 @@ void Stem::setValue(StemValue value) m_value = std::move(value); } -Stem parseStem(pugi::xml_node el) +Stem parseStem(pugi::xml_node el, const ParseContext &context) { Stem out; for (pugi::xml_attribute a : el.attributes()) @@ -82,36 +83,36 @@ Stem parseStem(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseStemContent(out, el); + parseStemContent(out, el, context); return out; } -void parseStemContent(Stem &out, pugi::xml_node el) +void parseStemContent(Stem &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(StemValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Stem.h b/src/private/mx/core/generated/Stem.h index 07ceaa3ea..66c25bcdc 100644 --- a/src/private/mx/core/generated/Stem.h +++ b/src/private/mx/core/generated/Stem.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Stems can be down, up, none, or double. For down and up stems, the position attributes can be /// used to specify stem length. The relative values specify the end of the stem relative to the /// program default. Default values specify an absolute end stem position. Negative values of @@ -48,9 +50,9 @@ class Stem final StemValue m_value{}; }; -Stem parseStem(pugi::xml_node el); +Stem parseStem(pugi::xml_node el, const ParseContext &context); -void parseStemContent(Stem &out, pugi::xml_node el); +void parseStemContent(Stem &out, pugi::xml_node el, const ParseContext &context); void serializeStem(const Stem &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/StemValue.cpp b/src/private/mx/core/generated/StemValue.cpp index 3053694e8..9897c5e2f 100644 --- a/src/private/mx/core/generated/StemValue.cpp +++ b/src/private/mx/core/generated/StemValue.cpp @@ -54,9 +54,15 @@ bool StemValue::tryParse(std::string_view text, StemValue &out) noexcept } StemValue StemValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +StemValue StemValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { StemValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/StemValue.h b/src/private/mx/core/generated/StemValue.h index d4ea80262..9fac44548 100644 --- a/src/private/mx/core/generated/StemValue.h +++ b/src/private/mx/core/generated/StemValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -44,6 +46,9 @@ class StemValue final /// (the import leniency policy; never produces an invalid value). static StemValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static StemValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const StemValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Step.cpp b/src/private/mx/core/generated/Step.cpp index 1eb1dea67..1f6e95f29 100644 --- a/src/private/mx/core/generated/Step.cpp +++ b/src/private/mx/core/generated/Step.cpp @@ -66,9 +66,15 @@ bool Step::tryParse(std::string_view text, Step &out) noexcept } Step Step::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +Step Step::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { Step v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/Step.h b/src/private/mx/core/generated/Step.h index ac1f36533..1d8f3aebe 100644 --- a/src/private/mx/core/generated/Step.h +++ b/src/private/mx/core/generated/Step.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -51,6 +53,9 @@ class Step final /// (the import leniency policy; never produces an invalid value). static Step parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static Step parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const Step &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Stick.cpp b/src/private/mx/core/generated/Stick.cpp index 2eaef158f..8a95ec6c9 100644 --- a/src/private/mx/core/generated/Stick.cpp +++ b/src/private/mx/core/generated/Stick.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Stick.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -60,7 +61,7 @@ void Stick::setStickMaterial(StickMaterial value) m_stickMaterial = std::move(value); } -Stick parseStick(pugi::xml_node el) +Stick parseStick(pugi::xml_node el, const ParseContext &context) { Stick out; for (pugi::xml_attribute a : el.attributes()) @@ -72,31 +73,31 @@ Stick parseStick(pugi::xml_node el) } if (aname == "tip") { - out.setTip(TipDirection::parse(a.value())); + out.setTip(parseValue(a.value(), context, el, "tip")); } else if (aname == "parentheses") { - out.setParentheses(YesNo::parse(a.value())); + out.setParentheses(parseValue(a.value(), context, el, "parentheses")); } else if (aname == "dashed-circle") { - out.setDashedCircle(YesNo::parse(a.value())); + out.setDashedCircle(parseValue(a.value(), context, el, "dashed-circle")); } else { throwUnknownAttribute(el, a.name()); } } - parseStickContent(out, el); + parseStickContent(out, el, context); return out; } -void parseStickContent(Stick &out, pugi::xml_node el) +void parseStickContent(Stick &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "stick-type")) { - out.setStickType(StickType::parse(childText(cursor))); + out.setStickType(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -105,7 +106,7 @@ void parseStickContent(Stick &out, pugi::xml_node el) } if (cursorIs(cursor, "stick-material")) { - out.setStickMaterial(StickMaterial::parse(childText(cursor))); + out.setStickMaterial(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/Stick.h b/src/private/mx/core/generated/Stick.h index c8018179d..fe9e1a0cf 100644 --- a/src/private/mx/core/generated/Stick.h +++ b/src/private/mx/core/generated/Stick.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The stick type represents pictograms where the material of the stick, mallet, or beater is /// included.The parentheses and dashed-circle attributes indicate the presence of these marks around /// the round beater part of a pictogram. Values for these attributes are "no" if not present. @@ -48,9 +50,9 @@ class Stick final StickMaterial m_stickMaterial{}; }; -Stick parseStick(pugi::xml_node el); +Stick parseStick(pugi::xml_node el, const ParseContext &context); -void parseStickContent(Stick &out, pugi::xml_node el); +void parseStickContent(Stick &out, pugi::xml_node el, const ParseContext &context); void serializeStick(const Stick &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/StickLocation.cpp b/src/private/mx/core/generated/StickLocation.cpp index 23956ba76..1fc405b84 100644 --- a/src/private/mx/core/generated/StickLocation.cpp +++ b/src/private/mx/core/generated/StickLocation.cpp @@ -54,9 +54,15 @@ bool StickLocation::tryParse(std::string_view text, StickLocation &out) noexcept } StickLocation StickLocation::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +StickLocation StickLocation::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { StickLocation v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/StickLocation.h b/src/private/mx/core/generated/StickLocation.h index 1cbf346a6..941974db6 100644 --- a/src/private/mx/core/generated/StickLocation.h +++ b/src/private/mx/core/generated/StickLocation.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -45,6 +47,9 @@ class StickLocation final /// (the import leniency policy; never produces an invalid value). static StickLocation parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static StickLocation parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const StickLocation &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/StickMaterial.cpp b/src/private/mx/core/generated/StickMaterial.cpp index 4bcf2667c..af8d93a3a 100644 --- a/src/private/mx/core/generated/StickMaterial.cpp +++ b/src/private/mx/core/generated/StickMaterial.cpp @@ -56,9 +56,15 @@ bool StickMaterial::tryParse(std::string_view text, StickMaterial &out) noexcept } StickMaterial StickMaterial::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +StickMaterial StickMaterial::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { StickMaterial v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/StickMaterial.h b/src/private/mx/core/generated/StickMaterial.h index 5450854e7..dfa79fbdb 100644 --- a/src/private/mx/core/generated/StickMaterial.h +++ b/src/private/mx/core/generated/StickMaterial.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -46,6 +48,9 @@ class StickMaterial final /// (the import leniency policy; never produces an invalid value). static StickMaterial parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static StickMaterial parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const StickMaterial &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/StickType.cpp b/src/private/mx/core/generated/StickType.cpp index 94f1f751c..2bae6c125 100644 --- a/src/private/mx/core/generated/StickType.cpp +++ b/src/private/mx/core/generated/StickType.cpp @@ -82,9 +82,15 @@ bool StickType::tryParse(std::string_view text, StickType &out) noexcept } StickType StickType::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +StickType StickType::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { StickType v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/StickType.h b/src/private/mx/core/generated/StickType.h index 7162f1d5e..ebb6d49e2 100644 --- a/src/private/mx/core/generated/StickType.h +++ b/src/private/mx/core/generated/StickType.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -57,6 +59,9 @@ class StickType final /// (the import leniency policy; never produces an invalid value). static StickType parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static StickType parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const StickType &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/String.cpp b/src/private/mx/core/generated/String.cpp index 0f8c39f56..3013162a4 100644 --- a/src/private/mx/core/generated/String.cpp +++ b/src/private/mx/core/generated/String.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/String.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -120,7 +121,7 @@ void String::setValue(StringNumber value) m_value = std::move(value); } -String parseString(pugi::xml_node el) +String parseString(pugi::xml_node el, const ParseContext &context) { String out; for (pugi::xml_attribute a : el.attributes()) @@ -132,56 +133,56 @@ String parseString(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else { throwUnknownAttribute(el, a.name()); } } - parseStringContent(out, el); + parseStringContent(out, el, context); return out; } -void parseStringContent(String &out, pugi::xml_node el) +void parseStringContent(String &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(StringNumber::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/String.h b/src/private/mx/core/generated/String.h index aaceccc6c..301cc8c7b 100644 --- a/src/private/mx/core/generated/String.h +++ b/src/private/mx/core/generated/String.h @@ -22,6 +22,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The string type is used with tablature notation, regular notation (where it is often circled), /// and chord diagrams. String numbers start with 1 for the highest pitched full-length string. class String final @@ -65,9 +67,9 @@ class String final StringNumber m_value{}; }; -String parseString(pugi::xml_node el); +String parseString(pugi::xml_node el, const ParseContext &context); -void parseStringContent(String &out, pugi::xml_node el); +void parseStringContent(String &out, pugi::xml_node el, const ParseContext &context); void serializeString(const String &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/StringMute.cpp b/src/private/mx/core/generated/StringMute.cpp index 68f4ff308..a5c16aa88 100644 --- a/src/private/mx/core/generated/StringMute.cpp +++ b/src/private/mx/core/generated/StringMute.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/StringMute.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -140,7 +141,7 @@ void StringMute::setID(std::optional value) m_id = std::move(value); } -StringMute parseStringMute(pugi::xml_node el) +StringMute parseStringMute(pugi::xml_node el, const ParseContext &context) { StringMute out; bool seen_type = false; @@ -154,55 +155,55 @@ StringMute parseStringMute(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(OnOff::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -213,13 +214,14 @@ StringMute parseStringMute(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseStringMuteContent(out, el); + parseStringMuteContent(out, el, context); return out; } -void parseStringMuteContent(StringMute &out, pugi::xml_node el) +void parseStringMuteContent(StringMute &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/StringMute.h b/src/private/mx/core/generated/StringMute.h index 129538ce7..01a9278a5 100644 --- a/src/private/mx/core/generated/StringMute.h +++ b/src/private/mx/core/generated/StringMute.h @@ -25,6 +25,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The string-mute type represents string mute on and mute off symbols. class StringMute final { @@ -73,9 +75,9 @@ class StringMute final std::optional m_id; }; -StringMute parseStringMute(pugi::xml_node el); +StringMute parseStringMute(pugi::xml_node el, const ParseContext &context); -void parseStringMuteContent(StringMute &out, pugi::xml_node el); +void parseStringMuteContent(StringMute &out, pugi::xml_node el, const ParseContext &context); void serializeStringMute(const StringMute &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/StringNumber.cpp b/src/private/mx/core/generated/StringNumber.cpp index 687823e62..a33f8308e 100644 --- a/src/private/mx/core/generated/StringNumber.cpp +++ b/src/private/mx/core/generated/StringNumber.cpp @@ -38,20 +38,33 @@ std::string StringNumber::toString() const bool StringNumber::tryParse(std::string_view text, StringNumber &out) { - int v{}; - if (!tryParseInt(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + StringNumber parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = StringNumber{v}; + out = std::move(parsed); return true; } StringNumber StringNumber::parse(std::string_view text) { - StringNumber v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +StringNumber StringNumber::parse(std::string_view text, ValueParseOutcome &outcome) +{ + int v{}; + if (!tryParseInt(text, v)) + { + outcome = ValueParseOutcome::invalid; + return StringNumber{}; + } + StringNumber out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/StringNumber.h b/src/private/mx/core/generated/StringNumber.h index ebfa2ad36..5e5323677 100644 --- a/src/private/mx/core/generated/StringNumber.h +++ b/src/private/mx/core/generated/StringNumber.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -36,6 +38,9 @@ class StringNumber final /// Lenient: non-numeric text yields the clamped zero. static StringNumber parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static StringNumber parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const StringNumber &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/StrongAccent.cpp b/src/private/mx/core/generated/StrongAccent.cpp index a24a3c6fb..6e820722c 100644 --- a/src/private/mx/core/generated/StrongAccent.cpp +++ b/src/private/mx/core/generated/StrongAccent.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/StrongAccent.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -20,7 +21,7 @@ void StrongAccent::setType(std::optional value) m_type = std::move(value); } -StrongAccent parseStrongAccent(pugi::xml_node el) +StrongAccent parseStrongAccent(pugi::xml_node el, const ParseContext &context) { StrongAccent out; for (pugi::xml_attribute a : el.attributes()) @@ -32,54 +33,54 @@ StrongAccent parseStrongAccent(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "type") { - out.setType(UpDown::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else { throwUnknownAttribute(el, a.name()); } } - parseEmptyPlacementContent(out, el); + parseEmptyPlacementContent(out, el, context); return out; } diff --git a/src/private/mx/core/generated/StrongAccent.h b/src/private/mx/core/generated/StrongAccent.h index c664abf83..b12316fad 100644 --- a/src/private/mx/core/generated/StrongAccent.h +++ b/src/private/mx/core/generated/StrongAccent.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The strong-accent type indicates a vertical accent mark. The type attribute indicates if the /// point of the accent is down or up. /// The schema's complexContent extension: non-polymorphic public @@ -31,7 +33,7 @@ class StrongAccent : public EmptyPlacement std::optional m_type; }; -StrongAccent parseStrongAccent(pugi::xml_node el); +StrongAccent parseStrongAccent(pugi::xml_node el, const ParseContext &context); void serializeStrongAccent(const StrongAccent &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/StyleText.cpp b/src/private/mx/core/generated/StyleText.cpp index ae9bb9536..2064e350c 100644 --- a/src/private/mx/core/generated/StyleText.cpp +++ b/src/private/mx/core/generated/StyleText.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/StyleText.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -110,7 +111,7 @@ void StyleText::setValue(std::string value) m_value = std::move(value); } -StyleText parseStyleText(pugi::xml_node el) +StyleText parseStyleText(pugi::xml_node el, const ParseContext &context) { StyleText out; for (pugi::xml_attribute a : el.attributes()) @@ -122,50 +123,50 @@ StyleText parseStyleText(pugi::xml_node el) } if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseStyleTextContent(out, el); + parseStyleTextContent(out, el, context); return out; } -void parseStyleTextContent(StyleText &out, pugi::xml_node el) +void parseStyleTextContent(StyleText &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/StyleText.h b/src/private/mx/core/generated/StyleText.h index e18b6bf75..ee17b5b99 100644 --- a/src/private/mx/core/generated/StyleText.h +++ b/src/private/mx/core/generated/StyleText.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The style-text type represents a text element with a print-style attribute group. class StyleText final { @@ -59,9 +61,9 @@ class StyleText final std::string m_value{}; }; -StyleText parseStyleText(pugi::xml_node el); +StyleText parseStyleText(pugi::xml_node el, const ParseContext &context); -void parseStyleTextContent(StyleText &out, pugi::xml_node el); +void parseStyleTextContent(StyleText &out, pugi::xml_node el, const ParseContext &context); void serializeStyleText(const StyleText &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Supports.cpp b/src/private/mx/core/generated/Supports.cpp index 8f627d13b..5b75b36ed 100644 --- a/src/private/mx/core/generated/Supports.cpp +++ b/src/private/mx/core/generated/Supports.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Supports.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,7 +51,7 @@ void Supports::setValue(std::optional value) m_value = std::move(value); } -Supports parseSupports(pugi::xml_node el) +Supports parseSupports(pugi::xml_node el, const ParseContext &context) { Supports out; bool seen_type = false; @@ -65,16 +66,16 @@ Supports parseSupports(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(YesNo::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "element") { seen_element = true; - out.setElement(NameToken::parse(a.value())); + out.setElement(parseValue(a.value(), context, el, "element")); } else if (aname == "attribute") { - out.setAttribute(NameToken::parse(a.value())); + out.setAttribute(parseValue(a.value(), context, el, "attribute")); } else if (aname == "value") { @@ -93,13 +94,14 @@ Supports parseSupports(pugi::xml_node el) { throwMissingAttribute(el, "element"); } - parseSupportsContent(out, el); + parseSupportsContent(out, el, context); return out; } -void parseSupportsContent(Supports &out, pugi::xml_node el) +void parseSupportsContent(Supports &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Supports.h b/src/private/mx/core/generated/Supports.h index 36838bf9b..36e255ed9 100644 --- a/src/private/mx/core/generated/Supports.h +++ b/src/private/mx/core/generated/Supports.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The supports type indicates if a MusicXML encoding supports a particular MusicXML element. This /// is recommended for elements like beam, stem, and accidental, where the absence of an element is /// ambiguous if you do not know if the encoding supports that element. For Version 2.0, the supports @@ -43,9 +45,9 @@ class Supports final std::optional m_value; }; -Supports parseSupports(pugi::xml_node el); +Supports parseSupports(pugi::xml_node el, const ParseContext &context); -void parseSupportsContent(Supports &out, pugi::xml_node el); +void parseSupportsContent(Supports &out, pugi::xml_node el, const ParseContext &context); void serializeSupports(const Supports &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Swing.cpp b/src/private/mx/core/generated/Swing.cpp index 3dd848114..89af41646 100644 --- a/src/private/mx/core/generated/Swing.cpp +++ b/src/private/mx/core/generated/Swing.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Swing.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Swing::setSwingStyle(std::optional value) m_swingStyle = std::move(value); } -Swing parseSwing(pugi::xml_node el) +Swing parseSwing(pugi::xml_node el, const ParseContext &context) { Swing out; for (pugi::xml_attribute a : el.attributes()) @@ -42,16 +43,16 @@ Swing parseSwing(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseSwingContent(out, el); + parseSwingContent(out, el, context); return out; } -void parseSwingContent(Swing &out, pugi::xml_node el) +void parseSwingContent(Swing &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "straight") || cursorIs(cursor, "first"))) { - out.setChoice(parseSwingChoice(el, cursor)); + out.setChoice(parseSwingChoice(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/Swing.h b/src/private/mx/core/generated/Swing.h index 60efe9e03..ce57a1312 100644 --- a/src/private/mx/core/generated/Swing.h +++ b/src/private/mx/core/generated/Swing.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The swing element specifies whether or not to use swing playback, where consecutive on-beat / /// off-beat eighth or 16th notes are played with unequal nominal durations. The straight element /// specifies that no swing is present, so consecutive notes have equal durations. The first and @@ -47,9 +49,9 @@ class Swing final std::optional m_swingStyle; }; -Swing parseSwing(pugi::xml_node el); +Swing parseSwing(pugi::xml_node el, const ParseContext &context); -void parseSwingContent(Swing &out, pugi::xml_node el); +void parseSwingContent(Swing &out, pugi::xml_node el, const ParseContext &context); void serializeSwing(const Swing &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/SwingChoice.cpp b/src/private/mx/core/generated/SwingChoice.cpp index 4236d3bb2..bacd5e70b 100644 --- a/src/private/mx/core/generated/SwingChoice.cpp +++ b/src/private/mx/core/generated/SwingChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/SwingChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,17 +26,17 @@ SwingChoice SwingChoice::group(SwingChoiceGroup value) return SwingChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -SwingChoice parseSwingChoice(pugi::xml_node el, pugi::xml_node &cursor) +SwingChoice parseSwingChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "straight"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return SwingChoice::straight(std::move(value)); } if (cursor && (cursorIs(cursor, "first"))) { - return SwingChoice::group(parseSwingChoiceGroup(el, cursor)); + return SwingChoice::group(parseSwingChoiceGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/SwingChoice.h b/src/private/mx/core/generated/SwingChoice.h index e7895c259..66ba73f10 100644 --- a/src/private/mx/core/generated/SwingChoice.h +++ b/src/private/mx/core/generated/SwingChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -80,7 +82,7 @@ class SwingChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -SwingChoice parseSwingChoice(pugi::xml_node el, pugi::xml_node &cursor); +SwingChoice parseSwingChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeSwingChoice(const SwingChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/SwingChoiceGroup.cpp b/src/private/mx/core/generated/SwingChoiceGroup.cpp index 392a9a619..b26d40f37 100644 --- a/src/private/mx/core/generated/SwingChoiceGroup.cpp +++ b/src/private/mx/core/generated/SwingChoiceGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/SwingChoiceGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,12 +41,12 @@ void SwingChoiceGroup::setSwingType(std::optional value) m_swingType = std::move(value); } -SwingChoiceGroup parseSwingChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor) +SwingChoiceGroup parseSwingChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { SwingChoiceGroup out; if (cursorIs(cursor, "first")) { - out.setFirst(parseInt(childText(cursor))); + out.setFirst(parseIntegerValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -54,7 +55,7 @@ SwingChoiceGroup parseSwingChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor } if (cursorIs(cursor, "second")) { - out.setSecond(parseInt(childText(cursor))); + out.setSecond(parseIntegerValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -63,7 +64,7 @@ SwingChoiceGroup parseSwingChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor } if (cursorIs(cursor, "swing-type")) { - out.setSwingType(SwingTypeValue::parse(childText(cursor))); + out.setSwingType(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/SwingChoiceGroup.h b/src/private/mx/core/generated/SwingChoiceGroup.h index 674f45314..e8cf16cf8 100644 --- a/src/private/mx/core/generated/SwingChoiceGroup.h +++ b/src/private/mx/core/generated/SwingChoiceGroup.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -39,7 +41,7 @@ class SwingChoiceGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -SwingChoiceGroup parseSwingChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor); +SwingChoiceGroup parseSwingChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeSwingChoiceGroup(const SwingChoiceGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/SwingTypeValue.cpp b/src/private/mx/core/generated/SwingTypeValue.cpp index a325aeb91..1cf64c570 100644 --- a/src/private/mx/core/generated/SwingTypeValue.cpp +++ b/src/private/mx/core/generated/SwingTypeValue.cpp @@ -42,9 +42,15 @@ bool SwingTypeValue::tryParse(std::string_view text, SwingTypeValue &out) noexce } SwingTypeValue SwingTypeValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +SwingTypeValue SwingTypeValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { SwingTypeValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/SwingTypeValue.h b/src/private/mx/core/generated/SwingTypeValue.h index 4203fd801..07eced4af 100644 --- a/src/private/mx/core/generated/SwingTypeValue.h +++ b/src/private/mx/core/generated/SwingTypeValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -41,6 +43,9 @@ class SwingTypeValue final /// (the import leniency policy; never produces an invalid value). static SwingTypeValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static SwingTypeValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const SwingTypeValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Syllabic.cpp b/src/private/mx/core/generated/Syllabic.cpp index 1481a215d..fb96b6475 100644 --- a/src/private/mx/core/generated/Syllabic.cpp +++ b/src/private/mx/core/generated/Syllabic.cpp @@ -54,9 +54,15 @@ bool Syllabic::tryParse(std::string_view text, Syllabic &out) noexcept } Syllabic Syllabic::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +Syllabic Syllabic::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { Syllabic v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/Syllabic.h b/src/private/mx/core/generated/Syllabic.h index 775390640..7eb5b7db7 100644 --- a/src/private/mx/core/generated/Syllabic.h +++ b/src/private/mx/core/generated/Syllabic.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -46,6 +48,9 @@ class Syllabic final /// (the import leniency policy; never produces an invalid value). static Syllabic parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static Syllabic parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const Syllabic &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/SymbolSize.cpp b/src/private/mx/core/generated/SymbolSize.cpp index 775e29cb9..d474febc4 100644 --- a/src/private/mx/core/generated/SymbolSize.cpp +++ b/src/private/mx/core/generated/SymbolSize.cpp @@ -54,9 +54,15 @@ bool SymbolSize::tryParse(std::string_view text, SymbolSize &out) noexcept } SymbolSize SymbolSize::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +SymbolSize SymbolSize::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { SymbolSize v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/SymbolSize.h b/src/private/mx/core/generated/SymbolSize.h index b143a6b0c..ff5c6f54f 100644 --- a/src/private/mx/core/generated/SymbolSize.h +++ b/src/private/mx/core/generated/SymbolSize.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -45,6 +47,9 @@ class SymbolSize final /// (the import leniency policy; never produces an invalid value). static SymbolSize parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static SymbolSize parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const SymbolSize &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Sync.cpp b/src/private/mx/core/generated/Sync.cpp index 2ba3e2e26..79c8e3cf0 100644 --- a/src/private/mx/core/generated/Sync.cpp +++ b/src/private/mx/core/generated/Sync.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Sync.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,7 +51,7 @@ void Sync::setTimeOnly(std::optional value) m_timeOnly = std::move(value); } -Sync parseSync(pugi::xml_node el) +Sync parseSync(pugi::xml_node el, const ParseContext &context) { Sync out; bool seen_type = false; @@ -64,19 +65,19 @@ Sync parseSync(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(SyncType::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "latency") { - out.setLatency(Milliseconds::parse(a.value())); + out.setLatency(parseValue(a.value(), context, el, "latency")); } else if (aname == "player") { - out.setPlayer(Token::parse(a.value())); + out.setPlayer(parseValue(a.value(), context, el, "player")); } else if (aname == "time-only") { - out.setTimeOnly(TimeOnly::parse(a.value())); + out.setTimeOnly(parseValue(a.value(), context, el, "time-only")); } else { @@ -87,13 +88,14 @@ Sync parseSync(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseSyncContent(out, el); + parseSyncContent(out, el, context); return out; } -void parseSyncContent(Sync &out, pugi::xml_node el) +void parseSyncContent(Sync &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Sync.h b/src/private/mx/core/generated/Sync.h index 03d18e03d..17a4ead92 100644 --- a/src/private/mx/core/generated/Sync.h +++ b/src/private/mx/core/generated/Sync.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The sync type specifies the style that a score following application should use the synchronize /// an accompaniment with a performer. If this type is not included in a score, default /// synchronization depends on the application. The optional latency attribute specifies a time in @@ -45,9 +47,9 @@ class Sync final std::optional m_timeOnly; }; -Sync parseSync(pugi::xml_node el); +Sync parseSync(pugi::xml_node el, const ParseContext &context); -void parseSyncContent(Sync &out, pugi::xml_node el); +void parseSyncContent(Sync &out, pugi::xml_node el, const ParseContext &context); void serializeSync(const Sync &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/SyncType.cpp b/src/private/mx/core/generated/SyncType.cpp index 06672cb25..92cbb102a 100644 --- a/src/private/mx/core/generated/SyncType.cpp +++ b/src/private/mx/core/generated/SyncType.cpp @@ -61,9 +61,15 @@ bool SyncType::tryParse(std::string_view text, SyncType &out) noexcept } SyncType SyncType::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +SyncType SyncType::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { SyncType v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/SyncType.h b/src/private/mx/core/generated/SyncType.h index 4993200d2..7a1f06714 100644 --- a/src/private/mx/core/generated/SyncType.h +++ b/src/private/mx/core/generated/SyncType.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -55,6 +57,9 @@ class SyncType final /// (the import leniency policy; never produces an invalid value). static SyncType parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static SyncType parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const SyncType &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/SystemDividers.cpp b/src/private/mx/core/generated/SystemDividers.cpp index d2a992619..9bc60b94c 100644 --- a/src/private/mx/core/generated/SystemDividers.cpp +++ b/src/private/mx/core/generated/SystemDividers.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/SystemDividers.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void SystemDividers::setRightDivider(EmptyPrintObjectStyleAlign value) m_rightDivider = std::move(value); } -SystemDividers parseSystemDividers(pugi::xml_node el) +SystemDividers parseSystemDividers(pugi::xml_node el, const ParseContext &context) { SystemDividers out; for (pugi::xml_attribute a : el.attributes()) @@ -42,16 +43,16 @@ SystemDividers parseSystemDividers(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseSystemDividersContent(out, el); + parseSystemDividersContent(out, el, context); return out; } -void parseSystemDividersContent(SystemDividers &out, pugi::xml_node el) +void parseSystemDividersContent(SystemDividers &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "left-divider")) { - out.setLeftDivider(parseEmptyPrintObjectStyleAlign(cursor)); + out.setLeftDivider(parseEmptyPrintObjectStyleAlign(cursor, context)); cursor = nextElement(cursor); } else @@ -60,7 +61,7 @@ void parseSystemDividersContent(SystemDividers &out, pugi::xml_node el) } if (cursorIs(cursor, "right-divider")) { - out.setRightDivider(parseEmptyPrintObjectStyleAlign(cursor)); + out.setRightDivider(parseEmptyPrintObjectStyleAlign(cursor, context)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/SystemDividers.h b/src/private/mx/core/generated/SystemDividers.h index e2359efea..bad300973 100644 --- a/src/private/mx/core/generated/SystemDividers.h +++ b/src/private/mx/core/generated/SystemDividers.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The system-dividers element indicates the presence or absence of system dividers (also known as /// system separation marks) between systems displayed on the same page. Dividers on the left and /// right side of the page are controlled by the left-divider and right-divider elements @@ -40,9 +42,9 @@ class SystemDividers final EmptyPrintObjectStyleAlign m_rightDivider{}; }; -SystemDividers parseSystemDividers(pugi::xml_node el); +SystemDividers parseSystemDividers(pugi::xml_node el, const ParseContext &context); -void parseSystemDividersContent(SystemDividers &out, pugi::xml_node el); +void parseSystemDividersContent(SystemDividers &out, pugi::xml_node el, const ParseContext &context); void serializeSystemDividers(const SystemDividers &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/SystemLayout.cpp b/src/private/mx/core/generated/SystemLayout.cpp index 2718e9c6a..859919c36 100644 --- a/src/private/mx/core/generated/SystemLayout.cpp +++ b/src/private/mx/core/generated/SystemLayout.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/SystemLayout.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,7 +51,7 @@ void SystemLayout::setSystemDividers(std::optional value) m_systemDividers = std::move(value); } -SystemLayout parseSystemLayout(pugi::xml_node el) +SystemLayout parseSystemLayout(pugi::xml_node el, const ParseContext &context) { SystemLayout out; for (pugi::xml_attribute a : el.attributes()) @@ -62,31 +63,31 @@ SystemLayout parseSystemLayout(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseSystemLayoutContent(out, el); + parseSystemLayoutContent(out, el, context); return out; } -void parseSystemLayoutContent(SystemLayout &out, pugi::xml_node el) +void parseSystemLayoutContent(SystemLayout &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "system-margins")) { - out.setSystemMargins(parseSystemMargins(cursor)); + out.setSystemMargins(parseSystemMargins(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "system-distance")) { - out.setSystemDistance(Tenths::parse(childText(cursor))); + out.setSystemDistance(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "top-system-distance")) { - out.setTopSystemDistance(Tenths::parse(childText(cursor))); + out.setTopSystemDistance(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "system-dividers")) { - out.setSystemDividers(parseSystemDividers(cursor)); + out.setSystemDividers(parseSystemDividers(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/SystemLayout.h b/src/private/mx/core/generated/SystemLayout.h index 0cd996814..a949bfc9f 100644 --- a/src/private/mx/core/generated/SystemLayout.h +++ b/src/private/mx/core/generated/SystemLayout.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A system is a group of staves that are read and played simultaneously. System layout includes /// left and right margins and the vertical distance from the previous system. The system distance is /// measured from the bottom line of the previous system to the top line of the current system. It is @@ -58,9 +60,9 @@ class SystemLayout final std::optional m_systemDividers; }; -SystemLayout parseSystemLayout(pugi::xml_node el); +SystemLayout parseSystemLayout(pugi::xml_node el, const ParseContext &context); -void parseSystemLayoutContent(SystemLayout &out, pugi::xml_node el); +void parseSystemLayoutContent(SystemLayout &out, pugi::xml_node el, const ParseContext &context); void serializeSystemLayout(const SystemLayout &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/SystemMargins.cpp b/src/private/mx/core/generated/SystemMargins.cpp index cc8298584..7e6d12716 100644 --- a/src/private/mx/core/generated/SystemMargins.cpp +++ b/src/private/mx/core/generated/SystemMargins.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/SystemMargins.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -20,7 +21,7 @@ void SystemMargins::setLeftRightMargins(LeftRightMarginsGroup value) m_leftRightMargins = std::move(value); } -SystemMargins parseSystemMargins(pugi::xml_node el) +SystemMargins parseSystemMargins(pugi::xml_node el, const ParseContext &context) { SystemMargins out; for (pugi::xml_attribute a : el.attributes()) @@ -32,16 +33,16 @@ SystemMargins parseSystemMargins(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseSystemMarginsContent(out, el); + parseSystemMarginsContent(out, el, context); return out; } -void parseSystemMarginsContent(SystemMargins &out, pugi::xml_node el) +void parseSystemMarginsContent(SystemMargins &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "left-margin"))) { - out.setLeftRightMargins(parseLeftRightMarginsGroup(el, cursor)); + out.setLeftRightMargins(parseLeftRightMarginsGroup(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/SystemMargins.h b/src/private/mx/core/generated/SystemMargins.h index 0c0a04fb7..480e75ff6 100644 --- a/src/private/mx/core/generated/SystemMargins.h +++ b/src/private/mx/core/generated/SystemMargins.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// System margins are relative to the page margins. Positive values indent and negative values /// reduce the margin size. /// Content fields mirror the schema grammar in declaration order; the @@ -32,9 +34,9 @@ class SystemMargins final LeftRightMarginsGroup m_leftRightMargins{}; }; -SystemMargins parseSystemMargins(pugi::xml_node el); +SystemMargins parseSystemMargins(pugi::xml_node el, const ParseContext &context); -void parseSystemMarginsContent(SystemMargins &out, pugi::xml_node el); +void parseSystemMarginsContent(SystemMargins &out, pugi::xml_node el, const ParseContext &context); void serializeSystemMargins(const SystemMargins &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/SystemRelation.cpp b/src/private/mx/core/generated/SystemRelation.cpp index ed7c0b9e1..fe1c94d40 100644 --- a/src/private/mx/core/generated/SystemRelation.cpp +++ b/src/private/mx/core/generated/SystemRelation.cpp @@ -48,9 +48,15 @@ bool SystemRelation::tryParse(std::string_view text, SystemRelation &out) noexce } SystemRelation SystemRelation::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +SystemRelation SystemRelation::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { SystemRelation v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/SystemRelation.h b/src/private/mx/core/generated/SystemRelation.h index d1db3861c..4e6cf04d3 100644 --- a/src/private/mx/core/generated/SystemRelation.h +++ b/src/private/mx/core/generated/SystemRelation.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -48,6 +50,9 @@ class SystemRelation final /// (the import leniency policy; never produces an invalid value). static SystemRelation parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static SystemRelation parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const SystemRelation &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/SystemRelationNumber.cpp b/src/private/mx/core/generated/SystemRelationNumber.cpp index 0b6beb5d3..93f0abad4 100644 --- a/src/private/mx/core/generated/SystemRelationNumber.cpp +++ b/src/private/mx/core/generated/SystemRelationNumber.cpp @@ -56,9 +56,15 @@ bool SystemRelationNumber::tryParse(std::string_view text, SystemRelationNumber } SystemRelationNumber SystemRelationNumber::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +SystemRelationNumber SystemRelationNumber::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { SystemRelationNumber v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/SystemRelationNumber.h b/src/private/mx/core/generated/SystemRelationNumber.h index c9ba73a69..90deec1a6 100644 --- a/src/private/mx/core/generated/SystemRelationNumber.h +++ b/src/private/mx/core/generated/SystemRelationNumber.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -53,6 +55,9 @@ class SystemRelationNumber final /// (the import leniency policy; never produces an invalid value). static SystemRelationNumber parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static SystemRelationNumber parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const SystemRelationNumber &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Tap.cpp b/src/private/mx/core/generated/Tap.cpp index a17ab2834..d12348a0c 100644 --- a/src/private/mx/core/generated/Tap.cpp +++ b/src/private/mx/core/generated/Tap.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Tap.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -130,7 +131,7 @@ void Tap::setValue(std::string value) m_value = std::move(value); } -Tap parseTap(pugi::xml_node el) +Tap parseTap(pugi::xml_node el, const ParseContext &context) { Tap out; for (pugi::xml_attribute a : el.attributes()) @@ -142,58 +143,58 @@ Tap parseTap(pugi::xml_node el) } if (aname == "hand") { - out.setHand(TapHand::parse(a.value())); + out.setHand(parseValue(a.value(), context, el, "hand")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else { throwUnknownAttribute(el, a.name()); } } - parseTapContent(out, el); + parseTapContent(out, el, context); return out; } -void parseTapContent(Tap &out, pugi::xml_node el) +void parseTapContent(Tap &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/Tap.h b/src/private/mx/core/generated/Tap.h index c0de7d951..60ec81543 100644 --- a/src/private/mx/core/generated/Tap.h +++ b/src/private/mx/core/generated/Tap.h @@ -22,6 +22,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The tap type indicates a tap on the fretboard. The text content allows specification of the /// notation; + and T are common choices. If the element is empty, the hand attribute is used to /// specify the symbol to use. The hand attribute is ignored if the tap glyph is already specified by @@ -71,9 +73,9 @@ class Tap final std::string m_value{}; }; -Tap parseTap(pugi::xml_node el); +Tap parseTap(pugi::xml_node el, const ParseContext &context); -void parseTapContent(Tap &out, pugi::xml_node el); +void parseTapContent(Tap &out, pugi::xml_node el, const ParseContext &context); void serializeTap(const Tap &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/TapHand.cpp b/src/private/mx/core/generated/TapHand.cpp index af037e044..d8a9beafc 100644 --- a/src/private/mx/core/generated/TapHand.cpp +++ b/src/private/mx/core/generated/TapHand.cpp @@ -42,9 +42,15 @@ bool TapHand::tryParse(std::string_view text, TapHand &out) noexcept } TapHand TapHand::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +TapHand TapHand::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { TapHand v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/TapHand.h b/src/private/mx/core/generated/TapHand.h index 7226fc015..ac31f061b 100644 --- a/src/private/mx/core/generated/TapHand.h +++ b/src/private/mx/core/generated/TapHand.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -41,6 +43,9 @@ class TapHand final /// (the import leniency policy; never produces an invalid value). static TapHand parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static TapHand parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const TapHand &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Technical.cpp b/src/private/mx/core/generated/Technical.cpp index bc09a017a..930a36209 100644 --- a/src/private/mx/core/generated/Technical.cpp +++ b/src/private/mx/core/generated/Technical.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Technical.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,7 +36,7 @@ void Technical::setChoice(std::vector value) m_choice = std::move(value); } -Technical parseTechnical(pugi::xml_node el) +Technical parseTechnical(pugi::xml_node el, const ParseContext &context) { Technical out; for (pugi::xml_attribute a : el.attributes()) @@ -47,18 +48,18 @@ Technical parseTechnical(pugi::xml_node el) } if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseTechnicalContent(out, el); + parseTechnicalContent(out, el, context); return out; } -void parseTechnicalContent(Technical &out, pugi::xml_node el) +void parseTechnicalContent(Technical &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); while (cursor && @@ -74,7 +75,7 @@ void parseTechnicalContent(Technical &out, pugi::xml_node el) cursorIs(cursor, "half-muted") || cursorIs(cursor, "harmon-mute") || cursorIs(cursor, "golpe") || cursorIs(cursor, "other-technical"))) { - out.addChoice(parseTechnicalChoice(el, cursor)); + out.addChoice(parseTechnicalChoice(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/Technical.h b/src/private/mx/core/generated/Technical.h index 7e78c3db6..ca6bd5bcf 100644 --- a/src/private/mx/core/generated/Technical.h +++ b/src/private/mx/core/generated/Technical.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Technical indications give performance information for individual instruments. /// Content fields mirror the schema grammar in declaration order; the /// serializer walks them, so a wrong order is unrepresentable (plan §2.3). @@ -37,9 +39,9 @@ class Technical final std::vector m_choice; }; -Technical parseTechnical(pugi::xml_node el); +Technical parseTechnical(pugi::xml_node el, const ParseContext &context); -void parseTechnicalContent(Technical &out, pugi::xml_node el); +void parseTechnicalContent(Technical &out, pugi::xml_node el, const ParseContext &context); void serializeTechnical(const Technical &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/TechnicalChoice.cpp b/src/private/mx/core/generated/TechnicalChoice.cpp index 6091fd261..7141bcb21 100644 --- a/src/private/mx/core/generated/TechnicalChoice.cpp +++ b/src/private/mx/core/generated/TechnicalChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/TechnicalChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -170,191 +171,191 @@ TechnicalChoice TechnicalChoice::otherTechnical(OtherPlacementText value) return TechnicalChoice{Storage{std::in_place_index<30>, std::move(value)}}; } -TechnicalChoice parseTechnicalChoice(pugi::xml_node el, pugi::xml_node &cursor) +TechnicalChoice parseTechnicalChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "up-bow"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::upBow(std::move(value)); } if (cursor && (cursorIs(cursor, "down-bow"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::downBow(std::move(value)); } if (cursor && (cursorIs(cursor, "harmonic"))) { - Harmonic value = parseHarmonic(cursor); + Harmonic value = parseHarmonic(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::harmonic(std::move(value)); } if (cursor && (cursorIs(cursor, "open-string"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::openString(std::move(value)); } if (cursor && (cursorIs(cursor, "thumb-position"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::thumbPosition(std::move(value)); } if (cursor && (cursorIs(cursor, "fingering"))) { - Fingering value = parseFingering(cursor); + Fingering value = parseFingering(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::fingering(std::move(value)); } if (cursor && (cursorIs(cursor, "pluck"))) { - PlacementText value = parsePlacementText(cursor); + PlacementText value = parsePlacementText(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::pluck(std::move(value)); } if (cursor && (cursorIs(cursor, "double-tongue"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::doubleTongue(std::move(value)); } if (cursor && (cursorIs(cursor, "triple-tongue"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::tripleTongue(std::move(value)); } if (cursor && (cursorIs(cursor, "stopped"))) { - EmptyPlacementSmufl value = parseEmptyPlacementSmufl(cursor); + EmptyPlacementSmufl value = parseEmptyPlacementSmufl(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::stopped(std::move(value)); } if (cursor && (cursorIs(cursor, "snap-pizzicato"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::snapPizzicato(std::move(value)); } if (cursor && (cursorIs(cursor, "fret"))) { - Fret value = parseFret(cursor); + Fret value = parseFret(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::fret(std::move(value)); } if (cursor && (cursorIs(cursor, "string"))) { - String value = parseString(cursor); + String value = parseString(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::string(std::move(value)); } if (cursor && (cursorIs(cursor, "hammer-on"))) { - HammerOnPullOff value = parseHammerOnPullOff(cursor); + HammerOnPullOff value = parseHammerOnPullOff(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::hammerOn(std::move(value)); } if (cursor && (cursorIs(cursor, "pull-off"))) { - HammerOnPullOff value = parseHammerOnPullOff(cursor); + HammerOnPullOff value = parseHammerOnPullOff(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::pullOff(std::move(value)); } if (cursor && (cursorIs(cursor, "bend"))) { - Bend value = parseBend(cursor); + Bend value = parseBend(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::bend(std::move(value)); } if (cursor && (cursorIs(cursor, "tap"))) { - Tap value = parseTap(cursor); + Tap value = parseTap(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::tap(std::move(value)); } if (cursor && (cursorIs(cursor, "heel"))) { - HeelToe value = parseHeelToe(cursor); + HeelToe value = parseHeelToe(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::heel(std::move(value)); } if (cursor && (cursorIs(cursor, "toe"))) { - HeelToe value = parseHeelToe(cursor); + HeelToe value = parseHeelToe(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::toe(std::move(value)); } if (cursor && (cursorIs(cursor, "fingernails"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::fingernails(std::move(value)); } if (cursor && (cursorIs(cursor, "hole"))) { - Hole value = parseHole(cursor); + Hole value = parseHole(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::hole(std::move(value)); } if (cursor && (cursorIs(cursor, "arrow"))) { - Arrow value = parseArrow(cursor); + Arrow value = parseArrow(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::arrow(std::move(value)); } if (cursor && (cursorIs(cursor, "handbell"))) { - Handbell value = parseHandbell(cursor); + Handbell value = parseHandbell(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::handbell(std::move(value)); } if (cursor && (cursorIs(cursor, "brass-bend"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::brassBend(std::move(value)); } if (cursor && (cursorIs(cursor, "flip"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::flip(std::move(value)); } if (cursor && (cursorIs(cursor, "smear"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::smear(std::move(value)); } if (cursor && (cursorIs(cursor, "open"))) { - EmptyPlacementSmufl value = parseEmptyPlacementSmufl(cursor); + EmptyPlacementSmufl value = parseEmptyPlacementSmufl(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::open(std::move(value)); } if (cursor && (cursorIs(cursor, "half-muted"))) { - EmptyPlacementSmufl value = parseEmptyPlacementSmufl(cursor); + EmptyPlacementSmufl value = parseEmptyPlacementSmufl(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::halfMuted(std::move(value)); } if (cursor && (cursorIs(cursor, "harmon-mute"))) { - HarmonMute value = parseHarmonMute(cursor); + HarmonMute value = parseHarmonMute(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::harmonMute(std::move(value)); } if (cursor && (cursorIs(cursor, "golpe"))) { - EmptyPlacement value = parseEmptyPlacement(cursor); + EmptyPlacement value = parseEmptyPlacement(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::golpe(std::move(value)); } if (cursor && (cursorIs(cursor, "other-technical"))) { - OtherPlacementText value = parseOtherPlacementText(cursor); + OtherPlacementText value = parseOtherPlacementText(cursor, context); cursor = nextElement(cursor); return TechnicalChoice::otherTechnical(std::move(value)); } diff --git a/src/private/mx/core/generated/TechnicalChoice.h b/src/private/mx/core/generated/TechnicalChoice.h index 62ff6d1a2..431f0f472 100644 --- a/src/private/mx/core/generated/TechnicalChoice.h +++ b/src/private/mx/core/generated/TechnicalChoice.h @@ -32,6 +32,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -505,7 +507,7 @@ class TechnicalChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -TechnicalChoice parseTechnicalChoice(pugi::xml_node el, pugi::xml_node &cursor); +TechnicalChoice parseTechnicalChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeTechnicalChoice(const TechnicalChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Tenths.cpp b/src/private/mx/core/generated/Tenths.cpp index aedf464d3..ccedb1e45 100644 --- a/src/private/mx/core/generated/Tenths.cpp +++ b/src/private/mx/core/generated/Tenths.cpp @@ -34,20 +34,33 @@ std::string Tenths::toString() const bool Tenths::tryParse(std::string_view text, Tenths &out) { - Decimal v; - if (!Decimal::tryParse(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + Tenths parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = Tenths{std::move(v)}; + out = std::move(parsed); return true; } Tenths Tenths::parse(std::string_view text) { - Tenths v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +Tenths Tenths::parse(std::string_view text, ValueParseOutcome &outcome) +{ + Decimal v; + if (!Decimal::tryParse(text, v)) + { + outcome = ValueParseOutcome::invalid; + return Tenths{}; + } + Tenths out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/Tenths.h b/src/private/mx/core/generated/Tenths.h index 021a7d6a7..258b5f246 100644 --- a/src/private/mx/core/generated/Tenths.h +++ b/src/private/mx/core/generated/Tenths.h @@ -3,6 +3,7 @@ #pragma once #include "mx/core/Decimal.h" +#include "mx/core/Lexical.h" #include #include @@ -44,6 +45,9 @@ class Tenths final /// Lenient: non-numeric text yields the clamped zero. static Tenths parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static Tenths parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const Tenths &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/TextDirection.cpp b/src/private/mx/core/generated/TextDirection.cpp index d3a342904..b059cd1fc 100644 --- a/src/private/mx/core/generated/TextDirection.cpp +++ b/src/private/mx/core/generated/TextDirection.cpp @@ -54,9 +54,15 @@ bool TextDirection::tryParse(std::string_view text, TextDirection &out) noexcept } TextDirection TextDirection::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +TextDirection TextDirection::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { TextDirection v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/TextDirection.h b/src/private/mx/core/generated/TextDirection.h index 409c62f11..ccbd5c03c 100644 --- a/src/private/mx/core/generated/TextDirection.h +++ b/src/private/mx/core/generated/TextDirection.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -50,6 +52,9 @@ class TextDirection final /// (the import leniency policy; never produces an invalid value). static TextDirection parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static TextDirection parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const TextDirection &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/TextElementData.cpp b/src/private/mx/core/generated/TextElementData.cpp index 835e69065..38b6b754f 100644 --- a/src/private/mx/core/generated/TextElementData.cpp +++ b/src/private/mx/core/generated/TextElementData.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/TextElementData.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -140,7 +141,7 @@ void TextElementData::setValue(std::string value) m_value = std::move(value); } -TextElementData parseTextElementData(pugi::xml_node el) +TextElementData parseTextElementData(pugi::xml_node el, const ParseContext &context) { TextElementData out; for (pugi::xml_attribute a : el.attributes()) @@ -156,58 +157,58 @@ TextElementData parseTextElementData(pugi::xml_node el) } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "underline") { - out.setUnderline(NumberOfLines::parse(a.value())); + out.setUnderline(parseValue(a.value(), context, el, "underline")); } else if (aname == "overline") { - out.setOverline(NumberOfLines::parse(a.value())); + out.setOverline(parseValue(a.value(), context, el, "overline")); } else if (aname == "line-through") { - out.setLineThrough(NumberOfLines::parse(a.value())); + out.setLineThrough(parseValue(a.value(), context, el, "line-through")); } else if (aname == "rotation") { - out.setRotation(RotationDegrees::parse(a.value())); + out.setRotation(parseValue(a.value(), context, el, "rotation")); } else if (aname == "letter-spacing") { - out.setLetterSpacing(NumberOrNormal::parse(a.value())); + out.setLetterSpacing(parseValue(a.value(), context, el, "letter-spacing")); } else if (aname == "dir") { - out.setDir(TextDirection::parse(a.value())); + out.setDir(parseValue(a.value(), context, el, "dir")); } else { throwUnknownAttribute(el, a.name()); } } - parseTextElementDataContent(out, el); + parseTextElementDataContent(out, el, context); return out; } -void parseTextElementDataContent(TextElementData &out, pugi::xml_node el) +void parseTextElementDataContent(TextElementData &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/TextElementData.h b/src/private/mx/core/generated/TextElementData.h index aeb7749d1..1d5bd6f1c 100644 --- a/src/private/mx/core/generated/TextElementData.h +++ b/src/private/mx/core/generated/TextElementData.h @@ -23,6 +23,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The text-element-data type represents a syllable or portion of a syllable for lyric text /// underlay. A hyphen in the string content should only be used for an actual hyphenated word. /// Language names for text elements come from ISO 639, with optional country subcodes from ISO 3166. @@ -73,9 +75,9 @@ class TextElementData final std::string m_value{}; }; -TextElementData parseTextElementData(pugi::xml_node el); +TextElementData parseTextElementData(pugi::xml_node el, const ParseContext &context); -void parseTextElementDataContent(TextElementData &out, pugi::xml_node el); +void parseTextElementDataContent(TextElementData &out, pugi::xml_node el, const ParseContext &context); void serializeTextElementData(const TextElementData &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Tie.cpp b/src/private/mx/core/generated/Tie.cpp index f53da18b0..3b5842422 100644 --- a/src/private/mx/core/generated/Tie.cpp +++ b/src/private/mx/core/generated/Tie.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Tie.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Tie::setTimeOnly(std::optional value) m_timeOnly = std::move(value); } -Tie parseTie(pugi::xml_node el) +Tie parseTie(pugi::xml_node el, const ParseContext &context) { Tie out; bool seen_type = false; @@ -44,11 +45,11 @@ Tie parseTie(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStop::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "time-only") { - out.setTimeOnly(TimeOnly::parse(a.value())); + out.setTimeOnly(parseValue(a.value(), context, el, "time-only")); } else { @@ -59,13 +60,14 @@ Tie parseTie(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseTieContent(out, el); + parseTieContent(out, el, context); return out; } -void parseTieContent(Tie &out, pugi::xml_node el) +void parseTieContent(Tie &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Tie.h b/src/private/mx/core/generated/Tie.h index 9bee94035..1b1a94344 100644 --- a/src/private/mx/core/generated/Tie.h +++ b/src/private/mx/core/generated/Tie.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The tie element indicates that a tie begins or ends with this note. If the tie element applies /// only particular times through a repeat, the time-only attribute indicates which times to apply /// it. The tie element indicates sound; the tied element indicates notation. @@ -33,9 +35,9 @@ class Tie final std::optional m_timeOnly; }; -Tie parseTie(pugi::xml_node el); +Tie parseTie(pugi::xml_node el, const ParseContext &context); -void parseTieContent(Tie &out, pugi::xml_node el); +void parseTieContent(Tie &out, pugi::xml_node el, const ParseContext &context); void serializeTie(const Tie &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Tied.cpp b/src/private/mx/core/generated/Tied.cpp index 855344c53..7e300b3c6 100644 --- a/src/private/mx/core/generated/Tied.cpp +++ b/src/private/mx/core/generated/Tied.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Tied.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -200,7 +201,7 @@ void Tied::setID(std::optional value) m_id = std::move(value); } -Tied parseTied(pugi::xml_node el) +Tied parseTied(pugi::xml_node el, const ParseContext &context) { Tied out; bool seen_type = false; @@ -214,79 +215,79 @@ Tied parseTied(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(TiedType::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "number") { - out.setNumber(NumberLevel::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "line-type") { - out.setLineType(LineType::parse(a.value())); + out.setLineType(parseValue(a.value(), context, el, "line-type")); } else if (aname == "dash-length") { - out.setDashLength(Tenths::parse(a.value())); + out.setDashLength(parseValue(a.value(), context, el, "dash-length")); } else if (aname == "space-length") { - out.setSpaceLength(Tenths::parse(a.value())); + out.setSpaceLength(parseValue(a.value(), context, el, "space-length")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "orientation") { - out.setOrientation(OverUnder::parse(a.value())); + out.setOrientation(parseValue(a.value(), context, el, "orientation")); } else if (aname == "bezier-x") { - out.setBezierX(Tenths::parse(a.value())); + out.setBezierX(parseValue(a.value(), context, el, "bezier-x")); } else if (aname == "bezier-y") { - out.setBezierY(Tenths::parse(a.value())); + out.setBezierY(parseValue(a.value(), context, el, "bezier-y")); } else if (aname == "bezier-x2") { - out.setBezierX2(Tenths::parse(a.value())); + out.setBezierX2(parseValue(a.value(), context, el, "bezier-x2")); } else if (aname == "bezier-y2") { - out.setBezierY2(Tenths::parse(a.value())); + out.setBezierY2(parseValue(a.value(), context, el, "bezier-y2")); } else if (aname == "bezier-offset") { - out.setBezierOffset(Divisions::parse(a.value())); + out.setBezierOffset(parseValue(a.value(), context, el, "bezier-offset")); } else if (aname == "bezier-offset2") { - out.setBezierOffset2(Divisions::parse(a.value())); + out.setBezierOffset2(parseValue(a.value(), context, el, "bezier-offset2")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -297,13 +298,14 @@ Tied parseTied(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseTiedContent(out, el); + parseTiedContent(out, el, context); return out; } -void parseTiedContent(Tied &out, pugi::xml_node el) +void parseTiedContent(Tied &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Tied.h b/src/private/mx/core/generated/Tied.h index e73ef0778..63981ad11 100644 --- a/src/private/mx/core/generated/Tied.h +++ b/src/private/mx/core/generated/Tied.h @@ -24,6 +24,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The tied element represents the notated tie. The tie element represents the tie sound. The number /// attribute is rarely needed to disambiguate ties, since note pitches will usually suffice. The /// attribute is implied rather than defaulting to 1 as with most elements. It is available for use @@ -101,9 +103,9 @@ class Tied final std::optional m_id; }; -Tied parseTied(pugi::xml_node el); +Tied parseTied(pugi::xml_node el, const ParseContext &context); -void parseTiedContent(Tied &out, pugi::xml_node el); +void parseTiedContent(Tied &out, pugi::xml_node el, const ParseContext &context); void serializeTied(const Tied &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/TiedType.cpp b/src/private/mx/core/generated/TiedType.cpp index 08a915432..eab35c88d 100644 --- a/src/private/mx/core/generated/TiedType.cpp +++ b/src/private/mx/core/generated/TiedType.cpp @@ -54,9 +54,15 @@ bool TiedType::tryParse(std::string_view text, TiedType &out) noexcept } TiedType TiedType::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +TiedType TiedType::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { TiedType v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/TiedType.h b/src/private/mx/core/generated/TiedType.h index 19b7f765b..243bed9d5 100644 --- a/src/private/mx/core/generated/TiedType.h +++ b/src/private/mx/core/generated/TiedType.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -54,6 +56,9 @@ class TiedType final /// (the import leniency policy; never produces an invalid value). static TiedType parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static TiedType parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const TiedType &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Time.cpp b/src/private/mx/core/generated/Time.cpp index de4ab4832..56d10bb47 100644 --- a/src/private/mx/core/generated/Time.cpp +++ b/src/private/mx/core/generated/Time.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Time.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -180,7 +181,7 @@ void Time::setChoice(TimeChoice value) m_choice = std::move(value); } -Time parseTime(pugi::xml_node el) +Time parseTime(pugi::xml_node el, const ParseContext &context) { Time out; for (pugi::xml_attribute a : el.attributes()) @@ -192,83 +193,83 @@ Time parseTime(pugi::xml_node el) } if (aname == "number") { - out.setNumber(StaffNumber::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "symbol") { - out.setSymbol(TimeSymbol::parse(a.value())); + out.setSymbol(parseValue(a.value(), context, el, "symbol")); } else if (aname == "separator") { - out.setSeparator(TimeSeparator::parse(a.value())); + out.setSeparator(parseValue(a.value(), context, el, "separator")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "halign") { - out.setHalign(LeftCenterRight::parse(a.value())); + out.setHalign(parseValue(a.value(), context, el, "halign")); } else if (aname == "valign") { - out.setValign(Valign::parse(a.value())); + out.setValign(parseValue(a.value(), context, el, "valign")); } else if (aname == "print-object") { - out.setPrintObject(YesNo::parse(a.value())); + out.setPrintObject(parseValue(a.value(), context, el, "print-object")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseTimeContent(out, el); + parseTimeContent(out, el, context); return out; } -void parseTimeContent(Time &out, pugi::xml_node el) +void parseTimeContent(Time &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "beats") || cursorIs(cursor, "senza-misura"))) { - out.setChoice(parseTimeChoice(el, cursor)); + out.setChoice(parseTimeChoice(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/Time.h b/src/private/mx/core/generated/Time.h index 5c8f9de63..cf479d025 100644 --- a/src/private/mx/core/generated/Time.h +++ b/src/private/mx/core/generated/Time.h @@ -31,6 +31,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Time signatures are represented by the beats element for the numerator and the beat-type element /// for the denominator. The symbol attribute is used to indicate common and cut time symbols as well /// as a single number display. Multiple pairs of beat and beat-type elements are used for composite @@ -100,9 +102,9 @@ class Time final TimeChoice m_choice{}; }; -Time parseTime(pugi::xml_node el); +Time parseTime(pugi::xml_node el, const ParseContext &context); -void parseTimeContent(Time &out, pugi::xml_node el); +void parseTimeContent(Time &out, pugi::xml_node el, const ParseContext &context); void serializeTime(const Time &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/TimeChoice.cpp b/src/private/mx/core/generated/TimeChoice.cpp index 8a7bf5eb3..3ed14ffcf 100644 --- a/src/private/mx/core/generated/TimeChoice.cpp +++ b/src/private/mx/core/generated/TimeChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/TimeChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,11 +26,11 @@ TimeChoice TimeChoice::senzaMisura(std::string value) return TimeChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -TimeChoice parseTimeChoice(pugi::xml_node el, pugi::xml_node &cursor) +TimeChoice parseTimeChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { if (cursor && (cursorIs(cursor, "beats"))) { - return TimeChoice::group(parseTimeChoiceGroup(el, cursor)); + return TimeChoice::group(parseTimeChoiceGroup(el, cursor, context)); } if (cursor && (cursorIs(cursor, "senza-misura"))) { diff --git a/src/private/mx/core/generated/TimeChoice.h b/src/private/mx/core/generated/TimeChoice.h index 1fe03fc95..31e6934e4 100644 --- a/src/private/mx/core/generated/TimeChoice.h +++ b/src/private/mx/core/generated/TimeChoice.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -79,7 +81,7 @@ class TimeChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -TimeChoice parseTimeChoice(pugi::xml_node el, pugi::xml_node &cursor); +TimeChoice parseTimeChoice(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeTimeChoice(const TimeChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/TimeChoiceGroup.cpp b/src/private/mx/core/generated/TimeChoiceGroup.cpp index 50029a2ac..22a7ead11 100644 --- a/src/private/mx/core/generated/TimeChoiceGroup.cpp +++ b/src/private/mx/core/generated/TimeChoiceGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/TimeChoiceGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,21 +36,21 @@ void TimeChoiceGroup::setInterchangeable(std::optional value) m_interchangeable = std::move(value); } -TimeChoiceGroup parseTimeChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor) +TimeChoiceGroup parseTimeChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { TimeChoiceGroup out; if (!(cursor && (cursorIs(cursor, "beats")))) { throwMissingElement(el, "beats"); } - out.setTimeSignature(OneOrMore{parseTimeSignatureGroup(el, cursor)}); + out.setTimeSignature(OneOrMore{parseTimeSignatureGroup(el, cursor, context)}); while (cursor && (cursorIs(cursor, "beats"))) { - out.addTimeSignature(parseTimeSignatureGroup(el, cursor)); + out.addTimeSignature(parseTimeSignatureGroup(el, cursor, context)); } if (cursorIs(cursor, "interchangeable")) { - out.setInterchangeable(parseInterchangeable(cursor)); + out.setInterchangeable(parseInterchangeable(cursor, context)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/TimeChoiceGroup.h b/src/private/mx/core/generated/TimeChoiceGroup.h index e570ce8b8..713b85b0f 100644 --- a/src/private/mx/core/generated/TimeChoiceGroup.h +++ b/src/private/mx/core/generated/TimeChoiceGroup.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -39,7 +41,7 @@ class TimeChoiceGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -TimeChoiceGroup parseTimeChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor); +TimeChoiceGroup parseTimeChoiceGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeTimeChoiceGroup(const TimeChoiceGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/TimeModification.cpp b/src/private/mx/core/generated/TimeModification.cpp index da79cf974..85b5e32c6 100644 --- a/src/private/mx/core/generated/TimeModification.cpp +++ b/src/private/mx/core/generated/TimeModification.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/TimeModification.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,7 +41,7 @@ void TimeModification::setGroup(std::optional value) m_group = std::move(value); } -TimeModification parseTimeModification(pugi::xml_node el) +TimeModification parseTimeModification(pugi::xml_node el, const ParseContext &context) { TimeModification out; for (pugi::xml_attribute a : el.attributes()) @@ -52,16 +53,16 @@ TimeModification parseTimeModification(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseTimeModificationContent(out, el); + parseTimeModificationContent(out, el, context); return out; } -void parseTimeModificationContent(TimeModification &out, pugi::xml_node el) +void parseTimeModificationContent(TimeModification &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "actual-notes")) { - out.setActualNotes(parseInt(childText(cursor))); + out.setActualNotes(parseIntegerValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -70,7 +71,7 @@ void parseTimeModificationContent(TimeModification &out, pugi::xml_node el) } if (cursorIs(cursor, "normal-notes")) { - out.setNormalNotes(parseInt(childText(cursor))); + out.setNormalNotes(parseIntegerValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -79,7 +80,7 @@ void parseTimeModificationContent(TimeModification &out, pugi::xml_node el) } if (cursor && (cursorIs(cursor, "normal-type"))) { - out.setGroup(parseTimeModificationGroup(el, cursor)); + out.setGroup(parseTimeModificationGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/TimeModification.h b/src/private/mx/core/generated/TimeModification.h index 861aa87c9..0d3f301af 100644 --- a/src/private/mx/core/generated/TimeModification.h +++ b/src/private/mx/core/generated/TimeModification.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Time modification indicates tuplets, double-note tremolos, and other durational changes. A /// time-modification element shows how the cumulative, sounding effect of tuplets and double-note /// tremolos compare to the written note type represented by the type and dot elements. Nested @@ -41,9 +43,9 @@ class TimeModification std::optional m_group; }; -TimeModification parseTimeModification(pugi::xml_node el); +TimeModification parseTimeModification(pugi::xml_node el, const ParseContext &context); -void parseTimeModificationContent(TimeModification &out, pugi::xml_node el); +void parseTimeModificationContent(TimeModification &out, pugi::xml_node el, const ParseContext &context); void serializeTimeModification(const TimeModification &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/TimeModificationGroup.cpp b/src/private/mx/core/generated/TimeModificationGroup.cpp index 5be39f2e4..4af04c3f2 100644 --- a/src/private/mx/core/generated/TimeModificationGroup.cpp +++ b/src/private/mx/core/generated/TimeModificationGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/TimeModificationGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,12 +36,12 @@ void TimeModificationGroup::setNormalDot(std::vector value) m_normalDot = std::move(value); } -TimeModificationGroup parseTimeModificationGroup(pugi::xml_node el, pugi::xml_node &cursor) +TimeModificationGroup parseTimeModificationGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { TimeModificationGroup out; if (cursorIs(cursor, "normal-type")) { - out.setNormalType(NoteTypeValue::parse(childText(cursor))); + out.setNormalType(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -49,7 +50,7 @@ TimeModificationGroup parseTimeModificationGroup(pugi::xml_node el, pugi::xml_no } while (cursorIs(cursor, "normal-dot")) { - out.addNormalDot(parseEmpty(cursor)); + out.addNormalDot(parseEmpty(cursor, context)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/TimeModificationGroup.h b/src/private/mx/core/generated/TimeModificationGroup.h index 274d1e3d8..69ac2e928 100644 --- a/src/private/mx/core/generated/TimeModificationGroup.h +++ b/src/private/mx/core/generated/TimeModificationGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A shared content group (synthesized from an anonymous /// particle of the schema): transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -38,7 +40,8 @@ class TimeModificationGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -TimeModificationGroup parseTimeModificationGroup(pugi::xml_node el, pugi::xml_node &cursor); +TimeModificationGroup parseTimeModificationGroup(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context); void serializeTimeModificationGroup(const TimeModificationGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/TimeOnly.cpp b/src/private/mx/core/generated/TimeOnly.cpp index 2e245e020..f9653deb9 100644 --- a/src/private/mx/core/generated/TimeOnly.cpp +++ b/src/private/mx/core/generated/TimeOnly.cpp @@ -103,4 +103,16 @@ TimeOnly TimeOnly::parse(std::string_view text) return out; } +TimeOnly TimeOnly::parse(std::string_view text, ValueParseOutcome &outcome) +{ + TimeOnly out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/TimeOnly.h b/src/private/mx/core/generated/TimeOnly.h index 9601a43cf..6e76662c4 100644 --- a/src/private/mx/core/generated/TimeOnly.h +++ b/src/private/mx/core/generated/TimeOnly.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include #include @@ -42,6 +44,9 @@ class TimeOnly final /// Lenient: repairs into the nearest valid list. static TimeOnly parse(std::string_view text); + /// Lenient, and says whether the text had to be repaired. + static TimeOnly parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const TimeOnly &other) const noexcept { return m_values == other.m_values; diff --git a/src/private/mx/core/generated/TimeRelation.cpp b/src/private/mx/core/generated/TimeRelation.cpp index 1f24a71bb..9338521f5 100644 --- a/src/private/mx/core/generated/TimeRelation.cpp +++ b/src/private/mx/core/generated/TimeRelation.cpp @@ -61,9 +61,15 @@ bool TimeRelation::tryParse(std::string_view text, TimeRelation &out) noexcept } TimeRelation TimeRelation::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +TimeRelation TimeRelation::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { TimeRelation v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/TimeRelation.h b/src/private/mx/core/generated/TimeRelation.h index 6e139049f..d28453571 100644 --- a/src/private/mx/core/generated/TimeRelation.h +++ b/src/private/mx/core/generated/TimeRelation.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -49,6 +51,9 @@ class TimeRelation final /// (the import leniency policy; never produces an invalid value). static TimeRelation parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static TimeRelation parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const TimeRelation &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/TimeSeparator.cpp b/src/private/mx/core/generated/TimeSeparator.cpp index 686fc0de0..56d070b9e 100644 --- a/src/private/mx/core/generated/TimeSeparator.cpp +++ b/src/private/mx/core/generated/TimeSeparator.cpp @@ -56,9 +56,15 @@ bool TimeSeparator::tryParse(std::string_view text, TimeSeparator &out) noexcept } TimeSeparator TimeSeparator::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +TimeSeparator TimeSeparator::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { TimeSeparator v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/TimeSeparator.h b/src/private/mx/core/generated/TimeSeparator.h index 235259add..0c465f050 100644 --- a/src/private/mx/core/generated/TimeSeparator.h +++ b/src/private/mx/core/generated/TimeSeparator.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -51,6 +53,9 @@ class TimeSeparator final /// (the import leniency policy; never produces an invalid value). static TimeSeparator parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static TimeSeparator parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const TimeSeparator &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/TimeSignatureGroup.cpp b/src/private/mx/core/generated/TimeSignatureGroup.cpp index a7439d74d..0633c6d45 100644 --- a/src/private/mx/core/generated/TimeSignatureGroup.cpp +++ b/src/private/mx/core/generated/TimeSignatureGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/TimeSignatureGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void TimeSignatureGroup::setBeatType(std::string value) m_beatType = std::move(value); } -TimeSignatureGroup parseTimeSignatureGroup(pugi::xml_node el, pugi::xml_node &cursor) +TimeSignatureGroup parseTimeSignatureGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { TimeSignatureGroup out; if (cursorIs(cursor, "beats")) diff --git a/src/private/mx/core/generated/TimeSignatureGroup.h b/src/private/mx/core/generated/TimeSignatureGroup.h index 3c8f8df4b..6a5d93919 100644 --- a/src/private/mx/core/generated/TimeSignatureGroup.h +++ b/src/private/mx/core/generated/TimeSignatureGroup.h @@ -15,6 +15,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Time signatures are represented by the beats element for the numerator and the beat-type element /// for the denominator. /// A shared content group: transparent on the wire, its @@ -35,7 +37,7 @@ class TimeSignatureGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -TimeSignatureGroup parseTimeSignatureGroup(pugi::xml_node el, pugi::xml_node &cursor); +TimeSignatureGroup parseTimeSignatureGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeTimeSignatureGroup(const TimeSignatureGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/TimeSymbol.cpp b/src/private/mx/core/generated/TimeSymbol.cpp index 186461b64..54c0e23a8 100644 --- a/src/private/mx/core/generated/TimeSymbol.cpp +++ b/src/private/mx/core/generated/TimeSymbol.cpp @@ -61,9 +61,15 @@ bool TimeSymbol::tryParse(std::string_view text, TimeSymbol &out) noexcept } TimeSymbol TimeSymbol::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +TimeSymbol TimeSymbol::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { TimeSymbol v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/TimeSymbol.h b/src/private/mx/core/generated/TimeSymbol.h index 0fa43656f..4b1874d27 100644 --- a/src/private/mx/core/generated/TimeSymbol.h +++ b/src/private/mx/core/generated/TimeSymbol.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -54,6 +56,9 @@ class TimeSymbol final /// (the import leniency policy; never produces an invalid value). static TimeSymbol parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static TimeSymbol parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const TimeSymbol &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/TimewiseMeasure.cpp b/src/private/mx/core/generated/TimewiseMeasure.cpp index ca1cc1977..57856e823 100644 --- a/src/private/mx/core/generated/TimewiseMeasure.cpp +++ b/src/private/mx/core/generated/TimewiseMeasure.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/TimewiseMeasure.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -85,7 +86,7 @@ void TimewiseMeasure::setPart(OneOrMore value) m_part = std::move(value); } -TimewiseMeasure parseTimewiseMeasure(pugi::xml_node el) +TimewiseMeasure parseTimewiseMeasure(pugi::xml_node el, const ParseContext &context) { TimewiseMeasure out; bool seen_number = false; @@ -103,23 +104,23 @@ TimewiseMeasure parseTimewiseMeasure(pugi::xml_node el) } else if (aname == "text") { - out.setText(MeasureText::parse(a.value())); + out.setText(parseValue(a.value(), context, el, "text")); } else if (aname == "implicit") { - out.setImplicit(YesNo::parse(a.value())); + out.setImplicit(parseValue(a.value(), context, el, "implicit")); } else if (aname == "non-controlling") { - out.setNonControlling(YesNo::parse(a.value())); + out.setNonControlling(parseValue(a.value(), context, el, "non-controlling")); } else if (aname == "width") { - out.setWidth(Tenths::parse(a.value())); + out.setWidth(parseValue(a.value(), context, el, "width")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -130,22 +131,22 @@ TimewiseMeasure parseTimewiseMeasure(pugi::xml_node el) { throwMissingAttribute(el, "number"); } - parseTimewiseMeasureContent(out, el); + parseTimewiseMeasureContent(out, el, context); return out; } -void parseTimewiseMeasureContent(TimewiseMeasure &out, pugi::xml_node el) +void parseTimewiseMeasureContent(TimewiseMeasure &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (!cursorIs(cursor, "part")) { throwMissingOrMisplaced(el, cursor, "part"); } - out.setPart(OneOrMore{parseTimewisePart(cursor)}); + out.setPart(OneOrMore{parseTimewisePart(cursor, context)}); cursor = nextElement(cursor); while (cursorIs(cursor, "part")) { - out.addPart(parseTimewisePart(cursor)); + out.addPart(parseTimewisePart(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/TimewiseMeasure.h b/src/private/mx/core/generated/TimewiseMeasure.h index db51faf3e..6e6a3ebfb 100644 --- a/src/private/mx/core/generated/TimewiseMeasure.h +++ b/src/private/mx/core/generated/TimewiseMeasure.h @@ -23,6 +23,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Content fields mirror the schema grammar in declaration order; the /// serializer walks them, so a wrong order is unrepresentable (plan §2.3). class TimewiseMeasure final @@ -55,9 +57,9 @@ class TimewiseMeasure final OneOrMore m_part; }; -TimewiseMeasure parseTimewiseMeasure(pugi::xml_node el); +TimewiseMeasure parseTimewiseMeasure(pugi::xml_node el, const ParseContext &context); -void parseTimewiseMeasureContent(TimewiseMeasure &out, pugi::xml_node el); +void parseTimewiseMeasureContent(TimewiseMeasure &out, pugi::xml_node el, const ParseContext &context); void serializeTimewiseMeasure(const TimewiseMeasure &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/TimewisePart.cpp b/src/private/mx/core/generated/TimewisePart.cpp index eb34c07dd..d24fa9508 100644 --- a/src/private/mx/core/generated/TimewisePart.cpp +++ b/src/private/mx/core/generated/TimewisePart.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/TimewisePart.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -35,7 +36,7 @@ void TimewisePart::setMusicData(std::vector value) m_musicData = std::move(value); } -TimewisePart parseTimewisePart(pugi::xml_node el) +TimewisePart parseTimewisePart(pugi::xml_node el, const ParseContext &context) { TimewisePart out; bool seen_id = false; @@ -49,7 +50,7 @@ TimewisePart parseTimewisePart(pugi::xml_node el) if (aname == "id") { seen_id = true; - out.setID(Token::parse(a.value())); + out.setID(parseValue(a.value(), context, el, "id")); } else { @@ -60,11 +61,11 @@ TimewisePart parseTimewisePart(pugi::xml_node el) { throwMissingAttribute(el, "id"); } - parseTimewisePartContent(out, el); + parseTimewisePartContent(out, el, context); return out; } -void parseTimewisePartContent(TimewisePart &out, pugi::xml_node el) +void parseTimewisePartContent(TimewisePart &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); while (cursor && (cursorIs(cursor, "note") || cursorIs(cursor, "backup") || cursorIs(cursor, "forward") || @@ -73,7 +74,7 @@ void parseTimewisePartContent(TimewisePart &out, pugi::xml_node el) cursorIs(cursor, "listening") || cursorIs(cursor, "barline") || cursorIs(cursor, "grouping") || cursorIs(cursor, "link") || cursorIs(cursor, "bookmark"))) { - out.addMusicData(parseMusicDataChoice(el, cursor)); + out.addMusicData(parseMusicDataChoice(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/TimewisePart.h b/src/private/mx/core/generated/TimewisePart.h index 711a516d9..7a83a555d 100644 --- a/src/private/mx/core/generated/TimewisePart.h +++ b/src/private/mx/core/generated/TimewisePart.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Content fields mirror the schema grammar in declaration order; the /// serializer walks them, so a wrong order is unrepresentable (plan §2.3). class TimewisePart final @@ -36,9 +38,9 @@ class TimewisePart final std::vector m_musicData; }; -TimewisePart parseTimewisePart(pugi::xml_node el); +TimewisePart parseTimewisePart(pugi::xml_node el, const ParseContext &context); -void parseTimewisePartContent(TimewisePart &out, pugi::xml_node el); +void parseTimewisePartContent(TimewisePart &out, pugi::xml_node el, const ParseContext &context); void serializeTimewisePart(const TimewisePart &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Timpani.cpp b/src/private/mx/core/generated/Timpani.cpp index e336159ac..116daf3c8 100644 --- a/src/private/mx/core/generated/Timpani.cpp +++ b/src/private/mx/core/generated/Timpani.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Timpani.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -20,7 +21,7 @@ void Timpani::setSmufl(std::optional value) m_smufl = std::move(value); } -Timpani parseTimpani(pugi::xml_node el) +Timpani parseTimpani(pugi::xml_node el, const ParseContext &context) { Timpani out; for (pugi::xml_attribute a : el.attributes()) @@ -32,20 +33,21 @@ Timpani parseTimpani(pugi::xml_node el) } if (aname == "smufl") { - out.setSmufl(SmuflPictogramGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else { throwUnknownAttribute(el, a.name()); } } - parseTimpaniContent(out, el); + parseTimpaniContent(out, el, context); return out; } -void parseTimpaniContent(Timpani &out, pugi::xml_node el) +void parseTimpaniContent(Timpani &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Timpani.h b/src/private/mx/core/generated/Timpani.h index a27634749..6ed4b21f6 100644 --- a/src/private/mx/core/generated/Timpani.h +++ b/src/private/mx/core/generated/Timpani.h @@ -15,6 +15,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The timpani type represents the timpani pictogram. The smufl attribute is used to distinguish /// different SMuFL stylistic alternates. class Timpani final @@ -28,9 +30,9 @@ class Timpani final std::optional m_smufl; }; -Timpani parseTimpani(pugi::xml_node el); +Timpani parseTimpani(pugi::xml_node el, const ParseContext &context); -void parseTimpaniContent(Timpani &out, pugi::xml_node el); +void parseTimpaniContent(Timpani &out, pugi::xml_node el, const ParseContext &context); void serializeTimpani(const Timpani &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/TipDirection.cpp b/src/private/mx/core/generated/TipDirection.cpp index 3fa0859c9..ab07a67e2 100644 --- a/src/private/mx/core/generated/TipDirection.cpp +++ b/src/private/mx/core/generated/TipDirection.cpp @@ -71,9 +71,15 @@ bool TipDirection::tryParse(std::string_view text, TipDirection &out) noexcept } TipDirection TipDirection::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +TipDirection TipDirection::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { TipDirection v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/TipDirection.h b/src/private/mx/core/generated/TipDirection.h index 3dffc4912..41108175c 100644 --- a/src/private/mx/core/generated/TipDirection.h +++ b/src/private/mx/core/generated/TipDirection.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -53,6 +55,9 @@ class TipDirection final /// (the import leniency policy; never produces an invalid value). static TipDirection parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static TipDirection parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const TipDirection &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/TopBottom.cpp b/src/private/mx/core/generated/TopBottom.cpp index 476639779..b26fc94ce 100644 --- a/src/private/mx/core/generated/TopBottom.cpp +++ b/src/private/mx/core/generated/TopBottom.cpp @@ -42,9 +42,15 @@ bool TopBottom::tryParse(std::string_view text, TopBottom &out) noexcept } TopBottom TopBottom::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +TopBottom TopBottom::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { TopBottom v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/TopBottom.h b/src/private/mx/core/generated/TopBottom.h index 2b33c0de7..eaf2eaf06 100644 --- a/src/private/mx/core/generated/TopBottom.h +++ b/src/private/mx/core/generated/TopBottom.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -41,6 +43,9 @@ class TopBottom final /// (the import leniency policy; never produces an invalid value). static TopBottom parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static TopBottom parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const TopBottom &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/TraditionalKeyGroup.cpp b/src/private/mx/core/generated/TraditionalKeyGroup.cpp index baf5a3268..0ac5c027d 100644 --- a/src/private/mx/core/generated/TraditionalKeyGroup.cpp +++ b/src/private/mx/core/generated/TraditionalKeyGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/TraditionalKeyGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,17 +41,17 @@ void TraditionalKeyGroup::setMode(std::optional value) m_mode = std::move(value); } -TraditionalKeyGroup parseTraditionalKeyGroup(pugi::xml_node el, pugi::xml_node &cursor) +TraditionalKeyGroup parseTraditionalKeyGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { TraditionalKeyGroup out; if (cursorIs(cursor, "cancel")) { - out.setCancel(parseCancel(cursor)); + out.setCancel(parseCancel(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "fifths")) { - out.setFifths(Fifths::parse(childText(cursor))); + out.setFifths(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -59,7 +60,7 @@ TraditionalKeyGroup parseTraditionalKeyGroup(pugi::xml_node el, pugi::xml_node & } if (cursorIs(cursor, "mode")) { - out.setMode(Mode::parse(childText(cursor))); + out.setMode(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/TraditionalKeyGroup.h b/src/private/mx/core/generated/TraditionalKeyGroup.h index 8f1d1d11a..4f2eaef2c 100644 --- a/src/private/mx/core/generated/TraditionalKeyGroup.h +++ b/src/private/mx/core/generated/TraditionalKeyGroup.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The traditional-key group represents a traditional key signature using the cycle of fifths. /// A shared content group: transparent on the wire, its /// fields serialize directly into the referencing element (plan §2.3). @@ -41,7 +43,7 @@ class TraditionalKeyGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -TraditionalKeyGroup parseTraditionalKeyGroup(pugi::xml_node el, pugi::xml_node &cursor); +TraditionalKeyGroup parseTraditionalKeyGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeTraditionalKeyGroup(const TraditionalKeyGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Transpose.cpp b/src/private/mx/core/generated/Transpose.cpp index 5495e3f81..80a05cfc4 100644 --- a/src/private/mx/core/generated/Transpose.cpp +++ b/src/private/mx/core/generated/Transpose.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Transpose.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,7 +41,7 @@ void Transpose::setTranspose(TransposeGroup value) m_transpose = std::move(value); } -Transpose parseTranspose(pugi::xml_node el) +Transpose parseTranspose(pugi::xml_node el, const ParseContext &context) { Transpose out; for (pugi::xml_attribute a : el.attributes()) @@ -52,27 +53,27 @@ Transpose parseTranspose(pugi::xml_node el) } if (aname == "number") { - out.setNumber(StaffNumber::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { throwUnknownAttribute(el, a.name()); } } - parseTransposeContent(out, el); + parseTransposeContent(out, el, context); return out; } -void parseTransposeContent(Transpose &out, pugi::xml_node el) +void parseTransposeContent(Transpose &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "diatonic") || cursorIs(cursor, "chromatic"))) { - out.setTranspose(parseTransposeGroup(el, cursor)); + out.setTranspose(parseTransposeGroup(el, cursor, context)); } else { diff --git a/src/private/mx/core/generated/Transpose.h b/src/private/mx/core/generated/Transpose.h index 00c6d8fd4..fb2553edf 100644 --- a/src/private/mx/core/generated/Transpose.h +++ b/src/private/mx/core/generated/Transpose.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The transpose type represents what must be added to a written pitch to get a correct sounding /// pitch. The optional number attribute refers to staff numbers, from top to bottom on the system. /// If absent, the transposition applies to all staves in the part. Per-staff transposition is most @@ -43,9 +45,9 @@ class Transpose final TransposeGroup m_transpose{}; }; -Transpose parseTranspose(pugi::xml_node el); +Transpose parseTranspose(pugi::xml_node el, const ParseContext &context); -void parseTransposeContent(Transpose &out, pugi::xml_node el); +void parseTransposeContent(Transpose &out, pugi::xml_node el, const ParseContext &context); void serializeTranspose(const Transpose &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/TransposeGroup.cpp b/src/private/mx/core/generated/TransposeGroup.cpp index d0cc8ec5c..53a65ebe8 100644 --- a/src/private/mx/core/generated/TransposeGroup.cpp +++ b/src/private/mx/core/generated/TransposeGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/TransposeGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -50,17 +51,17 @@ void TransposeGroup::setDouble(std::optional value) m_double_ = std::move(value); } -TransposeGroup parseTransposeGroup(pugi::xml_node el, pugi::xml_node &cursor) +TransposeGroup parseTransposeGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { TransposeGroup out; if (cursorIs(cursor, "diatonic")) { - out.setDiatonic(parseInt(childText(cursor))); + out.setDiatonic(parseIntegerValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "chromatic")) { - out.setChromatic(Semitones::parse(childText(cursor))); + out.setChromatic(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -69,12 +70,12 @@ TransposeGroup parseTransposeGroup(pugi::xml_node el, pugi::xml_node &cursor) } if (cursorIs(cursor, "octave-change")) { - out.setOctaveChange(parseInt(childText(cursor))); + out.setOctaveChange(parseIntegerValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "double")) { - out.setDouble(parseDouble(cursor)); + out.setDouble(parseDouble(cursor, context)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/TransposeGroup.h b/src/private/mx/core/generated/TransposeGroup.h index e88ed8afa..e73014e13 100644 --- a/src/private/mx/core/generated/TransposeGroup.h +++ b/src/private/mx/core/generated/TransposeGroup.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The transpose group represents what must be added to a written pitch to get a correct sounding /// pitch. /// A shared content group: transparent on the wire, its @@ -44,7 +46,7 @@ class TransposeGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -TransposeGroup parseTransposeGroup(pugi::xml_node el, pugi::xml_node &cursor); +TransposeGroup parseTransposeGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeTransposeGroup(const TransposeGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Tremolo.cpp b/src/private/mx/core/generated/Tremolo.cpp index d467c9b28..1c9699d23 100644 --- a/src/private/mx/core/generated/Tremolo.cpp +++ b/src/private/mx/core/generated/Tremolo.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Tremolo.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -140,7 +141,7 @@ void Tremolo::setValue(TremoloMarks value) m_value = std::move(value); } -Tremolo parseTremolo(pugi::xml_node el) +Tremolo parseTremolo(pugi::xml_node el, const ParseContext &context) { Tremolo out; for (pugi::xml_attribute a : el.attributes()) @@ -152,64 +153,64 @@ Tremolo parseTremolo(pugi::xml_node el) } if (aname == "type") { - out.setType(TremoloType::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "smufl") { - out.setSmufl(SmuflGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else { throwUnknownAttribute(el, a.name()); } } - parseTremoloContent(out, el); + parseTremoloContent(out, el, context); return out; } -void parseTremoloContent(Tremolo &out, pugi::xml_node el) +void parseTremoloContent(Tremolo &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(TremoloMarks::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Tremolo.h b/src/private/mx/core/generated/Tremolo.h index 6610987e2..838f4f8e9 100644 --- a/src/private/mx/core/generated/Tremolo.h +++ b/src/private/mx/core/generated/Tremolo.h @@ -24,6 +24,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The tremolo ornament can be used to indicate single-note, double-note, or unmeasured tremolos. /// Single-note tremolos use the single type, double-note tremolos use the start and stop types, and /// unmeasured tremolos use the unmeasured type. The default is "single" for compatibility with @@ -84,9 +86,9 @@ class Tremolo final TremoloMarks m_value{}; }; -Tremolo parseTremolo(pugi::xml_node el); +Tremolo parseTremolo(pugi::xml_node el, const ParseContext &context); -void parseTremoloContent(Tremolo &out, pugi::xml_node el); +void parseTremoloContent(Tremolo &out, pugi::xml_node el, const ParseContext &context); void serializeTremolo(const Tremolo &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/TremoloMarks.cpp b/src/private/mx/core/generated/TremoloMarks.cpp index c46c301ae..64972e792 100644 --- a/src/private/mx/core/generated/TremoloMarks.cpp +++ b/src/private/mx/core/generated/TremoloMarks.cpp @@ -42,20 +42,33 @@ std::string TremoloMarks::toString() const bool TremoloMarks::tryParse(std::string_view text, TremoloMarks &out) { - int v{}; - if (!tryParseInt(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + TremoloMarks parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = TremoloMarks{v}; + out = std::move(parsed); return true; } TremoloMarks TremoloMarks::parse(std::string_view text) { - TremoloMarks v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +TremoloMarks TremoloMarks::parse(std::string_view text, ValueParseOutcome &outcome) +{ + int v{}; + if (!tryParseInt(text, v)) + { + outcome = ValueParseOutcome::invalid; + return TremoloMarks{}; + } + TremoloMarks out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/TremoloMarks.h b/src/private/mx/core/generated/TremoloMarks.h index 3972a5b81..1a826c2c2 100644 --- a/src/private/mx/core/generated/TremoloMarks.h +++ b/src/private/mx/core/generated/TremoloMarks.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -36,6 +38,9 @@ class TremoloMarks final /// Lenient: non-numeric text yields the clamped zero. static TremoloMarks parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static TremoloMarks parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const TremoloMarks &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/TremoloType.cpp b/src/private/mx/core/generated/TremoloType.cpp index d0e969fcc..7b511763e 100644 --- a/src/private/mx/core/generated/TremoloType.cpp +++ b/src/private/mx/core/generated/TremoloType.cpp @@ -54,9 +54,15 @@ bool TremoloType::tryParse(std::string_view text, TremoloType &out) noexcept } TremoloType TremoloType::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +TremoloType TremoloType::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { TremoloType v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/TremoloType.h b/src/private/mx/core/generated/TremoloType.h index 1b0fdf951..c998c39d0 100644 --- a/src/private/mx/core/generated/TremoloType.h +++ b/src/private/mx/core/generated/TremoloType.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -44,6 +46,9 @@ class TremoloType final /// (the import leniency policy; never produces an invalid value). static TremoloType parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static TremoloType parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const TremoloType &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/TrillBeats.cpp b/src/private/mx/core/generated/TrillBeats.cpp index 36b1e76f8..43c47d6d3 100644 --- a/src/private/mx/core/generated/TrillBeats.cpp +++ b/src/private/mx/core/generated/TrillBeats.cpp @@ -38,20 +38,33 @@ std::string TrillBeats::toString() const bool TrillBeats::tryParse(std::string_view text, TrillBeats &out) { - Decimal v; - if (!Decimal::tryParse(text, v)) + ValueParseOutcome outcome = ValueParseOutcome::valid; + TrillBeats parsed = parse(text, outcome); + if (outcome == ValueParseOutcome::invalid) { return false; } - out = TrillBeats{std::move(v)}; + out = std::move(parsed); return true; } TrillBeats TrillBeats::parse(std::string_view text) { - TrillBeats v; - tryParse(text, v); - return v; + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +TrillBeats TrillBeats::parse(std::string_view text, ValueParseOutcome &outcome) +{ + Decimal v; + if (!Decimal::tryParse(text, v)) + { + outcome = ValueParseOutcome::invalid; + return TrillBeats{}; + } + TrillBeats out{v}; + outcome = out.value() == v ? ValueParseOutcome::valid : ValueParseOutcome::adjusted; + return out; } } // namespace mx::core diff --git a/src/private/mx/core/generated/TrillBeats.h b/src/private/mx/core/generated/TrillBeats.h index 38112e0cd..ac4066f54 100644 --- a/src/private/mx/core/generated/TrillBeats.h +++ b/src/private/mx/core/generated/TrillBeats.h @@ -3,6 +3,7 @@ #pragma once #include "mx/core/Decimal.h" +#include "mx/core/Lexical.h" #include #include @@ -38,6 +39,9 @@ class TrillBeats final /// Lenient: non-numeric text yields the clamped zero. static TrillBeats parse(std::string_view text); + /// Lenient, and says whether the text was non-numeric or clamped. + static TrillBeats parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const TrillBeats &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/TrillStep.cpp b/src/private/mx/core/generated/TrillStep.cpp index 02537b28e..8ab8f40ff 100644 --- a/src/private/mx/core/generated/TrillStep.cpp +++ b/src/private/mx/core/generated/TrillStep.cpp @@ -48,9 +48,15 @@ bool TrillStep::tryParse(std::string_view text, TrillStep &out) noexcept } TrillStep TrillStep::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +TrillStep TrillStep::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { TrillStep v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/TrillStep.h b/src/private/mx/core/generated/TrillStep.h index ce9a1381a..b8d5b48df 100644 --- a/src/private/mx/core/generated/TrillStep.h +++ b/src/private/mx/core/generated/TrillStep.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -43,6 +45,9 @@ class TrillStep final /// (the import leniency policy; never produces an invalid value). static TrillStep parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static TrillStep parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const TrillStep &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/TuningGroup.cpp b/src/private/mx/core/generated/TuningGroup.cpp index ce169fa0c..bed10873b 100644 --- a/src/private/mx/core/generated/TuningGroup.cpp +++ b/src/private/mx/core/generated/TuningGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/TuningGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,12 +41,12 @@ void TuningGroup::setTuningOctave(Octave value) m_tuningOctave = std::move(value); } -TuningGroup parseTuningGroup(pugi::xml_node el, pugi::xml_node &cursor) +TuningGroup parseTuningGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context) { TuningGroup out; if (cursorIs(cursor, "tuning-step")) { - out.setTuningStep(Step::parse(childText(cursor))); + out.setTuningStep(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else @@ -54,12 +55,12 @@ TuningGroup parseTuningGroup(pugi::xml_node el, pugi::xml_node &cursor) } if (cursorIs(cursor, "tuning-alter")) { - out.setTuningAlter(Semitones::parse(childText(cursor))); + out.setTuningAlter(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursorIs(cursor, "tuning-octave")) { - out.setTuningOctave(Octave::parse(childText(cursor))); + out.setTuningOctave(parseValue(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } else diff --git a/src/private/mx/core/generated/TuningGroup.h b/src/private/mx/core/generated/TuningGroup.h index 0f3aea5ea..7dba5e70f 100644 --- a/src/private/mx/core/generated/TuningGroup.h +++ b/src/private/mx/core/generated/TuningGroup.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The tuning group contains the sequence of elements common to the staff-tuning and accord /// elements. /// A shared content group: transparent on the wire, its @@ -42,7 +44,7 @@ class TuningGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -TuningGroup parseTuningGroup(pugi::xml_node el, pugi::xml_node &cursor); +TuningGroup parseTuningGroup(pugi::xml_node el, pugi::xml_node &cursor, const ParseContext &context); void serializeTuningGroup(const TuningGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Tuplet.cpp b/src/private/mx/core/generated/Tuplet.cpp index 4eb590319..5ba9086da 100644 --- a/src/private/mx/core/generated/Tuplet.cpp +++ b/src/private/mx/core/generated/Tuplet.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Tuplet.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -150,7 +151,7 @@ void Tuplet::setTupletNormal(std::optional value) m_tupletNormal = std::move(value); } -Tuplet parseTuplet(pugi::xml_node el) +Tuplet parseTuplet(pugi::xml_node el, const ParseContext &context) { Tuplet out; bool seen_type = false; @@ -164,51 +165,51 @@ Tuplet parseTuplet(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStop::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "number") { - out.setNumber(NumberLevel::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "bracket") { - out.setBracket(YesNo::parse(a.value())); + out.setBracket(parseValue(a.value(), context, el, "bracket")); } else if (aname == "show-number") { - out.setShowNumber(ShowTuplet::parse(a.value())); + out.setShowNumber(parseValue(a.value(), context, el, "show-number")); } else if (aname == "show-type") { - out.setShowType(ShowTuplet::parse(a.value())); + out.setShowType(parseValue(a.value(), context, el, "show-type")); } else if (aname == "line-shape") { - out.setLineShape(LineShape::parse(a.value())); + out.setLineShape(parseValue(a.value(), context, el, "line-shape")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -219,21 +220,21 @@ Tuplet parseTuplet(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseTupletContent(out, el); + parseTupletContent(out, el, context); return out; } -void parseTupletContent(Tuplet &out, pugi::xml_node el) +void parseTupletContent(Tuplet &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "tuplet-actual")) { - out.setTupletActual(parseTupletPortion(cursor)); + out.setTupletActual(parseTupletPortion(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "tuplet-normal")) { - out.setTupletNormal(parseTupletPortion(cursor)); + out.setTupletNormal(parseTupletPortion(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Tuplet.h b/src/private/mx/core/generated/Tuplet.h index c64d13981..909a332df 100644 --- a/src/private/mx/core/generated/Tuplet.h +++ b/src/private/mx/core/generated/Tuplet.h @@ -26,6 +26,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A tuplet element is present when a tuplet is to be displayed graphically, in addition to the /// sound data provided by the time-modification elements. The number attribute is used to /// distinguish nested tuplets. The bracket attribute is used to indicate the presence of a bracket. @@ -91,9 +93,9 @@ class Tuplet final std::optional m_tupletNormal; }; -Tuplet parseTuplet(pugi::xml_node el); +Tuplet parseTuplet(pugi::xml_node el, const ParseContext &context); -void parseTupletContent(Tuplet &out, pugi::xml_node el); +void parseTupletContent(Tuplet &out, pugi::xml_node el, const ParseContext &context); void serializeTuplet(const Tuplet &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/TupletDot.cpp b/src/private/mx/core/generated/TupletDot.cpp index 95f1314db..b86cb725d 100644 --- a/src/private/mx/core/generated/TupletDot.cpp +++ b/src/private/mx/core/generated/TupletDot.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/TupletDot.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -60,7 +61,7 @@ void TupletDot::setColor(std::optional value) m_color = std::move(value); } -TupletDot parseTupletDot(pugi::xml_node el) +TupletDot parseTupletDot(pugi::xml_node el, const ParseContext &context) { TupletDot out; for (pugi::xml_attribute a : el.attributes()) @@ -72,36 +73,37 @@ TupletDot parseTupletDot(pugi::xml_node el) } if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseTupletDotContent(out, el); + parseTupletDotContent(out, el, context); return out; } -void parseTupletDotContent(TupletDot &out, pugi::xml_node el) +void parseTupletDotContent(TupletDot &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/TupletDot.h b/src/private/mx/core/generated/TupletDot.h index fe15edbb9..8f62ac224 100644 --- a/src/private/mx/core/generated/TupletDot.h +++ b/src/private/mx/core/generated/TupletDot.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The tuplet-dot type is used to specify dotted tuplet types. class TupletDot final { @@ -43,9 +45,9 @@ class TupletDot final std::optional m_color; }; -TupletDot parseTupletDot(pugi::xml_node el); +TupletDot parseTupletDot(pugi::xml_node el, const ParseContext &context); -void parseTupletDotContent(TupletDot &out, pugi::xml_node el); +void parseTupletDotContent(TupletDot &out, pugi::xml_node el, const ParseContext &context); void serializeTupletDot(const TupletDot &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/TupletNumber.cpp b/src/private/mx/core/generated/TupletNumber.cpp index 271689b95..e274b4cd1 100644 --- a/src/private/mx/core/generated/TupletNumber.cpp +++ b/src/private/mx/core/generated/TupletNumber.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/TupletNumber.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -70,7 +71,7 @@ void TupletNumber::setValue(int value) m_value = std::move(value); } -TupletNumber parseTupletNumber(pugi::xml_node el) +TupletNumber parseTupletNumber(pugi::xml_node el, const ParseContext &context) { TupletNumber out; for (pugi::xml_attribute a : el.attributes()) @@ -82,36 +83,36 @@ TupletNumber parseTupletNumber(pugi::xml_node el) } if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseTupletNumberContent(out, el); + parseTupletNumberContent(out, el, context); return out; } -void parseTupletNumberContent(TupletNumber &out, pugi::xml_node el) +void parseTupletNumberContent(TupletNumber &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(parseInt(childText(el))); + out.setValue(parseIntegerValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/TupletNumber.h b/src/private/mx/core/generated/TupletNumber.h index d30ab4335..b188a1d85 100644 --- a/src/private/mx/core/generated/TupletNumber.h +++ b/src/private/mx/core/generated/TupletNumber.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The tuplet-number type indicates the number of notes for this portion of the tuplet. class TupletNumber final { @@ -46,9 +48,9 @@ class TupletNumber final int m_value{}; }; -TupletNumber parseTupletNumber(pugi::xml_node el); +TupletNumber parseTupletNumber(pugi::xml_node el, const ParseContext &context); -void parseTupletNumberContent(TupletNumber &out, pugi::xml_node el); +void parseTupletNumberContent(TupletNumber &out, pugi::xml_node el, const ParseContext &context); void serializeTupletNumber(const TupletNumber &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/TupletPortion.cpp b/src/private/mx/core/generated/TupletPortion.cpp index c5b363afe..860772545 100644 --- a/src/private/mx/core/generated/TupletPortion.cpp +++ b/src/private/mx/core/generated/TupletPortion.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/TupletPortion.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -45,7 +46,7 @@ void TupletPortion::setTupletDot(std::vector value) m_tupletDot = std::move(value); } -TupletPortion parseTupletPortion(pugi::xml_node el) +TupletPortion parseTupletPortion(pugi::xml_node el, const ParseContext &context) { TupletPortion out; for (pugi::xml_attribute a : el.attributes()) @@ -57,26 +58,26 @@ TupletPortion parseTupletPortion(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseTupletPortionContent(out, el); + parseTupletPortionContent(out, el, context); return out; } -void parseTupletPortionContent(TupletPortion &out, pugi::xml_node el) +void parseTupletPortionContent(TupletPortion &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "tuplet-number")) { - out.setTupletNumber(parseTupletNumber(cursor)); + out.setTupletNumber(parseTupletNumber(cursor, context)); cursor = nextElement(cursor); } if (cursorIs(cursor, "tuplet-type")) { - out.setTupletType(parseTupletType(cursor)); + out.setTupletType(parseTupletType(cursor, context)); cursor = nextElement(cursor); } while (cursorIs(cursor, "tuplet-dot")) { - out.addTupletDot(parseTupletDot(cursor)); + out.addTupletDot(parseTupletDot(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/TupletPortion.h b/src/private/mx/core/generated/TupletPortion.h index ea0d3111f..8698cb2f8 100644 --- a/src/private/mx/core/generated/TupletPortion.h +++ b/src/private/mx/core/generated/TupletPortion.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The tuplet-portion type provides optional full control over tuplet specifications. It allows the /// number and note type (including dots) to be set for the actual and normal portions of a single /// tuplet. If any of these elements are absent, their values are based on the time-modification @@ -43,9 +45,9 @@ class TupletPortion final std::vector m_tupletDot; }; -TupletPortion parseTupletPortion(pugi::xml_node el); +TupletPortion parseTupletPortion(pugi::xml_node el, const ParseContext &context); -void parseTupletPortionContent(TupletPortion &out, pugi::xml_node el); +void parseTupletPortionContent(TupletPortion &out, pugi::xml_node el, const ParseContext &context); void serializeTupletPortion(const TupletPortion &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/TupletType.cpp b/src/private/mx/core/generated/TupletType.cpp index 9faf2efef..d15bd7c3e 100644 --- a/src/private/mx/core/generated/TupletType.cpp +++ b/src/private/mx/core/generated/TupletType.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/TupletType.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -70,7 +71,7 @@ void TupletType::setValue(NoteTypeValue value) m_value = std::move(value); } -TupletType parseTupletType(pugi::xml_node el) +TupletType parseTupletType(pugi::xml_node el, const ParseContext &context) { TupletType out; for (pugi::xml_attribute a : el.attributes()) @@ -82,36 +83,36 @@ TupletType parseTupletType(pugi::xml_node el) } if (aname == "font-family") { - out.setFontFamily(FontFamily::parse(a.value())); + out.setFontFamily(parseValue(a.value(), context, el, "font-family")); } else if (aname == "font-style") { - out.setFontStyle(FontStyle::parse(a.value())); + out.setFontStyle(parseValue(a.value(), context, el, "font-style")); } else if (aname == "font-size") { - out.setFontSize(FontSize::parse(a.value())); + out.setFontSize(parseValue(a.value(), context, el, "font-size")); } else if (aname == "font-weight") { - out.setFontWeight(FontWeight::parse(a.value())); + out.setFontWeight(parseValue(a.value(), context, el, "font-weight")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else { throwUnknownAttribute(el, a.name()); } } - parseTupletTypeContent(out, el); + parseTupletTypeContent(out, el, context); return out; } -void parseTupletTypeContent(TupletType &out, pugi::xml_node el) +void parseTupletTypeContent(TupletType &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(NoteTypeValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/TupletType.h b/src/private/mx/core/generated/TupletType.h index d5c32505d..391bb2ebb 100644 --- a/src/private/mx/core/generated/TupletType.h +++ b/src/private/mx/core/generated/TupletType.h @@ -20,6 +20,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The tuplet-type type indicates the graphical note type of the notes for this portion of the /// tuplet. class TupletType final @@ -48,9 +50,9 @@ class TupletType final NoteTypeValue m_value{}; }; -TupletType parseTupletType(pugi::xml_node el); +TupletType parseTupletType(pugi::xml_node el, const ParseContext &context); -void parseTupletTypeContent(TupletType &out, pugi::xml_node el); +void parseTupletTypeContent(TupletType &out, pugi::xml_node el, const ParseContext &context); void serializeTupletType(const TupletType &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/TwoNoteTurn.cpp b/src/private/mx/core/generated/TwoNoteTurn.cpp index 4768f8459..7f801fb26 100644 --- a/src/private/mx/core/generated/TwoNoteTurn.cpp +++ b/src/private/mx/core/generated/TwoNoteTurn.cpp @@ -48,9 +48,15 @@ bool TwoNoteTurn::tryParse(std::string_view text, TwoNoteTurn &out) noexcept } TwoNoteTurn TwoNoteTurn::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +TwoNoteTurn TwoNoteTurn::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { TwoNoteTurn v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/TwoNoteTurn.h b/src/private/mx/core/generated/TwoNoteTurn.h index a92e168ca..1919cccfd 100644 --- a/src/private/mx/core/generated/TwoNoteTurn.h +++ b/src/private/mx/core/generated/TwoNoteTurn.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -43,6 +45,9 @@ class TwoNoteTurn final /// (the import leniency policy; never produces an invalid value). static TwoNoteTurn parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static TwoNoteTurn parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const TwoNoteTurn &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/TypedText.cpp b/src/private/mx/core/generated/TypedText.cpp index 9b94475da..ce8081aaf 100644 --- a/src/private/mx/core/generated/TypedText.cpp +++ b/src/private/mx/core/generated/TypedText.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/TypedText.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void TypedText::setValue(std::string value) m_value = std::move(value); } -TypedText parseTypedText(pugi::xml_node el) +TypedText parseTypedText(pugi::xml_node el, const ParseContext &context) { TypedText out; for (pugi::xml_attribute a : el.attributes()) @@ -49,11 +50,11 @@ TypedText parseTypedText(pugi::xml_node el) throwUnknownAttribute(el, a.name()); } } - parseTypedTextContent(out, el); + parseTypedTextContent(out, el, context); return out; } -void parseTypedTextContent(TypedText &out, pugi::xml_node el) +void parseTypedTextContent(TypedText &out, pugi::xml_node el, const ParseContext &context) { out.setValue(std::string{childText(el)}); if (firstElement(el)) diff --git a/src/private/mx/core/generated/TypedText.h b/src/private/mx/core/generated/TypedText.h index a7fe1f35a..43a9fbf27 100644 --- a/src/private/mx/core/generated/TypedText.h +++ b/src/private/mx/core/generated/TypedText.h @@ -13,6 +13,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The typed-text type represents a text element with a type attribute. class TypedText final { @@ -28,9 +30,9 @@ class TypedText final std::string m_value{}; }; -TypedText parseTypedText(pugi::xml_node el); +TypedText parseTypedText(pugi::xml_node el, const ParseContext &context); -void parseTypedTextContent(TypedText &out, pugi::xml_node el); +void parseTypedTextContent(TypedText &out, pugi::xml_node el, const ParseContext &context); void serializeTypedText(const TypedText &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Unpitched.cpp b/src/private/mx/core/generated/Unpitched.cpp index 9868ba894..40060e758 100644 --- a/src/private/mx/core/generated/Unpitched.cpp +++ b/src/private/mx/core/generated/Unpitched.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Unpitched.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -20,7 +21,7 @@ void Unpitched::setDisplayStepOctave(std::optional value m_displayStepOctave = std::move(value); } -Unpitched parseUnpitched(pugi::xml_node el) +Unpitched parseUnpitched(pugi::xml_node el, const ParseContext &context) { Unpitched out; for (pugi::xml_attribute a : el.attributes()) @@ -32,16 +33,16 @@ Unpitched parseUnpitched(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseUnpitchedContent(out, el); + parseUnpitchedContent(out, el, context); return out; } -void parseUnpitchedContent(Unpitched &out, pugi::xml_node el) +void parseUnpitchedContent(Unpitched &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursor && (cursorIs(cursor, "display-step"))) { - out.setDisplayStepOctave(parseDisplayStepOctaveGroup(el, cursor)); + out.setDisplayStepOctave(parseDisplayStepOctaveGroup(el, cursor, context)); } if (cursor) { diff --git a/src/private/mx/core/generated/Unpitched.h b/src/private/mx/core/generated/Unpitched.h index fc673eed5..2baf09a5d 100644 --- a/src/private/mx/core/generated/Unpitched.h +++ b/src/private/mx/core/generated/Unpitched.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The unpitched type represents musical elements that are notated on the staff but lack definite /// pitch, such as unpitched percussion and speaking voice. If the child elements are not present, /// the note is placed on the middle line of the staff. This is generally used with a one-line staff. @@ -34,9 +36,9 @@ class Unpitched final std::optional m_displayStepOctave; }; -Unpitched parseUnpitched(pugi::xml_node el); +Unpitched parseUnpitched(pugi::xml_node el, const ParseContext &context); -void parseUnpitchedContent(Unpitched &out, pugi::xml_node el); +void parseUnpitchedContent(Unpitched &out, pugi::xml_node el, const ParseContext &context); void serializeUnpitched(const Unpitched &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/UpDown.cpp b/src/private/mx/core/generated/UpDown.cpp index 411a68e32..6106d179e 100644 --- a/src/private/mx/core/generated/UpDown.cpp +++ b/src/private/mx/core/generated/UpDown.cpp @@ -42,9 +42,15 @@ bool UpDown::tryParse(std::string_view text, UpDown &out) noexcept } UpDown UpDown::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +UpDown UpDown::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { UpDown v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/UpDown.h b/src/private/mx/core/generated/UpDown.h index 4b9e0137c..633191797 100644 --- a/src/private/mx/core/generated/UpDown.h +++ b/src/private/mx/core/generated/UpDown.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -41,6 +43,9 @@ class UpDown final /// (the import leniency policy; never produces an invalid value). static UpDown parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static UpDown parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const UpDown &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/UpDownStopContinue.cpp b/src/private/mx/core/generated/UpDownStopContinue.cpp index eec3a75ad..3b3179ad3 100644 --- a/src/private/mx/core/generated/UpDownStopContinue.cpp +++ b/src/private/mx/core/generated/UpDownStopContinue.cpp @@ -54,9 +54,15 @@ bool UpDownStopContinue::tryParse(std::string_view text, UpDownStopContinue &out } UpDownStopContinue UpDownStopContinue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +UpDownStopContinue UpDownStopContinue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { UpDownStopContinue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/UpDownStopContinue.h b/src/private/mx/core/generated/UpDownStopContinue.h index 02a93473e..8b8dfaf7d 100644 --- a/src/private/mx/core/generated/UpDownStopContinue.h +++ b/src/private/mx/core/generated/UpDownStopContinue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -45,6 +47,9 @@ class UpDownStopContinue final /// (the import leniency policy; never produces an invalid value). static UpDownStopContinue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static UpDownStopContinue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const UpDownStopContinue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/UprightInverted.cpp b/src/private/mx/core/generated/UprightInverted.cpp index ba42097de..4780107f5 100644 --- a/src/private/mx/core/generated/UprightInverted.cpp +++ b/src/private/mx/core/generated/UprightInverted.cpp @@ -42,9 +42,15 @@ bool UprightInverted::tryParse(std::string_view text, UprightInverted &out) noex } UprightInverted UprightInverted::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +UprightInverted UprightInverted::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { UprightInverted v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/UprightInverted.h b/src/private/mx/core/generated/UprightInverted.h index dfc9d8b2d..83e06e9c0 100644 --- a/src/private/mx/core/generated/UprightInverted.h +++ b/src/private/mx/core/generated/UprightInverted.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -41,6 +43,9 @@ class UprightInverted final /// (the import leniency policy; never produces an invalid value). static UprightInverted parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static UprightInverted parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const UprightInverted &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Valign.cpp b/src/private/mx/core/generated/Valign.cpp index 66cadc7d0..45c3361ed 100644 --- a/src/private/mx/core/generated/Valign.cpp +++ b/src/private/mx/core/generated/Valign.cpp @@ -54,9 +54,15 @@ bool Valign::tryParse(std::string_view text, Valign &out) noexcept } Valign Valign::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +Valign Valign::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { Valign v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/Valign.h b/src/private/mx/core/generated/Valign.h index 5ef8b9bc9..cf5c13aa5 100644 --- a/src/private/mx/core/generated/Valign.h +++ b/src/private/mx/core/generated/Valign.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -46,6 +48,9 @@ class Valign final /// (the import leniency policy; never produces an invalid value). static Valign parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static Valign parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const Valign &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/ValignImage.cpp b/src/private/mx/core/generated/ValignImage.cpp index 94cc287d0..38226cf4f 100644 --- a/src/private/mx/core/generated/ValignImage.cpp +++ b/src/private/mx/core/generated/ValignImage.cpp @@ -48,9 +48,15 @@ bool ValignImage::tryParse(std::string_view text, ValignImage &out) noexcept } ValignImage ValignImage::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +ValignImage ValignImage::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { ValignImage v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/ValignImage.h b/src/private/mx/core/generated/ValignImage.h index e1f4a595f..c05cc38fd 100644 --- a/src/private/mx/core/generated/ValignImage.h +++ b/src/private/mx/core/generated/ValignImage.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -43,6 +45,9 @@ class ValignImage final /// (the import leniency policy; never produces an invalid value). static ValignImage parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static ValignImage parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const ValignImage &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/VirtualInstrument.cpp b/src/private/mx/core/generated/VirtualInstrument.cpp index 972697a79..b88080e90 100644 --- a/src/private/mx/core/generated/VirtualInstrument.cpp +++ b/src/private/mx/core/generated/VirtualInstrument.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/VirtualInstrument.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void VirtualInstrument::setVirtualName(std::optional value) m_virtualName = std::move(value); } -VirtualInstrument parseVirtualInstrument(pugi::xml_node el) +VirtualInstrument parseVirtualInstrument(pugi::xml_node el, const ParseContext &context) { VirtualInstrument out; for (pugi::xml_attribute a : el.attributes()) @@ -42,11 +43,11 @@ VirtualInstrument parseVirtualInstrument(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseVirtualInstrumentContent(out, el); + parseVirtualInstrumentContent(out, el, context); return out; } -void parseVirtualInstrumentContent(VirtualInstrument &out, pugi::xml_node el) +void parseVirtualInstrumentContent(VirtualInstrument &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "virtual-library")) diff --git a/src/private/mx/core/generated/VirtualInstrument.h b/src/private/mx/core/generated/VirtualInstrument.h index 570a7eccd..1d28f7687 100644 --- a/src/private/mx/core/generated/VirtualInstrument.h +++ b/src/private/mx/core/generated/VirtualInstrument.h @@ -15,6 +15,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The virtual-instrument element defines a specific virtual instrument used for an instrument /// sound. /// Content fields mirror the schema grammar in declaration order; the @@ -33,9 +35,9 @@ class VirtualInstrument final std::optional m_virtualName; }; -VirtualInstrument parseVirtualInstrument(pugi::xml_node el); +VirtualInstrument parseVirtualInstrument(pugi::xml_node el, const ParseContext &context); -void parseVirtualInstrumentContent(VirtualInstrument &out, pugi::xml_node el); +void parseVirtualInstrumentContent(VirtualInstrument &out, pugi::xml_node el, const ParseContext &context); void serializeVirtualInstrument(const VirtualInstrument &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/VirtualInstrumentDataGroup.cpp b/src/private/mx/core/generated/VirtualInstrumentDataGroup.cpp index 00713ebad..bb24ef1eb 100644 --- a/src/private/mx/core/generated/VirtualInstrumentDataGroup.cpp +++ b/src/private/mx/core/generated/VirtualInstrumentDataGroup.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/VirtualInstrumentDataGroup.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,21 +41,22 @@ void VirtualInstrumentDataGroup::setVirtualInstrument(std::optional(childText(cursor), context, cursor, nullptr)); cursor = nextElement(cursor); } if (cursor && (cursorIs(cursor, "solo") || cursorIs(cursor, "ensemble"))) { - out.setChoice(parseVirtualInstrumentDataGroupChoice(el, cursor)); + out.setChoice(parseVirtualInstrumentDataGroupChoice(el, cursor, context)); } if (cursorIs(cursor, "virtual-instrument")) { - out.setVirtualInstrument(parseVirtualInstrument(cursor)); + out.setVirtualInstrument(parseVirtualInstrument(cursor, context)); cursor = nextElement(cursor); } return out; diff --git a/src/private/mx/core/generated/VirtualInstrumentDataGroup.h b/src/private/mx/core/generated/VirtualInstrumentDataGroup.h index dff4fa865..0a0e3d267 100644 --- a/src/private/mx/core/generated/VirtualInstrumentDataGroup.h +++ b/src/private/mx/core/generated/VirtualInstrumentDataGroup.h @@ -19,6 +19,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Virtual instrument data can be part of either the score-instrument element at the start of a /// part, or an instrument-change element within a part. /// A shared content group: transparent on the wire, its @@ -42,7 +44,8 @@ class VirtualInstrumentDataGroup final /// Consumes matching siblings starting at `cursor` (which advances); `el` /// is the enclosing element, for error paths. -VirtualInstrumentDataGroup parseVirtualInstrumentDataGroup(pugi::xml_node el, pugi::xml_node &cursor); +VirtualInstrumentDataGroup parseVirtualInstrumentDataGroup(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context); void serializeVirtualInstrumentDataGroup(const VirtualInstrumentDataGroup &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/VirtualInstrumentDataGroupChoice.cpp b/src/private/mx/core/generated/VirtualInstrumentDataGroupChoice.cpp index 6d28f63c9..5d399eed1 100644 --- a/src/private/mx/core/generated/VirtualInstrumentDataGroupChoice.cpp +++ b/src/private/mx/core/generated/VirtualInstrumentDataGroupChoice.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/VirtualInstrumentDataGroupChoice.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -25,17 +26,18 @@ VirtualInstrumentDataGroupChoice VirtualInstrumentDataGroupChoice::ensemble(Posi return VirtualInstrumentDataGroupChoice{Storage{std::in_place_index<1>, std::move(value)}}; } -VirtualInstrumentDataGroupChoice parseVirtualInstrumentDataGroupChoice(pugi::xml_node el, pugi::xml_node &cursor) +VirtualInstrumentDataGroupChoice parseVirtualInstrumentDataGroupChoice(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context) { if (cursor && (cursorIs(cursor, "solo"))) { - Empty value = parseEmpty(cursor); + Empty value = parseEmpty(cursor, context); cursor = nextElement(cursor); return VirtualInstrumentDataGroupChoice::solo(std::move(value)); } if (cursor && (cursorIs(cursor, "ensemble"))) { - PositiveIntegerOrEmpty value = PositiveIntegerOrEmpty::parse(childText(cursor)); + PositiveIntegerOrEmpty value = parseValue(childText(cursor), context, cursor, nullptr); cursor = nextElement(cursor); return VirtualInstrumentDataGroupChoice::ensemble(std::move(value)); } diff --git a/src/private/mx/core/generated/VirtualInstrumentDataGroupChoice.h b/src/private/mx/core/generated/VirtualInstrumentDataGroupChoice.h index c8c7bafe1..0d1b4fae2 100644 --- a/src/private/mx/core/generated/VirtualInstrumentDataGroupChoice.h +++ b/src/private/mx/core/generated/VirtualInstrumentDataGroupChoice.h @@ -18,6 +18,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// A schema choice (synthesized from an anonymous particle /// of the schema): exactly one alternative by construction. /// Alternatives are positional; dispatch is by Kind/index, never by type @@ -80,7 +82,8 @@ class VirtualInstrumentDataGroupChoice final /// Consumes one alternative starting at `cursor` (which advances); `el` is /// the enclosing element, for error paths. -VirtualInstrumentDataGroupChoice parseVirtualInstrumentDataGroupChoice(pugi::xml_node el, pugi::xml_node &cursor); +VirtualInstrumentDataGroupChoice parseVirtualInstrumentDataGroupChoice(pugi::xml_node el, pugi::xml_node &cursor, + const ParseContext &context); void serializeVirtualInstrumentDataGroupChoice(const VirtualInstrumentDataGroupChoice &v, pugi::xml_node el); diff --git a/src/private/mx/core/generated/Wait.cpp b/src/private/mx/core/generated/Wait.cpp index 57ceac38d..6f0dd639f 100644 --- a/src/private/mx/core/generated/Wait.cpp +++ b/src/private/mx/core/generated/Wait.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Wait.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Wait::setTimeOnly(std::optional value) m_timeOnly = std::move(value); } -Wait parseWait(pugi::xml_node el) +Wait parseWait(pugi::xml_node el, const ParseContext &context) { Wait out; for (pugi::xml_attribute a : el.attributes()) @@ -42,24 +43,25 @@ Wait parseWait(pugi::xml_node el) } if (aname == "player") { - out.setPlayer(Token::parse(a.value())); + out.setPlayer(parseValue(a.value(), context, el, "player")); } else if (aname == "time-only") { - out.setTimeOnly(TimeOnly::parse(a.value())); + out.setTimeOnly(parseValue(a.value(), context, el, "time-only")); } else { throwUnknownAttribute(el, a.name()); } } - parseWaitContent(out, el); + parseWaitContent(out, el, context); return out; } -void parseWaitContent(Wait &out, pugi::xml_node el) +void parseWaitContent(Wait &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Wait.h b/src/private/mx/core/generated/Wait.h index af2473fcc..73e2d8ecb 100644 --- a/src/private/mx/core/generated/Wait.h +++ b/src/private/mx/core/generated/Wait.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The wait type specifies a point where the accompaniment should wait for a performer event before /// continuing. This typically happens at the start of new sections or after a held note or /// indeterminate music. These waiting points cannot always be inferred reliably from the contents of @@ -36,9 +38,9 @@ class Wait final std::optional m_timeOnly; }; -Wait parseWait(pugi::xml_node el); +Wait parseWait(pugi::xml_node el, const ParseContext &context); -void parseWaitContent(Wait &out, pugi::xml_node el); +void parseWaitContent(Wait &out, pugi::xml_node el, const ParseContext &context); void serializeWait(const Wait &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/WavyLine.cpp b/src/private/mx/core/generated/WavyLine.cpp index 1eaec5487..5a026d2af 100644 --- a/src/private/mx/core/generated/WavyLine.cpp +++ b/src/private/mx/core/generated/WavyLine.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/WavyLine.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -170,7 +171,7 @@ void WavyLine::setLastBeat(std::optional value) m_lastBeat = std::move(value); } -WavyLine parseWavyLine(pugi::xml_node el) +WavyLine parseWavyLine(pugi::xml_node el, const ParseContext &context) { WavyLine out; bool seen_type = false; @@ -184,67 +185,67 @@ WavyLine parseWavyLine(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(StartStopContinue::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "number") { - out.setNumber(NumberLevel::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "smufl") { - out.setSmufl(SmuflWavyLineGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "placement") { - out.setPlacement(AboveBelow::parse(a.value())); + out.setPlacement(parseValue(a.value(), context, el, "placement")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "start-note") { - out.setStartNote(StartNote::parse(a.value())); + out.setStartNote(parseValue(a.value(), context, el, "start-note")); } else if (aname == "trill-step") { - out.setTrillStep(TrillStep::parse(a.value())); + out.setTrillStep(parseValue(a.value(), context, el, "trill-step")); } else if (aname == "two-note-turn") { - out.setTwoNoteTurn(TwoNoteTurn::parse(a.value())); + out.setTwoNoteTurn(parseValue(a.value(), context, el, "two-note-turn")); } else if (aname == "accelerate") { - out.setAccelerate(YesNo::parse(a.value())); + out.setAccelerate(parseValue(a.value(), context, el, "accelerate")); } else if (aname == "beats") { - out.setBeats(TrillBeats::parse(a.value())); + out.setBeats(parseValue(a.value(), context, el, "beats")); } else if (aname == "second-beat") { - out.setSecondBeat(Percent::parse(a.value())); + out.setSecondBeat(parseValue(a.value(), context, el, "second-beat")); } else if (aname == "last-beat") { - out.setLastBeat(Percent::parse(a.value())); + out.setLastBeat(parseValue(a.value(), context, el, "last-beat")); } else { @@ -255,13 +256,14 @@ WavyLine parseWavyLine(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseWavyLineContent(out, el); + parseWavyLineContent(out, el, context); return out; } -void parseWavyLineContent(WavyLine &out, pugi::xml_node el) +void parseWavyLineContent(WavyLine &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/WavyLine.h b/src/private/mx/core/generated/WavyLine.h index 155f677cd..0a90226a7 100644 --- a/src/private/mx/core/generated/WavyLine.h +++ b/src/private/mx/core/generated/WavyLine.h @@ -26,6 +26,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Wavy lines are one way to indicate trills and vibrato. When used with a barline element, they /// should always have type="continue" set. The smufl attribute specifies a particular wavy line /// glyph from the SMuFL Multi-segment lines range. @@ -85,9 +87,9 @@ class WavyLine final std::optional m_lastBeat; }; -WavyLine parseWavyLine(pugi::xml_node el); +WavyLine parseWavyLine(pugi::xml_node el, const ParseContext &context); -void parseWavyLineContent(WavyLine &out, pugi::xml_node el); +void parseWavyLineContent(WavyLine &out, pugi::xml_node el, const ParseContext &context); void serializeWavyLine(const WavyLine &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/Wedge.cpp b/src/private/mx/core/generated/Wedge.cpp index 4def125e5..732f032e5 100644 --- a/src/private/mx/core/generated/Wedge.cpp +++ b/src/private/mx/core/generated/Wedge.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Wedge.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -140,7 +141,7 @@ void Wedge::setID(std::optional value) m_id = std::move(value); } -Wedge parseWedge(pugi::xml_node el) +Wedge parseWedge(pugi::xml_node el, const ParseContext &context) { Wedge out; bool seen_type = false; @@ -154,55 +155,55 @@ Wedge parseWedge(pugi::xml_node el) if (aname == "type") { seen_type = true; - out.setType(WedgeType::parse(a.value())); + out.setType(parseValue(a.value(), context, el, "type")); } else if (aname == "number") { - out.setNumber(NumberLevel::parse(a.value())); + out.setNumber(parseValue(a.value(), context, el, "number")); } else if (aname == "spread") { - out.setSpread(Tenths::parse(a.value())); + out.setSpread(parseValue(a.value(), context, el, "spread")); } else if (aname == "niente") { - out.setNiente(YesNo::parse(a.value())); + out.setNiente(parseValue(a.value(), context, el, "niente")); } else if (aname == "line-type") { - out.setLineType(LineType::parse(a.value())); + out.setLineType(parseValue(a.value(), context, el, "line-type")); } else if (aname == "dash-length") { - out.setDashLength(Tenths::parse(a.value())); + out.setDashLength(parseValue(a.value(), context, el, "dash-length")); } else if (aname == "space-length") { - out.setSpaceLength(Tenths::parse(a.value())); + out.setSpaceLength(parseValue(a.value(), context, el, "space-length")); } else if (aname == "default-x") { - out.setDefaultX(Tenths::parse(a.value())); + out.setDefaultX(parseValue(a.value(), context, el, "default-x")); } else if (aname == "default-y") { - out.setDefaultY(Tenths::parse(a.value())); + out.setDefaultY(parseValue(a.value(), context, el, "default-y")); } else if (aname == "relative-x") { - out.setRelativeX(Tenths::parse(a.value())); + out.setRelativeX(parseValue(a.value(), context, el, "relative-x")); } else if (aname == "relative-y") { - out.setRelativeY(Tenths::parse(a.value())); + out.setRelativeY(parseValue(a.value(), context, el, "relative-y")); } else if (aname == "color") { - out.setColor(Color::parse(a.value())); + out.setColor(parseValue(a.value(), context, el, "color")); } else if (aname == "id") { - out.setID(Token::parse(a.value())); + out.setID(parseIdValue(a.value(), context, el, "id")); } else { @@ -213,13 +214,14 @@ Wedge parseWedge(pugi::xml_node el) { throwMissingAttribute(el, "type"); } - parseWedgeContent(out, el); + parseWedgeContent(out, el, context); return out; } -void parseWedgeContent(Wedge &out, pugi::xml_node el) +void parseWedgeContent(Wedge &out, pugi::xml_node el, const ParseContext &context) { (void)out; + (void)context; if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Wedge.h b/src/private/mx/core/generated/Wedge.h index 01d8c223d..359b16385 100644 --- a/src/private/mx/core/generated/Wedge.h +++ b/src/private/mx/core/generated/Wedge.h @@ -22,6 +22,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The wedge type represents crescendo and diminuendo wedge symbols. The type attribute is crescendo /// for the start of a wedge that is closed at the left side, and diminuendo for the start of a wedge /// that is closed on the right side. Spread values are measured in tenths; those at the start of a @@ -76,9 +78,9 @@ class Wedge final std::optional m_id; }; -Wedge parseWedge(pugi::xml_node el); +Wedge parseWedge(pugi::xml_node el, const ParseContext &context); -void parseWedgeContent(Wedge &out, pugi::xml_node el); +void parseWedgeContent(Wedge &out, pugi::xml_node el, const ParseContext &context); void serializeWedge(const Wedge &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/WedgeType.cpp b/src/private/mx/core/generated/WedgeType.cpp index 16d431dc7..2b7903ec6 100644 --- a/src/private/mx/core/generated/WedgeType.cpp +++ b/src/private/mx/core/generated/WedgeType.cpp @@ -54,9 +54,15 @@ bool WedgeType::tryParse(std::string_view text, WedgeType &out) noexcept } WedgeType WedgeType::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +WedgeType WedgeType::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { WedgeType v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/WedgeType.h b/src/private/mx/core/generated/WedgeType.h index 42a91ffa1..b640fe406 100644 --- a/src/private/mx/core/generated/WedgeType.h +++ b/src/private/mx/core/generated/WedgeType.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -47,6 +49,9 @@ class WedgeType final /// (the import leniency policy; never produces an invalid value). static WedgeType parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static WedgeType parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const WedgeType &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Winged.cpp b/src/private/mx/core/generated/Winged.cpp index 7db6bdc18..f532f2ca4 100644 --- a/src/private/mx/core/generated/Winged.cpp +++ b/src/private/mx/core/generated/Winged.cpp @@ -56,9 +56,15 @@ bool Winged::tryParse(std::string_view text, Winged &out) noexcept } Winged Winged::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +Winged Winged::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { Winged v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/Winged.h b/src/private/mx/core/generated/Winged.h index fc151a2cd..600b21e12 100644 --- a/src/private/mx/core/generated/Winged.h +++ b/src/private/mx/core/generated/Winged.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -49,6 +51,9 @@ class Winged final /// (the import leniency policy; never produces an invalid value). static Winged parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static Winged parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const Winged &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Wood.cpp b/src/private/mx/core/generated/Wood.cpp index cd8b2c6cd..7382168f3 100644 --- a/src/private/mx/core/generated/Wood.cpp +++ b/src/private/mx/core/generated/Wood.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Wood.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -30,7 +31,7 @@ void Wood::setValue(WoodValue value) m_value = std::move(value); } -Wood parseWood(pugi::xml_node el) +Wood parseWood(pugi::xml_node el, const ParseContext &context) { Wood out; for (pugi::xml_attribute a : el.attributes()) @@ -42,20 +43,20 @@ Wood parseWood(pugi::xml_node el) } if (aname == "smufl") { - out.setSmufl(SmuflPictogramGlyphName::parse(a.value())); + out.setSmufl(parseValue(a.value(), context, el, "smufl")); } else { throwUnknownAttribute(el, a.name()); } } - parseWoodContent(out, el); + parseWoodContent(out, el, context); return out; } -void parseWoodContent(Wood &out, pugi::xml_node el) +void parseWoodContent(Wood &out, pugi::xml_node el, const ParseContext &context) { - out.setValue(WoodValue::parse(childText(el))); + out.setValue(parseValue(childText(el), context, el, nullptr)); if (firstElement(el)) { throwUnknownElement(firstElement(el)); diff --git a/src/private/mx/core/generated/Wood.h b/src/private/mx/core/generated/Wood.h index a300eb2f1..1c054b584 100644 --- a/src/private/mx/core/generated/Wood.h +++ b/src/private/mx/core/generated/Wood.h @@ -16,6 +16,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// The wood type represents pictograms for wood percussion instruments. The smufl attribute is used /// to distinguish different SMuFL stylistic alternates. class Wood final @@ -32,9 +34,9 @@ class Wood final WoodValue m_value{}; }; -Wood parseWood(pugi::xml_node el); +Wood parseWood(pugi::xml_node el, const ParseContext &context); -void parseWoodContent(Wood &out, pugi::xml_node el); +void parseWoodContent(Wood &out, pugi::xml_node el, const ParseContext &context); void serializeWood(const Wood &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/WoodValue.cpp b/src/private/mx/core/generated/WoodValue.cpp index 957a0f370..db1b1004e 100644 --- a/src/private/mx/core/generated/WoodValue.cpp +++ b/src/private/mx/core/generated/WoodValue.cpp @@ -140,9 +140,15 @@ bool WoodValue::tryParse(std::string_view text, WoodValue &out) noexcept } WoodValue WoodValue::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +WoodValue WoodValue::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { WoodValue v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/WoodValue.h b/src/private/mx/core/generated/WoodValue.h index 8edbe6616..3896ee371 100644 --- a/src/private/mx/core/generated/WoodValue.h +++ b/src/private/mx/core/generated/WoodValue.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -79,6 +81,9 @@ class WoodValue final /// (the import leniency policy; never produces an invalid value). static WoodValue parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static WoodValue parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const WoodValue &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/Work.cpp b/src/private/mx/core/generated/Work.cpp index 5372ede37..64de16afd 100644 --- a/src/private/mx/core/generated/Work.cpp +++ b/src/private/mx/core/generated/Work.cpp @@ -3,6 +3,7 @@ #include "mx/core/generated/Work.h" #include "mx/core/Lexical.h" +#include "mx/core/ParseContext.h" #include "mx/core/Xml.h" #include @@ -40,7 +41,7 @@ void Work::setOpus(std::optional value) m_opus = std::move(value); } -Work parseWork(pugi::xml_node el) +Work parseWork(pugi::xml_node el, const ParseContext &context) { Work out; for (pugi::xml_attribute a : el.attributes()) @@ -52,11 +53,11 @@ Work parseWork(pugi::xml_node el) } throwUnknownAttribute(el, a.name()); } - parseWorkContent(out, el); + parseWorkContent(out, el, context); return out; } -void parseWorkContent(Work &out, pugi::xml_node el) +void parseWorkContent(Work &out, pugi::xml_node el, const ParseContext &context) { pugi::xml_node cursor = firstElement(el); if (cursorIs(cursor, "work-number")) @@ -71,7 +72,7 @@ void parseWorkContent(Work &out, pugi::xml_node el) } if (cursorIs(cursor, "opus")) { - out.setOpus(parseOpus(cursor)); + out.setOpus(parseOpus(cursor, context)); cursor = nextElement(cursor); } if (cursor) diff --git a/src/private/mx/core/generated/Work.h b/src/private/mx/core/generated/Work.h index 57bffda99..39990cc46 100644 --- a/src/private/mx/core/generated/Work.h +++ b/src/private/mx/core/generated/Work.h @@ -17,6 +17,8 @@ class xml_node; namespace mx::core { +class ParseContext; + /// Works are optionally identified by number and title. The work type also may indicate a link to /// the opus document that composes multiple scores into a collection. /// Content fields mirror the schema grammar in declaration order; the @@ -38,9 +40,9 @@ class Work final std::optional m_opus; }; -Work parseWork(pugi::xml_node el); +Work parseWork(pugi::xml_node el, const ParseContext &context); -void parseWorkContent(Work &out, pugi::xml_node el); +void parseWorkContent(Work &out, pugi::xml_node el, const ParseContext &context); void serializeWork(const Work &v, pugi::xml_node parent, const char *tag); diff --git a/src/private/mx/core/generated/YesNo.cpp b/src/private/mx/core/generated/YesNo.cpp index 2b3bceb94..11796f5fc 100644 --- a/src/private/mx/core/generated/YesNo.cpp +++ b/src/private/mx/core/generated/YesNo.cpp @@ -42,9 +42,15 @@ bool YesNo::tryParse(std::string_view text, YesNo &out) noexcept } YesNo YesNo::parse(std::string_view text) noexcept +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +YesNo YesNo::parse(std::string_view text, ValueParseOutcome &outcome) noexcept { YesNo v; - tryParse(text, v); + outcome = tryParse(text, v) ? ValueParseOutcome::valid : ValueParseOutcome::invalid; return v; } diff --git a/src/private/mx/core/generated/YesNo.h b/src/private/mx/core/generated/YesNo.h index 7b179a7de..50cd2ef61 100644 --- a/src/private/mx/core/generated/YesNo.h +++ b/src/private/mx/core/generated/YesNo.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include namespace mx::core @@ -41,6 +43,9 @@ class YesNo final /// (the import leniency policy; never produces an invalid value). static YesNo parse(std::string_view text) noexcept; + /// Lenient, and says whether the literal was recognized. + static YesNo parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const YesNo &other) const noexcept = default; private: diff --git a/src/private/mx/core/generated/YesNoNumber.cpp b/src/private/mx/core/generated/YesNoNumber.cpp index 22b7da4f4..ad9af77ec 100644 --- a/src/private/mx/core/generated/YesNoNumber.cpp +++ b/src/private/mx/core/generated/YesNoNumber.cpp @@ -59,12 +59,20 @@ bool YesNoNumber::tryParse(std::string_view text, YesNoNumber &out) } YesNoNumber YesNoNumber::parse(std::string_view text) +{ + ValueParseOutcome outcome = ValueParseOutcome::valid; + return parse(text, outcome); +} + +YesNoNumber YesNoNumber::parse(std::string_view text, ValueParseOutcome &outcome) { YesNoNumber out; if (tryParse(text, out)) { + outcome = ValueParseOutcome::valid; return out; } + outcome = ValueParseOutcome::invalid; return YesNoNumber::yesNo(YesNo::parse(text)); } diff --git a/src/private/mx/core/generated/YesNoNumber.h b/src/private/mx/core/generated/YesNoNumber.h index dece695ca..e13602bbf 100644 --- a/src/private/mx/core/generated/YesNoNumber.h +++ b/src/private/mx/core/generated/YesNoNumber.h @@ -3,6 +3,7 @@ #pragma once #include "mx/core/Decimal.h" +#include "mx/core/Lexical.h" #include "mx/core/generated/YesNo.h" #include @@ -69,6 +70,10 @@ class YesNoNumber final /// parse (never produces an invalid value). static YesNoNumber parse(std::string_view text); + /// Lenient, and says whether no member matched or a matched number was + /// clamped. + static YesNoNumber parse(std::string_view text, ValueParseOutcome &outcome); + bool operator==(const YesNoNumber &other) const = default; private: diff --git a/src/private/mx/core/generated/YyyyMmDd.cpp b/src/private/mx/core/generated/YyyyMmDd.cpp index 3180fb5b0..ff369cd26 100644 --- a/src/private/mx/core/generated/YyyyMmDd.cpp +++ b/src/private/mx/core/generated/YyyyMmDd.cpp @@ -130,4 +130,16 @@ YyyyMmDd YyyyMmDd::parse(std::string_view text) noexcept return out; } +YyyyMmDd YyyyMmDd::parse(std::string_view text, ValueParseOutcome &outcome) noexcept +{ + YyyyMmDd out; + if (tryParse(text, out)) + { + outcome = ValueParseOutcome::valid; + return out; + } + outcome = ValueParseOutcome::invalid; + return parse(text); +} + } // namespace mx::core diff --git a/src/private/mx/core/generated/YyyyMmDd.h b/src/private/mx/core/generated/YyyyMmDd.h index 675e33dd1..9c7f31845 100644 --- a/src/private/mx/core/generated/YyyyMmDd.h +++ b/src/private/mx/core/generated/YyyyMmDd.h @@ -2,6 +2,8 @@ #pragma once +#include "mx/core/Lexical.h" + #include #include @@ -51,6 +53,9 @@ class YyyyMmDd final /// components clamp. static YyyyMmDd parse(std::string_view text) noexcept; + /// Lenient, and says whether the text had to be repaired. + static YyyyMmDd parse(std::string_view text, ValueParseOutcome &outcome) noexcept; + bool operator==(const YyyyMmDd &other) const noexcept = default; private: diff --git a/src/private/mx/examples/Read.cpp b/src/private/mx/examples/Read.cpp index 6ccc76e3b..1ba18dda6 100644 --- a/src/private/mx/examples/Read.cpp +++ b/src/private/mx/examples/Read.cpp @@ -56,17 +56,17 @@ int main(int argc, const char *argv[]) // place the xml from above into a stream object std::istringstream istr{xml}; + // collect any non-fatal adjustments made while reading. A handler may + // also display each one as it is found. + Diagnostics diagnostics{[](const Diagnostic &diagnostic) { std::cerr << formatDiagnostic(diagnostic) << '\n'; }}; + // parse the xml into a MusicXml document that we own - auto docResult = MusicXml::fromStream(istr); + auto docResult = MusicXml::fromStream(istr, diagnostics); if (!docResult.ok()) { return MX_IS_A_FAILURE; } - // collect any non-fatal adjustments made while reading. A handler may - // also display each one as it is found. - Diagnostics diagnostics{[](const Diagnostic &diagnostic) { std::cerr << formatDiagnostic(diagnostic) << '\n'; }}; - // take the score out of the document. intoScore also consumes the // document, so its memory is freed as the function returns const auto scoreResult = intoScore(std::move(docResult).value(), diagnostics); diff --git a/src/private/mx/impl/Cursor.cpp b/src/private/mx/impl/Cursor.cpp index a67ec560e..cb6f99983 100644 --- a/src/private/mx/impl/Cursor.cpp +++ b/src/private/mx/impl/Cursor.cpp @@ -43,16 +43,20 @@ int Cursor::convertDurationToGlobalTickScale(const core::PositiveDivisions &dura } int Cursor::convertDurationToGlobalTickScale(double durationValue) const +{ + return static_cast(std::ceil(convertDurationToExactGlobalTicks(durationValue) - 0.5)); +} + +double Cursor::convertDurationToExactGlobalTicks(double durationValue) const { if (this->ticksPerQuarter == this->getGlobalTicksPerQuarter()) { - return static_cast(std::ceil(durationValue - 0.5)); + return durationValue; } const double currentTicksPerQuarter = static_cast(this->ticksPerQuarter); const double globalTicksPerQuarter = static_cast(this->getGlobalTicksPerQuarter()); - const double convertedVal = durationValue * (globalTicksPerQuarter / currentTicksPerQuarter); - return static_cast(std::ceil(convertedVal - 0.5)); + return durationValue * (globalTicksPerQuarter / currentTicksPerQuarter); } int Cursor::convertDurationToGlobalTickScale(int durationValue) const diff --git a/src/private/mx/impl/Cursor.h b/src/private/mx/impl/Cursor.h index 5c626fc3a..82370896f 100644 --- a/src/private/mx/impl/Cursor.h +++ b/src/private/mx/impl/Cursor.h @@ -52,6 +52,8 @@ class Cursor int convertDurationToGlobalTickScale(const core::PositiveDivisions &duration) const; int convertDurationToGlobalTickScale(double durationValue) const; + // The duration in global ticks before it is rounded to a whole tick. + double convertDurationToExactGlobalTicks(double durationValue) const; int convertDurationToGlobalTickScale(int duration) const; private: diff --git a/src/private/mx/impl/CurveFunctions.h b/src/private/mx/impl/CurveFunctions.h index 7e68f0aa9..2f27fecc7 100644 --- a/src/private/mx/impl/CurveFunctions.h +++ b/src/private/mx/impl/CurveFunctions.h @@ -168,11 +168,12 @@ template api::CurveStop parseCurveStop(const // never look at the SpannerNumber directly. template void writeAttributesFromCurveStart(const api::CurveStart inCurve, ATTRIBUTES_TYPE &outAttributes, - const std::optional &inResolvedNumber) + const std::optional &inResolvedNumber, + const DiagnosticsContext &diagnostics = {}, const api::Location &location = {}) { using CurveTypeAttribute = std::decay_t; outAttributes.setType(CurveTypeAttribute::start()); - impl::setId(inCurve.id, outAttributes); + impl::setId(inCurve.id, outAttributes, diagnostics, location); impl::setAttributesFromPositionData(inCurve.curvePoints.positionData, outAttributes); impl::setAttributesFromLineData(inCurve.lineData, outAttributes); @@ -212,11 +213,12 @@ void writeAttributesFromCurveStart(const api::CurveStart inCurve, ATTRIBUTES_TYP template void writeAttributesFromCurveContinue(const api::CurveContinue inCurve, ATTRIBUTES_TYPE &outAttributes, - const std::optional &inResolvedNumber) + const std::optional &inResolvedNumber, + const DiagnosticsContext &diagnostics = {}, const api::Location &location = {}) { using CurveTypeAttribute = std::decay_t; outAttributes.setType(CurveTypeAttribute::continue_()); - impl::setId(inCurve.id, outAttributes); + impl::setId(inCurve.id, outAttributes, diagnostics, location); impl::setAttributesFromPositionData(inCurve.curvePoints.positionData, outAttributes); if (inResolvedNumber.has_value()) @@ -258,11 +260,12 @@ void writeAttributesFromCurveContinue(const api::CurveContinue inCurve, ATTRIBUT template void writeAttributesFromCurveStop(const api::CurveStop inCurve, ATTRIBUTES_TYPE &outAttributes, - const std::optional &inResolvedNumber) + const std::optional &inResolvedNumber, + const DiagnosticsContext &diagnostics = {}, const api::Location &location = {}) { using CurveTypeAttribute = std::decay_t; outAttributes.setType(CurveTypeAttribute::stop()); - impl::setId(inCurve.id, outAttributes); + impl::setId(inCurve.id, outAttributes, diagnostics, location); impl::setAttributesFromPositionData(inCurve.curvePoints.positionData, outAttributes); if (inResolvedNumber.has_value()) @@ -289,11 +292,13 @@ void writeAttributesFromCurveStop(const api::CurveStop inCurve, ATTRIBUTES_TYPE // Emits a lone from an api::TieLetRing, carrying its // shared visual attributes (position, orientation, color). -inline void writeAttributesFromTieLetRing(const api::TieLetRing &inTie, core::Tied &outTied) +inline void writeAttributesFromTieLetRing(const api::TieLetRing &inTie, core::Tied &outTied, + const DiagnosticsContext &diagnostics = {}, + const api::Location &location = {}) { outTied.setType(core::TiedType::letRing()); impl::setAttributesFromPositionData(inTie.positionData, outTied); - impl::setId(inTie.id, outTied); + impl::setId(inTie.id, outTied, diagnostics, location); if (inTie.isColorSpecified) { diff --git a/src/private/mx/impl/DiagnosticsContext.h b/src/private/mx/impl/DiagnosticsContext.h index c0ce5662c..6f66ec295 100644 --- a/src/private/mx/impl/DiagnosticsContext.h +++ b/src/private/mx/impl/DiagnosticsContext.h @@ -5,11 +5,14 @@ #pragma once #include "mx/api/Diagnostics.h" +#include "mx/core/Lexical.h" #include "mx/impl/MeasureCursor.h" +#include #include #include #include +#include #include namespace mx @@ -35,19 +38,83 @@ class DiagnosticsContext } void report(api::Severity severity, api::DiagnosticCode code, const MeasureCursor &cursor, - std::string message) const - { - api::Location location; - location.partIndex = cursor.partIndex; - location.measureIndex = cursor.measureIndex; - location.staffIndex = cursor.staffIndex; - location.voiceIndex = cursor.voiceIndex; - location.tickTimePosition = cursor.tickTimePosition; - report(severity, code, std::move(location), std::move(message)); - } + std::string message) const; private: std::shared_ptr> myDiagnostics; }; + +// The full score position of a cursor. +inline api::Location cursorLocation(const MeasureCursor &cursor) +{ + api::Location location; + location.partIndex = cursor.partIndex; + location.measureIndex = cursor.measureIndex; + location.staffIndex = cursor.staffIndex; + location.voiceIndex = cursor.voiceIndex; + location.tickTimePosition = cursor.tickTimePosition; + return location; +} + +inline void DiagnosticsContext::report(api::Severity severity, api::DiagnosticCode code, const MeasureCursor &cursor, + std::string message) const +{ + report(severity, code, cursorLocation(cursor), std::move(message)); +} + +// The part, measure and tick of a cursor, for a report that is not about one staff or voice. +inline api::Location measureLocation(const MeasureCursor &cursor) +{ + api::Location location; + location.partIndex = cursor.partIndex; + location.measureIndex = cursor.measureIndex; + location.tickTimePosition = cursor.tickTimePosition; + return location; +} + +// The part and measure of a cursor, for a report about a whole measure. +inline api::Location measureOnlyLocation(const MeasureCursor &cursor) +{ + api::Location location; + location.partIndex = cursor.partIndex; + location.measureIndex = cursor.measureIndex; + return location; +} + +// The location of a report about a whole part. +inline api::Location partLocation(int partIndex) +{ + api::Location location; + location.partIndex = partIndex; + return location; +} + +inline std::string formatReportedNumber(double value) +{ + return core::formatDouble(value); +} + +inline std::string formatReportedNumber(long long value) +{ + return std::to_string(value); +} + +// Reports when a core value mx built to write differs from the api value it was built from, because +// the core type holds a narrower range. Pass the value read back from the built core value. +template +bool reportAdjusted(const DiagnosticsContext &diagnostics, api::Location location, const std::string &name, T apiValue, + T writtenValue) +{ + const bool isAdjusted = + std::is_floating_point_v ? std::abs(apiValue - writtenValue) > 1e-9 : apiValue != writtenValue; + if (isAdjusted) + { + using Format = std::conditional_t, double, long long>; + diagnostics.report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, std::move(location), + name + " " + formatReportedNumber(static_cast(apiValue)) + + " is out of range; using " + formatReportedNumber(static_cast(writtenValue))); + } + return isAdjusted; +} } // namespace impl } // namespace mx diff --git a/src/private/mx/impl/DirectionReader.cpp b/src/private/mx/impl/DirectionReader.cpp index c951bd9b9..c79a07900 100644 --- a/src/private/mx/impl/DirectionReader.cpp +++ b/src/private/mx/impl/DirectionReader.cpp @@ -4,6 +4,7 @@ #include "mx/impl/DirectionReader.h" #include "mx/api/WedgeData.h" +#include "mx/core/Lexical.h" #include "mx/core/generated/Accord.h" #include "mx/core/generated/AccordionRegistration.h" #include "mx/core/generated/Barre.h" @@ -98,16 +99,33 @@ namespace mx { namespace impl { -DirectionReader::DirectionReader(const core::Direction &inDirection, Cursor inCursor) - : myDirection{&inDirection}, myHarmony{nullptr}, myCursor{inCursor}, myConverter{}, myOutDirectionData{} +DirectionReader::DirectionReader(const core::Direction &inDirection, MeasureCursor inCursor, + DiagnosticsContext diagnostics) + : myDirection{&inDirection}, myHarmony{nullptr}, myCursor{inCursor}, myConverter{}, myDiagnostics{diagnostics}, + myOutDirectionData{} { } -DirectionReader::DirectionReader(const core::Harmony &inHarmony, Cursor inCursor) - : myDirection{nullptr}, myHarmony{&inHarmony}, myCursor{inCursor}, myConverter{}, myOutDirectionData{} +DirectionReader::DirectionReader(const core::Harmony &inHarmony, MeasureCursor inCursor, DiagnosticsContext diagnostics) + : myDirection{nullptr}, myHarmony{&inHarmony}, myCursor{inCursor}, myConverter{}, myDiagnostics{diagnostics}, + myOutDirectionData{} { } +void DirectionReader::report(api::Severity severity, api::DiagnosticCode code, std::string message) const +{ + myDiagnostics.report(severity, code, measureLocation(myCursor), std::move(message)); +} + +void DirectionReader::reportRounded(const char *name, double value, int rounded) const +{ + if (value != static_cast(rounded)) + { + report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, + std::string{name} + " " + core::formatDouble(value) + " rounded to " + std::to_string(rounded)); + } +} + api::DirectionData DirectionReader::getDirectionData() { myOutDirectionData = initializeData(); @@ -149,6 +167,7 @@ void DirectionReader::parseOffset() { const auto rawVal = myDirection->offset()->value().value().value(); myOutDirectionData.offset = static_cast(std::ceil(rawVal - 0.5)); + reportRounded("offset", rawVal, *myOutDirectionData.offset); } } else if (myHarmony) @@ -157,6 +176,7 @@ void DirectionReader::parseOffset() { const auto rawVal = myHarmony->offset()->value().value().value(); myOutDirectionData.offset = static_cast(std::ceil(rawVal - 0.5)); + reportRounded("offset", rawVal, *myOutDirectionData.offset); } } } @@ -231,10 +251,16 @@ void DirectionReader::parseValues() if (myDirection->editorialVoiceDirection().voice().has_value()) { int parsedVoice = api::VALUE_UNSPECIFIED; - if (utility::stringToInt(myDirection->editorialVoiceDirection().voice()->c_str(), parsedVoice)) + const std::string voiceText = *myDirection->editorialVoiceDirection().voice(); + if (utility::stringToInt(voiceText, parsedVoice)) { myOutDirectionData.voice = parsedVoice; } + else + { + report(api::Severity::warning, api::DiagnosticCode::invalidValue, + "direction voice \"" + voiceText + "\" is not a number; it is not read"); + } } } else if (myHarmony) @@ -747,6 +773,13 @@ void DirectionReader::parseOctaveShift(const core::DirectionType &directionType) amount = *octaveShift.size(); } + if (amount != 8 && amount != 15 && amount != 22) + { + const int used = amount >= 22 ? 22 : (amount > 8 ? 15 : 8); + report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, + "octave-shift size " + std::to_string(amount) + " is not 8, 15 or 22; using " + std::to_string(used)); + } + // Per the MusicXML spec, octave-shift's type attribute describes the direction the // *written* notes are shifted from the true (sounding) pitch: an 8va, which sounds an // octave above what is written, is encoded as type="down" (notes are written below true @@ -1188,7 +1221,9 @@ void DirectionReader::parseHarmony(const core::Harmony &inHarmony, const core::H if (root.rootAlter().has_value()) { - chord.rootAlter = mx::utility::roundTo(root.rootAlter()->value().value().value()); + const double rawAlter = root.rootAlter()->value().value().value(); + chord.rootAlter = mx::utility::roundTo(rawAlter); + reportRounded("root-alter", rawAlter, chord.rootAlter); } break; } @@ -1205,7 +1240,9 @@ void DirectionReader::parseHarmony(const core::Harmony &inHarmony, const core::H if (numeral.numeralAlter().has_value()) { chord.hasNumeralAlter = true; - chord.numeralAlter = mx::utility::roundTo(numeral.numeralAlter()->value().value().value()); + const double rawAlter = numeral.numeralAlter()->value().value().value(); + chord.numeralAlter = mx::utility::roundTo(rawAlter); + reportRounded("numeral-alter", rawAlter, chord.numeralAlter); } if (numeral.numeralKey().has_value()) @@ -1291,7 +1328,9 @@ void DirectionReader::parseHarmony(const core::Harmony &inHarmony, const core::H if (bass.bassAlter().has_value()) { - chord.bassAlter = mx::utility::roundTo(bass.bassAlter()->value().value().value()); + const double rawAlter = bass.bassAlter()->value().value().value(); + chord.bassAlter = mx::utility::roundTo(rawAlter); + reportRounded("bass-alter", rawAlter, chord.bassAlter); } } @@ -1309,9 +1348,21 @@ void DirectionReader::parseHarmony(const core::Harmony &inHarmony, const core::H bool doAddExtension = true; const auto typeVal = degree.degreeType().value(); - const auto alter = mx::utility::roundTo(degree.degreeAlter().value().value().value()); + const double rawAlter = degree.degreeAlter().value().value().value(); + const auto alter = mx::utility::roundTo(rawAlter); const auto value = degree.degreeValue().value(); + if (alter < -2 || alter > 2) + { + report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, + "degree-alter " + core::formatDouble(rawAlter) + " is out of range; using " + + std::to_string(alter < 0 ? -2 : 2)); + } + else + { + reportRounded("degree-alter", rawAlter, alter); + } + switch (typeVal.tag()) { case core::DegreeTypeValue::Tag::alter: @@ -1382,6 +1433,8 @@ void DirectionReader::parseHarmony(const core::Harmony &inHarmony, const core::H break; default: doAddExtension = false; + report(api::Severity::error, api::DiagnosticCode::droppedData, + "degree-value " + std::to_string(value) + " is not supported; the degree is not read"); break; } @@ -1436,14 +1489,28 @@ void DirectionReader::parseHarmony(const core::Harmony &inHarmony, const core::H if (frameNote.fingering().has_value()) { const auto fingeringText = frameNote.fingering()->value(); + std::size_t length = 0; try { - frameNoteData.fingering = std::stoi(fingeringText); + frameNoteData.fingering = std::stoi(fingeringText, &length); frameNoteData.isFingeringSpecified = true; } catch (...) { } + + // reported outside the try so a throwing diagnostic handler is not swallowed + if (!frameNoteData.isFingeringSpecified) + { + report(api::Severity::error, api::DiagnosticCode::droppedData, + "frame-note fingering \"" + fingeringText + "\" is not a number; it is not read"); + } + else if (length != fingeringText.size()) + { + report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, + "frame-note fingering \"" + fingeringText + "\" is not a number; using " + + std::to_string(frameNoteData.fingering)); + } } if (frameNote.barre().has_value()) diff --git a/src/private/mx/impl/DirectionReader.h b/src/private/mx/impl/DirectionReader.h index 392f86e8e..b28f26f3d 100644 --- a/src/private/mx/impl/DirectionReader.h +++ b/src/private/mx/impl/DirectionReader.h @@ -12,7 +12,8 @@ #include "mx/core/generated/HarmonyChordGroup.h" #include "mx/core/generated/PercussionChoice.h" #include "mx/impl/Converter.h" -#include "mx/impl/Cursor.h" +#include "mx/impl/DiagnosticsContext.h" +#include "mx/impl/MeasureCursor.h" #include "mx/impl/PositionFunctions.h" #include @@ -25,19 +26,22 @@ namespace impl class DirectionReader { public: - DirectionReader(const core::Direction &inDirection, Cursor inCursor); - DirectionReader(const core::Harmony &inHarmony, Cursor inCursor); + DirectionReader(const core::Direction &inDirection, MeasureCursor inCursor, DiagnosticsContext diagnostics = {}); + DirectionReader(const core::Harmony &inHarmony, MeasureCursor inCursor, DiagnosticsContext diagnostics = {}); api::DirectionData getDirectionData(); private: const core::Direction *const myDirection; const core::Harmony *const myHarmony; - const Cursor myCursor; + const MeasureCursor myCursor; const Converter myConverter; + DiagnosticsContext myDiagnostics; api::DirectionData myOutDirectionData; private: mx::api::DirectionData initializeData(); + void report(api::Severity severity, api::DiagnosticCode code, std::string message) const; + void reportRounded(const char *name, double value, int rounded) const; void parseOffset(); void parsePlacement(); void parseSystemRelation(); diff --git a/src/private/mx/impl/DirectionWriter.cpp b/src/private/mx/impl/DirectionWriter.cpp index 3c3980dd5..cbab43f73 100644 --- a/src/private/mx/impl/DirectionWriter.cpp +++ b/src/private/mx/impl/DirectionWriter.cpp @@ -145,10 +145,10 @@ static void applyBracketLineData(const api::LineData &lineData, core::Bracket &b } } -DirectionWriter::DirectionWriter(const api::DirectionData &inDirectionData, const Cursor &inCursor, - const SpannerResolver &inSpannerResolver) +DirectionWriter::DirectionWriter(const api::DirectionData &inDirectionData, const MeasureCursor &inCursor, + const SpannerResolver &inSpannerResolver, DiagnosticsContext diagnostics) : myDirectionData{inDirectionData}, myCursor{inCursor}, mySpannerResolver{inSpannerResolver}, myConverter{}, - myPlacements{}, myIsFirstDirectionTypeAdded{false} + myDiagnostics{std::move(diagnostics)}, myPlacements{}, myIsFirstDirectionTypeAdded{false} { } @@ -172,7 +172,7 @@ std::vector DirectionWriter::getDirectionLikeThings() { direction.setDirective(myConverter.convert(myDirectionData.directive)); } - setId(myDirectionData.id, direction); + setId(myDirectionData.id, direction, myDiagnostics, cursorLocation(myCursor)); if (myDirectionData.isStaffValueSpecified || myCursor.staffIndex != 0) { @@ -225,7 +225,7 @@ std::vector DirectionWriter::getDirectionLikeThings() if (myDirectionData.isSoundDataSpecified && myDirectionData.soundData.isSpecified()) { core::Sound sound{}; - writeSoundData(myDirectionData.soundData, sound); + writeSoundData(myDirectionData.soundData, sound, myDiagnostics, cursorLocation(myCursor)); direction.setSound(std::move(sound)); } @@ -235,7 +235,7 @@ std::vector DirectionWriter::getDirectionLikeThings() { // The direction has no other content; emit a standalone element. core::Sound sound{}; - writeSoundData(myDirectionData.soundData, sound); + writeSoundData(myDirectionData.soundData, sound, myDiagnostics, cursorLocation(myCursor)); if (offset != 0) { @@ -268,7 +268,7 @@ void DirectionWriter::emitMark(api::MarkData mark, core::Direction &direction) // if !isDirection( mark ) continue; if (isMarkDynamic(mark.markType)) { - DynamicsWriter dynamicsWriter{mark, myCursor}; + DynamicsWriter dynamicsWriter{mark, myCursor, myDiagnostics}; core::OneOrMore dynamicsSet{dynamicsWriter.getDynamics()}; core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::dynamics(dynamicsSet)); @@ -301,6 +301,12 @@ void DirectionWriter::emitMark(api::MarkData mark, core::Direction &direction) dt.setChoice(core::DirectionTypeChoice::pedal(pedal)); addDirectionType(std::move(dt), direction); } + + if (!isMarkDynamic(mark.markType) && !isMarkPedal(mark.markType)) + { + myDiagnostics.report(api::Severity::error, api::DiagnosticCode::droppedData, cursorLocation(myCursor), + "a direction mark that is not a dynamic or pedal is not written"); + } } core::PedalType corePedalType(api::PedalLineKind kind) @@ -339,7 +345,7 @@ void DirectionWriter::emitPedal(const api::PedalLineData &item, core::Direction pedal.setType(corePedalType(item.kind)); pedal.setLine(core::YesNo::yes()); setAttributesFromPositionData(item.positionData, pedal); - setId(item.id, pedal); + setId(item.id, pedal, myDiagnostics, cursorLocation(myCursor)); core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::pedal(pedal)); addDirectionType(std::move(dt), direction); @@ -361,7 +367,7 @@ void DirectionWriter::emitWedgeStop(const api::WedgeStop &wedgeStop, const void wedge.setSpread(core::Tenths{core::Decimal{static_cast(wedgeStop.spread)}}); } setAttributesFromPositionData(wedgeStop.positionData, wedge); - setId(wedgeStop.id, wedge); + setId(wedgeStop.id, wedge, myDiagnostics, cursorLocation(myCursor)); core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::wedge(wedge)); addDirectionType(std::move(dt), direction); @@ -376,6 +382,11 @@ void DirectionWriter::emitWedgeStart(const api::WedgeStart &wedgeStart, const vo { wedge.setType(myConverter.convert(wedgeStart.wedgeType)); } + else + { + myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::missingValueDefaulted, + cursorLocation(myCursor), "wedge type is unspecified; using crescendo"); + } const auto number = mySpannerResolver.emittedNumber(wedgeStart.number, inIdentity); if (number.has_value()) @@ -394,7 +405,7 @@ void DirectionWriter::emitWedgeStart(const api::WedgeStart &wedgeStart, const vo { setAttributesFromColorData(wedgeStart.colorData, wedge); } - setId(wedgeStart.id, wedge); + setId(wedgeStart.id, wedge, myDiagnostics, cursorLocation(myCursor)); core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::wedge(wedge)); addDirectionType(std::move(dt), direction); @@ -405,7 +416,8 @@ void DirectionWriter::emitOttavaStop(const api::OttavaStop &ottavaStop, const vo { core::OctaveShift os{}; setAttributesFromSpannerStop(ottavaStop.spannerStop, os, - mySpannerResolver.emittedNumber(ottavaStop.spannerStop.number, inIdentity)); + mySpannerResolver.emittedNumber(ottavaStop.spannerStop.number, inIdentity), + myDiagnostics, cursorLocation(myCursor)); os.setType(core::UpDownStopContinue::stop()); // The stop has no size of its own; it inherits the size of the start it closes, which the @@ -429,7 +441,7 @@ void DirectionWriter::emitOttavaStart(const api::OttavaStart &ottavaStart, const impl::setAttributesFromPositionData(ottavaStart.spannerStart.positionData, os); impl::setAttributesFromPrintData(ottavaStart.spannerStart.printData, os); impl::setAttributesFromLineData(ottavaStart.spannerStart.lineData, os); - impl::setId(ottavaStart.spannerStart.id, os); + impl::setId(ottavaStart.spannerStart.id, os, myDiagnostics, cursorLocation(myCursor)); const auto number = mySpannerResolver.emittedNumber(ottavaStart.spannerStart.number, inIdentity); if (number.has_value()) @@ -454,6 +466,8 @@ void DirectionWriter::emitOttavaStart(const api::OttavaStart &ottavaStart, const break; default: + myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::missingValueDefaulted, + cursorLocation(myCursor), "octave-shift type is unspecified; using up"); break; } @@ -475,7 +489,8 @@ void DirectionWriter::emitBracketStart(const api::SpannerStart &item, const void core::Direction &direction) { core::Bracket bracket{}; - setAttributesFromSpannerStart(item, bracket, mySpannerResolver.emittedNumber(item.number, inIdentity)); + setAttributesFromSpannerStart(item, bracket, mySpannerResolver.emittedNumber(item.number, inIdentity), + myDiagnostics, cursorLocation(myCursor)); bracket.setType(core::StartStopContinue::start()); setAttributesFromPositionData(item.positionData, bracket); setAttributesFromPrintData(item.printData, bracket); @@ -488,7 +503,8 @@ void DirectionWriter::emitBracketStart(const api::SpannerStart &item, const void void DirectionWriter::emitBracketStop(const api::SpannerStop &item, const void *inIdentity, core::Direction &direction) { core::Bracket bracket{}; - setAttributesFromSpannerStop(item, bracket, mySpannerResolver.emittedNumber(item.number, inIdentity)); + setAttributesFromSpannerStop(item, bracket, mySpannerResolver.emittedNumber(item.number, inIdentity), myDiagnostics, + cursorLocation(myCursor)); bracket.setType(core::StartStopContinue::stop()); applyBracketLineData(item.lineData, bracket, myConverter); core::DirectionType dt{}; @@ -499,7 +515,8 @@ void DirectionWriter::emitBracketStop(const api::SpannerStop &item, const void * void DirectionWriter::emitDashesStart(const api::SpannerStart &item, const void *inIdentity, core::Direction &direction) { core::Dashes dashes{}; - setAttributesFromSpannerStart(item, dashes, mySpannerResolver.emittedNumber(item.number, inIdentity)); + setAttributesFromSpannerStart(item, dashes, mySpannerResolver.emittedNumber(item.number, inIdentity), myDiagnostics, + cursorLocation(myCursor)); dashes.setType(core::StartStopContinue::start()); setAttributesFromPositionData(item.positionData, dashes); setAttributesFromPrintData(item.printData, dashes); @@ -512,7 +529,8 @@ void DirectionWriter::emitDashesStart(const api::SpannerStart &item, const void void DirectionWriter::emitDashesStop(const api::SpannerStop &item, const void *inIdentity, core::Direction &direction) { core::Dashes dashes{}; - setAttributesFromSpannerStop(item, dashes, mySpannerResolver.emittedNumber(item.number, inIdentity)); + setAttributesFromSpannerStop(item, dashes, mySpannerResolver.emittedNumber(item.number, inIdentity), myDiagnostics, + cursorLocation(myCursor)); dashes.setType(core::StartStopContinue::stop()); core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::dashes(dashes)); @@ -696,7 +714,7 @@ void DirectionWriter::emitTempo(const api::TempoData &tempo, core::Direction &di { setAttributesFromColorData(*tempo.color, metronome); } - setId(tempo.id, metronome); + setId(tempo.id, metronome, myDiagnostics, cursorLocation(myCursor)); if (tempo.justify != api::HorizontalAlignment::unspecified) { metronome.setJustify(myConverter.convert(tempo.justify)); @@ -750,7 +768,7 @@ void DirectionWriter::emitWordsRun(const std::vector &inRun, c { outSymbol.setJustify(myConverter.convert(symbolData.justify)); } - setId(symbolData.id, outSymbol); + setId(symbolData.id, outSymbol, myDiagnostics, cursorLocation(myCursor)); choiceItem = core::DirectionTypeChoiceChoice::symbol(std::move(outSymbol)); } else @@ -772,7 +790,7 @@ void DirectionWriter::emitWordsRun(const std::vector &inRun, c { outWords.setJustify(myConverter.convert(wordsData.justify)); } - setId(wordsData.id, outWords); + setId(wordsData.id, outWords, myDiagnostics, cursorLocation(myCursor)); choiceItem = core::DirectionTypeChoiceChoice::words(std::move(outWords)); } @@ -805,7 +823,7 @@ void DirectionWriter::emitSegno(const api::SegnoData &item, core::Direction &dir { segno.setSmufl(core::SmuflSegnoGlyphName::parse(item.smufl)); } - setId(item.id, segno); + setId(item.id, segno, myDiagnostics, cursorLocation(myCursor)); core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::segno(core::OneOrMore{std::move(segno)})); addDirectionType(std::move(dt), direction); @@ -824,7 +842,7 @@ void DirectionWriter::emitCoda(const api::CodaData &item, core::Direction &direc { coda.setSmufl(core::SmuflCodaGlyphName::parse(item.smufl)); } - setId(item.id, coda); + setId(item.id, coda, myDiagnostics, cursorLocation(myCursor)); core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::coda(core::OneOrMore{std::move(coda)})); addDirectionType(std::move(dt), direction); @@ -848,7 +866,7 @@ void DirectionWriter::emitRehearsal(const api::RehearsalData &item, core::Direct { rehearsal.setJustify(myConverter.convert(item.justify)); } - setId(item.id, rehearsal); + setId(item.id, rehearsal, myDiagnostics, cursorLocation(myCursor)); core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::rehearsal(core::OneOrMore{std::move(rehearsal)})); addDirectionType(std::move(dt), direction); @@ -866,7 +884,7 @@ core::EmptyPrintStyleAlignID DirectionWriter::createEmptyPrintStyleAlign(const a { setAttributesFromColorData(*color, element); } - setId(id, element); + setId(id, element, myDiagnostics, cursorLocation(myCursor)); return element; } @@ -904,7 +922,7 @@ void DirectionWriter::emitStringMute(const api::StringMuteData &item, core::Dire { setAttributesFromColorData(*item.color, stringMute); } - setId(item.id, stringMute); + setId(item.id, stringMute, myDiagnostics, cursorLocation(myCursor)); core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::stringMute(std::move(stringMute))); addDirectionType(std::move(dt), direction); @@ -931,7 +949,7 @@ void DirectionWriter::emitStaffDivide(const api::StaffDivideData &item, core::Di { setAttributesFromColorData(*item.color, staffDivide); } - setId(item.id, staffDivide); + setId(item.id, staffDivide, myDiagnostics, cursorLocation(myCursor)); core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::staffDivide(std::move(staffDivide))); addDirectionType(std::move(dt), direction); @@ -964,7 +982,7 @@ void DirectionWriter::emitPrincipalVoice(const api::PrincipalVoiceData &item, co { setAttributesFromColorData(*item.color, principalVoice); } - setId(item.id, principalVoice); + setId(item.id, principalVoice, myDiagnostics, cursorLocation(myCursor)); core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::principalVoice(std::move(principalVoice))); addDirectionType(std::move(dt), direction); @@ -988,7 +1006,7 @@ void DirectionWriter::emitOtherDirection(const api::OtherDirectionData &item, co { setAttributesFromColorData(*item.color, otherDirection); } - setId(item.id, otherDirection); + setId(item.id, otherDirection, myDiagnostics, cursorLocation(myCursor)); core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::otherDirection(std::move(otherDirection))); addDirectionType(std::move(dt), direction); @@ -1027,7 +1045,7 @@ void DirectionWriter::emitImage(const api::ImageData &item, core::Direction &dir default: break; } - setId(item.id, image); + setId(item.id, image, myDiagnostics, cursorLocation(myCursor)); core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::image(std::move(image))); addDirectionType(std::move(dt), direction); @@ -1048,7 +1066,7 @@ void DirectionWriter::emitAccordionRegistration(const api::AccordionRegistration { setAttributesFromColorData(*item.color, accordion); } - setId(item.id, accordion); + setId(item.id, accordion, myDiagnostics, cursorLocation(myCursor)); core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::accordionRegistration(std::move(accordion))); addDirectionType(std::move(dt), direction); @@ -1067,6 +1085,11 @@ void DirectionWriter::emitHarpPedals(const api::HarpPedalsData &item, core::Dire for (const auto &tuning : item.pedalTunings) { core::PedalTuning pedalTuning{}; + if (tuning.step == api::Step::unspecified) + { + myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::missingValueDefaulted, + cursorLocation(myCursor), "pedal-step is unspecified; using C"); + } pedalTuning.setPedalStep(myConverter.convert(tuning.step)); pedalTuning.setPedalAlter( core::Semitones{core::Decimal{Converter::convertToAlter(tuning.alter, tuning.cents)}}); @@ -1086,7 +1109,7 @@ void DirectionWriter::emitHarpPedals(const api::HarpPedalsData &item, core::Dire { setAttributesFromColorData(*item.color, harpPedals); } - setId(item.id, harpPedals); + setId(item.id, harpPedals, myDiagnostics, cursorLocation(myCursor)); core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::harpPedals(std::move(harpPedals))); addDirectionType(std::move(dt), direction); @@ -1110,13 +1133,21 @@ void DirectionWriter::emitScordatura(const api::ScordaturaData &item, core::Dire accord.setString(core::StringNumber{*accordData.stringNumber}); } core::TuningGroup tuning{}; + if (accordData.tuningStep == api::Step::unspecified) + { + myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::missingValueDefaulted, + cursorLocation(myCursor), "accord tuning-step is unspecified; using C"); + } tuning.setTuningStep(myConverter.convert(accordData.tuningStep)); if (accordData.tuningAlter != 0 || accordData.tuningCents != 0.0) { tuning.setTuningAlter(core::Semitones{ core::Decimal{Converter::convertToAlter(accordData.tuningAlter, accordData.tuningCents)}}); } - tuning.setTuningOctave(core::Octave{accordData.tuningOctave}); + const core::Octave tuningOctave{accordData.tuningOctave}; + reportAdjusted(myDiagnostics, cursorLocation(myCursor), "accord tuning-octave", accordData.tuningOctave, + tuningOctave.value()); + tuning.setTuningOctave(tuningOctave); accord.setTuning(std::move(tuning)); if (!isFirstAccordAdded) { @@ -1128,7 +1159,7 @@ void DirectionWriter::emitScordatura(const api::ScordaturaData &item, core::Dire scordatura.addAccord(std::move(accord)); } } - setId(item.id, scordatura); + setId(item.id, scordatura, myDiagnostics, cursorLocation(myCursor)); core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::scordatura(std::move(scordatura))); addDirectionType(std::move(dt), direction); @@ -1258,7 +1289,7 @@ void DirectionWriter::emitPercussion(const api::PercussionData &item, core::Dire { setAttributesFromColorData(*item.color, percussion); } - setId(item.id, percussion); + setId(item.id, percussion, myDiagnostics, cursorLocation(myCursor)); core::DirectionType dt{}; dt.setChoice(core::DirectionTypeChoice::percussion(core::OneOrMore{std::move(percussion)})); addDirectionType(std::move(dt), direction); @@ -1501,6 +1532,11 @@ std::vector DirectionWriter::createHarmonyElements(int in } case api::HarmonyChordSource::root: default: { + if (chordIter->root == api::Step::unspecified) + { + myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::missingValueDefaulted, + cursorLocation(myCursor), "harmony root-step is unspecified; using C"); + } auto step = chordIter->root == api::Step::unspecified ? api::Step::c : chordIter->root; core::Root root{}; @@ -1661,6 +1697,11 @@ std::vector DirectionWriter::createHarmonyElements(int in } // Note: ProcessingInstruction is not available in the new core; miscData is skipped. + if (!chordIter->miscData.empty()) + { + myDiagnostics.report(api::Severity::error, api::DiagnosticCode::droppedData, cursorLocation(myCursor), + "harmony misc data is not written"); + } if (chordIter->hasFrameData) { @@ -1792,7 +1833,7 @@ std::vector DirectionWriter::createFiguredBassElements() } } - setId(figuredBassData.id, figuredBass); + setId(figuredBassData.id, figuredBass, myDiagnostics, cursorLocation(myCursor)); if (figuredBassData.parentheses != api::Bool::unspecified) { @@ -1802,8 +1843,11 @@ std::vector DirectionWriter::createFiguredBassElements() if (figuredBassData.durationTimeTicks >= 0) { - figuredBass.setDuration( - core::PositiveDivisions{core::Decimal{static_cast(figuredBassData.durationTimeTicks)}}); + const core::PositiveDivisions duration{ + core::Decimal{static_cast(figuredBassData.durationTimeTicks)}}; + reportAdjusted(myDiagnostics, cursorLocation(myCursor), "figured-bass duration", + static_cast(figuredBassData.durationTimeTicks), duration.value().value()); + figuredBass.setDuration(duration); } output.push_back(core::MusicDataChoice::figuredBass(figuredBass)); diff --git a/src/private/mx/impl/DirectionWriter.h b/src/private/mx/impl/DirectionWriter.h index a8c82913b..15b9f4422 100644 --- a/src/private/mx/impl/DirectionWriter.h +++ b/src/private/mx/impl/DirectionWriter.h @@ -10,7 +10,8 @@ #include "mx/core/generated/MusicDataChoice.h" #include "mx/core/generated/PercussionChoice.h" #include "mx/impl/Converter.h" -#include "mx/impl/Cursor.h" +#include "mx/impl/DiagnosticsContext.h" +#include "mx/impl/MeasureCursor.h" #include "mx/impl/SpannerResolver.h" #include @@ -26,8 +27,8 @@ class DirectionWriter // inSpannerResolver supplies the resolved 'number' for start/stop pairs such as wedge, // octave-shift, bracket, and dashes and stop semantics such as the size attribute of an // octave-shift (see SpannerResolver); it outlives this writer. - DirectionWriter(const api::DirectionData &inDirectionData, const Cursor &inCursor, - const SpannerResolver &inSpannerResolver); + DirectionWriter(const api::DirectionData &inDirectionData, const MeasureCursor &inCursor, + const SpannerResolver &inSpannerResolver, DiagnosticsContext diagnostics = {}); std::vector getDirectionLikeThings(); private: @@ -76,9 +77,10 @@ class DirectionWriter private: const api::DirectionData &myDirectionData; - const Cursor myCursor; + const MeasureCursor myCursor; const SpannerResolver &mySpannerResolver; const Converter myConverter; + DiagnosticsContext myDiagnostics; bool myIsFirstDirectionTypeAdded; std::set myPlacements; }; diff --git a/src/private/mx/impl/DynamicsWriter.cpp b/src/private/mx/impl/DynamicsWriter.cpp index 219022cd0..42dd1d264 100644 --- a/src/private/mx/impl/DynamicsWriter.cpp +++ b/src/private/mx/impl/DynamicsWriter.cpp @@ -89,15 +89,8 @@ core::DynamicsChoice dynamicsWriterMakeChoice(core::DynamicsChoice::Kind kind, c } } -DynamicsWriter::DynamicsWriter(const api::MarkData &inMark, impl::Cursor inCursor) - : myMarkData{inMark}, myCursor{inCursor}, myConverter{} -{ - MX_ASSERT(isMarkDynamic(inMark.markType)); -} - -DynamicsWriter::DynamicsWriter(const api::MarkData &inMark, impl::Cursor inCursor, - api::Placement /*directionPlacement*/) - : myMarkData{inMark}, myCursor{inCursor}, myConverter{} +DynamicsWriter::DynamicsWriter(const api::MarkData &inMark, MeasureCursor inCursor, DiagnosticsContext diagnostics) + : myMarkData{inMark}, myCursor{inCursor}, myConverter{}, myDiagnostics{std::move(diagnostics)} { MX_ASSERT(isMarkDynamic(inMark.markType)); } diff --git a/src/private/mx/impl/DynamicsWriter.h b/src/private/mx/impl/DynamicsWriter.h index f4d6ad621..abfa2ae77 100644 --- a/src/private/mx/impl/DynamicsWriter.h +++ b/src/private/mx/impl/DynamicsWriter.h @@ -7,7 +7,8 @@ #include "mx/api/MarkData.h" #include "mx/core/generated/Dynamics.h" #include "mx/impl/Converter.h" -#include "mx/impl/Cursor.h" +#include "mx/impl/DiagnosticsContext.h" +#include "mx/impl/MeasureCursor.h" namespace mx { @@ -16,14 +17,14 @@ namespace impl class DynamicsWriter { public: - DynamicsWriter(const api::MarkData &inMark, impl::Cursor inCursor); - DynamicsWriter(const api::MarkData &inMark, impl::Cursor inCursor, api::Placement directionPlacement); + DynamicsWriter(const api::MarkData &inMark, MeasureCursor inCursor, DiagnosticsContext diagnostics = {}); core::Dynamics getDynamics() const; private: const api::MarkData &myMarkData; - const impl::Cursor myCursor; + const MeasureCursor myCursor; const Converter myConverter; + DiagnosticsContext myDiagnostics; }; } // namespace impl } // namespace mx diff --git a/src/private/mx/impl/EncodingFunctions.cpp b/src/private/mx/impl/EncodingFunctions.cpp index cb8c065f3..81277b29c 100644 --- a/src/private/mx/impl/EncodingFunctions.cpp +++ b/src/private/mx/impl/EncodingFunctions.cpp @@ -16,7 +16,8 @@ namespace mx { namespace impl { -void createEncoding(const api::EncodingData &inEncoding, core::ScoreHeaderGroup &header) +void createEncoding(const api::EncodingData &inEncoding, core::ScoreHeaderGroup &header, + const DiagnosticsContext &diagnostics) { // The old code mutated co-allocated identification/encoding through // shared pointers; under value semantics we build local copies and @@ -52,6 +53,15 @@ void createEncoding(const api::EncodingData &inEncoding, core::ScoreHeaderGroup const bool isDayValid = inEncoding.encodingDate.day == tryDate.day(); if (isYearValid || isMonthValid || isDayValid) { + if (!isYearValid || !isMonthValid || !isDayValid) + { + const auto &date = inEncoding.encodingDate; + diagnostics.report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, api::Location{}, + "encoding-date " + std::to_string(date.year) + "-" + std::to_string(date.month) + "-" + + std::to_string(date.day) + " is not a valid date; using " + + std::to_string(tryDate.year()) + "-" + std::to_string(tryDate.month()) + "-" + + std::to_string(tryDate.day())); + } hasIdentification = true; hasEncoding = true; encoding.addChoice(core::EncodingChoice::encodingDate(tryDate)); diff --git a/src/private/mx/impl/EncodingFunctions.h b/src/private/mx/impl/EncodingFunctions.h index 5bfa61f52..c2ed15210 100644 --- a/src/private/mx/impl/EncodingFunctions.h +++ b/src/private/mx/impl/EncodingFunctions.h @@ -7,12 +7,14 @@ #include "mx/api/EncodingData.h" #include "mx/core/generated/Encoding.h" #include "mx/core/generated/ScoreHeaderGroup.h" +#include "mx/impl/DiagnosticsContext.h" namespace mx { namespace impl { -void createEncoding(const api::EncodingData &inEncoding, core::ScoreHeaderGroup &header); +void createEncoding(const api::EncodingData &inEncoding, core::ScoreHeaderGroup &header, + const DiagnosticsContext &diagnostics); api::EncodingData createEncoding(const core::Encoding &inEncoding); } // namespace impl } // namespace mx diff --git a/src/private/mx/impl/GlissandoFunctions.h b/src/private/mx/impl/GlissandoFunctions.h index 1cbfde7da..a3095a128 100644 --- a/src/private/mx/impl/GlissandoFunctions.h +++ b/src/private/mx/impl/GlissandoFunctions.h @@ -112,10 +112,11 @@ void parseGlissandoOrSlide(const GLISSANDO_OR_SLIDE_TYPE &inElement, api::NoteAt // writeAttributesFromCurveStart/Stop in CurveFunctions.h. template void writeAttributesFromGlissandoStart(const api::GlissandoStart &inStart, GLISSANDO_OR_SLIDE_TYPE &outElement, - const std::optional &inResolvedNumber) + const std::optional &inResolvedNumber, + const DiagnosticsContext &diagnostics = {}, const api::Location &location = {}) { outElement.setType(core::StartStop::start()); - setId(inStart.id, outElement); + setId(inStart.id, outElement, diagnostics, location); setAttributesFromPositionData(inStart.positionData, outElement); setAttributesFromPrintData(inStart.printData, outElement); setAttributesFromLineData(inStart.lineData, outElement); @@ -150,10 +151,11 @@ void writeAttributesFromGlissandoStart(const api::GlissandoStart &inStart, GLISS template void writeAttributesFromGlissandoStop(const api::GlissandoStop &inStop, GLISSANDO_OR_SLIDE_TYPE &outElement, - const std::optional &inResolvedNumber) + const std::optional &inResolvedNumber, + const DiagnosticsContext &diagnostics = {}, const api::Location &location = {}) { outElement.setType(core::StartStop::stop()); - setId(inStop.id, outElement); + setId(inStop.id, outElement, diagnostics, location); setAttributesFromPositionData(inStop.positionData, outElement); setAttributesFromLineData(inStop.lineData, outElement); diff --git a/src/private/mx/impl/IdFunctions.h b/src/private/mx/impl/IdFunctions.h index 42f49da50..23f57e4b8 100644 --- a/src/private/mx/impl/IdFunctions.h +++ b/src/private/mx/impl/IdFunctions.h @@ -6,6 +6,7 @@ #include "mx/api/Id.h" #include "mx/api/IdAccess.h" +#include "mx/impl/DiagnosticsContext.h" #include @@ -26,11 +27,20 @@ template std::optional getId(const CORE_TYPE &inCo } // Write the id attribute onto any core element that has one. An absent id writes no attribute. The -// element receives the token the Id already holds, so the text is not scrubbed again. -template void setId(const std::optional &inId, CORE_TYPE &outCoreElement) +// element receives the token the Id already holds, so the text is not scrubbed again; an Id whose text +// was scrubbed when it was built is reported at location. +template +void setId(const std::optional &inId, CORE_TYPE &outCoreElement, const DiagnosticsContext &diagnostics, + const api::Location &location) { if (inId.has_value()) { + const auto &scrubbedText = api::IdAccess::scrubbedText(*inId); + if (scrubbedText.has_value()) + { + diagnostics.report(api::Severity::warning, api::DiagnosticCode::invalidValue, location, + "id \"" + *scrubbedText + "\" is not a valid id; using \"" + inId->value() + "\""); + } outCoreElement.setID(api::IdAccess::token(*inId)); } } diff --git a/src/private/mx/impl/LayoutFunctions.cpp b/src/private/mx/impl/LayoutFunctions.cpp index c94291c6f..1edf8ed2b 100644 --- a/src/private/mx/impl/LayoutFunctions.cpp +++ b/src/private/mx/impl/LayoutFunctions.cpp @@ -37,9 +37,11 @@ namespace mx namespace impl { -static core::Tenths toTenths(double value) +static core::Tenths toTenths(double value, const DiagnosticsContext &diagnostics, const char *name) { - return core::Tenths{core::Decimal{value > 0.0 ? value : 0.0}}; + const double written = value > 0.0 ? value : 0.0; + reportAdjusted(diagnostics, api::Location{}, name, value, written); + return core::Tenths{core::Decimal{written}}; } static std::vector createPageMargins(const api::PageMarginsData &inPageMargins) @@ -106,12 +108,13 @@ static core::PageLayout createPageLayout(const api::PageLayoutData &inPageLayout return outPageLayout; } -void addDefaultsData(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup &outScoreHeaderGroup) +void addDefaultsData(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup &outScoreHeaderGroup, + const DiagnosticsContext &diagnostics) { - addScaling(inDefaults, outScoreHeaderGroup); + addScaling(inDefaults, outScoreHeaderGroup, diagnostics); addPageLayout(inDefaults.pageLayout, outScoreHeaderGroup); - addSystemMargins(inDefaults, outScoreHeaderGroup); - addAppearance(inDefaults, outScoreHeaderGroup); + addSystemMargins(inDefaults, outScoreHeaderGroup, diagnostics); + addAppearance(inDefaults, outScoreHeaderGroup, diagnostics); addDefaultsFonts(inDefaults, outScoreHeaderGroup); } @@ -161,13 +164,21 @@ void addDefaultsFonts(const api::DefaultsData &inDefaults, core::ScoreHeaderGrou outScoreHeaderGroup.setDefaults(defaults); } -void addScaling(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup &outScoreHeaderGroup) +void addScaling(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup &outScoreHeaderGroup, + const DiagnosticsContext &diagnostics) { if (inDefaults.scalingMillimeters <= 0 && inDefaults.scalingTenths <= 0) { return; } + if (inDefaults.scalingMillimeters <= 0 || inDefaults.scalingTenths <= 0) + { + diagnostics.report(api::Severity::warning, api::DiagnosticCode::missingValueDefaulted, api::Location{}, + inDefaults.scalingMillimeters <= 0 ? "scaling millimeters are unspecified; using 0" + : "scaling tenths are unspecified; using 0"); + } + auto defaults = outScoreHeaderGroup.defaults().value_or(core::Defaults{}); core::Scaling scaling{}; @@ -197,7 +208,8 @@ void addPageLayout(const api::PageLayoutData &inPageLayout, core::ScoreHeaderGro outScoreHeaderGroup.setDefaults(defaults); } -void addSystemMargins(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup &outScoreHeaderGroup) +void addSystemMargins(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup &outScoreHeaderGroup, + const DiagnosticsContext &diagnostics) { bool needsDefaults = false; auto defaults = outScoreHeaderGroup.defaults().value_or(core::Defaults{}); @@ -206,13 +218,15 @@ void addSystemMargins(const api::DefaultsData &inDefaults, core::ScoreHeaderGrou if (inDefaults.systemLayout.systemDistance) { - systemLayout.setSystemDistance(toTenths(inDefaults.systemLayout.systemDistance.value())); + systemLayout.setSystemDistance( + toTenths(inDefaults.systemLayout.systemDistance.value(), diagnostics, "system-distance")); needsDefaults = true; } if (inDefaults.systemLayout.topSystemDistance) { - systemLayout.setTopSystemDistance(toTenths(inDefaults.systemLayout.topSystemDistance.value())); + systemLayout.setTopSystemDistance( + toTenths(inDefaults.systemLayout.topSystemDistance.value(), diagnostics, "top-system-distance")); needsDefaults = true; } @@ -221,8 +235,8 @@ void addSystemMargins(const api::DefaultsData &inDefaults, core::ScoreHeaderGrou const auto &margins = inDefaults.systemLayout.margins.value(); core::SystemMargins sm; core::LeftRightMarginsGroup lrm; - lrm.setLeftMargin(toTenths(margins.left)); - lrm.setRightMargin(toTenths(margins.right)); + lrm.setLeftMargin(toTenths(margins.left, diagnostics, "left-margin")); + lrm.setRightMargin(toTenths(margins.right, diagnostics, "right-margin")); sm.setLeftRightMargins(lrm); systemLayout.setSystemMargins(sm); needsDefaults = true; @@ -231,7 +245,8 @@ void addSystemMargins(const api::DefaultsData &inDefaults, core::ScoreHeaderGrou if (inDefaults.systemLayout.staffDistance) { core::StaffLayout staffLayout; - staffLayout.setStaffDistance(toTenths(inDefaults.systemLayout.staffDistance.value())); + staffLayout.setStaffDistance( + toTenths(inDefaults.systemLayout.staffDistance.value(), diagnostics, "staff-distance")); layout.addStaffLayout(staffLayout); needsDefaults = true; } @@ -239,8 +254,10 @@ void addSystemMargins(const api::DefaultsData &inDefaults, core::ScoreHeaderGrou for (const auto &[staffIndex, staffDistance] : inDefaults.systemLayout.staffDistances) { core::StaffLayout staffLayout; - staffLayout.setNumber(core::StaffNumber{staffIndex + 1}); - staffLayout.setStaffDistance(toTenths(staffDistance)); + const core::StaffNumber staffNumber{staffIndex + 1}; + reportAdjusted(diagnostics, api::Location{}, "staff-layout number", staffIndex + 1, staffNumber.value()); + staffLayout.setNumber(staffNumber); + staffLayout.setStaffDistance(toTenths(staffDistance, diagnostics, "staff-distance")); layout.addStaffLayout(staffLayout); needsDefaults = true; } @@ -253,7 +270,8 @@ void addSystemMargins(const api::DefaultsData &inDefaults, core::ScoreHeaderGrou } } -void addAppearance(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup &outScoreHeaderGroup) +void addAppearance(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup &outScoreHeaderGroup, + const DiagnosticsContext &diagnostics) { if (inDefaults.appearance.empty()) { @@ -269,21 +287,23 @@ void addAppearance(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup & { core::LineWidth lw; lw.setType(core::LineWidthType{appearanceData.appearanceSubType}); - lw.setValue(toTenths(appearanceData.value)); + lw.setValue(toTenths(appearanceData.value, diagnostics, "line-width")); appearance.addLineWidth(lw); } else if (appearanceData.appearanceType == api::AppearanceType::NoteSize) { core::NoteSize ns; ns.setType(core::NoteSizeType::parse(appearanceData.appearanceSubType)); - ns.setValue(core::NonNegativeDecimal{core::Decimal{appearanceData.value}}); + const core::NonNegativeDecimal noteSize{core::Decimal{appearanceData.value}}; + reportAdjusted(diagnostics, api::Location{}, "note-size", appearanceData.value, noteSize.value().value()); + ns.setValue(noteSize); appearance.addNoteSize(ns); } else if (appearanceData.appearanceType == api::AppearanceType::Distance) { core::Distance di; di.setType(core::DistanceType{appearanceData.appearanceSubType}); - di.setValue(toTenths(appearanceData.value)); + di.setValue(toTenths(appearanceData.value, diagnostics, "distance")); appearance.addDistance(di); } else if (appearanceData.appearanceType == api::AppearanceType::OtherAppearance) @@ -299,14 +319,15 @@ void addAppearance(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup & outScoreHeaderGroup.setDefaults(defaults); } -api::DefaultsData createDefaults(const core::ScoreHeaderGroup &inScoreHeaderGroup) +api::DefaultsData createDefaults(const core::ScoreHeaderGroup &inScoreHeaderGroup, + const DiagnosticsContext &diagnostics) { api::DefaultsData defaults; addScaling(inScoreHeaderGroup, defaults); addPageMargins(inScoreHeaderGroup, defaults); addSystemMargins(inScoreHeaderGroup, defaults); addStaffLayout(inScoreHeaderGroup, defaults); - addAppearance(inScoreHeaderGroup, defaults); + addAppearance(inScoreHeaderGroup, defaults, diagnostics); addDefaultsFonts(inScoreHeaderGroup, defaults); return defaults; } @@ -457,7 +478,8 @@ void addStaffLayout(const core::ScoreHeaderGroup &inScoreHeaderGroup, api::Defau } } -void addAppearance(const core::ScoreHeaderGroup &inScoreHeaderGroup, api::DefaultsData &outDefaults) +void addAppearance(const core::ScoreHeaderGroup &inScoreHeaderGroup, api::DefaultsData &outDefaults, + const DiagnosticsContext &diagnostics) { outDefaults.appearance.clear(); @@ -507,6 +529,11 @@ void addAppearance(const core::ScoreHeaderGroup &inScoreHeaderGroup, api::Defaul { data.value = parsedOtherAppearanceValue.value(); } + else + { + diagnostics.report(api::Severity::warning, api::DiagnosticCode::invalidValue, api::Location{}, + "other-appearance \"" + oa.value() + "\" is not a number; using 0"); + } outDefaults.appearance.emplace_back(std::move(data)); } } diff --git a/src/private/mx/impl/LayoutFunctions.h b/src/private/mx/impl/LayoutFunctions.h index 463d79fc5..05b4f0dfa 100644 --- a/src/private/mx/impl/LayoutFunctions.h +++ b/src/private/mx/impl/LayoutFunctions.h @@ -6,6 +6,7 @@ #include "mx/api/DefaultsData.h" #include "mx/core/generated/ScoreHeaderGroup.h" +#include "mx/impl/DiagnosticsContext.h" namespace mx { @@ -13,21 +14,27 @@ namespace impl { //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // api::DefaultsData -> core::ScoreHeaderGroup -void addDefaultsData(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup &outScoreHeaderGroup); -void addScaling(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup &outScoreHeaderGroup); +void addDefaultsData(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup &outScoreHeaderGroup, + const DiagnosticsContext &diagnostics); +void addScaling(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup &outScoreHeaderGroup, + const DiagnosticsContext &diagnostics); void addPageLayout(const api::PageLayoutData &inPageLayout, core::ScoreHeaderGroup &outScoreHeaderGroup); -void addSystemMargins(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup &outScoreHeaderGroup); -void addAppearance(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup &outScoreHeaderGroup); +void addSystemMargins(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup &outScoreHeaderGroup, + const DiagnosticsContext &diagnostics); +void addAppearance(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup &outScoreHeaderGroup, + const DiagnosticsContext &diagnostics); void addDefaultsFonts(const api::DefaultsData &inDefaults, core::ScoreHeaderGroup &outScoreHeaderGroup); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // core::ScoreHeaderGroup -> api::DefaultsData -api::DefaultsData createDefaults(const core::ScoreHeaderGroup &inScoreHeaderGroup); +api::DefaultsData createDefaults(const core::ScoreHeaderGroup &inScoreHeaderGroup, + const DiagnosticsContext &diagnostics); void addScaling(const core::ScoreHeaderGroup &inScoreHeaderGroup, api::DefaultsData &outDefaults); void addPageMargins(const core::ScoreHeaderGroup &inScoreHeaderGroup, api::DefaultsData &outDefaults); void addSystemMargins(const core::ScoreHeaderGroup &inScoreHeaderGroup, api::DefaultsData &outDefaults); void addStaffLayout(const core::ScoreHeaderGroup &inScoreHeaderGroup, api::DefaultsData &outDefaults); -void addAppearance(const core::ScoreHeaderGroup &inScoreHeaderGroup, api::DefaultsData &outDefaults); +void addAppearance(const core::ScoreHeaderGroup &inScoreHeaderGroup, api::DefaultsData &outDefaults, + const DiagnosticsContext &diagnostics); void addDefaultsFonts(const core::ScoreHeaderGroup &inScoreHeaderGroup, api::DefaultsData &outDefaults); } // namespace impl } // namespace mx diff --git a/src/private/mx/impl/MeasureReader.cpp b/src/private/mx/impl/MeasureReader.cpp index 8a26d7b23..80e2634ea 100644 --- a/src/private/mx/impl/MeasureReader.cpp +++ b/src/private/mx/impl/MeasureReader.cpp @@ -7,6 +7,7 @@ #include "mx/api/DirectionData.h" #include "mx/api/KeyData.h" #include "mx/api/NoteData.h" +#include "mx/core/Lexical.h" #include "mx/core/generated/Attributes.h" #include "mx/core/generated/AttributesChoice.h" #include "mx/core/generated/Backup.h" @@ -242,6 +243,10 @@ void MeasureReader::parseTimeSignature() const entry.second.id.reset(); } + // the api holds one time signature per measure for all staves and one per staff + bool isAllStavesTimeFound = false; + std::set staffTimesFound; + TimeReader timeReader{myPartwiseMeasure.musicData()}; for (const auto &found : timeReader.getTimeSignatures()) // isImplicit == false on each { @@ -250,11 +255,8 @@ void MeasureReader::parseTimeSignature() const // clamp an out-of-range staff number to "all staves", mirroring the keys pattern if (staffIndex != api::INDEX_UNSPECIFIED && staffIndex > myCurrentCursor.getNumStaves() - 1) { - api::Location location; - location.partIndex = myCurrentCursor.partIndex; - location.measureIndex = myCurrentCursor.measureIndex; - location.tickTimePosition = myCurrentCursor.tickTimePosition; - myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, std::move(location), + myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, + measureLocation(myCurrentCursor), "time signature staff number " + std::to_string(staffIndex + 1) + " is out of range; applying it to all staves"); staffIndex = api::INDEX_UNSPECIFIED; @@ -262,6 +264,14 @@ void MeasureReader::parseTimeSignature() const if (staffIndex == api::INDEX_UNSPECIFIED) { + if (isAllStavesTimeFound) + { + myDiagnostics.report( + api::Severity::error, api::DiagnosticCode::droppedData, measureLocation(myCurrentCursor), + "a measure has more than one time signature for all staves; only the last is read"); + } + isAllStavesTimeFound = true; + // a restated unscoped time governs all staves: it supersedes carried-forward per-staff // overrides (but not overrides stated explicitly in this same measure) timeSignature = found.timeChoice; @@ -272,6 +282,13 @@ void MeasureReader::parseTimeSignature() const } else { + if (!staffTimesFound.insert(staffIndex).second) + { + myDiagnostics.report(api::Severity::error, api::DiagnosticCode::droppedData, + measureLocation(myCurrentCursor), + "a measure has more than one time signature for staff " + + std::to_string(staffIndex + 1) + "; only the last is read"); + } staffTimeSignatures[staffIndex] = found.timeChoice; } } @@ -382,10 +399,9 @@ void MeasureReader::parseNote(const core::Note &inMxNote, const core::Note *next myCurrentCursor.isBackupInProgress = false; impl::NoteReader noteReader{inMxNote}; - impl::NoteFunctions noteFunc{inMxNote, myCurrentCursor}; - auto noteData = noteFunc.parseNote(); int noteDataStaffIndex = noteReader.getStaffNumber() - 1; + const bool isStaffNumberOutOfRange = noteReader.getIsStaffSpecified() && noteDataStaffIndex < 0; if (noteDataStaffIndex < 0) { @@ -417,12 +433,30 @@ void MeasureReader::parseNote(const core::Note &inMxNote, const core::Note *next { bucketStaffIndex = noteDataStaffIndex; } - else if (bucketStaffIndex != noteDataStaffIndex) + + myCurrentCursor.staffIndex = bucketStaffIndex; + + // the cursor has the note's staff before the note is parsed, so the note's reports carry it + impl::NoteFunctions noteFunc{inMxNote, myCurrentCursor, myDiagnostics}; + auto noteData = noteFunc.parseNote(); + + if (bucketStaffIndex != noteDataStaffIndex) { noteData.crossStaffIndex = noteDataStaffIndex; } - myCurrentCursor.staffIndex = bucketStaffIndex; + auto location = measureLocation(myCurrentCursor); + location.staffIndex = bucketStaffIndex; + + if (isStaffNumberOutOfRange) + { + myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, location, + "note staff number " + std::to_string(noteReader.getStaffNumber()) + + " is out of range; using staff 1"); + } + + reportBeamRepairs(inMxNote, noteReader, location); + reportRoundedDuration("note", noteReader.getDurationValue(), location); bool isThisNotePartOfAChord = noteData.isChord || isNextNotePartOfAChord; noteData.isChord = isThisNotePartOfAChord; @@ -580,6 +614,8 @@ void MeasureReader::parseBackup(const core::Backup &inMxBackup) const // a chord cannot straddle a timeline jump myPreviousNoteBucketStaffIndex = -1; + reportRoundedDuration("backup", static_cast(inMxBackup.duration().value().value()), + measureLocation(myCurrentCursor)); const int backupAmount = myCurrentCursor.convertDurationToGlobalTickScale(inMxBackup.duration()); advanceTickTimePosition(-1 * backupAmount, "backup"); @@ -587,6 +623,9 @@ void MeasureReader::parseBackup(const core::Backup &inMxBackup) const { auto problemAmount = myCurrentCursor.tickTimePosition * -1; advanceTickTimePosition(problemAmount, "correct backup negative error"); + myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, + measureLocation(myCurrentCursor), + "backup moves before the start of the measure; using the measure start"); } } @@ -599,13 +638,15 @@ void MeasureReader::parseForward(const core::Forward &inMxForward) const // forward from, so a source's / voice/staff is expected to not round-trip. // a chord cannot straddle a timeline jump myPreviousNoteBucketStaffIndex = -1; + reportRoundedDuration("forward", static_cast(inMxForward.duration().value().value()), + measureLocation(myCurrentCursor)); const int forwardAmount = myCurrentCursor.convertDurationToGlobalTickScale(inMxForward.duration()); advanceTickTimePosition(forwardAmount, "forward"); } void MeasureReader::parseDirection(const core::Direction &inDirection) const { - DirectionReader reader{inDirection, myCurrentCursor}; + DirectionReader reader{inDirection, myCurrentCursor, myDiagnostics}; auto directionData = reader.getDirectionData(); // make an adjustment if the directionData refers to a non-existent staff @@ -620,6 +661,13 @@ void MeasureReader::parseDirection(const core::Direction &inDirection) const isStaffIndexInsane = staffIndex >= myOutMeasureData.staves.size(); + if (isStaffIndexSpecified && isStaffIndexInsane) + { + myDiagnostics.report( + api::Severity::warning, api::DiagnosticCode::valueAdjusted, measureLocation(myCurrentCursor), + "direction staff number " + std::to_string(*inDirection.staff()) + " is out of range; using staff 1"); + } + if (!isStaffIndexSpecified || isStaffIndexInsane) { staffIndex = 0; @@ -644,8 +692,20 @@ std::optional MeasureReader::parseAttributes(const core::Att if (inMxAttributes.divisions().has_value()) { - const auto newDivisionsValueDecimal = inMxAttributes.divisions()->value().value(); - myCurrentCursor.ticksPerQuarter = static_cast(std::ceil(newDivisionsValueDecimal - 0.5)); + const auto newDivisionsValueDecimal = static_cast(inMxAttributes.divisions()->value().value()); + + // divisions that round to 0 read as 1 rather than dividing durations by 0 + const int ticksPerQuarter = std::max(static_cast(std::ceil(newDivisionsValueDecimal - 0.5)), 1); + + if (static_cast(ticksPerQuarter) != newDivisionsValueDecimal) + { + myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, + measureLocation(myCurrentCursor), + "divisions " + core::formatDouble(newDivisionsValueDecimal) + " rounded to " + + std::to_string(ticksPerQuarter)); + } + + myCurrentCursor.ticksPerQuarter = ticksPerQuarter; } // TODO - continue work on measure numbering and style etc @@ -708,6 +768,10 @@ std::optional MeasureReader::parseAttributes(const core::Att keyData.staffIndex = key.number()->value() - 1; if (keyData.staffIndex > myCurrentCursor.getNumStaves() - 1) { + myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, + measureLocation(myCurrentCursor), + "key staff number " + std::to_string(keyData.staffIndex + 1) + + " is out of range; applying it to all staves"); keyData.staffIndex = api::INDEX_UNSPECIFIED; } } @@ -773,6 +837,10 @@ std::optional MeasureReader::parseAttributes(const core::Att transposeData.staffIndex = coreTranspose.number()->value() - 1; if (transposeData.staffIndex > myCurrentCursor.getNumStaves() - 1) { + myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, + measureLocation(myCurrentCursor), + "transpose staff number " + std::to_string(transposeData.staffIndex + 1) + + " is out of range; applying it to all staves"); transposeData.staffIndex = api::INDEX_UNSPECIFIED; } } @@ -787,7 +855,7 @@ std::optional MeasureReader::parseAttributes(const core::Att void MeasureReader::parseHarmony(const core::Harmony &inHarmony) const { - DirectionReader reader{inHarmony, myCurrentCursor}; + DirectionReader reader{inHarmony, myCurrentCursor, myDiagnostics}; auto directionData = reader.getDirectionData(); // make an adjustment if the directionData refers to a non-existent staff @@ -802,6 +870,13 @@ void MeasureReader::parseHarmony(const core::Harmony &inHarmony) const isStaffIndexInsane = staffIndex >= myOutMeasureData.staves.size(); + if (isStaffIndexSpecified && isStaffIndexInsane) + { + myDiagnostics.report( + api::Severity::warning, api::DiagnosticCode::valueAdjusted, measureLocation(myCurrentCursor), + "harmony staff number " + std::to_string(*inHarmony.staff()) + " is out of range; using staff 1"); + } + if (!isStaffIndexSpecified || isStaffIndexInsane) { staffIndex = 0; @@ -839,6 +914,8 @@ void MeasureReader::parseFiguredBass(const core::FiguredBass &inMxFiguredBass, c if (inMxFiguredBass.duration().has_value()) { + reportRoundedDuration("figured-bass", static_cast(inMxFiguredBass.duration()->value().value()), + measureLocation(myCurrentCursor)); figuredBass.durationTimeTicks = myCurrentCursor.convertDurationToGlobalTickScale(*inMxFiguredBass.duration()); } @@ -1032,6 +1109,9 @@ void MeasureReader::importStaffDetails(const core::Attributes &inMxAttributes) c if (staffIndex < 0 || staffIndex >= static_cast(myOutMeasureData.staves.size())) { + myDiagnostics.report( + api::Severity::error, api::DiagnosticCode::droppedData, measureLocation(myCurrentCursor), + "staff-details staff number " + std::to_string(staffIndex + 1) + " is out of range; it is not read"); continue; } @@ -1125,6 +1205,15 @@ void MeasureReader::importClef(const core::Clef &inClef) const const bool sourceHasNumber = inClef.number().has_value(); int celfStaffIndex = sourceHasNumber ? inClef.number()->value() - 1 : 0; + // PartReader counts clef numbers toward the staff count, so only a number below 1 is out of range + if (celfStaffIndex < 0) + { + myDiagnostics.report( + api::Severity::warning, api::DiagnosticCode::valueAdjusted, measureLocation(myCurrentCursor), + "clef staff number " + std::to_string(celfStaffIndex + 1) + " is out of range; using staff 1"); + celfStaffIndex = 0; + } + // Auto rule (see ClefData::writeStaffNumber): include the number unless this is a single-staff // part carrying the implied 1 (staff index 0). Record an explicit override only when the source // diverges from that rule, so the common case stays unspecified. @@ -1319,5 +1408,57 @@ void MeasureReader::advanceTickTimePosition(int amount, std::string reason) cons myHistory.push_back(record); // std::cout << record.reason << std::endl; } + +void MeasureReader::reportRoundedDuration(const char *element, double duration, api::Location location) const +{ + const double exactTicks = myCurrentCursor.convertDurationToExactGlobalTicks(duration); + const int ticks = myCurrentCursor.convertDurationToGlobalTickScale(duration); + + if (std::abs(exactTicks - static_cast(ticks)) > 1e-6) + { + myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, std::move(location), + std::string{element} + " duration " + core::formatDouble(duration) + " is " + + core::formatDouble(exactTicks) + " ticks; using " + std::to_string(ticks)); + } +} + +void MeasureReader::reportBeamRepairs(const core::Note &inMxNote, const NoteReader ¬eReader, + const api::Location &location) const +{ + const auto beams = inMxNote.beam(); + std::set numbers; + int duplicateNumber = 0; + + for (const auto &beam : beams) + { + const int number = beam.number().has_value() ? beam.number()->value() : core::BeamLevel{}.value(); + if (!numbers.insert(number).second && duplicateNumber == 0) + { + duplicateNumber = number; + } + } + + if (duplicateNumber != 0) + { + const std::string duplicate = "a note has more than one beam number " + std::to_string(duplicateNumber); + const auto droppedCount = beams.size() - noteReader.getBeams().size(); + + if (droppedCount > 0) + { + myDiagnostics.report(api::Severity::error, api::DiagnosticCode::droppedData, location, + duplicate + "; " + std::to_string(droppedCount) + " of its beams are not read"); + } + else + { + myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, location, + duplicate + "; renumbering its beams"); + } + } + else if (!numbers.empty() && *numbers.crbegin() != static_cast(numbers.size())) + { + myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, location, + "a note's beam numbers do not count up from 1; renumbering its beams"); + } +} } // namespace impl } // namespace mx diff --git a/src/private/mx/impl/MeasureReader.h b/src/private/mx/impl/MeasureReader.h index e075fec2e..5e6f8f05a 100644 --- a/src/private/mx/impl/MeasureReader.h +++ b/src/private/mx/impl/MeasureReader.h @@ -37,6 +37,7 @@ class Clef; namespace impl { +class NoteReader; class MeasureReader { @@ -130,6 +131,9 @@ class MeasureReader bool isUserRequestedVoiceNumberConsistentAcrossAllVoices(const api::StaffData &staff) const; int getUserRequestedVoiceNumber(const api::VoiceData &voiceData) const; void advanceTickTimePosition(int amount, std::string reason) const; + void reportRoundedDuration(const char *element, double duration, api::Location location) const; + void reportBeamRepairs(const core::Note &inMxNote, const NoteReader ¬eReader, + const api::Location &location) const; }; } // namespace impl } // namespace mx diff --git a/src/private/mx/impl/MeasureWriter.cpp b/src/private/mx/impl/MeasureWriter.cpp index 1c2908b30..6510d1bc9 100644 --- a/src/private/mx/impl/MeasureWriter.cpp +++ b/src/private/mx/impl/MeasureWriter.cpp @@ -60,7 +60,8 @@ MeasureWriter::MeasureWriter(const api::MeasureData &inMeasureData, const Measur core::PartwiseMeasure MeasureWriter::getPartwiseMeasure() { myOutMeasure = core::PartwiseMeasure{}; - myPropertiesWriter = std::unique_ptr{new PropertiesWriter{myOutMeasure}}; + myPropertiesWriter = std::unique_ptr{ + new PropertiesWriter{myOutMeasure, myScoreWriter.getDiagnostics(), measureOnlyLocation(myHistory.getCursor())}}; auto cursor = myHistory.getCursor(); cursor.reset(); myHistory = History{cursor}; @@ -71,12 +72,33 @@ core::PartwiseMeasure MeasureWriter::getPartwiseMeasure() writeMeasureGlobals(); writeStaves(); + + for (; myMeasureKeysIter != myMeasureKeysEnd; ++myMeasureKeysIter) + { + auto location = measureOnlyLocation(myHistory.getCursor()); + location.tickTimePosition = myMeasureKeysIter->tickTimePosition; + myScoreWriter.getDiagnostics().report(api::Severity::error, api::DiagnosticCode::droppedData, + std::move(location), "a key in the middle of the measure is not written"); + } myPropertiesWriter->flushBuffer(); writeBarlines(api::TICK_TIME_INFINITY); myPropertiesWriter = nullptr; return myOutMeasure; } +void MeasureWriter::reportMidMeasureKeyStaff() const +{ + if (myMeasureKeysIter->staffIndex >= 0) + { + auto location = measureOnlyLocation(myHistory.getCursor()); + location.tickTimePosition = myMeasureKeysIter->tickTimePosition; + myScoreWriter.getDiagnostics().report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, + std::move(location), + "a key for staff " + std::to_string(myMeasureKeysIter->staffIndex + 1) + + " in the middle of the measure is written for all staves"); + } +} + void MeasureWriter::writeMeasureGlobals() { writeBarlines(0); @@ -101,7 +123,7 @@ void MeasureWriter::writeMeasureGlobals() myOutMeasure.setWidth(core::Tenths{core::Decimal{static_cast(myMeasureData.width)}}); } - impl::setId(myMeasureData.id, myOutMeasure); + impl::setId(myMeasureData.id, myOutMeasure, myScoreWriter.getDiagnostics(), cursorLocation(myHistory.getCursor())); Converter converter; @@ -293,7 +315,10 @@ void MeasureWriter::writeSystemInfo() for (const auto &[staffIndex, staffDistance] : inSystemLayout.staffDistances) { core::StaffLayout outStaffLayout{}; - outStaffLayout.setNumber(core::StaffNumber{staffIndex + 1}); + const core::StaffNumber staffNumber{staffIndex + 1}; + reportAdjusted(myScoreWriter.getDiagnostics(), measureOnlyLocation(myHistory.getCursor()), + "staff-layout number", staffIndex + 1, staffNumber.value()); + outStaffLayout.setNumber(staffNumber); outStaffLayout.setStaffDistance(core::Tenths{core::Decimal{staffDistance}}); outLayoutGroup.addStaffLayout(outStaffLayout); outPrint.setLayout(outLayoutGroup); @@ -469,7 +494,10 @@ void MeasureWriter::writeMeasureNumbering() if (myMeasureData.measureNumberingStaffIndex.has_value()) { // the api index is zero-based; core staff numbers are one-based. - outMeasureNumbering.setStaff(core::StaffNumber{*myMeasureData.measureNumberingStaffIndex + 1}); + const core::StaffNumber staffNumber{*myMeasureData.measureNumberingStaffIndex + 1}; + reportAdjusted(myScoreWriter.getDiagnostics(), measureOnlyLocation(myHistory.getCursor()), + "measure-numbering staff", *myMeasureData.measureNumberingStaffIndex + 1, staffNumber.value()); + outMeasureNumbering.setStaff(staffNumber); } outPrint.setMeasureNumbering(std::move(outMeasureNumbering)); @@ -567,6 +595,7 @@ void MeasureWriter::writeVoices(const api::StaffData &inStaff) { if (myMeasureKeysIter->tickTimePosition <= myHistory.getCursor().tickTimePosition) { + reportMidMeasureKeyStaff(); myPropertiesWriter->writeKey(api::INDEX_UNSPECIFIED, *myMeasureKeysIter); ++myMeasureKeysIter; } @@ -635,6 +664,7 @@ void MeasureWriter::writeVoices(const api::StaffData &inStaff) { if (myMeasureKeysIter->tickTimePosition) { + reportMidMeasureKeyStaff(); myPropertiesWriter->writeKey(api::INDEX_UNSPECIFIED, *myMeasureKeysIter); ++myMeasureKeysIter; } @@ -824,7 +854,8 @@ void MeasureWriter::writeDirection(const api::DirectionData &inDirectionData) myPropertiesWriter->flushBuffer(); } - DirectionWriter directionWriter{inDirectionData, myHistory.getCursor(), myScoreWriter.getSpannerResolver()}; + DirectionWriter directionWriter{inDirectionData, myHistory.getCursor(), myScoreWriter.getSpannerResolver(), + myScoreWriter.getDiagnostics()}; auto mdcSet = directionWriter.getDirectionLikeThings(); for (const auto &mdc : mdcSet) { @@ -856,6 +887,11 @@ void MeasureWriter::writeBarlines(int tickTimePosition) // number is a required attribute; an empty list serializes as number="", which is // MusicXML's blank ending. + for (const int number : endingData.numbers) + { + reportAdjusted(myScoreWriter.getDiagnostics(), measureOnlyLocation(myHistory.getCursor()), + "ending number", number, std::max(number, 1)); + } ending.setNumber(core::EndingNumber{endingData.numbers}); // The text is written only when the author supplied one. Left empty, the ending @@ -917,7 +953,8 @@ void MeasureWriter::writeBarlines(int tickTimePosition) barlineElement.setRepeat(repeatElement); } - impl::setId(myBarlinesIter->id, barlineElement); + impl::setId(myBarlinesIter->id, barlineElement, myScoreWriter.getDiagnostics(), + cursorLocation(myHistory.getCursor())); myOutMeasure.addMusicData(core::MusicDataChoice::barline(barlineElement)); myHistory.log("writeBarline"); } diff --git a/src/private/mx/impl/MeasureWriter.h b/src/private/mx/impl/MeasureWriter.h index 8276c35b3..30ebc7f3b 100644 --- a/src/private/mx/impl/MeasureWriter.h +++ b/src/private/mx/impl/MeasureWriter.h @@ -193,6 +193,9 @@ class MeasureWriter core::PartwiseMeasure myOutMeasure; MeasureCursor myPreviousCursor; const ScoreWriter &myScoreWriter; + + // Reports a mid-measure key whose staff is dropped because it is written for all staves. + void reportMidMeasureKeyStaff() const; std::unique_ptr myPropertiesWriter; const Converter myConverter; std::vector::const_iterator myBarlinesIter; diff --git a/src/private/mx/impl/NotationsWriter.cpp b/src/private/mx/impl/NotationsWriter.cpp index aff3cd1c6..5cd53a701 100644 --- a/src/private/mx/impl/NotationsWriter.cpp +++ b/src/private/mx/impl/NotationsWriter.cpp @@ -97,15 +97,19 @@ void notationsWriterSetMordentSpecificAttributes(const api::MarkData &mark, core } } -core::NotationsChoice notationsWriterMakeTupletStop(const api::TupletStop &inTupletStop) +core::NotationsChoice notationsWriterMakeTupletStop(const api::TupletStop &inTupletStop, + const DiagnosticsContext &diagnostics, + const api::Location &location) { core::Tuplet tuplet; tuplet.setType(core::StartStop::stop()); - setId(inTupletStop.id, tuplet); + setId(inTupletStop.id, tuplet, diagnostics, location); if (inTupletStop.numberLevel > 0) { - tuplet.setNumber(core::NumberLevel{inTupletStop.numberLevel}); + const core::NumberLevel numberLevel{inTupletStop.numberLevel}; + reportAdjusted(diagnostics, location, "tuplet number", inTupletStop.numberLevel, numberLevel.value()); + tuplet.setNumber(numberLevel); } return core::NotationsChoice::tuplet(tuplet); @@ -130,19 +134,24 @@ core::Notations NotationsWriter::getNotations() const { if (curve.curveType != api::CurveType::tie && curve.curveType != api::CurveType::slur) { + myScoreWriter.getDiagnostics().report(api::Severity::error, api::DiagnosticCode::droppedData, + cursorLocation(myCursor), + "a curve with an unspecified type is not written"); continue; } const auto resolvedNumber = spannerResolver.emittedNumber(curve.number, &curve); if (curve.curveType == api::CurveType::tie) { core::Tied tied; - writeAttributesFromCurveStop(curve, tied, resolvedNumber); + writeAttributesFromCurveStop(curve, tied, resolvedNumber, myScoreWriter.getDiagnostics(), + cursorLocation(myCursor)); outNotations.addChoice(core::NotationsChoice::tied(tied)); } else if (curve.curveType == api::CurveType::slur) { core::Slur slur; - writeAttributesFromCurveStop(curve, slur, resolvedNumber); + writeAttributesFromCurveStop(curve, slur, resolvedNumber, myScoreWriter.getDiagnostics(), + cursorLocation(myCursor)); outNotations.addChoice(core::NotationsChoice::slur(slur)); } } @@ -151,19 +160,24 @@ core::Notations NotationsWriter::getNotations() const { if (curve.curveType != api::CurveType::tie && curve.curveType != api::CurveType::slur) { + myScoreWriter.getDiagnostics().report(api::Severity::error, api::DiagnosticCode::droppedData, + cursorLocation(myCursor), + "a curve with an unspecified type is not written"); continue; } const auto resolvedNumber = spannerResolver.emittedNumber(curve.number, &curve); if (curve.curveType == api::CurveType::tie) { core::Tied tied; - writeAttributesFromCurveContinue(curve, tied, resolvedNumber); + writeAttributesFromCurveContinue(curve, tied, resolvedNumber, myScoreWriter.getDiagnostics(), + cursorLocation(myCursor)); outNotations.addChoice(core::NotationsChoice::tied(tied)); } else if (curve.curveType == api::CurveType::slur) { core::Slur slur; - writeAttributesFromCurveContinue(curve, slur, resolvedNumber); + writeAttributesFromCurveContinue(curve, slur, resolvedNumber, myScoreWriter.getDiagnostics(), + cursorLocation(myCursor)); outNotations.addChoice(core::NotationsChoice::slur(slur)); } } @@ -172,19 +186,24 @@ core::Notations NotationsWriter::getNotations() const { if (curve.curveType != api::CurveType::tie && curve.curveType != api::CurveType::slur) { + myScoreWriter.getDiagnostics().report(api::Severity::error, api::DiagnosticCode::droppedData, + cursorLocation(myCursor), + "a curve with an unspecified type is not written"); continue; } const auto resolvedNumber = spannerResolver.emittedNumber(curve.number, &curve); if (curve.curveType == api::CurveType::tie) { core::Tied tied; - writeAttributesFromCurveStart(curve, tied, resolvedNumber); + writeAttributesFromCurveStart(curve, tied, resolvedNumber, myScoreWriter.getDiagnostics(), + cursorLocation(myCursor)); outNotations.addChoice(core::NotationsChoice::tied(tied)); } else if (curve.curveType == api::CurveType::slur) { core::Slur slur; - writeAttributesFromCurveStart(curve, slur, resolvedNumber); + writeAttributesFromCurveStart(curve, slur, resolvedNumber, myScoreWriter.getDiagnostics(), + cursorLocation(myCursor)); outNotations.addChoice(core::NotationsChoice::slur(slur)); } } @@ -195,7 +214,8 @@ core::Notations NotationsWriter::getNotations() const if (myNoteData.tieLetRing.has_value()) { core::Tied tied; - writeAttributesFromTieLetRing(*myNoteData.tieLetRing, tied); + writeAttributesFromTieLetRing(*myNoteData.tieLetRing, tied, myScoreWriter.getDiagnostics(), + cursorLocation(myCursor)); outNotations.addChoice(core::NotationsChoice::tied(tied)); } @@ -216,7 +236,7 @@ core::Notations NotationsWriter::getNotations() const { core::Tuplet tuplet; tuplet.setType(core::StartStop::start()); - setId(tupletStart.id, tuplet); + setId(tupletStart.id, tuplet, myScoreWriter.getDiagnostics(), cursorLocation(myCursor)); core::TupletPortion actual; core::TupletNumber tn1; @@ -246,7 +266,10 @@ core::Notations NotationsWriter::getNotations() const if (tupletStart.numberLevel > 0) { - tuplet.setNumber(core::NumberLevel{tupletStart.numberLevel}); + const core::NumberLevel numberLevel{tupletStart.numberLevel}; + reportAdjusted(myScoreWriter.getDiagnostics(), cursorLocation(myCursor), "tuplet number", + tupletStart.numberLevel, numberLevel.value()); + tuplet.setNumber(numberLevel); } if (tupletStart.bracket != api::Bool::unspecified) @@ -279,7 +302,8 @@ core::Notations NotationsWriter::getNotations() const { if (!tupletStopWritten[stopIndex] && tupletStops[stopIndex].numberLevel == tupletStart.numberLevel) { - outNotations.addChoice(notationsWriterMakeTupletStop(tupletStops[stopIndex])); + outNotations.addChoice(notationsWriterMakeTupletStop( + tupletStops[stopIndex], myScoreWriter.getDiagnostics(), cursorLocation(myCursor))); tupletStopWritten[stopIndex] = true; break; } @@ -290,7 +314,8 @@ core::Notations NotationsWriter::getNotations() const { if (!tupletStopWritten[stopIndex]) { - outNotations.addChoice(notationsWriterMakeTupletStop(tupletStops[stopIndex])); + outNotations.addChoice(notationsWriterMakeTupletStop(tupletStops[stopIndex], myScoreWriter.getDiagnostics(), + cursorLocation(myCursor))); } } @@ -317,7 +342,7 @@ core::Notations NotationsWriter::getNotations() const } else if (isMarkDynamic(mark.markType)) { - DynamicsWriter dynamicsWriter{mark, myCursor}; + DynamicsWriter dynamicsWriter{mark, myCursor, myScoreWriter.getDiagnostics()}; outNotations.addChoice(core::NotationsChoice::dynamics(dynamicsWriter.getDynamics())); } else if (isMarkFermata(mark.markType)) @@ -415,9 +440,12 @@ core::Notations NotationsWriter::getNotations() const : core::TopBottom::top()); if (nonArpeggiateData.number.has_value()) { - nonArpeggiate.setNumber(core::NumberLevel{*nonArpeggiateData.number}); + const core::NumberLevel numberLevel{*nonArpeggiateData.number}; + reportAdjusted(myScoreWriter.getDiagnostics(), cursorLocation(myCursor), "non-arpeggiate number", + *nonArpeggiateData.number, numberLevel.value()); + nonArpeggiate.setNumber(numberLevel); } - setId(nonArpeggiateData.id, nonArpeggiate); + setId(nonArpeggiateData.id, nonArpeggiate, myScoreWriter.getDiagnostics(), cursorLocation(myCursor)); outNotations.addChoice(core::NotationsChoice::nonArpeggiate(nonArpeggiate)); } @@ -442,14 +470,17 @@ core::Notations NotationsWriter::getNotations() const const auto arpeggiateData = mark.choice.arpeggiate(); if (arpeggiateData.number.has_value()) { - arpeggiate.setNumber(core::NumberLevel{*arpeggiateData.number}); + const core::NumberLevel numberLevel{*arpeggiateData.number}; + reportAdjusted(myScoreWriter.getDiagnostics(), cursorLocation(myCursor), "arpeggiate number", + *arpeggiateData.number, numberLevel.value()); + arpeggiate.setNumber(numberLevel); } if (arpeggiateData.unbroken != api::Bool::unspecified) { Converter converter; arpeggiate.setUnbroken(converter.convert(arpeggiateData.unbroken)); } - setId(arpeggiateData.id, arpeggiate); + setId(arpeggiateData.id, arpeggiate, myScoreWriter.getDiagnostics(), cursorLocation(myCursor)); outNotations.addChoice(core::NotationsChoice::arpeggiate(arpeggiate)); } @@ -463,16 +494,25 @@ core::Notations NotationsWriter::getNotations() const other.setType(myConverter.convert(payload.type)); if (payload.number.has_value()) { - other.setNumber(core::NumberLevel{*payload.number}); + const core::NumberLevel numberLevel{*payload.number}; + reportAdjusted(myScoreWriter.getDiagnostics(), cursorLocation(myCursor), "other-notation number", + *payload.number, numberLevel.value()); + other.setNumber(numberLevel); } if (payload.smufl.has_value()) { other.setSmufl(core::SmuflGlyphName{*payload.smufl}); } - setId(payload.id, other); + setId(payload.id, other, myScoreWriter.getDiagnostics(), cursorLocation(myCursor)); outNotations.addChoice(core::NotationsChoice::otherNotation(other)); } + else if (isMarkPedal(mark.markType)) + { + myScoreWriter.getDiagnostics().report(api::Severity::error, api::DiagnosticCode::droppedData, + cursorLocation(myCursor), + "a pedal mark on a note is not written; write it in a direction"); + } } addWavyLineStarts(ornaments); @@ -521,13 +561,15 @@ void NotationsWriter::addGlissandoAndSlide(core::Notations &outNotations) const if (glissandoStart.glissandoType == api::GlissandoType::slide) { core::Slide slide; - writeAttributesFromGlissandoStart(glissandoStart, slide, resolvedNumber); + writeAttributesFromGlissandoStart(glissandoStart, slide, resolvedNumber, myScoreWriter.getDiagnostics(), + cursorLocation(myCursor)); outNotations.addChoice(core::NotationsChoice::slide(slide)); } else { core::Glissando glissando; - writeAttributesFromGlissandoStart(glissandoStart, glissando, resolvedNumber); + writeAttributesFromGlissandoStart(glissandoStart, glissando, resolvedNumber, myScoreWriter.getDiagnostics(), + cursorLocation(myCursor)); outNotations.addChoice(core::NotationsChoice::glissando(glissando)); } @@ -553,13 +595,15 @@ void NotationsWriter::addGlissandoStop(const api::GlissandoStop &inGlissandoStop if (inGlissandoStop.glissandoType == api::GlissandoType::slide) { core::Slide slide; - writeAttributesFromGlissandoStop(inGlissandoStop, slide, resolvedNumber); + writeAttributesFromGlissandoStop(inGlissandoStop, slide, resolvedNumber, myScoreWriter.getDiagnostics(), + cursorLocation(myCursor)); outNotations.addChoice(core::NotationsChoice::slide(slide)); } else { core::Glissando glissando; - writeAttributesFromGlissandoStop(inGlissandoStop, glissando, resolvedNumber); + writeAttributesFromGlissandoStop(inGlissandoStop, glissando, resolvedNumber, myScoreWriter.getDiagnostics(), + cursorLocation(myCursor)); outNotations.addChoice(core::NotationsChoice::glissando(glissando)); } } @@ -1035,23 +1079,39 @@ void NotationsWriter::addTechnical(const api::MarkData &mark, core::Technical &o case core::TechnicalChoice::Kind::fret: { core::Fret f; int fretValue = 0; + std::size_t length = 0; if (!mark.name.empty()) { try { - fretValue = std::stoi(mark.name); + fretValue = std::stoi(mark.name, &length); } catch (...) { } } + // reported outside the try so a throwing diagnostic handler is not swallowed + if (length != mark.name.size()) + { + myScoreWriter.getDiagnostics().report( + api::Severity::warning, api::DiagnosticCode::invalidValue, cursorLocation(myCursor), + "fret \"" + mark.name + "\" is not a number; using " + std::to_string(fretValue)); + } f.setValue(fretValue); outTechnical.addChoice(core::TechnicalChoice::fret(f)); break; } case core::TechnicalChoice::Kind::string: { core::String s; - s.setValue(core::StringNumber::parse(mark.name)); + core::ValueParseOutcome outcome = core::ValueParseOutcome::valid; + const auto stringNumber = core::StringNumber::parse(mark.name, outcome); + if (outcome != core::ValueParseOutcome::valid) + { + myScoreWriter.getDiagnostics().report( + api::Severity::warning, api::DiagnosticCode::invalidValue, cursorLocation(myCursor), + "string \"" + mark.name + "\" is not a string number; using " + std::to_string(stringNumber.value())); + } + s.setValue(stringNumber); outTechnical.addChoice(core::TechnicalChoice::string(s)); break; } diff --git a/src/private/mx/impl/NoteFunctions.cpp b/src/private/mx/impl/NoteFunctions.cpp index 6f1a61c3e..6d327c2e2 100644 --- a/src/private/mx/impl/NoteFunctions.cpp +++ b/src/private/mx/impl/NoteFunctions.cpp @@ -30,8 +30,8 @@ namespace mx { namespace impl { -NoteFunctions::NoteFunctions(const core::Note &inMxNote, impl::Cursor inCursor) - : myNote{inMxNote}, myCursor{inCursor}, myOutNoteData{} +NoteFunctions::NoteFunctions(const core::Note &inMxNote, MeasureCursor inCursor, DiagnosticsContext diagnostics) + : myNote{inMxNote}, myCursor{inCursor}, myDiagnostics{diagnostics}, myOutNoteData{} { } @@ -260,7 +260,7 @@ void NoteFunctions::parseNotations() const break; } case core::NotationsChoice::Kind::ornaments: { - OrnamentsFunctions funcs{notationsChoice.asOrnaments(), myCursor}; + OrnamentsFunctions funcs{notationsChoice.asOrnaments(), myCursor, myDiagnostics}; funcs.parseOrnaments(myOutNoteData.noteAttachmentData); break; } diff --git a/src/private/mx/impl/NoteFunctions.h b/src/private/mx/impl/NoteFunctions.h index def668952..cd87d0787 100644 --- a/src/private/mx/impl/NoteFunctions.h +++ b/src/private/mx/impl/NoteFunctions.h @@ -7,7 +7,8 @@ #include "mx/api/NoteData.h" #include "mx/core/generated/Note.h" #include "mx/impl/Converter.h" -#include "mx/impl/Cursor.h" +#include "mx/impl/DiagnosticsContext.h" +#include "mx/impl/MeasureCursor.h" #include @@ -20,13 +21,14 @@ class NoteReader; class NoteFunctions { public: - NoteFunctions(const core::Note &inMxNote, impl::Cursor cursor); + NoteFunctions(const core::Note &inMxNote, MeasureCursor cursor, DiagnosticsContext diagnostics = {}); ~NoteFunctions() = default; api::NoteData parseNote() const; private: const core::Note &myNote; - const impl::Cursor myCursor; + const MeasureCursor myCursor; + DiagnosticsContext myDiagnostics; mutable api::NoteData myOutNoteData; mutable std::mutex myMutex; diff --git a/src/private/mx/impl/NoteWriter.cpp b/src/private/mx/impl/NoteWriter.cpp index 5f140fac8..0637af0b4 100644 --- a/src/private/mx/impl/NoteWriter.cpp +++ b/src/private/mx/impl/NoteWriter.cpp @@ -106,7 +106,7 @@ core::Note NoteWriter::getNote(bool isStartOfChord) const setNotehead(); setStemDirection(); setMiscData(); - impl::setId(myNoteData.id, myOutNote); + impl::setId(myNoteData.id, myOutNote, myScoreWriter.getDiagnostics(), cursorLocation(myCursor)); NotationsWriter notationsWriter{myNoteData, myCursor, myScoreWriter}; impl::setAttributesFromPositionData(myNoteData.positionData, myOutNote); if (myNoteData.printData.printObject != api::Bool::unspecified) @@ -170,6 +170,11 @@ core::Note NoteWriter::getNote(bool isStartOfChord) const { core::Beam mxBeam; mxBeam.setNumber(core::BeamLevel{beamIndex + 1}); + if (beam == api::Beam::unspecified) + { + myScoreWriter.getDiagnostics().report(api::Severity::warning, api::DiagnosticCode::missingValueDefaulted, + cursorLocation(myCursor), "beam type is unspecified; using begin"); + } mxBeam.setValue(myConverter.convert(beam)); const auto added = myOutNote.addBeam(std::move(mxBeam)); if (!added) @@ -281,6 +286,11 @@ void NoteWriter::assembleNoteChoice() const { const auto duration = core::PositiveDivisions{core::Decimal{static_cast(myNoteData.durationData.durationTimeTicks)}}; + if (!myNoteData.isGrace) + { + reportAdjusted(myScoreWriter.getDiagnostics(), cursorLocation(myCursor), "note duration", + static_cast(myNoteData.durationData.durationTimeTicks), duration.value().value()); + } if (myNoteData.isGrace) { @@ -340,6 +350,19 @@ void NoteWriter::assembleNoteChoice() const } } +core::Octave NoteWriter::writtenOctave() const +{ + const auto &diagnostics = myScoreWriter.getDiagnostics(); + if (myNoteData.pitchData.step == api::Step::unspecified) + { + diagnostics.report(api::Severity::warning, api::DiagnosticCode::missingValueDefaulted, cursorLocation(myCursor), + "note step is unspecified; using C"); + } + const core::Octave octave{myNoteData.pitchData.octave}; + reportAdjusted(diagnostics, cursorLocation(myCursor), "note octave", myNoteData.pitchData.octave, octave.value()); + return octave; +} + void NoteWriter::setFullNoteTypeChoice() const { if (myNoteData.isRest) @@ -349,7 +372,7 @@ void NoteWriter::setFullNoteTypeChoice() const { core::DisplayStepOctaveGroup pitch; pitch.setDisplayStep(myConverter.convert(myNoteData.pitchData.step)); - pitch.setDisplayOctave(core::Octave{myNoteData.pitchData.octave}); + pitch.setDisplayOctave(writtenOctave()); rest.setDisplayStepOctave(std::move(pitch)); } @@ -367,7 +390,7 @@ void NoteWriter::setFullNoteTypeChoice() const { core::DisplayStepOctaveGroup pitch; pitch.setDisplayStep(myConverter.convert(myNoteData.pitchData.step)); - pitch.setDisplayOctave(core::Octave{myNoteData.pitchData.octave}); + pitch.setDisplayOctave(writtenOctave()); unpitched.setDisplayStepOctave(std::move(pitch)); } @@ -382,7 +405,7 @@ void NoteWriter::setFullNoteTypeChoice() const const auto alter = Converter::convertToAlter(myNoteData.pitchData.alter, myNoteData.pitchData.cents); pitch.setAlter(core::Semitones{core::Decimal{alter}}); } - pitch.setOctave(core::Octave{myNoteData.pitchData.octave}); + pitch.setOctave(writtenOctave()); myOutFullNoteGroup.setChoice(core::FullNoteGroupChoice::pitch(std::move(pitch))); } } @@ -445,6 +468,11 @@ void NoteWriter::setDurationNameAndDots() const const bool isMeasureRest = myNoteData.isRest && myNoteData.isMeasureRest; if (myNoteData.durationData.isDurationNameSpecified && !isMeasureRest) { + if (myNoteData.durationData.durationName == api::DurationName::unspecified) + { + myScoreWriter.getDiagnostics().report(api::Severity::warning, api::DiagnosticCode::missingValueDefaulted, + cursorLocation(myCursor), "note type is unspecified; using maxima"); + } core::NoteType noteType; noteType.setValue(myConverter.convert(myNoteData.durationData.durationName)); myOutNote.setType(std::move(noteType)); @@ -505,7 +533,16 @@ void NoteWriter::setLyrics() const core::Lyric lyric; if (!lyricData.verseNumber.empty()) { - lyric.setNumber(core::NameToken{lyricData.verseNumber}); + core::ValueParseOutcome outcome = core::ValueParseOutcome::valid; + const auto number = core::NameToken::parse(lyricData.verseNumber, outcome); + if (outcome != core::ValueParseOutcome::valid) + { + myScoreWriter.getDiagnostics().report( + api::Severity::warning, api::DiagnosticCode::invalidValue, cursorLocation(myCursor), + "lyric number \"" + lyricData.verseNumber + "\" is not a valid name token; using \"" + + number.value() + "\""); + } + lyric.setNumber(number); } if (!lyricData.verseName.empty()) @@ -514,7 +551,7 @@ void NoteWriter::setLyrics() const } impl::setAttributesFromPositionData(lyricData.positionData, lyric); - impl::setId(lyricData.id, lyric); + impl::setId(lyricData.id, lyric, myScoreWriter.getDiagnostics(), cursorLocation(myCursor)); if (lyricData.positionData.horizontalAlignment != api::HorizontalAlignment::unspecified) { lyric.setJustify(myConverter.convert(lyricData.positionData.horizontalAlignment)); @@ -631,11 +668,18 @@ void NoteWriter::setMiscData() const { // Comma is the item separator in the miscellaneous-field wire encoding, so commas // inside user misc-data are irreversibly replaced with underscores. - std::string::size_type position = 0; - while ((position = s.find(comma, position)) != std::string::npos) + if (s.find(comma) != std::string::npos) { - s.replace(position, comma.size(), underscore); - position++; + const auto original = s; + std::string::size_type position = 0; + while ((position = s.find(comma, position)) != std::string::npos) + { + s.replace(position, comma.size(), underscore); + position++; + } + myScoreWriter.getDiagnostics().report( + api::Severity::warning, api::DiagnosticCode::valueAdjusted, cursorLocation(myCursor), + "note misc data \"" + original + "\" contains a comma; using \"" + s + "\""); } if (isFirst) diff --git a/src/private/mx/impl/NoteWriter.h b/src/private/mx/impl/NoteWriter.h index 8ea602f0d..c17098245 100644 --- a/src/private/mx/impl/NoteWriter.h +++ b/src/private/mx/impl/NoteWriter.h @@ -49,6 +49,8 @@ class NoteWriter void addTie(bool isStart) const; void setNoteChoiceAndFullNoteGroup(bool isStartOfChord) const; void assembleNoteChoice() const; + // The note's octave, reporting a clamp and an unspecified step, which the caller writes as C. + core::Octave writtenOctave() const; void setFullNoteTypeChoice() const; void setStaffAndVoice() const; void setDurationNameAndDots() const; diff --git a/src/private/mx/impl/OrnamentsFunctions.cpp b/src/private/mx/impl/OrnamentsFunctions.cpp index 1579188cc..b8ba69628 100644 --- a/src/private/mx/impl/OrnamentsFunctions.cpp +++ b/src/private/mx/impl/OrnamentsFunctions.cpp @@ -40,8 +40,9 @@ void ornamentsFunctionsParseMordentSpecificAttributes(const core::Mordent &m, ap } } -OrnamentsFunctions::OrnamentsFunctions(const core::Ornaments &inOrnaments, impl::Cursor inCursor) - : myOrnaments{inOrnaments}, myCursor{inCursor} +OrnamentsFunctions::OrnamentsFunctions(const core::Ornaments &inOrnaments, MeasureCursor inCursor, + DiagnosticsContext diagnostics) + : myOrnaments{inOrnaments}, myCursor{inCursor}, myDiagnostics{diagnostics} { } @@ -212,18 +213,20 @@ void OrnamentsFunctions::parseOrnament(const core::OrnamentsGroupChoice &choiceO case 8: outMark.markType = api::MarkType::tremoloSingleEight; break; - default: + default: { // Three slashes, the customary one-note tremolo. Reached by a count of 0, which is a // legal but degenerate single-type tremolo that would draw nothing -- an unmeasured // tremolo is also written with a count of 0, but says so with type="unmeasured" and is // handled above. - // - // TODO: the remap is silent and lossy -- a count of 0 reads back as 3 and is written - // out that way. mx has no warning channel, so there is nowhere to report it. Log the - // downgrade here if a logging framework is ever added. outMark.markType = api::MarkType::tremoloSingleThree; + auto location = measureLocation(myCursor); + location.staffIndex = myCursor.staffIndex; + myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, std::move(location), + "tremolo with " + std::to_string(tremolo.value().value()) + + " marks is not supported; using 3"); break; } + } break; } diff --git a/src/private/mx/impl/OrnamentsFunctions.h b/src/private/mx/impl/OrnamentsFunctions.h index acb1459e9..1a377d316 100644 --- a/src/private/mx/impl/OrnamentsFunctions.h +++ b/src/private/mx/impl/OrnamentsFunctions.h @@ -7,7 +7,8 @@ #include "mx/api/MarkData.h" #include "mx/api/NoteAttachmentData.h" #include "mx/impl/Converter.h" -#include "mx/impl/Cursor.h" +#include "mx/impl/DiagnosticsContext.h" +#include "mx/impl/MeasureCursor.h" namespace mx { @@ -22,7 +23,7 @@ namespace impl class OrnamentsFunctions { public: - OrnamentsFunctions(const core::Ornaments &inOrnaments, impl::Cursor inCursor); + OrnamentsFunctions(const core::Ornaments &inOrnaments, MeasureCursor inCursor, DiagnosticsContext diagnostics = {}); ~OrnamentsFunctions() = default; OrnamentsFunctions(const OrnamentsFunctions &) = default; OrnamentsFunctions(OrnamentsFunctions &&) = default; @@ -30,7 +31,8 @@ class OrnamentsFunctions private: const core::Ornaments &myOrnaments; - const impl::Cursor myCursor; + const MeasureCursor myCursor; + DiagnosticsContext myDiagnostics; private: void parseOrnamentsSet(api::NoteAttachmentData &outAttachments) const; diff --git a/src/private/mx/impl/PartReader.cpp b/src/private/mx/impl/PartReader.cpp index 180cdd9ee..b52df6fac 100644 --- a/src/private/mx/impl/PartReader.cpp +++ b/src/private/mx/impl/PartReader.cpp @@ -74,10 +74,9 @@ void PartReader::readNameDisplay(const core::PartName &nameElement, const std::o } PartReader::PartReader(const core::ScorePart &inScorePart, const core::PartwisePart &inPartwisePartRef, - int globalTicksPerMeasure, const core::ScorePartwise &inScore, int inDivisionsValue, - DiagnosticsContext diagnostics) + int globalTicksPerMeasure, int partIndex, int inDivisionsValue, DiagnosticsContext diagnostics) : myPartwisePart{inPartwisePartRef}, myScorePart{inScorePart}, myNumStaves{-1}, myIsStavesElementPresent{false}, - myGlobalTicksPerMeasure{globalTicksPerMeasure}, myScore{inScore}, myPartIndex{-1}, + myGlobalTicksPerMeasure{globalTicksPerMeasure}, myPartIndex{partIndex}, myConstructedDivisionsValue{inDivisionsValue}, myDiagnostics{diagnostics} { const auto ppId = myPartwisePart.id().value(); @@ -86,9 +85,6 @@ PartReader::PartReader(const core::ScorePart &inScorePart, const core::PartwiseP { MX_THROW("the partwise-part id must match the score-part id"); } - const auto partIndex = findPartIndex(ppId); - MX_ASSERT(partIndex >= 0); - myPartIndex = partIndex; myNumStaves = calculateNumStaves(myIsStavesElementPresent); } @@ -148,6 +144,7 @@ int PartReader::calculateNumStaves(bool &outIsStavesElementPresent) const { outIsStavesElementPresent = false; int numStaves = 1; + int declaredStaves = 1; for (const auto &measure : myPartwisePart.measure()) { @@ -173,11 +170,19 @@ int PartReader::calculateNumStaves(bool &outIsStavesElementPresent) const { outIsStavesElementPresent = true; int temp = *attributes.staves(); + declaredStaves = std::max(declaredStaves, temp); if (temp > numStaves) { numStaves = temp; } } + for (const auto &clef : attributes.clef()) + { + if (clef.number().has_value() && clef.number()->value() > numStaves) + { + numStaves = clef.number()->value(); + } + } break; } case core::MusicDataChoice::Kind::harmony: { @@ -200,6 +205,16 @@ int PartReader::calculateNumStaves(bool &outIsStavesElementPresent) const } } + if (numStaves > declaredStaves) + { + api::Location location; + location.partIndex = myPartIndex; + myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::valueAdjusted, std::move(location), + "staff number " + std::to_string(numStaves) + " is above the staff count " + + std::to_string(declaredStaves) + "; reading the part with " + + std::to_string(numStaves) + " staves"); + } + return numStaves; } @@ -277,6 +292,14 @@ void PartReader::parseScoreInstrument(const core::ScoreInstrument &scoreInstrume Converter c; myOutPartData.instrumentData.soundID = c.convert(instrSound.asSoundID()); } + else + { + api::Location location; + location.partIndex = myPartIndex; + myDiagnostics.report(api::Severity::error, api::DiagnosticCode::droppedData, std::move(location), + "instrument-sound \"" + instrSound.asString() + + "\" is not a known sound; it is not read"); + } } if (vidg.virtualInstrument().has_value()) @@ -367,31 +390,5 @@ void PartReader::parseMidiInstrument(const core::MIDIInstrument &inst) const } } -int PartReader::findPartIndex(const std::string &inPartId) const -{ - const auto &partList = myScore.scoreHeader().partList(); - const auto &firstPart = partList.scorePart(); - int index = 0; - - if (firstPart.id().value() == inPartId) - { - return index; - } - - ++index; - - for (const auto &p : partList.choice()) - { - if (p.isScorePart()) - { - if (p.asScorePart().id().value() == inPartId) - { - return index; - } - ++index; - } - } - return api::INDEX_UNSPECIFIED; -} } // namespace impl } // namespace mx diff --git a/src/private/mx/impl/PartReader.h b/src/private/mx/impl/PartReader.h index c3a5b7c12..9b713323e 100644 --- a/src/private/mx/impl/PartReader.h +++ b/src/private/mx/impl/PartReader.h @@ -32,8 +32,7 @@ class PartReader { public: PartReader(const core::ScorePart &inScorePart, const core::PartwisePart &inPartwisePartRef, - int globalTicksPerMeasure, const core::ScorePartwise &inScore, int inDivisionsValue, - DiagnosticsContext diagnostics = {}); + int globalTicksPerMeasure, int partIndex, int inDivisionsValue, DiagnosticsContext diagnostics = {}); api::PartData getPartData(); impl::MeasureCursor getCursor() const; @@ -48,7 +47,6 @@ class PartReader int myNumStaves; bool myIsStavesElementPresent; const int myGlobalTicksPerMeasure; - const core::ScorePartwise &myScore; int myPartIndex; const int myConstructedDivisionsValue; DiagnosticsContext myDiagnostics; @@ -67,7 +65,6 @@ class PartReader void parseVirtualInstrument(const core::VirtualInstrument &virtualInstrument) const; void parseMidiDeviceInstrumentGroup(const core::ScorePartMIDIGroup &grp) const; void parseMidiInstrument(const core::MIDIInstrument &inst) const; - int findPartIndex(const std::string &inPartId) const; template void updateNumStaves(const ELEMENT_TYPE &element, int &outNumStaves) const { diff --git a/src/private/mx/impl/PartWriter.cpp b/src/private/mx/impl/PartWriter.cpp index f8c9edac5..1b5ceb7b4 100644 --- a/src/private/mx/impl/PartWriter.cpp +++ b/src/private/mx/impl/PartWriter.cpp @@ -74,10 +74,28 @@ PartWriter::PartWriter(const api::PartData &inPartData, int inPartIndex, int inT { } +const DiagnosticsContext &PartWriter::diagnostics() const +{ + return myScoreWriter.getDiagnostics(); +} + +core::Token PartWriter::writtenToken(const char *name, const std::string &text) const +{ + core::ValueParseOutcome outcome = core::ValueParseOutcome::valid; + auto token = core::Token::parse(text, outcome); + if (outcome != core::ValueParseOutcome::valid) + { + diagnostics().report(api::Severity::warning, api::DiagnosticCode::invalidValue, partLocation(myPartIndex), + std::string{name} + " \"" + text + "\" is not a valid id; using \"" + token.value() + + "\""); + } + return token; +} + core::ScorePart PartWriter::getScorePart() const { core::ScorePart scorePart{}; - scorePart.setID(core::Token{myPartData.uniqueId}); + scorePart.setID(writtenToken("part id", myPartData.uniqueId)); // is required, so always write it. print-object is round- // tripped from the model (not force-hidden); deprecated formatting is never @@ -115,7 +133,9 @@ core::ScorePart PartWriter::getScorePart() const core::ScoreInstrument scoreInstrument{}; bool addScoreInstrument = false; - scoreInstrument.setID(core::Token{myPartData.instrumentData.uniqueId}); + scoreInstrument.setID(myPartData.instrumentData.uniqueId.empty() + ? core::Token{} + : writtenToken("instrument id", myPartData.instrumentData.uniqueId)); if (myPartData.instrumentData.name.size() > 0) { @@ -195,7 +215,10 @@ core::ScorePart PartWriter::getScorePart() const if (apiMidiData.devicePort.has_value()) { - midiDevice.setPort(core::MIDI16{*apiMidiData.devicePort}); + const core::MIDI16 port{*apiMidiData.devicePort}; + reportAdjusted(diagnostics(), partLocation(myPartIndex), "midi-device port", *apiMidiData.devicePort, + port.value()); + midiDevice.setPort(port); } // The midi-device attaches to this part's instrument, the same instrument the @@ -218,37 +241,55 @@ core::ScorePart PartWriter::getScorePart() const if (myPartData.instrumentData.midiData.bank >= 0) { addMidiElement = true; - midiInstrument.setMIDIBank(core::MIDI16384{myPartData.instrumentData.midiData.bank}); + const core::MIDI16384 bank{myPartData.instrumentData.midiData.bank}; + reportAdjusted(diagnostics(), partLocation(myPartIndex), "midi-bank", myPartData.instrumentData.midiData.bank, + bank.value()); + midiInstrument.setMIDIBank(bank); } if (myPartData.instrumentData.midiData.channel >= 0) { addMidiElement = true; - midiInstrument.setMIDIChannel(core::MIDI16{myPartData.instrumentData.midiData.channel}); + const core::MIDI16 channel{myPartData.instrumentData.midiData.channel}; + reportAdjusted(diagnostics(), partLocation(myPartIndex), "midi-channel", + myPartData.instrumentData.midiData.channel, channel.value()); + midiInstrument.setMIDIChannel(channel); } if (myPartData.instrumentData.midiData.program >= 0) { addMidiElement = true; - midiInstrument.setMIDIProgram(core::MIDI128{myPartData.instrumentData.midiData.program}); + const core::MIDI128 program{myPartData.instrumentData.midiData.program}; + reportAdjusted(diagnostics(), partLocation(myPartIndex), "midi-program", + myPartData.instrumentData.midiData.program, program.value()); + midiInstrument.setMIDIProgram(program); } if (myPartData.instrumentData.midiData.isElevationSpecified) { addMidiElement = true; - midiInstrument.setElevation(core::RotationDegrees{core::Decimal{myPartData.instrumentData.midiData.elevation}}); + const core::RotationDegrees elevation{core::Decimal{myPartData.instrumentData.midiData.elevation}}; + reportAdjusted(diagnostics(), partLocation(myPartIndex), "elevation", + myPartData.instrumentData.midiData.elevation, elevation.value().value()); + midiInstrument.setElevation(elevation); } if (myPartData.instrumentData.midiData.isPanSpecified) { addMidiElement = true; - midiInstrument.setPan(core::RotationDegrees{core::Decimal{myPartData.instrumentData.midiData.pan}}); + const core::RotationDegrees pan{core::Decimal{myPartData.instrumentData.midiData.pan}}; + reportAdjusted(diagnostics(), partLocation(myPartIndex), "pan", myPartData.instrumentData.midiData.pan, + pan.value().value()); + midiInstrument.setPan(pan); } if (myPartData.instrumentData.midiData.isVolumeSpecified) { addMidiElement = true; - midiInstrument.setVolume(core::Percent{core::Decimal{myPartData.instrumentData.midiData.volume}}); + const core::Percent volume{core::Decimal{myPartData.instrumentData.midiData.volume}}; + reportAdjusted(diagnostics(), partLocation(myPartIndex), "volume", myPartData.instrumentData.midiData.volume, + volume.value().value()); + midiInstrument.setVolume(volume); } if (addMidiElement) @@ -280,6 +321,7 @@ core::ScorePart PartWriter::getScorePart() const core::PartwisePart PartWriter::getPartwisePart() const { core::PartwisePart partwisePart{}; + // getScorePart reports a scrubbed part id partwisePart.setID(core::Token{myPartData.uniqueId}); writeMeasures(partwisePart); return partwisePart; diff --git a/src/private/mx/impl/PartWriter.h b/src/private/mx/impl/PartWriter.h index 865c29c74..77a28835d 100644 --- a/src/private/mx/impl/PartWriter.h +++ b/src/private/mx/impl/PartWriter.h @@ -7,6 +7,7 @@ #include "mx/api/PartData.h" #include +#include namespace mx { @@ -14,10 +15,12 @@ namespace core { class PartwisePart; class ScorePart; +class Token; } // namespace core namespace impl { +class DiagnosticsContext; class ScoreWriter; class PartWriter @@ -35,6 +38,10 @@ class PartWriter mutable std::mutex myMutex; const ScoreWriter &myScoreWriter; + const DiagnosticsContext &diagnostics() const; + // Builds an id token from text, reporting text that is not a valid id. + core::Token writtenToken(const char *name, const std::string &text) const; + private: /// Writes all the measures from myPartData to outPart void writeMeasures(core::PartwisePart &outPart) const; diff --git a/src/private/mx/impl/PropertiesWriter.cpp b/src/private/mx/impl/PropertiesWriter.cpp index 99bbe0a0d..460a7c8d7 100644 --- a/src/private/mx/impl/PropertiesWriter.cpp +++ b/src/private/mx/impl/PropertiesWriter.cpp @@ -47,8 +47,10 @@ namespace mx namespace impl { -PropertiesWriter::PropertiesWriter(core::PartwiseMeasure &inPartwiseMeasure) - : myAttributes{}, myHasContent{false}, myPartwiseMeasure{inPartwiseMeasure} +PropertiesWriter::PropertiesWriter(core::PartwiseMeasure &inPartwiseMeasure, DiagnosticsContext diagnostics, + api::Location location) + : myAttributes{}, myHasContent{false}, myPartwiseMeasure{inPartwiseMeasure}, myDiagnostics{std::move(diagnostics)}, + myLocation{std::move(location)} { } @@ -95,7 +97,7 @@ void PropertiesWriter::writeMultipleRest(int measureCount, api::Bool useSymbols) void PropertiesWriter::writeKey(int staffIndex, const api::KeyData &inKeyData) { core::Key key{}; - impl::setId(inKeyData.id, key); + impl::setId(inKeyData.id, key, myDiagnostics, myLocation); if (staffIndex >= 0) { @@ -115,7 +117,7 @@ void PropertiesWriter::writeKey(int staffIndex, const api::KeyData &inKeyData) myHasContent = true; } -void PropertiesWriter::writeNonTraditionalKey(const api::KeyData &inKeyData, core::Key &ioKey) +void PropertiesWriter::writeNonTraditionalKey(const api::KeyData &inKeyData, core::Key &ioKey) const { Converter converter; std::vector groups; @@ -129,6 +131,11 @@ void PropertiesWriter::writeNonTraditionalKey(const api::KeyData &inKeyData, cor nt.setKeyAccidental(ka); } const auto isUnknown = keyComponent.step == api::Step::unspecified; + if (isUnknown) + { + myDiagnostics.report(api::Severity::warning, api::DiagnosticCode::missingValueDefaulted, myLocation, + "key-step is unspecified; using C"); + } const auto step = isUnknown ? api::Step::c : keyComponent.step; nt.setKeyStep(converter.convert(step)); const auto alter = Converter::convertToAlter(keyComponent.alter, keyComponent.cents); @@ -170,7 +177,8 @@ void PropertiesWriter::writeTraditionalKey(const api::KeyData &inKeyData, core:: // converts a list of api fractions (a primary or interchangeable meter) to a core OneOrMore; // an (invalid) empty meter falls back to a single 4/4 pair static core::OneOrMore propertiesWriterTimeSignatureGroups( - const std::vector &inFractions) + const std::vector &inFractions, const DiagnosticsContext &diagnostics, + const api::Location &location) { std::vector groups; for (const auto &fraction : inFractions) @@ -182,6 +190,8 @@ static core::OneOrMore propertiesWriterTimeSignatureGr } if (groups.empty()) { + diagnostics.report(api::Severity::warning, api::DiagnosticCode::missingValueDefaulted, location, + "time signature has no fractions; using 4/4"); core::TimeSignatureGroup tsg{}; tsg.setBeats("4"); tsg.setBeatType("4"); @@ -195,16 +205,18 @@ static core::OneOrMore propertiesWriterTimeSignatureGr // builds the metered (group) TimeChoice from an api MeteredTimeSignature, and sets the meter's own // symbol/separator on the