Skip to content

Commit 53e432c

Browse files
authored
Keep Instruction additional data out of band in the Template (#657)
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent a5ceb92 commit 53e432c

File tree

19 files changed

+352
-199
lines changed

19 files changed

+352
-199
lines changed

src/compiler/compile.cc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ auto compile_subschema(const sourcemeta::blaze::Context &context,
6464
// Just a sanity check to ensure every keyword location is indeed valid
6565
assert(context.frame.locations().contains(
6666
{sourcemeta::core::SchemaReferenceType::Static,
67-
step.keyword_location}));
67+
context.extra[step.extra_index].keyword_location}));
6868
steps.push_back(std::move(step));
6969
}
7070
}
@@ -297,6 +297,7 @@ auto compile(const sourcemeta::core::JSON &schema,
297297
auto unevaluated{
298298
sourcemeta::blaze::unevaluated(schema, frame, walker, resolver)};
299299

300+
std::vector<InstructionExtra> instruction_extra;
300301
const Context context{.root = schema,
301302
.frame = frame,
302303
.resources = std::move(resources),
@@ -307,7 +308,8 @@ auto compile(const sourcemeta::core::JSON &schema,
307308
.uses_dynamic_scopes = uses_dynamic_scopes,
308309
.unevaluated = std::move(unevaluated),
309310
.tweaks = effective_tweaks,
310-
.targets = std::move(targets_map)};
311+
.targets = std::move(targets_map),
312+
.extra = instruction_extra};
311313

312314
///////////////////////////////////////////////////////////////////
313315
// (5) Build labels map for dynamic anchors
@@ -399,7 +401,8 @@ auto compile(const sourcemeta::core::JSON &schema,
399401
///////////////////////////////////////////////////////////////////
400402

401403
if (mode == Mode::FastValidation) {
402-
postprocess(compiled_targets, effective_tweaks, uses_dynamic_scopes);
404+
postprocess(compiled_targets, instruction_extra, effective_tweaks,
405+
uses_dynamic_scopes);
403406
}
404407

405408
///////////////////////////////////////////////////////////////////
@@ -417,7 +420,8 @@ auto compile(const sourcemeta::core::JSON &schema,
417420
return {.dynamic = uses_dynamic_scopes,
418421
.track = track,
419422
.targets = std::move(compiled_targets),
420-
.labels = std::move(labels_map)};
423+
.labels = std::move(labels_map),
424+
.extra = std::move(instruction_extra)};
421425
}
422426

423427
auto compile(const sourcemeta::core::JSON &schema,

src/compiler/compile_helpers.h

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -78,16 +78,19 @@ inline auto make_with_resource(const InstructionIndex type,
7878
? to_pointer(dynamic_context.base_schema_location)
7979
: to_pointer(dynamic_context.base_schema_location)
8080
.concat({dynamic_context.keyword})};
81+
const auto extra_index{context.extra.size()};
82+
context.extra.push_back(
83+
{.relative_schema_location = schema_location,
84+
.keyword_location =
85+
to_uri(schema_context.relative_pointer, schema_context.base)
86+
.recompose(),
87+
.schema_resource = schema_resource_id(context.resources, resource)});
8188
return {.type = type,
8289
.relative_instance_location =
8390
to_pointer(dynamic_context.base_instance_location),
8491
.value = value,
8592
.children = {},
86-
.relative_schema_location = schema_location,
87-
.keyword_location =
88-
to_uri(schema_context.relative_pointer, schema_context.base)
89-
.recompose(),
90-
.schema_resource = schema_resource_id(context.resources, resource)};
93+
.extra_index = extra_index};
9194
}
9295

9396
// Instantiate a value-oriented step
@@ -109,42 +112,47 @@ inline auto make(const InstructionIndex type, const Context &context,
109112
? to_pointer(dynamic_context.base_schema_location)
110113
: to_pointer(dynamic_context.base_schema_location)
111114
.concat({dynamic_context.keyword})};
115+
const auto extra_index{context.extra.size()};
116+
context.extra.push_back(
117+
{.relative_schema_location = schema_location,
118+
.keyword_location =
119+
to_uri(schema_context.relative_pointer, schema_context.base)
120+
.recompose(),
121+
.schema_resource = schema_resource_id(context.resources,
122+
schema_context.base.recompose())});
112123
return {.type = type,
113124
.relative_instance_location =
114125
to_pointer(dynamic_context.base_instance_location),
115126
.value = std::move(value),
116127
.children = std::move(children),
117-
.relative_schema_location = schema_location,
118-
.keyword_location =
119-
to_uri(schema_context.relative_pointer, schema_context.base)
120-
.recompose(),
121-
.schema_resource = schema_resource_id(
122-
context.resources, schema_context.base.recompose())};
128+
.extra_index = extra_index};
123129
}
124130

125-
inline auto unroll(const Instruction &step,
131+
inline auto unroll(const Context &context, const Instruction &step,
126132
const sourcemeta::core::WeakPointer &base_instance_location =
127133
sourcemeta::core::empty_weak_pointer) -> Instruction {
134+
auto source_extra{context.extra[step.extra_index]};
135+
const auto extra_index{context.extra.size()};
136+
context.extra.push_back(std::move(source_extra));
128137
return {.type = step.type,
129138
.relative_instance_location =
130139
to_pointer(base_instance_location)
131140
.concat(step.relative_instance_location),
132141
.value = step.value,
133142
.children = {},
134-
.relative_schema_location = step.relative_schema_location,
135-
.keyword_location = step.keyword_location,
136-
.schema_resource = step.schema_resource};
143+
.extra_index = extra_index};
137144
}
138145

139-
inline auto rephrase(const InstructionIndex type, const Instruction &step)
140-
-> Instruction {
146+
inline auto rephrase(const Context &context, const InstructionIndex type,
147+
const Instruction &step) -> Instruction {
148+
auto source_extra{context.extra[step.extra_index]};
149+
const auto extra_index{context.extra.size()};
150+
context.extra.push_back(std::move(source_extra));
141151
return {.type = type,
142152
.relative_instance_location = step.relative_instance_location,
143153
.value = step.value,
144154
.children = {},
145-
.relative_schema_location = step.relative_schema_location,
146-
.keyword_location = step.keyword_location,
147-
.schema_resource = step.schema_resource};
155+
.extra_index = extra_index};
148156
}
149157

150158
inline auto

src/compiler/compile_json.cc

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
namespace {
88
auto to_json(const sourcemeta::blaze::Instruction &instruction,
9+
const std::vector<sourcemeta::blaze::InstructionExtra> &extra,
910
std::vector<sourcemeta::core::JSON::String> &resources)
1011
-> sourcemeta::core::JSON {
1112
// Note that we purposely avoid objects to help consumers avoid potentially
@@ -16,29 +17,29 @@ auto to_json(const sourcemeta::blaze::Instruction &instruction,
1617
// is not meant to be human-readable anyway
1718
result.push_back(sourcemeta::core::to_json(instruction.type));
1819

19-
result.push_back(
20-
sourcemeta::core::to_json(instruction.relative_schema_location));
20+
const auto &meta{extra[instruction.extra_index]};
21+
result.push_back(sourcemeta::core::to_json(meta.relative_schema_location));
2122
result.push_back(
2223
sourcemeta::core::to_json(instruction.relative_instance_location));
2324

24-
const auto match{instruction.keyword_location.find('#')};
25-
if (instruction.schema_resource > 0 && match != std::string::npos) {
26-
if (resources.size() < instruction.schema_resource) {
27-
resources.resize(instruction.schema_resource);
25+
const auto match{meta.keyword_location.find('#')};
26+
if (meta.schema_resource > 0 && match != std::string::npos) {
27+
if (resources.size() < meta.schema_resource) {
28+
resources.resize(meta.schema_resource);
2829
}
2930

30-
if (resources[instruction.schema_resource - 1].empty()) {
31-
resources[instruction.schema_resource - 1] =
32-
instruction.keyword_location.substr(0, match);
31+
if (resources[meta.schema_resource - 1].empty()) {
32+
resources[meta.schema_resource - 1] =
33+
meta.keyword_location.substr(0, match);
3334
}
3435

3536
result.push_back(
36-
sourcemeta::core::JSON{instruction.keyword_location.substr(match)});
37+
sourcemeta::core::JSON{meta.keyword_location.substr(match)});
3738
} else {
38-
result.push_back(sourcemeta::core::to_json(instruction.keyword_location));
39+
result.push_back(sourcemeta::core::to_json(meta.keyword_location));
3940
}
4041

41-
result.push_back(sourcemeta::core::to_json(instruction.schema_resource));
42+
result.push_back(sourcemeta::core::to_json(meta.schema_resource));
4243

4344
// Note that we purposely avoid objects to help consumers avoid potentially
4445
// expensive hash-map or flat-map lookups when parsing back
@@ -59,8 +60,8 @@ auto to_json(const sourcemeta::blaze::Instruction &instruction,
5960
if (!instruction.children.empty()) {
6061
auto children_json{sourcemeta::core::JSON::make_array()};
6162
result.push_back(sourcemeta::core::to_json(
62-
instruction.children, [&resources](const auto &subinstruction) {
63-
return to_json(subinstruction, resources);
63+
instruction.children, [&extra, &resources](const auto &subinstruction) {
64+
return to_json(subinstruction, extra, resources);
6465
}));
6566
}
6667

@@ -81,8 +82,8 @@ auto to_json(const Template &schema_template) -> sourcemeta::core::JSON {
8182
auto targets{sourcemeta::core::JSON::make_array()};
8283
for (const auto &target : schema_template.targets) {
8384
targets.push_back(sourcemeta::core::to_json(
84-
target, [&resources](const auto &instruction) {
85-
return ::to_json(instruction, resources);
85+
target, [&schema_template, &resources](const auto &instruction) {
86+
return ::to_json(instruction, schema_template.extra, resources);
8687
}));
8788
}
8889

src/compiler/default_compiler_draft4.h

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ static auto parse_regex(const std::string &pattern,
2727
}
2828

2929
static auto
30-
relative_schema_location_size(const sourcemeta::blaze::Instruction &step)
30+
relative_schema_location_size(const sourcemeta::blaze::Context &context,
31+
const sourcemeta::blaze::Instruction &step)
3132
-> std::size_t {
32-
return step.relative_schema_location.size();
33+
return context.extra[step.extra_index].relative_schema_location.size();
3334
}
3435

3536
static auto
@@ -103,7 +104,8 @@ compile_properties(const sourcemeta::blaze::Context &context,
103104
// we prefer to evaluate smaller subschemas first, in the hope of failing
104105
// earlier without spending a lot of time on other subschemas
105106
if (context.tweaks.properties_reorder) {
106-
std::ranges::sort(properties, [](const auto &left, const auto &right) {
107+
std::ranges::sort(properties, [&context](const auto &left,
108+
const auto &right) {
107109
const auto left_size{recursive_template_size(left.second)};
108110
const auto right_size{recursive_template_size(right.second)};
109111
if (left_size == right_size) {
@@ -118,8 +120,9 @@ compile_properties(const sourcemeta::blaze::Context &context,
118120
// If both options have a direct enumeration, we choose
119121
// the one with the shorter relative schema location
120122
return relative_schema_location_size(
121-
left.second.at(left_direct_enumeration.value())) <
123+
context, left.second.at(left_direct_enumeration.value())) <
122124
relative_schema_location_size(
125+
context,
123126
right.second.at(right_direct_enumeration.value()));
124127
} else if (left_direct_enumeration.has_value()) {
125128
return true;
@@ -994,39 +997,45 @@ auto compiler_draft4_applicator_properties_with_options(
994997
if (context.mode == Mode::FastValidation && track_evaluation &&
995998
substeps.size() == 1 &&
996999
substeps.front().type == InstructionIndex::AssertionTypeStrict) {
997-
children.push_back(rephrase(sourcemeta::blaze::InstructionIndex::
1000+
children.push_back(rephrase(context,
1001+
sourcemeta::blaze::InstructionIndex::
9981002
AssertionPropertyTypeStrictEvaluate,
9991003
substeps.front()));
10001004
} else if (context.mode == Mode::FastValidation && track_evaluation &&
10011005
substeps.size() == 1 &&
10021006
substeps.front().type == InstructionIndex::AssertionType) {
10031007
children.push_back(rephrase(
1008+
context,
10041009
sourcemeta::blaze::InstructionIndex::AssertionPropertyTypeEvaluate,
10051010
substeps.front()));
10061011
} else if (context.mode == Mode::FastValidation && track_evaluation &&
10071012
substeps.size() == 1 &&
10081013
substeps.front().type ==
10091014
InstructionIndex::AssertionTypeStrictAny) {
1010-
children.push_back(rephrase(sourcemeta::blaze::InstructionIndex::
1015+
children.push_back(rephrase(context,
1016+
sourcemeta::blaze::InstructionIndex::
10111017
AssertionPropertyTypeStrictAnyEvaluate,
10121018
substeps.front()));
10131019

10141020
// NOLINTBEGIN(bugprone-branch-clone)
10151021
} else if (context.mode == Mode::FastValidation && substeps.size() == 1 &&
10161022
substeps.front().type ==
10171023
InstructionIndex::AssertionPropertyTypeStrict) {
1018-
children.push_back(unroll(
1019-
substeps.front(), effective_dynamic_context.base_instance_location));
1024+
children.push_back(
1025+
unroll(context, substeps.front(),
1026+
effective_dynamic_context.base_instance_location));
10201027
} else if (context.mode == Mode::FastValidation && substeps.size() == 1 &&
10211028
substeps.front().type ==
10221029
InstructionIndex::AssertionPropertyType) {
1023-
children.push_back(unroll(
1024-
substeps.front(), effective_dynamic_context.base_instance_location));
1030+
children.push_back(
1031+
unroll(context, substeps.front(),
1032+
effective_dynamic_context.base_instance_location));
10251033
} else if (context.mode == Mode::FastValidation && substeps.size() == 1 &&
10261034
substeps.front().type ==
10271035
InstructionIndex::AssertionPropertyTypeStrictAny) {
1028-
children.push_back(unroll(
1029-
substeps.front(), effective_dynamic_context.base_instance_location));
1036+
children.push_back(
1037+
unroll(context, substeps.front(),
1038+
effective_dynamic_context.base_instance_location));
10301039
// NOLINTEND(bugprone-branch-clone)
10311040

10321041
} else {
@@ -1608,26 +1617,22 @@ auto compiler_draft4_applicator_items_with_options(
16081617
context, schema_context, dynamic_context, ValueNone{},
16091618
std::move(children))};
16101619
if (std::get<ValueTypedHashes>(value_copy).second.first.size() == 3) {
1611-
return {Instruction{
1612-
.type = sourcemeta::blaze::InstructionIndex::
1613-
LoopItemsPropertiesExactlyTypeStrictHash3,
1614-
.relative_instance_location = current.relative_instance_location,
1615-
.value = std::move(value_copy),
1616-
.children = {},
1617-
.relative_schema_location = current.relative_schema_location,
1618-
.keyword_location = current.keyword_location,
1619-
.schema_resource = current.schema_resource}};
1620+
return {Instruction{.type = sourcemeta::blaze::InstructionIndex::
1621+
LoopItemsPropertiesExactlyTypeStrictHash3,
1622+
.relative_instance_location =
1623+
current.relative_instance_location,
1624+
.value = std::move(value_copy),
1625+
.children = {},
1626+
.extra_index = current.extra_index}};
16201627
}
16211628

1622-
return {Instruction{
1623-
.type = sourcemeta::blaze::InstructionIndex::
1624-
LoopItemsPropertiesExactlyTypeStrictHash,
1625-
.relative_instance_location = current.relative_instance_location,
1626-
.value = std::move(value_copy),
1627-
.children = {},
1628-
.relative_schema_location = current.relative_schema_location,
1629-
.keyword_location = current.keyword_location,
1630-
.schema_resource = current.schema_resource}};
1629+
return {Instruction{.type = sourcemeta::blaze::InstructionIndex::
1630+
LoopItemsPropertiesExactlyTypeStrictHash,
1631+
.relative_instance_location =
1632+
current.relative_instance_location,
1633+
.value = std::move(value_copy),
1634+
.children = {},
1635+
.extra_index = current.extra_index}};
16311636
}
16321637
}
16331638

src/compiler/include/sourcemeta/blaze/compiler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ struct Context {
127127
std::tuple<sourcemeta::core::SchemaReferenceType, std::string_view, bool>,
128128
std::pair<std::size_t, const sourcemeta::core::WeakPointer *>>
129129
targets;
130+
/// Accumulator for instruction extra data during compilation
131+
std::vector<InstructionExtra> &extra;
130132
// NOLINTEND(cppcoreguidelines-avoid-const-or-ref-data-members)
131133
};
132134

0 commit comments

Comments
 (0)