kpeg 0.9.0 → 1.2.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.
@@ -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
  | "\"" { "\\\"" }