-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathjson_parser.rb
119 lines (104 loc) · 2.13 KB
/
json_parser.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# frozen_string_literal: true
require "paco"
module JsonParser
extend Paco
module_function
def parse(io)
spaced(value).parse(io)
end
def value
memoize { alt(null, bool, number, str, array, object) }
end
def null
memoize { string("null").result(nil) }
end
def bool
memoize do
alt(
string("true").result(true),
string("false").result(false)
)
end
end
def sign
memoize { alt(string("-"), string("+")) }
end
def decimal
memoize { digits.fmap(&:to_i) }
end
def number
memoize do
seq(
optional(sign),
decimal,
optional(seq(
string("."),
decimal
)),
optional(seq(
one_of("eE"),
optional(sign),
decimal
))
).fmap do |sign, whole, (_, fractional), (_, exponent_sign, exponent)|
n = whole
n += fractional.to_f / 10**fractional.to_s.length if fractional
n *= -1 if sign == "-"
if exponent
e = exponent
e *= -1 if exponent_sign == "-"
n *= 10**e
end
n
end
end
end
def str
memoize do
wrap(
string('"'),
string('"'),
many(alt(none_of('"\\'), escaped_chars)).join
)
end
end
def array
memoize do
wrap(
string("["),
opt_ws > string("]"),
sep_by(spaced(lazy { value }), string(","))
)
end
end
def object
memoize do
wrap(string("{"), opt_ws > string("}"),
sep_by(
spaced(seq(
str < spaced(string(":")),
lazy { value }
)),
string(",")
)).fmap { |x| x.to_h }
end
end
def four_hex_digits
memoize { regexp(/\h{4}/) }
end
def escaped_chars
string("\\").next(
alt(
string('"'),
string("\\"),
string("/"),
string("f").result("\f"),
string("b").result("\b"),
string("r").result("\r"),
string("n").result("\n"),
string("t").result("\t"),
string("u").next(four_hex_digits.fmap { |s| [s.hex].pack("U") })
)
)
end
end