Class: Ibex::Runtime::CST::Blender
- Inherits:
-
Object
- Object
- Ibex::Runtime::CST::Blender
- Defined in:
- lib/ibex/runtime/cst/incremental/blender.rb,
sig/ibex/runtime/cst/incremental/blender.rbs
Overview
A state-aware token source that substitutes validated old subtrees.
Defined Under Namespace
Classes: Candidate
Constant Summary collapse
- UNSAFE_FLAGS =
Flags::CONTAINS_ERROR | Flags::CONTAINS_MISSING | Flags::CONTAINS_SKIPPED
Instance Attribute Summary collapse
- #decomposed_nodes ⇒ Integer readonly
- #fallback_reason ⇒ Symbol? readonly
- #reused_descendants ⇒ Integer readonly
Instance Method Summary collapse
- #add_candidate(node, preorder, old_offset, start_token, end_token) ⇒ void
- #build_candidates ⇒ void
- #consume_decomposition_budget! ⇒ void
-
#initialize(old_root:, parse_memo:, lexed:, edits:, max_decomposed_nodes:, enabled: true) ⇒ Blender
constructor
A new instance of Blender.
- #matching_follow_token?(old_index, new_index) ⇒ Boolean
- #matching_tokens?(old_index, new_index, count) ⇒ Boolean
-
#next_for_state(state) ⇒ Array[untyped], ...
Return the next token or a whole nonterminal valid in the current LR state.
- #offset_index(offsets) ⇒ Hash[Integer, Integer]
- #outside_damage?(start_offset, finish_offset) ⇒ Boolean
- #previous_trailing(token_index) ⇒ Array[GreenTrivia]
- #reusable_node?(node, old_offset) ⇒ Boolean
- #reusable_root? ⇒ Boolean
- #token_count ⇒ Integer
- #translated_offset(old_offset) ⇒ Integer
- #walk(element, offset:, preorder:, token_index:) ⇒ [ Integer, Integer, Integer ]
Constructor Details
#initialize(old_root:, parse_memo:, lexed:, edits:, max_decomposed_nodes:, enabled: true) ⇒ Blender
Returns a new instance of Blender.
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 60 def initialize(old_root:, parse_memo:, lexed:, edits:, max_decomposed_nodes:, enabled: true) @old_root = old_root @parse_memo = parse_memo @lexed = lexed @edits = TextEdit.normalize(edits) @max_decomposed_nodes = max_decomposed_nodes @enabled = enabled @candidates = {} #: Hash[Integer, Array[Candidate]] @new_offsets = offset_index(@lexed.memo.offsets) @old_tokens = @old_root.tokens.map(&:green) @token_index = 0 @reused_descendants = 0 @decomposed_nodes = 0 @fallback_reason = nil #: Symbol? build_candidates if @enabled && reusable_root? end |
Instance Attribute Details
#decomposed_nodes ⇒ Integer (readonly)
44 45 46 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 44 def decomposed_nodes @decomposed_nodes end |
#fallback_reason ⇒ Symbol? (readonly)
45 46 47 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 45 def fallback_reason @fallback_reason end |
#reused_descendants ⇒ Integer (readonly)
43 44 45 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 43 def reused_descendants @reused_descendants end |
Instance Method Details
#add_candidate(node, preorder, old_offset, start_token, end_token) ⇒ void
This method returns an undefined value.
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 135 def add_candidate(node, preorder, old_offset, start_token, end_token) return unless reusable_node?(node, old_offset) new_offset = translated_offset(old_offset) new_index = @new_offsets[new_offset] token_count = end_token - start_token return unless new_index && token_count.positive? return unless matching_tokens?(start_token, new_index, token_count) return unless matching_follow_token?(end_token, new_index + token_count) left_state = @parse_memo.left_state(preorder) return unless left_state entry = ReusableSubtree.new( green: node, lhs: @old_root.kinds.nonterminal_of(node.kind), left_state: left_state, left_states: @parse_memo.slice(preorder, node), previous_trailing: previous_trailing(new_index) ) empty = [] #: Array[Candidate] (@candidates[new_index] ||= empty) << Candidate.new(entry: entry, token_count: token_count) end |
#build_candidates ⇒ void
This method returns an undefined value.
107 108 109 110 111 112 113 114 115 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 107 def build_candidates walk(@old_root.green, offset: 0, preorder: 0, token_index: 0) @candidates.each_value do |values| values.sort_by! { |candidate| -candidate.entry.green.descendant_count } end rescue ResourceLimitError @candidates.clear @fallback_reason = :decomposition_budget end |
#consume_decomposition_budget! ⇒ void
This method returns an undefined value.
218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 218 def consume_decomposition_budget! @decomposed_nodes += 1 return if @decomposed_nodes <= @max_decomposed_nodes raise ResourceLimitError.new( resource: :incremental_decomposed_nodes, limit: @max_decomposed_nodes, observed: @decomposed_nodes, state: nil, location: nil ) end |
#matching_follow_token?(old_index, new_index) ⇒ Boolean
195 196 197 198 199 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 195 def matching_follow_token?(old_index, new_index) old_follow = @old_tokens[old_index] new_follow = @lexed.memo.tokens[new_index] old_follow && new_follow ? old_follow.equal?(new_follow) : false end |
#matching_tokens?(old_index, new_index, count) ⇒ Boolean
188 189 190 191 192 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 188 def matching_tokens?(old_index, new_index, count) count.times.all? do |relative| @old_tokens.fetch(old_index + relative).equal?(@lexed.memo.tokens[new_index + relative]) end end |
#next_for_state(state) ⇒ Array[untyped], ...
Return the next token or a whole nonterminal valid in the current LR state.
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 79 def next_for_state(state) candidates = @candidates[@token_index] candidate = candidates&.find { |item| item.entry.left_state == state } if candidate @token_index += candidate.token_count @reused_descendants += candidate.entry.green.descendant_count return candidate.entry end token = @lexed.raw_tokens[@token_index] return false unless token @token_index += 1 token end |
#offset_index(offsets) ⇒ Hash[Integer, Integer]
211 212 213 214 215 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 211 def offset_index(offsets) result = {} #: Hash[Integer, Integer] offsets.each_with_index { |offset, index| result[offset] ||= index } result end |
#outside_damage?(start_offset, finish_offset) ⇒ Boolean
168 169 170 171 172 173 174 175 176 177 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 168 def outside_damage?(start_offset, finish_offset) @edits.none? do |edit| edit_finish = edit.start + edit.delete_length if edit.delete_length.zero? edit.start >= start_offset && edit.start < finish_offset else edit.start < finish_offset && edit_finish > start_offset end end end |
#previous_trailing(token_index) ⇒ Array[GreenTrivia]
202 203 204 205 206 207 208 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 202 def previous_trailing(token_index) location = @lexed.raw_tokens.fetch(token_index).fetch(2) return [] unless location.is_a?(Hash) value = location[:cst_previous_trailing] value.is_a?(Array) ? value.grep(GreenTrivia) : [] end |
#reusable_node?(node, old_offset) ⇒ Boolean
160 161 162 163 164 165 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 160 def reusable_node?(node, old_offset) node.full_width.positive? && node.flags.nobits?(UNSAFE_FLAGS) && @old_root.kinds.nonterminal?(node.kind) && outside_damage?(old_offset, old_offset + node.full_width) end |
#reusable_root? ⇒ Boolean
101 102 103 104 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 101 def reusable_root? @old_root.green.flags.nobits?(UNSAFE_FLAGS) && @parse_memo.left_states.length == @old_root.green.descendant_count end |
#token_count ⇒ Integer
96 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 96 def token_count = @lexed.memo.tokens.length |
#translated_offset(old_offset) ⇒ Integer
180 181 182 183 184 185 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 180 def translated_offset(old_offset) delta = @edits.sum do |edit| edit.start + edit.delete_length <= old_offset ? edit.insert_text.bytesize - edit.delete_length : 0 end old_offset + delta end |
#walk(element, offset:, preorder:, token_index:) ⇒ [ Integer, Integer, Integer ]
119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 119 def walk(element, offset:, preorder:, token_index:) next_preorder = preorder + 1 return [next_preorder, offset + element.full_width, token_index + 1] if element.is_a?(GreenToken) consume_decomposition_budget! start_token = token_index element.children.each do |child| next_preorder, offset, token_index = walk( child, offset: offset, preorder: next_preorder, token_index: token_index ) end add_candidate(element, preorder, offset - element.full_width, start_token, token_index) [next_preorder, offset, token_index] end |