parser_combinator 0.0.3 → 0.0.4

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