kpeg 0.9.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,29 +1,24 @@
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
- @string = str
5
- @pos = 0
8
+ set_string str, 0
6
9
  @memoizations = Hash.new { |h,k| h[k] = {} }
7
10
  @result = nil
8
11
  @failed_rule = nil
9
12
  @failing_rule_offset = -1
13
+ @line_offsets = nil
10
14
 
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
27
22
  def current_column(target=pos)
28
23
  if c = string.rindex("\n", target-1)
29
24
  return target - c - 1
@@ -32,17 +27,33 @@ class KPeg::FormatParser
32
27
  target + 1
33
28
  end
34
29
 
35
- def current_line(target=pos)
36
- cur_offset = 0
37
- cur_line = 0
30
+ if [].respond_to? :bsearch_index
31
+ def current_line(target=pos)
32
+ unless @line_offsets
33
+ @line_offsets = [-1]
34
+ total = 0
35
+ string.each_line do |line|
36
+ @line_offsets << total
37
+ total += line.size
38
+ end
39
+ @line_offsets << total
40
+ end
38
41
 
39
- string.each_line do |line|
40
- cur_line += 1
41
- cur_offset += line.size
42
- return cur_line if cur_offset >= target
42
+ @line_offsets.bsearch_index {|x| x >= target } || -1
43
43
  end
44
+ else
45
+ def current_line(target=pos)
46
+ cur_offset = 0
47
+ cur_line = 0
44
48
 
45
- -1
49
+ string.each_line do |line|
50
+ cur_line += 1
51
+ cur_offset += line.size
52
+ return cur_line if cur_offset >= target
53
+ end
54
+
55
+ -1
56
+ end
46
57
  end
47
58
 
48
59
  def lines
@@ -51,12 +62,19 @@ class KPeg::FormatParser
51
62
  lines
52
63
  end
53
64
 
54
- #
65
+
55
66
 
56
67
  def get_text(start)
57
68
  @string[start..@pos-1]
58
69
  end
59
70
 
71
+ # Sets the string and current parsing position for the parser.
72
+ def set_string string, pos
73
+ @string = string
74
+ @string_size = string ? string.size : 0
75
+ @pos = pos
76
+ end
77
+
60
78
  def show_pos
61
79
  width = 10
62
80
  if @pos < width
@@ -154,28 +172,27 @@ class KPeg::FormatParser
154
172
  end
155
173
 
156
174
  def scan(reg)
157
- if m = reg.match(@string[@pos..-1])
158
- width = m.end(0)
159
- @pos += width
175
+ if m = reg.match(@string, @pos)
176
+ @pos = m.end(0)
160
177
  return true
161
178
  end
162
179
 
163
180
  return nil
164
181
  end
165
182
 
166
- if "".respond_to? :getbyte
183
+ if "".respond_to? :ord
167
184
  def get_byte
168
- if @pos >= @string.size
185
+ if @pos >= @string_size
169
186
  return nil
170
187
  end
171
188
 
172
- s = @string.getbyte @pos
189
+ s = @string[@pos].ord
173
190
  @pos += 1
174
191
  s
175
192
  end
176
193
  else
177
194
  def get_byte
178
- if @pos >= @string.size
195
+ if @pos >= @string_size
179
196
  return nil
180
197
  end
181
198
 
@@ -224,8 +241,7 @@ class KPeg::FormatParser
224
241
  old_pos = @pos
225
242
  old_string = @string
226
243
 
227
- @pos = other.pos
228
- @string = other.string
244
+ set_string other.string, other.pos
229
245
 
230
246
  begin
231
247
  if val = __send__(rule, *args)
@@ -236,15 +252,13 @@ class KPeg::FormatParser
236
252
  end
237
253
  val
238
254
  ensure
239
- @pos = old_pos
240
- @string = old_string
255
+ set_string old_string, old_pos
241
256
  end
242
257
  end
243
258
 
244
259
  def apply_with_args(rule, *args)
245
260
  memo_key = [rule, args]
246
261
  if m = @memoizations[memo_key][@pos]
247
- prev = @pos
248
262
  @pos = m.pos
249
263
  if !m.set
250
264
  m.left_rec = true
@@ -272,14 +286,11 @@ class KPeg::FormatParser
272
286
  else
273
287
  return ans
274
288
  end
275
-
276
- return ans
277
289
  end
278
290
  end
279
291
 
280
292
  def apply(rule)
281
293
  if m = @memoizations[rule][@pos]
282
- prev = @pos
283
294
  @pos = m.pos
284
295
  if !m.set
285
296
  m.left_rec = true
@@ -307,8 +318,6 @@ class KPeg::FormatParser
307
318
  else
308
319
  return ans
309
320
  end
310
-
311
- return ans
312
321
  end
313
322
  end
314
323
 
@@ -347,20 +356,28 @@ class KPeg::FormatParser
347
356
  RuleInfo.new(name, rendered)
348
357
  end
349
358
 
350
- #
359
+
360
+ # :startdoc:
351
361
 
352
362
 
353
- require 'kpeg/grammar'
363
+
364
+ ##
365
+ # Creates a new kpeg format parser for +str+.
354
366
 
355
367
  def initialize(str, debug=false)
356
368
  setup_parser(str, debug)
357
369
  @g = KPeg::Grammar.new
358
370
  end
359
371
 
372
+ ##
373
+ # The parsed grammar
374
+
360
375
  attr_reader :g
376
+
361
377
  alias_method :grammar, :g
362
378
 
363
379
 
380
+ # :stopdoc:
364
381
  def setup_foreign_grammar; end
365
382
 
366
383
  # eol = "\n"
@@ -509,7 +526,7 @@ class KPeg::FormatParser
509
526
  return _tmp
510
527
  end
511
528
 
512
- # var = < ("-" | /[a-zA-Z][\-_a-zA-Z0-9]*/) > { text }
529
+ # var = < ("-" | /[a-z][\w-]*/i) > { text }
513
530
  def _var
514
531
 
515
532
  _save = self.pos
@@ -521,7 +538,7 @@ class KPeg::FormatParser
521
538
  _tmp = match_string("-")
522
539
  break if _tmp
523
540
  self.pos = _save1
524
- _tmp = scan(/\A(?-mix:[a-zA-Z][\-_a-zA-Z0-9]*)/)
541
+ _tmp = scan(/\G(?i-mx:[a-z][\w-]*)/)
525
542
  break if _tmp
526
543
  self.pos = _save1
527
544
  break
@@ -546,13 +563,13 @@ class KPeg::FormatParser
546
563
  return _tmp
547
564
  end
548
565
 
549
- # method = < /[a-zA-Z_][a-zA-Z0-9_]*/ > { text }
566
+ # method = < /[a-z_]\w*/i > { text }
550
567
  def _method
551
568
 
552
569
  _save = self.pos
553
570
  while true # sequence
554
571
  _text_start = self.pos
555
- _tmp = scan(/\A(?-mix:[a-zA-Z_][a-zA-Z0-9_]*)/)
572
+ _tmp = scan(/\G(?i-mx:[a-z_]\w*)/)
556
573
  if _tmp
557
574
  text = get_text(_text_start)
558
575
  end
@@ -807,7 +824,7 @@ class KPeg::FormatParser
807
824
  return _tmp
808
825
  end
809
826
 
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") })
827
+ # 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
828
  def _num_escapes
812
829
 
813
830
  _save = self.pos
@@ -816,7 +833,7 @@ class KPeg::FormatParser
816
833
  _save1 = self.pos
817
834
  while true # sequence
818
835
  _text_start = self.pos
819
- _tmp = scan(/\A(?-mix:[0-7]{1,3})/)
836
+ _tmp = scan(/\G(?-mix:[0-7]{1,3})/)
820
837
  if _tmp
821
838
  text = get_text(_text_start)
822
839
  end
@@ -843,7 +860,7 @@ class KPeg::FormatParser
843
860
  break
844
861
  end
845
862
  _text_start = self.pos
846
- _tmp = scan(/\A(?-mix:[0-9a-fA-F]{2})/)
863
+ _tmp = scan(/\G(?i-mx:[a-f\d]{2})/)
847
864
  if _tmp
848
865
  text = get_text(_text_start)
849
866
  end
@@ -874,7 +891,7 @@ class KPeg::FormatParser
874
891
  _save = self.pos
875
892
  while true # sequence
876
893
  _text_start = self.pos
877
- _tmp = scan(/\A(?-mix:[^\\"]+)/)
894
+ _tmp = scan(/\G(?-mix:[^\\"]+)/)
878
895
  if _tmp
879
896
  text = get_text(_text_start)
880
897
  end
@@ -1012,7 +1029,7 @@ class KPeg::FormatParser
1012
1029
  _save = self.pos
1013
1030
  while true # sequence
1014
1031
  _text_start = self.pos
1015
- _tmp = scan(/\A(?-mix:[^'])/)
1032
+ _tmp = scan(/\G(?-mix:[^'])/)
1016
1033
  if _tmp
1017
1034
  text = get_text(_text_start)
1018
1035
  end
@@ -1137,7 +1154,7 @@ class KPeg::FormatParser
1137
1154
  _tmp = match_string("\\/")
1138
1155
  break if _tmp
1139
1156
  self.pos = _save2
1140
- _tmp = scan(/\A(?-mix:[^\/])/)
1157
+ _tmp = scan(/\G(?-mix:[^\/])/)
1141
1158
  break if _tmp
1142
1159
  self.pos = _save2
1143
1160
  break
@@ -1151,7 +1168,7 @@ class KPeg::FormatParser
1151
1168
  _tmp = match_string("\\/")
1152
1169
  break if _tmp
1153
1170
  self.pos = _save3
1154
- _tmp = scan(/\A(?-mix:[^\/])/)
1171
+ _tmp = scan(/\G(?-mix:[^\/])/)
1155
1172
  break if _tmp
1156
1173
  self.pos = _save3
1157
1174
  break
@@ -1258,13 +1275,13 @@ class KPeg::FormatParser
1258
1275
  return _tmp
1259
1276
  end
1260
1277
 
1261
- # char = < /[a-zA-Z0-9]/ > { text }
1278
+ # char = < /[a-z\d]/i > { text }
1262
1279
  def _char
1263
1280
 
1264
1281
  _save = self.pos
1265
1282
  while true # sequence
1266
1283
  _text_start = self.pos
1267
- _tmp = scan(/\A(?-mix:[a-zA-Z0-9])/)
1284
+ _tmp = scan(/\G(?i-mx:[a-z\d])/)
1268
1285
  if _tmp
1269
1286
  text = get_text(_text_start)
1270
1287
  end
@@ -1328,13 +1345,13 @@ class KPeg::FormatParser
1328
1345
  return _tmp
1329
1346
  end
1330
1347
 
1331
- # range_num = < /[1-9][0-9]*/ > { text }
1348
+ # range_num = < /[1-9]\d*/ > { text }
1332
1349
  def _range_num
1333
1350
 
1334
1351
  _save = self.pos
1335
1352
  while true # sequence
1336
1353
  _text_start = self.pos
1337
- _tmp = scan(/\A(?-mix:[1-9][0-9]*)/)
1354
+ _tmp = scan(/\G(?-mix:[1-9]\d*)/)
1338
1355
  if _tmp
1339
1356
  text = get_text(_text_start)
1340
1357
  end
@@ -1509,7 +1526,7 @@ class KPeg::FormatParser
1509
1526
  return _tmp
1510
1527
  end
1511
1528
 
1512
- # curly = "{" < (/[^{}"']+/ | string | curly)* > "}" { @g.action(text) }
1529
+ # curly = "{" < (spaces | /[^{}"']+/ | string | curly)* > "}" { @g.action(text) }
1513
1530
  def _curly
1514
1531
 
1515
1532
  _save = self.pos
@@ -1524,7 +1541,10 @@ class KPeg::FormatParser
1524
1541
 
1525
1542
  _save2 = self.pos
1526
1543
  while true # choice
1527
- _tmp = scan(/\A(?-mix:[^{}"']+)/)
1544
+ _tmp = apply(:_spaces)
1545
+ break if _tmp
1546
+ self.pos = _save2
1547
+ _tmp = scan(/\G(?-mix:[^{}"']+)/)
1528
1548
  break if _tmp
1529
1549
  self.pos = _save2
1530
1550
  _tmp = apply(:_string)
@@ -1577,7 +1597,7 @@ class KPeg::FormatParser
1577
1597
 
1578
1598
  _save2 = self.pos
1579
1599
  while true # choice
1580
- _tmp = scan(/\A(?-mix:[^()"']+)/)
1600
+ _tmp = scan(/\G(?-mix:[^()"']+)/)
1581
1601
  break if _tmp
1582
1602
  self.pos = _save2
1583
1603
  _tmp = apply(:_string)
@@ -2468,7 +2488,7 @@ class KPeg::FormatParser
2468
2488
  return _tmp
2469
2489
  end
2470
2490
 
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) })
2491
+ # 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
2492
  def _statement
2473
2493
 
2474
2494
  _save = self.pos
@@ -2614,7 +2634,7 @@ class KPeg::FormatParser
2614
2634
  break
2615
2635
  end
2616
2636
  _text_start = self.pos
2617
- _tmp = scan(/\A(?-mix:[::A-Za-z0-9_]+)/)
2637
+ _tmp = scan(/\G(?-mix:[:\w]+)/)
2618
2638
  if _tmp
2619
2639
  text = get_text(_text_start)
2620
2640
  end
@@ -2906,13 +2926,13 @@ class KPeg::FormatParser
2906
2926
  return _tmp
2907
2927
  end
2908
2928
 
2909
- # ast_constant = < /[A-Z][A-Za-z0-9_]*/ > { text }
2929
+ # ast_constant = < /[A-Z]\w*/ > { text }
2910
2930
  def _ast_constant
2911
2931
 
2912
2932
  _save = self.pos
2913
2933
  while true # sequence
2914
2934
  _text_start = self.pos
2915
- _tmp = scan(/\A(?-mix:[A-Z][A-Za-z0-9_]*)/)
2935
+ _tmp = scan(/\G(?-mix:[A-Z]\w*)/)
2916
2936
  if _tmp
2917
2937
  text = get_text(_text_start)
2918
2938
  end
@@ -2932,13 +2952,13 @@ class KPeg::FormatParser
2932
2952
  return _tmp
2933
2953
  end
2934
2954
 
2935
- # ast_word = < /[A-Za-z_][A-Za-z0-9_]*/ > { text }
2955
+ # ast_word = < /[a-z_]\w*/i > { text }
2936
2956
  def _ast_word
2937
2957
 
2938
2958
  _save = self.pos
2939
2959
  while true # sequence
2940
2960
  _text_start = self.pos
2941
- _tmp = scan(/\A(?-mix:[A-Za-z_][A-Za-z0-9_]*)/)
2961
+ _tmp = scan(/\G(?i-mx:[a-z_]\w*)/)
2942
2962
  if _tmp
2943
2963
  text = get_text(_text_start)
2944
2964
  end
@@ -3134,10 +3154,10 @@ class KPeg::FormatParser
3134
3154
  Rules[:_space] = rule_info("space", "(\" \" | \"\\t\" | eol)")
3135
3155
  Rules[:__hyphen_] = rule_info("-", "(space | comment)*")
3136
3156
  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 }")
3157
+ Rules[:_var] = rule_info("var", "< (\"-\" | /[a-z][\\w-]*/i) > { text }")
3158
+ Rules[:_method] = rule_info("method", "< /[a-z_]\\w*/i > { text }")
3139
3159
  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\") })")
3160
+ 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
3161
  Rules[:_dbl_seq] = rule_info("dbl_seq", "< /[^\\\\\"]+/ > { text }")
3142
3162
  Rules[:_dbl_not_quote] = rule_info("dbl_not_quote", "(\"\\\\\" dbl_escapes:s | dbl_seq:s)*:ary { Array(ary) }")
3143
3163
  Rules[:_dbl_string] = rule_info("dbl_string", "\"\\\"\" dbl_not_quote:s \"\\\"\" { @g.str(s.join) }")
@@ -3149,13 +3169,13 @@ class KPeg::FormatParser
3149
3169
  Rules[:_not_slash] = rule_info("not_slash", "< (\"\\\\/\" | /[^\\/]/)+ > { text }")
3150
3170
  Rules[:_regexp_opts] = rule_info("regexp_opts", "< [a-z]* > { text }")
3151
3171
  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 }")
3172
+ Rules[:_char] = rule_info("char", "< /[a-z\\d]/i > { text }")
3153
3173
  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 }")
3174
+ Rules[:_range_num] = rule_info("range_num", "< /[1-9]\\d*/ > { text }")
3155
3175
  Rules[:_range_elem] = rule_info("range_elem", "< (range_num | kleene) > { text }")
3156
3176
  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
3177
  Rules[:_curly_block] = rule_info("curly_block", "curly")
3158
- Rules[:_curly] = rule_info("curly", "\"{\" < (/[^{}\"']+/ | string | curly)* > \"}\" { @g.action(text) }")
3178
+ Rules[:_curly] = rule_info("curly", "\"{\" < (spaces | /[^{}\"']+/ | string | curly)* > \"}\" { @g.action(text) }")
3159
3179
  Rules[:_nested_paren] = rule_info("nested_paren", "\"(\" (/[^()\"']+/ | string | nested_paren)* \")\"")
3160
3180
  Rules[:_value] = rule_info("value", "(value:v \":\" var:n { @g.t(v,n) } | value:v \"?\" { @g.maybe(v) } | value:v \"+\" { @g.many(v) } | value:v \"*\" { @g.kleene(v) } | value:v mult_range:r { @g.multiple(v, *r) } | \"&\" value:v { @g.andp(v) } | \"!\" value:v { @g.notp(v) } | \"(\" - expression:o - \")\" { o } | \"@<\" - expression:o - \">\" { @g.bounds(o) } | \"<\" - expression:o - \">\" { @g.collect(o) } | curly_block | \"~\" method:m < nested_paren? > { @g.action(\"\#{m}\#{text}\") } | \".\" { @g.dot } | \"@\" var:name < nested_paren? > !(- \"=\") { @g.invoke(name, text.empty? ? nil : text) } | \"^\" var:name < nested_paren? > { @g.foreign_invoke(\"parent\", name, text) } | \"%\" var:gram \".\" var:name < nested_paren? > { @g.foreign_invoke(gram, name, text) } | var:name < nested_paren? > !(- \"=\") { @g.ref(name, nil, text.empty? ? nil : text) } | char_range | regexp | string)")
3161
3181
  Rules[:_spaces] = rule_info("spaces", "(space | comment)+")
@@ -3163,13 +3183,14 @@ class KPeg::FormatParser
3163
3183
  Rules[:_choose_cont] = rule_info("choose_cont", "- \"|\" - values:v { v }")
3164
3184
  Rules[:_expression] = rule_info("expression", "(values:v choose_cont+:alts { @g.any(v, *alts) } | values)")
3165
3185
  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) })")
3186
+ 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
3187
  Rules[:_statements] = rule_info("statements", "statement (- statements)?")
3168
3188
  Rules[:_eof] = rule_info("eof", "!.")
3169
3189
  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 }")
3190
+ Rules[:_ast_constant] = rule_info("ast_constant", "< /[A-Z]\\w*/ > { text }")
3191
+ Rules[:_ast_word] = rule_info("ast_word", "< /[a-z_]\\w*/i > { text }")
3172
3192
  Rules[:_ast_sp] = rule_info("ast_sp", "(\" \" | \"\\t\")*")
3173
3193
  Rules[:_ast_words] = rule_info("ast_words", "(ast_words:r ast_sp \",\" ast_sp ast_word:w { r + [w] } | ast_word:w { [w] })")
3174
3194
  Rules[:_ast_root] = rule_info("ast_root", "(ast_constant:c \"(\" ast_words:w \")\" { [c, w] } | ast_constant:c \"()\"? { [c, []] })")
3195
+ # :startdoc:
3175
3196
  end
data/lib/kpeg/grammar.rb CHANGED
@@ -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)
@@ -834,7 +834,7 @@ module KPeg
834
834
  # Invoke a rule defined on a foreign grammar
835
835
  # == Parameters:
836
836
  # gram::
837
- # The name of the grammar that the rule will be reference from
837
+ # The name of the grammar that the rule will be reference from
838
838
  # name::
839
839
  # The name of the rule that will be invoked
840
840
  # args::
@@ -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
data/lib/kpeg/position.rb CHANGED
@@ -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
@@ -9,17 +10,33 @@ module KPeg
9
10
  target + 1
10
11
  end
11
12
 
12
- def current_line(target=pos)
13
- cur_offset = 0
14
- cur_line = 0
13
+ if [].respond_to? :bsearch_index
14
+ def current_line(target=pos)
15
+ unless @line_offsets
16
+ @line_offsets = [-1]
17
+ total = 0
18
+ string.each_line do |line|
19
+ @line_offsets << total
20
+ total += line.size
21
+ end
22
+ @line_offsets << total
23
+ end
15
24
 
16
- string.each_line do |line|
17
- cur_line += 1
18
- cur_offset += line.size
19
- return cur_line if cur_offset >= target
25
+ @line_offsets.bsearch_index {|x| x >= target } || -1
20
26
  end
27
+ else
28
+ def current_line(target=pos)
29
+ cur_offset = 0
30
+ cur_line = 0
31
+
32
+ string.each_line do |line|
33
+ cur_line += 1
34
+ cur_offset += line.size
35
+ return cur_line if cur_offset >= target
36
+ end
21
37
 
22
- -1
38
+ -1
39
+ end
23
40
  end
24
41
 
25
42
  def lines
@@ -7,6 +7,7 @@
7
7
  segment = < /[\w ]+/ > { text } # Don't use \s because that matchs \n
8
8
  | "\\" { "\\\\" }
9
9
  | "\n" { "\\n" }
10
+ | "\r" { "\\r" }
10
11
  | "\t" { "\\t" }
11
12
  | "\b" { "\\b" }
12
13
  | "\"" { "\\\"" }