hecks 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/hecks/adapters/driven/claude_code.rb +3 -3
- data/lib/hecks/adapters/driven/d1.rb +48 -28
- data/lib/hecks/adapters/driven/folder.rb +10 -3
- data/lib/hecks/adapters/driven/google_authentication.rb +2 -4
- data/lib/hecks/adapters/driven/heki/journal.rb +1 -1
- data/lib/hecks/adapters/driven/heki.rb +3 -5
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +4 -1
- data/lib/hecks/adapters/driven/lambda.rb +2 -2
- data/lib/hecks/adapters/driven/local_storage.adapter +3 -0
- data/lib/hecks/adapters/driven/local_storage.rb +120 -0
- data/lib/hecks/adapters/driven/memory.rb +48 -3
- data/lib/hecks/adapters/driven/postgres/codec.rb +3 -1
- data/lib/hecks/adapters/driven/postgres/outbox.rb +97 -0
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +3 -2
- data/lib/hecks/adapters/driven/postgres.rb +25 -14
- data/lib/hecks/adapters/driven/prism.rb +11 -0
- data/lib/hecks/adapters/driven/sql_query_builder.rb +45 -20
- data/lib/hecks/adapters/driven/sqlite/codec.rb +3 -1
- data/lib/hecks/adapters/driven/sqlite/projection.rb +17 -1
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +23 -1
- data/lib/hecks/adapters/driven/sqlite.rb +87 -8
- data/lib/hecks/adapters/driven.rb +4 -0
- data/lib/hecks/adapters.rb +4 -0
- data/lib/hecks/behaviors/dsl.rb +7 -0
- data/lib/hecks/behaviors/expectations.rb +32 -4
- data/lib/hecks/behaviors/rspec.rb +2 -0
- data/lib/hecks/behaviors/runner.rb +4 -0
- data/lib/hecks/bluebook/aggregate.rb +6 -5
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +4 -4
- data/lib/hecks/bluebook/assembly/build.rb +6 -3
- data/lib/hecks/bluebook/assembly/contract.rb +5 -1
- data/lib/hecks/bluebook/assembly/contracts.rb +4 -0
- data/lib/hecks/bluebook/assembly/marks.rb +2 -1
- data/lib/hecks/bluebook/assembly.rb +1 -3
- data/lib/hecks/bluebook/attribute.rb +6 -0
- data/lib/hecks/bluebook/behaviour/policy.rb +18 -0
- data/lib/hecks/bluebook/behaviour/process_manager.rb +17 -1
- data/lib/hecks/bluebook/behaviour/traits.rb +1 -1
- data/lib/hecks/bluebook/command.rb +6 -5
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +4 -1
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +524 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +19 -472
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +14 -6
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +5 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +848 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +25 -784
- data/lib/hecks/bluebook/dsl/command_builder.rb +53 -13
- data/lib/hecks/bluebook/dsl/const_shim.rb +6 -0
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +20 -7
- data/lib/hecks/bluebook/dsl/entity_builder.rb +27 -6
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +12 -2
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +8 -1
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +24 -12
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +40 -1
- data/lib/hecks/bluebook/dsl/policy_builder.rb +10 -1
- data/lib/hecks/bluebook/dsl/port_builder.rb +6 -1
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +33 -12
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +56 -12
- data/lib/hecks/bluebook/dsl/query_builder.rb +7 -1
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +11 -3
- data/lib/hecks/bluebook/dsl/rule_reference.rb +3 -1
- data/lib/hecks/bluebook/dsl/translation_builder.rb +13 -2
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +8 -1
- data/lib/hecks/bluebook/dsl/word_gate.rb +5 -0
- data/lib/hecks/bluebook/dsl/world_builder.rb +13 -3
- data/lib/hecks/bluebook/entity.rb +5 -3
- data/lib/hecks/bluebook/expression/ast_json.rb +88 -9
- data/lib/hecks/bluebook/expression/ast_reader.rb +107 -0
- data/lib/hecks/bluebook/expression/canonical_form.rb +7 -0
- data/lib/hecks/bluebook/expression/evaluator.rb +64 -11
- data/lib/hecks/bluebook/expression/projection.json +12 -0
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +4 -3
- data/lib/hecks/bluebook/expression/resolver.rb +83 -11
- data/lib/hecks/bluebook/expression.rb +1 -0
- data/lib/hecks/bluebook/hexagon.rb +10 -0
- data/lib/hecks/bluebook/lifecycle.rb +9 -0
- data/lib/hecks/bluebook/meta_validator/judge.rb +10 -4
- data/lib/hecks/bluebook/meta_validator/plan.rb +4 -0
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/shapes.rb +3 -3
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +7 -4
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +42 -3
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +1 -1
- data/lib/hecks/bluebook/meta_validator.rb +5 -5
- data/lib/hecks/bluebook/model_check.rb +119 -82
- data/lib/hecks/bluebook/pattern_subset.rb +9 -0
- data/lib/hecks/bluebook/policy.rb +2 -1
- data/lib/hecks/bluebook/process_manager.rb +6 -0
- data/lib/hecks/bluebook/project_register.rb +45 -25
- data/lib/hecks/bluebook/query.rb +4 -0
- data/lib/hecks/bluebook/read_model.rb +2 -2
- data/lib/hecks/bluebook/smoke_test.rb +5 -0
- data/lib/hecks/bluebook/synthesizer.rb +2 -1
- data/lib/hecks/bluebook/translation.rb +3 -0
- data/lib/hecks/bluebook/value_object.rb +2 -2
- data/lib/hecks/codemod.rb +13 -0
- data/lib/hecks/doc/reference.rb +17 -11
- data/lib/hecks/facade/cli_runner.rb +2 -2
- data/lib/hecks/facade/command_request.rb +11 -11
- data/lib/hecks/facade/handle.rb +3 -3
- data/lib/hecks/facade/surface/aggregate_door.rb +52 -38
- data/lib/hecks/facade.rb +4 -0
- data/lib/hecks/forms/app.rb +10 -6
- data/lib/hecks/forms/command_form_renderer.rb +3 -3
- data/lib/hecks/forms/field_renderer.rb +14 -9
- data/lib/hecks/forms/field_shape.rb +7 -2
- data/lib/hecks/forms/html.rb +5 -2
- data/lib/hecks/forms/index_renderer.rb +1 -1
- data/lib/hecks/forms/page.rb +7 -3
- data/lib/hecks/forms/query_form_renderer.rb +3 -2
- data/lib/hecks/forms/record_renderer.rb +22 -16
- data/lib/hecks/forms.rb +4 -0
- data/lib/hecks/fqn.rb +21 -9
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +30 -8
- data/lib/hecks/fuzzing/invalid_value_generator.rb +0 -1
- data/lib/hecks/fuzzing/isolated_boot.rb +14 -6
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +264 -0
- data/lib/hecks/fuzzing/properties/guards.rb +251 -0
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +351 -0
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +99 -0
- data/lib/hecks/fuzzing/properties/querying.rb +206 -0
- data/lib/hecks/fuzzing/properties.rb +41 -1065
- data/lib/hecks/fuzzing/replay.rb +60 -16
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +6 -2
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +2 -1
- data/lib/hecks/fuzzing/sequence_generator.rb +0 -1
- data/lib/hecks/fuzzing/value_generator.rb +2 -2
- data/lib/hecks/grammar/evolve.rb +34 -10
- data/lib/hecks/grammar/expression_operators.json +124 -0
- data/lib/hecks/grammar.rb +15 -5
- data/lib/hecks/ir.rb +9 -0
- data/lib/hecks/language/bluebook/aggregate.bluebook +4 -2
- data/lib/hecks/language/bluebook/command.bluebook +2 -1
- data/lib/hecks/language/bluebook/entity.bluebook +2 -1
- data/lib/hecks/language/bluebook/projection.bluebook +2 -1
- data/lib/hecks/language/bluebook/query.bluebook +4 -2
- data/lib/hecks/language/bluebook/shape.bluebook +2 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -0
- data/lib/hecks/literal.rb +18 -8
- data/lib/hecks/naming.rb +6 -3
- data/lib/hecks/ports/access_control.rb +1 -1
- data/lib/hecks/ports/agent.rb +5 -3
- data/lib/hecks/ports/authentication.rb +3 -2
- data/lib/hecks/ports/authorization.rb +1 -1
- data/lib/hecks/ports/clock.rb +1 -1
- data/lib/hecks/ports/extraction.rb +5 -1
- data/lib/hecks/ports/identity_assignment.rb +1 -1
- data/lib/hecks/ports/identity_generation.rb +1 -1
- data/lib/hecks/ports/identity_resolution.rb +1 -1
- data/lib/hecks/ports/loading.rb +4 -1
- data/lib/hecks/ports/persistence/append_only.rb +40 -4
- data/lib/hecks/ports/persistence/binding_policy.rb +1 -1
- data/lib/hecks/ports/persistence/plugin.rb +3 -0
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -1
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +5 -5
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +14 -5
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +4 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +80 -16
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +23 -6
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +43 -5
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +25 -4
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +49 -9
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +10 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +49 -38
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +23 -13
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +19 -21
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +27 -9
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +9 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +25 -10
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +2 -6
- data/lib/hecks/ports/persistence/plugins/era.rb +3 -1
- data/lib/hecks/ports/persistence/remote_runtime.rb +1 -1
- data/lib/hecks/ports/persistence.rb +7 -3
- data/lib/hecks/ports/projection.rb +13 -5
- data/lib/hecks/ports/query/in_memory.rb +8 -2
- data/lib/hecks/ports/query.rb +1 -3
- data/lib/hecks/ports.rb +2 -0
- data/lib/hecks/projections/diagrams.rb +11 -3
- data/lib/hecks/projections/ir.rb +2 -0
- data/lib/hecks/projections/model/deviations.rb +2 -1
- data/lib/hecks/projections/model.rb +15 -10
- data/lib/hecks/projections/vocabulary.rb +1 -3
- data/lib/hecks/projector/cli_projector.rb +49 -29
- data/lib/hecks/projector/docs_projector.rb +21 -12
- data/lib/hecks/projector/exporter.rb +10 -1
- data/lib/hecks/projector/narrate_projector.rb +83 -25
- data/lib/hecks/projector.rb +2 -2
- data/lib/hecks/query_ir.rb +60 -36
- data/lib/hecks/query_specification/common/authorization_spec.rb +1 -1
- data/lib/hecks/query_specification/common/comparators.rb +4 -0
- data/lib/hecks/query_specification/common/comparison.rb +8 -1
- data/lib/hecks/query_specification/common/dsl.rb +5 -0
- data/lib/hecks/query_specification/common/null_policy.rb +6 -2
- data/lib/hecks/query_specification/common/options.rb +17 -5
- data/lib/hecks/query_specification/field_path.rb +4 -4
- data/lib/hecks/query_specification/read_model/specification.rb +5 -2
- data/lib/hecks/router/namespace_installer.rb +22 -10
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +57 -22
- data/lib/hecks/runtime/command_interpreter.rb +104 -65
- data/lib/hecks/runtime/command_rules/admissibility.rb +26 -8
- data/lib/hecks/runtime/command_rules/arithmetic.rb +35 -14
- data/lib/hecks/runtime/dependency_planning.rb +33 -21
- data/lib/hecks/runtime/dispatcher.rb +41 -7
- data/lib/hecks/runtime/entity_element.rb +25 -6
- data/lib/hecks/runtime/entity_interpreter.rb +28 -15
- data/lib/hecks/runtime/errors.rb +28 -2
- data/lib/hecks/runtime/instance.rb +9 -3
- data/lib/hecks/runtime/interpreting.rb +29 -2
- data/lib/hecks/runtime/loader.rb +21 -0
- data/lib/hecks/runtime/outbox.rb +365 -0
- data/lib/hecks/runtime/policy_interpreter.rb +32 -17
- data/lib/hecks/runtime/query_interpreter.rb +36 -16
- data/lib/hecks/runtime/reaction_invocation.rb +35 -22
- data/lib/hecks/runtime/read_model_interpreter.rb +26 -3
- data/lib/hecks/runtime/reference_hop.rb +1 -1
- data/lib/hecks/runtime/refusal_wording.rb +55 -49
- data/lib/hecks/runtime/registry/saga_persistence.rb +3 -1
- data/lib/hecks/runtime/registry/verification.rb +39 -2
- data/lib/hecks/runtime/registry.rb +21 -11
- data/lib/hecks/runtime/remote_dispatcher.rb +3 -1
- data/lib/hecks/runtime/routing.rb +52 -39
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +7 -7
- data/lib/hecks/runtime/saga_interpreter.rb +119 -90
- data/lib/hecks/runtime/saga_pending_dispatch.rb +6 -3
- data/lib/hecks/runtime/tenant_scope.rb +7 -0
- data/lib/hecks/runtime/value/coercion.rb +207 -48
- data/lib/hecks/runtime/value.rb +1 -1
- data/lib/hecks/runtime.rb +7 -2
- data/lib/hecks/storehouse.rb +15 -8
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +1 -0
- data/lib/hecks.rb +17 -13
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +1 -1
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +1 -1
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +1 -1
- metadata +14 -2
|
@@ -4,6 +4,13 @@ require_relative "../../vocabulary"
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module Bluebook
|
|
6
6
|
module Expression
|
|
7
|
+
# The boolean/comparison layer of a predicate's expression grammar —
|
|
8
|
+
# parses a canonical predicate string into an Or/And/Not/Compare/
|
|
9
|
+
# Include/Resolve AST (cached per distinct string, `ast_cache`) and
|
|
10
|
+
# interprets it against a call's own state/attrs. Leaf `Resolve`
|
|
11
|
+
# nodes delegate to `Resolver` for the dotted/arithmetic
|
|
12
|
+
# sub-grammar; `OPERATORS` is the comparison table projected from
|
|
13
|
+
# the grammar chapter (`PROJECTION`).
|
|
7
14
|
module Evaluator
|
|
8
15
|
Operator = Struct.new(:symbol, :compares_less_than, :compares_equal, :negated, keyword_init: true)
|
|
9
16
|
|
|
@@ -58,6 +65,54 @@ module Hecks
|
|
|
58
65
|
interpret(ast_cache[expr] ||= parse(expr), state, attrs)
|
|
59
66
|
end
|
|
60
67
|
|
|
68
|
+
# THE RULE-SHAPED ENTRY — evaluates a Given/Invariant (anything
|
|
69
|
+
# answering `canonical` and `ast`) by walking its STRUCTURED form,
|
|
70
|
+
# never re-parsing the text: the one parse happened at DSL-build
|
|
71
|
+
# time behind `AstJson`, and `AstReader` turns that tree back into
|
|
72
|
+
# the same nodes `parse` would have built (the equivalence is
|
|
73
|
+
# pinned by spec/expression_ast_spec.rb over the bounded-
|
|
74
|
+
# exhaustive generator). A rule with no `ast` (an Assembly-
|
|
75
|
+
# rebuilt one, or a placeholder resolved outside build_rule)
|
|
76
|
+
# falls back to parsing its canonical — same cache, same key.
|
|
77
|
+
#
|
|
78
|
+
# HECKS_EVAL=string reverts to the text path wholesale, kept for
|
|
79
|
+
# one release as the escape hatch while the ast path beds in.
|
|
80
|
+
def call_rule(rule, state, attrs = {})
|
|
81
|
+
return call(rule.canonical, state, attrs) if ENV["HECKS_EVAL"] == "string"
|
|
82
|
+
|
|
83
|
+
interpret(ast_cache[rule.canonical] ||= nodes_for(rule), state, attrs)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def nodes_for(rule)
|
|
87
|
+
rule.ast ? AstReader.read_predicate(rule.ast) : parse(rule.canonical)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# A refused `given`/`ensures`/`invariant` names its own DESCRIPTION
|
|
91
|
+
# ("not already superseded") but, on its own, not what the block
|
|
92
|
+
# actually evaluated to — the difference between "the rule is right
|
|
93
|
+
# and my data is wrong" and "the rule is subtly wrong" is often just
|
|
94
|
+
# seeing the two operands. Scoped to the single shape that has one
|
|
95
|
+
# honest answer: `expr`'s own TOP-LEVEL node is a bare `Compare` —
|
|
96
|
+
# not `Or`/`And`/`Not` (which of several sub-comparisons would even
|
|
97
|
+
# be "the" one at fault is genuinely ambiguous), `Include` (no
|
|
98
|
+
# left/right to show), or `Resolve` (a bare boolean read, nothing to
|
|
99
|
+
# compare against). Values are rendered with `Rendering.describe`,
|
|
100
|
+
# the same house style every other refusal already prints a value
|
|
101
|
+
# through. Returns `nil` — not raised — on anything else, INCLUDING
|
|
102
|
+
# an operand that itself fails to resolve (`EvaluationError`): a
|
|
103
|
+
# missing diagnostic is a worse debugging experience than none, a
|
|
104
|
+
# crash while building one is worse still.
|
|
105
|
+
def comparison_detail(expr, state, attrs = {})
|
|
106
|
+
node = ast_cache[expr] ||= parse(expr)
|
|
107
|
+
return nil unless node.is_a?(Compare)
|
|
108
|
+
|
|
109
|
+
lhs = Resolver.interpret(node.left, state, attrs)
|
|
110
|
+
rhs = Resolver.interpret(node.right, state, attrs)
|
|
111
|
+
"left: #{Resolver.describe(lhs)}, right: #{Resolver.describe(rhs)}"
|
|
112
|
+
rescue EvaluationError
|
|
113
|
+
nil
|
|
114
|
+
end
|
|
115
|
+
|
|
61
116
|
def parse(expr)
|
|
62
117
|
expr = strip_parens(expr.to_s.strip)
|
|
63
118
|
|
|
@@ -117,21 +172,21 @@ module Hecks
|
|
|
117
172
|
end
|
|
118
173
|
end
|
|
119
174
|
|
|
120
|
-
def compare(
|
|
175
|
+
def compare(comparator, left, right, state, attrs)
|
|
121
176
|
lhs = Resolver.interpret(left, state, attrs)
|
|
122
177
|
rhs = Resolver.interpret(right, state, attrs)
|
|
123
178
|
|
|
124
|
-
apply(
|
|
179
|
+
apply(comparator, lhs, rhs)
|
|
125
180
|
end
|
|
126
181
|
|
|
127
182
|
# The algebra itself, on values already resolved — split out so a sign
|
|
128
183
|
# test (SignTest#compares_via names an Operator symbol) can apply the
|
|
129
184
|
# SAME primitives compare() uses against the literal 0, rather than
|
|
130
185
|
# re-deriving positive?/negative?/zero? by hand a second time.
|
|
131
|
-
def apply(
|
|
132
|
-
result = (
|
|
133
|
-
(
|
|
134
|
-
|
|
186
|
+
def apply(comparator, lhs, rhs)
|
|
187
|
+
result = (comparator.compares_less_than && less_than(lhs, rhs)) ||
|
|
188
|
+
(comparator.compares_equal && equal?(lhs, rhs))
|
|
189
|
+
comparator.negated ? !result : result
|
|
135
190
|
end
|
|
136
191
|
|
|
137
192
|
def less_than(lhs, rhs)
|
|
@@ -198,9 +253,7 @@ module Hecks
|
|
|
198
253
|
case (found = Resolver.interpret(haystack, state, attrs))
|
|
199
254
|
when Array then found.any? { |item| equal?(item, wanted) }
|
|
200
255
|
when String
|
|
201
|
-
unless wanted.is_a?(String)
|
|
202
|
-
raise EvaluationError, "no implicit conversion of #{class_of(wanted)} into String"
|
|
203
|
-
end
|
|
256
|
+
raise EvaluationError, "no implicit conversion of #{class_of(wanted)} into String" unless wanted.is_a?(String)
|
|
204
257
|
|
|
205
258
|
found.include?(wanted)
|
|
206
259
|
else false
|
|
@@ -286,9 +339,9 @@ module Hecks
|
|
|
286
339
|
# its own (`[0, 0 + 0]`) used to read as a split point for
|
|
287
340
|
# THIS expression's own boolean/comparison grammar, exactly
|
|
288
341
|
# the way an un-tracked `{`/`}` once did for block predicates.
|
|
289
|
-
elsif
|
|
342
|
+
elsif ["(", "{", "["].include?(char)
|
|
290
343
|
depth += 1
|
|
291
|
-
elsif
|
|
344
|
+
elsif [")", "}", "]"].include?(char)
|
|
292
345
|
depth -= 1
|
|
293
346
|
elsif depth.zero? && expr[index, operator.length] == operator
|
|
294
347
|
return index if !block_given? || yield(index)
|
|
@@ -197,6 +197,18 @@
|
|
|
197
197
|
"category": "positional",
|
|
198
198
|
"precedence": 7,
|
|
199
199
|
"arity": 1
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"symbol": ".set?",
|
|
203
|
+
"category": "presence",
|
|
204
|
+
"precedence": 7,
|
|
205
|
+
"arity": 1
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"symbol": ".unset?",
|
|
209
|
+
"category": "presence",
|
|
210
|
+
"precedence": 7,
|
|
211
|
+
"arity": 1
|
|
200
212
|
}
|
|
201
213
|
],
|
|
202
214
|
"normalisations": [
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
module Hecks
|
|
13
13
|
module Bluebook
|
|
14
14
|
module Expression
|
|
15
|
+
# Reopens the SAME `Resolver` module resolver.rb defines — see this
|
|
16
|
+
# file's own header comment above for why the block-predicate/find
|
|
17
|
+
# family lives here rather than in resolver.rb itself.
|
|
15
18
|
module Resolver
|
|
16
19
|
# `receiver.all? { |x| PREDICATE }` / `.any? { ... }` / `.none? {
|
|
17
20
|
# ... }` -- vendored addition, not (yet) upstream hecks
|
|
@@ -184,9 +187,7 @@ module Hecks
|
|
|
184
187
|
# just `attrs` extended with the bound name for the span of that
|
|
185
188
|
# one predicate evaluation, discarded immediately after.
|
|
186
189
|
def evaluate_block_predicate(node, collection, state, attrs)
|
|
187
|
-
unless collection.is_a?(Array)
|
|
188
|
-
raise EvaluationError, "#{node.mode}? expects a list, got #{describe(collection)}"
|
|
189
|
-
end
|
|
190
|
+
raise EvaluationError, "#{node.mode}? expects a list, got #{describe(collection)}" unless collection.is_a?(Array)
|
|
190
191
|
|
|
191
192
|
outcomes = collection.map { |element| interpret_with_element(node, element, state, attrs) }
|
|
192
193
|
|
|
@@ -8,6 +8,14 @@ module Hecks
|
|
|
8
8
|
module Expression
|
|
9
9
|
class EvaluationError < StandardError; end
|
|
10
10
|
|
|
11
|
+
# The dotted/arithmetic leaf grammar a predicate's `Resolve` node
|
|
12
|
+
# bottoms out in — attribute lookups, literals, arithmetic, and the
|
|
13
|
+
# growing set of vendored method-call suffixes (`.match?`,
|
|
14
|
+
# `.present?`/`.blank?`, `.set?`/`.unset?`, `.split`, `.first`/
|
|
15
|
+
# `.last`, `.all?`/`.any?`/`.none?`/`.find`, ...) this file and its
|
|
16
|
+
# `resolver/block_predicates.rb` sibling parse and interpret. Each
|
|
17
|
+
# leaf's `parse` is a pure function of its own string, cached at the
|
|
18
|
+
# `Evaluator` layer; `interpret` reads state/attrs fresh each call.
|
|
11
19
|
module Resolver
|
|
12
20
|
SIGN_TESTS = Hecks::Vocabulary.fetch("SignTest")
|
|
13
21
|
|
|
@@ -109,6 +117,24 @@ module Hecks
|
|
|
109
117
|
# field that could legitimately be "" rather than absent.
|
|
110
118
|
Presence = Struct.new(:receiver, :negated, keyword_init: true)
|
|
111
119
|
|
|
120
|
+
# `.set?`/`.unset?` -- sibling of `.present?`/`.blank?` immediately
|
|
121
|
+
# above, added for a DELIBERATELY narrower question, spelled out in
|
|
122
|
+
# the name so choosing between the two pairs is a choice, not a
|
|
123
|
+
# trap: `.present?` means "not EMPTY" (Rails-standard -- nil/false,
|
|
124
|
+
# and an EMPTY String/Array/Hash, are blank; nothing else is),
|
|
125
|
+
# which quietly answers "was this ever assigned" and "does the
|
|
126
|
+
# assigned value happen to be empty" as the SAME question. For an
|
|
127
|
+
# optional field whose only legitimate unset state IS nil, that
|
|
128
|
+
# conflation is a real trap -- confirmed live: a corpus author
|
|
129
|
+
# reaching for `superseded_by.blank?` to guard an optional
|
|
130
|
+
# reference burned real time before landing on "just don't guard
|
|
131
|
+
# it," because `.blank?` cannot be asked to mean ONLY "was this
|
|
132
|
+
# never assigned." `.set?`/`.unset?` ask exactly that, and nothing
|
|
133
|
+
# else: `!receiver.nil?`, full stop -- an assigned-but-empty
|
|
134
|
+
# String/Array/Hash (or a VO wrapping one) is `.set?`, not
|
|
135
|
+
# `.unset?`, unlike `.blank?`'s own reading of the identical value.
|
|
136
|
+
Assignment = Struct.new(:receiver, :negated, keyword_init: true)
|
|
137
|
+
|
|
112
138
|
# `receiver.split("SEP")` -- vendored addition, not (yet) upstream
|
|
113
139
|
# hecks (migration plan task 9): confirmed the single
|
|
114
140
|
# highest-value new finding of the real-dispatch smoke sweep --
|
|
@@ -186,6 +212,18 @@ module Hecks
|
|
|
186
212
|
interpret(parse(expr), state, attrs)
|
|
187
213
|
end
|
|
188
214
|
|
|
215
|
+
# A grammar's own dispatch table — one `return Node.new(...) if
|
|
216
|
+
# expr =~ /pattern/` per leaf production, in a FIXED precedence
|
|
217
|
+
# order (`.length` before the generic suffixes, sign tests before
|
|
218
|
+
# the rest, etc. — see this file's own history comments on why
|
|
219
|
+
# several of these were added in exactly this position). Each
|
|
220
|
+
# branch is already one line; splitting the table into smaller
|
|
221
|
+
# methods would not shrink any single check, only hide the
|
|
222
|
+
# precedence order this method's own line-by-line sequence IS.
|
|
223
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
224
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
225
|
+
# rubocop:disable-next Metrics/MethodLength
|
|
226
|
+
# rubocop:disable-next Metrics/PerceivedComplexity
|
|
189
227
|
def parse(expr)
|
|
190
228
|
expr = expr.to_s.strip
|
|
191
229
|
|
|
@@ -215,7 +253,7 @@ module Hecks
|
|
|
215
253
|
|
|
216
254
|
return Size.new(receiver: parse(Regexp.last_match(1))) if expr =~ /\A(.+)\.size\z/
|
|
217
255
|
|
|
218
|
-
if expr =~
|
|
256
|
+
if expr =~ %r{\A(.+)\.match\?\(/(.*)/([a-z]*)\)\z}m
|
|
219
257
|
return MatchesRegex.new(receiver: parse(Regexp.last_match(1)),
|
|
220
258
|
pattern: Regexp.last_match(2),
|
|
221
259
|
flags: Regexp.last_match(3))
|
|
@@ -224,19 +262,25 @@ module Hecks
|
|
|
224
262
|
return Presence.new(receiver: parse(Regexp.last_match(1)), negated: false) if expr =~ /\A(.+)\.present\?\z/
|
|
225
263
|
return Presence.new(receiver: parse(Regexp.last_match(1)), negated: true) if expr =~ /\A(.+)\.blank\?\z/
|
|
226
264
|
|
|
265
|
+
return Assignment.new(receiver: parse(Regexp.last_match(1)), negated: false) if expr =~ /\A(.+)\.set\?\z/
|
|
266
|
+
return Assignment.new(receiver: parse(Regexp.last_match(1)), negated: true) if expr =~ /\A(.+)\.unset\?\z/
|
|
267
|
+
|
|
227
268
|
if expr =~ /\A(.+)\.split\("([^"]*)"\)\z/
|
|
228
|
-
return Split.new(receiver:
|
|
269
|
+
return Split.new(receiver: parse(Regexp.last_match(1)),
|
|
270
|
+
separator: Regexp.last_match(2))
|
|
229
271
|
end
|
|
230
272
|
|
|
231
273
|
return First.new(receiver: parse(Regexp.last_match(1))) if expr =~ /\A(.+)\.first\z/
|
|
232
274
|
return Last.new(receiver: parse(Regexp.last_match(1))) if expr =~ /\A(.+)\.last\z/
|
|
233
275
|
|
|
234
276
|
if expr =~ /\A(.+)\.start_with\?\("([^"]*)"\)\z/
|
|
235
|
-
return StartsWith.new(receiver:
|
|
277
|
+
return StartsWith.new(receiver: parse(Regexp.last_match(1)),
|
|
278
|
+
substring: Regexp.last_match(2))
|
|
236
279
|
end
|
|
237
280
|
|
|
238
281
|
if expr =~ /\A(.+)\.end_with\?\("([^"]*)"\)\z/
|
|
239
|
-
return EndsWith.new(receiver:
|
|
282
|
+
return EndsWith.new(receiver: parse(Regexp.last_match(1)),
|
|
283
|
+
substring: Regexp.last_match(2))
|
|
240
284
|
end
|
|
241
285
|
|
|
242
286
|
block_opener = parse_block_opener(expr)
|
|
@@ -252,6 +296,16 @@ module Hecks
|
|
|
252
296
|
SignTest.new(operator: operator, test: test, receiver: parse(receiver))
|
|
253
297
|
end
|
|
254
298
|
|
|
299
|
+
# `parse`'s own dispatch table, mirrored: one `when` per leaf node
|
|
300
|
+
# type it can produce, each already delegating to a small named
|
|
301
|
+
# helper (add, apply_sign_test, emptiness_of, ...) — the case
|
|
302
|
+
# itself IS the closed, declared set this method exists to
|
|
303
|
+
# exhaust; the `else` backstop's own comment explains why a
|
|
304
|
+
# missing arm is a bug this method is built to make loud, not
|
|
305
|
+
# quiet.
|
|
306
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
307
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
308
|
+
# rubocop:disable-next Metrics/MethodLength
|
|
255
309
|
def interpret(node, state, attrs)
|
|
256
310
|
case node
|
|
257
311
|
when IntegerLiteral, FloatLiteral, StringLiteral, BoolLiteral then node.value
|
|
@@ -274,6 +328,9 @@ module Hecks
|
|
|
274
328
|
when Presence
|
|
275
329
|
present = !blank?(interpret(node.receiver, state, attrs))
|
|
276
330
|
node.negated ? !present : present
|
|
331
|
+
when Assignment
|
|
332
|
+
set = !interpret(node.receiver, state, attrs).nil?
|
|
333
|
+
node.negated ? !set : set
|
|
277
334
|
when Split
|
|
278
335
|
split_value(interpret(node.receiver, state, attrs), node.separator)
|
|
279
336
|
when Last
|
|
@@ -339,8 +396,7 @@ module Hecks
|
|
|
339
396
|
# found live while building this, not assumed).
|
|
340
397
|
def matches_regex?(receiver_value, pattern, flags)
|
|
341
398
|
text = case receiver_value
|
|
342
|
-
when String, Symbol then receiver_value.to_s
|
|
343
|
-
when Integer, Float then receiver_value.to_s
|
|
399
|
+
when String, Symbol, Integer, Float then receiver_value.to_s
|
|
344
400
|
when NilClass then ""
|
|
345
401
|
else
|
|
346
402
|
raise EvaluationError, "match? expects a scalar, got #{receiver_value.class}"
|
|
@@ -442,8 +498,25 @@ module Hecks
|
|
|
442
498
|
nil
|
|
443
499
|
end
|
|
444
500
|
|
|
501
|
+
# Integer is signed 64-bit (C3.3, docs/semantics/bluebook-semantics.md)
|
|
502
|
+
# — Ruby's own Integer would promote past ±2^63-1 silently; the
|
|
503
|
+
# language does not, so a sum outside that range is an evaluation
|
|
504
|
+
# fault, worded exactly as the Rust kernel's `checked_add` words
|
|
505
|
+
# it. A Float sum that is not finite is the same fault (C3.4).
|
|
506
|
+
INT64_RANGE = (-(2**63))..((2**63) - 1)
|
|
507
|
+
|
|
445
508
|
def add(left, right)
|
|
446
|
-
|
|
509
|
+
lhs = require_number(left, "addition")
|
|
510
|
+
rhs = require_number(right, "addition")
|
|
511
|
+
sum = lhs + rhs
|
|
512
|
+
if sum.is_a?(Integer)
|
|
513
|
+
return sum if INT64_RANGE.cover?(sum)
|
|
514
|
+
|
|
515
|
+
raise EvaluationError, "addition overflowed: #{lhs} + #{rhs} does not fit in a 64-bit integer"
|
|
516
|
+
end
|
|
517
|
+
return sum if sum.finite?
|
|
518
|
+
|
|
519
|
+
raise EvaluationError, "addition overflowed: #{lhs} + #{rhs} is not a finite number"
|
|
447
520
|
end
|
|
448
521
|
|
|
449
522
|
def quoted?(expr)
|
|
@@ -533,10 +606,9 @@ module Hecks
|
|
|
533
606
|
|
|
534
607
|
def string_of(value)
|
|
535
608
|
case value
|
|
536
|
-
when String
|
|
537
|
-
when Integer, Float
|
|
538
|
-
when
|
|
539
|
-
when NilClass then ""
|
|
609
|
+
when String then value
|
|
610
|
+
when Integer, Float, TrueClass, FalseClass then value.to_s
|
|
611
|
+
when NilClass then ""
|
|
540
612
|
else
|
|
541
613
|
raise EvaluationError, "to_s expects a scalar, got #{describe(value)}"
|
|
542
614
|
end
|
|
@@ -18,6 +18,11 @@ module Hecks
|
|
|
18
18
|
def aggregate_name = Naming.demodulise(aggregate)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
# The built form of a `.hecksagon` file, produced by
|
|
22
|
+
# `DSL::HecksagonBuilder` — a domain's own binds (`Bind`, above),
|
|
23
|
+
# subscriptions, and attached framework/vendored members.
|
|
24
|
+
# `Behaviour::Hecksagon` supplies the bind lookups (`bind_for`/
|
|
25
|
+
# `binds_for`); this class holds only the declared data.
|
|
21
26
|
class Hecksagon
|
|
22
27
|
include Hecks::IR
|
|
23
28
|
include Behaviour::Hecksagon
|
|
@@ -41,6 +46,11 @@ module Hecks
|
|
|
41
46
|
end
|
|
42
47
|
end
|
|
43
48
|
|
|
49
|
+
# The built form of a `.world` file, produced by `DSL::WorldBuilder` —
|
|
50
|
+
# a domain's own `realm`/`latest` version markers and its adapter bind
|
|
51
|
+
# SETTINGS (as opposed to `Hecksagon`'s own bind LIST, above).
|
|
52
|
+
# `Behaviour::World` supplies the settings lookups (`for_verb`/
|
|
53
|
+
# `for_binding`); this class holds only the declared data.
|
|
44
54
|
class World
|
|
45
55
|
include Hecks::IR
|
|
46
56
|
include Behaviour::World
|
|
@@ -2,6 +2,10 @@ require_relative "behaviour/lifecycle"
|
|
|
2
2
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module Bluebook
|
|
5
|
+
# One declared `transition` row — the state a command moves an
|
|
6
|
+
# aggregate or entity TO, and, when guarded, the state(s) it must
|
|
7
|
+
# currently be in (`from:`) for the transition to apply. An unguarded
|
|
8
|
+
# transition (`from: nil`) applies from any current state.
|
|
5
9
|
class StateTransition
|
|
6
10
|
attr_reader :target, :from
|
|
7
11
|
|
|
@@ -17,6 +21,11 @@ module Hecks
|
|
|
17
21
|
def constrained? = !@from.nil?
|
|
18
22
|
end
|
|
19
23
|
|
|
24
|
+
# The built form of a `lifecycle :field, default: ... do ... end`
|
|
25
|
+
# block, produced by `DSL::LifecycleBuilder` — the field a state
|
|
26
|
+
# machine lives on, its starting value, and the declared transition
|
|
27
|
+
# table. `Behaviour::Lifecycle` supplies the reads (`states`,
|
|
28
|
+
# `target_for`, ...) built purely from this data.
|
|
20
29
|
class Lifecycle
|
|
21
30
|
include Hecks::IR
|
|
22
31
|
include Behaviour::Lifecycle
|
|
@@ -99,7 +99,7 @@ module Hecks
|
|
|
99
99
|
value
|
|
100
100
|
end
|
|
101
101
|
|
|
102
|
-
def args(pairs) = pairs.
|
|
102
|
+
def args(pairs) = pairs.compact
|
|
103
103
|
|
|
104
104
|
def offer(label)
|
|
105
105
|
yield
|
|
@@ -352,7 +352,10 @@ module Hecks
|
|
|
352
352
|
# meta store happened to iterate in insertion order — an accident that an
|
|
353
353
|
# ask ordered any other way would have taken away, and Reconstruction is
|
|
354
354
|
# the one reader that must have the SOURCE'S order rather than a stable one.
|
|
355
|
-
|
|
355
|
+
# `private` above has no effect on a constant; kept here anyway,
|
|
356
|
+
# beside the method that reads it, for the narrative.
|
|
357
|
+
# rubocop:disable-next Lint/UselessConstantScoping
|
|
358
|
+
POSITION = "position".freeze
|
|
356
359
|
|
|
357
360
|
def declare(plan, category, node, id, parent_id, index, extra = {})
|
|
358
361
|
return unless plan.declare
|
|
@@ -425,7 +428,7 @@ module Hecks
|
|
|
425
428
|
category == "Entity" ? parent_id : id
|
|
426
429
|
end
|
|
427
430
|
|
|
428
|
-
def sealers(plan,
|
|
431
|
+
def sealers(plan, _category, receiver)
|
|
429
432
|
id = receiver[:entities].last || receiver[:aggregate]
|
|
430
433
|
plan.sealers.each { |verb| send_to("Bluebook::#{verb_for(plan, verb)}", id, to: receiver) }
|
|
431
434
|
end
|
|
@@ -586,7 +589,10 @@ module Hecks
|
|
|
586
589
|
# `field_value` ; it is read the same way `plan.parent_key` already is,
|
|
587
590
|
# because it IS that fact, spelled for the walk's immediate parent
|
|
588
591
|
# rather than for one specific kind of one.
|
|
589
|
-
|
|
592
|
+
# `private` above has no effect on a constant; kept here anyway,
|
|
593
|
+
# beside the method that reads it, for the narrative.
|
|
594
|
+
# rubocop:disable-next Lint/UselessConstantScoping
|
|
595
|
+
OWNER = "owner_id".freeze
|
|
590
596
|
|
|
591
597
|
def identity_part(plan, path, parent_id, node, index, category)
|
|
592
598
|
head = path.to_s.split(".").first
|
|
@@ -35,6 +35,10 @@ module Hecks
|
|
|
35
35
|
# nothing — that is the judge's job.
|
|
36
36
|
class Plan
|
|
37
37
|
# One append command: the verb, and the value-object-field -> argument map.
|
|
38
|
+
# `:map` shadows Enumerable#map on purpose — `spec/plan_spec.rb`
|
|
39
|
+
# itself asserts `appends["emits"].map` reads the field (a Hash),
|
|
40
|
+
# not an Enumerable#map result. Verified before disabling this cop.
|
|
41
|
+
# rubocop:disable-next Lint/StructNewOverride
|
|
38
42
|
Append = Struct.new(:verb, :map, keyword_init: true)
|
|
39
43
|
|
|
40
44
|
# One setting command. `targets` is target -> argument ; Lifecycle sets two
|
|
@@ -148,7 +148,7 @@ module Hecks
|
|
|
148
148
|
# against, so the entity ones have to be told apart by `entity_id`. Rejecting
|
|
149
149
|
# from an ORDERED read keeps the order.
|
|
150
150
|
def own(category, aggregate_id)
|
|
151
|
-
declared(category, aggregate_id).
|
|
151
|
+
declared(category, aggregate_id).select { |row| text(row[:entity_id]).to_s == "" }
|
|
152
152
|
end
|
|
153
153
|
|
|
154
154
|
# A piece's own verbs and asks. There is no `DeclaredIn` keyed by entity, so
|
|
@@ -238,12 +238,12 @@ module Hecks
|
|
|
238
238
|
# Mutation's own `to_h` lambda does, rather than leaving the key
|
|
239
239
|
# silently absent (spec/round_trip_spec's whole point: a field the
|
|
240
240
|
# language does not hold is a named gap, not a byte-for-byte one).
|
|
241
|
-
def mutation(target,
|
|
242
|
-
base = { target: target.to_sym, op:
|
|
241
|
+
def mutation(target, oper, bindings)
|
|
242
|
+
base = { target: target.to_sym, op: oper.to_sym, sign: Hecks::Bluebook::Mutation.sign_for(oper) }
|
|
243
243
|
# `:delegate`/`:corrects` (CommandBuilder#delegates_to's and
|
|
244
244
|
# #corrects_impl's own comments) ride the SAME multi-binding
|
|
245
245
|
# shape `:append` does.
|
|
246
|
-
return base.merge(fields: appended(bindings)) if ["append", "delegate", "corrects"].include?(
|
|
246
|
+
return base.merge(fields: appended(bindings)) if ["append", "delegate", "corrects"].include?(oper)
|
|
247
247
|
|
|
248
248
|
base.merge(source: classified(bindings.first))
|
|
249
249
|
end
|
|
@@ -85,7 +85,9 @@ module Hecks
|
|
|
85
85
|
|
|
86
86
|
def same_chapters?(cached, current)
|
|
87
87
|
cached.size == current.size &&
|
|
88
|
-
cached.zip(current).all?
|
|
88
|
+
cached.zip(current).all? do |(cached_name, cached_chapter), (name, chapter)|
|
|
89
|
+
cached_name == name && cached_chapter.equal?(chapter)
|
|
90
|
+
end
|
|
89
91
|
end
|
|
90
92
|
|
|
91
93
|
def boot
|
|
@@ -140,7 +142,8 @@ module Hecks
|
|
|
140
142
|
def declare_syntax(runtime, bluebook)
|
|
141
143
|
syntax = bluebook.aggregate("Syntax")
|
|
142
144
|
runtime.dispatch("Bluebook::Bluebook.Declare", name: v(bluebook.hecks_name),
|
|
143
|
-
vision: v("the language's own grammar table,
|
|
145
|
+
vision: v("the language's own grammar table, " \
|
|
146
|
+
"dispatched into itself"),
|
|
144
147
|
classification: v("core"))
|
|
145
148
|
runtime.dispatch("Bluebook::Syntax.Declare", bluebook: bluebook.hecks_name, name: v(syntax.hecks_name))
|
|
146
149
|
end
|
|
@@ -239,8 +242,8 @@ module Hecks
|
|
|
239
242
|
end
|
|
240
243
|
|
|
241
244
|
def stringify(row)
|
|
242
|
-
row.to_h.
|
|
243
|
-
|
|
245
|
+
row.to_h.transform_values do |cell|
|
|
246
|
+
scalar(cell).to_s
|
|
244
247
|
end
|
|
245
248
|
end
|
|
246
249
|
|
|
@@ -82,8 +82,26 @@ module Hecks
|
|
|
82
82
|
Array(t.aggregates).each { |aggregate| judge_aggregate(t, aggregate) }
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
+
# One Declare, then one AddX pass per rule collection the aggregate
|
|
86
|
+
# carries — each pass is independent of the others (they mutate
|
|
87
|
+
# different fields), so each is its own private method below; only
|
|
88
|
+
# Declare-before-any-Add is order-sensitive, and that order is
|
|
89
|
+
# preserved here.
|
|
85
90
|
def judge_aggregate(translation, aggregate)
|
|
86
91
|
name = aggregate.name
|
|
92
|
+
declare_aggregate(translation, aggregate, name)
|
|
93
|
+
|
|
94
|
+
judge_renames(aggregate, name)
|
|
95
|
+
judge_moves(aggregate, name)
|
|
96
|
+
judge_converts(aggregate, name)
|
|
97
|
+
judge_drops(aggregate, name)
|
|
98
|
+
judge_retypes(aggregate, name)
|
|
99
|
+
judge_computes(aggregate, name)
|
|
100
|
+
judge_rekeys(aggregate, name)
|
|
101
|
+
judge_backfills(aggregate, name)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def declare_aggregate(translation, aggregate, name)
|
|
87
105
|
parent = {
|
|
88
106
|
domain: v(translation.domain),
|
|
89
107
|
from: v(translation.from),
|
|
@@ -91,45 +109,66 @@ module Hecks
|
|
|
91
109
|
}
|
|
92
110
|
send_to("Translation::TranslationAggregate.Declare", name,
|
|
93
111
|
with: { translation_ref: parent, name: v(name), was: v(aggregate.was) })
|
|
112
|
+
end
|
|
94
113
|
|
|
114
|
+
def judge_renames(aggregate, name)
|
|
95
115
|
Hash(aggregate.renames).each do |from, to|
|
|
96
116
|
send_to("Translation::TranslationAggregate.AddRename", name, to: name,
|
|
97
117
|
with: { from: v(from), to: v(to) })
|
|
98
118
|
end
|
|
119
|
+
end
|
|
99
120
|
|
|
121
|
+
def judge_moves(aggregate, name)
|
|
100
122
|
Array(aggregate.moves).each do |move|
|
|
101
123
|
send_to("Translation::TranslationAggregate.AddMove", name, to: name,
|
|
102
124
|
with: { from: v(move.from), to: v(move.to) })
|
|
103
125
|
end
|
|
126
|
+
end
|
|
104
127
|
|
|
128
|
+
def judge_converts(aggregate, name)
|
|
105
129
|
Array(aggregate.converts).each do |convert|
|
|
106
130
|
send_to("Translation::TranslationAggregate.AddConvert", name, to: name,
|
|
107
|
-
with: { from:
|
|
131
|
+
with: { from: v(convert.from),
|
|
132
|
+
to: v(convert.to),
|
|
133
|
+
values: v(convert.values.inspect) })
|
|
108
134
|
end
|
|
135
|
+
end
|
|
109
136
|
|
|
137
|
+
def judge_drops(aggregate, name)
|
|
110
138
|
Array(aggregate.drops).each do |dropped|
|
|
111
139
|
send_to("Translation::TranslationAggregate.AddDrop", name, to: name,
|
|
112
140
|
with: { value: v(dropped) })
|
|
113
141
|
end
|
|
142
|
+
end
|
|
114
143
|
|
|
144
|
+
def judge_retypes(aggregate, name)
|
|
115
145
|
Array(aggregate.retypes).each do |retype|
|
|
116
146
|
send_to("Translation::TranslationAggregate.AddRetype", name, to: name,
|
|
117
147
|
with: { from: v(retype.from), to: v(retype.to) })
|
|
118
148
|
end
|
|
149
|
+
end
|
|
119
150
|
|
|
151
|
+
def judge_computes(aggregate, name)
|
|
120
152
|
Array(aggregate.computes).each do |compute|
|
|
121
153
|
send_to("Translation::TranslationAggregate.AddCompute", name, to: name,
|
|
122
|
-
with: { from: v(compute.from),
|
|
154
|
+
with: { from: v(compute.from),
|
|
155
|
+
to: v(compute.to),
|
|
156
|
+
sql: v(compute.sql) })
|
|
123
157
|
end
|
|
158
|
+
end
|
|
124
159
|
|
|
160
|
+
def judge_rekeys(aggregate, name)
|
|
125
161
|
Array(aggregate.rekeys).each do |rekey|
|
|
126
162
|
send_to("Translation::TranslationAggregate.AddRekey", name, to: name,
|
|
127
163
|
with: { sql: v(rekey.sql) })
|
|
128
164
|
end
|
|
165
|
+
end
|
|
129
166
|
|
|
167
|
+
def judge_backfills(aggregate, name)
|
|
130
168
|
Array(aggregate.backfills).each do |backfill|
|
|
131
169
|
send_to("Translation::TranslationAggregate.AddBackfill", name, to: name,
|
|
132
|
-
with: { name:
|
|
170
|
+
with: { name: v(backfill.name),
|
|
171
|
+
default: v(backfill.default.inspect) })
|
|
133
172
|
end
|
|
134
173
|
end
|
|
135
174
|
end
|
|
@@ -52,11 +52,11 @@ module Hecks
|
|
|
52
52
|
# concept file requires no second catalog here; deterministic filename
|
|
53
53
|
# order is the source order exported to IR.
|
|
54
54
|
GRAMMAR_DIR = File.expand_path("../language/bluebook", __dir__).freeze
|
|
55
|
-
GRAMMAR_FILES = Dir.glob(File.join(GRAMMAR_DIR, "*.bluebook")).
|
|
55
|
+
GRAMMAR_FILES = Dir.glob(File.join(GRAMMAR_DIR, "*.bluebook")).freeze
|
|
56
56
|
# Sibling artifact languages use the same folder-is-the-chapter rule.
|
|
57
57
|
# Their arrays are discovered, sorted source sets—not filename catalogs.
|
|
58
|
-
WORLD_GRAMMAR = Dir.glob(File.expand_path("../language/world/*.bluebook", __dir__)).
|
|
59
|
-
HECKSAGON_GRAMMAR = Dir.glob(File.expand_path("../language/hecksagon/*.bluebook", __dir__)).
|
|
58
|
+
WORLD_GRAMMAR = Dir.glob(File.expand_path("../language/world/*.bluebook", __dir__)).freeze
|
|
59
|
+
HECKSAGON_GRAMMAR = Dir.glob(File.expand_path("../language/hecksagon/*.bluebook", __dir__)).freeze
|
|
60
60
|
# so is a port — whole-project table-unification survey, item #13's
|
|
61
61
|
# remaining builders. Backs the new PortJudge door the same way
|
|
62
62
|
# WORLD_GRAMMAR backs WorldJudge.
|
|
@@ -65,7 +65,7 @@ module Hecks
|
|
|
65
65
|
ADAPTER_GRAMMAR = File.expand_path("../language/adapter.bluebook", __dir__).freeze
|
|
66
66
|
# so is a translation — same reasoning, one file over. Backs
|
|
67
67
|
# TranslationJudge.
|
|
68
|
-
TRANSLATION_GRAMMAR = Dir.glob(File.expand_path("../language/translation/*.bluebook", __dir__)).
|
|
68
|
+
TRANSLATION_GRAMMAR = Dir.glob(File.expand_path("../language/translation/*.bluebook", __dir__)).freeze
|
|
69
69
|
|
|
70
70
|
# ADR 0026's OWN SEAM: THE CORE DOES NOT NAME ITS EXTENSION POINTS.
|
|
71
71
|
#
|
|
@@ -483,7 +483,7 @@ module Hecks
|
|
|
483
483
|
# is added there.
|
|
484
484
|
def self.load_attached_grammar_into(registry)
|
|
485
485
|
Hecks.with_registry(registry) do
|
|
486
|
-
Dir.glob(File.join(ATTACHED_GRAMMAR_DIR, "*.bluebook")).
|
|
486
|
+
Dir.glob(File.join(ATTACHED_GRAMMAR_DIR, "*.bluebook")).each { |file| Kernel.load(file) }
|
|
487
487
|
end
|
|
488
488
|
end
|
|
489
489
|
|