abnf-parsing 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (8) hide show
  1. checksums.yaml +15 -0
  2. data/README +25 -0
  3. data/Rakefile +40 -0
  4. data/TODO +4 -0
  5. data/lib/abnf.rb +264 -0
  6. data/test/basics.rb +123 -0
  7. data/test/url.rb +206 -0
  8. metadata +52 -0
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ Y2Y2ZTliMTljZGZlYTA3YTJiOWUwMjJkM2FiOWFjM2NmYTFhNDY2OQ==
5
+ data.tar.gz: !binary |-
6
+ NjM4MzZjYjlkOTNkMWE2MWJjMTIxMDE4ODM3MmFjMTAyNzFhNGZkNA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NTJhMWUxZmI4YzBmNGI4MWQzZjc3NjhlODI3NDVjOWE3YmM2NTA3NDBkYzdh
10
+ OWQ5MDQzNzI1ZDY3ZjQ1ZTcyNGUyZjYzN2ZhOWU5N2U1ZTFiYmNjYmU4ZTE3
11
+ NjQzODdhZjQ2ZmJhOTk4NzJmZTdlOWViNjRhMjc4OWY2OTgxOTQ=
12
+ data.tar.gz: !binary |-
13
+ NzAzMzg5MjBlMzk4ODIwYmFlN2JkYzJkMDI5ZTAyYTMxYWNlOWRhODg2MzUy
14
+ MjQwMjZjYzEyNTI5OTA2MzYyZGVkZjg0NGFjMzA1MzA5YTFmZDUyMTA4MjIx
15
+ MTZmY2YwMDQ5ZjYwODgwNGJlNDc1MWNiMTA5MzhlMTkzNjYzYjE=
data/README ADDED
@@ -0,0 +1,25 @@
1
+
2
+ == Overview ==
3
+
4
+ This is a simple library for easily implementing parsers that are specified in Augmented Backus-Naur Form (ABNF).
5
+ Many IETF RFCs specify protocols and other entities using ABNF. This library lets you use those core operators and
6
+ rules almost directly as Ruby objects. An optional block can be specified for a rule. If the rule matches, it will
7
+ pass the match into the block. This makes it possible to build up state as a parse progresses.
8
+
9
+ This version of the library was written to RFC 2234, which is an obsoleted version of the RFC. Not too much
10
+ changed in the subsequent versions however. I'm hoping to do a pass through soon and make sure this library
11
+ is compliant with RFC 5234.
12
+
13
+ Check out: http://tools.ietf.org/html/rfc5234 for more information.
14
+
15
+ == Example ==
16
+
17
+ For a detailed example, tests/url.rb contains a full URL parser using the ABNF grammar from RFC 3986. It parses
18
+ a URL from a string returns a structure containing the components.
19
+
20
+ == Caveats ==
21
+
22
+ * There's no lookahead, so when using Alternate, specify your longest matches first if one of the smaller alternative
23
+ matches could potentially fire.
24
+
25
+ * It works on an 8-bit character basis - at least for now.
@@ -0,0 +1,40 @@
1
+
2
+
3
+
4
+
5
+ require 'rubygems'
6
+ require 'rake'
7
+ require 'rake/testtask'
8
+ require 'rubygems/package_task'
9
+
10
+ Rake::TestTask.new do |t|
11
+ t.libs << "test"
12
+ t.test_files = FileList['test/*.rb']
13
+ t.verbose = true
14
+ end
15
+
16
+ GEM = "abnf-parsing"
17
+
18
+ spec = Gem::Specification.new do |s|
19
+ s.name = GEM
20
+ s.version = "0.2.0"
21
+ s.platform = Gem::Platform::RUBY
22
+ s.has_rdoc = true
23
+ s.extra_rdoc_files = ["README", "TODO"]
24
+ s.summary = "ABNF parsing in Ruby"
25
+ s.description = "A Ruby library for implementing parsers specified with Augmented Backus Naur Form (ABNF)."
26
+ s.authors = ["Rob Day", "Ryan Tecco"]
27
+ s.homepage = "https://github.com/rkday/ruby-abnf"
28
+ s.email = "ruby-abnf@rkd.me.uk"
29
+ s.require_path = "lib"
30
+ s.files = %w{README Rakefile TODO} + Dir.glob("{lib,test}/*")
31
+ end
32
+
33
+ Gem::PackageTask.new(spec) do |pkg|
34
+
35
+ end
36
+
37
+ desc "Create the gemspec file."
38
+ task :gemspec do
39
+ File.open("#{GEM}.gemspec", "w"){|f| f.puts spec.to_ruby}
40
+ end
data/TODO ADDED
@@ -0,0 +1,4 @@
1
+ * ABNF in ABNF. Write an ABNF grammar to parse and create parser objects from an ABNF spec.
2
+ * Test query parsing (with another ABNF parser).
3
+ * On match, call a method (if defined) (or pass to a block like currently (if defined)).
4
+ * Update to RFC 5234?
@@ -0,0 +1,264 @@
1
+
2
+ # ABNF (RFC 2234) parser objects.
3
+
4
+ module ABNF
5
+ class ::Range
6
+ def until_end # &blk
7
+ self.last.times{|i| yield i}
8
+ end
9
+ end
10
+
11
+ class RangeWithInfiniteUpperBound
12
+ def initialize(first)
13
+ @first = first
14
+ end
15
+
16
+ def first
17
+ @first
18
+ end
19
+
20
+ def until_end # &blk
21
+ i = 0
22
+
23
+ while true
24
+ yield i
25
+ i += 1
26
+ end
27
+ end
28
+ end
29
+
30
+ class Stream
31
+ attr_reader :pos
32
+
33
+ def initialize(s, full = nil, pos = 0)
34
+ @s = s
35
+ @full = full.nil? ? s : full
36
+ @pos = pos
37
+ end
38
+
39
+ def first
40
+ @s[0].ord if @s[0]
41
+ end
42
+
43
+ def rest
44
+ Stream.new(@s[1..-1], @full, @pos + 1)
45
+ end
46
+
47
+ def clip(start)
48
+ @full[start...@pos]
49
+ end
50
+ end
51
+
52
+ #
53
+ # Helper Classes
54
+
55
+ class Satisfies
56
+
57
+ # Takes a stream, returns a stream or failure.
58
+ def match(s)
59
+ s.rest if predicate(s.first)
60
+ end
61
+ end
62
+
63
+ class Range < Satisfies
64
+ def initialize(r)
65
+ @r = r
66
+ end
67
+
68
+ def predicate(c)
69
+ @r.member?(c)
70
+ end
71
+ end
72
+
73
+ #
74
+ # Core Operators
75
+
76
+ # Remember to place the longest matches first!
77
+ class Alternate
78
+ def initialize(*choices, &blk)
79
+ @choices = choices
80
+ @blk = blk
81
+ end
82
+
83
+ def set_block(&blk)
84
+ @blk = blk
85
+ return self
86
+ end
87
+
88
+ def match(strm)
89
+ start = strm.pos
90
+
91
+ @choices.each {
92
+ |c|
93
+
94
+ n_strm = c.match(strm);
95
+
96
+ if n_strm
97
+ @blk.call(n_strm.clip(start)) unless @blk.nil?
98
+
99
+ return n_strm
100
+ end
101
+ }
102
+
103
+ return nil
104
+ end
105
+ end
106
+
107
+ class AlternateChars < Alternate
108
+ def initialize(string, &blk)
109
+ @choices = string.each_char.map { |c| Char.new(c) }
110
+ @blk = blk
111
+ end
112
+ end
113
+
114
+ class Concat
115
+ def initialize(*choices, &blk)
116
+ @choices = choices
117
+ @blk = blk
118
+ end
119
+
120
+ def set_block(&blk)
121
+ @blk = blk
122
+ return self
123
+ end
124
+
125
+ def match(strm)
126
+ c_strm = strm
127
+ start = c_strm.pos
128
+
129
+ @choices.each {
130
+ |c|
131
+
132
+ c_strm = c.match(c_strm)
133
+
134
+ return nil if c_strm.nil?
135
+ }
136
+
137
+ @blk.call(c_strm.clip(start)) unless @blk.nil?
138
+
139
+ return c_strm
140
+ end
141
+ end
142
+
143
+ class Literal < Concat
144
+ def initialize(string, &blk)
145
+ @choices = string.each_char.map { |c| Char.new(c) }
146
+ @blk = blk
147
+ end
148
+ end
149
+
150
+ class Repetition
151
+ # Spec: range (between), integer (exact), [:at_most, N], [:at_least, N], :any (zero or more)
152
+ def initialize(spec, what, &blk)
153
+ @spec = spec
154
+ @what = what
155
+ @blk = blk
156
+ end
157
+
158
+ def set_block(&blk)
159
+ @blk = blk
160
+ return self
161
+ end
162
+
163
+ def match(strm)
164
+ c_strm = strm
165
+ start = strm.pos
166
+
167
+ r = \
168
+ case @spec
169
+ when Array # :at_least, :at_most
170
+ option, i = @spec
171
+
172
+ if option == :at_most
173
+ (0..i)
174
+ elsif option == :at_least
175
+ RangeWithInfiniteUpperBound.new(i)
176
+ end
177
+ when Integer # Exact
178
+ @spec..@spec
179
+ when ::Range # Between
180
+ @spec
181
+ when Symbol # Any (zero or more)
182
+ RangeWithInfiniteUpperBound.new(0)
183
+ end
184
+
185
+ r.until_end {
186
+ |i|
187
+
188
+ tried = @what.match(c_strm)
189
+
190
+ if tried.nil?
191
+ if i < r.first
192
+ return nil
193
+ else
194
+ break
195
+ end
196
+ else
197
+ c_strm = tried
198
+ end
199
+ }
200
+
201
+ @blk.call(c_strm.clip(start)) unless @blk.nil?
202
+
203
+ c_strm
204
+ end
205
+ end
206
+
207
+ class Optional < Repetition
208
+ def initialize(what)
209
+ super([:at_most, 1], what)
210
+ end
211
+ end
212
+
213
+ class OptionalConcat < Optional
214
+ def initialize(*what)
215
+ super(Concat.new(*what))
216
+ end
217
+ end
218
+
219
+ #
220
+ # Core Rules
221
+ #
222
+ # I tried to preserve the RFC names where possible.
223
+
224
+ class Char < Satisfies
225
+ def initialize(c)
226
+ @char = c.ord
227
+ end
228
+
229
+ def predicate(c)
230
+ c == @char
231
+ end
232
+ end
233
+
234
+ class Alpha < Alternate; def initialize; super(Range.new(0x41..0x5A), Range.new(0x61..0x7A)) end end
235
+ class AsciiChar < Range; def initialize; super(0x1..0x7F) end end # char
236
+ class Bit < Alternate; def initialize; super(Char.new(?0), Char.new(?1)) end end
237
+ class CR < Char; def initialize; super(0x0D) end end # carriage return
238
+ class LF < Char; def initialize; super(0x0A) end end # line feed
239
+ class CRLF < Concat; def initialize; super(CR.new, LF.new) end end # Internet standard newline
240
+ class Ctl < Alternate; def initialize; super(Range.new(0..0x1F), Char.new(0x7F)) end end # control characters
241
+ class Digit < Range; def initialize; super(0x30..0x39) end end
242
+ class DQuote < Char; def initialize; super(0x22) end end # double quote
243
+ class HTab < Char; def initialize; super(0x9) end end # horizontal tab
244
+ class SP < Char; def initialize; super(0x20) end end # space
245
+ class Octet < Range; def initialize; super(0..255) end end # any 8-bit data value
246
+ class VChar < Range; def initialize; super(0x21..0x7E) end end # visible (printing) characters
247
+ class WSP < Alternate; def initialize; super(SP.new, HTab.new) end end # whitespace
248
+
249
+ class HexDigit < Alternate
250
+ def initialize
251
+ super(Digit.new, Range.new(0x41..0x46), Range.new(0x61..0x66))
252
+ end
253
+ end
254
+
255
+ class LWSP < Repetition # Linear white space (past newline)
256
+ def initialize
257
+ super(:any, Alternate.new(WSP.new, Concat.new(CRLF.new, WSP.new)))
258
+ end
259
+ end
260
+
261
+ def parse(parser, str)
262
+ parser.match(Stream.new(str))
263
+ end
264
+ end
@@ -0,0 +1,123 @@
1
+ $: << File.join(File.dirname(__FILE__), "../lib")
2
+
3
+ require 'test/unit'
4
+ require 'abnf'
5
+
6
+ class ABNFTest < Test::Unit::TestCase
7
+ include ABNF
8
+
9
+ def assert_blank(s)
10
+ assert s.nil? || (s.class == String && s.empty?)
11
+ end
12
+
13
+ def test_variable_repetition
14
+ assert parse(Repetition.new(3, Alpha.new), "abcdefg")
15
+ assert parse(Repetition.new(1, Alpha.new), "a12345")
16
+ assert_nil parse(Repetition.new(3, Alpha.new), "ab123")
17
+ assert_nil parse(Repetition.new(1, Alpha.new), "123456")
18
+ assert parse(Repetition.new(1..3, Alpha.new), "ab1234")
19
+ assert_nil parse(Repetition.new(3..5, Alpha.new), "ab1234")
20
+ assert parse(Repetition.new(0, Alpha.new), "12345")
21
+ assert parse(Repetition.new(0..4, Alpha.new), "a1234")
22
+ assert parse(Repetition.new(0..1, Alpha.new), "1234")
23
+ assert parse(Repetition.new(0..1, Alpha.new), "a1234")
24
+ assert parse(Repetition.new(0..1, Alpha.new), "ab1234")
25
+ assert parse(Repetition.new([:at_least, 2], Alpha.new){|r| assert_equal "ab", r}, "ab1234")
26
+ assert parse(Repetition.new([:at_least, 2], Alpha.new){|r| assert_equal "abcde", r}, "abcde12")
27
+ assert parse(Repetition.new([:at_most, 3], Alpha.new){|r| assert_equal "abc", r}, "abcdefgh")
28
+ assert parse(Repetition.new([:at_most, 3], Alpha.new), "12345")
29
+ assert parse(Repetition.new([:at_most, 3], Alpha.new){|r| assert_equal "ab", r}, "ab12345")
30
+ assert parse(Concat.new(Optional.new(Digit.new), Alpha.new), "a")
31
+ assert parse(Concat.new(Optional.new(Digit.new), Alpha.new), "1a")
32
+ assert parse(Repetition.new(:any, Alpha.new), "12345")
33
+ assert parse(Repetition.new(:any, Alpha.new), "abcdefg12345")
34
+ end
35
+
36
+ def test_single_char_parse
37
+ parser = Char.new(?-)
38
+ assert_nil parse(parser, ")")
39
+ assert parse(parser, "-")
40
+ end
41
+
42
+ def test_alternate_char_parse
43
+ parser = AlternateChars.new("abcdef")
44
+ assert_nil parse(parser, ")")
45
+ assert_nil parse(parser, "-")
46
+ assert parse(parser, "a")
47
+ assert parse(parser, "b")
48
+ assert parse(parser, "c")
49
+ assert parse(parser, "d")
50
+ assert parse(parser, "e")
51
+ assert parse(parser, "f")
52
+ assert_nil parse(parser, "g")
53
+ end
54
+
55
+ def test_literal_parse
56
+ parser = Literal.new("abcdef")
57
+
58
+ assert parse(parser, "abcdef")
59
+
60
+ assert_nil parse(parser, ")")
61
+ assert_nil parse(parser, "-")
62
+ assert_nil parse(parser, "a")
63
+ assert_nil parse(parser, "abcd")
64
+ end
65
+
66
+ def test_set_block
67
+ output = nil
68
+ parser = Literal.new("abcdef")
69
+
70
+ assert parse(parser, "abcdef")
71
+ assert_nil output
72
+
73
+ parser.set_block {|m| output = m}
74
+
75
+ assert parse(parser, "abcdef")
76
+
77
+ assert (output == "abcdef")
78
+ end
79
+
80
+ def test_optional_concat_parse
81
+ # Parser represents ["a" "b"] "c"
82
+ parser = Concat.new(OptionalConcat.new(Char.new(?a), Char.new(?b)), Char.new(?c))
83
+
84
+ assert parse(parser, "abc")
85
+ assert parse(parser, "c")
86
+
87
+ assert_nil parse(parser, ")")
88
+ assert_nil parse(parser, "-")
89
+ assert_nil parse(parser, "a")
90
+ assert_nil parse(parser, "ab")
91
+ assert_nil parse(parser, "ac")
92
+ end
93
+
94
+ def test_example_1
95
+
96
+ # Phone Number
97
+ p1 = Concat.new(Repetition.new(3, Digit.new), Char.new(?-), Repetition.new(4, Digit.new))
98
+ assert_nil parse(p1, "22-3452")
99
+ assert_nil parse(p1, "hello world")
100
+ assert_nil parse(p1, "4293-603")
101
+ assert_nil parse(p1, "")
102
+ assert_nil parse(p1, "4293-3603")
103
+ assert_nil parse(p1, "734-904-2840")
104
+ assert parse(p1, "429-3603")
105
+
106
+ # Phone Number w/ optional Area Code, same delimiters
107
+ phone_number = Concat.new(Repetition.new(3, Digit.new), Char.new(?-), Repetition.new(4, Digit.new)){|pn| assert_equal "904-2840", pn}
108
+ area_code = Concat.new(Repetition.new(3, Digit.new), Char.new(?-))
109
+ p2 = Alternate.new(Concat.new(area_code, phone_number){|f| assert_equal "734-904-2840", f}, phone_number)
110
+ assert_nil parse(p2, "22-3452")
111
+ assert_nil parse(p2, "hello world")
112
+ assert_nil parse(p2, "4293-603")
113
+ assert_nil parse(p2, "")
114
+ assert_nil parse(p2, "4293-3603")
115
+ assert parse(p2, "904-2840")
116
+ assert parse(p2, "734-904-2840")
117
+
118
+ # Phone Number w/ optional Area Code, different delimiters
119
+ area_code = Concat.new(Char.new(?(), Repetition.new(3, Digit.new), Char.new(?)))
120
+ p3 = Concat.new(Optional.new(area_code), phone_number)
121
+ assert parse(p3, "904-2840")
122
+ end
123
+ end
@@ -0,0 +1,206 @@
1
+ $: << File.join(File.dirname(__FILE__), "../lib")
2
+
3
+ require 'test/unit'
4
+ require 'abnf'
5
+
6
+ # The rules and state for a URL parser. Taken almost verbatim from RFC 3986.
7
+ # There is no support for IP-literal, IPvFuture, or IPv6address.
8
+ # These rules are not implemented: absolute_uri, path, reserved and gen_delims although there is
9
+ # no functional reason that they couldn't be.
10
+
11
+ class URLParser
12
+ include ABNF
13
+
14
+ URL = Struct.new("URL", :fragment, :host, :path, :port, :scheme, :user)
15
+
16
+ # API
17
+
18
+ def parse(str)
19
+ @result = Struct::URL.new
20
+
21
+ if uri_reference.match(Stream.new(str))
22
+ @result
23
+ else
24
+ raise RuntimeError.new("Parse error")
25
+ end
26
+ end
27
+
28
+ # Rules
29
+
30
+ def scheme
31
+ Concat.new(Alpha.new, Repetition.new(:any, Alternate.new(Alpha.new, Digit.new,
32
+ Char.new(?+), Char.new(?-), Char.new(?.)))){|s| @result.scheme = s}
33
+ end
34
+
35
+ def dec_octet
36
+ Alternate.new(Concat.new(Char.new(?2), Char.new(?5), Range.new(0x30..0x35)), # 250-255
37
+ Concat.new(Char.new(?2), Range.new(0x30..0x34), Digit.new), # 200-249
38
+ Concat.new(Char.new(?1), Repetition.new(2, Digit.new)), # 100-199
39
+ Concat.new(Range.new(0x31..0x39), Digit.new), # 10-99
40
+ Digit.new) # 0-9
41
+ end
42
+
43
+ def pct_encoded
44
+ Concat.new(Char.new(?%), HexDigit.new, HexDigit.new)
45
+ end
46
+
47
+ def sub_delims
48
+ Alternate.new(Char.new(?!), Char.new(?$), Char.new(?&), Char.new(?'), Char.new(?(),
49
+ Char.new(?)), Char.new(?*), Char.new(?+), Char.new(?,), Char.new(?;), Char.new(?=))
50
+ end
51
+
52
+ def unreserved
53
+ Alternate.new(Alpha.new, Digit.new, Char.new(?-), Char.new(?.), Char.new(?_), Char.new(?~))
54
+ end
55
+
56
+ def pchar
57
+ Alternate.new(unreserved, pct_encoded, sub_delims, Char.new(?:), Char.new(?@))
58
+ end
59
+
60
+ def ipv4_address
61
+ Concat.new(dec_octet, Char.new(?.), dec_octet, Char.new(?.), dec_octet, Char.new(?.), dec_octet)
62
+ end
63
+
64
+ def reg_name
65
+ Repetition.new(:any, Alternate.new(unreserved, pct_encoded, sub_delims))
66
+ end
67
+
68
+ def host
69
+ Alternate.new(ipv4_address, reg_name){|host| @result.host = host}
70
+ end
71
+
72
+ def port
73
+ Repetition.new(:any, Digit.new){|port| @result.port = port.to_i}
74
+ end
75
+
76
+ def userinfo
77
+ Repetition.new(:any, Alternate.new(unreserved, pct_encoded, sub_delims, Char.new(?:))){|u| @result.user = u}
78
+ end
79
+
80
+ def authority
81
+ Concat.new(Optional.new(Concat.new(userinfo, Char.new(?@))), host, Optional.new(Concat.new(Char.new(?:), port)))
82
+ end
83
+
84
+ def segment
85
+ Repetition.new(:any, pchar)
86
+ end
87
+
88
+ def segment_nz
89
+ Repetition.new([:at_least, 1], pchar)
90
+ end
91
+
92
+ def segment_nz_nc # non-zero-length segment without any colon
93
+ Repetition.new([:at_least, 1], Alternate.new(unreserved, pct_encoded, sub_delims, Char.new(?@)))
94
+ end
95
+
96
+ def path_abempty
97
+ Repetition.new(:any, Concat.new(Char.new(?/), segment)){|path| @result.path = path}
98
+ end
99
+
100
+ def path_absolute
101
+ Concat.new(Char.new(?/),
102
+ Optional.new(Concat.new(segment_nz, Repetition.new(:any, Concat.new(Char.new(?/), segment))))){|path| @result.path = path}
103
+ end
104
+
105
+ def path_empty
106
+ Repetition.new(0, pchar){|path| @result.path = path}
107
+ end
108
+
109
+ def path_noscheme
110
+ Concat.new(segment_nz_nc, Repetition.new(:any, Concat.new(Char.new(?/), segment)))
111
+ end
112
+
113
+ def path_rootless
114
+ Concat.new(segment_nz, Repetition.new(:any, Concat.new(Char.new(?/), segment))){|path| @result.path = path}
115
+ end
116
+
117
+ def hier_part
118
+ Alternate.new(Concat.new(Char.new(?/), Char.new(?/), authority, path_abempty),
119
+ path_absolute,
120
+ path_rootless,
121
+ path_empty)
122
+ end
123
+
124
+ def query
125
+ Repetition.new(:any, Alternate.new(pchar, Char.new(?/), Char.new(??))){|query| puts query}
126
+ end
127
+
128
+ def fragment
129
+ Repetition.new(:any, Alternate.new(pchar, Char.new(?/), Char.new(??))){|fragment| @result.fragment = fragment}
130
+ end
131
+
132
+ def uri
133
+ Concat.new(scheme,
134
+ Char.new(?:),
135
+ hier_part,
136
+ Optional.new(Concat.new(Char.new(??), query)),
137
+ Optional.new(Concat.new(Char.new(?#), fragment)))
138
+ end
139
+
140
+ def relative_part
141
+ Alternate.new(Concat.new(Char.new(?/), Char.new(?/), authority, path_abempty),
142
+ path_absolute,
143
+ path_noscheme,
144
+ path_empty)
145
+ end
146
+
147
+ def relative_ref
148
+ Concat.new(relative_part,
149
+ Optional.new(Concat.new(Char.new(??), query)),
150
+ Optional.new(Concat.new(Char.new(?#), fragment)))
151
+ end
152
+
153
+ def uri_reference
154
+ Alternate.new(uri, relative_ref)
155
+ end
156
+ end
157
+
158
+ class ABNFTest < Test::Unit::TestCase
159
+ include ABNF
160
+
161
+ def test_url_parser
162
+
163
+ #
164
+ # URLs
165
+
166
+ u = URLParser.new.parse("http://karmalab.org")
167
+ assert u
168
+ assert_equal "http", u.scheme
169
+ assert_equal "karmalab.org", u.host
170
+ assert_nil u.port
171
+ assert_equal "", u.path
172
+
173
+ u = URLParser.new.parse("http://rt@karmalab.org:9001")
174
+ assert u
175
+ assert_equal "http", u.scheme
176
+ assert_equal "karmalab.org", u.host
177
+ assert_equal 9001, u.port
178
+ assert_equal "rt", u.user
179
+ assert_equal "", u.path
180
+
181
+ u = URLParser.new.parse("file:///tmp/file.txt")
182
+ assert u
183
+ assert_equal "file", u.scheme
184
+ assert_equal "/tmp/file.txt", u.path
185
+ assert_blank u.host
186
+ assert_blank u.port
187
+ assert_blank u.user
188
+
189
+ u = URLParser.new.parse("http://triggit.com/j?u=rt10880&p=http://lambda/#fragment16")
190
+ assert u
191
+ assert_equal "http", u.scheme
192
+ assert_equal "triggit.com", u.host
193
+ assert_equal "/j", u.path
194
+ assert_equal "fragment16", u.fragment
195
+
196
+ u = URLParser.new.parse("about:config")
197
+ assert u
198
+ assert_equal "about", u.scheme
199
+ assert_equal "config", u.path
200
+
201
+ u = URLParser.new.parse("/path/to/file.html#fr23-1")
202
+ assert u
203
+ assert_equal "/path/to/file.html", u.path
204
+ assert_equal "fr23-1", u.fragment
205
+ end
206
+ end
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: abnf-parsing
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Rob Day
8
+ - Ryan Tecco
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-03-09 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: A Ruby library for implementing parsers specified with Augmented Backus
15
+ Naur Form (ABNF).
16
+ email: ruby-abnf@rkd.me.uk
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files:
20
+ - README
21
+ - TODO
22
+ files:
23
+ - README
24
+ - Rakefile
25
+ - TODO
26
+ - lib/abnf.rb
27
+ - test/basics.rb
28
+ - test/url.rb
29
+ homepage: https://github.com/rkday/ruby-abnf
30
+ licenses: []
31
+ metadata: {}
32
+ post_install_message:
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ requirements: []
47
+ rubyforge_project:
48
+ rubygems_version: 2.2.2
49
+ signing_key:
50
+ specification_version: 4
51
+ summary: ABNF parsing in Ruby
52
+ test_files: []