babel_bridge 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -4,7 +4,7 @@
4
4
  terminal_node
5
5
  non_terminal_node
6
6
  rule_node
7
- many_node
7
+ root_node
8
8
  }.each do |file|
9
9
  require File.join(File.dirname(__FILE__),"nodes",file)
10
10
  end
@@ -11,29 +11,10 @@ module BabelBridge
11
11
  # not subclassed
12
12
  class EmptyNode < Node
13
13
  def inspect(options={})
14
- "EmptyNode" unless options[:simple]
14
+ "EmptyNode" if options[:verbose]
15
15
  end
16
16
 
17
17
  def matches; [self]; end
18
-
19
18
  end
20
19
 
21
- class RollbackWhitespaceNode < Node
22
- def inspect(options={})
23
- "RollbackWhitespace" unless options[:simple]
24
- end
25
-
26
- def matches; [self]; end
27
-
28
- def initialize(parent)
29
- super
30
- self.match_length = 0
31
- self.offset = parent.postwhitespace_range.first
32
- end
33
-
34
- def postwhitespace_range
35
- @postwhitespace_range ||= offset_after_match .. offset_after_match-1
36
- end
37
-
38
- end
39
20
  end
@@ -7,15 +7,10 @@ end
7
7
 
8
8
  # base class for all parse-tree nodes
9
9
  class Node
10
- attr_accessor :src,:offset,:match_length,:parent,:parser,:prewhitespace_range
10
+ attr_accessor :src,:offset,:match_length,:parent,:parser,:delimiter,:many_delimiter
11
11
 
12
- # no_postwhitespace is used when parsing to temporarilly rollback the preceeding whitespace while
13
- # attempting to match an ignore_whitespace pattern.
14
- # It should always be false again once parsing completes or fails.
15
- attr_accessor :no_postwhitespace
16
-
17
- def whitespace_regexp
18
- parser.whitespace_regexp
12
+ def relative_class_name
13
+ (self.class.to_s.split(parser.class.to_s+"::",2)[1]||self.class.to_s).strip
19
14
  end
20
15
 
21
16
  # the index of the first character after the match
@@ -23,29 +18,30 @@ class Node
23
18
  offset + match_length
24
19
  end
25
20
 
26
- def match_range
27
- offset..(offset+match_length-1)
21
+ def remaining_src(sub_offset)
22
+ src[self.next+sub_offset..-1]
28
23
  end
29
24
 
30
- def postwhitespace_range_without_no_postwhitespace
31
- parser.white_space_range offset_after_match
25
+ def match_range
26
+ offset..(offset+match_length-1)
32
27
  end
33
28
 
34
- def postwhitespace_range
35
- r = postwhitespace_range_without_no_postwhitespace
36
- no_postwhitespace ? r.first..r.first-1 : r
29
+ # called when a ruled is matched
30
+ def on_matched
37
31
  end
38
32
 
39
- def postwhitespace
40
- src[postwhitespace_range]
33
+ def init_line_column
34
+ @line, @column = Tools.line_column(src, offset)
41
35
  end
42
36
 
43
- def prewhitespace
44
- src[prewhitespace_range]
37
+ def line
38
+ init_line_column unless @line
39
+ @line
45
40
  end
46
41
 
47
- # called when a ruled is matched
48
- def matched
42
+ def column
43
+ init_line_column unless @column
44
+ @column
49
45
  end
50
46
 
51
47
  def to_s
@@ -67,7 +63,6 @@ class Node
67
63
  self.parent=parent_or_parser
68
64
  self.parser=parent.parser
69
65
  self.offset=parent.next
70
- self.prewhitespace_range=parent.postwhitespace_range
71
66
  self.src=parent.src
72
67
  raise "parent node does not have parser set" unless parser
73
68
  else
@@ -79,12 +74,6 @@ class Node
79
74
  node_init(parent)
80
75
  end
81
76
 
82
- # after a node has been matched, the node will get this called on itself
83
- # It can then rewrite itself however it wishes
84
- def post_match
85
- self
86
- end
87
-
88
77
  # Returns a human-readable representation of the parse tree
89
78
  # options
90
79
  # :simple => output a simplified representation of the parse tree
@@ -95,7 +84,7 @@ class Node
95
84
  #********************
96
85
  # info methods
97
86
  #********************
98
- def next; postwhitespace_range.last+1 end # index of first character after match and any trailing whitespace
87
+ alias :next :offset_after_match
99
88
  def text; src[match_range] end # the substring in src matched
100
89
 
101
90
  # length returns the number of sub-nodes
@@ -107,11 +96,23 @@ class Node
107
96
  return parent ? parent.parent_list+[parent] : []
108
97
  end
109
98
 
99
+ # walk down the children chain as long as there is only one child at each level
100
+ # log and return the path
101
+ def onlychildren_list
102
+ if matches.length == 1
103
+ [self] + matches[0].onlychildren_list
104
+ else
105
+ [self]
106
+ end
107
+ end
108
+
109
+ def path_string(node_list)
110
+ node_list.collect{|n|n.class}.join ' > '
111
+ end
112
+
110
113
  def node_path
111
- "#{parent && (parent.node_path+' > ')}#{self.class}(#{offset})"
114
+ path_string parent_list
112
115
  end
113
116
  end
114
117
 
115
- class RootNode < Node
116
- end
117
118
  end
@@ -8,18 +8,9 @@ module BabelBridge
8
8
  # rule node
9
9
  # subclassed automatically by parser.rule for each unique non-terminal
10
10
  class NonTerminalNode < Node
11
- attr_accessor :last_non_empty_node
12
-
13
- def postwhitespace_range_without_no_postwhitespace
14
- if last_non_empty_node
15
- last_non_empty_node.postwhitespace_range
16
- else
17
- prewhitespace_range || (0..-1)
18
- end
19
- end
20
11
 
21
12
  def update_match_length
22
- @match_length = last_non_empty_node ? last_non_empty_node.offset_after_match - offset : 0
13
+ @match_length = last_match ? last_match.offset_after_match - offset : 0
23
14
  end
24
15
 
25
16
  #*****************************
@@ -29,15 +20,21 @@ class NonTerminalNode < Node
29
20
  @matches ||= []
30
21
  end
31
22
 
23
+ def last_match
24
+ matches[-1]
25
+ end
26
+
32
27
  include Enumerable
33
28
  def length
34
29
  matches.length
35
30
  end
36
31
 
37
32
  def add_match(node)
38
- @last_non_empty_node = node unless node.kind_of?(EmptyNode)
39
- matches<<node
40
- update_match_length
33
+ return if !node || node.kind_of?(EmptyNode) || node == self
34
+ node.tap do
35
+ matches << node
36
+ update_match_length
37
+ end
41
38
  end
42
39
 
43
40
  def [](i)
@@ -0,0 +1,4 @@
1
+ module BabelBridge
2
+ class RootNode < Node
3
+ end
4
+ end
@@ -9,40 +9,50 @@ module BabelBridge
9
9
  # subclassed automatically by parser.rule for each unique non-terminal
10
10
  class RuleNode < NonTerminalNode
11
11
 
12
+ def initialize(parent, delimiter_pattern = nil)
13
+ @num_match_attempts = 0
14
+ @delimiter_pattern = delimiter_pattern
15
+ super parent
16
+ end
17
+
12
18
  def match_names
13
19
  @match_names ||= []
14
20
  end
15
21
 
16
- def matches_by_name
17
- @matches_by_name||= begin
18
- raise "matches.length #{matches.length} != match_names.length #{match_names.length}" unless matches.length==match_names.length
19
- mbn={}
20
- mn=match_names
21
- matches.each_with_index do |match,i|
22
- name=mn[i]
23
- next unless name
24
- if current=mbn[name] # name already used
25
- # convert to MultiMatchesArray if not already
26
- mbn[name]=MultiMatchesArray.new([current]) if !current.kind_of? MultiMatchesArray
27
- # add to array
28
- mbn[name]<<match
29
- else
30
- mbn[name]=match
31
- end
32
- end
33
- mbn
22
+ def match_name_is_poly(name)
23
+ return unless name
24
+ if current = matches_by_name[name]
25
+ matches_by_name[name] = MultiMatchesArray.new([current]) if !current.kind_of? MultiMatchesArray
26
+ else
27
+ matches_by_name[name] = MultiMatchesArray.new
34
28
  end
35
29
  end
36
30
 
31
+ def add_match_name(match,name)
32
+ return unless name
33
+ if current = matches_by_name[name]
34
+ matches_by_name[name] = MultiMatchesArray.new([current]) if !current.kind_of? MultiMatchesArray
35
+ matches_by_name[name] << match
36
+ else
37
+ matches_by_name[name] = match
38
+ end
39
+ end
40
+
41
+ def matches_by_name
42
+ @matches_by_name||={}
43
+ end
44
+
37
45
  def inspect(options={})
38
- return "#{self.class}" if matches.length==0
39
- matches_inspected=matches.collect{|a|a.inspect(options)}.compact
46
+ return relative_class_name if matches.length==0
47
+ matches = @matches
48
+ matches=matches.select{|m|!m.many_delimiter} unless options[:verbose]
49
+ matches_inspected = matches.collect{|a|a.inspect(options)}.compact
40
50
  if matches_inspected.length==0 then nil
41
51
  elsif matches_inspected.length==1
42
- m=matches_inspected[0]
43
- ret="#{self.class} > "+matches_inspected[0]
52
+ m = matches_inspected[0]
53
+ ret = "#{relative_class_name} > "+matches_inspected[0]
44
54
  if options[:simple]
45
- ret=if m["\n"] then m
55
+ ret = if m["\n"] then m
46
56
  else
47
57
  # just show the first and last nodes in the chain
48
58
  ret.gsub(/( > [A-Z][a-zA-Z0-9:]+ > (\.\.\. > )?)/," > ... > ")
@@ -50,18 +60,17 @@ class RuleNode < NonTerminalNode
50
60
  end
51
61
  ret
52
62
  else
53
- (["#{self.class}"]+matches_inspected).join("\n").gsub("\n","\n ")
63
+ (["#{relative_class_name}"]+matches_inspected).join("\n").gsub("\n","\n ")
54
64
  end
55
65
  end
56
66
 
57
67
  #********************
58
68
  # alter methods
59
69
  #********************
60
- def reset_matches_by_name
61
- @matches_by_name=nil
62
- end
63
70
 
64
- # defines where to forward missing methods to; override for custom behavior
71
+ # returns where to forward missing methods calls to (safe to override for custom behavior; respond_to? and method_missing will "do the right thing")
72
+ # returns nil if there is no object to forward to that will respond to the call
73
+ # default: forward to the first match that responds to method_name
65
74
  def forward_to(method_name)
66
75
  matches.each {|m| return m if m.respond_to?(method_name)}
67
76
  nil
@@ -74,29 +83,73 @@ class RuleNode < NonTerminalNode
74
83
  end
75
84
 
76
85
  def method_missing(method_name, *args) #method_name is a symbol
77
- unless matches_by_name.has_key? method_name
78
- if f=forward_to(method_name)
79
- return f.send(method_name,*args)
80
- end
81
- match_path = [self]
82
- while match_path[-1].matches.length==1
83
- match_path<<match_path[-1].matches[0]
84
- end
85
- raise "#{match_path.collect{|m|m.class}.join(' > ')}: no methods or named pattern elements match: #{method_name.inspect}"
86
- end
87
- case ret=matches_by_name[method_name]
88
- when EmptyNode then nil
89
- else ret
86
+ return matches_by_name[method_name] if matches_by_name.has_key?(method_name)
87
+
88
+ if f = forward_to(method_name)
89
+ return f.send(method_name,*args)
90
90
  end
91
+
92
+ raise "#{path_string onlychildren_list}: no methods or named pattern elements match: #{method_name.inspect} on #{self.class} instance"
91
93
  end
92
94
 
93
95
  # adds a match with name (optional)
94
96
  def add_match(match,name=nil)
95
- reset_matches_by_name
96
- super match
97
- match_names<<name
97
+ return unless match
98
+ return match if match==self
99
+
100
+ add_match_name(super(match),name)
98
101
 
99
102
  update_match_length
103
+ match
104
+ end
105
+
106
+ def pop_match
107
+ matches.pop.tap {update_match_length}
108
+ end
109
+
110
+ # Attempts to match the pattern_element starting at the end of what has already been matched
111
+ # If successful, adds the resulting Node to matches.
112
+ # returns nil on if pattern_element wasn't matched; non-nil if it was skipped or matched
113
+ def match(pattern_element)
114
+ @num_match_attempts += 1
115
+ return :no_pattern_element unless pattern_element
116
+ return :skipped if pattern_element.delimiter &&
117
+ (
118
+ if last_match
119
+ last_match.delimiter # don't match two delimiters in a row
120
+ else
121
+ @num_match_attempts > 1 # don't match a delimiter as the first element unless this is the first match attempt
122
+ end
123
+ )
124
+
125
+ if result = pattern_element.parse(self)
126
+ add_match result, pattern_element.name # success, but don't keep EmptyNodes
127
+ end
100
128
  end
129
+
130
+ def match_delimiter
131
+ match @delimiter_pattern
132
+ end
133
+
134
+ # called after matching is done and it was a success
135
+ # returns the node which is actually added to the parse tree
136
+ def post_match_processing
137
+ on_matched
138
+ self
139
+ end
140
+
141
+ # a simple "transaction" - logs the curent number of matches,
142
+ # if the block's result is false, it discards all new matches
143
+ def attempt_match
144
+ matches_before = matches.length
145
+ match_length_before = match_length
146
+ (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)
147
+ unless success
148
+ @matches = matches[0..matches_before-1]
149
+ update_match_length
150
+ end
151
+ end
152
+ end
153
+
101
154
  end
102
155
  end
@@ -8,7 +8,7 @@ module BabelBridge
8
8
  # used for String and Regexp PatternElements
9
9
  # not subclassed
10
10
  class TerminalNode < Node
11
- attr_accessor :pattern, :postwhitespace_offset
11
+ attr_accessor :pattern
12
12
  def initialize(parent,range,pattern)
13
13
  node_init(parent)
14
14
  self.offset = range.min
@@ -17,10 +17,10 @@ class TerminalNode < Node
17
17
  end
18
18
 
19
19
  def inspect(options={})
20
- "#{text.inspect}" unless options[:simple] && text[/^\s*$/] # if simple && node only matched white-space, return nil
20
+ "#{text.inspect}" unless !options[:verbose] && text[/^\s*$/] # if only show whitespace matches if verbose
21
21
  end
22
22
 
23
- def matches; [self]; end
23
+ def matches; []; end
24
24
 
25
25
  end
26
26
  end
@@ -8,7 +8,7 @@ class Parser
8
8
  # These methods are used in the creation of a Parser Sub-Class to define
9
9
  # its grammar
10
10
  class <<self
11
- attr_accessor :rules, :module_name, :root_rule, :whitespace_regexp
11
+ attr_accessor :rules, :module_name, :root_rule, :delimiter_pattern
12
12
 
13
13
  def rules
14
14
  @rules||={}
@@ -37,19 +37,20 @@ class Parser
37
37
  #
38
38
  # The block is executed in the context of the rule-varient's node type, a subclass of: RuleNode
39
39
  # This allows you to add whatever functionality you want to a your nodes in the final parse tree.
40
- # Also note you can override the post_match method. This allows you to restructure the parse tree as it is parsed.
40
+ # Also note you can override the on_post_match method. This allows you to restructure the parse tree as it is parsed.
41
41
  def rule(name,*pattern,&block)
42
42
  rule = self.rules[name] ||= Rule.new(name,self)
43
- self.root_rule ||= name
44
- rule.add_variant(pattern,&block)
43
+ @root_rule ||= name
44
+ options = pattern[-1].kind_of?(Hash) ? pattern.pop : {}
45
+ rule.add_variant options.merge(:pattern => pattern), &block
45
46
  end
46
47
 
47
48
  # options
48
49
  # => right_operators: list of all operators that should be evaluated right to left instead of left-to-write
49
50
  # typical example is the "**" exponentiation operator which should be evaluated right-to-left.
50
- def binary_operators_rule(name,elements_pattern,operators,options={},&block)
51
+ def binary_operators_rule(name,operand_rule_name,operators,options={},&block)
51
52
  right_operators = options[:right_operators]
52
- rule(name,many(elements_pattern,Tools::array_to_or_regexp(operators))) do
53
+ rule name, many(operand_rule_name,Tools::array_to_or_regexp(operators)).delimiter_name(:operators).as(:operands) do
53
54
  self.class_eval &block if block
54
55
  class <<self
55
56
  attr_accessor :operators_from_rule, :right_operators
@@ -64,16 +65,17 @@ class Parser
64
65
  @operator||=operator_node.to_s.to_sym
65
66
  end
66
67
 
67
- # Override the post_match method to take the results of the "many" match
68
+ def operator_processor
69
+ self.class.operator_processor
70
+ end
71
+
72
+ # Override the on_post_match method to take the results of the "many" match
68
73
  # and restructure it into a binary tree of nodes based on the precidence of
69
74
  # the "operators".
70
- # TODO - I think maybe post_match should be run after the whole tree matches. If not, will this screw up caching?
71
- def post_match
72
- many_match = matches[0]
73
- operands = many_match.matches
74
- operators = many_match.delimiter_matches
75
- # TODO - now! take many_match.matches and many_match.delimiter_matches, mishy-mashy, and make the super-tree!
76
- self.class.operator_processor.generate_tree operands, operators, parent
75
+ # TODO - Should on_post_match be run after the whole tree matches? If not, will this screw up caching?
76
+ def post_match_processing
77
+ super
78
+ operator_processor.generate_tree operands, operators, parent
77
79
  end
78
80
  end
79
81
  end
@@ -95,13 +97,25 @@ class Parser
95
97
  @root_rule=rule
96
98
  end
97
99
 
98
- def ignore_whitespace(regexp = /\s*/)
99
- @whitespace_regexp = /\A(#{regexp})?/
100
+ def ignore_whitespace
101
+ delimiter /\s*/
102
+ end
103
+
104
+ def delimiter(*pattern)
105
+ @delimiter = pattern
106
+ end
107
+
108
+ def delimiter_pattern
109
+ @delimiter_pattern ||= @delimiter && PatternElement.new(@delimiter, :parser_class => self, :delimiter => true)
100
110
  end
101
111
  end
102
112
 
103
- def whitespace_regexp
104
- self.class.whitespace_regexp || /\A/
113
+ def delimiter_pattern
114
+ self.class.delimiter_pattern
115
+ end
116
+
117
+ def rules
118
+ self.class.rules
105
119
  end
106
120
 
107
121
  #*********************************************
@@ -137,11 +151,10 @@ class Parser
137
151
  def match(*args) PatternElementHash.new.match(*args) end
138
152
  def match!(*args) PatternElementHash.new.dont.match(*args) end
139
153
 
140
- def rewind_whitespace; PatternElementHash.new.rewind_whitespace end
141
-
142
154
  def dont; PatternElementHash.new.dont end
143
155
  def optionally; PatternElementHash.new.optionally end
144
156
  def could; PatternElementHash.new.could end
157
+ def custom_parser(&block); PatternElementHash.new.parser(lambda &block) end
145
158
  end
146
159
 
147
160
 
@@ -162,24 +175,12 @@ class Parser
162
175
  end
163
176
 
164
177
  def reset_parser_tracking
178
+ @matching_negative_depth = 0
165
179
  @parsing_did_not_match_entire_input = false
166
180
  @src = nil
167
181
  @failure_index = 0
168
182
  @expecting_list = {}
169
183
  @parse_cache = {}
170
- @white_space_ranges = {}
171
- end
172
-
173
- # memoizing whitespace parser
174
- def white_space_range(start)
175
- @white_space_ranges[start]||=begin
176
- # src should always be a string - unless this is called AFTER parsing is done. Currently this can happen with the way ManyNode handles .match_length and .next
177
- # We should be able to just use:
178
- # src[start..-1].index whitespace_regexp
179
- ((src||"")[start..-1]||"").index whitespace_regexp
180
- r = $~.offset 0
181
- start+r[0] .. start+r[1]-1
182
- end
183
184
  end
184
185
 
185
186
  def cached(rule_class,offset)
@@ -195,7 +196,9 @@ class Parser
195
196
  end
196
197
 
197
198
  def log_parsing_failure(index,expecting)
198
- if index>failure_index
199
+ if matching_negative?
200
+ # ignored
201
+ elsif index>failure_index
199
202
  @expecting_list = {expecting[:pattern] => expecting}
200
203
  @failure_index = index
201
204
  elsif index == failure_index
@@ -205,25 +208,45 @@ class Parser
205
208
  end
206
209
  end
207
210
 
208
- def parse(src,offset=0,rule=nil)
211
+ def matching_negative
212
+ @matching_negative_depth||=0
213
+ @matching_negative_depth+=1
214
+ end
215
+
216
+ def unmatching_negative
217
+ @matching_negative_depth-=1
218
+ end
219
+
220
+ def matching_negative?
221
+ (@matching_negative_depth||0) > 0
222
+ end
223
+
224
+ # parse a string, return the root node of the parse tree.
225
+ # If nil is returned, parsing failed. Call .parser_failure_info after failure for a human-readable description of the failure.
226
+ # src: the string to parse
227
+ # options:
228
+ # offset: where to start in the string for parsing
229
+ # rule: lets you specify the root rule for matching
230
+ # partial_match: allow partial matching
231
+ def parse(src, options={})
232
+ offset = options[:offset] || 0
233
+ rule = options[:rule] || self.class.root_rule
209
234
  reset_parser_tracking
210
- @start_time=Time.now
211
- self.src=src
212
- root_node=RootNode.new(self)
213
- raise "No root rule defined." unless rule || self.class.root_rule
214
- ret=self.class[rule||self.class.root_rule].parse(root_node)
215
- unless rule
216
- if ret
217
- if ret.next<src.length # parse only succeeds if the whole input is matched
218
- if ret.next >= @failure_index
219
- @parsing_did_not_match_entire_input=true
220
- @failure_index = ret.next
221
- @failed_parse = ret
222
- end
223
- ret=nil
224
- else
225
- reset_parser_tracking
235
+ @start_time = Time.now
236
+ self.src = src
237
+ root_node = RootNode.new(self)
238
+ raise "No root rule defined." unless rule
239
+ ret = rules[rule].parse(root_node)
240
+ if ret
241
+ if ret.next<src.length && !options[:partial_match] # parse only succeeds if the whole input is matched
242
+ if ret.next >= @failure_index
243
+ @parsing_did_not_match_entire_input=true
244
+ @failure_index = ret.next
245
+ @failed_parse = ret
226
246
  end
247
+ ret=nil
248
+ else
249
+ reset_parser_tracking
227
250
  end
228
251
  end
229
252
  @end_time=Time.now
@@ -242,8 +265,18 @@ class Parser
242
265
  ret
243
266
  end
244
267
 
245
- def node_list_string(node_list,common_root=[])
246
- node_list && node_list[common_root.length..-1].map{|p|"#{p.class}(#{p.offset})"}.join(" > ")
268
+ # options[:verbose] => false
269
+ def node_list_string(node_list,common_root=[],options={})
270
+ return unless node_list
271
+ if options[:verbose]
272
+ node_list[common_root.length..-1]
273
+ else
274
+ [node_list[-1]]
275
+ end.select do |p|
276
+ p.class.to_s.index(BabelBridge.to_s)!=0
277
+ end.map do |p|
278
+ "#{p.relative_class_name}"
279
+ end.join(" > ")
247
280
  end
248
281
 
249
282
  def nodes_interesting_parse_path(node)
@@ -254,7 +287,8 @@ class Parser
254
287
  end
255
288
 
256
289
 
257
- def expecting_output
290
+ # options[:verbose] => false
291
+ def expecting_output(options={})
258
292
  return "" if expecting_list.length==0
259
293
  common_root=nil
260
294
  expecting_list.values.each do |e|
@@ -275,13 +309,13 @@ class Parser
275
309
  <<ENDTXT
276
310
 
277
311
  Parse path at failure:
278
- #{node_list_string(common_root)}
312
+ #{node_list_string(common_root,[],:verbose=>true)}
279
313
 
280
314
  Expecting#{expecting_list.length>1 ? ' one of' : ''}:
281
- #{expecting_list.values.collect do |a|
282
- list=node_list_string(nodes_interesting_parse_path(a[:node]),common_root)
283
- [list,"#{a[:pattern].inspect} (#{list})"]
284
- end.sort.map{|i|i[1]}.join("\n ")}
315
+ #{Tools.uniform_tabs(Tools.indent(expecting_list.values.collect do |a|
316
+ list=node_list_string(nodes_interesting_parse_path(a[:node]),common_root,options)
317
+ "#{a[:pattern].inspect}\t#{list}"
318
+ end.sort.join("\n")," "))}
285
319
  ENDTXT
286
320
  end
287
321
 
@@ -290,7 +324,7 @@ ENDTXT
290
324
  return unless src
291
325
  verbose = options[:verbose]
292
326
  bracketing_lines=5
293
- line,col=src.line_col(failure_index)
327
+ line,col = Tools.line_column(src, failure_index)
294
328
  ret=<<-ENDTXT
295
329
  Parsing error at line #{line} column #{col} offset #{failure_index}
296
330
 
@@ -303,7 +337,7 @@ ENDTXT
303
337
  if @parsing_did_not_match_entire_input
304
338
  ret+="\nParser did not match entire input.\n"
305
339
  if verbose
306
- ret+="\nParsed:\n#{Tools::indent failed_parse.inspect}\n"
340
+ ret+="\nParsed:\n#{Tools::indent failed_parse.inspect||"(nothing)"}\n"
307
341
  end
308
342
  end
309
343