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.
- checksums.yaml +7 -0
- data/.hoeignore +12 -0
- data/Gemfile +2 -11
- data/History.txt +22 -1
- data/Manifest.txt +10 -3
- data/README.rdoc +40 -8
- data/Rakefile +12 -3
- data/bin/kpeg +9 -2
- data/examples/lua_string/lua_string.kpeg.rb +0 -2
- data/examples/tiny_markdown/Rakefile +3 -0
- data/examples/tiny_markdown/driver.rb +10 -0
- data/examples/tiny_markdown/node.rb +107 -0
- data/examples/tiny_markdown/sample.md +51 -0
- data/examples/tiny_markdown/tiny_markdown.kpeg +199 -0
- data/examples/tiny_markdown/tiny_markdown.kpeg.rb +3892 -0
- data/kpeg.gemspec +20 -20
- data/lib/hoe/kpeg.rb +6 -5
- data/lib/kpeg/code_generator.rb +81 -45
- data/lib/kpeg/compiled_parser.rb +32 -28
- data/lib/kpeg/format_parser.kpeg +22 -10
- data/lib/kpeg/format_parser.rb +94 -73
- data/lib/kpeg/grammar.rb +2 -2
- data/lib/kpeg/grammar_renderer.rb +14 -0
- data/lib/kpeg/position.rb +25 -8
- data/lib/kpeg/string_escape.kpeg +1 -0
- data/lib/kpeg/string_escape.rb +399 -13
- data/lib/kpeg.rb +1 -1
- data/test/test_kpeg.rb +1 -1
- data/test/test_kpeg_code_generator.rb +186 -14
- data/test/test_kpeg_compiled_parser.rb +1 -1
- data/test/test_kpeg_format.rb +25 -6
- data/test/{test_file_parser_roundtrip.rb → test_kpeg_format_parser_round_trip.rb} +1 -1
- data/test/{test_gen_calc.rb → test_kpeg_grammar.rb} +48 -5
- data/test/test_kpeg_grammar_renderer.rb +47 -6
- data/test/test_kpeg_string_escape.rb +42 -0
- metadata +90 -101
- data/.gemtest +0 -0
- data/test/test_left_recursion.rb +0 -50
data/lib/kpeg/format_parser.rb
CHANGED
@@ -1,29 +1,24 @@
|
|
1
|
+
require 'kpeg/grammar'
|
1
2
|
class KPeg::FormatParser
|
2
|
-
#
|
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
|
-
|
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
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
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
|
-
|
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
|
158
|
-
|
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? :
|
183
|
+
if "".respond_to? :ord
|
167
184
|
def get_byte
|
168
|
-
if @pos >= @
|
185
|
+
if @pos >= @string_size
|
169
186
|
return nil
|
170
187
|
end
|
171
188
|
|
172
|
-
s = @string
|
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 >= @
|
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
|
-
|
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
|
-
|
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
|
-
|
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-
|
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(/\
|
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-
|
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(/\
|
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" < /[
|
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(/\
|
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(/\
|
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(/\
|
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(/\
|
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(/\
|
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(/\
|
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-
|
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(/\
|
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]
|
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(/\
|
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 =
|
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(/\
|
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 - "=" - < /[
|
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(/\
|
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]
|
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(/\
|
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 = < /[
|
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(/\
|
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-
|
3138
|
-
Rules[:_method] = rule_info("method", "< /[a-
|
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\" < /[
|
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-
|
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]
|
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 - \"=\" - < /[
|
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]
|
3171
|
-
Rules[:_ast_word] = rule_info("ast_word", "< /[
|
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
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
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
|
-
|
38
|
+
-1
|
39
|
+
end
|
23
40
|
end
|
24
41
|
|
25
42
|
def lines
|