55
66#include < sourcemeta/core/jsonschema.h>
77#include < sourcemeta/core/regex.h>
8+ #include < sourcemeta/core/uri.h>
89
910#include < cassert> // assert
1011#include < string_view> // std::string_view
@@ -55,9 +56,9 @@ auto handle_string(const sourcemeta::core::JSON &schema,
5556 const sourcemeta::core::SchemaResolver &,
5657 const sourcemeta::core::JSON &subschema) -> IRScalar {
5758 ONLY_WHITELIST_KEYWORDS (schema, subschema, location.pointer ,
58- {" $schema" , " $id" , " $anchor" , " $defs " , " $vocabulary " ,
59- " type " , " minLength " , " maxLength " , " pattern " ,
60- " format" });
59+ {" $schema" , " $id" , " $anchor" , " $dynamicAnchor " ,
60+ " $defs " , " $vocabulary " , " type " , " minLength " ,
61+ " maxLength " , " pattern " , " format" });
6162 return IRScalar{{.pointer = sourcemeta::core::to_pointer (location.pointer ),
6263 .symbol = symbol (frame, location)},
6364 IRScalarType::String};
@@ -71,8 +72,8 @@ auto handle_object(const sourcemeta::core::JSON &schema,
7172 const sourcemeta::core::JSON &subschema) -> IRObject {
7273 ONLY_WHITELIST_KEYWORDS (
7374 schema, subschema, location.pointer ,
74- {" $defs" , " $schema" , " $id" , " $anchor" , " $vocabulary " , " type " ,
75- " properties" , " required" ,
75+ {" $defs" , " $schema" , " $id" , " $anchor" , " $dynamicAnchor " , " $vocabulary " ,
76+ " type " , " properties" , " required" ,
7677 // Note that most programming languages CANNOT represent the idea
7778 // of additional properties, mainly if they differ from the types of the
7879 // other properties. Therefore, we whitelist this, but we consider it to
@@ -175,9 +176,10 @@ auto handle_integer(const sourcemeta::core::JSON &schema,
175176 const sourcemeta::core::SchemaResolver &,
176177 const sourcemeta::core::JSON &subschema) -> IRScalar {
177178 ONLY_WHITELIST_KEYWORDS (schema, subschema, location.pointer ,
178- {" $schema" , " $id" , " $anchor" , " $defs" , " $vocabulary" ,
179- " type" , " minimum" , " maximum" , " exclusiveMinimum" ,
180- " exclusiveMaximum" , " multipleOf" });
179+ {" $schema" , " $id" , " $anchor" , " $dynamicAnchor" ,
180+ " $defs" , " $vocabulary" , " type" , " minimum" , " maximum" ,
181+ " exclusiveMinimum" , " exclusiveMaximum" ,
182+ " multipleOf" });
181183 return IRScalar{{.pointer = sourcemeta::core::to_pointer (location.pointer ),
182184 .symbol = symbol (frame, location)},
183185 IRScalarType::Integer};
@@ -190,9 +192,10 @@ auto handle_number(const sourcemeta::core::JSON &schema,
190192 const sourcemeta::core::SchemaResolver &,
191193 const sourcemeta::core::JSON &subschema) -> IRScalar {
192194 ONLY_WHITELIST_KEYWORDS (schema, subschema, location.pointer ,
193- {" $schema" , " $id" , " $anchor" , " $defs" , " $vocabulary" ,
194- " type" , " minimum" , " maximum" , " exclusiveMinimum" ,
195- " exclusiveMaximum" , " multipleOf" });
195+ {" $schema" , " $id" , " $anchor" , " $dynamicAnchor" ,
196+ " $defs" , " $vocabulary" , " type" , " minimum" , " maximum" ,
197+ " exclusiveMinimum" , " exclusiveMaximum" ,
198+ " multipleOf" });
196199 return IRScalar{{.pointer = sourcemeta::core::to_pointer (location.pointer ),
197200 .symbol = symbol (frame, location)},
198201 IRScalarType::Number};
@@ -205,9 +208,9 @@ auto handle_array(const sourcemeta::core::JSON &schema,
205208 const sourcemeta::core::SchemaResolver &,
206209 const sourcemeta::core::JSON &subschema) -> IREntity {
207210 ONLY_WHITELIST_KEYWORDS (schema, subschema, location.pointer ,
208- {" $schema" , " $id" , " $anchor" , " $defs " , " $vocabulary " ,
209- " type " , " items " , " minItems " , " maxItems " ,
210- " uniqueItems" , " contains" , " minContains" ,
211+ {" $schema" , " $id" , " $anchor" , " $dynamicAnchor " ,
212+ " $defs " , " $vocabulary " , " type " , " items " , " minItems " ,
213+ " maxItems " , " uniqueItems" , " contains" , " minContains" ,
211214 " maxContains" , " additionalItems" , " prefixItems" });
212215
213216 using Vocabularies = sourcemeta::core::Vocabularies;
@@ -321,9 +324,9 @@ auto handle_enum(const sourcemeta::core::JSON &schema,
321324 const sourcemeta::core::Vocabularies &,
322325 const sourcemeta::core::SchemaResolver &,
323326 const sourcemeta::core::JSON &subschema) -> IREntity {
324- ONLY_WHITELIST_KEYWORDS (
325- schema, subschema, location. pointer ,
326- { " $schema " , " $id " , " $anchor " , " $defs" , " $vocabulary" , " enum" });
327+ ONLY_WHITELIST_KEYWORDS (schema, subschema, location. pointer ,
328+ { " $ schema" , " $id " , " $anchor " , " $dynamicAnchor " ,
329+ " $defs" , " $vocabulary" , " enum" });
327330 const auto &enum_json{subschema.at (" enum" )};
328331
329332 // Boolean and null special cases
@@ -357,9 +360,9 @@ auto handle_anyof(const sourcemeta::core::JSON &schema,
357360 const sourcemeta::core::Vocabularies &,
358361 const sourcemeta::core::SchemaResolver &,
359362 const sourcemeta::core::JSON &subschema) -> IREntity {
360- ONLY_WHITELIST_KEYWORDS (
361- schema, subschema, location. pointer ,
362- { " $schema " , " $id " , " $anchor " , " $defs" , " $vocabulary" , " anyOf" });
363+ ONLY_WHITELIST_KEYWORDS (schema, subschema, location. pointer ,
364+ { " $ schema" , " $id " , " $anchor " , " $dynamicAnchor " ,
365+ " $defs" , " $vocabulary" , " anyOf" });
363366
364367 const auto &any_of{subschema.at (" anyOf" )};
365368 assert (any_of.is_array ());
@@ -391,9 +394,9 @@ auto handle_oneof(const sourcemeta::core::JSON &schema,
391394 const sourcemeta::core::Vocabularies &,
392395 const sourcemeta::core::SchemaResolver &,
393396 const sourcemeta::core::JSON &subschema) -> IREntity {
394- ONLY_WHITELIST_KEYWORDS (
395- schema, subschema, location. pointer ,
396- { " $schema " , " $id " , " $anchor " , " $defs" , " $vocabulary" , " oneOf" });
397+ ONLY_WHITELIST_KEYWORDS (schema, subschema, location. pointer ,
398+ { " $ schema" , " $id " , " $anchor " , " $dynamicAnchor " ,
399+ " $defs" , " $vocabulary" , " oneOf" });
397400
398401 const auto &one_of{subschema.at (" oneOf" )};
399402 assert (one_of.is_array ());
@@ -425,9 +428,9 @@ auto handle_ref(const sourcemeta::core::JSON &schema,
425428 const sourcemeta::core::Vocabularies &,
426429 const sourcemeta::core::SchemaResolver &,
427430 const sourcemeta::core::JSON &subschema) -> IREntity {
428- ONLY_WHITELIST_KEYWORDS (
429- schema, subschema, location. pointer ,
430- { " $schema " , " $id " , " $anchor " , " $defs" , " $vocabulary" , " $ref" });
431+ ONLY_WHITELIST_KEYWORDS (schema, subschema, location. pointer ,
432+ { " $ schema" , " $id " , " $anchor " , " $dynamicAnchor " ,
433+ " $defs" , " $vocabulary" , " $ref" });
431434
432435 auto ref_pointer{sourcemeta::core::to_pointer (location.pointer )};
433436 ref_pointer.push_back (" $ref" );
@@ -454,6 +457,73 @@ auto handle_ref(const sourcemeta::core::JSON &schema,
454457 .symbol = symbol (frame, target_location)}};
455458}
456459
460+ auto handle_dynamic_ref (const sourcemeta::core::JSON &schema,
461+ const sourcemeta::core::SchemaFrame &frame,
462+ const sourcemeta::core::SchemaFrame::Location &location,
463+ const sourcemeta::core::Vocabularies &,
464+ const sourcemeta::core::SchemaResolver &,
465+ const sourcemeta::core::JSON &subschema) -> IREntity {
466+ ONLY_WHITELIST_KEYWORDS (schema, subschema, location.pointer ,
467+ {" $schema" , " $id" , " $anchor" , " $dynamicAnchor" ,
468+ " $defs" , " $vocabulary" , " $dynamicRef" });
469+
470+ auto ref_pointer{sourcemeta::core::to_pointer (location.pointer )};
471+ ref_pointer.push_back (" $dynamicRef" );
472+ const auto ref_weak_pointer{sourcemeta::core::to_weak_pointer (ref_pointer)};
473+
474+ const auto &references{frame.references ()};
475+
476+ // The frame converts single-target dynamic references to static references
477+ const auto static_reference{references.find (
478+ {sourcemeta::core::SchemaReferenceType::Static, ref_weak_pointer})};
479+ if (static_reference != references.cend ()) {
480+ const auto &destination{static_reference->second .destination };
481+ const auto target{frame.traverse (destination)};
482+ if (!target.has_value ()) {
483+ throw UnexpectedSchemaError (schema, location.pointer ,
484+ " Could not resolve reference destination" );
485+ }
486+
487+ const auto &target_location{target.value ().get ()};
488+
489+ return IRReference{
490+ {.pointer = sourcemeta::core::to_pointer (location.pointer ),
491+ .symbol = symbol (frame, location)},
492+ {.pointer = sourcemeta::core::to_pointer (target_location.pointer ),
493+ .symbol = symbol (frame, target_location)}};
494+ }
495+
496+ // Multi-target dynamic reference: find all dynamic anchors with the matching
497+ // fragment and emit a union of all possible targets
498+ const auto dynamic_reference{references.find (
499+ {sourcemeta::core::SchemaReferenceType::Dynamic, ref_weak_pointer})};
500+ assert (dynamic_reference != references.cend ());
501+ assert (dynamic_reference->second .fragment .has_value ());
502+ const auto &fragment{dynamic_reference->second .fragment .value ()};
503+
504+ std::vector<IRType> branches;
505+ for (const auto &[key, entry] : frame.locations ()) {
506+ if (key.first != sourcemeta::core::SchemaReferenceType::Dynamic ||
507+ entry.type != sourcemeta::core::SchemaFrame::LocationType::Anchor) {
508+ continue ;
509+ }
510+
511+ const sourcemeta::core::URI anchor_uri{key.second };
512+ const auto anchor_fragment{anchor_uri.fragment ()};
513+ if (!anchor_fragment.has_value () || anchor_fragment.value () != fragment) {
514+ continue ;
515+ }
516+
517+ branches.push_back ({.pointer = sourcemeta::core::to_pointer (entry.pointer ),
518+ .symbol = symbol (frame, entry)});
519+ }
520+
521+ assert (!branches.empty ());
522+ return IRUnion{{.pointer = sourcemeta::core::to_pointer (location.pointer ),
523+ .symbol = symbol (frame, location)},
524+ std::move (branches)};
525+ }
526+
457527auto default_compiler (const sourcemeta::core::JSON &schema,
458528 const sourcemeta::core::SchemaFrame &frame,
459529 const sourcemeta::core::SchemaFrame::Location &location,
@@ -536,6 +606,9 @@ auto default_compiler(const sourcemeta::core::JSON &schema,
536606 } else if (subschema.defines (" oneOf" )) {
537607 return handle_oneof (schema, frame, location, vocabularies, resolver,
538608 subschema);
609+ } else if (subschema.defines (" $dynamicRef" )) {
610+ return handle_dynamic_ref (schema, frame, location, vocabularies, resolver,
611+ subschema);
539612 } else if (subschema.defines (" $ref" )) {
540613 return handle_ref (schema, frame, location, vocabularies, resolver,
541614 subschema);
0 commit comments