abnf-parsing 0.2.0

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.
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: []