Module: Ibex::NormalizeParameters
- Included in:
- Normalizer
- Defined in:
- lib/ibex/normalize/parameters.rb,
sig/ibex/normalize/parameters.rbs
Overview
Iterative, hygienic specialization of validated parameterized user rules.
Instance Method Summary collapse
- #clear_parameter_alternative(frame) ⇒ void
- #drain_parameter_worklist ⇒ void
- #enforce_parameter_limits!(reference) ⇒ void
-
#growing_parameter_arguments?(candidates, ancestors) ⇒ Boolean
A constructor-growing recursive call can create infinitely many distinct specializations.
- #new_parameter_helper(reference) ⇒ String
- #parameter_argument_contains?(candidate, target) ⇒ Boolean
- #parameter_frame(reference, helper, arguments, rendered_arguments) ⇒ Hash[Symbol, untyped]
- #prepare_parameter_alternative?(frame) ⇒ Boolean
- #prepare_parameter_alternative_entry(frame, template, index) ⇒ void
- #schedule_parameter_specialization(reference) ⇒ [ String, bool ]
- #specialize_parameterized_reference(reference) ⇒ String
Instance Method Details
#clear_parameter_alternative(frame) ⇒ void
This method returns an undefined value.
190 191 192 193 194 195 196 197 |
# File 'lib/ibex/normalize/parameters.rb', line 190 def clear_parameter_alternative(frame) frame[:current] = nil frame[:item_index] = 0 frame[:rhs] = [] frame[:named_refs] = [] frame[:operations] = [] frame[:values] = [] end |
#drain_parameter_worklist ⇒ void
This method returns an undefined value.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/ibex/normalize/parameters.rb', line 137 def drain_parameter_worklist # @type self: Normalizer @parameter_worklist_active = true until @parameter_worklist.empty? frame = @parameter_worklist.last unless frame[:current] || prepare_parameter_alternative?(frame) @parameter_worklist.pop next end advance_parameter_alternative(frame) end ensure @parameter_worklist_active = false end |
#enforce_parameter_limits!(reference) ⇒ void
This method returns an undefined value.
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/ibex/normalize/parameters.rb', line 37 def enforce_parameter_limits!(reference) # @type self: Normalizer cyclic = @parameter_worklist.reverse.find do |frame| frame.fetch(:reference).name == reference.name && growing_parameter_arguments?(reference.arguments, frame.fetch(:arguments)) end if cyclic path = (@parameter_worklist.drop_while { |frame| frame != cyclic } + [{ reference: reference }]) .map { |frame| frame.fetch(:reference).name } fail_at(reference.loc, "cyclic parameter specialization #{path.join(' -> ')}") end return unless @parameter_specializations.length >= @max_parameter_specializations fail_at( reference.loc, "parameter specialization limit of #{@max_parameter_specializations} exceeded" ) end |
#growing_parameter_arguments?(candidates, ancestors) ⇒ Boolean
A constructor-growing recursive call can create infinitely many distinct specializations. Calls whose arguments shrink or merely change are allowed; they may reach a memoized fixed point after finite expansion.
60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/ibex/normalize/parameters.rb', line 60 def growing_parameter_arguments?(candidates, ancestors) return false unless candidates.length == ancestors.length contained = candidates.each_index.all? do |index| candidate = candidates.fetch(index) ancestor = ancestors.fetch(index) parameter_argument_contains?(candidate, NormalizeExpression.render(ancestor)) end contained && candidates.each_index.any? do |index| candidate = candidates.fetch(index) ancestor = ancestors.fetch(index) NormalizeExpression.render(candidate) != NormalizeExpression.render(ancestor) end end |
#new_parameter_helper(reference) ⇒ String
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/ibex/normalize/parameters.rb', line 95 def new_parameter_helper(reference) # @type self: Normalizer @helper_sequence += 1 name = "$parameter_#{@helper_sequence}" definition = intern( name, :nonterminal, location: reference.loc.to_h, documentation: @rule_documentation[reference.name], metadata_name: reference.name ) if @inline_rule_names.include?(reference.name) @inline_symbol_ids << definition.id @inline_rule_by_symbol[definition.id] = reference.name end name end |
#parameter_argument_contains?(candidate, target) ⇒ Boolean
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/ibex/normalize/parameters.rb', line 76 def parameter_argument_contains?(candidate, target) pending = [candidate] #: Array[Frontend::AST::item] until pending.empty? item = pending.pop raise Ibex::Error, "missing parameter argument" unless item return true if NormalizeExpression.render(item) == target case item when Frontend::AST::ParameterizedReference then pending.concat(item.arguments) when Frontend::AST::Group then item.alternatives.each { |alternative| pending.concat(alternative) } when Frontend::AST::Optional, Frontend::AST::Star, Frontend::AST::Plus then pending << item.item when Frontend::AST::SeparatedList then pending.push(item.item, item.separator) end end false end |
#parameter_frame(reference, helper, arguments, rendered_arguments) ⇒ Hash[Symbol, untyped]
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/ibex/normalize/parameters.rb', line 115 def parameter_frame(reference, helper, arguments, rendered_arguments) # @type self: Normalizer formals = @parameter_formals.fetch(reference.name) { reference: reference, arguments: arguments, helper: helper, bindings: formals.zip(arguments).to_h, rendered_arguments: rendered_arguments, templates: @parameter_templates.fetch(reference.name), template_index: 0, alternative_index: 0, current: nil, item_index: 0, rhs: [], named_refs: [], operations: [], values: [] } end |
#prepare_parameter_alternative?(frame) ⇒ Boolean
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/ibex/normalize/parameters.rb', line 154 def prepare_parameter_alternative?(frame) # @type self: Normalizer templates = frame.fetch(:templates) while frame.fetch(:template_index) < templates.length template = templates.fetch(frame.fetch(:template_index)) index = frame.fetch(:alternative_index) if index < template.alternatives.length prepare_parameter_alternative_entry(frame, template, index) return true end frame[:template_index] += 1 frame[:alternative_index] = 0 end false end |
#prepare_parameter_alternative_entry(frame, template, index) ⇒ void
This method returns an undefined value.
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/ibex/normalize/parameters.rb', line 171 def prepare_parameter_alternative_entry(frame, template, index) # @type self: Normalizer alternative = substitute_parameter_alternative( template.alternatives.fetch(index), frame.fetch(:bindings) ) rule = Frontend::AST::Rule.new( lhs: frame.fetch(:helper), parameters: [], alternatives: [alternative], loc: template.loc, documentation: template.documentation, inline: false ) frame[:alternative_index] = index + 1 frame[:current] = [template, rule, alternative] frame[:item_index] = 0 frame[:rhs] = [] frame[:named_refs] = [] frame[:operations] = [] frame[:values] = [] end |
#schedule_parameter_specialization(reference) ⇒ [ String, bool ]
21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/ibex/normalize/parameters.rb', line 21 def schedule_parameter_specialization(reference) # @type self: Normalizer arguments = reference.arguments rendered_arguments = arguments.map { |argument| NormalizeExpression.render(argument) } key = [reference.name, rendered_arguments] #: [String, Array[String]] existing = @parameter_specializations[key] return [existing, false] if existing enforce_parameter_limits!(reference) helper = new_parameter_helper(reference) @parameter_specializations[key] = helper @parameter_worklist << parameter_frame(reference, helper, arguments, rendered_arguments) [helper, true] end |
#specialize_parameterized_reference(reference) ⇒ String
9 10 11 12 13 14 15 16 17 18 |
# File 'lib/ibex/normalize/parameters.rb', line 9 def specialize_parameterized_reference(reference) # @type self: Normalizer helper, scheduled = schedule_parameter_specialization(reference) if scheduled && @parameter_worklist_active raise Ibex::Error, "#{reference.loc}: internal parameter worklist ordering failure" end drain_parameter_worklist unless @parameter_worklist_active helper end |