kpeg 1.3.1 → 1.3.3
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 +4 -4
- data/History.txt +9 -0
- data/Rakefile +9 -11
- data/examples/calculator/calculator.kpeg +1 -1
- data/kpeg.gemspec +16 -22
- data/lib/kpeg/compiled_parser.rb +14 -21
- data/lib/kpeg/format_parser.kpeg +1 -1
- data/lib/kpeg/format_parser.rb +68 -47
- data/lib/kpeg/grammar.rb +1 -11
- data/lib/kpeg/position.rb +52 -22
- data/lib/kpeg/string_escape.rb +66 -43
- data/test/test_kpeg.rb +9 -9
- data/test/test_kpeg_code_generator.rb +1 -1
- data/test/test_kpeg_compiled_parser.rb +93 -7
- metadata +18 -13
- data/.hoeignore +0 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d833890a30055ccb0575be00d3ef66bfcb447d3b18f518d2ed3dfb2aa273eec7
|
|
4
|
+
data.tar.gz: 3e056a615b305db820064549a9a94292aadc19a9af20293f9939ae4305897536
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3becd99c88978dd486ecb9db5074ed453f8b953bd29d5a22fb8e112e66b5deb5d2d199b791077e16a10734b6d1de61f3d381ec5461c8e531399df2e2eda75150
|
|
7
|
+
data.tar.gz: 44a83605de6a673d5cc1ac98017680581d374595a6eb028c08ed09098f433e7d297775b9474dcef8737c1483e0a19b6dac34574423c0a600fc0308cf70e2dab4
|
data/History.txt
CHANGED
data/Rakefile
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
# -*- ruby -*-
|
|
2
2
|
|
|
3
3
|
require 'rubygems'
|
|
4
|
-
require '
|
|
4
|
+
require 'rake/testtask'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Hoe.plugin :travis
|
|
10
|
-
|
|
11
|
-
Hoe.spec 'kpeg' do
|
|
12
|
-
self.readme_file = "README.rdoc"
|
|
13
|
-
developer 'Evan Phoenix', 'evan@fallingsnow.net'
|
|
14
|
-
|
|
15
|
-
dependency 'minitest', '~> 5.0', :dev
|
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
|
7
|
+
t.libs << "test"
|
|
8
|
+
t.libs << "lib"
|
|
16
9
|
end
|
|
17
10
|
|
|
18
11
|
task :test => :parser
|
|
@@ -39,9 +32,14 @@ PARSER_FILES.map do |parser_file|
|
|
|
39
32
|
file parser_file => 'lib/kpeg/compiled_parser.rb'
|
|
40
33
|
file parser_file => 'lib/kpeg/code_generator.rb'
|
|
41
34
|
file parser_file => 'lib/kpeg/position.rb'
|
|
35
|
+
file parser_file => parser_file.sub(/\.rb$/, '.kpeg')
|
|
42
36
|
end
|
|
43
37
|
|
|
44
38
|
desc "build the parser"
|
|
45
39
|
task :parser => PARSER_FILES
|
|
46
40
|
|
|
41
|
+
task :gem do
|
|
42
|
+
sh "gem build"
|
|
43
|
+
end
|
|
44
|
+
|
|
47
45
|
# vim: syntax=ruby
|
|
@@ -9,7 +9,7 @@ space = " "
|
|
|
9
9
|
num = < /[1-9][0-9]*/ > { text.to_i }
|
|
10
10
|
|
|
11
11
|
term = term:t1 - "+" - term:t2 { t1 + t2 }
|
|
12
|
-
| term:t1 - "-" -
|
|
12
|
+
| term:t1 - "-" - term:t2 { t1 - t2 }
|
|
13
13
|
| fact
|
|
14
14
|
fact = fact:f1 - "*" - fact:f2 { f1 * f2 }
|
|
15
15
|
| fact:f1 - "/" - fact:f2 { f1 / f2 }
|
data/kpeg.gemspec
CHANGED
|
@@ -1,42 +1,36 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# stub: kpeg 1.0.0.20140103162640 ruby lib
|
|
3
2
|
|
|
4
3
|
Gem::Specification.new do |s|
|
|
5
4
|
s.name = "kpeg"
|
|
6
|
-
s.version = "1.
|
|
5
|
+
s.version = "1.3.3"
|
|
7
6
|
|
|
8
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
8
|
+
s.metadata = { "bug_tracker_uri" => "https://github.com/evanphx/kpeg/issues", "homepage_uri" => "https://github.com/evanphx/kpeg" } if s.respond_to? :metadata=
|
|
9
|
+
s.require_paths = ["lib"]
|
|
9
10
|
s.authors = ["Evan Phoenix"]
|
|
10
|
-
s.date = "
|
|
11
|
+
s.date = "2022-11-02"
|
|
11
12
|
s.description = "KPeg is a simple PEG library for Ruby. It provides an API as well as native\ngrammar to build the grammar.\n\nKPeg strives to provide a simple, powerful API without being too exotic.\n\nKPeg supports direct left recursion of rules via the\n{OMeta memoization}[http://www.vpri.org/pdf/tr2008003_experimenting.pdf] trick."
|
|
12
13
|
s.email = ["evan@fallingsnow.net"]
|
|
13
14
|
s.executables = ["kpeg"]
|
|
14
|
-
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.rdoc", "examples/phone_number/README.md", "examples/upper/README.md"]
|
|
15
|
-
s.files = [".autotest", "
|
|
15
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.rdoc", "examples/phone_number/README.md", "examples/tiny_markdown/sample.md", "examples/upper/README.md"]
|
|
16
|
+
s.files = [".autotest", "Gemfile", "History.txt", "LICENSE", "Manifest.txt", "README.rdoc", "Rakefile", "bin/kpeg", "examples/calculator/calculator.kpeg", "examples/calculator/calculator.rb", "examples/foreign_reference/literals.kpeg", "examples/foreign_reference/matcher.kpeg", "examples/foreign_reference/matcher.rb", "examples/lua_string/driver.rb", "examples/lua_string/lua_string.kpeg", "examples/lua_string/lua_string.kpeg.rb", "examples/phone_number/README.md", "examples/phone_number/phone_number.kpeg", "examples/phone_number/phone_number.rb", "examples/tiny_markdown/Rakefile", "examples/tiny_markdown/driver.rb", "examples/tiny_markdown/node.rb", "examples/tiny_markdown/sample.md", "examples/tiny_markdown/tiny_markdown.kpeg", "examples/tiny_markdown/tiny_markdown.kpeg.rb", "examples/upper/README.md", "examples/upper/upper.kpeg", "examples/upper/upper.rb", "kpeg.gemspec", "lib/hoe/kpeg.rb", "lib/kpeg.rb", "lib/kpeg/code_generator.rb", "lib/kpeg/compiled_parser.rb", "lib/kpeg/format_parser.kpeg", "lib/kpeg/format_parser.rb", "lib/kpeg/grammar.rb", "lib/kpeg/grammar_renderer.rb", "lib/kpeg/match.rb", "lib/kpeg/parser.rb", "lib/kpeg/position.rb", "lib/kpeg/string_escape.kpeg", "lib/kpeg/string_escape.rb", "test/inputs/comments.kpeg", "test/test_kpeg.rb", "test/test_kpeg_code_generator.rb", "test/test_kpeg_compiled_parser.rb", "test/test_kpeg_format.rb", "test/test_kpeg_format_parser_round_trip.rb", "test/test_kpeg_grammar.rb", "test/test_kpeg_grammar_renderer.rb", "test/test_kpeg_string_escape.rb", "vim/syntax_kpeg/ftdetect/kpeg.vim", "vim/syntax_kpeg/syntax/kpeg.vim"]
|
|
16
17
|
s.homepage = "https://github.com/evanphx/kpeg"
|
|
17
|
-
s.licenses = ["
|
|
18
|
+
s.licenses = ["BSD-3-Clause"]
|
|
18
19
|
s.rdoc_options = ["--main", "README.rdoc"]
|
|
19
|
-
s.
|
|
20
|
-
s.rubyforge_project = "kpeg"
|
|
21
|
-
s.rubygems_version = "2.1.10"
|
|
20
|
+
s.rubygems_version = "3.3.7"
|
|
22
21
|
s.summary = "KPeg is a simple PEG library for Ruby"
|
|
23
|
-
s.test_files = ["test/test_kpeg.rb", "test/test_kpeg_code_generator.rb", "test/test_kpeg_compiled_parser.rb", "test/test_kpeg_format.rb", "test/test_kpeg_format_parser_round_trip.rb", "test/test_kpeg_grammar.rb", "test/test_kpeg_grammar_renderer.rb", "test/test_kpeg_string_escape.rb"]
|
|
24
22
|
|
|
25
23
|
if s.respond_to? :specification_version then
|
|
26
24
|
s.specification_version = 4
|
|
25
|
+
end
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
else
|
|
33
|
-
s.add_dependency(%q<minitest>, ["~> 5.2"])
|
|
34
|
-
s.add_dependency(%q<rdoc>, ["~> 4.0"])
|
|
35
|
-
s.add_dependency(%q<hoe>, ["~> 3.7"])
|
|
36
|
-
end
|
|
27
|
+
if s.respond_to? :add_runtime_dependency then
|
|
28
|
+
s.add_development_dependency(%q<minitest>, ["~> 5.16"])
|
|
29
|
+
s.add_development_dependency(%q<rdoc>, [">= 4.0", "< 7"])
|
|
30
|
+
s.add_development_dependency(%q<rake>, [">= 0.8", "< 15.0"])
|
|
37
31
|
else
|
|
38
|
-
s.add_dependency(%q<minitest>, ["~> 5.
|
|
39
|
-
s.add_dependency(%q<rdoc>, ["
|
|
40
|
-
s.add_dependency(%q<
|
|
32
|
+
s.add_dependency(%q<minitest>, ["~> 5.16"])
|
|
33
|
+
s.add_dependency(%q<rdoc>, [">= 4.0", "< 7"])
|
|
34
|
+
s.add_dependency(%q<rake>, [">= 0.8", "< 15.0"])
|
|
41
35
|
end
|
|
42
36
|
end
|
data/lib/kpeg/compiled_parser.rb
CHANGED
|
@@ -52,6 +52,7 @@ module KPeg
|
|
|
52
52
|
@string = string
|
|
53
53
|
@string_size = string ? string.size : 0
|
|
54
54
|
@pos = pos
|
|
55
|
+
@position_line_offsets = nil
|
|
55
56
|
end
|
|
56
57
|
|
|
57
58
|
def show_pos
|
|
@@ -76,30 +77,22 @@ module KPeg
|
|
|
76
77
|
end
|
|
77
78
|
|
|
78
79
|
def failure_caret
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
line = lines[l-1]
|
|
83
|
-
"#{line}\n#{' ' * (c - 1)}^"
|
|
80
|
+
p = current_pos_info @failing_rule_offset
|
|
81
|
+
"#{p.line.chomp}\n#{' ' * (p.col - 1)}^"
|
|
84
82
|
end
|
|
85
83
|
|
|
86
84
|
def failure_character
|
|
87
|
-
|
|
88
|
-
c = current_column @failing_rule_offset
|
|
89
|
-
lines[l-1][c-1, 1]
|
|
85
|
+
current_character @failing_rule_offset
|
|
90
86
|
end
|
|
91
87
|
|
|
92
88
|
def failure_oneline
|
|
93
|
-
|
|
94
|
-
c = current_column @failing_rule_offset
|
|
95
|
-
|
|
96
|
-
char = lines[l-1][c-1, 1]
|
|
89
|
+
p = current_pos_info @failing_rule_offset
|
|
97
90
|
|
|
98
91
|
if @failed_rule.kind_of? Symbol
|
|
99
92
|
info = self.class::Rules[@failed_rule]
|
|
100
|
-
"@#{
|
|
93
|
+
"@#{p.lno}:#{p.col} failed rule '#{info.name}', got '#{p.char}'"
|
|
101
94
|
else
|
|
102
|
-
"@#{
|
|
95
|
+
"@#{p.lno}:#{p.col} failed rule '#{@failed_rule}', got '#{p.char}'"
|
|
103
96
|
end
|
|
104
97
|
end
|
|
105
98
|
|
|
@@ -112,10 +105,9 @@ module KPeg
|
|
|
112
105
|
|
|
113
106
|
def show_error(io=STDOUT)
|
|
114
107
|
error_pos = @failing_rule_offset
|
|
115
|
-
|
|
116
|
-
col_no = current_column(error_pos)
|
|
108
|
+
p = current_pos_info(error_pos)
|
|
117
109
|
|
|
118
|
-
io.puts "On line #{
|
|
110
|
+
io.puts "On line #{p.lno}, column #{p.col}:"
|
|
119
111
|
|
|
120
112
|
if @failed_rule.kind_of? Symbol
|
|
121
113
|
info = self.class::Rules[@failed_rule]
|
|
@@ -124,10 +116,9 @@ module KPeg
|
|
|
124
116
|
io.puts "Failed to match rule '#{@failed_rule}'"
|
|
125
117
|
end
|
|
126
118
|
|
|
127
|
-
io.puts "Got: #{
|
|
128
|
-
|
|
129
|
-
io.
|
|
130
|
-
io.print(" " * (col_no + 3))
|
|
119
|
+
io.puts "Got: #{p.char.inspect}"
|
|
120
|
+
io.puts "=> #{p.line}"
|
|
121
|
+
io.print(" " * (p.col + 2))
|
|
131
122
|
io.puts "^"
|
|
132
123
|
end
|
|
133
124
|
|
|
@@ -236,6 +227,7 @@ module KPeg
|
|
|
236
227
|
end
|
|
237
228
|
|
|
238
229
|
def apply_with_args(rule, *args)
|
|
230
|
+
@result = nil
|
|
239
231
|
memo_key = [rule, args]
|
|
240
232
|
if m = @memoizations[memo_key][@pos]
|
|
241
233
|
@pos = m.pos
|
|
@@ -269,6 +261,7 @@ module KPeg
|
|
|
269
261
|
end
|
|
270
262
|
|
|
271
263
|
def apply(rule)
|
|
264
|
+
@result = nil
|
|
272
265
|
if m = @memoizations[rule][@pos]
|
|
273
266
|
@pos = m.pos
|
|
274
267
|
if !m.set
|
data/lib/kpeg/format_parser.kpeg
CHANGED
|
@@ -53,7 +53,7 @@ require 'kpeg/grammar'
|
|
|
53
53
|
| "x" < /[a-f\d]{2}/i > { [text.to_i(16)].pack("U") }
|
|
54
54
|
# TODO use /\h{2}/ after 1.8 support is dropped
|
|
55
55
|
dbl_seq = < /[^\\"]+/ > { text }
|
|
56
|
-
dbl_not_quote = ("\\" dbl_escapes
|
|
56
|
+
dbl_not_quote = ("\\" dbl_escapes | dbl_seq)*:ary { Array(ary) }
|
|
57
57
|
dbl_string = "\"" dbl_not_quote:s "\"" { @g.str(s.join) }
|
|
58
58
|
sgl_escape_quote = "\\'" { "'" }
|
|
59
59
|
sgl_seq = < /[^']/ > { text }
|
data/lib/kpeg/format_parser.rb
CHANGED
|
@@ -20,45 +20,75 @@ class KPeg::FormatParser
|
|
|
20
20
|
attr_accessor :result, :pos
|
|
21
21
|
|
|
22
22
|
def current_column(target=pos)
|
|
23
|
-
if c = string.rindex("\n", target-1)
|
|
24
|
-
return target - c
|
|
23
|
+
if string[target] == "\n" && (c = string.rindex("\n", target-1) || -1)
|
|
24
|
+
return target - c
|
|
25
|
+
elsif c = string.rindex("\n", target)
|
|
26
|
+
return target - c
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
target + 1
|
|
28
30
|
end
|
|
29
31
|
|
|
32
|
+
def position_line_offsets
|
|
33
|
+
unless @position_line_offsets
|
|
34
|
+
@position_line_offsets = []
|
|
35
|
+
total = 0
|
|
36
|
+
string.each_line do |line|
|
|
37
|
+
total += line.size
|
|
38
|
+
@position_line_offsets << total
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
@position_line_offsets
|
|
42
|
+
end
|
|
43
|
+
|
|
30
44
|
if [].respond_to? :bsearch_index
|
|
31
45
|
def current_line(target=pos)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
total = 0
|
|
35
|
-
string.each_line do |line|
|
|
36
|
-
total += line.size
|
|
37
|
-
@line_offsets << total
|
|
38
|
-
end
|
|
46
|
+
if line = position_line_offsets.bsearch_index {|x| x > target }
|
|
47
|
+
return line + 1
|
|
39
48
|
end
|
|
40
|
-
|
|
41
|
-
@line_offsets.bsearch_index {|x| x >= target } + 1 || -1
|
|
49
|
+
raise "Target position #{target} is outside of string"
|
|
42
50
|
end
|
|
43
51
|
else
|
|
44
52
|
def current_line(target=pos)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
string.each_line do |line|
|
|
49
|
-
cur_line += 1
|
|
50
|
-
cur_offset += line.size
|
|
51
|
-
return cur_line if cur_offset >= target
|
|
53
|
+
if line = position_line_offsets.index {|x| x > target }
|
|
54
|
+
return line + 1
|
|
52
55
|
end
|
|
53
56
|
|
|
54
|
-
|
|
57
|
+
raise "Target position #{target} is outside of string"
|
|
55
58
|
end
|
|
56
59
|
end
|
|
57
60
|
|
|
61
|
+
def current_character(target=pos)
|
|
62
|
+
if target < 0 || target >= string.size
|
|
63
|
+
raise "Target position #{target} is outside of string"
|
|
64
|
+
end
|
|
65
|
+
string[target, 1]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
KpegPosInfo = Struct.new(:pos, :lno, :col, :line, :char)
|
|
69
|
+
|
|
70
|
+
def current_pos_info(target=pos)
|
|
71
|
+
l = current_line target
|
|
72
|
+
c = current_column target
|
|
73
|
+
ln = get_line(l-1)
|
|
74
|
+
chr = string[target,1]
|
|
75
|
+
KpegPosInfo.new(target, l, c, ln, chr)
|
|
76
|
+
end
|
|
77
|
+
|
|
58
78
|
def lines
|
|
59
|
-
lines
|
|
60
|
-
|
|
61
|
-
|
|
79
|
+
string.lines
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def get_line(no)
|
|
83
|
+
loff = position_line_offsets
|
|
84
|
+
if no < 0
|
|
85
|
+
raise "Line No is out of range: #{no} < 0"
|
|
86
|
+
elsif no >= loff.size
|
|
87
|
+
raise "Line No is out of range: #{no} >= #{loff.size}"
|
|
88
|
+
end
|
|
89
|
+
lend = loff[no]-1
|
|
90
|
+
lstart = no > 0 ? loff[no-1] : 0
|
|
91
|
+
string[lstart..lend]
|
|
62
92
|
end
|
|
63
93
|
|
|
64
94
|
|
|
@@ -72,6 +102,7 @@ class KPeg::FormatParser
|
|
|
72
102
|
@string = string
|
|
73
103
|
@string_size = string ? string.size : 0
|
|
74
104
|
@pos = pos
|
|
105
|
+
@position_line_offsets = nil
|
|
75
106
|
end
|
|
76
107
|
|
|
77
108
|
def show_pos
|
|
@@ -96,30 +127,22 @@ class KPeg::FormatParser
|
|
|
96
127
|
end
|
|
97
128
|
|
|
98
129
|
def failure_caret
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
line = lines[l-1]
|
|
103
|
-
"#{line}\n#{' ' * (c - 1)}^"
|
|
130
|
+
p = current_pos_info @failing_rule_offset
|
|
131
|
+
"#{p.line.chomp}\n#{' ' * (p.col - 1)}^"
|
|
104
132
|
end
|
|
105
133
|
|
|
106
134
|
def failure_character
|
|
107
|
-
|
|
108
|
-
c = current_column @failing_rule_offset
|
|
109
|
-
lines[l-1][c-1, 1]
|
|
135
|
+
current_character @failing_rule_offset
|
|
110
136
|
end
|
|
111
137
|
|
|
112
138
|
def failure_oneline
|
|
113
|
-
|
|
114
|
-
c = current_column @failing_rule_offset
|
|
115
|
-
|
|
116
|
-
char = lines[l-1][c-1, 1]
|
|
139
|
+
p = current_pos_info @failing_rule_offset
|
|
117
140
|
|
|
118
141
|
if @failed_rule.kind_of? Symbol
|
|
119
142
|
info = self.class::Rules[@failed_rule]
|
|
120
|
-
"@#{
|
|
143
|
+
"@#{p.lno}:#{p.col} failed rule '#{info.name}', got '#{p.char}'"
|
|
121
144
|
else
|
|
122
|
-
"@#{
|
|
145
|
+
"@#{p.lno}:#{p.col} failed rule '#{@failed_rule}', got '#{p.char}'"
|
|
123
146
|
end
|
|
124
147
|
end
|
|
125
148
|
|
|
@@ -132,10 +155,9 @@ class KPeg::FormatParser
|
|
|
132
155
|
|
|
133
156
|
def show_error(io=STDOUT)
|
|
134
157
|
error_pos = @failing_rule_offset
|
|
135
|
-
|
|
136
|
-
col_no = current_column(error_pos)
|
|
158
|
+
p = current_pos_info(error_pos)
|
|
137
159
|
|
|
138
|
-
io.puts "On line #{
|
|
160
|
+
io.puts "On line #{p.lno}, column #{p.col}:"
|
|
139
161
|
|
|
140
162
|
if @failed_rule.kind_of? Symbol
|
|
141
163
|
info = self.class::Rules[@failed_rule]
|
|
@@ -144,10 +166,9 @@ class KPeg::FormatParser
|
|
|
144
166
|
io.puts "Failed to match rule '#{@failed_rule}'"
|
|
145
167
|
end
|
|
146
168
|
|
|
147
|
-
io.puts "Got: #{
|
|
148
|
-
|
|
149
|
-
io.
|
|
150
|
-
io.print(" " * (col_no + 3))
|
|
169
|
+
io.puts "Got: #{p.char.inspect}"
|
|
170
|
+
io.puts "=> #{p.line}"
|
|
171
|
+
io.print(" " * (p.col + 2))
|
|
151
172
|
io.puts "^"
|
|
152
173
|
end
|
|
153
174
|
|
|
@@ -256,6 +277,7 @@ class KPeg::FormatParser
|
|
|
256
277
|
end
|
|
257
278
|
|
|
258
279
|
def apply_with_args(rule, *args)
|
|
280
|
+
@result = nil
|
|
259
281
|
memo_key = [rule, args]
|
|
260
282
|
if m = @memoizations[memo_key][@pos]
|
|
261
283
|
@pos = m.pos
|
|
@@ -289,6 +311,7 @@ class KPeg::FormatParser
|
|
|
289
311
|
end
|
|
290
312
|
|
|
291
313
|
def apply(rule)
|
|
314
|
+
@result = nil
|
|
292
315
|
if m = @memoizations[rule][@pos]
|
|
293
316
|
@pos = m.pos
|
|
294
317
|
if !m.set
|
|
@@ -910,7 +933,7 @@ class KPeg::FormatParser
|
|
|
910
933
|
return _tmp
|
|
911
934
|
end
|
|
912
935
|
|
|
913
|
-
# dbl_not_quote = ("\\" dbl_escapes
|
|
936
|
+
# dbl_not_quote = ("\\" dbl_escapes | dbl_seq)*:ary { Array(ary) }
|
|
914
937
|
def _dbl_not_quote
|
|
915
938
|
|
|
916
939
|
_save = self.pos
|
|
@@ -929,7 +952,6 @@ class KPeg::FormatParser
|
|
|
929
952
|
break
|
|
930
953
|
end
|
|
931
954
|
_tmp = apply(:_dbl_escapes)
|
|
932
|
-
s = @result
|
|
933
955
|
unless _tmp
|
|
934
956
|
self.pos = _save3
|
|
935
957
|
end
|
|
@@ -939,7 +961,6 @@ class KPeg::FormatParser
|
|
|
939
961
|
break if _tmp
|
|
940
962
|
self.pos = _save2
|
|
941
963
|
_tmp = apply(:_dbl_seq)
|
|
942
|
-
s = @result
|
|
943
964
|
break if _tmp
|
|
944
965
|
self.pos = _save2
|
|
945
966
|
break
|
|
@@ -3158,7 +3179,7 @@ class KPeg::FormatParser
|
|
|
3158
3179
|
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 })")
|
|
3159
3180
|
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\") })")
|
|
3160
3181
|
Rules[:_dbl_seq] = rule_info("dbl_seq", "< /[^\\\\\"]+/ > { text }")
|
|
3161
|
-
Rules[:_dbl_not_quote] = rule_info("dbl_not_quote", "(\"\\\\\" dbl_escapes
|
|
3182
|
+
Rules[:_dbl_not_quote] = rule_info("dbl_not_quote", "(\"\\\\\" dbl_escapes | dbl_seq)*:ary { Array(ary) }")
|
|
3162
3183
|
Rules[:_dbl_string] = rule_info("dbl_string", "\"\\\"\" dbl_not_quote:s \"\\\"\" { @g.str(s.join) }")
|
|
3163
3184
|
Rules[:_sgl_escape_quote] = rule_info("sgl_escape_quote", "\"\\\\'\" { \"'\" }")
|
|
3164
3185
|
Rules[:_sgl_seq] = rule_info("sgl_seq", "< /[^']/ > { text }")
|
data/lib/kpeg/grammar.rb
CHANGED
|
@@ -106,20 +106,10 @@ module KPeg
|
|
|
106
106
|
|
|
107
107
|
if reg.kind_of? String
|
|
108
108
|
flags = 0
|
|
109
|
-
lang = nil
|
|
110
109
|
|
|
111
110
|
if opts
|
|
112
111
|
opts.split("").each do |o|
|
|
113
112
|
case o
|
|
114
|
-
when "n", "N", "e", "E", "s", "S"
|
|
115
|
-
lang = o.downcase
|
|
116
|
-
when "u", "U"
|
|
117
|
-
if RUBY_VERSION > "1.8.7"
|
|
118
|
-
# Ruby 1.9 defaults to UTF-8 for string matching
|
|
119
|
-
lang = ""
|
|
120
|
-
else
|
|
121
|
-
lang = "u"
|
|
122
|
-
end
|
|
123
113
|
when "m"
|
|
124
114
|
flags |= Regexp::MULTILINE
|
|
125
115
|
when "x"
|
|
@@ -130,7 +120,7 @@ module KPeg
|
|
|
130
120
|
end
|
|
131
121
|
end
|
|
132
122
|
|
|
133
|
-
@regexp = Regexp.new(reg, flags
|
|
123
|
+
@regexp = Regexp.new(reg, flags)
|
|
134
124
|
else
|
|
135
125
|
@regexp = reg
|
|
136
126
|
end
|
data/lib/kpeg/position.rb
CHANGED
|
@@ -3,45 +3,75 @@ module KPeg
|
|
|
3
3
|
# STANDALONE START
|
|
4
4
|
|
|
5
5
|
def current_column(target=pos)
|
|
6
|
-
if c = string.rindex("\n", target-1)
|
|
7
|
-
return target - c
|
|
6
|
+
if string[target] == "\n" && (c = string.rindex("\n", target-1) || -1)
|
|
7
|
+
return target - c
|
|
8
|
+
elsif c = string.rindex("\n", target)
|
|
9
|
+
return target - c
|
|
8
10
|
end
|
|
9
11
|
|
|
10
12
|
target + 1
|
|
11
13
|
end
|
|
12
14
|
|
|
15
|
+
def position_line_offsets
|
|
16
|
+
unless @position_line_offsets
|
|
17
|
+
@position_line_offsets = []
|
|
18
|
+
total = 0
|
|
19
|
+
string.each_line do |line|
|
|
20
|
+
total += line.size
|
|
21
|
+
@position_line_offsets << total
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
@position_line_offsets
|
|
25
|
+
end
|
|
26
|
+
|
|
13
27
|
if [].respond_to? :bsearch_index
|
|
14
28
|
def current_line(target=pos)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
total = 0
|
|
18
|
-
string.each_line do |line|
|
|
19
|
-
total += line.size
|
|
20
|
-
@line_offsets << total
|
|
21
|
-
end
|
|
29
|
+
if line = position_line_offsets.bsearch_index {|x| x > target }
|
|
30
|
+
return line + 1
|
|
22
31
|
end
|
|
23
|
-
|
|
24
|
-
@line_offsets.bsearch_index {|x| x >= target } + 1 || -1
|
|
32
|
+
raise "Target position #{target} is outside of string"
|
|
25
33
|
end
|
|
26
34
|
else
|
|
27
35
|
def current_line(target=pos)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
string.each_line do |line|
|
|
32
|
-
cur_line += 1
|
|
33
|
-
cur_offset += line.size
|
|
34
|
-
return cur_line if cur_offset >= target
|
|
36
|
+
if line = position_line_offsets.index {|x| x > target }
|
|
37
|
+
return line + 1
|
|
35
38
|
end
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
raise "Target position #{target} is outside of string"
|
|
38
41
|
end
|
|
39
42
|
end
|
|
40
43
|
|
|
44
|
+
def current_character(target=pos)
|
|
45
|
+
if target < 0 || target >= string.size
|
|
46
|
+
raise "Target position #{target} is outside of string"
|
|
47
|
+
end
|
|
48
|
+
string[target, 1]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
KpegPosInfo = Struct.new(:pos, :lno, :col, :line, :char)
|
|
52
|
+
|
|
53
|
+
def current_pos_info(target=pos)
|
|
54
|
+
l = current_line target
|
|
55
|
+
c = current_column target
|
|
56
|
+
ln = get_line(l-1)
|
|
57
|
+
chr = string[target,1]
|
|
58
|
+
KpegPosInfo.new(target, l, c, ln, chr)
|
|
59
|
+
end
|
|
60
|
+
|
|
41
61
|
def lines
|
|
42
|
-
lines
|
|
43
|
-
|
|
44
|
-
|
|
62
|
+
string.lines
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def get_line(no)
|
|
66
|
+
loff = position_line_offsets
|
|
67
|
+
if no < 0
|
|
68
|
+
raise "Line No is out of range: #{no} < 0"
|
|
69
|
+
elsif no >= loff.size
|
|
70
|
+
raise "Line No is out of range: #{no} >= #{loff.size}"
|
|
71
|
+
end
|
|
72
|
+
lend = loff[no]-1
|
|
73
|
+
lstart = no > 0 ? loff[no-1] : 0
|
|
74
|
+
string[lstart..lend]
|
|
45
75
|
end
|
|
46
76
|
|
|
47
77
|
# STANDALONE END
|
data/lib/kpeg/string_escape.rb
CHANGED
|
@@ -28,45 +28,75 @@ class KPeg::StringEscape
|
|
|
28
28
|
attr_accessor :result, :pos
|
|
29
29
|
|
|
30
30
|
def current_column(target=pos)
|
|
31
|
-
if c = string.rindex("\n", target-1)
|
|
32
|
-
return target - c
|
|
31
|
+
if string[target] == "\n" && (c = string.rindex("\n", target-1) || -1)
|
|
32
|
+
return target - c
|
|
33
|
+
elsif c = string.rindex("\n", target)
|
|
34
|
+
return target - c
|
|
33
35
|
end
|
|
34
36
|
|
|
35
37
|
target + 1
|
|
36
38
|
end
|
|
37
39
|
|
|
40
|
+
def position_line_offsets
|
|
41
|
+
unless @position_line_offsets
|
|
42
|
+
@position_line_offsets = []
|
|
43
|
+
total = 0
|
|
44
|
+
string.each_line do |line|
|
|
45
|
+
total += line.size
|
|
46
|
+
@position_line_offsets << total
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
@position_line_offsets
|
|
50
|
+
end
|
|
51
|
+
|
|
38
52
|
if [].respond_to? :bsearch_index
|
|
39
53
|
def current_line(target=pos)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
total = 0
|
|
43
|
-
string.each_line do |line|
|
|
44
|
-
total += line.size
|
|
45
|
-
@line_offsets << total
|
|
46
|
-
end
|
|
54
|
+
if line = position_line_offsets.bsearch_index {|x| x > target }
|
|
55
|
+
return line + 1
|
|
47
56
|
end
|
|
48
|
-
|
|
49
|
-
@line_offsets.bsearch_index {|x| x >= target } + 1 || -1
|
|
57
|
+
raise "Target position #{target} is outside of string"
|
|
50
58
|
end
|
|
51
59
|
else
|
|
52
60
|
def current_line(target=pos)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
string.each_line do |line|
|
|
57
|
-
cur_line += 1
|
|
58
|
-
cur_offset += line.size
|
|
59
|
-
return cur_line if cur_offset >= target
|
|
61
|
+
if line = position_line_offsets.index {|x| x > target }
|
|
62
|
+
return line + 1
|
|
60
63
|
end
|
|
61
64
|
|
|
62
|
-
|
|
65
|
+
raise "Target position #{target} is outside of string"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def current_character(target=pos)
|
|
70
|
+
if target < 0 || target >= string.size
|
|
71
|
+
raise "Target position #{target} is outside of string"
|
|
63
72
|
end
|
|
73
|
+
string[target, 1]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
KpegPosInfo = Struct.new(:pos, :lno, :col, :line, :char)
|
|
77
|
+
|
|
78
|
+
def current_pos_info(target=pos)
|
|
79
|
+
l = current_line target
|
|
80
|
+
c = current_column target
|
|
81
|
+
ln = get_line(l-1)
|
|
82
|
+
chr = string[target,1]
|
|
83
|
+
KpegPosInfo.new(target, l, c, ln, chr)
|
|
64
84
|
end
|
|
65
85
|
|
|
66
86
|
def lines
|
|
67
|
-
lines
|
|
68
|
-
|
|
69
|
-
|
|
87
|
+
string.lines
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def get_line(no)
|
|
91
|
+
loff = position_line_offsets
|
|
92
|
+
if no < 0
|
|
93
|
+
raise "Line No is out of range: #{no} < 0"
|
|
94
|
+
elsif no >= loff.size
|
|
95
|
+
raise "Line No is out of range: #{no} >= #{loff.size}"
|
|
96
|
+
end
|
|
97
|
+
lend = loff[no]-1
|
|
98
|
+
lstart = no > 0 ? loff[no-1] : 0
|
|
99
|
+
string[lstart..lend]
|
|
70
100
|
end
|
|
71
101
|
|
|
72
102
|
|
|
@@ -80,6 +110,7 @@ class KPeg::StringEscape
|
|
|
80
110
|
@string = string
|
|
81
111
|
@string_size = string ? string.size : 0
|
|
82
112
|
@pos = pos
|
|
113
|
+
@position_line_offsets = nil
|
|
83
114
|
end
|
|
84
115
|
|
|
85
116
|
def show_pos
|
|
@@ -104,30 +135,22 @@ class KPeg::StringEscape
|
|
|
104
135
|
end
|
|
105
136
|
|
|
106
137
|
def failure_caret
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
line = lines[l-1]
|
|
111
|
-
"#{line}\n#{' ' * (c - 1)}^"
|
|
138
|
+
p = current_pos_info @failing_rule_offset
|
|
139
|
+
"#{p.line.chomp}\n#{' ' * (p.col - 1)}^"
|
|
112
140
|
end
|
|
113
141
|
|
|
114
142
|
def failure_character
|
|
115
|
-
|
|
116
|
-
c = current_column @failing_rule_offset
|
|
117
|
-
lines[l-1][c-1, 1]
|
|
143
|
+
current_character @failing_rule_offset
|
|
118
144
|
end
|
|
119
145
|
|
|
120
146
|
def failure_oneline
|
|
121
|
-
|
|
122
|
-
c = current_column @failing_rule_offset
|
|
123
|
-
|
|
124
|
-
char = lines[l-1][c-1, 1]
|
|
147
|
+
p = current_pos_info @failing_rule_offset
|
|
125
148
|
|
|
126
149
|
if @failed_rule.kind_of? Symbol
|
|
127
150
|
info = self.class::Rules[@failed_rule]
|
|
128
|
-
"@#{
|
|
151
|
+
"@#{p.lno}:#{p.col} failed rule '#{info.name}', got '#{p.char}'"
|
|
129
152
|
else
|
|
130
|
-
"@#{
|
|
153
|
+
"@#{p.lno}:#{p.col} failed rule '#{@failed_rule}', got '#{p.char}'"
|
|
131
154
|
end
|
|
132
155
|
end
|
|
133
156
|
|
|
@@ -140,10 +163,9 @@ class KPeg::StringEscape
|
|
|
140
163
|
|
|
141
164
|
def show_error(io=STDOUT)
|
|
142
165
|
error_pos = @failing_rule_offset
|
|
143
|
-
|
|
144
|
-
col_no = current_column(error_pos)
|
|
166
|
+
p = current_pos_info(error_pos)
|
|
145
167
|
|
|
146
|
-
io.puts "On line #{
|
|
168
|
+
io.puts "On line #{p.lno}, column #{p.col}:"
|
|
147
169
|
|
|
148
170
|
if @failed_rule.kind_of? Symbol
|
|
149
171
|
info = self.class::Rules[@failed_rule]
|
|
@@ -152,10 +174,9 @@ class KPeg::StringEscape
|
|
|
152
174
|
io.puts "Failed to match rule '#{@failed_rule}'"
|
|
153
175
|
end
|
|
154
176
|
|
|
155
|
-
io.puts "Got: #{
|
|
156
|
-
|
|
157
|
-
io.
|
|
158
|
-
io.print(" " * (col_no + 3))
|
|
177
|
+
io.puts "Got: #{p.char.inspect}"
|
|
178
|
+
io.puts "=> #{p.line}"
|
|
179
|
+
io.print(" " * (p.col + 2))
|
|
159
180
|
io.puts "^"
|
|
160
181
|
end
|
|
161
182
|
|
|
@@ -264,6 +285,7 @@ class KPeg::StringEscape
|
|
|
264
285
|
end
|
|
265
286
|
|
|
266
287
|
def apply_with_args(rule, *args)
|
|
288
|
+
@result = nil
|
|
267
289
|
memo_key = [rule, args]
|
|
268
290
|
if m = @memoizations[memo_key][@pos]
|
|
269
291
|
@pos = m.pos
|
|
@@ -297,6 +319,7 @@ class KPeg::StringEscape
|
|
|
297
319
|
end
|
|
298
320
|
|
|
299
321
|
def apply(rule)
|
|
322
|
+
@result = nil
|
|
300
323
|
if m = @memoizations[rule][@pos]
|
|
301
324
|
@pos = m.pos
|
|
302
325
|
if !m.set
|
data/test/test_kpeg.rb
CHANGED
|
@@ -22,7 +22,7 @@ class TestKPeg < Minitest::Test
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
assert_match KPeg.match("hello", gram), "hello"
|
|
25
|
-
|
|
25
|
+
assert_nil KPeg.match("vador", gram)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def test_reg
|
|
@@ -48,7 +48,7 @@ class TestKPeg < Minitest::Test
|
|
|
48
48
|
|
|
49
49
|
assert_match KPeg.match("hello", gram), "hello"
|
|
50
50
|
assert_match KPeg.match("chicken", gram), "chicken"
|
|
51
|
-
|
|
51
|
+
assert_nil KPeg.match("vador", gram)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def test_maybe
|
|
@@ -78,7 +78,7 @@ class TestKPeg < Minitest::Test
|
|
|
78
78
|
assert_match sm, "run"
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
assert_nil KPeg.match("vador", gram)
|
|
82
82
|
end
|
|
83
83
|
|
|
84
84
|
def test_kleene
|
|
@@ -124,9 +124,9 @@ class TestKPeg < Minitest::Test
|
|
|
124
124
|
assert_match sm, "run"
|
|
125
125
|
end
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
assert_nil KPeg.match("run", gram)
|
|
128
|
+
assert_nil KPeg.match("runrunrunrunrun", gram)
|
|
129
|
+
assert_nil KPeg.match("vador", gram)
|
|
130
130
|
end
|
|
131
131
|
|
|
132
132
|
def test_seq
|
|
@@ -141,8 +141,8 @@ class TestKPeg < Minitest::Test
|
|
|
141
141
|
|
|
142
142
|
assert_equal m.value, ["hello", ", world"]
|
|
143
143
|
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
assert_nil KPeg.match("vador", gram)
|
|
145
|
+
assert_nil KPeg.match("hello, vador", gram)
|
|
146
146
|
end
|
|
147
147
|
|
|
148
148
|
def test_andp
|
|
@@ -346,7 +346,7 @@ class TestKPeg < Minitest::Test
|
|
|
346
346
|
parser = KPeg::Parser.new "hello", gram
|
|
347
347
|
m = parser.parse
|
|
348
348
|
|
|
349
|
-
|
|
349
|
+
assert_nil m
|
|
350
350
|
end
|
|
351
351
|
|
|
352
352
|
def test_math_grammar
|
|
@@ -20,12 +20,50 @@ class TestKPegCompiledParser < Minitest::Test
|
|
|
20
20
|
|
|
21
21
|
KPeg.compile gram, "CompTestParser", self
|
|
22
22
|
|
|
23
|
+
gram = <<-GRAM
|
|
24
|
+
letter = < [a-z] > { text }
|
|
25
|
+
number = [0-9]
|
|
26
|
+
n_or_l = letter | number
|
|
27
|
+
root = letter:l n_or_l*:n { [l, n] }
|
|
28
|
+
GRAM
|
|
29
|
+
|
|
30
|
+
KPeg.compile gram, "ProdTestParser", self
|
|
31
|
+
|
|
23
32
|
def test_current_column
|
|
24
|
-
r = TestParser.new "hello\nsir"
|
|
33
|
+
r = TestParser.new "hello\nsir\nand goodbye"
|
|
34
|
+
assert_equal 1, r.current_column(0)
|
|
25
35
|
assert_equal 2, r.current_column(1)
|
|
26
36
|
assert_equal 6, r.current_column(5)
|
|
27
|
-
assert_equal
|
|
28
|
-
assert_equal 4, r.current_column(
|
|
37
|
+
assert_equal 2, r.current_column(7)
|
|
38
|
+
assert_equal 4, r.current_column(9)
|
|
39
|
+
assert_equal 1, r.current_column(10)
|
|
40
|
+
assert_equal 11, r.current_column(20)
|
|
41
|
+
assert_equal 13, r.current_column(22)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_current_line
|
|
45
|
+
r = TestParser.new "hello\nsir\nand goodbye"
|
|
46
|
+
assert_equal 1, r.current_line(0)
|
|
47
|
+
assert_equal 1, r.current_line(1)
|
|
48
|
+
assert_equal 1, r.current_line(5)
|
|
49
|
+
assert_equal 2, r.current_line(7)
|
|
50
|
+
assert_equal 2, r.current_line(9)
|
|
51
|
+
assert_equal 3, r.current_line(10)
|
|
52
|
+
assert_equal 3, r.current_line(20)
|
|
53
|
+
assert_raises { r.current_line(22) }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_current_character
|
|
58
|
+
r = TestParser.new "hello\nsir\nand goodbye"
|
|
59
|
+
assert_equal ?h, r.current_character(0)
|
|
60
|
+
assert_equal ?e, r.current_character(1)
|
|
61
|
+
assert_equal ?\n, r.current_character(5)
|
|
62
|
+
assert_equal ?i, r.current_character(7)
|
|
63
|
+
assert_equal ?\n, r.current_character(9)
|
|
64
|
+
assert_equal ?a, r.current_character(10)
|
|
65
|
+
assert_equal ?e, r.current_character(20)
|
|
66
|
+
assert_raises { r.current_character(22) }
|
|
29
67
|
end
|
|
30
68
|
|
|
31
69
|
def test_failed_rule
|
|
@@ -36,7 +74,7 @@ class TestKPegCompiledParser < Minitest::Test
|
|
|
36
74
|
end
|
|
37
75
|
|
|
38
76
|
def test_failure_info
|
|
39
|
-
r = TestParser.new "9"
|
|
77
|
+
r = TestParser.new "9\n1"
|
|
40
78
|
assert !r.parse, "shouldn't parse"
|
|
41
79
|
|
|
42
80
|
expected = "line 1, column 1: failed rule 'letter' = '[a-z]'"
|
|
@@ -45,21 +83,21 @@ class TestKPegCompiledParser < Minitest::Test
|
|
|
45
83
|
end
|
|
46
84
|
|
|
47
85
|
def test_failure_caret
|
|
48
|
-
r = TestParser.new "9"
|
|
86
|
+
r = TestParser.new "9\n1"
|
|
49
87
|
assert !r.parse, "shouldn't parse"
|
|
50
88
|
|
|
51
89
|
assert_equal "9\n^", r.failure_caret
|
|
52
90
|
end
|
|
53
91
|
|
|
54
92
|
def test_failure_character
|
|
55
|
-
r = TestParser.new "9"
|
|
93
|
+
r = TestParser.new "9\n1"
|
|
56
94
|
assert !r.parse, "shouldn't parse"
|
|
57
95
|
|
|
58
96
|
assert_equal "9", r.failure_character
|
|
59
97
|
end
|
|
60
98
|
|
|
61
99
|
def test_failure_oneline
|
|
62
|
-
r = TestParser.new "9"
|
|
100
|
+
r = TestParser.new "9\n1"
|
|
63
101
|
assert !r.parse, "shouldn't parse"
|
|
64
102
|
|
|
65
103
|
expected = "@1:1 failed rule 'letter', got '9'"
|
|
@@ -87,4 +125,52 @@ class TestKPegCompiledParser < Minitest::Test
|
|
|
87
125
|
assert_equal expected, r.failure_oneline
|
|
88
126
|
end
|
|
89
127
|
|
|
128
|
+
def test_producing_parser_one_product
|
|
129
|
+
r = ProdTestParser.new "a"
|
|
130
|
+
assert r.parse, "should parse"
|
|
131
|
+
|
|
132
|
+
assert_equal ["a", []], r.result
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def test_producing_parser_two_products
|
|
136
|
+
r = ProdTestParser.new "ab"
|
|
137
|
+
assert r.parse, "should parse"
|
|
138
|
+
|
|
139
|
+
assert_equal ["a", ["b"]], r.result
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def test_producing_parser_three_products
|
|
143
|
+
r = ProdTestParser.new "abc"
|
|
144
|
+
assert r.parse, "should parse"
|
|
145
|
+
|
|
146
|
+
assert_equal ["a", ["b", "c"]], r.result
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def test_producing_parser_product_and_nil
|
|
150
|
+
r = ProdTestParser.new "a1"
|
|
151
|
+
assert r.parse, "should parse"
|
|
152
|
+
|
|
153
|
+
assert_equal ["a", [nil]], r.result
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def test_producing_parser_product_and_nil2
|
|
157
|
+
r = ProdTestParser.new "a1b"
|
|
158
|
+
assert r.parse, "should parse"
|
|
159
|
+
|
|
160
|
+
assert_equal ["a", [nil, "b"]], r.result
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def test_producing_parser_product_and_nil3
|
|
164
|
+
r = ProdTestParser.new "ab1"
|
|
165
|
+
assert r.parse, "should parse"
|
|
166
|
+
|
|
167
|
+
assert_equal ["a", ["b", nil]], r.result
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def test_producing_parser_product_and_nil4
|
|
171
|
+
r = ProdTestParser.new "a12"
|
|
172
|
+
assert r.parse, "should parse"
|
|
173
|
+
|
|
174
|
+
assert_equal ["a", [nil, nil]], r.result
|
|
175
|
+
end
|
|
90
176
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kpeg
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.3.
|
|
4
|
+
version: 1.3.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Evan Phoenix
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-
|
|
11
|
+
date: 2022-11-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: minitest
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '5.
|
|
19
|
+
version: '5.16'
|
|
20
20
|
type: :development
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '5.
|
|
26
|
+
version: '5.16'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: rdoc
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -45,19 +45,25 @@ dependencies:
|
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
46
|
version: '7'
|
|
47
47
|
- !ruby/object:Gem::Dependency
|
|
48
|
-
name:
|
|
48
|
+
name: rake
|
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
|
-
- - "
|
|
51
|
+
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '
|
|
53
|
+
version: '0.8'
|
|
54
|
+
- - "<"
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: '15.0'
|
|
54
57
|
type: :development
|
|
55
58
|
prerelease: false
|
|
56
59
|
version_requirements: !ruby/object:Gem::Requirement
|
|
57
60
|
requirements:
|
|
58
|
-
- - "
|
|
61
|
+
- - ">="
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: '0.8'
|
|
64
|
+
- - "<"
|
|
59
65
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '
|
|
66
|
+
version: '15.0'
|
|
61
67
|
description: |-
|
|
62
68
|
KPeg is a simple PEG library for Ruby. It provides an API as well as native
|
|
63
69
|
grammar to build the grammar.
|
|
@@ -80,7 +86,6 @@ extra_rdoc_files:
|
|
|
80
86
|
- examples/upper/README.md
|
|
81
87
|
files:
|
|
82
88
|
- ".autotest"
|
|
83
|
-
- ".hoeignore"
|
|
84
89
|
- Gemfile
|
|
85
90
|
- History.txt
|
|
86
91
|
- LICENSE
|
|
@@ -135,10 +140,10 @@ files:
|
|
|
135
140
|
- vim/syntax_kpeg/syntax/kpeg.vim
|
|
136
141
|
homepage: https://github.com/evanphx/kpeg
|
|
137
142
|
licenses:
|
|
138
|
-
-
|
|
143
|
+
- BSD-3-Clause
|
|
139
144
|
metadata:
|
|
140
|
-
homepage_uri: https://github.com/evanphx/kpeg
|
|
141
145
|
bug_tracker_uri: https://github.com/evanphx/kpeg/issues
|
|
146
|
+
homepage_uri: https://github.com/evanphx/kpeg
|
|
142
147
|
post_install_message:
|
|
143
148
|
rdoc_options:
|
|
144
149
|
- "--main"
|
|
@@ -156,7 +161,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
156
161
|
- !ruby/object:Gem::Version
|
|
157
162
|
version: '0'
|
|
158
163
|
requirements: []
|
|
159
|
-
rubygems_version: 3.
|
|
164
|
+
rubygems_version: 3.3.7
|
|
160
165
|
signing_key:
|
|
161
166
|
specification_version: 4
|
|
162
167
|
summary: KPeg is a simple PEG library for Ruby
|