Class: Gullah::Node
- Inherits:
-
Object
- Object
- Gullah::Node
- Defined in:
- lib/gullah/node.rb
Overview
a node in an AST
Defined Under Namespace
Classes: Ancestors, Descendants
Instance Attribute Summary collapse
-
#attributes ⇒ Object
(also: #atts)
readonly
A hash of attributes, including indicators of tests that passed or failed.
-
#children ⇒ Object
readonly
The children of this node, if any, as an array.
-
#parent ⇒ Object
readonly
The parent node of this node, if any.
-
#rule ⇒ Object
readonly
:nodoc:.
-
#summary ⇒ Object
readonly
A concise stringification of the structure of this node’s subtree.
Instance Method Summary collapse
-
#_ancestors(skip) ⇒ Object
:nodoc:.
-
#_attributes=(attributes) ⇒ Object
:nodoc:.
-
#_children=(children) ⇒ Object
:nodoc:.
-
#_descendants(skip) ⇒ Object
:nodoc:.
-
#_failed_test=(bool) ⇒ Object
:nodoc:.
-
#_loop_check?(seen = nil) ⇒ Boolean
used during parsing make sure we don’t have any repeated symbols in a unary branch.
-
#_parent=(other) ⇒ Object
:nodoc:.
-
#_summary=(str) ⇒ Object
:stopdoc:.
-
#ancestors ⇒ Object
Returns an Enumerable enumerating the nodes immediately above this node in the tree: its parent, its parent’s parent, etc.
-
#boundary? ⇒ Boolean
Is this node one that cannot be the child of another node?.
-
#clone ⇒ Object
:nodoc:.
-
#contains?(offset) ⇒ Boolean
Does this node contain the given text offset?.
-
#dbg(so: false) ⇒ Object
Produces a simplified representation of the node to facilitate debugging.
-
#depth ⇒ Object
Distance of the node from the root node of the parse tree.
-
#descendants ⇒ Object
Returns an Enumerable over the descendants of this node: its children, its children’s children, etc.
-
#end ⇒ Object
The node’s end text offset.
-
#error? ⇒ Boolean
Does this node have some failed test?.
-
#failed? ⇒ Boolean
Does this node have some failed test or does it represent characters no leaf rule mached?.
-
#find(pos) ⇒ Object
Finds the node at the given position within this node’s subtree.
-
#first_child? ⇒ Boolean
Is this node the first of its parent’s children?.
-
#full_text ⇒ Object
A reference to the full text the node’s text is embedded in.
-
#height ⇒ Object
The distance of a node from the first leaf node in its subtree.
-
#ignorable? ⇒ Boolean
Was this node created by an
ignorerule?. -
#initialize(parse, s, e, rule) ⇒ Node
constructor
:nodoc:.
-
#last_child? ⇒ Boolean
Is this node the last of its parent’s children?.
-
#later ⇒ Object
The collection of nodes in the subtree containing this node whose start offset is at or after its end offset.
-
#later_sibling ⇒ Object
The immediately following sibling to this node.
-
#later_siblings ⇒ Object
Returns the children of this node’s parent that follow it.
-
#leaf? ⇒ Boolean
Is this a leaf node?.
-
#leaves ⇒ Object
The leaves of this node’s subtree.
-
#name ⇒ Object
The name of the rule that created this node.
-
#nonterminal? ⇒ Boolean
Is this a node that has other nodes as children?.
-
#pending_tests? ⇒ Boolean
Does this node’s subtree contain unsatisfied syntactic requirements? These are tests that depend on nodes not in the node’s own subtree.
-
#position ⇒ Object
A pair consisting of the nodes start and height.
-
#prior ⇒ Object
The collection of nodes in the subtree containing this node that do not
containthe node and whose start offset precedes its start offset. -
#prior_sibling ⇒ Object
The immediately prior sibling to this node.
-
#prior_siblings ⇒ Object
Returns the children of this node’s parent that precede it.
-
#root ⇒ Object
The root of this node’s current parse tree.
-
#root? ⇒ Boolean
Does this node have any parent? If not, it is a root.
-
#sibling_index ⇒ Object
The index of this node among its parent’s children.
-
#siblings ⇒ Object
Returns the children of this node’s parent’s children minus this node itself.
-
#significant? ⇒ Boolean
Was this node created by something other than an
ignorerule?. -
#size ⇒ Object
The number of nodes in this node’s subtree.
-
#start ⇒ Object
The node’s start text offset.
-
#subtree ⇒ Object
Returns an Enumerable over this node and its descendants.
-
#text ⇒ Object
The portion of the original text covered by this node.
-
#text_after ⇒ Object
The text following this node’s text.
-
#text_before ⇒ Object
The text preceding this node’s text.
-
#trash? ⇒ Boolean
Does this node represent a character sequence no leaf rule matched?.
-
#traversible? ⇒ Boolean
is this node some sort of boundary to further matching.
Constructor Details
#initialize(parse, s, e, rule) ⇒ Node
:nodoc:
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/gullah/node.rb', line 29 def initialize(parse, s, e, rule) # :nodoc: @rule = rule @leaf = rule.is_a?(Leaf) || trash? @text = parse.text @attributes = {} @failed_test = false if @leaf @start = s @end = e else @children = parse.roots[s...e] @children.each { |n| adopt n } end unless trash? rule.tests.each do |t| result, *extra = Array(t.call(self)) case result when :ignore # no-op test when :pass (attributes[:satisfied] ||= []) << [t.name, *extra] when :fail @failed_test = true (attributes[:failures] ||= []) << [t.name, *extra] break else raise Error, " test \#{t.name} returned an unexpected value:\n \#{result.inspect}\n expected values: \#{%i[ignore pass fail].inspect}\n MSG\n end\n end\n end\n unless failed?\n # if any test failed, this node will not be the child of another node\n rule.ancestor_tests.each do |t|\n # use position rather than node itself for the sake of clonability\n (attributes[:pending] ||= []) << [t, position]\n end\n end\nend\n" |
Instance Attribute Details
#attributes ⇒ Object (readonly) Also known as: atts
A hash of attributes, including indicators of tests that passed or failed. The atts alias of attributes exists for when a more telegraphic coding style is useful.
15 16 17 |
# File 'lib/gullah/node.rb', line 15 def attributes @attributes end |
#children ⇒ Object (readonly)
The children of this node, if any, as an array.
19 20 21 |
# File 'lib/gullah/node.rb', line 19 def children @children end |
#parent ⇒ Object (readonly)
The parent node of this node, if any.
8 9 10 |
# File 'lib/gullah/node.rb', line 8 def parent @parent end |
#rule ⇒ Object (readonly)
:nodoc:
10 11 12 |
# File 'lib/gullah/node.rb', line 10 def rule @rule end |
#summary ⇒ Object (readonly)
A concise stringification of the structure of this node’s subtree.
23 24 25 |
# File 'lib/gullah/node.rb', line 23 def summary @summary end |
Instance Method Details
#_ancestors(skip) ⇒ Object
:nodoc:
456 457 458 |
# File 'lib/gullah/node.rb', line 456 def _ancestors(skip) # :nodoc: Ancestors.new(self, skip) end |
#_attributes=(attributes) ⇒ Object
:nodoc:
440 441 442 |
# File 'lib/gullah/node.rb', line 440 def _attributes=(attributes) # :nodoc: @attributes = attributes end |
#_children=(children) ⇒ Object
:nodoc:
448 449 450 |
# File 'lib/gullah/node.rb', line 448 def _children=(children) # :nodoc: @children = children end |
#_descendants(skip) ⇒ Object
:nodoc:
452 453 454 |
# File 'lib/gullah/node.rb', line 452 def _descendants(skip) # :nodoc: Descendants.new(self, skip) end |
#_failed_test=(bool) ⇒ Object
:nodoc:
460 461 462 |
# File 'lib/gullah/node.rb', line 460 def _failed_test=(bool) # :nodoc: @failed_test = bool end |
#_loop_check?(seen = nil) ⇒ Boolean
used during parsing make sure we don’t have any repeated symbols in a unary branch
426 427 428 429 430 431 432 433 434 435 436 437 438 |
# File 'lib/gullah/node.rb', line 426 def _loop_check?(seen = nil) # :nodoc: return true if seen == name return false if !@leaf && children.length > 1 if seen.nil? # this is the beginning of the check # the only name we need look for is this rule's name, since # all those below it must have passed the check seen = name end @leaf ? false : children.first._loop_check?(seen) end |
#_parent=(other) ⇒ Object
:nodoc:
444 445 446 |
# File 'lib/gullah/node.rb', line 444 def _parent=(other) # :nodoc: @parent = other end |
#_summary=(str) ⇒ Object
:stopdoc:
420 421 422 |
# File 'lib/gullah/node.rb', line 420 def _summary=(str) # :nodoc: @summary = str end |
#ancestors ⇒ Object
Returns an Enumerable enumerating the nodes immediately above this node in the tree: its parent, its parent’s parent, etc.
251 252 253 |
# File 'lib/gullah/node.rb', line 251 def ancestors _ancestors self end |
#boundary? ⇒ Boolean
Is this node one that cannot be the child of another node?
86 87 88 |
# File 'lib/gullah/node.rb', line 86 def boundary? false end |
#clone ⇒ Object
:nodoc:
340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/gullah/node.rb', line 340 def clone # :nodoc: super.tap do |c| c._attributes = deep_clone(attributes) unless c.leaf? c._children = deep_clone(children) c.children.each do |child| child._parent = c end end end end |
#contains?(offset) ⇒ Boolean
Does this node contain the given text offset?
205 206 207 |
# File 'lib/gullah/node.rb', line 205 def contains?(offset) start <= offset && offset < self.end end |
#dbg(so: false) ⇒ Object
Produces a simplified representation of the node to facilitate debugging. The so named parameter, if true, will cause the representation to drop ignored nodes. The name “so” stands for “significant only”.
> pp root.dbg
{:name=>:S,
:pos=>{:start=>0, :end=>11, :depth=>0},
:children=>
[{:name=>:NP,
:pos=>{:start=>0, :end=>7, :depth=>1},
:children=>
[{:name=>:D, :pos=>{:start=>0, :end=>3, :depth=>2}, :text=>"the"},
{:name=>:_ws,
:pos=>{:start=>3, :end=>4, :depth=>2},
:ignorable=>true,
:text=>" "},
{:name=>:N, :pos=>{:start=>4, :end=>7, :depth=>2}, :text=>"cat"}]},
{:name=>:_ws,
:pos=>{:start=>7, :end=>8, :depth=>1},
:ignorable=>true,
:text=>" "},
{:name=>:VP,
:pos=>{:start=>8, :end=>11, :depth=>1},
:children=>
[{:name=>:V, :pos=>{:start=>8, :end=>11, :depth=>2}, :text=>"sat"}]}]}
> pp root.dbg so: true
{:name=>:S,
:pos=>{:start=>0, :end=>11, :depth=>0},
:children=>
[{:name=>:NP,
:pos=>{:start=>0, :end=>7, :depth=>1},
:children=>
[{:name=>:D, :pos=>{:start=>0, :end=>3, :depth=>2}, :text=>"the"},
{:name=>:_ws, :pos=>{:start=>3, :end=>4, :depth=>2}, :text=>" "},
{:name=>:N, :pos=>{:start=>4, :end=>7, :depth=>2}, :text=>"cat"}]},
{:name=>:_ws, :pos=>{:start=>7, :end=>8, :depth=>1}, :text=>" "},
{:name=>:VP,
:pos=>{:start=>8, :end=>11, :depth=>1},
:children=>
[{:name=>:V, :pos=>{:start=>8, :end=>11, :depth=>2}, :text=>"sat"}]}]}
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 |
# File 'lib/gullah/node.rb', line 395 def dbg(so: false) { name: name, pos: { start: start, end: self.end, depth: depth } }.tap do |simpleton| simpleton[:failed] = true if @failed_test simpleton[:attributes] = deep_clone attributes if attributes.any? if leaf? simpleton[:trash] = true if trash? simpleton[:ignorable] = true unless so || significant? simpleton[:text] = text else simpleton[:children] = children.map { |c| c.dbg so: so } end end end |
#depth ⇒ Object
Distance of the node from the root node of the parse tree. During parsing, while nodes are being added, this distance may change, unlike the height.
The root node has a depth of 0. It’s children have a depth of 1. Their children have a depth of 2. And so forth.
184 185 186 |
# File 'lib/gullah/node.rb', line 184 def depth parent ? 1 + parent.depth : 0 end |
#descendants ⇒ Object
Returns an Enumerable over the descendants of this node: its children, its children’s children, etc. This enumeration is depth-first.
258 259 260 |
# File 'lib/gullah/node.rb', line 258 def descendants _descendants self end |
#end ⇒ Object
The node’s end text offset. For a non-terminal node, this will be the same as the end of the last leaf node of its subtree.
173 174 175 |
# File 'lib/gullah/node.rb', line 173 def end @end ||= @children[-1].end end |
#error? ⇒ Boolean
Does this node have some failed test?
109 110 111 |
# File 'lib/gullah/node.rb', line 109 def error? @failed_test end |
#failed? ⇒ Boolean
Does this node have some failed test or does it represent characters no leaf rule mached?
98 99 100 |
# File 'lib/gullah/node.rb', line 98 def failed? trash? || error? end |
#find(pos) ⇒ Object
Finds the node at the given position within this node’s subtree.
211 212 213 214 215 216 217 218 219 220 |
# File 'lib/gullah/node.rb', line 211 def find(pos) offset = pos.first return nil unless contains?(offset) return self if pos == position if (child = children&.find { |c| c.contains? offset }) child.find(pos) end end |
#first_child? ⇒ Boolean
Is this node the first of its parent’s children?
301 302 303 |
# File 'lib/gullah/node.rb', line 301 def first_child? sibling_index.zero? end |
#full_text ⇒ Object
A reference to the full text the node’s text is embedded in.
147 148 149 |
# File 'lib/gullah/node.rb', line 147 def full_text @text end |
#height ⇒ Object
The distance of a node from the first leaf node in its subtree. If the node is the immediate parent of this leaf, its distance will be one. Leaves have a height of zero.
192 193 194 |
# File 'lib/gullah/node.rb', line 192 def height @height ||= @leaf ? 0 : 1 + children[0].height end |
#ignorable? ⇒ Boolean
Was this node created by an ignore rule?
122 123 124 |
# File 'lib/gullah/node.rb', line 122 def ignorable? @leaf && rule.ignorable end |
#last_child? ⇒ Boolean
Is this node the last of its parent’s children?
295 296 297 |
# File 'lib/gullah/node.rb', line 295 def last_child? parent && sibling_index == parent.children.length - 1 end |
#later ⇒ Object
The collection of nodes in the subtree containing this node whose start offset is at or after its end offset.
336 337 338 |
# File 'lib/gullah/node.rb', line 336 def later root.descendants.select { |n| n.start >= self.end } end |
#later_sibling ⇒ Object
The immediately following sibling to this node.
315 316 317 |
# File 'lib/gullah/node.rb', line 315 def later_sibling parent && parent.children[sibling_index + 1] end |
#later_siblings ⇒ Object
Returns the children of this node’s parent that follow it.
289 290 291 |
# File 'lib/gullah/node.rb', line 289 def later_siblings parent && siblings[(sibling_index + 1)..] end |
#leaf? ⇒ Boolean
Is this a leaf node?
92 93 94 |
# File 'lib/gullah/node.rb', line 92 def leaf? @leaf end |
#leaves ⇒ Object
The leaves of this node’s subtree. If the node is a leaf, this returns a single-member array containing the node itself.
322 323 324 |
# File 'lib/gullah/node.rb', line 322 def leaves @leaf ? [self] : descendants.select(&:leaf?) end |
#name ⇒ Object
The name of the rule that created this node.
74 75 76 |
# File 'lib/gullah/node.rb', line 74 def name rule.name end |
#nonterminal? ⇒ Boolean
Is this a node that has other nodes as children?
134 135 136 |
# File 'lib/gullah/node.rb', line 134 def nonterminal? !@leaf end |
#pending_tests? ⇒ Boolean
Does this node’s subtree contain unsatisfied syntactic requirements? These are tests that depend on nodes not in the node’s own subtree.
116 117 118 |
# File 'lib/gullah/node.rb', line 116 def pending_tests? !!attributes[:pending] end |
#position ⇒ Object
A pair consisting of the nodes start and height. This will be a unique identifier for the node in its parse and is constant at all stages of parsing.
199 200 201 |
# File 'lib/gullah/node.rb', line 199 def position @position ||= [start, height] end |
#prior ⇒ Object
The collection of nodes in the subtree containing this node that do not contain the node and whose start offset precedes its start offset.
329 330 331 |
# File 'lib/gullah/node.rb', line 329 def prior root.descendants.reject { |n| n.contains? start }.select { |n| n.start < start } end |
#prior_sibling ⇒ Object
The immediately prior sibling to this node.
307 308 309 310 311 |
# File 'lib/gullah/node.rb', line 307 def prior_sibling if parent first_child? ? nil : parent.children[sibling_index - 1] end end |
#prior_siblings ⇒ Object
Returns the children of this node’s parent that precede it.
283 284 285 |
# File 'lib/gullah/node.rb', line 283 def prior_siblings parent && siblings[0...sibling_index] end |
#root ⇒ Object
The root of this node’s current parse tree.
Note, if you use this in a node test the root will always be the same as the node itself because these tests are run when the node is being added to the tree. If you use it in structure tests, it will be some ancestor of the node but not necessarily the final root. The current root is always the first argument to structure tests. Using this argument is more efficient than using the root method. Really, the root method is only useful in completed parses.
238 239 240 |
# File 'lib/gullah/node.rb', line 238 def root parent ? parent.root : self end |
#root? ⇒ Boolean
Does this node have any parent? If not, it is a root.
244 245 246 |
# File 'lib/gullah/node.rb', line 244 def root? parent.nil? end |
#sibling_index ⇒ Object
The index of this node among its parent’s children.
277 278 279 |
# File 'lib/gullah/node.rb', line 277 def sibling_index @sibling_index ||= parent.children.index self if parent end |
#siblings ⇒ Object
Returns the children of this node’s parent’s children minus this node itself.
271 272 273 |
# File 'lib/gullah/node.rb', line 271 def siblings parent&.children&.reject { |n| n == self } end |
#significant? ⇒ Boolean
Was this node created by something other than an ignore rule?
128 129 130 |
# File 'lib/gullah/node.rb', line 128 def significant? !ignorable? end |
#size ⇒ Object
The number of nodes in this node’s subtree. Leaves always have a size of 1.
224 225 226 |
# File 'lib/gullah/node.rb', line 224 def size @size ||= @leaf ? 1 : @children.map(&:size).sum + 1 end |
#start ⇒ Object
The node’s start text offset. For a non-terminal node, this will be the same as the start of the first leaf node of its subtree.
166 167 168 |
# File 'lib/gullah/node.rb', line 166 def start @start ||= @children[0].start end |
#subtree ⇒ Object
Returns an Enumerable over this node and its descendants. The node itself is the first node returned.
265 266 267 |
# File 'lib/gullah/node.rb', line 265 def subtree _descendants nil end |
#text ⇒ Object
The portion of the original text covered by this node. This is in effect the text of the leaves of its subtree.
141 142 143 |
# File 'lib/gullah/node.rb', line 141 def text @text[start...self.end] end |
#text_after ⇒ Object
The text following this node’s text. Useful for lookaround tests and preconditions.
159 160 161 |
# File 'lib/gullah/node.rb', line 159 def text_after @text[self.end..] end |
#text_before ⇒ Object
The text preceding this node’s text. Useful for lookaround tests and preconditions.
153 154 155 |
# File 'lib/gullah/node.rb', line 153 def text_before @text[0...start] end |
#trash? ⇒ Boolean
Does this node represent a character sequence no leaf rule matched?
80 81 82 |
# File 'lib/gullah/node.rb', line 80 def trash? false end |
#traversible? ⇒ Boolean
is this node some sort of boundary to further matching
103 104 105 |
# File 'lib/gullah/node.rb', line 103 def traversible? # :nodoc: !(boundary? || trash? || error?) end |