parser_combinator 0.0.1

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