package json import "core:mem" // NOTE(bill): is_valid will not check for duplicate keys is_valid :: proc(data: []byte, spec := Specification.JSON, parse_integers := false) -> bool { p := make_parser(data, spec, parse_integers, 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 == .String { expect_token(p, .String) return true } else if tok.kind == .Ident { expect_token(p, .Ident) return true } } err := expect_token(p, .String) return err == Error.None } validate_object :: proc(p: ^Parser) -> bool { if err := expect_token(p, .Open_Brace); err != Error.None { return false } for p.curr_token.kind != .Close_Brace { if !validate_object_key(p) { return false } if colon_err := expect_token(p, .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, .Comma) { continue } } else { // Disallow trailing commas if allow_token(p, .Comma) { continue } else { break } } } if err := expect_token(p, .Close_Brace); err != Error.None { return false } return true } validate_array :: proc(p: ^Parser) -> bool { if err := expect_token(p, .Open_Bracket); err != Error.None { return false } for p.curr_token.kind != .Close_Bracket { if !validate_value(p) { return false } // Disallow trailing commas for the time being if allow_token(p, .Comma) { continue } else { break } } if err := expect_token(p, .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 }