abnf 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +194 -0
- data/Rakefile +18 -0
- data/abnf.gemspec +24 -0
- data/lib/abnf.rb +57 -0
- data/lib/abnf/abnf.rb +136 -0
- data/lib/abnf/corerules.rb +28 -0
- data/lib/abnf/grammar.rb +183 -0
- data/lib/abnf/parser.output +348 -0
- data/lib/abnf/parser.rb +821 -0
- data/lib/abnf/parser.y +156 -0
- data/lib/abnf/regexp.rb +394 -0
- data/lib/abnf/version.rb +3 -0
- data/lib/natset.rb +411 -0
- data/lib/regexptree.rb +530 -0
- data/sample/in-place.rb +26 -0
- data/sample/ipv6.rb +42 -0
- data/sample/multiples-of-3.rb +19 -0
- data/sample/uri.rb +75 -0
- data/test/abnf_test.rb +82 -0
- data/test/regexptree_test.rb +12 -0
- data/test/test_helper.rb +3 -0
- metadata +115 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dd62c9239754c0264cfc03bd29bd5a3f5473f9d2
|
4
|
+
data.tar.gz: 68adb3f6b144396412d52379021fa5d0c32b7676
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c8a9a996d80087aa76a05fb19e2fb94fa736e19549787cadb12c31e10f358175ff9640856bfafe7e5b0de85df0aae079036332719e4161c8d89ad51e651b15ae
|
7
|
+
data.tar.gz: 379e25d8d244b2f2154a8e934943a42084bd05b70d301533eb82ba85587111b30eda0e01b7c762345d1292e142bdab9ba409a3b4a0a1993eb49218ece2c7aa4a
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Steve Klabnik
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
# Abnf
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/steveklabnik/abnf.png?branch=master)](https://travis-ci.org/steveklabnik/abnf) [![Code Climate](https://codeclimate.com/github/steveklabnik/abnf.png)](https://codeclimate.com/github/steveklabnik/abnf)
|
4
|
+
|
5
|
+
This is a library to convert ABNF (Augmented Backus-Naur Form) to Regexp
|
6
|
+
(Regular Expression) written in Ruby. It parses a description according to
|
7
|
+
ABNF defined by RFC2234 and some variants. Then the parsed grammar is
|
8
|
+
transformed to recursion free. Although the transformation is impossible in
|
9
|
+
general, the library transform left- and right-recursion. The recursion free
|
10
|
+
grammar can be printed as Regexp literal which is suitable in Ruby script. The
|
11
|
+
literal is pretty readable because parentheses are minimized and properly
|
12
|
+
indented by the Wadler's pretty printing algebra. It is also possible to use
|
13
|
+
the Regexp just in place.
|
14
|
+
|
15
|
+
I (@steveklabnik) have gem-ified the code originally located at
|
16
|
+
[https://github.com/akr/abnf](https://github.com/akr/abnf).
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Add this line to your application's Gemfile:
|
21
|
+
|
22
|
+
gem 'abnf'
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
$ bundle
|
27
|
+
|
28
|
+
Or install it yourself as:
|
29
|
+
|
30
|
+
$ gem install abnf
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
Following example shows URI-reference defined by RFC 2396 can be converted to
|
35
|
+
Regexp.
|
36
|
+
Note that URI-reference has no recursion.
|
37
|
+
Also note that the example works well without installing the library after make.
|
38
|
+
|
39
|
+
require 'pp'
|
40
|
+
require 'abnf'
|
41
|
+
|
42
|
+
# URI-reference [RFC2396]
|
43
|
+
pp ABNF.regexp_tree(<<-'End')
|
44
|
+
URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
|
45
|
+
absoluteURI = scheme ":" ( hier_part | opaque_part )
|
46
|
+
relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ]
|
47
|
+
|
48
|
+
(...snip...)
|
49
|
+
|
50
|
+
lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
|
51
|
+
"j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
|
52
|
+
"s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
|
53
|
+
upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
|
54
|
+
"J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
|
55
|
+
"S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
|
56
|
+
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
|
57
|
+
"8" | "9"
|
58
|
+
End
|
59
|
+
|
60
|
+
The result is follows:
|
61
|
+
%r{(?:[a-z][\x2b\x2d\x2e0-9a-z]*:
|
62
|
+
(?:(?://
|
63
|
+
(?:(?:(?:(?:[!'-\x2a\x2d\x2e0-9_a-z~]|
|
64
|
+
%[0-9a-f][0-9a-f]|
|
65
|
+
[\x24&\x2b,:;=])*@)?
|
66
|
+
(?:(?:(?:[0-9a-z]|[0-9a-z][\x2d0-9a-z]*[0-9a-z])\x2e)*
|
67
|
+
(?:[a-z]|[a-z][\x2d0-9a-z]*[0-9a-z])\x2e?|
|
68
|
+
\d+\x2e\d+\x2e\d+\x2e\d+)(?::\d*)?)?|
|
69
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|
|
70
|
+
%[0-9a-f][0-9a-f]|
|
71
|
+
[\x24&\x2b,:;=@])+)
|
72
|
+
(?:/
|
73
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|%[0-9a-f][0-9a-f]|[\x24&\x2b,:=@])*
|
74
|
+
(?:;
|
75
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|
|
76
|
+
%[0-9a-f][0-9a-f]|
|
77
|
+
[\x24&\x2b,:=@])*)*
|
78
|
+
(?:/
|
79
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|
|
80
|
+
%[0-9a-f][0-9a-f]|
|
81
|
+
[\x24&\x2b,:=@])*
|
82
|
+
(?:;
|
83
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|
|
84
|
+
%[0-9a-f][0-9a-f]|
|
85
|
+
[\x24&\x2b,:=@])*)*)*)?|
|
86
|
+
/(?:[!'-\x2a\x2d\x2e0-9_a-z~]|%[0-9a-f][0-9a-f]|[\x24&\x2b,:=@])*
|
87
|
+
(?:;
|
88
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|
|
89
|
+
%[0-9a-f][0-9a-f]|
|
90
|
+
[\x24&\x2b,:=@])*)*
|
91
|
+
(?:/
|
92
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|%[0-9a-f][0-9a-f]|[\x24&\x2b,:=@])*
|
93
|
+
(?:;
|
94
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|
|
95
|
+
%[0-9a-f][0-9a-f]|
|
96
|
+
[\x24&\x2b,:=@])*)*)*)
|
97
|
+
(?:\x3f(?:[!\x24&-;=\x3f@_a-z~]|%[0-9a-f][0-9a-f])*)?|
|
98
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|%[0-9a-f][0-9a-f]|[\x24&\x2b,:;=\x3f@])
|
99
|
+
(?:[!\x24&-;=\x3f@_a-z~]|%[0-9a-f][0-9a-f])*)|
|
100
|
+
(?://
|
101
|
+
(?:(?:(?:(?:[!'-\x2a\x2d\x2e0-9_a-z~]|
|
102
|
+
%[0-9a-f][0-9a-f]|
|
103
|
+
[\x24&\x2b,:;=])*@)?
|
104
|
+
(?:(?:(?:[0-9a-z]|[0-9a-z][\x2d0-9a-z]*[0-9a-z])\x2e)*
|
105
|
+
(?:[a-z]|[a-z][\x2d0-9a-z]*[0-9a-z])\x2e?|
|
106
|
+
\d+\x2e\d+\x2e\d+\x2e\d+)(?::\d*)?)?|
|
107
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|%[0-9a-f][0-9a-f]|[\x24&\x2b,:;=@])+)
|
108
|
+
(?:/(?:[!'-\x2a\x2d\x2e0-9_a-z~]|%[0-9a-f][0-9a-f]|[\x24&\x2b,:=@])*
|
109
|
+
(?:;
|
110
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|
|
111
|
+
%[0-9a-f][0-9a-f]|
|
112
|
+
[\x24&\x2b,:=@])*)*
|
113
|
+
(?:/
|
114
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|%[0-9a-f][0-9a-f]|[\x24&\x2b,:=@])*
|
115
|
+
(?:;
|
116
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|
|
117
|
+
%[0-9a-f][0-9a-f]|
|
118
|
+
[\x24&\x2b,:=@])*)*)*)?|
|
119
|
+
/(?:[!'-\x2a\x2d\x2e0-9_a-z~]|%[0-9a-f][0-9a-f]|[\x24&\x2b,:=@])*
|
120
|
+
(?:;(?:[!'-\x2a\x2d\x2e0-9_a-z~]|%[0-9a-f][0-9a-f]|[\x24&\x2b,:=@])*)*
|
121
|
+
(?:/(?:[!'-\x2a\x2d\x2e0-9_a-z~]|%[0-9a-f][0-9a-f]|[\x24&\x2b,:=@])*
|
122
|
+
(?:;
|
123
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|
|
124
|
+
%[0-9a-f][0-9a-f]|
|
125
|
+
[\x24&\x2b,:=@])*)*)*|
|
126
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|%[0-9a-f][0-9a-f]|[\x24&\x2b,;=@])+
|
127
|
+
(?:/(?:[!'-\x2a\x2d\x2e0-9_a-z~]|%[0-9a-f][0-9a-f]|[\x24&\x2b,:=@])*
|
128
|
+
(?:;
|
129
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|
|
130
|
+
%[0-9a-f][0-9a-f]|
|
131
|
+
[\x24&\x2b,:=@])*)*
|
132
|
+
(?:/
|
133
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|%[0-9a-f][0-9a-f]|[\x24&\x2b,:=@])*
|
134
|
+
(?:;
|
135
|
+
(?:[!'-\x2a\x2d\x2e0-9_a-z~]|
|
136
|
+
%[0-9a-f][0-9a-f]|
|
137
|
+
[\x24&\x2b,:=@])*)*)*)?)
|
138
|
+
(?:\x3f(?:[!\x24&-;=\x3f@_a-z~]|%[0-9a-f][0-9a-f])*)?)?
|
139
|
+
(?:\x23(?:[!\x24&-;=\x3f@_a-z~]|%[0-9a-f][0-9a-f])*)?}xi
|
140
|
+
|
141
|
+
|
142
|
+
Following example contains right-recursion:
|
143
|
+
|
144
|
+
pp ABNF.regexp_tree(<<'End')
|
145
|
+
s0 = n0 s0 / n1 s2 / n2 s1 / ""
|
146
|
+
s1 = n0 s1 / n1 s0 / n2 s2
|
147
|
+
s2 = n0 s2 / n1 s1 / n2 s0
|
148
|
+
n0 = "0" / "3" / "6" / "9"
|
149
|
+
n1 = "1" / "4" / "7"
|
150
|
+
n2 = "2" / "5" / "8"
|
151
|
+
End
|
152
|
+
|
153
|
+
The above ABNF description represents decimal numbers
|
154
|
+
which are multiples of 3 and the result is follows:
|
155
|
+
|
156
|
+
%r{(?:[0369]|
|
157
|
+
[147](?:[0369]|[147][0369]*[258])*(?:[147][0369]*[147]|[258])|
|
158
|
+
[258][0369]*
|
159
|
+
(?:[147]|
|
160
|
+
[258](?:[0369]|[147][0369]*[258])*(?:[147][0369]*[147]|[258])))*}xi
|
161
|
+
|
162
|
+
A converted regexp can be used to match in place as:
|
163
|
+
|
164
|
+
p /\A#{ABNF.regexp <<'End'}\z/o =~ "::13.1.68.3"
|
165
|
+
IPv6address = "::" /
|
166
|
+
7( hex4 ":" ) hex4 /
|
167
|
+
1*8( hex4 ":" ) ":" /
|
168
|
+
7( hex4 ":" ) ( ":" hex4 ) /
|
169
|
+
6( hex4 ":" ) 1*2( ":" hex4 ) /
|
170
|
+
5( hex4 ":" ) 1*3( ":" hex4 ) /
|
171
|
+
4( hex4 ":" ) 1*4( ":" hex4 ) /
|
172
|
+
3( hex4 ":" ) 1*5( ":" hex4 ) /
|
173
|
+
2( hex4 ":" ) 1*6( ":" hex4 ) /
|
174
|
+
( hex4 ":" ) 1*7( ":" hex4 ) /
|
175
|
+
":" 1*8( ":" hex4 ) /
|
176
|
+
6( hex4 ":" ) IPv4address /
|
177
|
+
6( hex4 ":" ) ":" IPv4address /
|
178
|
+
5( hex4 ":" ) ":" 0*1( hex4 ":" ) IPv4address /
|
179
|
+
4( hex4 ":" ) ":" 0*2( hex4 ":" ) IPv4address /
|
180
|
+
3( hex4 ":" ) ":" 0*3( hex4 ":" ) IPv4address /
|
181
|
+
2( hex4 ":" ) ":" 0*4( hex4 ":" ) IPv4address /
|
182
|
+
( hex4 ":" ) ":" 0*5( hex4 ":" ) IPv4address /
|
183
|
+
"::" 0*6( hex4 ":" ) IPv4address
|
184
|
+
IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
|
185
|
+
hex4 = 1*4HEXDIG
|
186
|
+
End
|
187
|
+
|
188
|
+
## Contributing
|
189
|
+
|
190
|
+
1. Fork it
|
191
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
192
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
193
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
194
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
file "lib/abnf/parser.rb" => ["lib/abnf/parser.y"] do |t|
|
4
|
+
sh "racc -E -o lib/abnf/parser.rb -v lib/abnf/parser.y"
|
5
|
+
end
|
6
|
+
|
7
|
+
task :build_parser => ["lib/abnf/parser.rb"]
|
8
|
+
|
9
|
+
require 'rake/testtask'
|
10
|
+
|
11
|
+
Rake::TestTask.new do |t|
|
12
|
+
t.libs << "lib"
|
13
|
+
t.test_files = FileList['test/*_test.rb']
|
14
|
+
t.ruby_opts = ['-r./test/test_helper.rb']
|
15
|
+
t.verbose = true
|
16
|
+
end
|
17
|
+
|
18
|
+
task :default => [:build_parser, :test]
|
data/abnf.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'abnf/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "abnf"
|
8
|
+
spec.version = Abnf::VERSION
|
9
|
+
spec.authors = ["Steve Klabnik"]
|
10
|
+
spec.email = ["steve@steveklabnik.com"]
|
11
|
+
spec.description = %q{An Augmented Backus Naur form parser in Ruby.}
|
12
|
+
spec.summary = %q{An Augmented Backus Naur form parser in Ruby.}
|
13
|
+
spec.homepage = "https://github.com/steveklabnik/abnf"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "racc"
|
24
|
+
end
|
data/lib/abnf.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
=begin
|
2
|
+
= ABNF
|
3
|
+
|
4
|
+
convert ABNF to Regexp.
|
5
|
+
|
6
|
+
== Example
|
7
|
+
|
8
|
+
# IPv6 [RFC2373]
|
9
|
+
p %r{\A#{ABNF.regexp <<'End'}\z}o =~ "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210"
|
10
|
+
IPv6address = hexpart [ ":" IPv4address ]
|
11
|
+
IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
|
12
|
+
hexpart = hexseq | hexseq "::" [ hexseq ] | "::" [ hexseq ]
|
13
|
+
hexseq = hex4 *( ":" hex4)
|
14
|
+
hex4 = 1*4HEXDIG
|
15
|
+
End
|
16
|
+
|
17
|
+
Note that this is wrong because it doesn't match to "::13.1.68.3".
|
18
|
+
|
19
|
+
# URI-reference [RFC2396]
|
20
|
+
p %r{\A#{ABNF.regexp <<'End'}\z}o
|
21
|
+
URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
|
22
|
+
...
|
23
|
+
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
|
24
|
+
"8" | "9"
|
25
|
+
End
|
26
|
+
|
27
|
+
== ABNF
|
28
|
+
|
29
|
+
=== class methods
|
30
|
+
|
31
|
+
--- ABNF.regexp(abnf_description, start_symbol=nil)
|
32
|
+
converts ((|abnf_description|)) to a Regexp object corresponding with
|
33
|
+
((|start_symbol|)).
|
34
|
+
|
35
|
+
If ((|start_symbol|)) is not specified, first symbol in
|
36
|
+
((|abnf_description|)) is used.
|
37
|
+
|
38
|
+
--- ABNF.regexp_tree(abnf_description, start_symbol=nil)
|
39
|
+
converts ((|abnf_description|)) to a ((<RegexpTree|URL:regexptree.html>)) object corresponding with
|
40
|
+
((|start_symbol|)).
|
41
|
+
|
42
|
+
=== constants
|
43
|
+
|
44
|
+
--- TooComplex
|
45
|
+
is a subclass of StandardError.
|
46
|
+
It is raised when ABNF grammar is too complex to convert to Regexp.
|
47
|
+
|
48
|
+
= Note
|
49
|
+
|
50
|
+
* Wrong ABNF description produces wrong regexp.
|
51
|
+
|
52
|
+
=end
|
53
|
+
|
54
|
+
require 'abnf/abnf'
|
55
|
+
require 'abnf/parser'
|
56
|
+
require 'abnf/corerules'
|
57
|
+
require 'abnf/regexp'
|
data/lib/abnf/abnf.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'tsort'
|
2
|
+
require 'abnf/grammar'
|
3
|
+
|
4
|
+
class ABNF
|
5
|
+
def initialize
|
6
|
+
@names = []
|
7
|
+
@rules = {}
|
8
|
+
@start = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def start_symbol=(name)
|
12
|
+
@start = name
|
13
|
+
end
|
14
|
+
|
15
|
+
def start_symbol
|
16
|
+
return @start if @start
|
17
|
+
raise StandardError.new("no symbol defined") if @names.empty?
|
18
|
+
@names.first
|
19
|
+
end
|
20
|
+
|
21
|
+
def names
|
22
|
+
@names.dup
|
23
|
+
end
|
24
|
+
|
25
|
+
def merge(g)
|
26
|
+
g.each {|name, rhs|
|
27
|
+
self.add(name, rhs)
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def [](name)
|
32
|
+
@rules[name]
|
33
|
+
end
|
34
|
+
|
35
|
+
def []=(name, rhs)
|
36
|
+
@names << name unless @rules.include? name
|
37
|
+
@rules[name] = rhs
|
38
|
+
end
|
39
|
+
|
40
|
+
def add(name, rhs)
|
41
|
+
if @rules.include? name
|
42
|
+
@rules[name] |= rhs
|
43
|
+
else
|
44
|
+
@names << name
|
45
|
+
@rules[name] = rhs
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def include?(name)
|
50
|
+
@rules.include? name
|
51
|
+
end
|
52
|
+
|
53
|
+
def each(&block)
|
54
|
+
@names.each {|name|
|
55
|
+
yield name, @rules[name]
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def delete_unreachable!(starts)
|
60
|
+
rules = {}
|
61
|
+
id_map = {}
|
62
|
+
stack = []
|
63
|
+
starts.each {|name|
|
64
|
+
next if id_map.include? name
|
65
|
+
each_strongly_connected_component_from(name, id_map, stack) {|syms|
|
66
|
+
syms.each {|sym|
|
67
|
+
rules[sym] = @rules[sym] if @rules.include? sym
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
@rules = rules
|
72
|
+
@names.reject! {|name| !@rules.include?(name)}
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
def delete_useless!(starts=nil)
|
77
|
+
if starts
|
78
|
+
starts = [starts] if Symbol === starts
|
79
|
+
delete_unreachable!(starts)
|
80
|
+
end
|
81
|
+
|
82
|
+
useful_names = {}
|
83
|
+
using_names = {}
|
84
|
+
|
85
|
+
@rules.each {|name, rhs|
|
86
|
+
useful_names[name] = true if rhs.useful?(useful_names)
|
87
|
+
rhs.each_var {|n|
|
88
|
+
(using_names[n] ||= {})[name] = true
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
queue = useful_names.keys
|
93
|
+
until queue.empty?
|
94
|
+
n = queue.pop
|
95
|
+
next unless using_names[n]
|
96
|
+
using_names[n].keys.each {|name|
|
97
|
+
if useful_names[name]
|
98
|
+
using_names[n].delete name
|
99
|
+
elsif @rules[name].useful?(useful_names)
|
100
|
+
using_names[n].delete name
|
101
|
+
useful_names[name] = true
|
102
|
+
queue << name
|
103
|
+
end
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
rules = {}
|
108
|
+
@rules.each {|name, rhs|
|
109
|
+
rhs = rhs.subst_var {|n| useful_names[n] ? nil : EmptySet}
|
110
|
+
rules[name] = rhs unless rhs.empty_set?
|
111
|
+
}
|
112
|
+
|
113
|
+
#xxx: raise if some of start symbol becomes empty set?
|
114
|
+
|
115
|
+
@rules = rules
|
116
|
+
@names.reject! {|name| !@rules.include?(name)}
|
117
|
+
self
|
118
|
+
end
|
119
|
+
|
120
|
+
class Alt; def useful?(useful_names) @elts.any? {|e| e.useful?(useful_names)} end end
|
121
|
+
class Seq; def useful?(useful_names) @elts.all? {|e| e.useful?(useful_names)} end end
|
122
|
+
class Rep; def useful?(useful_names) @min == 0 ? true : @elt.useful?(useful_names) end end
|
123
|
+
class Var; def useful?(useful_names) useful_names[@name] end end
|
124
|
+
class Term; def useful?(useful_names) true end end
|
125
|
+
|
126
|
+
include TSort
|
127
|
+
def tsort_each_node(&block)
|
128
|
+
@names.each(&block)
|
129
|
+
end
|
130
|
+
def tsort_each_child(name)
|
131
|
+
return unless @rules.include? name
|
132
|
+
@rules.fetch(name).each_var {|n| yield n}
|
133
|
+
end
|
134
|
+
|
135
|
+
class ABNFError < StandardError; end
|
136
|
+
end
|