Module: Hecks::Grammar
- Defined in:
- lib/hecks/grammar.rb,
lib/hecks/grammar/evolve.rb
Overview
The sublanguage grammar domains (grammar/*.bluebook) and the one boot path for reading them as DATA — the expression chapter replayed through its own admission ledger, so anything derived from it (the operator projections, the conformance specs) reads the set that actually survived the Admit gates, never a hand-copied list.
Booted on CALL, never at require: the Prism adapter normalises every predicate through CanonicalForm while a bluebook loads, so the expression machinery cannot boot the chapter that configures it — this module exists precisely so generators and specs boot it in a scratch registry instead.
Defined Under Namespace
Modules: Evolve
Constant Summary collapse
- DIR =
File.("grammar", __dir__)
- LEDGER =
File.join(DIR, "expression_operators.json")
Class Method Summary collapse
- .admitted_normalisations(dispatcher = expression) ⇒ Object
- .admitted_operators(dispatcher = expression) ⇒ Object
-
.expression ⇒ Object
Boot the expression chapter and replay the admission ledger through its real commands.
-
.grammar_chapters ⇒ Object
Every grammar/*.bluebook chapter, booted the same way the corpus boots them — each alone, in a scratch registry.
-
.operators_in(canonical) ⇒ Object
Which admitted operators one canonical text evaluates through — the evaluator's own parse, walked for its operator nodes, leaves walked for the resolver's arithmetic.
- .records(dispatcher, aggregate_name) ⇒ Object
-
.self_bearing_operators ⇒ Object
THE OPERATORS THE LANGUAGE STANDS ON.
- .symbolize(value) ⇒ Object
- .walk_operators(node, evaluator) ⇒ Object
Class Method Details
.admitted_normalisations(dispatcher = expression) ⇒ Object
63 64 65 66 67 68 69 70 71 72 |
# File 'lib/hecks/grammar.rb', line 63 def admitted_normalisations(dispatcher = expression) records(dispatcher, "Normalisation") .select { |rule| rule[:status] == "admitted" } .sort_by { |rule| rule[:position].value } .map do |rule| { strategy: rule[:strategy].value, source_token: rule[:source_token].value, replacement: rule[:replacement].value, boundary: rule[:boundary].value, position: rule[:position].value } end end |
.admitted_operators(dispatcher = expression) ⇒ Object
55 56 57 58 59 60 61 |
# File 'lib/hecks/grammar.rb', line 55 def admitted_operators(dispatcher = expression) records(dispatcher, "Operator").select { |op| op[:status] == "admitted" }.map do |op| { symbol: op[:symbol].value, category: op[:category].value, precedence: op[:precedence].value, arity: op[:arity].value, renderings: Array(op[:renderings]).map { |r| { target: r[:target], form: r[:form] } } } end end |
.expression ⇒ Object
Boot the expression chapter and replay the admission ledger through its real commands. A refused step raises — a generator running off a half-admitted ledger would project a table the gates never accepted.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/hecks/grammar.rb', line 30 def expression registry = Runtime::Registry.new root = File.("../..", __dir__) Hecks.with_registry(registry) do Kernel.load(File.join(root, "lib/hecks/ports/persistence.port")) Kernel.load(File.join(root, "lib/hecks/ports/extraction.port")) Kernel.load(File.join(root, "lib/hecks/adapters/driven/memory.adapter")) Kernel.load(File.join(root, "lib/hecks/adapters/driven/prism.adapter")) Kernel.load(File.join(DIR, "expression.bluebook")) end dispatcher = Runtime::Dispatcher.new(registry) JSON.parse(File.read(LEDGER)).fetch("steps").each do |step| args = symbolize(step.fetch("args")) begin dispatcher.dispatch(step.fetch("verb"), **args) rescue *Runtime::DOMAIN_REFUSALS => refusal raise Runtime::WiringError, "the admission ledger refused at #{step['verb']} #{step['args']} — #{refusal.}" end end dispatcher end |
.grammar_chapters ⇒ Object
Every grammar/*.bluebook chapter, booted the same way the corpus boots them — each alone, in a scratch registry.
121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/hecks/grammar.rb', line 121 def grammar_chapters Dir[File.join(DIR, "*.bluebook")].sort.map do |chapter| registry = Runtime::Registry.new root = File.("../..", __dir__) Hecks.with_registry(registry) do Kernel.load(File.join(root, "lib/hecks/ports/persistence.port")) Kernel.load(File.join(root, "lib/hecks/ports/extraction.port")) Kernel.load(File.join(root, "lib/hecks/adapters/driven/memory.adapter")) Kernel.load(File.join(root, "lib/hecks/adapters/driven/prism.adapter")) Kernel.load(chapter) end registry.bluebooks.values.first end end |
.operators_in(canonical) ⇒ Object
Which admitted operators one canonical text evaluates through — the evaluator's own parse, walked for its operator nodes, leaves walked for the resolver's arithmetic.
139 140 141 142 143 144 145 146 147 |
# File 'lib/hecks/grammar.rb', line 139 def operators_in(canonical) evaluator = Bluebook::Expression::Evaluator node = begin evaluator.parse(canonical) rescue StandardError return [] end walk_operators(node, evaluator).uniq end |
.records(dispatcher, aggregate_name) ⇒ Object
74 75 76 77 78 |
# File 'lib/hecks/grammar.rb', line 74 def records(dispatcher, aggregate_name) registry = dispatcher.registry aggregate = registry.bluebook("Expression").aggregate(aggregate_name) registry.repository("Expression", aggregate).all end |
.self_bearing_operators ⇒ Object
THE OPERATORS THE LANGUAGE STANDS ON. Every guard and invariant in
the language's own chapters — the meta-domain (Bluebook, World) and
the grammar chapters beside this file — evaluates through the very
operator table the ledger admits. An operator one of those
predicates uses is SELF-BEARING: retire it and the language can no
longer read its own rules — found the hard way, as a projection
missing != that could not boot the chapter to fix itself. This
derives the set, with a usage site per operator, so the generator
and the conformance spec can refuse the retirement BY NAME instead
of wedging.
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/hecks/grammar.rb', line 90 def self_bearing_operators sites = Hash.new { |h, k| h[k] = [] } chapters = Bluebook::MetaValidator.grammar_registry .then { |reg| %w[Bluebook World].map { |name| reg.bluebook(name) } } chapters += grammar_chapters chapters.compact.each do |chapter| chapter.aggregates.each do |aggregate| aggregate.commands.each do |command| command.givens.each do |given| operators_in(given.canonical).each do |symbol| sites[symbol] << "#{chapter.name} #{aggregate.name}.#{command.hecks_name}" end end end aggregate.value_objects.each do |value_object| value_object.invariants.each do |invariant| operators_in(invariant.canonical).each do |symbol| sites[symbol] << "#{chapter.name} #{aggregate.name}::#{value_object.hecks_name}" end end end end end sites.transform_values(&:uniq) end |
.symbolize(value) ⇒ Object
170 171 172 173 174 175 176 |
# File 'lib/hecks/grammar.rb', line 170 def symbolize(value) case value when Hash then value.to_h { |k, v| [k.to_sym, symbolize(v)] } when Array then value.map { |v| symbolize(v) } else value end end |
.walk_operators(node, evaluator) ⇒ Object
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/hecks/grammar.rb', line 149 def walk_operators(node, evaluator) resolver = Bluebook::Expression::Resolver case node when evaluator::Or then ["||"] + walk_operators(node.left, evaluator) + walk_operators(node.right, evaluator) when evaluator::And then ["&&"] + walk_operators(node.left, evaluator) + walk_operators(node.right, evaluator) when evaluator::Not then ["!"] + walk_operators(node.node, evaluator) when evaluator::Compare [node.operator.symbol] + walk_operators(node.left, evaluator) + walk_operators(node.right, evaluator) when evaluator::Include [".include?"] + walk_operators(node.haystack, evaluator) + walk_operators(node.needle, evaluator) when evaluator::Resolve then walk_operators(node.expr, evaluator) when resolver::Addition then ["+"] + walk_operators(node.left, evaluator) + walk_operators(node.right, evaluator) when resolver::Modulo then [".modulo"] + walk_operators(node.receiver, evaluator) + walk_operators(node.divisor, evaluator) when Struct node.members.flat_map { |member| walk_operators(node[member], evaluator) } else [] end end |