Class: Ibex::Runtime::CST::SyntaxNode
- Inherits:
-
Object
- Object
- Ibex::Runtime::CST::SyntaxNode
- Includes:
- Enumerable
- Defined in:
- lib/ibex/runtime/cst/syntax_node.rb
Overview
Lazy Red navigation facade for one Green node occurrence.
Instance Attribute Summary collapse
-
#green ⇒ Object
readonly
: GreenNode.
-
#index ⇒ Object
readonly
: Integer.
-
#kinds ⇒ Object
readonly
: Kind.
-
#offset ⇒ Object
readonly
: Integer.
-
#parent ⇒ Object
readonly
: SyntaxNode?.
-
#source_text ⇒ Object
readonly
: SourceText.
-
#trivia_policy ⇒ Object
readonly
: Symbol.
Instance Method Summary collapse
- #==(other) ⇒ Object
- #ancestors ⇒ Object
- #annotate(annotation) ⇒ Object
- #annotated(annotation) ⇒ Object
- #child_at(child_index) ⇒ Object
- #child_covering_offset(source_offset) ⇒ Object protected
- #child_nodes ⇒ Object
- #children ⇒ Object
- #contains_error? ⇒ Boolean
-
#covering(range) ⇒ Object
Return the smallest syntax element whose full span covers a range.
- #cursor ⇒ Object
- #deconstruct ⇒ Object
- #deconstruct_keys(_keys) ⇒ Object
- #descendants ⇒ Object
-
#each(&block) ⇒ Object
@rbs! def each: () -> Enumerator[element, self] | () { (element) -> void } -> self.
- #each_error(&block) ⇒ Object
- #error? ⇒ Boolean
- #find(kind:) ⇒ Object
- #first_token ⇒ Object
- #full_span ⇒ Object
- #full_text ⇒ Object (also: #to_source)
- #incomplete_input? ⇒ Boolean
-
#initialize(green:, kinds:, parent: nil, index: 0, offset: 0, trivia_policy: :leading, source_text: nil) ⇒ SyntaxNode
constructor
A new instance of SyntaxNode.
- #insert_child(child_index, child) ⇒ Object
- #kind ⇒ Object
- #kind_name ⇒ Object
- #last_token ⇒ Object
- #location ⇒ Object
- #missing? ⇒ Boolean
- #next_sibling ⇒ Object
- #prev_sibling ⇒ Object
-
#production_id ⇒ Object
Green kinds replace occurrence-local production ids.
- #remove_child(child_index) ⇒ Object
- #replace_with(replacement) ⇒ Object
- #root ⇒ Object
- #same_node?(other) ⇒ Boolean
- #span ⇒ Object
-
#symbol ⇒ Object
Compatibility name for the physical grammar symbol.
- #text ⇒ Object
- #to_h ⇒ Object
-
#token_at(source_offset) ⇒ Object
Find the full-span-owning token for a byte offset.
- #tokens ⇒ Object
-
#trailing_trivia ⇒ Object
Compatibility view of file-tail trivia.
- #walk(&block) ⇒ Object
- #with_child(child_index, child) ⇒ Object
Constructor Details
#initialize(green:, kinds:, parent: nil, index: 0, offset: 0, trivia_policy: :leading, source_text: nil) ⇒ SyntaxNode
Returns a new instance of SyntaxNode.
27 28 29 30 31 32 33 34 35 36 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 27 def initialize(green:, kinds:, parent: nil, index: 0, offset: 0, trivia_policy: :leading, source_text: nil) @green = green @kinds = kinds @parent = parent @index = index @offset = offset @trivia_policy = trivia_policy @source_text = source_text || SourceText.new(root_source) @children = Array.new(@green.children.length) #: Array[element?] end |
Instance Attribute Details
#green ⇒ Object (readonly)
: GreenNode
17 18 19 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 17 def green @green end |
#index ⇒ Object (readonly)
: Integer
19 20 21 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 19 def index @index end |
#kinds ⇒ Object (readonly)
: Kind
21 22 23 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 21 def kinds @kinds end |
#offset ⇒ Object (readonly)
: Integer
20 21 22 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 20 def offset @offset end |
#parent ⇒ Object (readonly)
: SyntaxNode?
18 19 20 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 18 def parent @parent end |
#source_text ⇒ Object (readonly)
: SourceText
23 24 25 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 23 def source_text @source_text end |
#trivia_policy ⇒ Object (readonly)
: Symbol
22 23 24 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 22 def trivia_policy @trivia_policy end |
Instance Method Details
#==(other) ⇒ Object
352 353 354 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 352 def ==(other) other.is_a?(SyntaxNode) && @green == other.green end |
#ancestors ⇒ Object
125 126 127 128 129 130 131 132 133 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 125 def ancestors Enumerator.new do |yielder| ancestor = @parent while ancestor yielder << ancestor ancestor = ancestor.parent end end end |
#annotate(annotation) ⇒ Object
248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 248 def annotate(annotation) raise TypeError, "annotation must be a SyntaxAnnotation" unless annotation.is_a?(SyntaxAnnotation) return root if @green.annotations.include?(annotation) replace_with( GreenNode.new( kind: @green.kind, children: @green.children, flags: @green.intrinsic_flags, annotations: @green.annotations + [annotation] ) ) end |
#annotated(annotation) ⇒ Object
261 262 263 264 265 266 267 268 269 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 261 def annotated(annotation) Enumerator.new do |yielder| visit = lambda do |node| yielder << node if node.green.annotations.include?(annotation) node.child_nodes.each { |child| visit.call(child) } end visit.call(self) end end |
#child_at(child_index) ⇒ Object
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 70 def child_at(child_index) cached = @children.fetch(child_index) return cached if cached green_child = @green.children.fetch(child_index) child_offset = offset_for(child_index) value = if green_child.is_a?(GreenNode) self.class.new( green: green_child, kinds: @kinds, parent: self, index: child_index, offset: child_offset, trivia_policy: @trivia_policy, source_text: @source_text ) else SyntaxToken.new(green: green_child, parent: self, index: child_index, offset: child_offset) end @children[child_index] = value end |
#child_covering_offset(source_offset) ⇒ Object (protected)
378 379 380 381 382 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 378 def child_covering_offset(source_offset) children.find do |child| source_offset >= child.offset && source_offset < child.offset + child.green.full_width end end |
#child_nodes ⇒ Object
88 89 90 91 92 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 88 def child_nodes nodes = [] #: Array[SyntaxNode] children.each { |child| nodes << child if child.is_a?(SyntaxNode) } nodes.freeze end |
#children ⇒ Object
54 55 56 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 54 def children @green.children.each_index.map { |child_index| child_at(child_index) }.freeze end |
#contains_error? ⇒ Boolean
198 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 198 def contains_error? = @green.flags.anybits?(Flags::CONTAINS_ERROR) |
#covering(range) ⇒ Object
Return the smallest syntax element whose full span covers a range.
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 289 def covering(range) ensure_coordinates! start_offset, end_offset = normalize_range(range) return unless covers_offsets?(start_offset, end_offset) node = self loop do child = node.children.find do |candidate| candidate_start = candidate.offset candidate_end = candidate.offset + candidate.green.full_width candidate_start <= start_offset && candidate_end >= end_offset end return node unless child return child if child.is_a?(SyntaxToken) node = child end end |
#cursor ⇒ Object
344 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 344 def cursor = Cursor.new(self) |
#deconstruct ⇒ Object
357 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 357 def deconstruct = children |
#deconstruct_keys(_keys) ⇒ Object
360 361 362 363 364 365 366 367 368 369 370 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 360 def deconstruct_keys(_keys) values = { kind: :node, symbol: symbol, production_id: production_id, children: children, location: location, trailing_trivia: trailing_trivia } #: Hash[Symbol, untyped] @kinds.fields(kind).each do |name, slot| index = slot.is_a?(Hash) ? slot.fetch(:index) : slot values[name.to_sym] = child_at(index) end values.freeze end |
#descendants ⇒ Object
136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 136 def descendants Enumerator.new do |yielder| visit = lambda do |node| node.children.each do |child| yielder << child visit.call(child) if child.is_a?(SyntaxNode) end end visit.call(self) end end |
#each(&block) ⇒ Object
@rbs! def each: () -> Enumerator[element, self] | () { (element) -> void } -> self
62 63 64 65 66 67 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 62 def each(&block) return enum_for(:each) unless block children.each(&block) self end |
#each_error(&block) ⇒ Object
318 319 320 321 322 323 324 325 326 327 328 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 318 def each_error(&block) errors = Enumerator.new do |yielder| descendants.each do |element| yielder << element if element.error? || element.missing? end end #: Enumerator[element, void] return errors unless block errors.each(&block) self end |
#error? ⇒ Boolean
192 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 192 def error? = @kinds.error?(kind) |
#find(kind:) ⇒ Object
309 310 311 312 313 314 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 309 def find(kind:) expected = kind.is_a?(Integer) ? kind : @kinds.fetch(kind) Enumerator.new do |yielder| descendants.each { |element| yielder << element if element.kind == expected } end end |
#first_token ⇒ Object
102 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 102 def first_token = tokens.first |
#full_span ⇒ Object
155 156 157 158 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 155 def full_span ensure_coordinates! @offset...(@offset + @green.full_width) end |
#full_text ⇒ Object Also known as: to_source
170 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 170 def full_text = @green.to_source |
#incomplete_input? ⇒ Boolean
201 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 201 def incomplete_input? = @green.flags.anybits?(Flags::INCOMPLETE_INPUT) |
#insert_child(child_index, child) ⇒ Object
220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 220 def insert_child(child_index, child) raise IndexError, "child index #{child_index} is outside 0..#{@green.children.length}" unless child_index.between?(0, @green.children.length) children = @green.children.dup children.insert(child_index, Editing.green_element(child)) replace_with( GreenNode.new( kind: @green.kind, children: children, flags: @green.intrinsic_flags, annotations: @green.annotations ) ) end |
#kind ⇒ Object
39 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 39 def kind = @green.kind |
#kind_name ⇒ Object
42 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 42 def kind_name = @kinds.name(kind) |
#last_token ⇒ Object
105 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 105 def last_token = tokens.last |
#location ⇒ Object
167 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 167 def location = @source_text.location(span) |
#missing? ⇒ Boolean
195 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 195 def missing? = false |
#next_sibling ⇒ Object
108 109 110 111 112 113 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 108 def next_sibling parent = @parent return unless parent parent.children[@index + 1] end |
#prev_sibling ⇒ Object
116 117 118 119 120 121 122 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 116 def prev_sibling parent = @parent return unless parent return if @index.zero? parent.children[@index - 1] end |
#production_id ⇒ Object
Green kinds replace occurrence-local production ids. Keep the legacy key and reader with the historical synthetic sentinel.
51 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 51 def production_id = -1 |
#remove_child(child_index) ⇒ Object
235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 235 def remove_child(child_index) children = @green.children.dup children.fetch(child_index) children.delete_at(child_index) replace_with( GreenNode.new( kind: @green.kind, children: children, flags: @green.intrinsic_flags, annotations: @green.annotations ) ) end |
#replace_with(replacement) ⇒ Object
204 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 204 def replace_with(replacement) = Editing.replace(self, replacement) |
#root ⇒ Object
149 150 151 152 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 149 def root parent = @parent parent ? parent.root : self end |
#same_node?(other) ⇒ Boolean
347 348 349 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 347 def same_node?(other) root.equal?(other.root) && @green.equal?(other.green) && @offset == other.offset end |
#span ⇒ Object
161 162 163 164 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 161 def span ensure_coordinates! (@offset + @green.leading_width)...(@offset + @green.full_width - @green.trailing_width) end |
#symbol ⇒ Object
Compatibility name for the physical grammar symbol.
46 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 46 def symbol = @kinds.name(@kinds.nonterminal_of(kind)) |
#text ⇒ Object
174 175 176 177 178 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 174 def text start_offset = @green.leading_width width = @green.full_width - @green.leading_width - @green.trailing_width full_text.byteslice(start_offset, width) || "".b end |
#to_h ⇒ Object
373 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 373 def to_h = deconstruct_keys(nil) |
#token_at(source_offset) ⇒ Object
Find the full-span-owning token for a byte offset.
273 274 275 276 277 278 279 280 281 282 283 284 285 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 273 def token_at(source_offset) ensure_coordinates! return unless source_offset >= @offset && source_offset < @offset + @green.full_width node = self loop do child = node.child_covering_offset(source_offset) return unless child return child if child.is_a?(SyntaxToken) node = child end end |
#tokens ⇒ Object
95 96 97 98 99 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 95 def tokens values = [] #: Array[SyntaxToken] descendants.each { |element| values << element if element.is_a?(SyntaxToken) } values.freeze end |
#trailing_trivia ⇒ Object
Compatibility view of file-tail trivia. The Red/Green layout owns it on EOF rather than on the start node.
183 184 185 186 187 188 189 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 183 def trailing_trivia eof = compatibility_eof return (eof.green.leading + eof.green.trailing).freeze if eof token = last_token token ? token.green.trailing : [] end |
#walk(&block) ⇒ Object
331 332 333 334 335 336 337 338 339 340 341 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 331 def walk(&block) raise ArgumentError, "walk requires a block" unless block visit = lambda do |element| block.call(:enter, element) element.children.each { |child| visit.call(child) } if element.is_a?(SyntaxNode) block.call(:leave, element) end visit.call(self) self end |
#with_child(child_index, child) ⇒ Object
207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/ibex/runtime/cst/syntax_node.rb', line 207 def with_child(child_index, child) children = @green.children.dup children.fetch(child_index) children[child_index] = Editing.green_element(child) replace_with( GreenNode.new( kind: @green.kind, children: children, flags: @green.intrinsic_flags, annotations: @green.annotations ) ) end |