Skip to content

Commit

Permalink
[WIP] Decouple unevaluatedProperties from annotations
Browse files Browse the repository at this point in the history
Signed-off-by: Juan Cruz Viotti <[email protected]>
  • Loading branch information
jviotti committed Oct 16, 2024
1 parent 43cac42 commit 0c81da0
Show file tree
Hide file tree
Showing 13 changed files with 371 additions and 139 deletions.
1 change: 1 addition & 0 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ if(BLAZE_EVALUATOR)
evaluator_draft4.cc
evaluator_draft6.cc
evaluator_draft7.cc
evaluator_2019_09.cc
evaluator_2020_12.cc)
endif()

Expand Down
45 changes: 45 additions & 0 deletions benchmark/evaluator_2019_09.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <benchmark/benchmark.h>

#include <cassert> // assert

#include <sourcemeta/jsontoolkit/json.h>
#include <sourcemeta/jsontoolkit/jsonschema.h>

#include <sourcemeta/blaze/compiler.h>
#include <sourcemeta/blaze/evaluator.h>

static void Evaluator_2019_09_Unevaluated_Properties(benchmark::State &state) {
const sourcemeta::jsontoolkit::JSON schema{
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "object",
"properties": {
"name": true,
"prohibited": false
},
"unevaluatedProperties": false,
"$ref": "#/$defs/extension",
"$defs": {
"extension": {
"patternProperties": { "^x-": true }
}
}
})JSON")};

const auto instance{sourcemeta::jsontoolkit::parse(R"JSON([
"name": "John Doe",
"x-foo": "bar"
])JSON")};

const auto schema_template{sourcemeta::blaze::compile(
schema, sourcemeta::jsontoolkit::default_schema_walker,
sourcemeta::jsontoolkit::official_resolver,
sourcemeta::blaze::default_schema_compiler)};
for (auto _ : state) {
auto result{sourcemeta::blaze::evaluate(schema_template, instance)};
assert(result);
benchmark::DoNotOptimize(result);
}
}

BENCHMARK(Evaluator_2019_09_Unevaluated_Properties);
4 changes: 4 additions & 0 deletions src/compiler/compile_describe.cc
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,10 @@ struct DescribeVisitor {
return describe_reference(this->target);
}

auto operator()(const ControlEvaluate &) const -> std::string {
return unknown();
}

auto operator()(const ControlJump &) const -> std::string {
return describe_reference(this->target);
}
Expand Down
6 changes: 6 additions & 0 deletions src/compiler/compile_json.cc
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@ auto value_to_json(const T &value) -> sourcemeta::jsontoolkit::JSON {
data.push_back(sourcemeta::jsontoolkit::JSON{value.second});
result.assign("value", std::move(data));
return result;
} else if constexpr (std::is_same_v<ValuePointer, T>) {
result.assign("type", sourcemeta::jsontoolkit::JSON{"pointer"});
result.assign("value", sourcemeta::jsontoolkit::JSON{
sourcemeta::jsontoolkit::to_string(value)});
return result;
} else {
static_assert(std::is_same_v<ValueNone, T>);
return sourcemeta::jsontoolkit::JSON{nullptr};
Expand Down Expand Up @@ -296,6 +301,7 @@ struct StepVisitor {
HANDLE_STEP("loop", "contains", LoopContains)
HANDLE_STEP("control", "label", ControlLabel)
HANDLE_STEP("control", "mark", ControlMark)
HANDLE_STEP("control", "evaluate", ControlEvaluate)
HANDLE_STEP("control", "jump", ControlJump)
HANDLE_STEP("control", "dynamic-anchor-jump", ControlDynamicAnchorJump)

Expand Down
50 changes: 21 additions & 29 deletions src/compiler/default_compiler_2019_09.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ auto compiler_2019_09_core_annotation(const Context &context,
schema_context.schema.at(dynamic_context.keyword)})};
}

auto compiler_2019_09_applicator_contains_conditional_annotate(
auto compiler_2019_09_applicator_contains_with_options(
const Context &context, const SchemaContext &schema_context,
const DynamicContext &dynamic_context, const bool annotate) -> Template {
if (schema_context.schema.defines("type") &&
Expand Down Expand Up @@ -170,31 +170,31 @@ auto compiler_2019_09_applicator_contains(const Context &context,
const SchemaContext &schema_context,
const DynamicContext &dynamic_context)
-> Template {
return compiler_2019_09_applicator_contains_conditional_annotate(
return compiler_2019_09_applicator_contains_with_options(
context, schema_context, dynamic_context, false);
}

auto compiler_2019_09_applicator_additionalproperties(
const Context &context, const SchemaContext &schema_context,
const DynamicContext &dynamic_context) -> Template {
return compiler_draft4_applicator_additionalproperties_conditional_annotation(
return compiler_draft4_applicator_additionalproperties_with_options(
context, schema_context, dynamic_context,
context.uses_unevaluated_properties || context.mode == Mode::Exhaustive);
context.mode == Mode::Exhaustive, context.uses_unevaluated_properties);
}

auto compiler_2019_09_applicator_items(const Context &context,
const SchemaContext &schema_context,
const DynamicContext &dynamic_context)
-> Template {
return compiler_draft4_applicator_items_conditional_annotation(
return compiler_draft4_applicator_items_with_options(
context, schema_context, dynamic_context,
context.uses_unevaluated_items || context.mode == Mode::Exhaustive);
}

auto compiler_2019_09_applicator_additionalitems(
const Context &context, const SchemaContext &schema_context,
const DynamicContext &dynamic_context) -> Template {
return compiler_draft4_applicator_additionalitems_conditional_annotation(
return compiler_draft4_applicator_additionalitems_with_options(
context, schema_context, dynamic_context,
context.uses_unevaluated_items || context.mode == Mode::Exhaustive);
}
Expand Down Expand Up @@ -247,30 +247,22 @@ auto compiler_2019_09_applicator_unevaluatedproperties(
return {};
}

ValueStrings dependencies{"unevaluatedProperties"};

if (schema_context.vocabularies.contains(
"https://json-schema.org/draft/2019-09/vocab/applicator")) {
dependencies.push_back("properties");
dependencies.push_back("patternProperties");
dependencies.push_back("additionalProperties");
}

if (schema_context.vocabularies.contains(
"https://json-schema.org/draft/2020-12/vocab/applicator")) {
dependencies.push_back("properties");
dependencies.push_back("patternProperties");
dependencies.push_back("additionalProperties");
}

Template children{compile(context, schema_context, relative_dynamic_context,
sourcemeta::jsontoolkit::empty_pointer,
sourcemeta::jsontoolkit::empty_pointer)};
children.push_back(make<AnnotationBasenameToParent>(
true, context, schema_context, relative_dynamic_context, ValueNone{}));

if (context.mode == Mode::Exhaustive) {
children.push_back(make<AnnotationBasenameToParent>(
true, context, schema_context, relative_dynamic_context, ValueNone{}));
}

// TODO: Do this out the box on LoopPropertiesUnevaluated?
children.push_back(make<ControlEvaluate>(false, context, schema_context,
relative_dynamic_context,
ValuePointer{}));

return {make<AnnotationLoopPropertiesUnevaluated>(
true, context, schema_context, dynamic_context, std::move(dependencies),
true, context, schema_context, dynamic_context, ValueNone{},
std::move(children))};
}

Expand All @@ -297,17 +289,17 @@ auto compiler_2019_09_core_recursiveref(const Context &context,
auto compiler_2019_09_applicator_properties(
const Context &context, const SchemaContext &schema_context,
const DynamicContext &dynamic_context) -> Template {
return compiler_draft4_applicator_properties_conditional_annotation(
return compiler_draft4_applicator_properties_with_options(
context, schema_context, dynamic_context,
context.uses_unevaluated_properties || context.mode == Mode::Exhaustive);
context.mode == Mode::Exhaustive, context.uses_unevaluated_properties);
}

auto compiler_2019_09_applicator_patternproperties(
const Context &context, const SchemaContext &schema_context,
const DynamicContext &dynamic_context) -> Template {
return compiler_draft4_applicator_patternproperties_conditional_annotation(
return compiler_draft4_applicator_patternproperties_with_options(
context, schema_context, dynamic_context,
context.uses_unevaluated_properties || context.mode == Mode::Exhaustive);
context.mode == Mode::Exhaustive, context.uses_unevaluated_properties);
}

} // namespace internal
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/default_compiler_2020_12.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ auto compiler_2020_12_applicator_contains(const Context &context,
const SchemaContext &schema_context,
const DynamicContext &dynamic_context)
-> Template {
return compiler_2019_09_applicator_contains_conditional_annotate(
return compiler_2019_09_applicator_contains_with_options(
context, schema_context, dynamic_context,
context.uses_unevaluated_items || context.mode == Mode::Exhaustive);
}
Expand Down
57 changes: 40 additions & 17 deletions src/compiler/default_compiler_draft4.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

#include "compile_helpers.h"

#include <iostream>

static auto parse_regex(const std::string &pattern,
const sourcemeta::jsontoolkit::URI &base,
const sourcemeta::jsontoolkit::Pointer &schema_location)
Expand Down Expand Up @@ -382,9 +384,10 @@ auto compiler_draft4_applicator_oneof(const Context &context,
std::move(disjunctors))};
}

auto compiler_draft4_applicator_properties_conditional_annotation(
auto compiler_draft4_applicator_properties_with_options(
const Context &context, const SchemaContext &schema_context,
const DynamicContext &dynamic_context, const bool annotate) -> Template {
const DynamicContext &dynamic_context, const bool annotate,
const bool track_evaluation) -> Template {
assert(schema_context.schema.at(dynamic_context.keyword).is_object());
if (schema_context.schema.at(dynamic_context.keyword).empty()) {
return {};
Expand Down Expand Up @@ -501,6 +504,12 @@ auto compiler_draft4_applicator_properties_conditional_annotation(
Template children;

for (auto &&[name, substeps] : properties) {
if (track_evaluation) {
substeps.push_back(make<ControlEvaluate>(false, context, schema_context,
relative_dynamic_context,
ValuePointer{name}));
}

if (annotate) {
substeps.push_back(make<AnnotationEmit>(
true, context, schema_context, relative_dynamic_context,
Expand Down Expand Up @@ -579,13 +588,14 @@ auto compiler_draft4_applicator_properties_conditional_annotation(
auto compiler_draft4_applicator_properties(
const Context &context, const SchemaContext &schema_context,
const DynamicContext &dynamic_context) -> Template {
return compiler_draft4_applicator_properties_conditional_annotation(
context, schema_context, dynamic_context, false);
return compiler_draft4_applicator_properties_with_options(
context, schema_context, dynamic_context, false, false);
}

auto compiler_draft4_applicator_patternproperties_conditional_annotation(
auto compiler_draft4_applicator_patternproperties_with_options(
const Context &context, const SchemaContext &schema_context,
const DynamicContext &dynamic_context, const bool annotate) -> Template {
const DynamicContext &dynamic_context, const bool annotate,
const bool track_evaluation) -> Template {
assert(schema_context.schema.at(dynamic_context.keyword).is_object());
if (schema_context.schema.at(dynamic_context.keyword).empty()) {
return {};
Expand Down Expand Up @@ -622,6 +632,12 @@ auto compiler_draft4_applicator_patternproperties_conditional_annotation(
ValueNone{}));
}

if (track_evaluation) {
substeps.push_back(make<ControlEvaluate>(false, context, schema_context,
relative_dynamic_context,
ValuePointer{}));
}

// If the `patternProperties` subschema for the given pattern does
// nothing, then we can avoid generating an entire loop for it
if (!substeps.empty()) {
Expand Down Expand Up @@ -649,13 +665,14 @@ auto compiler_draft4_applicator_patternproperties_conditional_annotation(
auto compiler_draft4_applicator_patternproperties(
const Context &context, const SchemaContext &schema_context,
const DynamicContext &dynamic_context) -> Template {
return compiler_draft4_applicator_patternproperties_conditional_annotation(
context, schema_context, dynamic_context, false);
return compiler_draft4_applicator_patternproperties_with_options(
context, schema_context, dynamic_context, false, false);
}

auto compiler_draft4_applicator_additionalproperties_conditional_annotation(
auto compiler_draft4_applicator_additionalproperties_with_options(
const Context &context, const SchemaContext &schema_context,
const DynamicContext &dynamic_context, const bool annotate) -> Template {
const DynamicContext &dynamic_context, const bool annotate,
const bool track_evaluation) -> Template {
if (schema_context.schema.defines("type") &&
schema_context.schema.at("type").is_string() &&
schema_context.schema.at("type").to_string() != "object") {
Expand All @@ -671,6 +688,12 @@ auto compiler_draft4_applicator_additionalproperties_conditional_annotation(
true, context, schema_context, relative_dynamic_context, ValueNone{}));
}

if (track_evaluation) {
children.push_back(make<ControlEvaluate>(false, context, schema_context,
relative_dynamic_context,
ValuePointer{}));
}

ValuePropertyFilter filter;

if (schema_context.schema.defines("properties") &&
Expand Down Expand Up @@ -726,8 +749,8 @@ auto compiler_draft4_applicator_additionalproperties_conditional_annotation(
auto compiler_draft4_applicator_additionalproperties(
const Context &context, const SchemaContext &schema_context,
const DynamicContext &dynamic_context) -> Template {
return compiler_draft4_applicator_additionalproperties_conditional_annotation(
context, schema_context, dynamic_context, false);
return compiler_draft4_applicator_additionalproperties_with_options(
context, schema_context, dynamic_context, false, false);
}

auto compiler_draft4_validation_pattern(const Context &context,
Expand Down Expand Up @@ -895,7 +918,7 @@ auto compiler_draft4_applicator_items_array(
std::move(children))};
}

auto compiler_draft4_applicator_items_conditional_annotation(
auto compiler_draft4_applicator_items_with_options(
const Context &context, const SchemaContext &schema_context,
const DynamicContext &dynamic_context, const bool annotate) -> Template {
if (schema_context.schema.defines("type") &&
Expand Down Expand Up @@ -937,8 +960,8 @@ auto compiler_draft4_applicator_items(const Context &context,
const SchemaContext &schema_context,
const DynamicContext &dynamic_context)
-> Template {
return compiler_draft4_applicator_items_conditional_annotation(
context, schema_context, dynamic_context, false);
return compiler_draft4_applicator_items_with_options(context, schema_context,
dynamic_context, false);
}

auto compiler_draft4_applicator_additionalitems_from_cursor(
Expand Down Expand Up @@ -969,7 +992,7 @@ auto compiler_draft4_applicator_additionalitems_from_cursor(
ValueUnsignedInteger{cursor}, std::move(children))};
}

auto compiler_draft4_applicator_additionalitems_conditional_annotation(
auto compiler_draft4_applicator_additionalitems_with_options(
const Context &context, const SchemaContext &schema_context,
const DynamicContext &dynamic_context, const bool annotate) -> Template {
if (schema_context.schema.defines("type") &&
Expand Down Expand Up @@ -998,7 +1021,7 @@ auto compiler_draft4_applicator_additionalitems_conditional_annotation(
auto compiler_draft4_applicator_additionalitems(
const Context &context, const SchemaContext &schema_context,
const DynamicContext &dynamic_context) -> Template {
return compiler_draft4_applicator_additionalitems_conditional_annotation(
return compiler_draft4_applicator_additionalitems_with_options(
context, schema_context, dynamic_context, false);
}

Expand Down
30 changes: 30 additions & 0 deletions src/evaluator/context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#include <cassert> // assert

#include <iostream>

namespace sourcemeta::blaze {

auto EvaluationContext::prepare(const sourcemeta::jsontoolkit::JSON &instance)
Expand Down Expand Up @@ -317,4 +319,32 @@ auto EvaluationContext::find_dynamic_anchor(const std::string &anchor) const
return std::nullopt;
}

auto EvaluationContext::evaluate(
const sourcemeta::jsontoolkit::Pointer &relative_instance_location)
-> void {
std::cerr << "EVALUATED! ";

// TODO: Improve Pointer API to make this easier
auto new_instance_location = this->instance_location_;
for (const auto &token : relative_instance_location) {
if (token.is_property()) {
new_instance_location.push_back(token.to_property());
} else {
new_instance_location.push_back(token.to_index());
}
}

sourcemeta::jsontoolkit::stringify(new_instance_location, std::cerr);
std::cerr << "\n";
this->evaluated_.push_back(std::move(new_instance_location));
}

auto EvaluationContext::is_evaluated(
const sourcemeta::jsontoolkit::JSON::String &property) const -> bool {
auto expected_instance_location = this->instance_location_;
expected_instance_location.push_back(property);
return std::find(this->evaluated_.cbegin(), this->evaluated_.cend(),
expected_instance_location) != this->evaluated_.cend();
}

} // namespace sourcemeta::blaze
Loading

0 comments on commit 0c81da0

Please sign in to comment.