parser_combinator 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bb0d5aae337910defc3d0eb3a61be9a37d99f5b5
4
+ data.tar.gz: 0513a0c7ca661716f31c7a1dc8561c74d6330cce
5
+ SHA512:
6
+ metadata.gz: 21b5c9876e6d8a7bb88b17fe07ab59422b7e8381a6b66a47bc66aa105084fe924c22f7e532cd6dba09288a79b7f6b9002bcb462eacfc739a21a167e1c542bd0f
7
+ data.tar.gz: c209e1110f96a141e70b20c97b1ba944b58a303e43443e186d32376c2d1ac3650f0d35f571551c25936ee3e85f2c2582c648705e20cfb1ae741858c9dc02bcc9
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in parser_combinator.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Kensuke Sawada
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,153 @@
1
+ # ParserCombinator
2
+
3
+ Yet another class-base parser combinator (monadic parser) library.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'parser_combinator'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install parser_combinator
20
+
21
+ ## Basics
22
+
23
+ ### Item class
24
+ Abstraction of Char (in Ruby, it is String of 1-length).
25
+ Parsing charactors through this abstraction, you can handle meta-info of source-string such as file-name, line-number, column-number.
26
+
27
+ ### Items class
28
+ Abstraction of String. This is Array of Item.
29
+
30
+ ## Usage
31
+
32
+ ### Basic Example
33
+ ```ruby
34
+ require 'parser_combinator/string_parser'
35
+
36
+ class MyParser < ParserCombinator::StringParser
37
+ parser :noun do
38
+ str("I") | str("you") | str("it")
39
+ end
40
+
41
+ parser :verb do
42
+ str("love") | str("hate") | str("live") | str("die")
43
+ end
44
+
45
+ parser :token do |p|
46
+ many(str("\s")) > p < many(str("\s"))
47
+ end
48
+
49
+ parser :sentence_way1 do
50
+ token(noun) >> proc{|n1|
51
+ token(verb) >> proc{|v|
52
+ token(noun) >> proc{|n2|
53
+ ok("You said, '#{n1} #{v} #{n2}'")
54
+ }}}
55
+ end
56
+
57
+ parser :sentence_way2 do
58
+ seq(token(noun), token(verb), token(noun)).map do |x|
59
+ "You said, '#{x[0]} #{x[1]} #{x[2]}'"
60
+ end
61
+ end
62
+
63
+ parser :sentence_way3 do
64
+ seq(token(noun).name(:a), token(verb).name(:b), token(noun).name(:c)).map do |x|
65
+ "You said, '#{x[:a]} #{x[:b]} #{x[:c]}'"
66
+ end
67
+ end
68
+ end
69
+
70
+ result = MyParser.sentence_way1.parse_from_string("I love you")
71
+ puts result.parsed # => You said, 'I love you.'
72
+
73
+ result = MyParser.sentence_way2.parse_from_string("I love you")
74
+ puts result.parsed # => You said, 'I love you.'
75
+
76
+ result = MyParser.sentence_way3.parse_from_string("I love you")
77
+ puts result.parsed # => You said, 'I love you.'
78
+
79
+ ```
80
+
81
+ ### Error Handling
82
+ `^` is error handling version of `|`.
83
+ ```ruby
84
+ require 'parser_combinator/string_parser'
85
+
86
+ class MyParser < ParserCombinator::StringParser
87
+ parser :love_sentence do
88
+ str("I") > str("\s") > str("love") > str("you").onfail("Who do you love?")
89
+ end
90
+
91
+ parser :hate_sentence do
92
+ str("I") > str("\s") > str("hate") > str("you").onfail("Who do you hate?")
93
+ end
94
+
95
+ parser :sentence do
96
+ love_sentence ^ hate_sentence
97
+ end
98
+ end
99
+
100
+ result = MyParser.sentence.parse_from_string("I love")
101
+ puts result.status.message # => Who do you love?
102
+
103
+ result = MyParser.sentence.parse_from_string("I hate")
104
+ puts result.status.message # => Who do you hate?
105
+
106
+ result = MyParser.sentence.parse_from_string("I am laughing")
107
+ puts result.status == nil # => true
108
+ ```
109
+
110
+ ### Recursive parsing and Left recursion
111
+ ```ruby
112
+ require 'parser_combinator/string_parser'
113
+
114
+ class MyParser < ParserCombinator::StringParser
115
+ parser :expression do
116
+ add_sub
117
+ end
118
+
119
+ parser :add_sub do
120
+ add_op = str("+").map{ proc{|l, r| l + r}}
121
+ sub_op = str("-").map{ proc{|l, r| l - r}}
122
+ binopl(mul_div, add_op | sub_op)
123
+ end
124
+
125
+ parser :mul_div do
126
+ mul_op = str("*").map{ proc{|l, r| l * r}}
127
+ div_op = str("/").map{ proc{|l, r| l / r}}
128
+ binopl(integer | parenth, mul_op | div_op)
129
+ end
130
+
131
+ parser :integer do
132
+ many1(digit).map{|x| x.map{|i| i.item}.join.to_i}
133
+ end
134
+
135
+ parser :parenth do
136
+ str("(") > expression < str(")")
137
+ end
138
+ end
139
+
140
+ result = MyParser.expression.parse_from_string("(1+2)*3+10/2")
141
+ puts result.parsed # => 14
142
+
143
+ result = MyParser.expression.parse_from_string("3-2-1")
144
+ puts result.parsed # => 0
145
+ ```
146
+
147
+ ## Contributing
148
+
149
+ 1. Fork it ( https://github.com/sawaken/parser_combinator/fork )
150
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
151
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
152
+ 4. Push to the branch (`git push origin my-new-feature`)
153
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/examples/basic.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'parser_combinator/string_parser'
2
+
3
+ class MyParser < ParserCombinator::StringParser
4
+ parser :noun do
5
+ str("I") | str("you") | str("it")
6
+ end
7
+
8
+ parser :verb do
9
+ str("love") | str("hate") | str("live") | str("die")
10
+ end
11
+
12
+ parser :token do |p|
13
+ many(str("\s")) > p < many(str("\s"))
14
+ end
15
+
16
+ parser :sentence_way1 do
17
+ token(noun) >> proc{|n1|
18
+ token(verb) >> proc{|v|
19
+ token(noun) >> proc{|n2|
20
+ ok("You said, '#{n1} #{v} #{n2}'")
21
+ }}}
22
+ end
23
+
24
+ parser :sentence_way2 do
25
+ seq(token(noun), token(verb), token(noun)).map do |x|
26
+ "You said, '#{x[0]} #{x[1]} #{x[2]}'"
27
+ end
28
+ end
29
+
30
+ parser :sentence_way3 do
31
+ seq(token(noun).name(:a), token(verb).name(:b), token(noun).name(:c)).map do |x|
32
+ "You said, '#{x[:a]} #{x[:b]} #{x[:c]}'"
33
+ end
34
+ end
35
+ end
36
+
37
+ result = MyParser.sentence_way1.parse_from_string("I love you")
38
+ puts result.parsed # => You said, 'I love you.'
39
+
40
+ result = MyParser.sentence_way2.parse_from_string("I love you")
41
+ puts result.parsed # => You said, 'I love you.'
42
+
43
+ result = MyParser.sentence_way3.parse_from_string("I love you")
44
+ puts result.parsed # => You said, 'I love you.'
@@ -0,0 +1,24 @@
1
+ require 'parser_combinator/string_parser'
2
+
3
+ class MyParser < ParserCombinator::StringParser
4
+ parser :love_sentence do
5
+ str("I") > str("\s") > str("love") > str("you").onfail("Who do you love?")
6
+ end
7
+
8
+ parser :hate_sentence do
9
+ str("I") > str("\s") > str("hate") > str("you").onfail("Who do you hate?")
10
+ end
11
+
12
+ parser :sentence do
13
+ love_sentence ^ hate_sentence
14
+ end
15
+ end
16
+
17
+ result = MyParser.sentence.parse_from_string("I love")
18
+ puts result.status.message # => Who do you love?
19
+
20
+ result = MyParser.sentence.parse_from_string("I hate")
21
+ puts result.status.message # => Who do you hate?
22
+
23
+ result = MyParser.sentence.parse_from_string("I am laughing")
24
+ puts result.status == nil # => true
@@ -0,0 +1,33 @@
1
+ require 'parser_combinator/string_parser'
2
+
3
+ class MyParser < ParserCombinator::StringParser
4
+ parser :expression do
5
+ add_sub
6
+ end
7
+
8
+ parser :add_sub do
9
+ add_op = str("+").map{ proc{|l, r| l + r}}
10
+ sub_op = str("-").map{ proc{|l, r| l - r}}
11
+ binopl(mul_div, add_op | sub_op)
12
+ end
13
+
14
+ parser :mul_div do
15
+ mul_op = str("*").map{ proc{|l, r| l * r}}
16
+ div_op = str("/").map{ proc{|l, r| l / r}}
17
+ binopl(integer | parenth, mul_op | div_op)
18
+ end
19
+
20
+ parser :integer do
21
+ many1(digit).map{|x| x.map{|i| i.item}.join.to_i}
22
+ end
23
+
24
+ parser :parenth do
25
+ str("(") > expression < str(")")
26
+ end
27
+ end
28
+
29
+ result = MyParser.expression.parse_from_string("(1+2)*3+10/2")
30
+ puts result.parsed # => 14
31
+
32
+ result = MyParser.expression.parse_from_string("3-2-1")
33
+ puts result.parsed # => 0
@@ -0,0 +1,49 @@
1
+ require "parser_combinator"
2
+
3
+ class ParserCombinator
4
+ class StringParser < ParserCombinator
5
+ def self.convert_string_into_items(string, document_name)
6
+ integers = (1..100000).lazy
7
+ items = string.each_line.zip(integers).map{|line, line_number|
8
+ line.chars.zip(integers).map{|char, column_number|
9
+ Item.new(char, :document_name => document_name, :line_number => line_number, :column_number => column_number)
10
+ }
11
+ }.flatten
12
+ return Items.new(items)
13
+ end
14
+
15
+ def parse_from_string(input_string, document_name="anonymous")
16
+ parse(self.class.convert_string_into_items(input_string, document_name))
17
+ end
18
+
19
+ def self.char(char)
20
+ sat{|c| c == char}
21
+ end
22
+
23
+ def self.notchar(char)
24
+ sat{|c| c != char}
25
+ end
26
+
27
+ def self.str(object)
28
+ seq(*object.to_s.chars.map{|c| char(c)}) >> proc{|items|
29
+ ok Items.new(items.to_a)
30
+ }
31
+ end
32
+
33
+ def self.lower_alpha
34
+ sat{|c| "a" <= c && c <= "z"}
35
+ end
36
+
37
+ def self.upper_alpha
38
+ sat{|c| "A" <= c && c <= "Z"}
39
+ end
40
+
41
+ def self.digit
42
+ sat{|c| "0" <= c && c <= "9"}
43
+ end
44
+
45
+ def self.pdigit
46
+ sat{|c| "1" <= c && c <= "9"}
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ class ParserCombinator
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,314 @@
1
+ require "parser_combinator/version"
2
+
3
+ class ParserCombinator
4
+ ParserCombinatorError = Class.new(RuntimeError)
5
+
6
+ class Ok
7
+ attr_reader :parsed, :rest
8
+ def initialize(parsed, rest)
9
+ @parsed, @rest = parsed, rest
10
+ end
11
+ end
12
+
13
+ class Fail
14
+ attr_reader :status
15
+ def initialize(status=nil)
16
+ @status = status
17
+ end
18
+ end
19
+
20
+ class StandardFailStatus
21
+ attr_reader :message, :rest
22
+ def initialize(message, rest)
23
+ @message, @rest = message, rest
24
+ end
25
+ end
26
+
27
+ class Item
28
+ attr_reader :item, :tag
29
+
30
+ def initialize(item, tag)
31
+ @item, @tag = item, tag
32
+ end
33
+
34
+ def to_s
35
+ item.to_s
36
+ end
37
+
38
+ def inspect
39
+ "Item {item = #{item}, tag = #{tag}}"
40
+ end
41
+ end
42
+
43
+ class Items < Array
44
+ def head
45
+ self.first
46
+ end
47
+
48
+ def rest
49
+ self.drop(1)
50
+ end
51
+
52
+ def to_s
53
+ self.map(&:to_s).join
54
+ end
55
+
56
+ def inspect
57
+ "Items [#{self.map(&:inspect).join(",\n")}]"
58
+ end
59
+ end
60
+
61
+ class ParsedSeq
62
+ def initialize(seq)
63
+ @seq = seq
64
+ end
65
+
66
+ def to_a
67
+ @seq.map{|e| e[:entity]}
68
+ end
69
+
70
+ def to_h
71
+ @seq.select{|e| e[:name]}.map{|e| [e[:name], e[:entity]]}.to_h
72
+ end
73
+
74
+ def self.empty
75
+ new([])
76
+ end
77
+
78
+ def cons(entity, name)
79
+ self.class.new([{:entity => entity, :name => name}] + @seq)
80
+ end
81
+
82
+ def [](key)
83
+ case key
84
+ when Integer
85
+ if 0 <= key && key < @seq.length
86
+ @seq[key][:entity]
87
+ else
88
+ raise "out of bounds for ParsedSeq"
89
+ end
90
+ else
91
+ if e = @seq.find{|e| e[:name] == key}
92
+ e[:entity]
93
+ else
94
+ raise "key #{key} is not found in ParsedSeq"
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ attr_accessor :parser_proc
101
+ attr_reader :parser_name
102
+ def initialize(status_handler=nil, name=nil &proc)
103
+ @status_handler = status_handler
104
+ @parser_name = name
105
+ @parser_proc = proc
106
+ end
107
+
108
+ def parse(items)
109
+ result = @parser_proc.call(items)
110
+ if result.is_a?(Fail) && @status_handler
111
+ result.class.new(@status_handler.call(items, result.status))
112
+ else
113
+ result
114
+ end
115
+ end
116
+
117
+ def onfail(message=nil, ifnotyet=false, &status_handler)
118
+ raise "Only eihter message or fail_handler can be specified" if message && status_handler
119
+ if message
120
+ onfail{|items, status| status == nil ? StandardFailStatus.new(message, items) : status}
121
+ elsif status_handler
122
+ self.class.new(status_handler, @parser_name, &parser_proc)
123
+ else
124
+ self
125
+ end
126
+ end
127
+
128
+ def name(new_name)
129
+ self.class.new(@status_handler, new_name, &parser_proc)
130
+ end
131
+
132
+ def map(&mapping)
133
+ self >> proc{|x| self.class.ok(mapping.call(x))}
134
+ end
135
+
136
+ def >>(proc)
137
+ self.class.so_then(self, &proc)
138
+ end
139
+
140
+ def |(other)
141
+ self.class.either(self, other)
142
+ end
143
+
144
+ def ^(other)
145
+ self.class.either_fail(self, other)
146
+ end
147
+
148
+ def >(other)
149
+ self.class.discardl(self, other)
150
+ end
151
+
152
+ def <(other)
153
+ self.class.discardr(self, other)
154
+ end
155
+
156
+ # CoreCombinator
157
+ # --------------------
158
+
159
+ def self.ok(object)
160
+ new{|i| Ok.new(object, i)}
161
+ end
162
+
163
+ def self.fail(status=nil)
164
+ new{|i| Fail.new(status)}
165
+ end
166
+
167
+ def self.so_then(parser, &continuation_proc)
168
+ new{|i|
169
+ case result = parser.parse(i)
170
+ when Fail
171
+ result
172
+ when Ok
173
+ continuation_proc.call(result.parsed).parse(result.rest)
174
+ end
175
+ }
176
+ end
177
+
178
+ def self.either(parser1, parser2)
179
+ new{|i|
180
+ case result1 = parser1.parse(i)
181
+ when Fail
182
+ parser2.parse(i)
183
+ when Ok
184
+ result1
185
+ end
186
+ }
187
+ end
188
+
189
+ def self.either_fail(parser1, parser2)
190
+ new{|i|
191
+ case result1 = parser1.parse(i)
192
+ when Fail
193
+ if result1.status == nil
194
+ parser2.parse(i)
195
+ else
196
+ result1
197
+ end
198
+ when Ok
199
+ result1
200
+ end
201
+ }
202
+ end
203
+
204
+ def self.item
205
+ new{|i| i.size == 0 ? Fail.new : Ok.new(i.head, i.rest)}
206
+ end
207
+
208
+ def self.end_of_input
209
+ new{|i| i.size == 0 ? Ok.new(nil, i) : Fail.new}
210
+ end
211
+
212
+ def self.sat(&item_cond_proc)
213
+ item >> proc{|i|
214
+ item_cond_proc.call(i.item) ? ok(i) : fail
215
+ }
216
+ end
217
+
218
+ # UtilCombinator
219
+ # --------------------
220
+
221
+ def self.seq(*parsers)
222
+ if parsers.size == 0
223
+ ok(ParsedSeq.empty)
224
+ else
225
+ parsers.first >> proc{|x|
226
+ seq(*parsers.drop(1)) >> proc{|xs|
227
+ ok(xs.cons(x, parsers.first.parser_name))
228
+ }}
229
+ end
230
+ end
231
+
232
+ def self.opt(parser)
233
+ parser.map{|x| [x]} | ok([])
234
+ end
235
+
236
+ def self.many(parser, separator_parser=ok(nil))
237
+ many1(parser, separator_parser) | ok([])
238
+ end
239
+
240
+ def self.many1(parser, separator_parser=ok(nil))
241
+ parser >> proc{|x|
242
+ many(separator_parser > parser) >> proc{|xs|
243
+ ok([x] + xs)
244
+ }}
245
+ end
246
+
247
+ def self.opt_fail(parser)
248
+ parser.map{|x| [x]} ^ ok([])
249
+ end
250
+
251
+ def self.many_fail(parser, separator_parser=ok(nil))
252
+ many1_fail(parser, separator_parser) ^ ok([])
253
+ end
254
+
255
+ def self.many1_fail(parser, separator_parser=ok(nil))
256
+ parser >> proc{|x|
257
+ many_fail(separator_parser > parser) >> proc{|xs|
258
+ ok([x] + xs)
259
+ }}
260
+ end
261
+
262
+ def self.discardl(parser1, parser2)
263
+ parser1 >> proc{parser2}
264
+ end
265
+
266
+ def self.discardr(parser1, parser2)
267
+ parser1 >> proc{|x|
268
+ parser2 >> proc{
269
+ ok(x)
270
+ }}
271
+ end
272
+
273
+ def self.binopl(parser, op_proc_parser)
274
+ rest = proc{|a|
275
+ op_proc_parser >> proc{|f|
276
+ parser >> proc{|b|
277
+ rest.call(f.call(a, b))
278
+ }} | ok(a)
279
+ }
280
+ parser >> proc{|a|
281
+ rest.call(a)
282
+ }
283
+ end
284
+
285
+ def self.binopl_fail(parser, op_proc_parser)
286
+ rest = proc{|a|
287
+ op_proc_parser >> proc{|f|
288
+ parser >> proc{|b|
289
+ rest.call(f.call(a, b))
290
+ }} ^ ok(a)
291
+ }
292
+ parser >> proc{|a|
293
+ rest.call(a)
294
+ }
295
+ end
296
+
297
+ # Memorization DSL suport (for recursive grammer)
298
+ # --------------------
299
+
300
+ def self.parser(name, &proc)
301
+ @cache ||= {}
302
+ spcls = class << self; self end
303
+ spcls.send(:define_method, name) do |*args|
304
+ key = [name, args]
305
+ if @cache[key]
306
+ return @cache[key]
307
+ else
308
+ @cache[key] = self.new{}
309
+ @cache[key].parser_proc = proc.call(*args).parser_proc
310
+ return @cache[key]
311
+ end
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'parser_combinator/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "parser_combinator"
8
+ spec.version = ParserCombinator::VERSION
9
+ spec.authors = ["Kensuke Sawada"]
10
+ spec.email = ["sasasawada@gmail.com"]
11
+ spec.summary = %q{Yet another class-base parser combinator library. }
12
+ spec.homepage = ""
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.7"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: parser_combinator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Kensuke Sawada
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description:
42
+ email:
43
+ - sasasawada@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - Gemfile
50
+ - LICENSE.txt
51
+ - README.md
52
+ - Rakefile
53
+ - examples/basic.rb
54
+ - examples/error_handling.rb
55
+ - examples/recursive.rb
56
+ - lib/parser_combinator.rb
57
+ - lib/parser_combinator/string_parser.rb
58
+ - lib/parser_combinator/version.rb
59
+ - parser_combinator.gemspec
60
+ homepage: ''
61
+ licenses:
62
+ - MIT
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project:
80
+ rubygems_version: 2.2.2
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: Yet another class-base parser combinator library.
84
+ test_files: []