From 9e713e2940ad366c5c0a597c5a6725da71c44cb3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 29 Jun 2026 13:44:03 +0100 Subject: [PATCH] Update `core:encoding/json` to use `proc` groups and `@(require_results)` --- core/encoding/json/marshal.odin | 1 + core/encoding/json/parser.odin | 32 ++++++++++++++++++++++++++++--- core/encoding/json/tokenizer.odin | 19 ++++++++++-------- core/encoding/json/types.odin | 1 + core/encoding/json/unmarshal.odin | 2 +- core/encoding/json/validator.odin | 6 ++++++ 6 files changed, 49 insertions(+), 12 deletions(-) diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index a38c6b6d9..e6c8fba38 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -134,6 +134,7 @@ register_user_marshaler :: proc(id: typeid, marshaler: User_Marshaler) -> Regist return .None } +@(require_results) marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Marshal_Error) { b := strings.builder_make(allocator, loc) defer if err != nil { diff --git a/core/encoding/json/parser.odin b/core/encoding/json/parser.odin index 58a47d308..91bc42e4c 100644 --- a/core/encoding/json/parser.odin +++ b/core/encoding/json/parser.odin @@ -14,9 +14,16 @@ Parser :: struct { parse_integers: bool, } -make_parser :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> Parser { +make_parser :: proc{ + make_parser_from_bytes, + make_parser_from_string, +} + +@(require_results) +make_parser_from_bytes :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> Parser { return make_parser_from_string(string(data), spec, parse_integers, allocator) } +@(require_results) make_parser_from_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> Parser { p: Parser p.tok = make_tokenizer(data, spec, parse_integers) @@ -27,11 +34,18 @@ make_parser_from_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, par return p } +parse :: proc{ + parse_bytes, + parse_string, +} -parse :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) { + +@(require_results) +parse_bytes :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) { return parse_string(string(data), spec, parse_integers, allocator, loc) } +@(require_results) parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) { context.allocator = allocator p := make_parser_from_string(data, spec, parse_integers, allocator) @@ -51,6 +65,7 @@ parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers return parse_object(&p, loc) } +@(require_results) token_end_pos :: proc(tok: Token) -> Pos { end := tok.pos end.offset += len(tok.text) @@ -65,6 +80,7 @@ advance_token :: proc(p: ^Parser) -> (Token, Error) { } +@(require_results) allow_token :: proc(p: ^Parser, kind: Token_Kind) -> bool { if p.curr_token.kind == kind { advance_token(p) @@ -73,6 +89,7 @@ allow_token :: proc(p: ^Parser, kind: Token_Kind) -> bool { return false } +@(require_results) expect_token :: proc(p: ^Parser, kind: Token_Kind) -> Error { prev := p.curr_token advance_token(p) @@ -83,6 +100,7 @@ expect_token :: proc(p: ^Parser, kind: Token_Kind) -> Error { } +@(require_results) parse_colon :: proc(p: ^Parser) -> (err: Error) { colon_err := expect_token(p, .Colon) if colon_err == nil { @@ -91,6 +109,7 @@ parse_colon :: proc(p: ^Parser) -> (err: Error) { return .Expected_Colon_After_Key } +@(require_results) parse_comma :: proc(p: ^Parser) -> (do_break: bool) { switch p.spec { case .JSON5, .MJSON: @@ -106,6 +125,7 @@ parse_comma :: proc(p: ^Parser) -> (do_break: bool) { return false } +@(require_results) parse_value :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) { err = .None token := p.curr_token @@ -176,6 +196,7 @@ parse_value :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: return } +@(require_results) parse_array :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) { err = .None expect_token(p, .Open_Bracket) or_return @@ -203,7 +224,7 @@ parse_array :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: return } -@(private) +@(private, require_results) bytes_make :: proc(size, alignment: int, allocator: mem.Allocator, loc := #caller_location) -> (bytes: []byte, err: Error) { b, berr := mem.alloc_bytes(size, alignment, allocator, loc) if berr != nil { @@ -217,6 +238,7 @@ bytes_make :: proc(size, alignment: int, allocator: mem.Allocator, loc := #calle return } +@(require_results) clone_string :: proc(s: string, allocator: mem.Allocator, loc := #caller_location) -> (str: string, err: Error) { n := len(s) b := bytes_make(n+1, 1, allocator, loc) or_return @@ -228,6 +250,7 @@ clone_string :: proc(s: string, allocator: mem.Allocator, loc := #caller_locatio return } +@(require_results) parse_object_key :: proc(p: ^Parser, key_allocator: mem.Allocator, loc := #caller_location) -> (key: string, err: Error) { tok := p.curr_token if p.spec != .JSON { @@ -242,6 +265,7 @@ parse_object_key :: proc(p: ^Parser, key_allocator: mem.Allocator, loc := #calle return unquote_string(tok, p.spec, key_allocator, loc) } +@(require_results) parse_object_body :: proc(p: ^Parser, end_token: Token_Kind, loc := #caller_location) -> (obj: Object, err: Error) { obj = make(Object, allocator=p.allocator, loc=loc) @@ -282,6 +306,7 @@ parse_object_body :: proc(p: ^Parser, end_token: Token_Kind, loc := #caller_loca return obj, .None } +@(require_results) parse_object :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) { expect_token(p, .Open_Brace) or_return obj := parse_object_body(p, .Close_Brace, loc) or_return @@ -291,6 +316,7 @@ parse_object :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: // IMPORTANT NOTE(bill): unquote_string assumes a mostly valid string +@(require_results) unquote_string :: proc(token: Token, spec: Specification, allocator := context.allocator, loc := #caller_location) -> (value: string, err: Error) { get_u2_rune :: proc(s: string) -> rune { if len(s) < 4 || s[0] != '\\' || s[1] != 'x' { diff --git a/core/encoding/json/tokenizer.odin b/core/encoding/json/tokenizer.odin index ad928b7d9..153c41b29 100644 --- a/core/encoding/json/tokenizer.odin +++ b/core/encoding/json/tokenizer.odin @@ -49,11 +49,12 @@ Tokenizer :: struct { curr_line_offset: int, spec: Specification, parse_integers: bool, - insert_comma: bool, + insert_comma: bool, } +@(require_results) make_tokenizer :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false) -> Tokenizer { t := Tokenizer{pos = {line=1}, data = data, spec = spec, parse_integers = parse_integers} next_rune(&t) @@ -78,6 +79,7 @@ next_rune :: proc(t: ^Tokenizer) -> rune #no_bounds_check { } +@(require_results) get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) { skip_digits :: proc(t: ^Tokenizer) { for t.offset < len(t.data) { @@ -101,6 +103,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) { } } + @(require_results) scan_escape :: proc(t: ^Tokenizer) -> bool { switch t.r { case '"', '\'', '\\', '/', 'b', 'n', 'r', 't', 'f': @@ -125,7 +128,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) { return false } - skip_whitespace :: proc(t: ^Tokenizer, on_newline: bool) -> rune { + skip_whitespace :: proc(t: ^Tokenizer, on_newline: bool) { loop: for t.offset < len(t.data) { switch t.r { case ' ', '\t', '\v', '\f', '\r': @@ -149,7 +152,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) { break loop } } - return t.r + return } skip_to_next_line :: proc(t: ^Tokenizer) { @@ -310,7 +313,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) { break } if r == '\\' { - scan_escape(t) + _ = scan_escape(t) } } @@ -347,10 +350,8 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) { case '*': // None-nested multi-line comments for t.offset < len(t.data) { - next_rune(t) - if t.r == '*' { - next_rune(t) - if t.r == '/' { + if next_rune(t) == '*' { + if next_rune(t) == '/' { next_rune(t) return get_token(t) } @@ -385,6 +386,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) { +@(require_results) is_valid_number :: proc(str: string, spec: Specification) -> bool { s := str if s == "" { @@ -473,6 +475,7 @@ is_valid_number :: proc(str: string, spec: Specification) -> bool { return s == "" } +@(require_results) is_valid_string_literal :: proc(str: string, spec: Specification) -> bool { s := str if len(s) < 2 { diff --git a/core/encoding/json/types.odin b/core/encoding/json/types.odin index 6b6d8aae9..66ff5c700 100644 --- a/core/encoding/json/types.odin +++ b/core/encoding/json/types.odin @@ -112,6 +112,7 @@ destroy_value :: proc(value: Value, allocator := context.allocator, loc := #call } } +@(require_results) clone_value :: proc(value: Value, allocator := context.allocator) -> Value { value := value context.allocator = allocator diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index d3cb083d1..0f8c19b5d 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -635,7 +635,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm defer p.allocator = allocator p.allocator = mem.nil_allocator() - parse_value(p) or_return + _ = parse_value(p) or_return if parse_comma(p) { break struct_loop } diff --git a/core/encoding/json/validator.odin b/core/encoding/json/validator.odin index e90270335..e8b5cc49d 100644 --- a/core/encoding/json/validator.odin +++ b/core/encoding/json/validator.odin @@ -3,6 +3,7 @@ package encoding_json import "core:mem" // NOTE(bill): is_valid will not check for duplicate keys +@(require_results) is_valid :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false) -> bool { p := make_parser(data, spec, parse_integers, mem.nil_allocator()) @@ -21,6 +22,7 @@ is_valid :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := return validate_object(&p) } +@(require_results) validate_object_key :: proc(p: ^Parser) -> bool { if p.spec != .JSON { if allow_token(p, .Ident) { @@ -31,6 +33,7 @@ validate_object_key :: proc(p: ^Parser) -> bool { return err == .None } +@(require_results) validate_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> bool { for p.curr_token.kind != end_token { if !validate_object_key(p) { @@ -48,6 +51,7 @@ validate_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> bool { return true } +@(require_results) validate_object :: proc(p: ^Parser) -> bool { if err := expect_token(p, .Open_Brace); err != .None { return false @@ -61,6 +65,7 @@ validate_object :: proc(p: ^Parser) -> bool { return true } +@(require_results) validate_array :: proc(p: ^Parser) -> bool { if err := expect_token(p, .Open_Bracket); err != .None { return false @@ -83,6 +88,7 @@ validate_array :: proc(p: ^Parser) -> bool { return true } +@(require_results) validate_value :: proc(p: ^Parser) -> bool { token := p.curr_token