json-expect-parser 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +202 -0
- data/Rakefile +6 -0
- data/benchmark/comparison.rb +47 -0
- data/example/pipe.rb +26 -0
- data/example/simple.rb +38 -0
- data/example/stdin.rb +8 -0
- data/example/string.rb +10 -0
- data/json-expect-parser.gemspec +25 -0
- data/lib/json/expect/parser.rb +350 -0
- data/lib/json/expect/parser/version.rb +7 -0
- data/mrbgem.rake +8 -0
- data/mrblib +1 -0
- data/test/parser.rb +12 -0
- metadata +130 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3a6dda865f0de7159d92fba2db57f98f9220bb68
|
4
|
+
data.tar.gz: aec146fb010f935f35b1ab79a3caa1cf7b2c4bab
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7f734a7edd681dd9a15ce6d65bfe0464dc693ef9594a541d039bd364d5c6dced4639a168734f4b8f881e426c7c162325dc7250c77ed1e11061aebdd226e57bd6
|
7
|
+
data.tar.gz: 4fbdeaffdfa23c1829459f6fe086554dd7ef87cc76e492c3c6c18c7cbef9ac3f02653267835efdadad30d7feb22681de734695177105028048dca344ecac90aa
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Gemfile.lock
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 ksss
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
json-expect-parser
|
2
|
+
===
|
3
|
+
|
4
|
+
An alternative JSON parser.
|
5
|
+
|
6
|
+
## Synopsis
|
7
|
+
|
8
|
+
t.json
|
9
|
+
|
10
|
+
```json
|
11
|
+
[
|
12
|
+
{
|
13
|
+
"id": 1,
|
14
|
+
"name": "ksss",
|
15
|
+
"admin": true
|
16
|
+
},
|
17
|
+
{
|
18
|
+
"id": 2,
|
19
|
+
"name": "foo",
|
20
|
+
"admin": false
|
21
|
+
},
|
22
|
+
{
|
23
|
+
"id": 3,
|
24
|
+
"name": "bar",
|
25
|
+
"admin": false
|
26
|
+
}
|
27
|
+
]
|
28
|
+
```
|
29
|
+
|
30
|
+
```rb
|
31
|
+
require 'json/expect/parser'
|
32
|
+
|
33
|
+
File.open("t.json") do |io|
|
34
|
+
expect = JSON::Expect::Parser.new(io)
|
35
|
+
expect.array do
|
36
|
+
expect.object do
|
37
|
+
case expect.key
|
38
|
+
when "id"
|
39
|
+
p expect.integer #=> 1, 2, 3
|
40
|
+
when "name"
|
41
|
+
p expect.string #=> "ksss", "foo", "bar"
|
42
|
+
when "admin"
|
43
|
+
p expect.boolean #=> true, false, false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
## Explicitly parse
|
51
|
+
|
52
|
+
json-expect-parser parse JSON explicitly.
|
53
|
+
|
54
|
+
```rb
|
55
|
+
expect = JSON::Expect::Parser.new(%([10, 20, 30]))
|
56
|
+
expect.array { p expect.integer } #=> 10, 20, 30
|
57
|
+
```
|
58
|
+
|
59
|
+
If get unexpected value, It failed.
|
60
|
+
|
61
|
+
```rb
|
62
|
+
expect = JSON::Expect::Parser.new(%([10, 20, 30]))
|
63
|
+
expect.array { p expect.string }
|
64
|
+
#=> JSON::Expect::ParseError: expected "\"" but was "1"
|
65
|
+
```
|
66
|
+
|
67
|
+
## API
|
68
|
+
|
69
|
+
### object
|
70
|
+
|
71
|
+
```rb
|
72
|
+
expect = JSON::Expect::Parser.new(%({"a": 10, "b": 20}))
|
73
|
+
|
74
|
+
expect.object
|
75
|
+
#=> #<Enumerator: #<JSON::Expect::Parser ...>>
|
76
|
+
|
77
|
+
expect.object do
|
78
|
+
expect.key #=> "a", "b"
|
79
|
+
expect.integer #=> 10, 20
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
### array
|
84
|
+
|
85
|
+
```rb
|
86
|
+
expect = JSON::Expect::Parser.new(%(["foo", "bar", "baz"]))
|
87
|
+
|
88
|
+
expect.array
|
89
|
+
#=> #<Enumerator: #<JSON::Expect::Parser ...>>
|
90
|
+
|
91
|
+
expect.array do
|
92
|
+
expect.string #=> "foo", "bar", "baz"
|
93
|
+
end
|
94
|
+
|
95
|
+
expect.rewind
|
96
|
+
|
97
|
+
expect.array.map { expect.string }
|
98
|
+
#=> ["foo", "bar", "baz"]
|
99
|
+
```
|
100
|
+
|
101
|
+
### integer
|
102
|
+
|
103
|
+
```rb
|
104
|
+
expect = JSON::Expect::Parser.new(%(100))
|
105
|
+
expect.integer #=> 100
|
106
|
+
```
|
107
|
+
|
108
|
+
### number(alias float)
|
109
|
+
|
110
|
+
```rb
|
111
|
+
expect = JSON::Expect::Parser.new(%(1.1))
|
112
|
+
expect.float #=> 1.1
|
113
|
+
```
|
114
|
+
|
115
|
+
### string
|
116
|
+
|
117
|
+
```rb
|
118
|
+
expect = JSON::Expect::Parser.new(%("foo"))
|
119
|
+
expect.string #=> "foo"
|
120
|
+
```
|
121
|
+
|
122
|
+
### key
|
123
|
+
|
124
|
+
Use in `object` then get object key string
|
125
|
+
|
126
|
+
### boolean
|
127
|
+
|
128
|
+
```rb
|
129
|
+
expect = JSON::Expect::Parser.new(%("true"))
|
130
|
+
expect.boolean #=> true
|
131
|
+
```
|
132
|
+
|
133
|
+
### null
|
134
|
+
|
135
|
+
```rb
|
136
|
+
expect = JSON::Expect::Parser.new(%("null"))
|
137
|
+
expect.null #=> nil
|
138
|
+
```
|
139
|
+
|
140
|
+
### object_or_null
|
141
|
+
|
142
|
+
```rb
|
143
|
+
expect = JSON::Expect::Parser.new(%([{"a": 1}, null]))
|
144
|
+
expect.array do
|
145
|
+
expect.object_or_null do
|
146
|
+
expect.key #=> "a"
|
147
|
+
expect.integer #=> 1
|
148
|
+
end
|
149
|
+
end
|
150
|
+
```
|
151
|
+
|
152
|
+
### array_or_null
|
153
|
+
|
154
|
+
```rb
|
155
|
+
expect = JSON::Expect::Parser.new(%([[1, 2, 3], null]))
|
156
|
+
expect.array do
|
157
|
+
expect.array_or_null do
|
158
|
+
expect.integer #=> 1, 2, 3
|
159
|
+
end
|
160
|
+
end
|
161
|
+
```
|
162
|
+
|
163
|
+
### null_or
|
164
|
+
|
165
|
+
```rb
|
166
|
+
expect = JSON::Expect::Parser.new(%({"a": "foo", "b": null}))
|
167
|
+
expect.object do
|
168
|
+
expect.key #=> "a", "b"
|
169
|
+
expect.null_or { expect.string } #=> "foo", nil
|
170
|
+
end
|
171
|
+
```
|
172
|
+
|
173
|
+
### value(alias parse)
|
174
|
+
|
175
|
+
```rb
|
176
|
+
expect = JSON::Expect::Parser.new(%([[true, false], null, 1, "foo"]))
|
177
|
+
expect.value
|
178
|
+
#=> [[true, false], nil, 1.0, "foo"]
|
179
|
+
```
|
180
|
+
|
181
|
+
### rewind
|
182
|
+
|
183
|
+
```rb
|
184
|
+
expect = JSON::Expect::Parser.new(%("foo"))
|
185
|
+
expect.string #=> "foo"
|
186
|
+
expect.string #=> "foo"
|
187
|
+
#=> JSON::Expect::ParseError: expected "\"" but was nil
|
188
|
+
expect.rewind
|
189
|
+
expect.string #=> "foo"
|
190
|
+
```
|
191
|
+
|
192
|
+
# Benchmark
|
193
|
+
|
194
|
+
```
|
195
|
+
8320.0kb
|
196
|
+
38420.0kb
|
197
|
+
ruby t.rb 0.19s user 0.06s system 98% cpu 0.257 total
|
198
|
+
|
199
|
+
8124.0kb
|
200
|
+
10732.0kb
|
201
|
+
ruby j.rb 0.83s user 0.06s system 99% cpu 0.891 total
|
202
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'benchmark'
|
4
|
+
require 'get_process_mem'
|
5
|
+
require 'json/pure/parser'
|
6
|
+
require 'json/ext/parser'
|
7
|
+
require 'json/expect/parser'
|
8
|
+
|
9
|
+
puts "ruby v#{RUBY_VERSION}"
|
10
|
+
puts `uname -v`
|
11
|
+
|
12
|
+
b = []
|
13
|
+
30000.times do
|
14
|
+
b << %({"abcdefg": 123456, "hijklmn": "fooooooooooooooo", "opqrstu": -123.456})
|
15
|
+
end
|
16
|
+
json = "[#{b.join(",")}]"
|
17
|
+
puts "#{json.length / 1024}KB json string"
|
18
|
+
|
19
|
+
puts
|
20
|
+
|
21
|
+
[
|
22
|
+
['JSON::Ext::Parser', -> { JSON::Ext::Parser.new(json).parse }],
|
23
|
+
['JSON::Pure::Parser', -> { JSON::Pure::Parser.new(json).parse }],
|
24
|
+
['JSON::Expect::Parser', -> {
|
25
|
+
e = JSON::Expect::Parser.new(json)
|
26
|
+
e.array do
|
27
|
+
e.object do
|
28
|
+
e.key
|
29
|
+
e.integer
|
30
|
+
e.key
|
31
|
+
e.string
|
32
|
+
e.key
|
33
|
+
e.float
|
34
|
+
end
|
35
|
+
end
|
36
|
+
}],
|
37
|
+
].each do |name, proc|
|
38
|
+
GC.start
|
39
|
+
Process.wait fork {
|
40
|
+
puts "=== #{name} ==="
|
41
|
+
mem = GetProcessMem.new.kb
|
42
|
+
print "time: "
|
43
|
+
puts Benchmark.realtime(&proc)
|
44
|
+
puts "memory: #{GetProcessMem.new.kb - mem}KB"
|
45
|
+
puts
|
46
|
+
}
|
47
|
+
end
|
data/example/pipe.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
require 'json/expect/parser'
|
3
|
+
|
4
|
+
IO.pipe do |r, w|
|
5
|
+
t = Thread.new do
|
6
|
+
expect = JSON::Expect::Parser.new(r, buffer_size: 1)
|
7
|
+
expect.array do
|
8
|
+
puts expect.integer
|
9
|
+
end
|
10
|
+
end
|
11
|
+
w.write "["
|
12
|
+
w.write "10,"
|
13
|
+
sleep 1
|
14
|
+
w.write "20,"
|
15
|
+
sleep 1
|
16
|
+
w.write "30"
|
17
|
+
sleep 1
|
18
|
+
w.write "]"
|
19
|
+
w.close
|
20
|
+
t.join
|
21
|
+
end
|
22
|
+
__END__
|
23
|
+
$ bundle ex ruby example/pipe.rb
|
24
|
+
10
|
25
|
+
20
|
26
|
+
30
|
data/example/simple.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
require 'json/expect/parser'
|
3
|
+
|
4
|
+
values = []
|
5
|
+
expect = JSON::Expect::Parser.new(DATA)
|
6
|
+
expect.array do
|
7
|
+
expect.object do
|
8
|
+
case expect.key
|
9
|
+
when "id"
|
10
|
+
values << expect.integer
|
11
|
+
when "name"
|
12
|
+
values << expect.string
|
13
|
+
when "admin"
|
14
|
+
values << expect.boolean
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
p values
|
19
|
+
# $ bundle ex ruby example/simple.rb
|
20
|
+
# [1, "ksss", true, 2, "foo", false, 3, "bar", false]
|
21
|
+
__END__
|
22
|
+
[
|
23
|
+
{
|
24
|
+
"id": 1,
|
25
|
+
"name": "ksss",
|
26
|
+
"admin": true
|
27
|
+
},
|
28
|
+
{
|
29
|
+
"id": 2,
|
30
|
+
"name": "foo",
|
31
|
+
"admin": false
|
32
|
+
},
|
33
|
+
{
|
34
|
+
"id": 3,
|
35
|
+
"name": "bar",
|
36
|
+
"admin": false
|
37
|
+
}
|
38
|
+
]
|
data/example/stdin.rb
ADDED
data/example/string.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'json/expect/parser/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "json-expect-parser"
|
8
|
+
spec.version = JSON::Expect::Parser::VERSION
|
9
|
+
spec.authors = ["ksss"]
|
10
|
+
spec.email = ["co000ri@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = "JSON expect parser"
|
13
|
+
spec.description = "An alternative JSON parser"
|
14
|
+
spec.homepage = "https://github.com/ksss/json-expect-parser"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{_test.rb}) }
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "json"
|
21
|
+
spec.add_development_dependency "get_process_mem"
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.11"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "rgot"
|
25
|
+
end
|
@@ -0,0 +1,350 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JSON
|
4
|
+
module Expect
|
5
|
+
ParseError = Class.new(StandardError)
|
6
|
+
class Buffer
|
7
|
+
def initialize(input, size)
|
8
|
+
unless 0 < size
|
9
|
+
raise ArgumentError, "negative buffer_size"
|
10
|
+
end
|
11
|
+
@io = case input
|
12
|
+
when String
|
13
|
+
StringIO.new(input)
|
14
|
+
when IO, StringIO
|
15
|
+
input
|
16
|
+
else
|
17
|
+
raise ArgumentError, "#{input.class} is not supported"
|
18
|
+
end
|
19
|
+
@string = String.new
|
20
|
+
@index = 0
|
21
|
+
@size = size
|
22
|
+
end
|
23
|
+
|
24
|
+
def fetch(len = 0)
|
25
|
+
if str = @io.read(@size + len)
|
26
|
+
@string = @string[@index..-1] << str
|
27
|
+
@index = 0
|
28
|
+
else
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def next(len = 1)
|
34
|
+
if @string.length - @index < len
|
35
|
+
if str = @io.read(@size + len)
|
36
|
+
@string = @string[@index..-1] << str
|
37
|
+
@index = 0
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
if @string.length <= @index
|
42
|
+
nil
|
43
|
+
else
|
44
|
+
r = @string[@index, len]
|
45
|
+
@index += r.length
|
46
|
+
r
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def back(len = 1)
|
51
|
+
@index -= len
|
52
|
+
end
|
53
|
+
|
54
|
+
def scan(regexp)
|
55
|
+
if m = regexp.match(@string, @index)
|
56
|
+
@index += m[0].length
|
57
|
+
m[0]
|
58
|
+
else
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def next_without_whitespace
|
64
|
+
while ch = self.next
|
65
|
+
case ch
|
66
|
+
when " ", "\t", "\n", "\r"
|
67
|
+
else
|
68
|
+
return ch
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def rewind
|
74
|
+
@string.clear
|
75
|
+
@index = 0
|
76
|
+
@io.rewind
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class Parser
|
81
|
+
NUM_FIRST = /\A-|\d/
|
82
|
+
# from json/pure
|
83
|
+
STRING = /"(?:[^\x0-\x1f"\\] |
|
84
|
+
# escaped special characters:
|
85
|
+
\\["\\\/bfnrt] |
|
86
|
+
\\u[0-9a-fA-F]{4} |
|
87
|
+
# match all but escaped special characters:
|
88
|
+
\\[\x20-\x21\x23-\x2e\x30-\x5b\x5d-\x61\x63-\x65\x67-\x6d\x6f-\x71\x73\x75-\xff])*
|
89
|
+
"/nx
|
90
|
+
INTEGER = /\A-?0|-?[1-9]\d*/
|
91
|
+
FLOAT = /\A-?
|
92
|
+
(?:0|[1-9]\d*)
|
93
|
+
(?:
|
94
|
+
\.\d+(?i:e[+-]?\d+) |
|
95
|
+
\.\d+ |
|
96
|
+
(?i:e[+-]?\d+)
|
97
|
+
)?
|
98
|
+
/x
|
99
|
+
|
100
|
+
def initialize(input, opts = {})
|
101
|
+
buffer_size = if opts[:buffer_size]
|
102
|
+
opts[:buffer_size]
|
103
|
+
else
|
104
|
+
4092
|
105
|
+
end
|
106
|
+
@count_stack = []
|
107
|
+
@buffer = Buffer.new(input, buffer_size)
|
108
|
+
end
|
109
|
+
|
110
|
+
def object
|
111
|
+
return to_enum(:object) unless block_given?
|
112
|
+
|
113
|
+
expect_char("{")
|
114
|
+
count_up
|
115
|
+
@count_stack.push(0)
|
116
|
+
while true
|
117
|
+
case ch = @buffer.next_without_whitespace
|
118
|
+
when "}"
|
119
|
+
check_tail
|
120
|
+
break @count_stack.pop
|
121
|
+
when "\""
|
122
|
+
@buffer.back(ch.length)
|
123
|
+
before_count = current_count
|
124
|
+
yield
|
125
|
+
raise ParseError, "nothing expectation in block" unless before_count != current_count
|
126
|
+
check_tail
|
127
|
+
else
|
128
|
+
raise ParseError, "expected \"}\" or \"\"\" but was #{ch.inspect}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def array
|
134
|
+
return to_enum(:array) unless block_given?
|
135
|
+
|
136
|
+
expect_char("[")
|
137
|
+
count_up
|
138
|
+
@count_stack.push(0)
|
139
|
+
while true
|
140
|
+
case ch = @buffer.next_without_whitespace
|
141
|
+
when "]"
|
142
|
+
check_tail
|
143
|
+
break @count_stack.pop
|
144
|
+
when nil
|
145
|
+
raise ParseError, "expected any array item but EOF"
|
146
|
+
else
|
147
|
+
@buffer.back(ch.length)
|
148
|
+
before_count = current_count
|
149
|
+
yield
|
150
|
+
raise ParseError, "nothing expectation in block" unless before_count != current_count
|
151
|
+
check_tail
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def key
|
157
|
+
expect_char("\"")
|
158
|
+
@buffer.back
|
159
|
+
until m = @buffer.scan(STRING)
|
160
|
+
if @buffer.fetch.nil?
|
161
|
+
raise ParseError, "expected \"\"\" but was EOF"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
expect_char(":")
|
165
|
+
m[1..-2]
|
166
|
+
end
|
167
|
+
|
168
|
+
def string
|
169
|
+
expect_char("\"")
|
170
|
+
@buffer.back
|
171
|
+
until m = @buffer.scan(STRING)
|
172
|
+
if @buffer.fetch.nil?
|
173
|
+
raise ParseError, "expected \"\"\" but was EOF"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
count_up
|
177
|
+
check_tail
|
178
|
+
m[1..-2]
|
179
|
+
end
|
180
|
+
|
181
|
+
def integer
|
182
|
+
buffer = @buffer.next_without_whitespace
|
183
|
+
if NUM_FIRST =~ buffer
|
184
|
+
i = @buffer.next(16)
|
185
|
+
buffer << i if i
|
186
|
+
while m = INTEGER.match(buffer)
|
187
|
+
break if m[0].length != buffer.length
|
188
|
+
i = @buffer.next(16)
|
189
|
+
break unless i
|
190
|
+
buffer << i
|
191
|
+
end
|
192
|
+
raise ParseError, "expected integer but was #{buffer.inspect}" unless m
|
193
|
+
@buffer.back(buffer.length - m[0].length)
|
194
|
+
count_up
|
195
|
+
check_tail
|
196
|
+
m[0].to_i
|
197
|
+
else
|
198
|
+
raise ParseError, "expected integer but was #{buffer.inspect}"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def number
|
203
|
+
buffer = @buffer.next_without_whitespace
|
204
|
+
if NUM_FIRST =~ buffer
|
205
|
+
i = @buffer.next(16)
|
206
|
+
buffer << i if i
|
207
|
+
if buffer[-1] == "."
|
208
|
+
i = @buffer.next(16)
|
209
|
+
raise ParseError, "expected number after \".\" but nothing" unless i
|
210
|
+
buffer << i
|
211
|
+
end
|
212
|
+
while m = FLOAT.match(buffer)
|
213
|
+
break if m[0].length != buffer.length
|
214
|
+
i = @buffer.next(16)
|
215
|
+
break unless i
|
216
|
+
buffer << i
|
217
|
+
end
|
218
|
+
raise ParseError, "expected number but was #{buffer.inspect}" unless m
|
219
|
+
@buffer.back(buffer.length - m[0].length)
|
220
|
+
count_up
|
221
|
+
check_tail
|
222
|
+
m[0].to_f
|
223
|
+
else
|
224
|
+
raise ParseError, "expected number but was #{buffer.inspect}"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
alias float number
|
228
|
+
|
229
|
+
def boolean
|
230
|
+
case ch = @buffer.next_without_whitespace
|
231
|
+
when "t"
|
232
|
+
if "rue" == (rue = @buffer.next(3))
|
233
|
+
count_up
|
234
|
+
check_tail
|
235
|
+
true
|
236
|
+
else
|
237
|
+
raise ParseError, "expected true or false but was \"#{ch}#{rue}\""
|
238
|
+
end
|
239
|
+
when "f"
|
240
|
+
if "alse" == (alse = @buffer.next(4))
|
241
|
+
count_up
|
242
|
+
check_tail
|
243
|
+
false
|
244
|
+
else
|
245
|
+
raise ParseError, "expected true or false but was \"#{ch}#{alse}\""
|
246
|
+
end
|
247
|
+
else
|
248
|
+
raise ParseError, "expected true or false but was \"#{ch}\""
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def null
|
253
|
+
case ch = @buffer.next_without_whitespace
|
254
|
+
when "n"
|
255
|
+
if "ull" == (ull = @buffer.next(3))
|
256
|
+
count_up
|
257
|
+
check_tail
|
258
|
+
nil
|
259
|
+
else
|
260
|
+
raise ParseError, "expected null but was \"#{ch}#{ull}\""
|
261
|
+
end
|
262
|
+
else
|
263
|
+
raise ParseError, "expected null but was #{ch.inspect}"
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def object_or_null
|
268
|
+
null_or { object { yield } }
|
269
|
+
end
|
270
|
+
|
271
|
+
def array_or_null
|
272
|
+
null_or { array { yield } }
|
273
|
+
end
|
274
|
+
|
275
|
+
def null_or
|
276
|
+
ch = @buffer.next_without_whitespace
|
277
|
+
@buffer.back
|
278
|
+
if ch == 'n'
|
279
|
+
null
|
280
|
+
else
|
281
|
+
yield
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def value
|
286
|
+
ch = @buffer.next_without_whitespace
|
287
|
+
@buffer.back
|
288
|
+
case ch
|
289
|
+
when "["
|
290
|
+
array.map { value }
|
291
|
+
when "{"
|
292
|
+
Hash[object.map { [key, value] }]
|
293
|
+
when "n"
|
294
|
+
null
|
295
|
+
when "t", "f"
|
296
|
+
boolean
|
297
|
+
when "\""
|
298
|
+
string
|
299
|
+
when NUM_FIRST
|
300
|
+
number
|
301
|
+
else
|
302
|
+
raise ParseError, "expected any value but was #{ch.inspect}"
|
303
|
+
end
|
304
|
+
end
|
305
|
+
alias parse value
|
306
|
+
|
307
|
+
def rewind
|
308
|
+
@buffer.rewind
|
309
|
+
end
|
310
|
+
|
311
|
+
private
|
312
|
+
|
313
|
+
def count_up
|
314
|
+
if !@count_stack.empty?
|
315
|
+
@count_stack[-1] += 1
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def current_count
|
320
|
+
@count_stack[-1]
|
321
|
+
end
|
322
|
+
|
323
|
+
def check_tail
|
324
|
+
case ch = @buffer.next_without_whitespace
|
325
|
+
when ","
|
326
|
+
raise ParseError, "unexpected \",\"" if @count_stack.empty?
|
327
|
+
ch = @buffer.next_without_whitespace
|
328
|
+
raise ParseError, "expected next token but was EOF" unless ch
|
329
|
+
case ch
|
330
|
+
when "]", "}", ","
|
331
|
+
raise ParseError, "expected next token but was #{ch.inspect}"
|
332
|
+
else
|
333
|
+
@buffer.back
|
334
|
+
end
|
335
|
+
when nil
|
336
|
+
else
|
337
|
+
@buffer.back
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
def expect_char(expect)
|
342
|
+
actual = @buffer.next_without_whitespace
|
343
|
+
unless actual == expect
|
344
|
+
raise ParseError, "expected #{expect.inspect} but was #{actual.inspect}"
|
345
|
+
end
|
346
|
+
actual
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
data/mrbgem.rake
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
MRuby::Gem::Specification.new('json-expect-parser') do |spec|
|
2
|
+
spec.license = 'MIT'
|
3
|
+
spec.author = 'mruby developers'
|
4
|
+
spec.summary = 'A JSON Parser'
|
5
|
+
spec.add_dependency('mruby-string-ext', core: 'mruby-string-ext')
|
6
|
+
spec.add_dependency('mruby-stringio')
|
7
|
+
spec.add_dependency('mruby-onig-regexp')
|
8
|
+
end
|
data/mrblib
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
lib
|
data/test/parser.rb
ADDED
metadata
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: json-expect-parser
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- ksss
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-08-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: get_process_mem
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.11'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.11'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rgot
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: An alternative JSON parser
|
84
|
+
email:
|
85
|
+
- co000ri@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- Gemfile
|
92
|
+
- LICENSE.txt
|
93
|
+
- README.md
|
94
|
+
- Rakefile
|
95
|
+
- benchmark/comparison.rb
|
96
|
+
- example/pipe.rb
|
97
|
+
- example/simple.rb
|
98
|
+
- example/stdin.rb
|
99
|
+
- example/string.rb
|
100
|
+
- json-expect-parser.gemspec
|
101
|
+
- lib/json/expect/parser.rb
|
102
|
+
- lib/json/expect/parser/version.rb
|
103
|
+
- mrbgem.rake
|
104
|
+
- mrblib
|
105
|
+
- test/parser.rb
|
106
|
+
homepage: https://github.com/ksss/json-expect-parser
|
107
|
+
licenses:
|
108
|
+
- MIT
|
109
|
+
metadata: {}
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
requirements: []
|
125
|
+
rubyforge_project:
|
126
|
+
rubygems_version: 2.5.1
|
127
|
+
signing_key:
|
128
|
+
specification_version: 4
|
129
|
+
summary: JSON expect parser
|
130
|
+
test_files: []
|