kpeg 1.3.1 → 1.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +5 -13
- data/kpeg.gemspec +16 -22
- data/lib/kpeg/compiled_parser.rb +12 -21
- data/lib/kpeg/format_parser.kpeg +1 -1
- data/lib/kpeg/format_parser.rb +66 -47
- data/lib/kpeg/position.rb +52 -22
- data/lib/kpeg/string_escape.rb +64 -43
- data/test/test_kpeg.rb +9 -9
- data/test/test_kpeg_code_generator.rb +1 -1
- data/test/test_kpeg_compiled_parser.rb +36 -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: abed16806bd433f58dd9911eb8c68f2d50e4a56af15b4e2a7f4eba333727a969
|
4
|
+
data.tar.gz: 28e547321489a71fa5fc8058d9b236123d1e4da143fe6e7d4f437a8f8bd2c27f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96523e0c2694a0f5e5af682e22ecbebf268d40add7dd97e25db90725c686982d220fb77c22962bf21025cbddfe570763c01b3720304a8b57d99508e9eab0d00a
|
7
|
+
data.tar.gz: 94716fa06ffb6e5a9322753ae60de879ad4aab8bdeb854590e9b5a201cb73b35aeec7378a5907b38e8571433586f28c323251a66c274368ce687f4994614b041
|
data/Rakefile
CHANGED
@@ -1,19 +1,6 @@
|
|
1
1
|
# -*- ruby -*-
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
|
-
require 'hoe'
|
5
|
-
|
6
|
-
Hoe.plugin :gemspec
|
7
|
-
Hoe.plugin :git
|
8
|
-
Hoe.plugin :minitest
|
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
|
16
|
-
end
|
17
4
|
|
18
5
|
task :test => :parser
|
19
6
|
|
@@ -39,9 +26,14 @@ PARSER_FILES.map do |parser_file|
|
|
39
26
|
file parser_file => 'lib/kpeg/compiled_parser.rb'
|
40
27
|
file parser_file => 'lib/kpeg/code_generator.rb'
|
41
28
|
file parser_file => 'lib/kpeg/position.rb'
|
29
|
+
file parser_file => parser_file.sub(/\.rb$/, '.kpeg')
|
42
30
|
end
|
43
31
|
|
44
32
|
desc "build the parser"
|
45
33
|
task :parser => PARSER_FILES
|
46
34
|
|
35
|
+
task :gem do
|
36
|
+
sh "gem build"
|
37
|
+
end
|
38
|
+
|
47
39
|
# vim: syntax=ruby
|
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.2"
|
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
|
|
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"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def current_character(target=pos)
|
62
|
+
if target < 0 || target >= string.size
|
63
|
+
raise "Target position #{target} is outside of string"
|
55
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)
|
56
76
|
end
|
57
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
|
|
@@ -910,7 +931,7 @@ class KPeg::FormatParser
|
|
910
931
|
return _tmp
|
911
932
|
end
|
912
933
|
|
913
|
-
# dbl_not_quote = ("\\" dbl_escapes
|
934
|
+
# dbl_not_quote = ("\\" dbl_escapes | dbl_seq)*:ary { Array(ary) }
|
914
935
|
def _dbl_not_quote
|
915
936
|
|
916
937
|
_save = self.pos
|
@@ -929,7 +950,6 @@ class KPeg::FormatParser
|
|
929
950
|
break
|
930
951
|
end
|
931
952
|
_tmp = apply(:_dbl_escapes)
|
932
|
-
s = @result
|
933
953
|
unless _tmp
|
934
954
|
self.pos = _save3
|
935
955
|
end
|
@@ -939,7 +959,6 @@ class KPeg::FormatParser
|
|
939
959
|
break if _tmp
|
940
960
|
self.pos = _save2
|
941
961
|
_tmp = apply(:_dbl_seq)
|
942
|
-
s = @result
|
943
962
|
break if _tmp
|
944
963
|
self.pos = _save2
|
945
964
|
break
|
@@ -3158,7 +3177,7 @@ class KPeg::FormatParser
|
|
3158
3177
|
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
3178
|
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
3179
|
Rules[:_dbl_seq] = rule_info("dbl_seq", "< /[^\\\\\"]+/ > { text }")
|
3161
|
-
Rules[:_dbl_not_quote] = rule_info("dbl_not_quote", "(\"\\\\\" dbl_escapes
|
3180
|
+
Rules[:_dbl_not_quote] = rule_info("dbl_not_quote", "(\"\\\\\" dbl_escapes | dbl_seq)*:ary { Array(ary) }")
|
3162
3181
|
Rules[:_dbl_string] = rule_info("dbl_string", "\"\\\"\" dbl_not_quote:s \"\\\"\" { @g.str(s.join) }")
|
3163
3182
|
Rules[:_sgl_escape_quote] = rule_info("sgl_escape_quote", "\"\\\\'\" { \"'\" }")
|
3164
3183
|
Rules[:_sgl_seq] = rule_info("sgl_seq", "< /[^']/ > { text }")
|
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
|
|
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
|
@@ -21,11 +21,40 @@ class TestKPegCompiledParser < Minitest::Test
|
|
21
21
|
KPeg.compile gram, "CompTestParser", self
|
22
22
|
|
23
23
|
def test_current_column
|
24
|
-
r = TestParser.new "hello\nsir"
|
24
|
+
r = TestParser.new "hello\nsir\nand goodbye"
|
25
|
+
assert_equal 1, r.current_column(0)
|
25
26
|
assert_equal 2, r.current_column(1)
|
26
27
|
assert_equal 6, r.current_column(5)
|
27
|
-
assert_equal
|
28
|
-
assert_equal 4, r.current_column(
|
28
|
+
assert_equal 2, r.current_column(7)
|
29
|
+
assert_equal 4, r.current_column(9)
|
30
|
+
assert_equal 1, r.current_column(10)
|
31
|
+
assert_equal 11, r.current_column(20)
|
32
|
+
assert_equal 13, r.current_column(22)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_current_line
|
36
|
+
r = TestParser.new "hello\nsir\nand goodbye"
|
37
|
+
assert_equal 1, r.current_line(0)
|
38
|
+
assert_equal 1, r.current_line(1)
|
39
|
+
assert_equal 1, r.current_line(5)
|
40
|
+
assert_equal 2, r.current_line(7)
|
41
|
+
assert_equal 2, r.current_line(9)
|
42
|
+
assert_equal 3, r.current_line(10)
|
43
|
+
assert_equal 3, r.current_line(20)
|
44
|
+
assert_raises { r.current_line(22) }
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
def test_current_character
|
49
|
+
r = TestParser.new "hello\nsir\nand goodbye"
|
50
|
+
assert_equal ?h, r.current_character(0)
|
51
|
+
assert_equal ?e, r.current_character(1)
|
52
|
+
assert_equal ?\n, r.current_character(5)
|
53
|
+
assert_equal ?i, r.current_character(7)
|
54
|
+
assert_equal ?\n, r.current_character(9)
|
55
|
+
assert_equal ?a, r.current_character(10)
|
56
|
+
assert_equal ?e, r.current_character(20)
|
57
|
+
assert_raises { r.current_character(22) }
|
29
58
|
end
|
30
59
|
|
31
60
|
def test_failed_rule
|
@@ -36,7 +65,7 @@ class TestKPegCompiledParser < Minitest::Test
|
|
36
65
|
end
|
37
66
|
|
38
67
|
def test_failure_info
|
39
|
-
r = TestParser.new "9"
|
68
|
+
r = TestParser.new "9\n1"
|
40
69
|
assert !r.parse, "shouldn't parse"
|
41
70
|
|
42
71
|
expected = "line 1, column 1: failed rule 'letter' = '[a-z]'"
|
@@ -45,21 +74,21 @@ class TestKPegCompiledParser < Minitest::Test
|
|
45
74
|
end
|
46
75
|
|
47
76
|
def test_failure_caret
|
48
|
-
r = TestParser.new "9"
|
77
|
+
r = TestParser.new "9\n1"
|
49
78
|
assert !r.parse, "shouldn't parse"
|
50
79
|
|
51
80
|
assert_equal "9\n^", r.failure_caret
|
52
81
|
end
|
53
82
|
|
54
83
|
def test_failure_character
|
55
|
-
r = TestParser.new "9"
|
84
|
+
r = TestParser.new "9\n1"
|
56
85
|
assert !r.parse, "shouldn't parse"
|
57
86
|
|
58
87
|
assert_equal "9", r.failure_character
|
59
88
|
end
|
60
89
|
|
61
90
|
def test_failure_oneline
|
62
|
-
r = TestParser.new "9"
|
91
|
+
r = TestParser.new "9\n1"
|
63
92
|
assert !r.parse, "shouldn't parse"
|
64
93
|
|
65
94
|
expected = "@1:1 failed rule 'letter', got '9'"
|
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.2
|
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
|