abnf 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,16 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - 2.0.0
7
+ - jruby-18mode
8
+ - jruby-19mode
9
+ - rbx-18mode
10
+ - rbx-19mode
11
+ matrix:
12
+ allow_failures:
13
+ - rvm: jruby-18mode
14
+ - rvm: jruby-19mode
15
+ - rvm: rbx-18mode
16
+ - rvm: rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in abnf.gemspec
4
+ gemspec
@@ -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.
@@ -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
@@ -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]
@@ -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
@@ -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'
@@ -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