package 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_object(&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 }