babel_bridge 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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