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.
- data/README +16 -0
- data/lib/parser.rb +271 -0
- data/tests/test_calc.rb +129 -0
- data/tests/test_parser.rb +167 -0
- metadata +50 -0
data/README
ADDED
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
|
data/tests/test_calc.rb
ADDED
@@ -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
|
+
|