mirror of
https://github.com/odin-lang/Odin.git
synced 2025-12-29 09:24:33 +00:00
123 lines
2.4 KiB
Odin
123 lines
2.4 KiB
Odin
package json
|
|
|
|
import "core:mem"
|
|
|
|
// NOTE(bill): is_valid will not check for duplicate keys
|
|
is_valid :: proc(data: []byte, spec := Specification.JSON) -> bool {
|
|
p := make_parser(data, spec, mem.nil_allocator());
|
|
if p.spec == Specification.JSON5 {
|
|
return validate_value(&p);
|
|
}
|
|
return validate_object(&p);
|
|
}
|
|
|
|
validate_object_key :: proc(p: ^Parser) -> bool {
|
|
tok := p.curr_token;
|
|
if p.spec == Specification.JSON5 {
|
|
if tok.kind == Kind.String {
|
|
expect_token(p, Kind.String);
|
|
return true;
|
|
} else if tok.kind == Kind.Ident {
|
|
expect_token(p, Kind.Ident);
|
|
return true;
|
|
}
|
|
}
|
|
err := expect_token(p, Kind.String);
|
|
return err == Error.None;
|
|
}
|
|
validate_object :: proc(p: ^Parser) -> bool {
|
|
if err := expect_token(p, Kind.Open_Brace); err != Error.None {
|
|
return false;
|
|
}
|
|
|
|
for p.curr_token.kind != Kind.Close_Brace {
|
|
if !validate_object_key(p) {
|
|
return false;
|
|
}
|
|
if colon_err := expect_token(p, Kind.Colon); colon_err != Error.None {
|
|
return false;
|
|
}
|
|
|
|
if !validate_value(p) {
|
|
return false;
|
|
}
|
|
|
|
if p.spec == Specification.JSON5 {
|
|
// Allow trailing commas
|
|
if allow_token(p, Kind.Comma) {
|
|
continue;
|
|
}
|
|
} else {
|
|
// Disallow trailing commas
|
|
if allow_token(p, Kind.Comma) {
|
|
continue;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if err := expect_token(p, Kind.Close_Brace); err != Error.None {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
validate_array :: proc(p: ^Parser) -> bool {
|
|
if err := expect_token(p, Kind.Open_Bracket); err != Error.None {
|
|
return false;
|
|
}
|
|
|
|
for p.curr_token.kind != Kind.Close_Bracket {
|
|
if !validate_value(p) {
|
|
return false;
|
|
}
|
|
|
|
// Disallow trailing commas for the time being
|
|
if allow_token(p, Kind.Comma) {
|
|
continue;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if err := expect_token(p, Kind.Close_Bracket); err != Error.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:
|
|
if p.spec == Specification.JSON5 {
|
|
#partial switch token.kind {
|
|
case .Infinity, .NaN:
|
|
advance_token(p);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|