yaparc 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.
Files changed (5) hide show
  1. data/README +16 -0
  2. data/lib/parser.rb +271 -0
  3. data/tests/test_calc.rb +129 -0
  4. data/tests/test_parser.rb +167 -0
  5. metadata +50 -0
data/README ADDED
@@ -0,0 +1,16 @@
1
+ = Synopsis
2
+
3
+ This is a yet another simple combinator parser library in ruby.
4
+
5
+ = Requirements
6
+
7
+ * Ruby (http://www.ruby-lang.org/)
8
+
9
+ = Install
10
+
11
+ $ gem install yaparc
12
+
13
+ = Usage
14
+
15
+ Please look at unit test files.
16
+
data/lib/parser.rb ADDED
@@ -0,0 +1,271 @@
1
+
2
+ module Yaparc
3
+ module Parsable
4
+ include Yaparc
5
+
6
+ IS_LOWER = lambda {|c| c >= 'a' and c <= 'z'}
7
+ IS_ALPHANUM = lambda {|c| (c >= 'a' and c <= 'z') or (c >= '0' and c <= '9')}
8
+ IS_DIGIT = lambda {|i| i > '0' and i < '9'}
9
+ IS_SPACE = lambda {|i| i == ' '}
10
+
11
+ def self.included(mod)
12
+ mod.extend ClassMethods
13
+ end
14
+
15
+
16
+ def parse(input)
17
+ @parser.call(input)
18
+ end
19
+
20
+ def eval
21
+ tree = parse(input)
22
+ end
23
+
24
+ def define_parser
25
+ raise
26
+ end
27
+
28
+ module ClassMethods
29
+ def included(mod)
30
+ end
31
+
32
+ # def self.initialize
33
+ # @parser = @@parser
34
+ # end
35
+
36
+ def define_parser(&block)
37
+ @@parser = lambda do |input|
38
+ parser = yield
39
+ parser.parse(input)
40
+ end
41
+ end
42
+ end
43
+ end # of Parsable
44
+
45
+ class SucceedParser
46
+ include Parsable
47
+ def initialize(value)
48
+ @parser = lambda {|input| [[value, input]]}
49
+ end
50
+ end
51
+
52
+ class FailParser
53
+ include Parsable
54
+ def initialize
55
+ @parser = lambda {|input| []}
56
+ end
57
+ end
58
+
59
+ class ItemParser
60
+ include Parsable
61
+ def initialize
62
+ @parser = lambda do |input|
63
+ if input.nil? or input.empty?
64
+ []
65
+ else
66
+ [[input[0..0],input[1..input.length]]]
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ class SatisfyParser
73
+ include Parsable
74
+ def initialize(predicate)
75
+ @parser = lambda do |input|
76
+ item = ItemParser.new.parse(input)
77
+ if item == []
78
+ FailParser.new.parse(input)
79
+ else
80
+ if predicate.call(item[0][0])
81
+ SucceedParser.new(item[0][0]).parse(item[0][1])
82
+ else
83
+ FailParser.new.parse(input)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ class SeqParser
91
+ include Parsable
92
+ def initialize(*parsers, &block)
93
+ @parser = lambda do |input|
94
+ args = []
95
+ remains = parsers.inject(input) do |accumulator, parser|
96
+ result = parser.parse(accumulator)
97
+ unless result == []
98
+ args << result[0][0]
99
+ result[0][1]
100
+ else
101
+ break []
102
+ end
103
+ end
104
+ unless remains == []
105
+ retval = yield(*args)
106
+ [[retval, remains]]
107
+ else
108
+ []
109
+ end
110
+ end
111
+ end # of initialize
112
+ end # of SeqParser
113
+
114
+ class AltParser
115
+ include Parsable
116
+ def initialize(*parsers)
117
+ @parser = lambda do |input|
118
+ parsers.inject([]) do |accum, parser|
119
+ result = parser.parse(input)
120
+ if result == []
121
+ result
122
+ else
123
+ break [result[0]]
124
+ end
125
+ end
126
+ end
127
+ end # of initialize
128
+ end
129
+
130
+ class CharParser
131
+ include Parsable
132
+ def initialize(char)
133
+ equal_char = lambda {|i| i == char}
134
+ @parser = lambda do |input|
135
+ SatisfyParser.new(equal_char).parse(input)
136
+ end
137
+ end
138
+ end
139
+
140
+ class StringParser
141
+ include Parsable
142
+ def initialize(string)
143
+ @parser = lambda do |input|
144
+ result = ItemParser.new.parse(string)
145
+ if result == []
146
+ SucceedParser.new(result).parse(input)
147
+ # FailParser.new.parse(input)
148
+ else
149
+ SeqParser.new(
150
+ CharParser.new(result[0][0]),
151
+ StringParser.new(result[0][1]),
152
+ SucceedParser.new(result[0][0] + result[0][1])
153
+ ) do |char_result, string_result, succeed_result|
154
+ succeed_result
155
+ end.parse(input)
156
+ end
157
+ end
158
+ end
159
+ end
160
+
161
+ class ManyParser
162
+ include Parsable
163
+ def initialize(predicate)
164
+ @parser = lambda do |input|
165
+ AltParser.new(ManyOneParser.new(predicate), SucceedParser.new([])).parse(input)
166
+ end
167
+ end
168
+ end
169
+
170
+ class ManyOneParser
171
+ include Parsable
172
+ def initialize(predicate)
173
+ @parser = lambda do |input|
174
+ SeqParser.new(
175
+ SatisfyParser.new(predicate),
176
+ ManyParser.new(predicate)
177
+ ) do |v, vs|
178
+ if vs == []
179
+ v
180
+ else
181
+ v + vs.to_s
182
+ end
183
+ end.parse(input)
184
+ end
185
+ end
186
+ end
187
+
188
+
189
+ class Ident
190
+ include Parsable
191
+ def initialize
192
+ @parser = lambda do |input|
193
+ SeqParser.new(
194
+ SatisfyParser.new(IS_LOWER),
195
+ ManyParser.new(IS_ALPHANUM)
196
+ ) do |v, vs|
197
+ if vs == []
198
+ v
199
+ else
200
+ v + vs.to_s
201
+ end
202
+ end.parse(input)
203
+ end
204
+ end
205
+ end
206
+
207
+ class Nat
208
+ include Parsable
209
+ def initialize
210
+ @parser = lambda do |input|
211
+ SeqParser.new(ManyOneParser.new(IS_DIGIT)) do |vs|
212
+ if vs == []
213
+ vs
214
+ else
215
+ vs.to_i
216
+ end
217
+ end.parse(input)
218
+ end
219
+ end
220
+ end
221
+
222
+ class Space
223
+ include Parsable
224
+ def initialize
225
+ @parser = lambda do |input|
226
+ SeqParser.new(ManyParser.new(IS_SPACE)) do |vs|
227
+ []
228
+ end.parse(input)
229
+ end
230
+ end
231
+ end
232
+
233
+ class Token
234
+ include Parsable
235
+ def initialize(parser)
236
+ @parser = lambda do |input|
237
+ SeqParser.new(Space.new, parser, Space.new) do |_, vs, _|
238
+ vs
239
+ end.parse(input)
240
+ end
241
+ end
242
+ end
243
+
244
+ class Identifier
245
+ include Parsable
246
+ def initialize
247
+ @parser = lambda do |input|
248
+ Token.new(Ident.new).parse(input)
249
+ end
250
+ end
251
+ end
252
+
253
+ class Natural
254
+ include Parsable
255
+ def initialize
256
+ @parser = lambda do |input|
257
+ Token.new(Nat.new).parse(input)
258
+ end
259
+ end
260
+ end
261
+
262
+ class Symbol
263
+ include Parsable
264
+ def initialize(literal)
265
+ @parser = lambda do |input|
266
+ Token.new(StringParser.new(literal)).parse(input)
267
+ end
268
+ end
269
+ end
270
+
271
+ end
@@ -0,0 +1,129 @@
1
+ require 'lib/parser.rb'
2
+ require 'test/unit'
3
+ require 'pp'
4
+
5
+
6
+
7
+ class Expr
8
+ include Yaparc::Parsable
9
+
10
+ # define_parser do
11
+ # AltParser.new(
12
+ # SeqParser.new(Term.new,
13
+ # Symbol.new('+'),
14
+ # Expr.new) do |term, _, expr|
15
+ # ['+', term,expr]
16
+ # end,
17
+ # Term.new
18
+ # )
19
+ # end
20
+
21
+ def initialize
22
+ @parser = lambda do |input|
23
+ AltParser.new(
24
+ SeqParser.new(Term.new,
25
+ Symbol.new('+'),
26
+ Expr.new) do |term, _, expr|
27
+ ['+', term,expr]
28
+ end,
29
+ Term.new
30
+ ).parse(input)
31
+ end
32
+ end
33
+
34
+ def eval(input)
35
+ result = parse(input)
36
+ tree = result[0][0]
37
+ # p tree
38
+ eval_tree(tree)
39
+ end
40
+
41
+ def eval_tree(tree)
42
+ case tree
43
+ when Array
44
+ case tree[0]
45
+ when '+'
46
+ eval_tree(tree[1]) + eval_tree(tree[2])
47
+ when '-'
48
+ eval_tree(tree[1]) - eval_tree(tree[2])
49
+ when '*'
50
+ eval_tree(tree[1]) * eval_tree(tree[2])
51
+ when '/'
52
+ eval_tree(tree[1]) / eval_tree(tree[2])
53
+ end
54
+ else
55
+ tree
56
+ end
57
+
58
+ end
59
+ end
60
+
61
+ class Term
62
+ include Yaparc::Parsable
63
+
64
+ # define_parser do
65
+ # AltParser.new(
66
+ # SeqParser.new(Factor.new,
67
+ # Symbol.new('*'),
68
+ # Term.new) do |factor, _, term|
69
+ # ['*', factor,term]
70
+ # end,
71
+ # Factor.new
72
+ # )
73
+ # end
74
+
75
+ def initialize
76
+ @parser = lambda do |input|
77
+ AltParser.new(
78
+ SeqParser.new(Factor.new,
79
+ Symbol.new('*'),
80
+ Term.new) do |factor, _, term|
81
+ ['*', factor,term]
82
+ end,
83
+ Factor.new
84
+ ).parse(input)
85
+ end
86
+ end
87
+ end
88
+
89
+ class Factor
90
+ include Yaparc::Parsable
91
+
92
+ # define_parser do
93
+ # AltParser.new(
94
+ # SeqParser.new(Symbol.new('('),
95
+ # Expr.new,
96
+ # Symbol.new(')')) do |_,expr, _|
97
+ # expr
98
+ # end,
99
+ # Natural.new
100
+ # )
101
+ # end
102
+
103
+ def initialize
104
+ @parser = lambda do |input|
105
+ AltParser.new(
106
+ SeqParser.new(Symbol.new('('),
107
+ Expr.new,
108
+ Symbol.new(')')) do |_,expr, _|
109
+ expr
110
+ end,
111
+ Natural.new
112
+ ).parse(input)
113
+ end
114
+ end
115
+ end
116
+
117
+ class YaparcCalcTest < Test::Unit::TestCase
118
+ include ::Yaparc
119
+
120
+ def setup
121
+ end
122
+ def test_expr
123
+ parser = Expr.new
124
+ result = parser.parse("1 + 2 ")
125
+ assert_equal [[["+", 1, 2], ""]], result
126
+ assert_equal 3, parser.eval("1 + 2 ")
127
+ assert_equal 9, parser.eval("(1 + 2) * 3 ")
128
+ end
129
+ end
@@ -0,0 +1,167 @@
1
+ require 'lib/parser.rb'
2
+ require 'test/unit'
3
+ require 'pp'
4
+
5
+ class YaparcTest < Test::Unit::TestCase
6
+ include ::Yaparc
7
+
8
+
9
+ def setup
10
+
11
+ end
12
+
13
+ def teardown
14
+ end
15
+
16
+ def test_succeed_parse
17
+ parser = ::Yaparc::SucceedParser.new(1)
18
+ assert_equal [[1, "abs"]], parser.parse("abs")
19
+ end
20
+
21
+ def test_fail_parse
22
+ parser = ::Yaparc::FailParser.new
23
+ result = parser.parse("abc")
24
+ assert_equal [], result
25
+ end
26
+
27
+ def test_item_parse
28
+ parser = ::Yaparc::ItemParser.new
29
+ result = parser.parse("")
30
+ assert_equal [], result
31
+ result = parser.parse("abc")
32
+ assert_equal [["a", "bc"]], result
33
+ end
34
+
35
+ def test_seq_parse
36
+ parser = SeqParser.new(ItemParser.new, ItemParser.new) do |item1, item2|
37
+ [item1, item2]
38
+ end
39
+ result = parser.parse("abcdef")
40
+ assert_equal [[["a", "b"], "cdef"]], result
41
+
42
+ parser = SeqParser.new(ItemParser.new, ItemParser.new, ItemParser.new) do |item1, item2, item3|
43
+ [item1, item3]
44
+ end
45
+ result = parser.parse("ABCDEF")
46
+ assert_equal [[["A", "C"], "DEF"]], result
47
+
48
+ parser = SeqParser.new(ItemParser.new, ItemParser.new, ItemParser.new) do |item1, item2, item3|
49
+ [item2]
50
+ end
51
+ result = parser.parse("ABCDEF")
52
+ assert_equal [[["B"], "DEF"]], result
53
+ end
54
+
55
+ def test_alt_parse
56
+ parser = AltParser.new(ItemParser.new, SucceedParser.new('d'))
57
+ result = parser.parse("abc")
58
+ assert_equal [["a", "bc"]], result
59
+
60
+ parser = AltParser.new(FailParser.new, SucceedParser.new('d'))
61
+ result = parser.parse("abc")
62
+ assert_equal [["d", "abc"]], result
63
+
64
+ parser = AltParser.new(FailParser.new, FailParser.new)
65
+ result = parser.parse("abc")
66
+ assert_equal [], result
67
+ end
68
+
69
+ def test_satisfy_parse
70
+ is_integer = lambda do |i|
71
+ begin
72
+ Integer(i)
73
+ true
74
+ rescue
75
+ false
76
+ end
77
+ end
78
+ parser = SatisfyParser.new(is_integer)
79
+ result = parser.parse("123")
80
+ assert_equal [["1", "23"]], result
81
+
82
+ parser = SatisfyParser.new(is_integer)
83
+ result = parser.parse("abc")
84
+ assert_equal [], result
85
+
86
+ is_char = lambda do |i|
87
+ begin
88
+ String(i)
89
+ true
90
+ rescue
91
+ false
92
+ end
93
+ end
94
+ parser = SatisfyParser.new(is_char)
95
+ result = parser.parse("abc")
96
+ assert_equal [["a", "bc"]], result
97
+ end
98
+
99
+ def test_char_parse
100
+ parser = CharParser.new("a")
101
+ result = parser.parse("abc")
102
+ assert_equal [["a", "bc"]], result
103
+
104
+ parser = CharParser.new("a")
105
+ result = parser.parse("123")
106
+ assert_equal [], result
107
+ end
108
+
109
+ def test_string_parse
110
+ parser = StringParser.new("abc")
111
+ result = parser.parse("abcdef")
112
+ assert_equal [["abc", "def"]], result
113
+
114
+ parser = StringParser.new("abc")
115
+ result = parser.parse("ab1234")
116
+ assert_equal [], result
117
+ end
118
+
119
+ def test_many_parse
120
+ is_digit = lambda {|i| i > '0' and i < '9'}
121
+
122
+ parser = ManyParser.new(is_digit)
123
+ result = parser.parse("123abc")
124
+ assert_equal [["123", "abc"]], result
125
+
126
+ result = parser.parse("abcdef")
127
+ assert_equal [[[], "abcdef"]], result
128
+ end
129
+
130
+ def test_ident
131
+ parser = Ident.new
132
+ result = parser.parse("abc def")
133
+ assert_equal [["abc", " def"]], result
134
+ end
135
+
136
+ def test_nat
137
+ parser = Nat.new
138
+ result = parser.parse("123 abc")
139
+ assert_equal [[123, " abc"]], result
140
+ end
141
+
142
+ def test_space
143
+ parser = Space.new
144
+ result = parser.parse(" abc")
145
+ assert_equal [[[], "abc"]], result
146
+ end
147
+
148
+ def test_identifier
149
+ parser = Identifier.new
150
+ result = parser.parse(" abc ")
151
+ assert_equal [["abc", ""]], result
152
+ end
153
+
154
+ def test_natural
155
+ parser = Natural.new
156
+ result = parser.parse(" 1234 ")
157
+ assert_equal [[1234, ""]], result
158
+ end
159
+
160
+ def test_symbol
161
+ parser = Symbol.new('%')
162
+ result = parser.parse(" % ")
163
+ assert_equal [["%", ""]], result
164
+ end
165
+
166
+ end
167
+
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.3
3
+ specification_version: 1
4
+ name: yaparc
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.0.1
7
+ date: 2007-12-06 00:00:00 +09:00
8
+ summary: Yet Another Combinator Parser Library
9
+ require_paths:
10
+ - lib
11
+ email: akimichi_tatsukawa@nifty.com
12
+ homepage: http://rubyforge.org/projects/yaparc/
13
+ rubyforge_project:
14
+ description:
15
+ autorequire: parser
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Akimichi Tatsukawa
31
+ files:
32
+ - tests/test_calc.rb
33
+ - tests/test_parser.rb
34
+ - lib/parser.rb
35
+ - README
36
+ test_files:
37
+ - tests/test_calc.rb
38
+ - tests/test_parser.rb
39
+ rdoc_options: []
40
+
41
+ extra_rdoc_files:
42
+ - README
43
+ executables: []
44
+
45
+ extensions: []
46
+
47
+ requirements: []
48
+
49
+ dependencies: []
50
+