Update core:encoding/json to use proc groups and @(require_results)

This commit is contained in:
gingerBill
2026-06-29 13:44:03 +01:00
parent 70607eeb0b
commit 9e713e2940
6 changed files with 49 additions and 12 deletions

View File

@@ -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 {

View File

@@ -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' {

View File

@@ -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 {

View File

@@ -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

View File

@@ -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
}

View File

@@ -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