kpeg 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,9 @@
1
+ require 'kpeg/grammar'
1
2
  class KPeg::FormatParser
2
- # STANDALONE START
3
+ # :stopdoc:
4
+
5
+ # Prepares for parsing +str+. If you define a custom initialize you must
6
+ # call this method before #parse
3
7
  def setup_parser(str, debug=false)
4
8
  @string = str
5
9
  @pos = 0
@@ -11,19 +15,11 @@ class KPeg::FormatParser
11
15
  setup_foreign_grammar
12
16
  end
13
17
 
14
- # This is distinct from setup_parser so that a standalone parser
15
- # can redefine #initialize and still have access to the proper
16
- # parser setup code.
17
- #
18
- def initialize(str, debug=false)
19
- setup_parser(str, debug)
20
- end
21
-
22
18
  attr_reader :string
23
19
  attr_reader :failing_rule_offset
24
20
  attr_accessor :result, :pos
25
21
 
26
- # STANDALONE START
22
+
27
23
  def current_column(target=pos)
28
24
  if c = string.rindex("\n", target-1)
29
25
  return target - c - 1
@@ -51,7 +47,7 @@ class KPeg::FormatParser
51
47
  lines
52
48
  end
53
49
 
54
- #
50
+
55
51
 
56
52
  def get_text(start)
57
53
  @string[start..@pos-1]
@@ -244,7 +240,6 @@ class KPeg::FormatParser
244
240
  def apply_with_args(rule, *args)
245
241
  memo_key = [rule, args]
246
242
  if m = @memoizations[memo_key][@pos]
247
- prev = @pos
248
243
  @pos = m.pos
249
244
  if !m.set
250
245
  m.left_rec = true
@@ -279,7 +274,6 @@ class KPeg::FormatParser
279
274
 
280
275
  def apply(rule)
281
276
  if m = @memoizations[rule][@pos]
282
- prev = @pos
283
277
  @pos = m.pos
284
278
  if !m.set
285
279
  m.left_rec = true
@@ -347,20 +341,28 @@ class KPeg::FormatParser
347
341
  RuleInfo.new(name, rendered)
348
342
  end
349
343
 
350
- #
344
+
345
+ # :startdoc:
346
+
351
347
 
352
348
 
353
- require 'kpeg/grammar'
349
+ ##
350
+ # Creates a new kpeg format parser for +str+.
354
351
 
355
352
  def initialize(str, debug=false)
356
353
  setup_parser(str, debug)
357
354
  @g = KPeg::Grammar.new
358
355
  end
359
356
 
357
+ ##
358
+ # The parsed grammar
359
+
360
360
  attr_reader :g
361
+
361
362
  alias_method :grammar, :g
362
363
 
363
364
 
365
+ # :stopdoc:
364
366
  def setup_foreign_grammar; end
365
367
 
366
368
  # eol = "\n"
@@ -509,7 +511,7 @@ class KPeg::FormatParser
509
511
  return _tmp
510
512
  end
511
513
 
512
- # var = < ("-" | /[a-zA-Z][\-_a-zA-Z0-9]*/) > { text }
514
+ # var = < ("-" | /[a-z][\w-]*/i) > { text }
513
515
  def _var
514
516
 
515
517
  _save = self.pos
@@ -521,7 +523,7 @@ class KPeg::FormatParser
521
523
  _tmp = match_string("-")
522
524
  break if _tmp
523
525
  self.pos = _save1
524
- _tmp = scan(/\A(?-mix:[a-zA-Z][\-_a-zA-Z0-9]*)/)
526
+ _tmp = scan(/\A(?i-mx:[a-z][\w-]*)/)
525
527
  break if _tmp
526
528
  self.pos = _save1
527
529
  break
@@ -546,13 +548,13 @@ class KPeg::FormatParser
546
548
  return _tmp
547
549
  end
548
550
 
549
- # method = < /[a-zA-Z_][a-zA-Z0-9_]*/ > { text }
551
+ # method = < /[a-z_]\w*/i > { text }
550
552
  def _method
551
553
 
552
554
  _save = self.pos
553
555
  while true # sequence
554
556
  _text_start = self.pos
555
- _tmp = scan(/\A(?-mix:[a-zA-Z_][a-zA-Z0-9_]*)/)
557
+ _tmp = scan(/\A(?i-mx:[a-z_]\w*)/)
556
558
  if _tmp
557
559
  text = get_text(_text_start)
558
560
  end
@@ -807,7 +809,7 @@ class KPeg::FormatParser
807
809
  return _tmp
808
810
  end
809
811
 
810
- # num_escapes = (< /[0-7]{1,3}/ > { [text.to_i(8)].pack("U") } | "x" < /[0-9a-fA-F]{2}/ > { [text.to_i(16)].pack("U") })
812
+ # num_escapes = (< /[0-7]{1,3}/ > { [text.to_i(8)].pack("U") } | "x" < /[a-f\d]{2}/i > { [text.to_i(16)].pack("U") })
811
813
  def _num_escapes
812
814
 
813
815
  _save = self.pos
@@ -843,7 +845,7 @@ class KPeg::FormatParser
843
845
  break
844
846
  end
845
847
  _text_start = self.pos
846
- _tmp = scan(/\A(?-mix:[0-9a-fA-F]{2})/)
848
+ _tmp = scan(/\A(?i-mx:[a-f\d]{2})/)
847
849
  if _tmp
848
850
  text = get_text(_text_start)
849
851
  end
@@ -1258,13 +1260,13 @@ class KPeg::FormatParser
1258
1260
  return _tmp
1259
1261
  end
1260
1262
 
1261
- # char = < /[a-zA-Z0-9]/ > { text }
1263
+ # char = < /[a-z\d]/i > { text }
1262
1264
  def _char
1263
1265
 
1264
1266
  _save = self.pos
1265
1267
  while true # sequence
1266
1268
  _text_start = self.pos
1267
- _tmp = scan(/\A(?-mix:[a-zA-Z0-9])/)
1269
+ _tmp = scan(/\A(?i-mx:[a-z\d])/)
1268
1270
  if _tmp
1269
1271
  text = get_text(_text_start)
1270
1272
  end
@@ -1328,13 +1330,13 @@ class KPeg::FormatParser
1328
1330
  return _tmp
1329
1331
  end
1330
1332
 
1331
- # range_num = < /[1-9][0-9]*/ > { text }
1333
+ # range_num = < /[1-9]\d*/ > { text }
1332
1334
  def _range_num
1333
1335
 
1334
1336
  _save = self.pos
1335
1337
  while true # sequence
1336
1338
  _text_start = self.pos
1337
- _tmp = scan(/\A(?-mix:[1-9][0-9]*)/)
1339
+ _tmp = scan(/\A(?-mix:[1-9]\d*)/)
1338
1340
  if _tmp
1339
1341
  text = get_text(_text_start)
1340
1342
  end
@@ -2468,7 +2470,7 @@ class KPeg::FormatParser
2468
2470
  return _tmp
2469
2471
  end
2470
2472
 
2471
- # statement = (- var:v "(" args:a ")" - "=" - expression:o { @g.set(v, o, a) } | - var:v - "=" - expression:o { @g.set(v, o) } | - "%" var:name - "=" - < /[::A-Za-z0-9_]+/ > { @g.add_foreign_grammar(name, text) } | - "%%" - curly:act { @g.add_setup act } | - "%%" - var:name - curly:act { @g.add_directive name, act } | - "%%" - var:name - "=" - < (!"\n" .)+ > { @g.set_variable(name, text) })
2473
+ # statement = (- var:v "(" args:a ")" - "=" - expression:o { @g.set(v, o, a) } | - var:v - "=" - expression:o { @g.set(v, o) } | - "%" var:name - "=" - < /[:\w]+/ > { @g.add_foreign_grammar(name, text) } | - "%%" - curly:act { @g.add_setup act } | - "%%" - var:name - curly:act { @g.add_directive name, act } | - "%%" - var:name - "=" - < (!"\n" .)+ > { @g.set_variable(name, text) })
2472
2474
  def _statement
2473
2475
 
2474
2476
  _save = self.pos
@@ -2614,7 +2616,7 @@ class KPeg::FormatParser
2614
2616
  break
2615
2617
  end
2616
2618
  _text_start = self.pos
2617
- _tmp = scan(/\A(?-mix:[::A-Za-z0-9_]+)/)
2619
+ _tmp = scan(/\A(?-mix:[:\w]+)/)
2618
2620
  if _tmp
2619
2621
  text = get_text(_text_start)
2620
2622
  end
@@ -2906,13 +2908,13 @@ class KPeg::FormatParser
2906
2908
  return _tmp
2907
2909
  end
2908
2910
 
2909
- # ast_constant = < /[A-Z][A-Za-z0-9_]*/ > { text }
2911
+ # ast_constant = < /[A-Z]\w*/ > { text }
2910
2912
  def _ast_constant
2911
2913
 
2912
2914
  _save = self.pos
2913
2915
  while true # sequence
2914
2916
  _text_start = self.pos
2915
- _tmp = scan(/\A(?-mix:[A-Z][A-Za-z0-9_]*)/)
2917
+ _tmp = scan(/\A(?-mix:[A-Z]\w*)/)
2916
2918
  if _tmp
2917
2919
  text = get_text(_text_start)
2918
2920
  end
@@ -2932,13 +2934,13 @@ class KPeg::FormatParser
2932
2934
  return _tmp
2933
2935
  end
2934
2936
 
2935
- # ast_word = < /[A-Za-z_][A-Za-z0-9_]*/ > { text }
2937
+ # ast_word = < /[a-z_]\w*/i > { text }
2936
2938
  def _ast_word
2937
2939
 
2938
2940
  _save = self.pos
2939
2941
  while true # sequence
2940
2942
  _text_start = self.pos
2941
- _tmp = scan(/\A(?-mix:[A-Za-z_][A-Za-z0-9_]*)/)
2943
+ _tmp = scan(/\A(?i-mx:[a-z_]\w*)/)
2942
2944
  if _tmp
2943
2945
  text = get_text(_text_start)
2944
2946
  end
@@ -3134,10 +3136,10 @@ class KPeg::FormatParser
3134
3136
  Rules[:_space] = rule_info("space", "(\" \" | \"\\t\" | eol)")
3135
3137
  Rules[:__hyphen_] = rule_info("-", "(space | comment)*")
3136
3138
  Rules[:_kleene] = rule_info("kleene", "\"*\"")
3137
- Rules[:_var] = rule_info("var", "< (\"-\" | /[a-zA-Z][\\-_a-zA-Z0-9]*/) > { text }")
3138
- Rules[:_method] = rule_info("method", "< /[a-zA-Z_][a-zA-Z0-9_]*/ > { text }")
3139
+ Rules[:_var] = rule_info("var", "< (\"-\" | /[a-z][\\w-]*/i) > { text }")
3140
+ Rules[:_method] = rule_info("method", "< /[a-z_]\\w*/i > { text }")
3139
3141
  Rules[:_dbl_escapes] = rule_info("dbl_escapes", "(\"n\" { \"\\n\" } | \"s\" { \" \" } | \"r\" { \"\\r\" } | \"t\" { \"\\t\" } | \"v\" { \"\\v\" } | \"f\" { \"\\f\" } | \"b\" { \"\\b\" } | \"a\" { \"\\a\" } | \"e\" { \"\\e\" } | \"\\\\\" { \"\\\\\" } | \"\\\"\" { \"\\\"\" } | num_escapes | < . > { text })")
3140
- Rules[:_num_escapes] = rule_info("num_escapes", "(< /[0-7]{1,3}/ > { [text.to_i(8)].pack(\"U\") } | \"x\" < /[0-9a-fA-F]{2}/ > { [text.to_i(16)].pack(\"U\") })")
3142
+ Rules[:_num_escapes] = rule_info("num_escapes", "(< /[0-7]{1,3}/ > { [text.to_i(8)].pack(\"U\") } | \"x\" < /[a-f\\d]{2}/i > { [text.to_i(16)].pack(\"U\") })")
3141
3143
  Rules[:_dbl_seq] = rule_info("dbl_seq", "< /[^\\\\\"]+/ > { text }")
3142
3144
  Rules[:_dbl_not_quote] = rule_info("dbl_not_quote", "(\"\\\\\" dbl_escapes:s | dbl_seq:s)*:ary { Array(ary) }")
3143
3145
  Rules[:_dbl_string] = rule_info("dbl_string", "\"\\\"\" dbl_not_quote:s \"\\\"\" { @g.str(s.join) }")
@@ -3149,9 +3151,9 @@ class KPeg::FormatParser
3149
3151
  Rules[:_not_slash] = rule_info("not_slash", "< (\"\\\\/\" | /[^\\/]/)+ > { text }")
3150
3152
  Rules[:_regexp_opts] = rule_info("regexp_opts", "< [a-z]* > { text }")
3151
3153
  Rules[:_regexp] = rule_info("regexp", "\"/\" not_slash:body \"/\" regexp_opts:opts { @g.reg body, opts }")
3152
- Rules[:_char] = rule_info("char", "< /[a-zA-Z0-9]/ > { text }")
3154
+ Rules[:_char] = rule_info("char", "< /[a-z\\d]/i > { text }")
3153
3155
  Rules[:_char_range] = rule_info("char_range", "\"[\" char:l \"-\" char:r \"]\" { @g.range(l,r) }")
3154
- Rules[:_range_num] = rule_info("range_num", "< /[1-9][0-9]*/ > { text }")
3156
+ Rules[:_range_num] = rule_info("range_num", "< /[1-9]\\d*/ > { text }")
3155
3157
  Rules[:_range_elem] = rule_info("range_elem", "< (range_num | kleene) > { text }")
3156
3158
  Rules[:_mult_range] = rule_info("mult_range", "(\"[\" - range_elem:l - \",\" - range_elem:r - \"]\" { [l == \"*\" ? nil : l.to_i, r == \"*\" ? nil : r.to_i] } | \"[\" - range_num:e - \"]\" { [e.to_i, e.to_i] })")
3157
3159
  Rules[:_curly_block] = rule_info("curly_block", "curly")
@@ -3163,13 +3165,14 @@ class KPeg::FormatParser
3163
3165
  Rules[:_choose_cont] = rule_info("choose_cont", "- \"|\" - values:v { v }")
3164
3166
  Rules[:_expression] = rule_info("expression", "(values:v choose_cont+:alts { @g.any(v, *alts) } | values)")
3165
3167
  Rules[:_args] = rule_info("args", "(args:a \",\" - var:n - { a + [n] } | - var:n - { [n] })")
3166
- Rules[:_statement] = rule_info("statement", "(- var:v \"(\" args:a \")\" - \"=\" - expression:o { @g.set(v, o, a) } | - var:v - \"=\" - expression:o { @g.set(v, o) } | - \"%\" var:name - \"=\" - < /[::A-Za-z0-9_]+/ > { @g.add_foreign_grammar(name, text) } | - \"%%\" - curly:act { @g.add_setup act } | - \"%%\" - var:name - curly:act { @g.add_directive name, act } | - \"%%\" - var:name - \"=\" - < (!\"\\n\" .)+ > { @g.set_variable(name, text) })")
3168
+ Rules[:_statement] = rule_info("statement", "(- var:v \"(\" args:a \")\" - \"=\" - expression:o { @g.set(v, o, a) } | - var:v - \"=\" - expression:o { @g.set(v, o) } | - \"%\" var:name - \"=\" - < /[:\\w]+/ > { @g.add_foreign_grammar(name, text) } | - \"%%\" - curly:act { @g.add_setup act } | - \"%%\" - var:name - curly:act { @g.add_directive name, act } | - \"%%\" - var:name - \"=\" - < (!\"\\n\" .)+ > { @g.set_variable(name, text) })")
3167
3169
  Rules[:_statements] = rule_info("statements", "statement (- statements)?")
3168
3170
  Rules[:_eof] = rule_info("eof", "!.")
3169
3171
  Rules[:_root] = rule_info("root", "statements - eof_comment? eof")
3170
- Rules[:_ast_constant] = rule_info("ast_constant", "< /[A-Z][A-Za-z0-9_]*/ > { text }")
3171
- Rules[:_ast_word] = rule_info("ast_word", "< /[A-Za-z_][A-Za-z0-9_]*/ > { text }")
3172
+ Rules[:_ast_constant] = rule_info("ast_constant", "< /[A-Z]\\w*/ > { text }")
3173
+ Rules[:_ast_word] = rule_info("ast_word", "< /[a-z_]\\w*/i > { text }")
3172
3174
  Rules[:_ast_sp] = rule_info("ast_sp", "(\" \" | \"\\t\")*")
3173
3175
  Rules[:_ast_words] = rule_info("ast_words", "(ast_words:r ast_sp \",\" ast_sp ast_word:w { r + [w] } | ast_word:w { [w] })")
3174
3176
  Rules[:_ast_root] = rule_info("ast_root", "(ast_constant:c \"(\" ast_words:w \")\" { [c, w] } | ast_constant:c \"()\"? { [c, []] })")
3177
+ # :startdoc:
3175
3178
  end
@@ -786,7 +786,7 @@ module KPeg
786
786
  end
787
787
 
788
788
  def maybe(node, &b)
789
- op = multiple Grammar.resolve(node), 0, 1, &b
789
+ multiple Grammar.resolve(node), 0, 1, &b
790
790
  end
791
791
 
792
792
  def many(node, &b)
@@ -10,6 +10,20 @@ module KPeg
10
10
  widest = @grammar.rules.keys.sort { |a,b| a.size <=> b.size }.last
11
11
  indent = widest.size
12
12
 
13
+ @grammar.variables.sort.each do |name, value|
14
+ io.print "%% #{name} = #{value}\n"
15
+ end
16
+
17
+ unless @grammar.variables.empty?
18
+ io.print "\n"
19
+ end
20
+
21
+ @grammar.directives.sort_by { |name,| name }.each do |name, act|
22
+ io.print "%% #{name} {"
23
+ io.print act.action
24
+ io.print "}\n\n"
25
+ end
26
+
13
27
  @grammar.setup_actions.each do |act|
14
28
  io.print "%% {"
15
29
  io.print act.action
@@ -1,6 +1,7 @@
1
1
  module KPeg
2
2
  module Position
3
3
  # STANDALONE START
4
+
4
5
  def current_column(target=pos)
5
6
  if c = string.rindex("\n", target-1)
6
7
  return target - c - 1
@@ -1,11 +1,363 @@
1
- require 'kpeg/compiled_parser'
1
+ class KPeg::StringEscape
2
+ # :stopdoc:
3
+
4
+ # This is distinct from setup_parser so that a standalone parser
5
+ # can redefine #initialize and still have access to the proper
6
+ # parser setup code.
7
+ def initialize(str, debug=false)
8
+ setup_parser(str, debug)
9
+ end
10
+
11
+
12
+
13
+ # Prepares for parsing +str+. If you define a custom initialize you must
14
+ # call this method before #parse
15
+ def setup_parser(str, debug=false)
16
+ @string = str
17
+ @pos = 0
18
+ @memoizations = Hash.new { |h,k| h[k] = {} }
19
+ @result = nil
20
+ @failed_rule = nil
21
+ @failing_rule_offset = -1
22
+
23
+ setup_foreign_grammar
24
+ end
25
+
26
+ attr_reader :string
27
+ attr_reader :failing_rule_offset
28
+ attr_accessor :result, :pos
29
+
30
+
31
+ def current_column(target=pos)
32
+ if c = string.rindex("\n", target-1)
33
+ return target - c - 1
34
+ end
35
+
36
+ target + 1
37
+ end
38
+
39
+ def current_line(target=pos)
40
+ cur_offset = 0
41
+ cur_line = 0
42
+
43
+ string.each_line do |line|
44
+ cur_line += 1
45
+ cur_offset += line.size
46
+ return cur_line if cur_offset >= target
47
+ end
48
+
49
+ -1
50
+ end
51
+
52
+ def lines
53
+ lines = []
54
+ string.each_line { |l| lines << l }
55
+ lines
56
+ end
57
+
58
+
59
+
60
+ def get_text(start)
61
+ @string[start..@pos-1]
62
+ end
63
+
64
+ def show_pos
65
+ width = 10
66
+ if @pos < width
67
+ "#{@pos} (\"#{@string[0,@pos]}\" @ \"#{@string[@pos,width]}\")"
68
+ else
69
+ "#{@pos} (\"... #{@string[@pos - width, width]}\" @ \"#{@string[@pos,width]}\")"
70
+ end
71
+ end
72
+
73
+ def failure_info
74
+ l = current_line @failing_rule_offset
75
+ c = current_column @failing_rule_offset
76
+
77
+ if @failed_rule.kind_of? Symbol
78
+ info = self.class::Rules[@failed_rule]
79
+ "line #{l}, column #{c}: failed rule '#{info.name}' = '#{info.rendered}'"
80
+ else
81
+ "line #{l}, column #{c}: failed rule '#{@failed_rule}'"
82
+ end
83
+ end
84
+
85
+ def failure_caret
86
+ l = current_line @failing_rule_offset
87
+ c = current_column @failing_rule_offset
88
+
89
+ line = lines[l-1]
90
+ "#{line}\n#{' ' * (c - 1)}^"
91
+ end
92
+
93
+ def failure_character
94
+ l = current_line @failing_rule_offset
95
+ c = current_column @failing_rule_offset
96
+ lines[l-1][c-1, 1]
97
+ end
98
+
99
+ def failure_oneline
100
+ l = current_line @failing_rule_offset
101
+ c = current_column @failing_rule_offset
102
+
103
+ char = lines[l-1][c-1, 1]
104
+
105
+ if @failed_rule.kind_of? Symbol
106
+ info = self.class::Rules[@failed_rule]
107
+ "@#{l}:#{c} failed rule '#{info.name}', got '#{char}'"
108
+ else
109
+ "@#{l}:#{c} failed rule '#{@failed_rule}', got '#{char}'"
110
+ end
111
+ end
112
+
113
+ class ParseError < RuntimeError
114
+ end
115
+
116
+ def raise_error
117
+ raise ParseError, failure_oneline
118
+ end
119
+
120
+ def show_error(io=STDOUT)
121
+ error_pos = @failing_rule_offset
122
+ line_no = current_line(error_pos)
123
+ col_no = current_column(error_pos)
124
+
125
+ io.puts "On line #{line_no}, column #{col_no}:"
126
+
127
+ if @failed_rule.kind_of? Symbol
128
+ info = self.class::Rules[@failed_rule]
129
+ io.puts "Failed to match '#{info.rendered}' (rule '#{info.name}')"
130
+ else
131
+ io.puts "Failed to match rule '#{@failed_rule}'"
132
+ end
133
+
134
+ io.puts "Got: #{string[error_pos,1].inspect}"
135
+ line = lines[line_no-1]
136
+ io.puts "=> #{line}"
137
+ io.print(" " * (col_no + 3))
138
+ io.puts "^"
139
+ end
140
+
141
+ def set_failed_rule(name)
142
+ if @pos > @failing_rule_offset
143
+ @failed_rule = name
144
+ @failing_rule_offset = @pos
145
+ end
146
+ end
147
+
148
+ attr_reader :failed_rule
149
+
150
+ def match_string(str)
151
+ len = str.size
152
+ if @string[pos,len] == str
153
+ @pos += len
154
+ return str
155
+ end
156
+
157
+ return nil
158
+ end
159
+
160
+ def scan(reg)
161
+ if m = reg.match(@string[@pos..-1])
162
+ width = m.end(0)
163
+ @pos += width
164
+ return true
165
+ end
166
+
167
+ return nil
168
+ end
169
+
170
+ if "".respond_to? :getbyte
171
+ def get_byte
172
+ if @pos >= @string.size
173
+ return nil
174
+ end
175
+
176
+ s = @string.getbyte @pos
177
+ @pos += 1
178
+ s
179
+ end
180
+ else
181
+ def get_byte
182
+ if @pos >= @string.size
183
+ return nil
184
+ end
185
+
186
+ s = @string[@pos]
187
+ @pos += 1
188
+ s
189
+ end
190
+ end
191
+
192
+ def parse(rule=nil)
193
+ # We invoke the rules indirectly via apply
194
+ # instead of by just calling them as methods because
195
+ # if the rules use left recursion, apply needs to
196
+ # manage that.
197
+
198
+ if !rule
199
+ apply(:_root)
200
+ else
201
+ method = rule.gsub("-","_hyphen_")
202
+ apply :"_#{method}"
203
+ end
204
+ end
205
+
206
+ class MemoEntry
207
+ def initialize(ans, pos)
208
+ @ans = ans
209
+ @pos = pos
210
+ @result = nil
211
+ @set = false
212
+ @left_rec = false
213
+ end
214
+
215
+ attr_reader :ans, :pos, :result, :set
216
+ attr_accessor :left_rec
217
+
218
+ def move!(ans, pos, result)
219
+ @ans = ans
220
+ @pos = pos
221
+ @result = result
222
+ @set = true
223
+ @left_rec = false
224
+ end
225
+ end
226
+
227
+ def external_invoke(other, rule, *args)
228
+ old_pos = @pos
229
+ old_string = @string
230
+
231
+ @pos = other.pos
232
+ @string = other.string
233
+
234
+ begin
235
+ if val = __send__(rule, *args)
236
+ other.pos = @pos
237
+ other.result = @result
238
+ else
239
+ other.set_failed_rule "#{self.class}##{rule}"
240
+ end
241
+ val
242
+ ensure
243
+ @pos = old_pos
244
+ @string = old_string
245
+ end
246
+ end
247
+
248
+ def apply_with_args(rule, *args)
249
+ memo_key = [rule, args]
250
+ if m = @memoizations[memo_key][@pos]
251
+ @pos = m.pos
252
+ if !m.set
253
+ m.left_rec = true
254
+ return nil
255
+ end
256
+
257
+ @result = m.result
258
+
259
+ return m.ans
260
+ else
261
+ m = MemoEntry.new(nil, @pos)
262
+ @memoizations[memo_key][@pos] = m
263
+ start_pos = @pos
264
+
265
+ ans = __send__ rule, *args
266
+
267
+ lr = m.left_rec
268
+
269
+ m.move! ans, @pos, @result
270
+
271
+ # Don't bother trying to grow the left recursion
272
+ # if it's failing straight away (thus there is no seed)
273
+ if ans and lr
274
+ return grow_lr(rule, args, start_pos, m)
275
+ else
276
+ return ans
277
+ end
278
+
279
+ return ans
280
+ end
281
+ end
282
+
283
+ def apply(rule)
284
+ if m = @memoizations[rule][@pos]
285
+ @pos = m.pos
286
+ if !m.set
287
+ m.left_rec = true
288
+ return nil
289
+ end
290
+
291
+ @result = m.result
292
+
293
+ return m.ans
294
+ else
295
+ m = MemoEntry.new(nil, @pos)
296
+ @memoizations[rule][@pos] = m
297
+ start_pos = @pos
298
+
299
+ ans = __send__ rule
300
+
301
+ lr = m.left_rec
302
+
303
+ m.move! ans, @pos, @result
304
+
305
+ # Don't bother trying to grow the left recursion
306
+ # if it's failing straight away (thus there is no seed)
307
+ if ans and lr
308
+ return grow_lr(rule, nil, start_pos, m)
309
+ else
310
+ return ans
311
+ end
312
+
313
+ return ans
314
+ end
315
+ end
316
+
317
+ def grow_lr(rule, args, start_pos, m)
318
+ while true
319
+ @pos = start_pos
320
+ @result = m.result
321
+
322
+ if args
323
+ ans = __send__ rule, *args
324
+ else
325
+ ans = __send__ rule
326
+ end
327
+ return nil unless ans
328
+
329
+ break if @pos <= m.pos
330
+
331
+ m.move! ans, @pos, @result
332
+ end
333
+
334
+ @result = m.result
335
+ @pos = m.pos
336
+ return m.ans
337
+ end
338
+
339
+ class RuleInfo
340
+ def initialize(name, rendered)
341
+ @name = name
342
+ @rendered = rendered
343
+ end
344
+
345
+ attr_reader :name, :rendered
346
+ end
347
+
348
+ def self.rule_info(name, rendered)
349
+ RuleInfo.new(name, rendered)
350
+ end
351
+
2
352
 
3
- class KPeg::StringEscape < KPeg::CompiledParser
353
+ # :startdoc:
4
354
 
5
355
 
6
356
  attr_reader :text
7
357
 
8
358
 
359
+ # :stopdoc:
360
+ def setup_foreign_grammar; end
9
361
 
10
362
  # segment = (< /[\w ]+/ > { text } | "\\" { "\\\\" } | "\n" { "\\n" } | "\t" { "\\t" } | "\b" { "\\b" } | "\"" { "\\\"" } | < . > { text })
11
363
  def _segment
@@ -251,4 +603,5 @@ class KPeg::StringEscape < KPeg::CompiledParser
251
603
  Rules[:_root] = rule_info("root", "segment*:s { @text = s.join }")
252
604
  Rules[:_embed_seg] = rule_info("embed_seg", "(\"\#\" { \"\\\\\#\" } | segment)")
253
605
  Rules[:_embed] = rule_info("embed", "embed_seg*:s { @text = s.join }")
606
+ # :startdoc:
254
607
  end