parser_combinator 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 3868c0360f7f7a56d60413eacf5ce4e257b45324
4
- data.tar.gz: 8345b5a9780a95e5b05aecea7aeb0b331fdcfcd9
2
+ SHA256:
3
+ metadata.gz: 67329f878af48506d74c9053ee84a35927b05e1b91782dfee376624eee18d415
4
+ data.tar.gz: f7f732e42c45e23709deb657eb257e841e1aaea40dcc77139730024f5ad992cd
5
5
  SHA512:
6
- metadata.gz: d09a00160babecd7bbd80570ae526e5997d03ced197a0b81abefa0995f36cb7d8f5ef0a1d86dccd304dcd0fc0cd3d0557f403c787f7a1c8bba9e9f872bc6a6d6
7
- data.tar.gz: fdff95bc8692fc4699d29cc534efdc047668dff6f0d0a136560c4fceabfded5fe4c680b4b461feda686c024664cd69ab79ce9a216e6bfcb00e8a4fffa037e491
6
+ metadata.gz: 75aa1c3a98a3bb230764914eefe28fbaf0dd39afeabe7fd053c363987f99a6b703690398b20d859756a8e81a6983ea8ae294c77d9e811df953bf696bb54ff6ef
7
+ data.tar.gz: 787d06cb0d7d9ccd8fbeeba6285d725c3b87979eba3d27a11b2e72ed58cd81ed82e1189d3c2d4b2872a7f4cc145ca1914db0b4f7206945bd6b99dba8d509c33d
data/.gitignore CHANGED
@@ -1,14 +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
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 CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in parser_combinator.gemspec
4
- gemspec
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in parser_combinator.gemspec
4
+ gemspec
data/LICENSE.txt CHANGED
@@ -1,22 +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.
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 CHANGED
@@ -1,153 +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
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 CHANGED
@@ -1,9 +1,9 @@
1
- require "bundler/gem_tasks"
2
-
3
- task :examples do
4
- $LOAD_PATH.unshift File.expand_path('../examples', __FILE__)
5
- require 'basic'
6
- require 'error_handling'
7
- require 'recursive'
8
- end
9
-
1
+ require "bundler/gem_tasks"
2
+
3
+ task :examples do
4
+ $LOAD_PATH.unshift File.expand_path('../examples', __FILE__)
5
+ require 'basic'
6
+ require 'error_handling'
7
+ require 'recursive'
8
+ end
9
+
data/examples/basic.rb CHANGED
File without changes
File without changes
File without changes
File without changes
@@ -1,3 +1,3 @@
1
- class ParserCombinator
2
- VERSION = "0.0.3"
3
- end
1
+ class ParserCombinator
2
+ VERSION = "0.0.4"
3
+ end
@@ -1,337 +1,373 @@
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
- Hash[@seq.select{|e| e[:name]}.map{|e| [e[:name], e[:entity]]}]
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_reader :status_handler, :parser_name, :parser_proc
101
- def initialize(status_handler=proc{nil}, name=nil, &proc)
102
- @status_handler = status_handler
103
- @parser_name = name
104
- @parser_proc = proc
105
- end
106
-
107
- def parse(items)
108
- result = @parser_proc.call(items)
109
- case result
110
- when Fail
111
- if @status_handler.call(items, result.status) != nil
112
- result.class.new(@status_handler.call(items, result.status))
113
- else
114
- result
115
- end
116
- when Ok
117
- result
118
- else
119
- raise "parsed object is #{result.inspect}/#{result.class}"
120
- end
121
- end
122
-
123
- def onfail(message=nil, ifnotyet=false, &status_handler)
124
- raise "Only eihter message or fail_handler can be specified" if message && status_handler
125
- if message
126
- onfail{|items, status| status == nil ? StandardFailStatus.new(message, items) : status}
127
- elsif status_handler
128
- self.class.new(status_handler, @parser_name, &parser_proc)
129
- else
130
- self
131
- end
132
- end
133
-
134
- def onparse(&proc)
135
- self.class.new(@status_handler, @parser_name) do |*args|
136
- proc.call(*args)
137
- @parser_proc.call(*args)
138
- end
139
- end
140
-
141
- def name(new_name)
142
- self.class.new(@status_handler, new_name, &parser_proc)
143
- end
144
-
145
- def map(&mapping)
146
- self >> proc{|x| self.class.ok(mapping.call(x))}
147
- end
148
-
149
- def >>(proc)
150
- self.class.so_then(self, &proc)
151
- end
152
-
153
- def |(other)
154
- self.class.either(self, other)
155
- end
156
-
157
- def ^(other)
158
- self.class.either_fail(self, other)
159
- end
160
-
161
- def >(other)
162
- self.class.discardl(self, other)
163
- end
164
-
165
- def <(other)
166
- self.class.discardr(self, other)
167
- end
168
-
169
- # CoreCombinator
170
- # --------------------
171
-
172
- def self.ok(object)
173
- new{|i| Ok.new(object, i)}
174
- end
175
-
176
- def self.fail(status=nil)
177
- new{|i| Fail.new(status)}
178
- end
179
-
180
- def self.so_then(parser, &continuation_proc)
181
- new{|i|
182
- case result = parser.parse(i)
183
- when Fail
184
- result
185
- when Ok
186
- continuation_proc.call(result.parsed).parse(result.rest)
187
- else
188
- raise "error"
189
- end
190
- }
191
- end
192
-
193
- def self.either(parser1, parser2)
194
- new{|i|
195
- case result1 = parser1.parse(i)
196
- when Fail
197
- parser2.parse(i)
198
- when Ok
199
- result1
200
- else
201
- raise "error"
202
- end
203
- }
204
- end
205
-
206
- def self.either_fail(parser1, parser2)
207
- new{|i|
208
- case result1 = parser1.parse(i)
209
- when Fail
210
- if result1.status == nil
211
- parser2.parse(i)
212
- else
213
- result1
214
- end
215
- when Ok
216
- result1
217
- else
218
- raise "error"
219
- end
220
- }
221
- end
222
-
223
- def self.item
224
- new{|i| i.size == 0 ? Fail.new : Ok.new(i.head, i.rest)}
225
- end
226
-
227
- def self.end_of_input
228
- new{|i| i.size == 0 ? Ok.new(nil, i) : Fail.new}
229
- end
230
-
231
- def self.sat(&item_cond_proc)
232
- item >> proc{|i|
233
- item_cond_proc.call(i.item) ? ok(i) : fail
234
- }
235
- end
236
-
237
- # UtilCombinator
238
- # --------------------
239
-
240
- def self.seq(*parsers)
241
- if parsers.size == 0
242
- ok(ParsedSeq.empty)
243
- else
244
- parsers.first >> proc{|x|
245
- seq(*parsers.drop(1)) >> proc{|xs|
246
- ok(xs.cons(x, parsers.first.parser_name))
247
- }}
248
- end
249
- end
250
-
251
- def self.opt(parser)
252
- parser.map{|x| [x]} | ok([])
253
- end
254
-
255
- def self.many(parser, separator_parser=ok(nil))
256
- many1(parser, separator_parser) | ok([])
257
- end
258
-
259
- def self.many1(parser, separator_parser=ok(nil))
260
- parser >> proc{|x|
261
- many(separator_parser > parser) >> proc{|xs|
262
- ok([x] + xs)
263
- }}
264
- end
265
-
266
- def self.opt_fail(parser)
267
- parser.map{|x| [x]} ^ ok([])
268
- end
269
-
270
- def self.many_fail(parser, separator_parser=ok(nil))
271
- many1_fail(parser, separator_parser) ^ ok([])
272
- end
273
-
274
- def self.many1_fail(parser, separator_parser=ok(nil))
275
- parser >> proc{|x|
276
- many_fail(separator_parser > parser) >> proc{|xs|
277
- ok([x] + xs)
278
- }}
279
- end
280
-
281
- def self.discardl(parser1, parser2)
282
- parser1 >> proc{parser2}
283
- end
284
-
285
- def self.discardr(parser1, parser2)
286
- parser1 >> proc{|x|
287
- parser2 >> proc{
288
- ok(x)
289
- }}
290
- end
291
-
292
- def self.binopl(parser, op_proc_parser)
293
- rest = proc{|a|
294
- op_proc_parser >> proc{|f|
295
- parser >> proc{|b|
296
- rest.call(f.call(a, b))
297
- }} | ok(a)
298
- }
299
- parser >> proc{|a|
300
- rest.call(a)
301
- }
302
- end
303
-
304
- def self.binopl_fail(parser, op_proc_parser)
305
- rest = proc{|a|
306
- op_proc_parser >> proc{|f|
307
- parser >> proc{|b|
308
- rest.call(f.call(a, b))
309
- }} ^ ok(a)
310
- }
311
- parser >> proc{|a|
312
- rest.call(a)
313
- }
314
- end
315
-
316
- # Memorization DSL suport (for recursive grammer)
317
- # --------------------
318
-
319
- def self.parser(name, &proc)
320
- @cache ||= {}
321
- spcls = class << self; self end
322
- spcls.send(:define_method, name) do |*args|
323
- key = [name, args]
324
- if @cache[key]
325
- return @cache[key]
326
- else
327
- status_handler = proc{ raise "this is never-called proc (status_handler)" }
328
- parser_proc = proc{ raise "this is never-called proc (parser_proc)" }
329
- @cache[key] = self.new(proc{|*args| status_handler.call(*args)}){|*args| parser_proc.call(*args)}
330
- generated_parser = proc.call(*args)
331
- parser_proc = generated_parser.parser_proc
332
- status_handler = generated_parser.status_handler
333
- return @cache[key]
334
- end
335
- end
336
- end
337
- end
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 drop(v)
45
+ Items.new(super(v))
46
+ end
47
+
48
+ def drop_while(&p)
49
+ Items.new(super(&p))
50
+ end
51
+
52
+ def flatten
53
+ Items.new(super())
54
+ end
55
+
56
+ def slice(nth)
57
+ if nth.instance_of? Range then
58
+ Items.new(super(nth))
59
+ else
60
+ super(nth)
61
+ end
62
+ end
63
+
64
+ def slice(pos, len)
65
+ Items.new(super(pos, len))
66
+ end
67
+
68
+ def take(n)
69
+ Items.new(super(n))
70
+ end
71
+
72
+ def take_while(&p)
73
+ Items.new(super(&p))
74
+ end
75
+
76
+ def uniq
77
+ Items.new(super())
78
+ end
79
+
80
+ def head
81
+ self.first
82
+ end
83
+
84
+ def rest
85
+ self.drop(1)
86
+ end
87
+
88
+ def to_s
89
+ self.map(&:to_s).join
90
+ end
91
+
92
+ def inspect
93
+ "Items [#{self.map(&:inspect).join(",\n")}]"
94
+ end
95
+ end
96
+
97
+ class ParsedSeq
98
+ def initialize(seq)
99
+ @seq = seq
100
+ end
101
+
102
+ def to_a
103
+ @seq.map{|e| e[:entity]}
104
+ end
105
+
106
+ def to_h
107
+ Hash[@seq.select{|e| e[:name]}.map{|e| [e[:name], e[:entity]]}]
108
+ end
109
+
110
+ def self.empty
111
+ new([])
112
+ end
113
+
114
+ def cons(entity, name)
115
+ self.class.new([{:entity => entity, :name => name}] + @seq)
116
+ end
117
+
118
+ def [](key)
119
+ case key
120
+ when Integer
121
+ if 0 <= key && key < @seq.length
122
+ @seq[key][:entity]
123
+ else
124
+ raise "out of bounds for ParsedSeq"
125
+ end
126
+ else
127
+ if e = @seq.find{|e| e[:name] == key}
128
+ e[:entity]
129
+ else
130
+ raise "key #{key} is not found in ParsedSeq"
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ attr_reader :status_handler, :parser_name, :parser_proc
137
+ def initialize(status_handler=proc{nil}, name=nil, &proc)
138
+ @status_handler = status_handler
139
+ @parser_name = name
140
+ @parser_proc = proc
141
+ end
142
+
143
+ def parse(items)
144
+ result = @parser_proc.call(items)
145
+ case result
146
+ when Fail
147
+ if @status_handler.call(items, result.status) != nil
148
+ result.class.new(@status_handler.call(items, result.status))
149
+ else
150
+ result
151
+ end
152
+ when Ok
153
+ result
154
+ else
155
+ raise "parsed object is #{result.inspect}/#{result.class}"
156
+ end
157
+ end
158
+
159
+ def onfail(message=nil, ifnotyet=false, &status_handler)
160
+ raise "Only eihter message or fail_handler can be specified" if message && status_handler
161
+ if message
162
+ onfail{|items, status| status == nil ? StandardFailStatus.new(message, items) : status}
163
+ elsif status_handler
164
+ self.class.new(status_handler, @parser_name, &parser_proc)
165
+ else
166
+ self
167
+ end
168
+ end
169
+
170
+ def onparse(&proc)
171
+ self.class.new(@status_handler, @parser_name) do |*args|
172
+ proc.call(*args)
173
+ @parser_proc.call(*args)
174
+ end
175
+ end
176
+
177
+ def name(new_name)
178
+ self.class.new(@status_handler, new_name, &parser_proc)
179
+ end
180
+
181
+ def map(&mapping)
182
+ self >> proc{|x| self.class.ok(mapping.call(x))}
183
+ end
184
+
185
+ def >>(proc)
186
+ self.class.so_then(self, &proc)
187
+ end
188
+
189
+ def |(other)
190
+ self.class.either(self, other)
191
+ end
192
+
193
+ def ^(other)
194
+ self.class.either_fail(self, other)
195
+ end
196
+
197
+ def >(other)
198
+ self.class.discardl(self, other)
199
+ end
200
+
201
+ def <(other)
202
+ self.class.discardr(self, other)
203
+ end
204
+
205
+ # CoreCombinator
206
+ # --------------------
207
+
208
+ def self.ok(object)
209
+ new{|i| Ok.new(object, i)}
210
+ end
211
+
212
+ def self.fail(status=nil)
213
+ new{|i| Fail.new(status)}
214
+ end
215
+
216
+ def self.so_then(parser, &continuation_proc)
217
+ new{|i|
218
+ case result = parser.parse(i)
219
+ when Fail
220
+ result
221
+ when Ok
222
+ continuation_proc.call(result.parsed).parse(result.rest)
223
+ else
224
+ raise "error"
225
+ end
226
+ }
227
+ end
228
+
229
+ def self.either(parser1, parser2)
230
+ new{|i|
231
+ case result1 = parser1.parse(i)
232
+ when Fail
233
+ parser2.parse(i)
234
+ when Ok
235
+ result1
236
+ else
237
+ raise "error"
238
+ end
239
+ }
240
+ end
241
+
242
+ def self.either_fail(parser1, parser2)
243
+ new{|i|
244
+ case result1 = parser1.parse(i)
245
+ when Fail
246
+ if result1.status == nil
247
+ parser2.parse(i)
248
+ else
249
+ result1
250
+ end
251
+ when Ok
252
+ result1
253
+ else
254
+ raise "error"
255
+ end
256
+ }
257
+ end
258
+
259
+ def self.item
260
+ new{|i| i.size == 0 ? Fail.new : Ok.new(i.head, i.rest)}
261
+ end
262
+
263
+ def self.end_of_input
264
+ new{|i| i.size == 0 ? Ok.new(nil, i) : Fail.new}
265
+ end
266
+
267
+ def self.sat(&item_cond_proc)
268
+ item >> proc{|i|
269
+ item_cond_proc.call(i.item) ? ok(i) : fail
270
+ }
271
+ end
272
+
273
+ # UtilCombinator
274
+ # --------------------
275
+
276
+ def self.seq(*parsers)
277
+ if parsers.size == 0
278
+ ok(ParsedSeq.empty)
279
+ else
280
+ parsers.first >> proc{|x|
281
+ seq(*parsers.drop(1)) >> proc{|xs|
282
+ ok(xs.cons(x, parsers.first.parser_name))
283
+ }}
284
+ end
285
+ end
286
+
287
+ def self.opt(parser)
288
+ parser.map{|x| [x]} | ok([])
289
+ end
290
+
291
+ def self.many(parser, separator_parser=ok(nil))
292
+ many1(parser, separator_parser) | ok([])
293
+ end
294
+
295
+ def self.many1(parser, separator_parser=ok(nil))
296
+ parser >> proc{|x|
297
+ many(separator_parser > parser) >> proc{|xs|
298
+ ok([x] + xs)
299
+ }}
300
+ end
301
+
302
+ def self.opt_fail(parser)
303
+ parser.map{|x| [x]} ^ ok([])
304
+ end
305
+
306
+ def self.many_fail(parser, separator_parser=ok(nil))
307
+ many1_fail(parser, separator_parser) ^ ok([])
308
+ end
309
+
310
+ def self.many1_fail(parser, separator_parser=ok(nil))
311
+ parser >> proc{|x|
312
+ many_fail(separator_parser > parser) >> proc{|xs|
313
+ ok([x] + xs)
314
+ }}
315
+ end
316
+
317
+ def self.discardl(parser1, parser2)
318
+ parser1 >> proc{parser2}
319
+ end
320
+
321
+ def self.discardr(parser1, parser2)
322
+ parser1 >> proc{|x|
323
+ parser2 >> proc{
324
+ ok(x)
325
+ }}
326
+ end
327
+
328
+ def self.binopl(parser, op_proc_parser)
329
+ rest = proc{|a|
330
+ op_proc_parser >> proc{|f|
331
+ parser >> proc{|b|
332
+ rest.call(f.call(a, b))
333
+ }} | ok(a)
334
+ }
335
+ parser >> proc{|a|
336
+ rest.call(a)
337
+ }
338
+ end
339
+
340
+ def self.binopl_fail(parser, op_proc_parser)
341
+ rest = proc{|a|
342
+ op_proc_parser >> proc{|f|
343
+ parser >> proc{|b|
344
+ rest.call(f.call(a, b))
345
+ }} ^ ok(a)
346
+ }
347
+ parser >> proc{|a|
348
+ rest.call(a)
349
+ }
350
+ end
351
+
352
+ # Memorization DSL suport (for recursive grammer)
353
+ # --------------------
354
+
355
+ def self.parser(name, &proc)
356
+ @cache ||= {}
357
+ spcls = class << self; self end
358
+ spcls.send(:define_method, name) do |*args|
359
+ key = [name, args]
360
+ if @cache[key]
361
+ return @cache[key]
362
+ else
363
+ status_handler = proc{ raise "this is never-called proc (status_handler)" }
364
+ parser_proc = proc{ raise "this is never-called proc (parser_proc)" }
365
+ @cache[key] = self.new(proc{|*args| status_handler.call(*args)}){|*args| parser_proc.call(*args)}
366
+ generated_parser = proc.call(*args)
367
+ parser_proc = generated_parser.parser_proc
368
+ status_handler = generated_parser.status_handler
369
+ return @cache[key]
370
+ end
371
+ end
372
+ end
373
+ end
@@ -1,22 +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
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 CHANGED
@@ -1,51 +1,51 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parser_combinator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kensuke Sawada
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-11 00:00:00.000000000 Z
11
+ date: 2022-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.7'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.7'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '10.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
- description:
41
+ description:
42
42
  email:
43
43
  - sasasawada@gmail.com
44
44
  executables: []
45
45
  extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
- - .gitignore
48
+ - ".gitignore"
49
49
  - Gemfile
50
50
  - LICENSE.txt
51
51
  - README.md
@@ -61,24 +61,23 @@ homepage: ''
61
61
  licenses:
62
62
  - MIT
63
63
  metadata: {}
64
- post_install_message:
64
+ post_install_message:
65
65
  rdoc_options: []
66
66
  require_paths:
67
67
  - lib
68
68
  required_ruby_version: !ruby/object:Gem::Requirement
69
69
  requirements:
70
- - - '>='
70
+ - - ">="
71
71
  - !ruby/object:Gem::Version
72
72
  version: '0'
73
73
  required_rubygems_version: !ruby/object:Gem::Requirement
74
74
  requirements:
75
- - - '>='
75
+ - - ">="
76
76
  - !ruby/object:Gem::Version
77
77
  version: '0'
78
78
  requirements: []
79
- rubyforge_project:
80
- rubygems_version: 2.0.14
81
- signing_key:
79
+ rubygems_version: 3.3.7
80
+ signing_key:
82
81
  specification_version: 4
83
82
  summary: Yet another class-base parser combinator library.
84
83
  test_files: []