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
120
121
122
123
124
|
package encoding_json
import "core:mem"
// NOTE(bill): is_valid will not check for duplicate keys
is_valid :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false) -> bool {
p := make_parser(data, spec, parse_integers, mem.nil_allocator())
switch p.spec {
case .JSON:
return validate_value(&p)
case .JSON5:
return validate_value(&p)
case .MJSON:
#partial switch p.curr_token.kind {
case .Ident, .String:
return validate_object_body(&p, .EOF)
}
return validate_value(&p)
}
return validate_object(&p)
}
validate_object_key :: proc(p: ^Parser) -> bool {
if p.spec != .JSON {
if allow_token(p, .Ident) {
return true
}
}
err := expect_token(p, .String)
return err == .None
}
validate_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> bool {
for p.curr_token.kind != end_token {
if !validate_object_key(p) {
return false
}
if parse_colon(p) != nil {
return false
}
validate_value(p) or_return
if parse_comma(p) {
break
}
}
return true
}
validate_object :: proc(p: ^Parser) -> bool {
if err := expect_token(p, .Open_Brace); err != .None {
return false
}
validate_object_body(p, .Close_Brace) or_return
if err := expect_token(p, .Close_Brace); err != .None {
return false
}
return true
}
validate_array :: proc(p: ^Parser) -> bool {
if err := expect_token(p, .Open_Bracket); err != .None {
return false
}
for p.curr_token.kind != .Close_Bracket {
if !validate_value(p) {
return false
}
if parse_comma(p) {
break
}
}
if err := expect_token(p, .Close_Bracket); err != .None {
return false
}
return true
}
validate_value :: proc(p: ^Parser) -> bool {
token := p.curr_token
#partial switch token.kind {
case .Null, .False, .True:
advance_token(p)
return true
case .Integer, .Float:
advance_token(p)
return true
case .String:
advance_token(p)
return is_valid_string_literal(token.text, p.spec)
case .Open_Brace:
return validate_object(p)
case .Open_Bracket:
return validate_array(p)
case .Ident:
if p.spec == .MJSON {
advance_token(p)
return true
}
return false
case:
if p.spec != .JSON {
#partial switch token.kind {
case .Infinity, .NaN:
advance_token(p)
return true
}
}
}
return false
}
|