From 1be12f8d92bc559e9a338ec5e297694d1d3a7628 Mon Sep 17 00:00:00 2001 From: Adam Easterling Date: Thu, 8 Dec 2022 15:21:58 -0800 Subject: [PATCH 1/5] Made JSON encoder a public function --- lib/json/json.go | 69 +++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/lib/json/json.go b/lib/json/json.go index 6d9e99e2..1a808063 100644 --- a/lib/json/json.go +++ b/lib/json/json.go @@ -27,29 +27,30 @@ import ( // Module json is a Starlark module of JSON-related functions. // -// json = module( -// encode, -// decode, -// indent, -// ) +// json = module( +// encode, +// decode, +// indent, +// ) // // def encode(x): // // The encode function accepts one required positional argument, // which it converts to JSON by cases: -// - A Starlark value that implements Go's standard json.Marshal -// interface defines its own JSON encoding. -// - None, True, and False are converted to null, true, and false, respectively. -// - Starlark int values, no matter how large, are encoded as decimal integers. -// Some decoders may not be able to decode very large integers. -// - Starlark float values are encoded using decimal point notation, -// even if the value is an integer. -// It is an error to encode a non-finite floating-point value. -// - Starlark strings are encoded as JSON strings, using UTF-16 escapes. -// - a Starlark IterableMapping (e.g. dict) is encoded as a JSON object. -// It is an error if any key is not a string. -// - any other Starlark Iterable (e.g. list, tuple) is encoded as a JSON array. -// - a Starlark HasAttrs (e.g. struct) is encoded as a JSON object. +// - A Starlark value that implements Go's standard json.Marshal +// interface defines its own JSON encoding. +// - None, True, and False are converted to null, true, and false, respectively. +// - Starlark int values, no matter how large, are encoded as decimal integers. +// Some decoders may not be able to decode very large integers. +// - Starlark float values are encoded using decimal point notation, +// even if the value is an integer. +// It is an error to encode a non-finite floating-point value. +// - Starlark strings are encoded as JSON strings, using UTF-16 escapes. +// - a Starlark IterableMapping (e.g. dict) is encoded as a JSON object. +// It is an error if any key is not a string. +// - any other Starlark Iterable (e.g. list, tuple) is encoded as a JSON array. +// - a Starlark HasAttrs (e.g. struct) is encoded as a JSON object. +// // It an application-defined type matches more than one the cases describe above, // (e.g. it implements both Iterable and HasFields), the first case takes precedence. // Encoding any other value yields an error. @@ -58,10 +59,11 @@ import ( // // The decode function accepts one positional parameter, a JSON string. // It returns the Starlark value that the string denotes. -// - Numbers are parsed as int or float, depending on whether they -// contain a decimal point. -// - JSON objects are parsed as new unfrozen Starlark dicts. -// - JSON arrays are parsed as new unfrozen Starlark lists. +// - Numbers are parsed as int or float, depending on whether they +// contain a decimal point. +// - JSON objects are parsed as new unfrozen Starlark dicts. +// - JSON arrays are parsed as new unfrozen Starlark lists. +// // Decoding fails if x is not a valid JSON string. // // def indent(str, *, prefix="", indent="\t"): @@ -71,7 +73,6 @@ import ( // It accepts one required positional parameter, the JSON string, // and two optional keyword-only string parameters, prefix and indent, // that specify a prefix of each new line, and the unit of indentation. -// var Module = &starlarkstruct.Module{ Name: "json", Members: starlark.StringDict{ @@ -81,12 +82,8 @@ var Module = &starlarkstruct.Module{ }, } -func encode(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var x starlark.Value - if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &x); err != nil { - return nil, err - } - +// Encodes the given Starlark value as a string in JSON format. +func EncodeJSON(x starlark.Value) (string, error) { buf := new(bytes.Buffer) var quoteSpace [128]byte @@ -221,9 +218,21 @@ func encode(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, k } if err := emit(x); err != nil { + return "", err + } + return buf.String(), nil +} + +func encode(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + var x starlark.Value + if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &x); err != nil { + return nil, err + } + s, err := EncodeJSON(x) + if err != nil { return nil, fmt.Errorf("%s: %v", b.Name(), err) } - return starlark.String(buf.String()), nil + return starlark.String(s), nil } func pointer(i interface{}) unsafe.Pointer { From 6facf3dd47afb967b71211d4dd3ca746eade1d37 Mon Sep 17 00:00:00 2001 From: Adam Easterling Date: Fri, 9 Dec 2022 13:22:03 -0800 Subject: [PATCH 2/5] Added public Decode functionality to a Starlark value --- lib/json/json.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/json/json.go b/lib/json/json.go index 1a808063..fb6ca36b 100644 --- a/lib/json/json.go +++ b/lib/json/json.go @@ -292,12 +292,8 @@ func indent(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, k return starlark.String(buf.String()), nil } -func decode(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (_ starlark.Value, err error) { - var s string - if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &s); err != nil { - return nil, err - } - +// Decodes the given JSON string into a Starlark value. +func Decode(s string) (_ starlark.Value, err error) { // The decoder necessarily makes certain representation choices // such as list vs tuple, struct vs dict, int vs float. // In principle, we could parameterize it to allow the caller to @@ -519,6 +515,14 @@ func decode(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, k return x, nil } +func decode(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (_ starlark.Value, err error) { + var s string + if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &s); err != nil { + return nil, err + } + return Decode(s) +} + func isdigit(b byte) bool { return b >= '0' && b <= '9' } From 894d433c165d822d28daa0f1702d6af824a8bb0f Mon Sep 17 00:00:00 2001 From: Adam Easterling Date: Fri, 9 Dec 2022 13:26:01 -0800 Subject: [PATCH 3/5] Removed some auto-indentions that were accidentally committed --- lib/json/json.go | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/lib/json/json.go b/lib/json/json.go index fb6ca36b..2ce2c932 100644 --- a/lib/json/json.go +++ b/lib/json/json.go @@ -27,30 +27,29 @@ import ( // Module json is a Starlark module of JSON-related functions. // -// json = module( -// encode, -// decode, -// indent, -// ) +// json = module( +// encode, +// decode, +// indent, +// ) // // def encode(x): // // The encode function accepts one required positional argument, // which it converts to JSON by cases: -// - A Starlark value that implements Go's standard json.Marshal -// interface defines its own JSON encoding. -// - None, True, and False are converted to null, true, and false, respectively. -// - Starlark int values, no matter how large, are encoded as decimal integers. -// Some decoders may not be able to decode very large integers. -// - Starlark float values are encoded using decimal point notation, -// even if the value is an integer. -// It is an error to encode a non-finite floating-point value. -// - Starlark strings are encoded as JSON strings, using UTF-16 escapes. -// - a Starlark IterableMapping (e.g. dict) is encoded as a JSON object. -// It is an error if any key is not a string. -// - any other Starlark Iterable (e.g. list, tuple) is encoded as a JSON array. -// - a Starlark HasAttrs (e.g. struct) is encoded as a JSON object. -// +// - A Starlark value that implements Go's standard json.Marshal +// interface defines its own JSON encoding. +// - None, True, and False are converted to null, true, and false, respectively. +// - Starlark int values, no matter how large, are encoded as decimal integers. +// Some decoders may not be able to decode very large integers. +// - Starlark float values are encoded using decimal point notation, +// even if the value is an integer. +// It is an error to encode a non-finite floating-point value. +// - Starlark strings are encoded as JSON strings, using UTF-16 escapes. +// - a Starlark IterableMapping (e.g. dict) is encoded as a JSON object. +// It is an error if any key is not a string. +// - any other Starlark Iterable (e.g. list, tuple) is encoded as a JSON array. +// - a Starlark HasAttrs (e.g. struct) is encoded as a JSON object. // It an application-defined type matches more than one the cases describe above, // (e.g. it implements both Iterable and HasFields), the first case takes precedence. // Encoding any other value yields an error. @@ -59,11 +58,10 @@ import ( // // The decode function accepts one positional parameter, a JSON string. // It returns the Starlark value that the string denotes. -// - Numbers are parsed as int or float, depending on whether they -// contain a decimal point. -// - JSON objects are parsed as new unfrozen Starlark dicts. -// - JSON arrays are parsed as new unfrozen Starlark lists. -// +// - Numbers are parsed as int or float, depending on whether they +// contain a decimal point. +// - JSON objects are parsed as new unfrozen Starlark dicts. +// - JSON arrays are parsed as new unfrozen Starlark lists. // Decoding fails if x is not a valid JSON string. // // def indent(str, *, prefix="", indent="\t"): @@ -73,6 +71,7 @@ import ( // It accepts one required positional parameter, the JSON string, // and two optional keyword-only string parameters, prefix and indent, // that specify a prefix of each new line, and the unit of indentation. +// var Module = &starlarkstruct.Module{ Name: "json", Members: starlark.StringDict{ From 8a8b15cdfff852cb4a2c2e586675c00db0471837 Mon Sep 17 00:00:00 2001 From: Adam Easterling Date: Fri, 9 Dec 2022 13:37:19 -0800 Subject: [PATCH 4/5] Improved name of JSON encoding function and changed its return value to a byte slice --- lib/json/json.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/json/json.go b/lib/json/json.go index 2ce2c932..355256c9 100644 --- a/lib/json/json.go +++ b/lib/json/json.go @@ -82,7 +82,7 @@ var Module = &starlarkstruct.Module{ } // Encodes the given Starlark value as a string in JSON format. -func EncodeJSON(x starlark.Value) (string, error) { +func Encode(x starlark.Value) ([]byte, error) { buf := new(bytes.Buffer) var quoteSpace [128]byte @@ -217,9 +217,9 @@ func EncodeJSON(x starlark.Value) (string, error) { } if err := emit(x); err != nil { - return "", err + return nil, err } - return buf.String(), nil + return buf.Bytes(), nil } func encode(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { @@ -227,11 +227,11 @@ func encode(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, k if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &x); err != nil { return nil, err } - s, err := EncodeJSON(x) + bytes, err := Encode(x) if err != nil { return nil, fmt.Errorf("%s: %v", b.Name(), err) } - return starlark.String(s), nil + return starlark.String(string(bytes[:])), nil } func pointer(i interface{}) unsafe.Pointer { @@ -291,7 +291,7 @@ func indent(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, k return starlark.String(buf.String()), nil } -// Decodes the given JSON string into a Starlark value. +// Decodes the given JSON-encoded string into a Starlark value. func Decode(s string) (_ starlark.Value, err error) { // The decoder necessarily makes certain representation choices // such as list vs tuple, struct vs dict, int vs float. From ad5c184a30ef83262f4a2dd05d4a2a319a002f54 Mon Sep 17 00:00:00 2001 From: Adam Easterling Date: Fri, 9 Dec 2022 16:32:11 -0800 Subject: [PATCH 5/5] Fixing improper bytes/string conversion; Updating comments --- lib/json/json.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/json/json.go b/lib/json/json.go index 355256c9..16e681c7 100644 --- a/lib/json/json.go +++ b/lib/json/json.go @@ -81,7 +81,7 @@ var Module = &starlarkstruct.Module{ }, } -// Encodes the given Starlark value as a string in JSON format. +// Encode returns the JSON encoding of a Starlark value. func Encode(x starlark.Value) ([]byte, error) { buf := new(bytes.Buffer) @@ -231,7 +231,7 @@ func encode(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, k if err != nil { return nil, fmt.Errorf("%s: %v", b.Name(), err) } - return starlark.String(string(bytes[:])), nil + return starlark.String(bytes), nil } func pointer(i interface{}) unsafe.Pointer { @@ -291,7 +291,7 @@ func indent(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, k return starlark.String(buf.String()), nil } -// Decodes the given JSON-encoded string into a Starlark value. +// Decodes returns the Starlark value of a given JSON-encoded string. func Decode(s string) (_ starlark.Value, err error) { // The decoder necessarily makes certain representation choices // such as list vs tuple, struct vs dict, int vs float.