neg 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +7 -0
- data/README.md +16 -13
- data/TODO.txt +8 -15
- data/lib/neg/errors.rb +22 -6
- data/lib/neg/parser.rb +42 -20
- data/lib/neg/translator.rb +16 -2
- data/lib/neg/version.rb +1 -1
- data/neg.gemspec +1 -1
- data/spec/greedy_spec.rb +115 -0
- data/spec/parser_repetition_spec.rb +25 -22
- data/spec/parser_spec.rb +39 -17
- data/spec/sample_arith_spec.rb +6 -6
- data/spec/sample_compact_spec.rb +5 -5
- data/spec/sample_json_parser_spec.rb +4 -10
- data/spec/sample_scheme_spec.rb +39 -0
- metadata +20 -18
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,13 @@
|
|
2
2
|
# neg - CHANGELOG.md
|
3
3
|
|
4
4
|
|
5
|
+
## neg - 1.1.0 released 2013-02-28
|
6
|
+
|
7
|
+
- giving the "right" error on unconsumed input
|
8
|
+
- get rid of UnconsumedInputError
|
9
|
+
- translator: introduce n.flattened_results
|
10
|
+
|
11
|
+
|
5
12
|
## neg - 1.0.0 released 2013-01-16
|
6
13
|
|
7
14
|
- initial release
|
data/README.md
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
# neg
|
3
2
|
|
4
3
|
A neg narser.
|
@@ -30,11 +29,11 @@ Here is the classical arithmetic example:
|
|
30
29
|
|
31
30
|
expression == operation
|
32
31
|
|
33
|
-
operator
|
34
|
-
operation
|
35
|
-
value
|
36
|
-
|
37
|
-
number
|
32
|
+
operator == `+` | `-` | `*` | `/`
|
33
|
+
operation == value + (operator + value) * 0
|
34
|
+
value == parentheses | number
|
35
|
+
parentheses == `(` + expression + `)`
|
36
|
+
number == `-` * -1 + _('0-9') * 1
|
38
37
|
end
|
39
38
|
|
40
39
|
tree = ArithParser.parse("1+(2*12)")
|
@@ -116,7 +115,7 @@ It's a nested assemblage of result nodes.
|
|
116
115
|
[ :bfalse, [ 0, 1, 1 ], true, 'false', [] ]
|
117
116
|
```
|
118
117
|
|
119
|
-
In case of successful parsing, the
|
118
|
+
In case of successful parsing, the success? == false also get all pruned. In case of failed parsing, they are left in the output parse tree.
|
120
119
|
|
121
120
|
A translator turns a raw parse tree into some final result. Look below and at the JSON parser sample in the specs for more information. If the parse failed and a translator is present, a ParseError is raised.
|
122
121
|
|
@@ -132,11 +131,11 @@ class CompactArithParser < Neg::Parser
|
|
132
131
|
|
133
132
|
expression == operation
|
134
133
|
|
135
|
-
operator
|
136
|
-
operation
|
137
|
-
value
|
138
|
-
|
139
|
-
number
|
134
|
+
operator == `+` | `-` | `*` | `/`
|
135
|
+
operation == value + (operator + value) * 0
|
136
|
+
value == parentheses | number
|
137
|
+
parentheses == `(` + expression + `)`
|
138
|
+
number == `-` * -1 + _('0-9') * 1
|
140
139
|
end
|
141
140
|
|
142
141
|
translator do
|
@@ -146,7 +145,7 @@ class CompactArithParser < Neg::Parser
|
|
146
145
|
on(:value) { |n| n.results.first }
|
147
146
|
|
148
147
|
on(:expression) { |n|
|
149
|
-
results = n.
|
148
|
+
results = n.flattened_results
|
150
149
|
results.size == 1 ? results.first : results
|
151
150
|
}
|
152
151
|
end
|
@@ -156,8 +155,12 @@ CompactArithParser.parse("1+2+3")
|
|
156
155
|
# => [ 1, '+', 2, '+', 3 ]
|
157
156
|
```
|
158
157
|
|
158
|
+
The original of this parser lies in [spec/sample_arith_spec.rb](spec/sample_arith_spec.rb). Please note that it's very dumb (like everything in neg) and, for example, avoids carefully dealing with operator precedence for its target language.
|
159
|
+
|
159
160
|
As said above, when a translator is present and the parsing fails (before the translator kicks in), a ParseError is raised, with fancy methods to navigate the failed parse tree.
|
160
161
|
|
162
|
+
See also the [sample JSON parser](spec/sample_json_parser_spec.rb) and a [tiny toy scheme parser](spec/sample_scheme_spec.rb).
|
163
|
+
|
161
164
|
|
162
165
|
## presentations
|
163
166
|
|
data/TODO.txt
CHANGED
@@ -1,17 +1,10 @@
|
|
1
1
|
|
2
|
-
[o]
|
3
|
-
|
4
|
-
[o]
|
5
|
-
[o]
|
6
|
-
[o]
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
[ ] blankslate
|
12
|
-
[ ] drop UnconsumedInput, replace with regular [ false, ... ] output
|
13
|
-
[ ] x * '?' / x * '+' / x * '*' as shortcuts
|
14
|
-
[ ] memoization (only at non-terminal level?)
|
15
|
-
|
16
|
-
[ ] "xxx" instead of `xxx` (trick on the right side)
|
2
|
+
[o] investigate translator rule returning [] (json empty array for instance)
|
3
|
+
does it get discarded like the others?
|
4
|
+
[o] include the on(nil) rule by default?
|
5
|
+
[o] raise error (with #error_tree) when translator hits unparsable input
|
6
|
+
[o] document ...['name'] in the README
|
7
|
+
|
8
|
+
[o] verify that the Parslet UnconsumedInput problem doesn't appear in neg
|
9
|
+
[o] second look at the blankslate
|
17
10
|
|
data/lib/neg/errors.rb
CHANGED
@@ -27,19 +27,20 @@ module Neg
|
|
27
27
|
|
28
28
|
class NegError < StandardError; end
|
29
29
|
|
30
|
-
class UnconsumedInputError < NegError; end
|
31
30
|
class ParserError < NegError; end
|
32
31
|
|
33
32
|
class ParseError < NegError
|
34
33
|
|
35
|
-
attr_reader :tree
|
34
|
+
attr_reader :tree, :position
|
36
35
|
|
37
36
|
def initialize(tree)
|
38
37
|
|
39
38
|
@tree = tree
|
40
39
|
@nodes = list_nodes(tree)
|
41
40
|
|
42
|
-
|
41
|
+
d = deepest_error
|
42
|
+
@position = d[1]
|
43
|
+
super(d[3])
|
43
44
|
end
|
44
45
|
|
45
46
|
def errors
|
@@ -49,15 +50,30 @@ module Neg
|
|
49
50
|
|
50
51
|
def deepest_error
|
51
52
|
|
52
|
-
|
53
|
+
# let's keep the tree depth (e[5]) for later
|
54
|
+
|
55
|
+
errors.inject do |eold, enew|
|
56
|
+
if eold[1][0] <= enew[1][0]
|
57
|
+
enew
|
58
|
+
else
|
59
|
+
eold
|
60
|
+
end
|
61
|
+
end
|
53
62
|
end
|
54
63
|
|
64
|
+
def offset; @position[0]; end
|
65
|
+
def line; @position[1]; end
|
66
|
+
def column; @position[2]; end
|
67
|
+
|
55
68
|
protected
|
56
69
|
|
57
|
-
def list_nodes(start, accumulator=[])
|
70
|
+
def list_nodes(start, depth=0, accumulator=[])
|
71
|
+
|
72
|
+
start = start.dup
|
73
|
+
start << depth
|
58
74
|
|
59
75
|
accumulator << start
|
60
|
-
start[4].each { |n| list_nodes(n, accumulator) }
|
76
|
+
start[4].each { |n| list_nodes(n, depth + 1, accumulator) }
|
61
77
|
|
62
78
|
accumulator
|
63
79
|
end
|
data/lib/neg/parser.rb
CHANGED
@@ -58,15 +58,26 @@ module Neg
|
|
58
58
|
pa
|
59
59
|
end
|
60
60
|
|
61
|
+
def self.reduce(result)
|
62
|
+
|
63
|
+
if result[0] && result[2] && result[3]
|
64
|
+
result[4] =
|
65
|
+
[]
|
66
|
+
else
|
67
|
+
result[4] =
|
68
|
+
result[4].each_with_object([]) { |cr, a| a << reduce(cr) if cr[2] }
|
69
|
+
end
|
70
|
+
|
71
|
+
result
|
72
|
+
end
|
73
|
+
|
61
74
|
def self.parse(s, opts={})
|
62
75
|
|
63
76
|
i = Neg::Input(s)
|
64
77
|
|
65
78
|
result = __send__(@root).parse(i, opts)
|
66
79
|
|
67
|
-
|
68
|
-
"remaining: #{i.remains.inspect}"
|
69
|
-
) if result[2] && ( ! i.eoi?)
|
80
|
+
result[2] = false if result[2] && ( ! i.eoi?)
|
70
81
|
|
71
82
|
if @translator && opts[:translate] != false
|
72
83
|
if result[2]
|
@@ -74,8 +85,10 @@ module Neg
|
|
74
85
|
else
|
75
86
|
raise ParseError.new(result)
|
76
87
|
end
|
77
|
-
|
88
|
+
elsif result[2] == false || opts[:noreduce]
|
78
89
|
result
|
90
|
+
else
|
91
|
+
reduce(result)
|
79
92
|
end
|
80
93
|
end
|
81
94
|
|
@@ -123,10 +136,6 @@ module Neg
|
|
123
136
|
|
124
137
|
input.rewind(start) unless success
|
125
138
|
|
126
|
-
#if success && children.size == 1 && children.first[1] == start
|
127
|
-
# return children.first
|
128
|
-
#end
|
129
|
-
|
130
139
|
[ nil, start, success, result, children ]
|
131
140
|
end
|
132
141
|
end
|
@@ -144,13 +153,13 @@ module Neg
|
|
144
153
|
@child = pa
|
145
154
|
end
|
146
155
|
|
147
|
-
def
|
156
|
+
def refine(children_results)
|
148
157
|
|
149
158
|
children_results.collect { |cr|
|
150
159
|
if cr[0] && cr[0].to_s.match(/^_/).nil?
|
151
160
|
false
|
152
161
|
elsif cr[2]
|
153
|
-
cr[3] ? cr[3] :
|
162
|
+
cr[3] ? cr[3] : refine(cr[4])
|
154
163
|
else
|
155
164
|
nil
|
156
165
|
end
|
@@ -166,11 +175,10 @@ module Neg
|
|
166
175
|
return r if r[0] == false
|
167
176
|
return r if r[1].is_a?(String)
|
168
177
|
|
169
|
-
report =
|
178
|
+
report = refine(r[2])
|
179
|
+
report = report.include?(false) ? nil : report.join
|
170
180
|
|
171
|
-
|
172
|
-
|
173
|
-
[ true, report.join, opts[:noreduce] ? r[2] : [] ]
|
181
|
+
[ true, report, r[2] ]
|
174
182
|
end
|
175
183
|
|
176
184
|
def parse(input_or_string, opts)
|
@@ -214,14 +222,28 @@ module Neg
|
|
214
222
|
rs = []
|
215
223
|
|
216
224
|
loop do
|
217
|
-
|
218
|
-
break if
|
219
|
-
rs << r
|
220
|
-
break if ! r[2]
|
221
|
-
break if rs.size == @max
|
225
|
+
rs << @child.parse(i, opts)
|
226
|
+
break if rs.last[2] == false
|
222
227
|
end
|
223
228
|
|
224
|
-
|
229
|
+
rs.size.downto(1) do |i|
|
230
|
+
|
231
|
+
r = rs[i - 1]
|
232
|
+
|
233
|
+
next if r[2] == false
|
234
|
+
|
235
|
+
if @max && i > @max
|
236
|
+
r[2] = false
|
237
|
+
r[3] = "did not expect #{r[3].inspect} (min #{@min} max #{@max})"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
trs = rs.take_while { |r| r[2] == true }
|
242
|
+
rs = trs + [ rs.find { |r| r[2] == false } ]
|
243
|
+
|
244
|
+
success = trs.size >= @min && (@max.nil? || trs.size <= @max)
|
245
|
+
|
246
|
+
i.rewind(rs.last[1]) if success && rs.last[2] == false
|
225
247
|
|
226
248
|
[ success, nil, rs ]
|
227
249
|
end
|
data/lib/neg/translator.rb
CHANGED
@@ -40,8 +40,8 @@ module Neg
|
|
40
40
|
def self.translate(parse_tree)
|
41
41
|
|
42
42
|
results =
|
43
|
-
parse_tree[4].each_with_object([]) { |
|
44
|
-
catch(nil) { a << translate(
|
43
|
+
parse_tree[4].each_with_object([]) { |tree, a|
|
44
|
+
catch(nil) { a << translate(tree) } if tree[2]
|
45
45
|
}
|
46
46
|
|
47
47
|
apply(parse_tree, results)
|
@@ -70,6 +70,20 @@ module Neg
|
|
70
70
|
def column; @parse_tree[1][2]; end
|
71
71
|
|
72
72
|
def result; @parse_tree[3]; end
|
73
|
+
|
74
|
+
# Useful when the grammar has something of the form:
|
75
|
+
#
|
76
|
+
# array == `[` + (value + (`,` + value) * 0) * 0 + `]`
|
77
|
+
#
|
78
|
+
# It flattens the value array.
|
79
|
+
#
|
80
|
+
# Look at the spec/sample_* files to see it in action.
|
81
|
+
#
|
82
|
+
def flattened_results
|
83
|
+
|
84
|
+
f2 = results.flatten(2)
|
85
|
+
f2.any? ? [ f2.shift ] + f2.flatten(2) : []
|
86
|
+
end
|
73
87
|
end
|
74
88
|
end
|
75
89
|
end
|
data/lib/neg/version.rb
CHANGED
data/neg.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.platform = Gem::Platform::RUBY
|
11
11
|
s.authors = [ 'John Mettraux' ]
|
12
12
|
s.email = [ 'jmettraux@gmail.com' ]
|
13
|
-
s.homepage = 'https://github.com/jmettraux/
|
13
|
+
s.homepage = 'https://github.com/jmettraux/neg'
|
14
14
|
s.rubyforge_project = 'ruote'
|
15
15
|
s.summary = 'a neg narser'
|
16
16
|
|
data/spec/greedy_spec.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
|
5
|
+
describe 'neg and errors' do
|
6
|
+
|
7
|
+
class BlockParser < Neg::Parser
|
8
|
+
|
9
|
+
parser do
|
10
|
+
|
11
|
+
blocks == nl? + block + (nl + block) * 0 + nl?
|
12
|
+
|
13
|
+
block == sp? + `begin` + sp + _('a-z') + nl + body + sp? + `end`
|
14
|
+
body == ((line | block) + nl) * 0
|
15
|
+
line == sp + `line`
|
16
|
+
|
17
|
+
nl == _("\s\n") * 1
|
18
|
+
nl? == _("\s\n") * 0
|
19
|
+
sp == _(" ") * 1
|
20
|
+
sp? == _(" ") * 0
|
21
|
+
end
|
22
|
+
|
23
|
+
translator do
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'parses a single empty block' do
|
28
|
+
|
29
|
+
BlockParser.parse(%{
|
30
|
+
begin a
|
31
|
+
end
|
32
|
+
})
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'parses nested blocks' do
|
36
|
+
|
37
|
+
BlockParser.parse(%{
|
38
|
+
begin a
|
39
|
+
begin b
|
40
|
+
end
|
41
|
+
end
|
42
|
+
})
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'parses successive blocks' do
|
46
|
+
|
47
|
+
BlockParser.parse(%{
|
48
|
+
begin a
|
49
|
+
end
|
50
|
+
begin b
|
51
|
+
end
|
52
|
+
})
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'fails gracefully on a missing end (0)' do
|
56
|
+
|
57
|
+
err = nil
|
58
|
+
|
59
|
+
begin
|
60
|
+
BlockParser.parse(%{
|
61
|
+
begin a
|
62
|
+
begin b
|
63
|
+
end
|
64
|
+
})
|
65
|
+
rescue => err
|
66
|
+
end
|
67
|
+
|
68
|
+
err.class.should == Neg::ParseError
|
69
|
+
err.position.should == [ 53, 5, 6 ]
|
70
|
+
err.message.should == 'expected "end", got ""'
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'fails gracefully on a missing end (1)' do
|
74
|
+
|
75
|
+
err = nil
|
76
|
+
|
77
|
+
begin
|
78
|
+
BlockParser.parse(%{
|
79
|
+
begin a
|
80
|
+
end
|
81
|
+
begin b
|
82
|
+
begin c
|
83
|
+
end
|
84
|
+
})
|
85
|
+
rescue => err
|
86
|
+
end
|
87
|
+
|
88
|
+
err.class.should == Neg::ParseError
|
89
|
+
err.position.should == [ 81, 7, 6 ]
|
90
|
+
err.message.should == 'expected "end", got ""'
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'fails gracefully on a li too much (2)' do
|
94
|
+
|
95
|
+
err = nil
|
96
|
+
|
97
|
+
begin
|
98
|
+
BlockParser.parse(%{
|
99
|
+
begin a
|
100
|
+
end
|
101
|
+
begin b
|
102
|
+
begin c
|
103
|
+
li
|
104
|
+
end
|
105
|
+
end
|
106
|
+
})
|
107
|
+
rescue => err
|
108
|
+
end
|
109
|
+
|
110
|
+
err.class.should == Neg::ParseError
|
111
|
+
err.position.should == [ 75, 6, 12 ]
|
112
|
+
err.message.should == 'expected "end", got "li\n"'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
@@ -18,14 +18,15 @@ describe Neg::Parser::RepetitionParser do
|
|
18
18
|
[ :text, [ 0, 1, 1 ], true, '', [] ]
|
19
19
|
end
|
20
20
|
|
21
|
-
it 'fails gracefully' do
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
21
|
+
#it 'fails gracefully' do
|
22
|
+
# lambda {
|
23
|
+
# parser.parse('xx')
|
24
|
+
# }.should raise_error(
|
25
|
+
# Neg::UnconsumedInputError,
|
26
|
+
# 'remaining: "x"')
|
27
|
+
#end
|
28
|
+
#
|
29
|
+
# UnconsumedInputError is gone.
|
29
30
|
|
30
31
|
it 'is rendered correctly via #to_s' do
|
31
32
|
|
@@ -57,14 +58,15 @@ describe Neg::Parser::RepetitionParser do
|
|
57
58
|
[ :text, [ 0, 1, 1 ], true, 'xxx', [] ]
|
58
59
|
end
|
59
60
|
|
60
|
-
it 'fails gracefully' do
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
61
|
+
#it 'fails gracefully' do
|
62
|
+
# lambda {
|
63
|
+
# parser.parse('a')
|
64
|
+
# }.should raise_error(
|
65
|
+
# Neg::UnconsumedInputError,
|
66
|
+
# 'remaining: "a"')
|
67
|
+
#end
|
68
|
+
#
|
69
|
+
# UnconsumedInputError is gone.
|
68
70
|
end
|
69
71
|
|
70
72
|
context '`x` * 2 (at least 2)' do
|
@@ -113,12 +115,13 @@ describe Neg::Parser::RepetitionParser do
|
|
113
115
|
[ nil, [ 2, 1, 3 ], false, 'expected "x", got ""', [] ] ] ]
|
114
116
|
end
|
115
117
|
|
116
|
-
it 'fails gracefully (unconsumed input)' do
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
118
|
+
#it 'fails gracefully (unconsumed input)' do
|
119
|
+
# lambda {
|
120
|
+
# parser.parse('xxxx')
|
121
|
+
# }.should raise_error(Neg::UnconsumedInputError, 'remaining: "x"')
|
122
|
+
#end
|
123
|
+
#
|
124
|
+
# UnconsumedInputError is gone.
|
122
125
|
|
123
126
|
it 'is rendered correctly via #to_s' do
|
124
127
|
|
data/spec/parser_spec.rb
CHANGED
@@ -33,30 +33,52 @@ describe 'Neg::Parser' do
|
|
33
33
|
#
|
34
34
|
# not worth the pain for now
|
35
35
|
|
36
|
-
describe '.parse' do
|
36
|
+
# describe '.parse' do
|
37
|
+
#
|
38
|
+
# let(:parser) {
|
39
|
+
# Class.new(Neg::Parser) do
|
40
|
+
# text == `x`
|
41
|
+
# end
|
42
|
+
# }
|
43
|
+
#
|
44
|
+
# it 'raises on unconsumed input' do
|
45
|
+
#
|
46
|
+
# lambda {
|
47
|
+
# parser.parse('xy')
|
48
|
+
# }.should raise_error(
|
49
|
+
# Neg::UnconsumedInputError,
|
50
|
+
# 'remaining: "y"')
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# it 'raises on unconsumed input (...)' do
|
54
|
+
#
|
55
|
+
# lambda {
|
56
|
+
# parser.parse('xyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy')
|
57
|
+
# }.should raise_error(
|
58
|
+
# Neg::UnconsumedInputError,
|
59
|
+
# 'remaining: "yyyyyyy..."')
|
60
|
+
# end
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# UnconsumedInputError is gone.
|
64
|
+
|
65
|
+
context 'unconsumed input' do
|
37
66
|
|
38
67
|
let(:parser) {
|
39
68
|
Class.new(Neg::Parser) do
|
40
|
-
text == `x`
|
69
|
+
text == `x` * -1
|
41
70
|
end
|
42
71
|
}
|
43
72
|
|
44
|
-
it '
|
45
|
-
|
46
|
-
lambda {
|
47
|
-
parser.parse('xy')
|
48
|
-
}.should raise_error(
|
49
|
-
Neg::UnconsumedInputError,
|
50
|
-
'remaining: "y"')
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'raises on unconsumed input (...)' do
|
73
|
+
it 'fails gracefully' do
|
54
74
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
75
|
+
parser.parse('xx').should ==
|
76
|
+
[ :text,
|
77
|
+
[ 0, 1, 1 ],
|
78
|
+
false,
|
79
|
+
'x',
|
80
|
+
[ [ nil, [0, 1, 1], true, 'x', [] ],
|
81
|
+
[ nil, [1, 1, 2], false, 'did not expect "x" (min 0 max 1)', [] ] ] ]
|
60
82
|
end
|
61
83
|
end
|
62
84
|
end
|
data/spec/sample_arith_spec.rb
CHANGED
@@ -8,11 +8,11 @@ describe 'sample math parser' do
|
|
8
8
|
|
9
9
|
expression == operation
|
10
10
|
|
11
|
-
operator
|
12
|
-
operation
|
13
|
-
value
|
14
|
-
|
15
|
-
number
|
11
|
+
operator == `+` | `-` | `*` | `/`
|
12
|
+
operation == value + (operator + value) * 0
|
13
|
+
value == parentheses | number
|
14
|
+
parentheses == `(` + expression + `)`
|
15
|
+
number == `-` * -1 + _('0-9') * 1
|
16
16
|
end
|
17
17
|
|
18
18
|
class ArithTranslator < Neg::Translator
|
@@ -22,7 +22,7 @@ describe 'sample math parser' do
|
|
22
22
|
on(:value) { |n| n.results.first }
|
23
23
|
|
24
24
|
on(:expression) { |n|
|
25
|
-
results = n.
|
25
|
+
results = n.flattened_results
|
26
26
|
results.size == 1 ? results.first : results
|
27
27
|
}
|
28
28
|
end
|
data/spec/sample_compact_spec.rb
CHANGED
@@ -10,11 +10,11 @@ describe 'sample compact arith parser' do
|
|
10
10
|
|
11
11
|
expression == operation
|
12
12
|
|
13
|
-
operator
|
14
|
-
operation
|
15
|
-
value
|
16
|
-
|
17
|
-
number
|
13
|
+
operator == `+` | `-` | `*` | `/`
|
14
|
+
operation == value + (operator + value) * 0
|
15
|
+
value == parentheses | number
|
16
|
+
parentheses == `(` + expression + `)`
|
17
|
+
number == `-` * -1 + _('0-9') * 1
|
18
18
|
end
|
19
19
|
|
20
20
|
translator do
|
@@ -40,14 +40,8 @@ describe 'sample JSON parser' do
|
|
40
40
|
on(:value) { |n| n.results.first.first }
|
41
41
|
on(:spaces?) { throw nil }
|
42
42
|
|
43
|
-
on(:object) { |n|
|
44
|
-
|
45
|
-
Hash[f2.any? ? [ f2.shift ] + f2.flatten(2) : []]
|
46
|
-
}
|
47
|
-
on(:array) { |n|
|
48
|
-
f2 = n.results.flatten(2)
|
49
|
-
f2.any? ? [ f2.shift ] + f2.flatten(2) : []
|
50
|
-
}
|
43
|
+
on(:object) { |n| Hash[n.flattened_results] }
|
44
|
+
on(:array) { |n| n.flattened_results }
|
51
45
|
|
52
46
|
on(:string) { |n| eval(n.result) }
|
53
47
|
|
@@ -198,8 +192,8 @@ describe 'sample JSON parser' do
|
|
198
192
|
it 'raises a ParseError on incorrect input' do
|
199
193
|
|
200
194
|
lambda do
|
201
|
-
JsonParser.parse(
|
202
|
-
end.should raise_error(Neg::ParseError, 'expected "
|
195
|
+
JsonParser.parse('x')
|
196
|
+
end.should raise_error(Neg::ParseError, 'expected "null", got "x"')
|
203
197
|
end
|
204
198
|
end
|
205
199
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
|
5
|
+
describe 'sample scheme parser' do
|
6
|
+
|
7
|
+
class SchemeParser < Neg::Parser
|
8
|
+
|
9
|
+
parser do
|
10
|
+
|
11
|
+
expression == list | atom
|
12
|
+
|
13
|
+
list == `(` + (expression + (` ` + expression) * 0) * 0 + `)`
|
14
|
+
atom == _("^() ") * 1
|
15
|
+
end
|
16
|
+
|
17
|
+
translator do
|
18
|
+
|
19
|
+
on(:expression) { |n| n.results.first }
|
20
|
+
on(:atom) { |n| n.result }
|
21
|
+
|
22
|
+
on(:list) { |n| n.flattened_results }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'parses numbers' do
|
27
|
+
|
28
|
+
SchemeParser.parse("13").should == "13"
|
29
|
+
SchemeParser.parse("-13").should == "-13"
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'parses lists' do
|
33
|
+
|
34
|
+
SchemeParser.parse("()").should == []
|
35
|
+
SchemeParser.parse("(a b c)").should == [ 'a', 'b', 'c' ]
|
36
|
+
SchemeParser.parse("(a (b c))").should == [ 'a', [ 'b', 'c' ] ]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
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: 1.
|
4
|
+
version: 1.1.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: 2013-
|
12
|
+
date: 2013-02-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -51,31 +51,33 @@ extensions: []
|
|
51
51
|
extra_rdoc_files: []
|
52
52
|
files:
|
53
53
|
- Rakefile
|
54
|
-
- lib/neg/errors.rb
|
55
54
|
- lib/neg/input.rb
|
55
|
+
- lib/neg/errors.rb
|
56
|
+
- lib/neg/version.rb
|
56
57
|
- lib/neg/parser.rb
|
57
58
|
- lib/neg/translator.rb
|
58
|
-
- lib/neg/version.rb
|
59
59
|
- lib/neg.rb
|
60
|
-
- spec/
|
61
|
-
- spec/
|
62
|
-
- spec/parser_character_spec.rb
|
63
|
-
- spec/parser_lookahead_parser_spec.rb
|
60
|
+
- spec/sample_arith_spec.rb
|
61
|
+
- spec/spec_helper.rb
|
64
62
|
- spec/parser_non_terminal_spec.rb
|
65
|
-
- spec/
|
63
|
+
- spec/greedy_spec.rb
|
64
|
+
- spec/parser_character_spec.rb
|
65
|
+
- spec/parser_string_spec.rb
|
66
|
+
- spec/sample_scheme_spec.rb
|
66
67
|
- spec/parser_sequence_spec.rb
|
68
|
+
- spec/sample_json_parser_spec.rb
|
69
|
+
- spec/parser_alternative_spec.rb
|
67
70
|
- spec/parser_spec.rb
|
68
|
-
- spec/parser_string_spec.rb
|
69
|
-
- spec/sample_arith_spec.rb
|
70
71
|
- spec/sample_compact_spec.rb
|
71
|
-
- spec/
|
72
|
-
- spec/
|
72
|
+
- spec/input_spec.rb
|
73
|
+
- spec/parser_lookahead_parser_spec.rb
|
74
|
+
- spec/parser_repetition_spec.rb
|
73
75
|
- neg.gemspec
|
74
|
-
- LICENSE.txt
|
75
76
|
- TODO.txt
|
76
|
-
-
|
77
|
+
- LICENSE.txt
|
77
78
|
- README.md
|
78
|
-
|
79
|
+
- CHANGELOG.md
|
80
|
+
homepage: https://github.com/jmettraux/neg
|
79
81
|
licenses: []
|
80
82
|
post_install_message:
|
81
83
|
rdoc_options: []
|
@@ -89,7 +91,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
89
91
|
version: '0'
|
90
92
|
segments:
|
91
93
|
- 0
|
92
|
-
hash:
|
94
|
+
hash: 4523696272151903388
|
93
95
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
96
|
none: false
|
95
97
|
requirements:
|
@@ -98,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
100
|
version: '0'
|
99
101
|
segments:
|
100
102
|
- 0
|
101
|
-
hash:
|
103
|
+
hash: 4523696272151903388
|
102
104
|
requirements: []
|
103
105
|
rubyforge_project: ruote
|
104
106
|
rubygems_version: 1.8.24
|