Class: Ibex::Runtime::CST::Blender

Inherits:
Object
  • Object
show all
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 =

Signature:

  • Integer

Returns:

  • (Integer)
Flags::CONTAINS_ERROR | Flags::CONTAINS_MISSING | Flags::CONTAINS_SKIPPED

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(old_root:, parse_memo:, lexed:, edits:, max_decomposed_nodes:, enabled: true) ⇒ Blender

Returns a new instance of Blender.

RBS:

  • (old_root: SyntaxNode, parse_memo: ParseMemo, lexed: LexedSyntax, edits: Array[TextEdit], max_decomposed_nodes: Integer, ?enabled: bool) -> void

Parameters:



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_nodesInteger (readonly)

Signature:

  • Integer

Returns:

  • (Integer)


44
45
46
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 44

def decomposed_nodes
  @decomposed_nodes
end

#fallback_reasonSymbol? (readonly)

Signature:

  • Symbol?

Returns:

  • (Symbol, nil)


45
46
47
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 45

def fallback_reason
  @fallback_reason
end

#reused_descendantsInteger (readonly)

Signature:

  • Integer

Returns:

  • (Integer)


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.

RBS:

  • (GreenNode node, Integer preorder, Integer old_offset, Integer start_token, Integer end_token) -> void

Parameters:

  • node (GreenNode)
  • preorder (Integer)
  • old_offset (Integer)
  • start_token (Integer)
  • end_token (Integer)


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_candidatesvoid

This method returns an undefined value.

RBS:

  • () -> void



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.

RBS:

  • () -> void



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

RBS:

  • (Integer old_index, Integer new_index) -> bool

Parameters:

  • old_index (Integer)
  • new_index (Integer)

Returns:

  • (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

RBS:

  • (Integer old_index, Integer new_index, Integer count) -> bool

Parameters:

  • old_index (Integer)
  • new_index (Integer)
  • count (Integer)

Returns:

  • (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.

RBS:

  • (Integer state) -> (Array[untyped] | ReusableSubtree | false)

Parameters:

  • state (Integer)

Returns:



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]

RBS:

  • (Array[Integer] offsets) -> Hash[Integer, Integer]

Parameters:

  • offsets (Array[Integer])

Returns:

  • (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

RBS:

  • (Integer start_offset, Integer finish_offset) -> bool

Parameters:

  • start_offset (Integer)
  • finish_offset (Integer)

Returns:

  • (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]

RBS:

  • (Integer token_index) -> Array[GreenTrivia]

Parameters:

  • token_index (Integer)

Returns:



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

RBS:

  • (GreenNode node, Integer old_offset) -> bool

Parameters:

Returns:

  • (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

RBS:

  • () -> bool

Returns:

  • (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_countInteger

RBS:

  • () -> Integer

Returns:

  • (Integer)


96
# File 'lib/ibex/runtime/cst/incremental/blender.rb', line 96

def token_count = @lexed.memo.tokens.length

#translated_offset(old_offset) ⇒ Integer

RBS:

  • (Integer old_offset) -> Integer

Parameters:

  • old_offset (Integer)

Returns:

  • (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 ]

RBS:

  • (GreenNode | GreenToken element, offset: Integer, preorder: Integer, token_index: Integer) -> [Integer, Integer, Integer]

Parameters:

Returns:

  • ([ 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