neg 0.2.0 → 0.3.0
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/TODO.txt +4 -3
- data/lib/neg/parser.rb +88 -47
- data/lib/neg/version.rb +1 -1
- data/spec/parser_alternative_spec.rb +8 -8
- data/spec/parser_character_spec.rb +39 -13
- data/spec/parser_lookahead_parser_spec.rb +126 -0
- data/spec/parser_non_terminal_spec.rb +24 -11
- data/spec/parser_repetition_spec.rb +21 -21
- data/spec/parser_sequence_spec.rb +16 -13
- data/spec/parser_string_spec.rb +3 -3
- data/spec/sample_json_parser_spec.rb +38 -35
- metadata +3 -2
data/TODO.txt
CHANGED
@@ -2,13 +2,14 @@
|
|
2
2
|
[o] unconsumed input!
|
3
3
|
[o] ^ 0, 1, [ min, max ]
|
4
4
|
[o] switch from ^ to * (how * is related to +)
|
5
|
+
[o] _ (any)
|
6
|
+
[o] chars
|
5
7
|
[ ] lookahead present/absent
|
6
8
|
~ x --> present
|
7
9
|
! x --> absent
|
8
|
-
[ ] any
|
9
|
-
[ ] chars
|
10
10
|
[ ] blankslate
|
11
|
-
|
11
|
+
[ ] drop UnconsumedInput, replace with regular [ false, ... ] output
|
12
|
+
[ ] x * '?' / x * '+' / x * '*' as shortcuts
|
12
13
|
|
13
14
|
`x` + [.]
|
14
15
|
`x` + [a-z]
|
data/lib/neg/parser.rb
CHANGED
@@ -29,9 +29,13 @@ require 'neg/input'
|
|
29
29
|
module Neg
|
30
30
|
|
31
31
|
class UnconsumedInputError < StandardError; end
|
32
|
+
class ParseError < StandardError; end
|
32
33
|
|
33
34
|
class Parser
|
34
35
|
|
36
|
+
def self.`(s) ; StringParser.new(s); end
|
37
|
+
def self._(c=nil) ; CharacterParser.new(c); end
|
38
|
+
|
35
39
|
def self.method_missing(m, *args)
|
36
40
|
|
37
41
|
return super if args.any?
|
@@ -53,21 +57,11 @@ module Neg
|
|
53
57
|
|
54
58
|
raise UnconsumedInputError.new(
|
55
59
|
"remaining: #{i.remains.inspect}"
|
56
|
-
) if result[
|
60
|
+
) if result[2] && ( ! i.eoi?)
|
57
61
|
|
58
62
|
result
|
59
63
|
end
|
60
64
|
|
61
|
-
def self.`(s)
|
62
|
-
|
63
|
-
StringParser.new(s)
|
64
|
-
end
|
65
|
-
|
66
|
-
def self._(c=nil)
|
67
|
-
|
68
|
-
CharacterParser.new(c)
|
69
|
-
end
|
70
|
-
|
71
65
|
def self.to_s
|
72
66
|
|
73
67
|
s = [ "#{name}:" ]
|
@@ -96,36 +90,23 @@ module Neg
|
|
96
90
|
|
97
91
|
class SubParser
|
98
92
|
|
99
|
-
def +(pa)
|
100
|
-
|
101
|
-
|
102
|
-
end
|
103
|
-
|
104
|
-
def
|
105
|
-
|
106
|
-
AlternativeParser.new(self, pa)
|
107
|
-
end
|
108
|
-
|
109
|
-
def [](name)
|
110
|
-
|
111
|
-
NonTerminalParser.new(name.to_s, self)
|
112
|
-
end
|
113
|
-
|
114
|
-
def *(range)
|
115
|
-
|
116
|
-
RepetitionParser.new(self, range)
|
117
|
-
end
|
93
|
+
def +(pa) ; SequenceParser.new(self, pa); end
|
94
|
+
def |(pa) ; AlternativeParser.new(self, pa); end
|
95
|
+
def [](name) ; NonTerminalParser.new(name.to_s, self); end
|
96
|
+
def *(range) ; RepetitionParser.new(self, range); end
|
97
|
+
def ~ ; LookaheadParser.new(self, true); end
|
98
|
+
def -@ ; LookaheadParser.new(self, false); end
|
118
99
|
|
119
100
|
def parse(input_or_string)
|
120
101
|
|
121
102
|
input = Neg::Input(input_or_string)
|
122
103
|
start = input.position
|
123
104
|
|
124
|
-
success, result = do_parse(input)
|
105
|
+
success, result, children = do_parse(input)
|
125
106
|
|
126
107
|
input.rewind(start) unless success
|
127
108
|
|
128
|
-
[ nil,
|
109
|
+
[ nil, start, success, result, children ]
|
129
110
|
end
|
130
111
|
end
|
131
112
|
|
@@ -142,9 +123,34 @@ module Neg
|
|
142
123
|
@child = pa
|
143
124
|
end
|
144
125
|
|
126
|
+
# def reduce(children_results)
|
127
|
+
#
|
128
|
+
# children_results.collect { |cr|
|
129
|
+
# if cr[0] && cr[0] != :digit
|
130
|
+
# false
|
131
|
+
# elsif cr[2]
|
132
|
+
# cr[3] ? cr[3] : reduce(cr[4])
|
133
|
+
# else
|
134
|
+
# nil
|
135
|
+
# end
|
136
|
+
# }.flatten.compact
|
137
|
+
# end
|
138
|
+
|
145
139
|
def do_parse(i)
|
146
140
|
|
147
|
-
@child.
|
141
|
+
raise ParseError.new("\"#{@name}\" is missing") if @child.nil?
|
142
|
+
|
143
|
+
r = @child.do_parse(i)
|
144
|
+
|
145
|
+
return r
|
146
|
+
|
147
|
+
# return r if r[0] == false
|
148
|
+
#
|
149
|
+
# report = reduce(r[2])
|
150
|
+
#
|
151
|
+
# return r if report.include?(false)
|
152
|
+
#
|
153
|
+
# [ true, report.join, [] ]
|
148
154
|
end
|
149
155
|
|
150
156
|
def parse(input_or_string)
|
@@ -189,15 +195,15 @@ module Neg
|
|
189
195
|
|
190
196
|
loop do
|
191
197
|
r = @child.parse(i)
|
192
|
-
break if ! r[
|
198
|
+
break if ! r[2] && rs.size >= @min && (@max.nil? || rs.size <= @max)
|
193
199
|
rs << r
|
194
|
-
break if ! r[
|
200
|
+
break if ! r[2]
|
195
201
|
break if rs.size == @max
|
196
202
|
end
|
197
203
|
|
198
|
-
success = (rs.empty? || rs.last[
|
204
|
+
success = (rs.empty? || rs.last[2]) && (rs.size >= @min)
|
199
205
|
|
200
|
-
[ success, rs ]
|
206
|
+
[ success, nil, rs ]
|
201
207
|
end
|
202
208
|
|
203
209
|
def to_s(parent=nil)
|
@@ -216,9 +222,9 @@ module Neg
|
|
216
222
|
def do_parse(i)
|
217
223
|
|
218
224
|
if (s = i.read(@s.length)) == @s
|
219
|
-
[ true, @s ]
|
225
|
+
[ true, @s, [] ]
|
220
226
|
else
|
221
|
-
[ false, "expected #{@s.inspect}, got #{s.inspect}" ]
|
227
|
+
[ false, "expected #{@s.inspect}, got #{s.inspect}", [] ]
|
222
228
|
end
|
223
229
|
end
|
224
230
|
|
@@ -239,9 +245,9 @@ module Neg
|
|
239
245
|
def do_parse(i)
|
240
246
|
|
241
247
|
if (s = i.read(1)).match(@r)
|
242
|
-
[ true, s ]
|
248
|
+
[ true, s, [] ]
|
243
249
|
else
|
244
|
-
[ false, "#{s.inspect} doesn't match #{@c.inspect}" ]
|
250
|
+
[ false, "#{s.inspect} doesn't match #{@c.inspect}", [] ]
|
245
251
|
end
|
246
252
|
end
|
247
253
|
|
@@ -275,10 +281,10 @@ module Neg
|
|
275
281
|
@children.each do |c|
|
276
282
|
|
277
283
|
results << c.parse(i)
|
278
|
-
break unless results.last[
|
284
|
+
break unless results.last[2]
|
279
285
|
end
|
280
286
|
|
281
|
-
[ results.last[
|
287
|
+
[ results.last[2], nil, results ]
|
282
288
|
end
|
283
289
|
|
284
290
|
def to_s(parent=nil)
|
@@ -298,14 +304,14 @@ module Neg
|
|
298
304
|
|
299
305
|
def do_parse(i)
|
300
306
|
|
301
|
-
|
307
|
+
results = []
|
302
308
|
|
303
309
|
@children.each { |c|
|
304
|
-
|
305
|
-
break if
|
310
|
+
results << c.parse(i)
|
311
|
+
break if results.last[2]
|
306
312
|
}
|
307
313
|
|
308
|
-
[
|
314
|
+
[ results.last[2], nil, results ]
|
309
315
|
end
|
310
316
|
|
311
317
|
def to_s(parent=nil)
|
@@ -313,6 +319,41 @@ module Neg
|
|
313
319
|
"(#{@children.collect { |c| c.to_s(self) }.join(' | ')})"
|
314
320
|
end
|
315
321
|
end
|
322
|
+
|
323
|
+
class LookaheadParser < SubParser
|
324
|
+
|
325
|
+
def initialize(child, presence)
|
326
|
+
|
327
|
+
@child = child
|
328
|
+
@presence = presence
|
329
|
+
end
|
330
|
+
|
331
|
+
def do_parse(i)
|
332
|
+
|
333
|
+
start = i.position
|
334
|
+
|
335
|
+
r = @child.parse(i)
|
336
|
+
i.rewind(start)
|
337
|
+
|
338
|
+
success = r[2]
|
339
|
+
success = ! success if ! @presence
|
340
|
+
|
341
|
+
result = if success
|
342
|
+
nil
|
343
|
+
else
|
344
|
+
[
|
345
|
+
@child.to_s(nil), 'is not', @presence ? 'present' : 'absent'
|
346
|
+
].join(' ')
|
347
|
+
end
|
348
|
+
|
349
|
+
[ success, result, [ r ] ]
|
350
|
+
end
|
351
|
+
|
352
|
+
def to_s(parent=nil)
|
353
|
+
|
354
|
+
"#{@presence ? '~' : '-'}#{@child.to_s(self)}"
|
355
|
+
end
|
356
|
+
end
|
316
357
|
end
|
317
358
|
end
|
318
359
|
|
data/lib/neg/version.rb
CHANGED
@@ -11,24 +11,24 @@ describe Neg::Parser::AlternativeParser do
|
|
11
11
|
it 'parses' do
|
12
12
|
|
13
13
|
AltParser.parse('x').should ==
|
14
|
-
[ :text,
|
15
|
-
[ nil,
|
14
|
+
[ :text, [ 0, 1, 1 ], true, nil, [
|
15
|
+
[ nil, [ 0, 1, 1 ], true, 'x', [] ] ] ]
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'parses (2nd alternative succeeds)' do
|
19
19
|
|
20
20
|
AltParser.parse('y').should ==
|
21
|
-
[ :text,
|
22
|
-
[ nil,
|
23
|
-
[ nil,
|
21
|
+
[ :text, [ 0, 1, 1 ], true, nil, [
|
22
|
+
[ nil, [ 0, 1, 1 ], false, 'expected "x", got "y"', [] ],
|
23
|
+
[ nil, [ 0, 1, 1 ], true, 'y', [] ] ] ]
|
24
24
|
end
|
25
25
|
|
26
26
|
it 'fails gracefully' do
|
27
27
|
|
28
28
|
AltParser.parse('z').should ==
|
29
|
-
[ :text,
|
30
|
-
[ nil,
|
31
|
-
[ nil,
|
29
|
+
[ :text, [ 0, 1, 1 ], false, nil, [
|
30
|
+
[ nil, [ 0, 1, 1 ], false, 'expected "x", got "z"', [] ],
|
31
|
+
[ nil, [ 0, 1, 1 ], false, 'expected "y", got "z"', [] ] ] ]
|
32
32
|
end
|
33
33
|
|
34
34
|
it 'goes beyond two elements' do
|
@@ -16,20 +16,22 @@ describe Neg::Parser::CharacterParser do
|
|
16
16
|
|
17
17
|
parser.parse('xy').should ==
|
18
18
|
[ :text,
|
19
|
-
true,
|
20
19
|
[ 0, 1, 1 ],
|
21
|
-
|
22
|
-
|
20
|
+
true,
|
21
|
+
nil,
|
22
|
+
[ [ nil, [ 0, 1, 1 ], true, "x", [] ],
|
23
|
+
[ nil, [ 1, 1, 2 ], true, "y", [] ] ] ]
|
23
24
|
end
|
24
25
|
|
25
26
|
it 'fails gracefully' do
|
26
27
|
|
27
28
|
parser.parse('x').should ==
|
28
29
|
[ :text,
|
29
|
-
false,
|
30
30
|
[ 0, 1, 1],
|
31
|
-
|
32
|
-
|
31
|
+
false,
|
32
|
+
nil,
|
33
|
+
[ [ nil, [ 0, 1, 1 ], true, "x", [] ],
|
34
|
+
[ nil, [ 1, 1, 2 ], false, "\"\" doesn't match nil", [] ] ] ]
|
33
35
|
end
|
34
36
|
|
35
37
|
it 'is rendered correctly via #to_s' do
|
@@ -54,19 +56,43 @@ describe Neg::Parser::CharacterParser do
|
|
54
56
|
|
55
57
|
parser.parse('tel:0-99').should ==
|
56
58
|
[ :text,
|
57
|
-
true,
|
58
59
|
[ 0, 1, 1 ],
|
59
|
-
|
60
|
+
true,
|
61
|
+
nil,
|
62
|
+
[ [ nil, [ 0, 1, 1 ], true, "tel:", [] ],
|
60
63
|
[ nil,
|
64
|
+
[ 4, 1, 5 ],
|
61
65
|
true,
|
66
|
+
nil,
|
67
|
+
[ [ nil, [ 4, 1, 5 ], true, "0", [] ],
|
68
|
+
[ nil, [ 5, 1, 6 ], true, "-", [] ],
|
69
|
+
[ nil, [ 6, 1, 7 ], true, "9", [] ],
|
70
|
+
[ nil, [ 7, 1, 8 ], true, "9", [] ] ] ] ] ]
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'fails gracefully' do
|
74
|
+
|
75
|
+
parser.parse('tel:a-bc').should ==
|
76
|
+
[ :text,
|
77
|
+
[ 0, 1, 1 ],
|
78
|
+
false,
|
79
|
+
nil,
|
80
|
+
[ [ nil, [ 0, 1, 1 ], true, "tel:", [] ],
|
81
|
+
[ nil,
|
62
82
|
[ 4, 1, 5 ],
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
[ nil, true, [ 7, 1, 8 ], "9" ] ] ] ] ]
|
83
|
+
false,
|
84
|
+
nil,
|
85
|
+
[ [ nil, [ 4, 1, 5 ], false, "\"a\" doesn't match \"0-9-\"", [] ] ] ] ] ]
|
67
86
|
end
|
68
87
|
|
69
|
-
it '
|
88
|
+
it 'is rendered correctly via #to_s' do
|
89
|
+
|
90
|
+
parser.to_s.strip.should == %q{
|
91
|
+
:
|
92
|
+
text == (`tel:` + _("0-9-") * 1)
|
93
|
+
root: text
|
94
|
+
}.strip
|
95
|
+
end
|
70
96
|
end
|
71
97
|
end
|
72
98
|
|
@@ -0,0 +1,126 @@
|
|
1
|
+
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
|
5
|
+
describe Neg::Parser::LookaheadParser do
|
6
|
+
|
7
|
+
context '~x (presence)' do
|
8
|
+
|
9
|
+
let(:parser) {
|
10
|
+
Class.new(Neg::Parser) do
|
11
|
+
root == x + z
|
12
|
+
x == `x` + ~`z`
|
13
|
+
z == `z`
|
14
|
+
end
|
15
|
+
}
|
16
|
+
|
17
|
+
it 'parses' do
|
18
|
+
|
19
|
+
parser.parse('xz').should ==
|
20
|
+
[ :root,
|
21
|
+
[ 0, 1, 1 ],
|
22
|
+
true,
|
23
|
+
nil,
|
24
|
+
[ [ :x,
|
25
|
+
[ 0, 1, 1 ],
|
26
|
+
true,
|
27
|
+
nil,
|
28
|
+
[ [ nil, [ 0, 1, 1 ], true, "x", [] ],
|
29
|
+
[ nil, [ 1, 1, 2 ], true, nil, [
|
30
|
+
[ nil, [ 1, 1, 2 ], true, "z", [] ] ] ] ] ],
|
31
|
+
[ :z, [ 1, 1, 2 ], true, "z", [] ] ] ]
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'fails gracefully' do
|
35
|
+
|
36
|
+
parser.parse('xx').should ==
|
37
|
+
[:root,
|
38
|
+
[0, 1, 1],
|
39
|
+
false,
|
40
|
+
nil,
|
41
|
+
[[:x,
|
42
|
+
[0, 1, 1],
|
43
|
+
false,
|
44
|
+
nil,
|
45
|
+
[[nil, [0, 1, 1], true, "x", []],
|
46
|
+
[nil,
|
47
|
+
[1, 1, 2],
|
48
|
+
false,
|
49
|
+
"`z` is not present",
|
50
|
+
[[nil, [1, 1, 2], false, "expected \"z\", got \"x\"", []]]]]]]]
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'is rendered correctly via #to_s' do
|
54
|
+
|
55
|
+
parser.to_s.strip.should == %q{
|
56
|
+
:
|
57
|
+
root == (x + z)
|
58
|
+
x == (`x` + ~`z`)
|
59
|
+
z == `z`
|
60
|
+
root: root
|
61
|
+
}.strip
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context '-x (absence)' do
|
66
|
+
|
67
|
+
let(:parser) {
|
68
|
+
Class.new(Neg::Parser) do
|
69
|
+
root == x + z
|
70
|
+
x == `x` + -`y`
|
71
|
+
z == `z`
|
72
|
+
end
|
73
|
+
}
|
74
|
+
|
75
|
+
it 'parses' do
|
76
|
+
|
77
|
+
parser.parse('xz').should ==
|
78
|
+
[ :root,
|
79
|
+
[ 0, 1, 1 ],
|
80
|
+
true,
|
81
|
+
nil,
|
82
|
+
[ [ :x,
|
83
|
+
[ 0, 1, 1 ],
|
84
|
+
true,
|
85
|
+
nil,
|
86
|
+
[ [ nil, [ 0, 1, 1 ], true, "x", [] ],
|
87
|
+
[ nil,
|
88
|
+
[ 1, 1, 2 ],
|
89
|
+
true,
|
90
|
+
nil,
|
91
|
+
[ [ nil, [ 1, 1, 2 ], false, "expected \"y\", got \"z\"", [] ] ] ] ] ],
|
92
|
+
[ :z, [ 1, 1, 2 ], true, "z", [] ] ] ]
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'fails gracefully' do
|
96
|
+
|
97
|
+
parser.parse('xy').should ==
|
98
|
+
[:root,
|
99
|
+
[0, 1, 1],
|
100
|
+
false,
|
101
|
+
nil,
|
102
|
+
[[:x,
|
103
|
+
[0, 1, 1],
|
104
|
+
false,
|
105
|
+
nil,
|
106
|
+
[[nil, [0, 1, 1], true, "x", []],
|
107
|
+
[nil,
|
108
|
+
[1, 1, 2],
|
109
|
+
false,
|
110
|
+
"`y` is not absent",
|
111
|
+
[[nil, [1, 1, 2], true, "y", []]]]]]]]
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'is rendered correctly via #to_s' do
|
115
|
+
|
116
|
+
parser.to_s.strip.should == %q{
|
117
|
+
:
|
118
|
+
root == (x + z)
|
119
|
+
x == (`x` + -`y`)
|
120
|
+
z == `z`
|
121
|
+
root: root
|
122
|
+
}.strip
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
@@ -14,10 +14,10 @@ describe Neg::Parser::NonTerminalParser do
|
|
14
14
|
z == `zz` | `z`
|
15
15
|
end
|
16
16
|
|
17
|
-
parser.parse('x')[
|
18
|
-
parser.parse('z')[
|
19
|
-
parser.parse('zz')[
|
20
|
-
parser.parse('y')[
|
17
|
+
parser.parse('x')[2].should == true
|
18
|
+
parser.parse('z')[2].should == true
|
19
|
+
parser.parse('zz')[2].should == true
|
20
|
+
parser.parse('y')[2].should == false
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'sets its name in the result (as a Symbol)' do
|
@@ -27,9 +27,9 @@ describe Neg::Parser::NonTerminalParser do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
parser.parse('X').should ==
|
30
|
-
[ :x,
|
31
|
-
[ nil,
|
32
|
-
[ nil,
|
30
|
+
[ :x, [ 0, 1, 1 ], true, nil, [
|
31
|
+
[ nil, [ 0, 1, 1 ], false, "expected \"x\", got \"X\"", [] ],
|
32
|
+
[ nil, [ 0, 1, 1 ], true, "X", [] ] ] ]
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'is rendered as x when on the right side' do
|
@@ -48,6 +48,16 @@ describe Neg::Parser::NonTerminalParser do
|
|
48
48
|
root: word
|
49
49
|
}.strip
|
50
50
|
end
|
51
|
+
|
52
|
+
it 'flips burgers' do
|
53
|
+
|
54
|
+
parser = Class.new(Neg::Parser) do
|
55
|
+
sentence == (word + ` `) * 1
|
56
|
+
word == _("a-z") * 1
|
57
|
+
end
|
58
|
+
|
59
|
+
pp parser.parse("ab cd ")
|
60
|
+
end
|
51
61
|
end
|
52
62
|
|
53
63
|
context '...["name"]' do
|
@@ -74,11 +84,14 @@ describe Neg::Parser::NonTerminalParser do
|
|
74
84
|
|
75
85
|
parser.parse('car_cluj').should ==
|
76
86
|
[ :transportation,
|
87
|
+
[ 0, 1, 1 ],
|
77
88
|
true,
|
78
|
-
|
79
|
-
[ [ 'vehicle',
|
80
|
-
|
81
|
-
[
|
89
|
+
nil,
|
90
|
+
[ [ 'vehicle', [ 0, 1, 1 ], true, nil, [
|
91
|
+
[ nil, [ 0, 1, 1 ], true, 'car', [] ] ] ],
|
92
|
+
[ nil, [ 3, 1, 4 ], true, '_', [] ],
|
93
|
+
[ 'city', [ 4, 1, 5 ], true, nil, [
|
94
|
+
[ nil, [ 4, 1, 5 ], true, 'cluj', [] ] ] ] ] ]
|
82
95
|
end
|
83
96
|
end
|
84
97
|
end
|
@@ -15,7 +15,7 @@ describe Neg::Parser::RepetitionParser do
|
|
15
15
|
it 'parses the empty string' do
|
16
16
|
|
17
17
|
parser.parse('').should ==
|
18
|
-
[ :text,
|
18
|
+
[ :text, [ 0, 1, 1 ], true, nil, [] ]
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'fails gracefully' do
|
@@ -48,16 +48,16 @@ describe Neg::Parser::RepetitionParser do
|
|
48
48
|
it 'parses the empty string' do
|
49
49
|
|
50
50
|
parser.parse('').should ==
|
51
|
-
[ :text,
|
51
|
+
[ :text, [ 0, 1, 1 ], true, nil, [] ]
|
52
52
|
end
|
53
53
|
|
54
54
|
it 'parses' do
|
55
55
|
|
56
56
|
parser.parse('xxx').should ==
|
57
|
-
[ :text,
|
58
|
-
[ nil,
|
59
|
-
[ nil,
|
60
|
-
[ nil,
|
57
|
+
[ :text, [ 0, 1, 1 ], true, nil, [
|
58
|
+
[ nil, [ 0, 1, 1 ], true, 'x', [] ],
|
59
|
+
[ nil, [ 1, 1, 2 ], true, 'x', [] ],
|
60
|
+
[ nil, [ 2, 1, 3 ], true, 'x', [] ] ] ]
|
61
61
|
end
|
62
62
|
|
63
63
|
it 'fails gracefully' do
|
@@ -81,18 +81,18 @@ describe Neg::Parser::RepetitionParser do
|
|
81
81
|
it 'parses' do
|
82
82
|
|
83
83
|
parser.parse('xxx').should ==
|
84
|
-
[ :text,
|
85
|
-
[ nil,
|
86
|
-
[ nil,
|
87
|
-
[ nil,
|
84
|
+
[ :text, [ 0, 1, 1 ], true, nil, [
|
85
|
+
[ nil, [ 0, 1, 1 ], true, 'x', [] ],
|
86
|
+
[ nil, [ 1, 1, 2 ], true, 'x', [] ],
|
87
|
+
[ nil, [ 2, 1, 3 ], true, 'x', [] ] ] ]
|
88
88
|
end
|
89
89
|
|
90
90
|
it 'fails gracefully' do
|
91
91
|
|
92
92
|
parser.parse('x').should ==
|
93
|
-
[ :text,
|
94
|
-
[ nil,
|
95
|
-
[ nil,
|
93
|
+
[ :text, [ 0, 1, 1 ], false, nil, [
|
94
|
+
[ nil, [ 0, 1, 1 ], true, 'x', [] ],
|
95
|
+
[ nil, [ 1, 1, 2 ], false, 'expected "x", got ""', [] ] ] ]
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
@@ -107,19 +107,19 @@ describe Neg::Parser::RepetitionParser do
|
|
107
107
|
it 'parses' do
|
108
108
|
|
109
109
|
parser.parse('xxx').should ==
|
110
|
-
[ :text,
|
111
|
-
[ nil,
|
112
|
-
[ nil,
|
113
|
-
[ nil,
|
110
|
+
[ :text, [ 0, 1, 1 ], true, nil, [
|
111
|
+
[ nil, [ 0, 1, 1 ], true, 'x', [] ],
|
112
|
+
[ nil, [ 1, 1, 2 ], true, 'x', [] ],
|
113
|
+
[ nil, [ 2, 1, 3 ], true, 'x', [] ] ] ]
|
114
114
|
end
|
115
115
|
|
116
116
|
it 'fails gracefully' do
|
117
117
|
|
118
118
|
parser.parse('xx').should ==
|
119
|
-
[ :text,
|
120
|
-
[ nil,
|
121
|
-
[ nil,
|
122
|
-
[ nil,
|
119
|
+
[ :text, [ 0, 1, 1 ], false, nil, [
|
120
|
+
[ nil, [ 0, 1, 1 ], true, 'x', [] ],
|
121
|
+
[ nil, [ 1, 1, 2 ], true, 'x', [] ],
|
122
|
+
[ nil, [ 2, 1, 3 ], false, 'expected "x", got ""', [] ] ] ]
|
123
123
|
end
|
124
124
|
|
125
125
|
it 'fails gracefully (unconsumed input)' do
|
@@ -11,17 +11,17 @@ describe Neg::Parser::SequenceParser do
|
|
11
11
|
it 'parses' do
|
12
12
|
|
13
13
|
SeqParser.parse('xy').should ==
|
14
|
-
[ :text,
|
15
|
-
[ nil,
|
16
|
-
[ nil,
|
14
|
+
[ :text, [ 0, 1, 1 ], true, nil, [
|
15
|
+
[ nil, [ 0, 1, 1 ], true, 'x', [] ],
|
16
|
+
[ nil, [ 1, 1, 2 ], true, 'y', [] ] ] ]
|
17
17
|
end
|
18
18
|
|
19
19
|
it 'fails gracefully' do
|
20
20
|
|
21
21
|
SeqParser.parse('xx').should ==
|
22
|
-
[ :text,
|
23
|
-
[ nil,
|
24
|
-
[ nil,
|
22
|
+
[ :text, [ 0, 1, 1 ], false, nil, [
|
23
|
+
[ nil, [ 0, 1, 1 ], true, 'x', [] ],
|
24
|
+
[ nil, [ 1, 1, 2 ], false, 'expected "y", got "x"', [] ] ] ]
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'goes beyond two elements' do
|
@@ -48,18 +48,21 @@ describe Neg::Parser::SequenceParser do
|
|
48
48
|
|
49
49
|
parser.parse('pool').should ==
|
50
50
|
[ :word,
|
51
|
-
true,
|
52
51
|
[ 0, 1, 1 ],
|
52
|
+
true,
|
53
|
+
nil,
|
53
54
|
[ [ :poodle,
|
54
|
-
false,
|
55
55
|
[ 0, 1, 1 ],
|
56
|
-
|
57
|
-
|
56
|
+
false,
|
57
|
+
nil,
|
58
|
+
[ [ nil, [ 0, 1, 1 ], true, "poo", [] ],
|
59
|
+
[ nil, [ 3, 1, 4 ], false, "expected \"dle\", got \"l\"", [] ] ] ],
|
58
60
|
[ :pool,
|
61
|
+
[ 0, 1, 1 ],
|
59
62
|
true,
|
60
|
-
|
61
|
-
[ [ nil,
|
62
|
-
[ nil,
|
63
|
+
nil,
|
64
|
+
[ [ nil, [ 0, 1, 1 ], true, "poo", [] ],
|
65
|
+
[ nil, [ 3, 1, 4 ], true, "l", [] ] ] ] ] ]
|
63
66
|
end
|
64
67
|
end
|
65
68
|
|
data/spec/parser_string_spec.rb
CHANGED
@@ -11,7 +11,7 @@ describe Neg::Parser::StringParser do
|
|
11
11
|
parser = Neg::Parser::StringParser.new('xxx')
|
12
12
|
|
13
13
|
parser.parse('xxx').should ==
|
14
|
-
[ nil,
|
14
|
+
[ nil, [ 0, 1, 1 ], true, 'xxx', [] ]
|
15
15
|
end
|
16
16
|
|
17
17
|
it 'fails gracefully' do
|
@@ -19,7 +19,7 @@ describe Neg::Parser::StringParser do
|
|
19
19
|
parser = Neg::Parser::StringParser.new('xxx')
|
20
20
|
|
21
21
|
parser.parse('yyy').should ==
|
22
|
-
[ nil,
|
22
|
+
[ nil, [ 0, 1, 1 ], false, 'expected "xxx", got "yyy"', [] ]
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -34,7 +34,7 @@ describe Neg::Parser::StringParser do
|
|
34
34
|
it 'parses an exact string' do
|
35
35
|
|
36
36
|
parser.parse('x').should ==
|
37
|
-
[ :root,
|
37
|
+
[ :root, [ 0, 1, 1 ], true, 'x', [] ]
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -6,23 +6,7 @@ describe 'sample JSON parser' do
|
|
6
6
|
|
7
7
|
class JsonParser < Neg::Parser
|
8
8
|
|
9
|
-
#rule(:spaces) { match('\s').repeat(1) }
|
10
|
-
#rule(:spaces?) { spaces.maybe }
|
11
|
-
|
12
9
|
#rule(:comma) { spaces? >> str(',') >> spaces? }
|
13
|
-
#rule(:digit) { match('[0-9]') }
|
14
|
-
|
15
|
-
#rule(:number) {
|
16
|
-
# (
|
17
|
-
# str('-').maybe >> (
|
18
|
-
# str('0') | (match('[1-9]') >> digit.repeat)
|
19
|
-
# ) >> (
|
20
|
-
# str('.') >> digit.repeat(1)
|
21
|
-
# ).maybe >> (
|
22
|
-
# match('[eE]') >> (str('+') | str('-')).maybe >> digit.repeat(1)
|
23
|
-
# ).maybe
|
24
|
-
# ).as(:number)
|
25
|
-
#}
|
26
10
|
|
27
11
|
#rule(:array) {
|
28
12
|
# str('[') >> spaces? >>
|
@@ -46,31 +30,32 @@ describe 'sample JSON parser' do
|
|
46
30
|
|
47
31
|
#rule(:attribute) { (entry | value).as(:attribute) }
|
48
32
|
|
49
|
-
json == spaces? + value + spaces?
|
50
|
-
|
51
|
-
value == string | number | object | array | btrue | bfalse | null
|
52
|
-
|
53
|
-
btrue == `true`
|
54
|
-
bfalse == `false`
|
55
|
-
null == `null`
|
56
|
-
|
57
|
-
# TODO: continue here with "string"
|
58
|
-
|
59
33
|
#rule(:string) {
|
60
34
|
# str('"') >> (
|
61
|
-
# str('\\') >> any | str('"').absent? >> any
|
35
|
+
# #str('\\') >> any | str('"').absent? >> any
|
36
|
+
# #(str('\\') | str('"').absent?) >> any
|
37
|
+
# (str('\\') >> any | match('[^"]')
|
62
38
|
# ).repeat.as(:string) >> str('"')
|
63
39
|
#}
|
64
40
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
# str('true').as(:true) | str('false').as(:false) |
|
69
|
-
# str('null').as(:null)
|
70
|
-
#}
|
41
|
+
json == spaces? + value + spaces?
|
42
|
+
|
43
|
+
spaces? == _(" \t") * 0
|
71
44
|
|
72
|
-
#
|
73
|
-
|
45
|
+
#value == string | number | object | array | btrue | bfalse | null
|
46
|
+
value == number | btrue | bfalse | null
|
47
|
+
|
48
|
+
digit == _("0-9")
|
49
|
+
|
50
|
+
number ==
|
51
|
+
`-` * -1 +
|
52
|
+
(`0` | (_("1-9") + digit * 0)) +
|
53
|
+
(`.` + digit * 1) * -1 +
|
54
|
+
(_("eE") + _("+-") * -1 + digit * 1) * -1
|
55
|
+
|
56
|
+
btrue == `true`
|
57
|
+
bfalse == `false`
|
58
|
+
null == `null`
|
74
59
|
end
|
75
60
|
|
76
61
|
it 'flips burgers' do
|
@@ -79,5 +64,23 @@ describe 'sample JSON parser' do
|
|
79
64
|
puts JsonParser
|
80
65
|
puts '-' * 80
|
81
66
|
end
|
67
|
+
|
68
|
+
it 'parses "false"' do
|
69
|
+
|
70
|
+
pp JsonParser.parse("false")
|
71
|
+
JsonParser.parse("false")[2].should == true
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'parses "13"' do
|
75
|
+
|
76
|
+
pp JsonParser.parse("13")
|
77
|
+
JsonParser.parse("13")[2].should == true
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'parses "-12"' do
|
81
|
+
|
82
|
+
pp JsonParser.parse("-12")
|
83
|
+
JsonParser.parse("-12")[2].should == true
|
84
|
+
end
|
82
85
|
end
|
83
86
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: neg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-10-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -58,6 +58,7 @@ files:
|
|
58
58
|
- spec/input_spec.rb
|
59
59
|
- spec/parser_alternative_spec.rb
|
60
60
|
- spec/parser_character_spec.rb
|
61
|
+
- spec/parser_lookahead_parser_spec.rb
|
61
62
|
- spec/parser_non_terminal_spec.rb
|
62
63
|
- spec/parser_repetition_spec.rb
|
63
64
|
- spec/parser_sequence_spec.rb
|