Class: BabelBridge::RuleNode

Inherits:
NonTerminalNode show all
Defined in:
lib/nodes/rule_node.rb

Overview

subclassed automatically by parser.rule for each unique non-terminal

Instance Attribute Summary

Attributes inherited from Node

#delimiter, #many_delimiter, #match_length, #offset, #parent, #parser, #src

Instance Method Summary collapse

Methods inherited from NonTerminalNode

#[], #each, #last_match, #length, #matches, #update_match_length

Methods inherited from Node

#column, #init_line_column, #length, #line, #match_range, #node_init, #node_path, #offset_after_match, #on_matched, #onlychildren_list, #parent_list, #path_string, #relative_class_name, #remaining_src, #text, #to_s, #to_sym

Constructor Details

#initialize(parent, delimiter_pattern = nil) ⇒ RuleNode

Returns a new instance of RuleNode.



12
13
14
15
16
# File 'lib/nodes/rule_node.rb', line 12

def initialize(parent, delimiter_pattern = nil)
  @num_match_attempts = 0
  @delimiter_pattern = delimiter_pattern
  super parent
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args) ⇒ Object

method_name is a symbol



85
86
87
88
89
90
91
92
93
# File 'lib/nodes/rule_node.rb', line 85

def method_missing(method_name, *args)  #method_name is a symbol
  return matches_by_name[method_name] if matches_by_name.has_key?(method_name)

  if f = forward_to(method_name)
    return f.send(method_name,*args)
  end

  raise "#{path_string onlychildren_list}: no methods or named pattern elements match: #{method_name.inspect} on #{self.class} instance"
end

Instance Method Details

#add_match(match, name = nil) ⇒ Object

adds a match with name (optional)



96
97
98
99
100
101
102
103
104
# File 'lib/nodes/rule_node.rb', line 96

def add_match(match,name=nil)
  return unless match
  return match if match==self

  add_match_name(super(match),name)

  update_match_length
  match
end

#add_match_name(match, name) ⇒ Object



31
32
33
34
35
36
37
38
39
# File 'lib/nodes/rule_node.rb', line 31

def add_match_name(match,name)
  return unless name
  if current = matches_by_name[name]
    matches_by_name[name] = MultiMatchesArray.new([current]) if !current.kind_of? MultiMatchesArray
    matches_by_name[name] << match
  else
    matches_by_name[name] = match
  end
end

#attempt_matchObject

a simple “transaction” - logs the curent number of matches, if the block’s result is false, it discards all new matches



143
144
145
146
147
148
149
150
151
152
# File 'lib/nodes/rule_node.rb', line 143

def attempt_match
  matches_before = matches.length
  match_length_before = match_length
  (yield && match_length > match_length_before).tap do |success|  # match_length test returns failure if no progress is made (our source position isn't advanced)
    unless success
      @matches = matches[0..matches_before-1]
      update_match_length
    end
  end
end

#forward_to(method_name) ⇒ Object

returns where to forward missing methods calls to (safe to override for custom behavior; respond_to? and method_missing will “do the right thing”) returns nil if there is no object to forward to that will respond to the call default: forward to the first match that responds to method_name



74
75
76
77
# File 'lib/nodes/rule_node.rb', line 74

def forward_to(method_name)
  matches.each {|m| return m if m.respond_to?(method_name)}
  nil
end

#inspect(options = {}) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/nodes/rule_node.rb', line 45

def inspect(options={})
  return relative_class_name if matches.length==0
  matches = @matches
  matches=matches.select{|m|!m.many_delimiter} unless options[:verbose]
  matches_inspected = matches.collect{|a|a.inspect(options)}.compact
  if matches_inspected.length==0 then nil
  elsif matches_inspected.length==1
    m = matches_inspected[0]
    ret = "#{relative_class_name} > "+matches_inspected[0]
    if options[:simple]
      ret = if m["\n"] then m
      else
        # just show the first and last nodes in the chain
        ret.gsub(/( > [A-Z][a-zA-Z0-9:]+ > (\.\.\. > )?)/," > ... > ")
      end
    end
    ret
  else
    (["#{relative_class_name}"]+matches_inspected).join("\n").gsub("\n","\n  ")
  end
end

#match(pattern_element) ⇒ Object

Attempts to match the pattern_element starting at the end of what has already been matched If successful, adds the resulting Node to matches. returns nil on if pattern_element wasn’t matched; non-nil if it was skipped or matched



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/nodes/rule_node.rb', line 113

def match(pattern_element)
  @num_match_attempts += 1
  return :no_pattern_element unless pattern_element
  return :skipped if pattern_element.delimiter &&
    (
    if last_match
      last_match.delimiter       # don't match two delimiters in a row
    else
      @num_match_attempts > 1   # don't match a delimiter as the first element unless this is the first match attempt
    end
    )

  if result = pattern_element.parse(self)
    add_match result, pattern_element.name # success, but don't keep EmptyNodes
  end
end

#match_delimiterObject



130
131
132
# File 'lib/nodes/rule_node.rb', line 130

def match_delimiter
  match @delimiter_pattern
end

#match_name_is_poly(name) ⇒ Object



22
23
24
25
26
27
28
29
# File 'lib/nodes/rule_node.rb', line 22

def match_name_is_poly(name)
  return unless name
  if current = matches_by_name[name]
    matches_by_name[name] = MultiMatchesArray.new([current]) if !current.kind_of? MultiMatchesArray
  else
    matches_by_name[name] = MultiMatchesArray.new
  end
end

#match_namesObject



18
19
20
# File 'lib/nodes/rule_node.rb', line 18

def match_names
  @match_names ||= []
end

#matches_by_nameObject



41
42
43
# File 'lib/nodes/rule_node.rb', line 41

def matches_by_name
  @matches_by_name||={}
end

#pop_matchObject



106
107
108
# File 'lib/nodes/rule_node.rb', line 106

def pop_match
  matches.pop.tap {update_match_length}
end

#post_match_processingObject

called after matching is done and it was a success returns the node which is actually added to the parse tree



136
137
138
139
# File 'lib/nodes/rule_node.rb', line 136

def post_match_processing
  on_matched
  self
end

#respond_to?(method_name) ⇒ Boolean

Returns:

  • (Boolean)


79
80
81
82
83
# File 'lib/nodes/rule_node.rb', line 79

def respond_to?(method_name)
  super ||
  matches_by_name[method_name] ||
  forward_to(method_name)
end