Skip to content

Commit

Permalink
Remove the idea of explicit conditions in the compiler
Browse files Browse the repository at this point in the history
Signed-off-by: Juan Cruz Viotti <[email protected]>
  • Loading branch information
jviotti committed Sep 2, 2024
1 parent 1abf1db commit ff6e845
Show file tree
Hide file tree
Showing 16 changed files with 527 additions and 1,459 deletions.
5 changes: 2 additions & 3 deletions src/jsonschema/compile.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ auto compile_subschema(
} else {
return {make<SchemaCompilerAssertionFail>(true, context, schema_context,
dynamic_context,
SchemaCompilerValueNone{}, {})};
SchemaCompilerValueNone{})};
}
}

Expand Down Expand Up @@ -154,8 +154,7 @@ auto compile(const JSON &schema, const SchemaWalker &walker,
true, context, nested_schema_context, dynamic_context,
SchemaCompilerValueUnsignedInteger{label},
compile(context, nested_schema_context, relative_dynamic_context,
empty_pointer, empty_pointer, entry.first.second),
{}));
empty_pointer, empty_pointer, entry.first.second)));
}
}

Expand Down
162 changes: 91 additions & 71 deletions src/jsonschema/compile_describe.cc
Original file line number Diff line number Diff line change
Expand Up @@ -839,11 +839,11 @@ struct DescribeVisitor {
return message.str();
}

auto operator()(const SchemaCompilerAssertionSizeGreater &step) const
auto operator()(const SchemaCompilerAssertionStringSizeLess &step) const
-> std::string {
if (this->keyword == "minLength") {
if (this->keyword == "maxLength") {
std::ostringstream message;
const auto minimum{step_value(step) + 1};
const auto maximum{step_value(step) - 1};

if (is_within_keyword(this->evaluate_path, "propertyNames")) {
assert(this->instance_location.back().is_property());
Expand All @@ -854,8 +854,8 @@ struct DescribeVisitor {
stringify(this->target, message);
}

message << " was expected to consist of at least " << minimum
<< (minimum == 1 ? " character" : " characters");
message << " was expected to consist of at most " << maximum
<< (maximum == 1 ? " character" : " characters");

if (this->valid) {
message << " and";
Expand All @@ -878,45 +878,63 @@ struct DescribeVisitor {
return message.str();
}

if (this->keyword == "minItems") {
assert(this->target.is_array());
return unknown();
}

auto operator()(const SchemaCompilerAssertionStringSizeGreater &step) const
-> std::string {
if (this->keyword == "minLength") {
std::ostringstream message;
const auto minimum{step_value(step) + 1};
message << "The array value was expected to contain at least " << minimum;
assert(minimum > 0);
if (minimum == 1) {
message << " item";

if (is_within_keyword(this->evaluate_path, "propertyNames")) {
assert(this->instance_location.back().is_property());
message << "The object property name "
<< escape_string(this->instance_location.back().to_property());
} else {
message << " items";
message << "The string value ";
stringify(this->target, message);
}

message << " was expected to consist of at least " << minimum
<< (minimum == 1 ? " character" : " characters");

if (this->valid) {
message << " and";
} else {
message << " but";
}

message << " it contained " << this->target.size();
if (this->target.size() == 1) {
message << " item";
message << " it consisted of ";

if (is_within_keyword(this->evaluate_path, "propertyNames")) {
message << this->instance_location.back().to_property().size();
message << (this->instance_location.back().to_property().size() == 1
? " character"
: " characters");
} else {
message << " items";
message << this->target.size();
message << (this->target.size() == 1 ? " character" : " characters");
}

return message.str();
}

if (this->keyword == "minProperties") {
assert(this->target.is_object());
return unknown();
}

auto operator()(const SchemaCompilerAssertionArraySizeLess &step) const
-> std::string {
if (this->keyword == "maxItems") {
assert(this->target.is_array());
std::ostringstream message;
const auto minimum{step_value(step) + 1};
message << "The object value was expected to contain at least "
<< minimum;
assert(minimum > 0);
if (minimum == 1) {
message << " property";
const auto maximum{step_value(step) - 1};
message << "The array value was expected to contain at most " << maximum;
assert(maximum > 0);
if (maximum == 1) {
message << " item";
} else {
message << " properties";
message << " items";
}

if (this->valid) {
Expand All @@ -927,18 +945,9 @@ struct DescribeVisitor {

message << " it contained " << this->target.size();
if (this->target.size() == 1) {
message << " property: ";
message << escape_string(this->target.as_object().cbegin()->first);
message << " item";
} else {
message << " properties: ";
for (auto iterator = this->target.as_object().cbegin();
iterator != this->target.as_object().cend(); ++iterator) {
if (std::next(iterator) == this->target.as_object().cend()) {
message << "and " << escape_string(iterator->first);
} else {
message << escape_string(iterator->first) << ", ";
}
}
message << " items";
}

return message.str();
Expand All @@ -947,55 +956,51 @@ struct DescribeVisitor {
return unknown();
}

auto
operator()(const SchemaCompilerAssertionSizeLess &step) const -> std::string {
if (this->keyword == "maxLength") {
auto operator()(const SchemaCompilerAssertionArraySizeGreater &step) const
-> std::string {
if (this->keyword == "minItems") {
assert(this->target.is_array());
std::ostringstream message;
const auto maximum{step_value(step) - 1};

if (is_within_keyword(this->evaluate_path, "propertyNames")) {
assert(this->instance_location.back().is_property());
message << "The object property name "
<< escape_string(this->instance_location.back().to_property());
const auto minimum{step_value(step) + 1};
message << "The array value was expected to contain at least " << minimum;
assert(minimum > 0);
if (minimum == 1) {
message << " item";
} else {
message << "The string value ";
stringify(this->target, message);
message << " items";
}

message << " was expected to consist of at most " << maximum
<< (maximum == 1 ? " character" : " characters");

if (this->valid) {
message << " and";
} else {
message << " but";
}

message << " it consisted of ";

if (is_within_keyword(this->evaluate_path, "propertyNames")) {
message << this->instance_location.back().to_property().size();
message << (this->instance_location.back().to_property().size() == 1
? " character"
: " characters");
message << " it contained " << this->target.size();
if (this->target.size() == 1) {
message << " item";
} else {
message << this->target.size();
message << (this->target.size() == 1 ? " character" : " characters");
message << " items";
}

return message.str();
}

if (this->keyword == "maxItems") {
assert(this->target.is_array());
return unknown();
}

auto operator()(const SchemaCompilerAssertionObjectSizeLess &step) const
-> std::string {
if (this->keyword == "maxProperties") {
assert(this->target.is_object());
std::ostringstream message;
const auto maximum{step_value(step) - 1};
message << "The array value was expected to contain at most " << maximum;
message << "The object value was expected to contain at most " << maximum;
assert(maximum > 0);
if (maximum == 1) {
message << " item";
message << " property";
} else {
message << " items";
message << " properties";
}

if (this->valid) {
Expand All @@ -1006,21 +1011,36 @@ struct DescribeVisitor {

message << " it contained " << this->target.size();
if (this->target.size() == 1) {
message << " item";
message << " property: ";
message << escape_string(this->target.as_object().cbegin()->first);
} else {
message << " items";
message << " properties: ";
for (auto iterator = this->target.as_object().cbegin();
iterator != this->target.as_object().cend(); ++iterator) {
if (std::next(iterator) == this->target.as_object().cend()) {
message << "and " << escape_string(iterator->first);
} else {
message << escape_string(iterator->first) << ", ";
}
}
}

return message.str();
}

if (this->keyword == "maxProperties") {
return unknown();
}

auto operator()(const SchemaCompilerAssertionObjectSizeGreater &step) const
-> std::string {
if (this->keyword == "minProperties") {
assert(this->target.is_object());
std::ostringstream message;
const auto maximum{step_value(step) - 1};
message << "The object value was expected to contain at most " << maximum;
assert(maximum > 0);
if (maximum == 1) {
const auto minimum{step_value(step) + 1};
message << "The object value was expected to contain at least "
<< minimum;
assert(minimum > 0);
if (minimum == 1) {
message << " property";
} else {
message << " properties";
Expand Down
97 changes: 68 additions & 29 deletions src/jsonschema/compile_evaluate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -347,16 +347,6 @@ auto evaluate_step(
return true; \
}

#define EVALUATE_CONDITION_GUARD(title, step, instance) \
for (const auto &child : step.condition) { \
if (!evaluate_step(child, instance, SchemaCompilerEvaluationMode::Fast, \
std::nullopt, context)) { \
context.pop(step); \
SOURCEMETA_TRACE_END(trace_id, title); \
return true; \
} \
}

if (std::holds_alternative<SchemaCompilerAssertionFail>(step)) {
SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionFail");
const auto &assertion{std::get<SchemaCompilerAssertionFail>(step)};
Expand Down Expand Up @@ -483,30 +473,80 @@ auto evaluate_step(
CALLBACK_PRE(assertion, context.instance_location());
result = std::regex_search(target.to_string(), assertion.value.first);
CALLBACK_POST("SchemaCompilerAssertionRegex", assertion);
} else if (std::holds_alternative<SchemaCompilerAssertionSizeGreater>(step)) {
SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionSizeGreater");
const auto &assertion{std::get<SchemaCompilerAssertionSizeGreater>(step)};
} else if (std::holds_alternative<SchemaCompilerAssertionStringSizeLess>(
step)) {
SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionStringSizeLess");
const auto &assertion{
std::get<SchemaCompilerAssertionStringSizeLess>(step)};
context.push(assertion);
const auto &target{context.resolve_target(instance)};
EVALUATE_IMPLICIT_PRECONDITION("SchemaCompilerAssertionStringSizeLess",
assertion, target.is_string());
CALLBACK_PRE(assertion, context.instance_location());
result = (target.size() < assertion.value);
CALLBACK_POST("SchemaCompilerAssertionStringSizeLess", assertion);
} else if (std::holds_alternative<SchemaCompilerAssertionStringSizeGreater>(
step)) {
SOURCEMETA_TRACE_START(trace_id,
"SchemaCompilerAssertionStringSizeGreater");
const auto &assertion{
std::get<SchemaCompilerAssertionStringSizeGreater>(step)};
context.push(assertion);
const auto &target{context.resolve_target(instance)};
EVALUATE_IMPLICIT_PRECONDITION("SchemaCompilerAssertionStringSizeGreater",
assertion, target.is_string());
CALLBACK_PRE(assertion, context.instance_location());
result = (target.size() > assertion.value);
CALLBACK_POST("SchemaCompilerAssertionStringSizeGreater", assertion);

} else if (std::holds_alternative<SchemaCompilerAssertionArraySizeLess>(
step)) {
SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionArraySizeLess");
const auto &assertion{std::get<SchemaCompilerAssertionArraySizeLess>(step)};
context.push(assertion);
// TODO: Get rid of this
EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionSizeGreater", assertion,
instance);
const auto &target{context.resolve_target(instance)};
EVALUATE_IMPLICIT_PRECONDITION("SchemaCompilerAssertionArraySizeLess",
assertion, target.is_array());
CALLBACK_PRE(assertion, context.instance_location());
result = (target.size() < assertion.value);
CALLBACK_POST("SchemaCompilerAssertionArraySizeLess", assertion);
} else if (std::holds_alternative<SchemaCompilerAssertionArraySizeGreater>(
step)) {
SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionArraySizeGreater");
const auto &assertion{
std::get<SchemaCompilerAssertionArraySizeGreater>(step)};
context.push(assertion);
const auto &target{context.resolve_target(instance)};
result = (target.is_array() || target.is_object() || target.is_string()) &&
(target.size() > assertion.value);
CALLBACK_POST("SchemaCompilerAssertionSizeGreater", assertion);
} else if (std::holds_alternative<SchemaCompilerAssertionSizeLess>(step)) {
SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionSizeLess");
const auto &assertion{std::get<SchemaCompilerAssertionSizeLess>(step)};
EVALUATE_IMPLICIT_PRECONDITION("SchemaCompilerAssertionArraySizeGreater",
assertion, target.is_array());
CALLBACK_PRE(assertion, context.instance_location());
result = (target.size() > assertion.value);
CALLBACK_POST("SchemaCompilerAssertionArraySizeGreater", assertion);
} else if (std::holds_alternative<SchemaCompilerAssertionObjectSizeLess>(
step)) {
SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionObjectSizeLess");
const auto &assertion{
std::get<SchemaCompilerAssertionObjectSizeLess>(step)};
context.push(assertion);
// TODO: Get rid of this
EVALUATE_CONDITION_GUARD("SchemaCompilerAssertionSizeLess", assertion,
instance);
const auto &target{context.resolve_target(instance)};
EVALUATE_IMPLICIT_PRECONDITION("SchemaCompilerAssertionObjectSizeLess",
assertion, target.is_object());
CALLBACK_PRE(assertion, context.instance_location());
result = (target.size() < assertion.value);
CALLBACK_POST("SchemaCompilerAssertionObjectSizeLess", assertion);
} else if (std::holds_alternative<SchemaCompilerAssertionObjectSizeGreater>(
step)) {
SOURCEMETA_TRACE_START(trace_id,
"SchemaCompilerAssertionObjectSizeGreater");
const auto &assertion{
std::get<SchemaCompilerAssertionObjectSizeGreater>(step)};
context.push(assertion);
const auto &target{context.resolve_target(instance)};
result = (target.is_array() || target.is_object() || target.is_string()) &&
(target.size() < assertion.value);
CALLBACK_POST("SchemaCompilerAssertionSizeLess", assertion);
EVALUATE_IMPLICIT_PRECONDITION("SchemaCompilerAssertionObjectSizeGreater",
assertion, target.is_object());
CALLBACK_PRE(assertion, context.instance_location());
result = (target.size() > assertion.value);
CALLBACK_POST("SchemaCompilerAssertionObjectSizeGreater", assertion);
} else if (std::holds_alternative<SchemaCompilerAssertionEqual>(step)) {
SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionEqual");
const auto &assertion{std::get<SchemaCompilerAssertionEqual>(step)};
Expand Down Expand Up @@ -1358,7 +1398,6 @@ auto evaluate_step(
#undef CALLBACK_POST
#undef CALLBACK_ANNOTATION
#undef EVALUATE_IMPLICIT_PRECONDITION
#undef EVALUATE_CONDITION_GUARD
// We should never get here
assert(false);
return result;
Expand Down
Loading

0 comments on commit ff6e845

Please sign in to comment.