Repository: bytedance/sonic
Pull request #278 (2022-08-09 07:15:18)

chore:(ast) support cast `null` to zero value
### Breaking Change
node `V_NULL` will be supported to cast into zero value of below API:
- `String()`: return `""` instead of `"null"`
- `Bool()`: return `false` without error
- `Int64()`: return `int64(0)` without error
- `Float64()`: return `float64(0)` without error
- `Number()`: return `Number("0")` without error


Patch:
diff --git a/ast/node.go b/ast/node.go
index ad66dec..652ef24 100644
--- a/ast/node.go
+++ b/ast/node.go
@@ -177,6 +177,7 @@ func (self *Node) Bool() (bool, error) {
     switch self.t {
         case types.V_TRUE  : return true , nil
         case types.V_FALSE : return false, nil
+        case types.V_NULL  : return false, nil
         case _V_ANY        :        
             if v, ok := self.packAny().(bool); ok {
                 return v, nil
@@ -197,6 +198,7 @@ func (self *Node) Int64() (int64, error) {
         case _V_NUMBER, types.V_STRING : return numberToInt64(self)
         case types.V_TRUE     : return 1, nil
         case types.V_FALSE    : return 0, nil
+        case types.V_NULL     : return 0, nil
         case _V_ANY           :  
             any := self.packAny()
             switch v := any.(type) {
@@ -260,7 +262,8 @@ func (self *Node) Number() (json.Number, error) {
             }
         case types.V_TRUE     : return json.Number("1"), nil
         case types.V_FALSE    : return json.Number("0"), nil
-        case _V_ANY        :        
+        case types.V_NULL     : return json.Number("0"), nil
+        case _V_ANY           :        
             if v, ok := self.packAny().(json.Number); ok {
                 return v, nil
             } else {
@@ -289,7 +292,7 @@ func (self *Node) StrictNumber() (json.Number, error) {
 
 // String returns raw string value if node type is V_STRING.
 // Or return the string representation of other types:
-//  V_NULL => "null",
+//  V_NULL => "",
 //  V_TRUE => "true",
 //  V_FALSE => "false",
 //  V_NUMBER => "[0-9\.]*"
@@ -300,7 +303,7 @@ func (self *Node) String() (string, error) {
     }
     switch self.t {
         case _V_NUMBER       : return toNumber(self).String(), nil
-        case types.V_NULL    : return "null" , nil
+        case types.V_NULL    : return "" , nil
         case types.V_TRUE    : return "true" , nil
         case types.V_FALSE   : return "false", nil
         case types.V_STRING  : return addr2str(self.p, self.v), nil
@@ -321,7 +324,6 @@ func (self *Node) StrictString() (string, error) {
         return "", err
     }
     switch self.t {
-        case types.V_NULL    : return "", nil
         case types.V_STRING  : return addr2str(self.p, self.v), nil
         case _V_ANY          :        
             if v, ok := self.packAny().(string); ok {
@@ -342,7 +344,8 @@ func (self *Node) Float64() (float64, error) {
         case _V_NUMBER, types.V_STRING : return numberToFloat64(self)
         case types.V_TRUE    : return 1.0, nil
         case types.V_FALSE   : return 0.0, nil
-        case _V_ANY        :        
+        case types.V_NULL    : return 0.0, nil
+        case _V_ANY          :        
             any := self.packAny()
             switch v := any.(type) {
                 case float32 : return float64(v), nil


Test patch:
diff --git a/ast/node_test.go b/ast/node_test.go
index 5cc42c7..1173e5e 100644
--- a/ast/node_test.go
+++ b/ast/node_test.go
@@ -282,6 +282,7 @@ func TestTypeCast(t *testing.T) {
         {"Bool", NewAny(false), false, nil},
         {"Bool", NewRaw("true"), true, nil},
         {"Bool", NewRaw("false"), false, nil},
+        {"Bool", NewRaw("null"), false, nil},
         {"Int64", NewRaw("true"), int64(1), nil},
         {"Int64", NewRaw("false"), int64(0), nil},
         {"Int64", NewRaw("\"1\""), int64(1), nil},
@@ -297,6 +298,7 @@ func TestTypeCast(t *testing.T) {
         {"Int64", NewAny(uint64(0)), int64(0), nil},
         {"Int64", Node{}, int64(0), ErrUnsupportType},
         {"Int64", NewRaw("0"), int64(0), nil},
+        {"Int64", NewRaw("null"), int64(0), nil},
         {"StrictInt64", NewRaw("true"), int64(0), ErrUnsupportType},
         {"StrictInt64", NewRaw("false"), int64(0), ErrUnsupportType},
         {"StrictInt64", NewAny(int(0)), int64(0), nil},
@@ -310,6 +312,7 @@ func TestTypeCast(t *testing.T) {
         {"StrictInt64", NewAny(uint64(0)), int64(0), nil},
         {"StrictInt64", Node{}, int64(0), ErrUnsupportType},
         {"StrictInt64", NewRaw("0"), int64(0), nil},
+        {"StrictInt64", NewRaw("null"), int64(0), ErrUnsupportType},
         {"Float64", NewRaw("true"), float64(1), nil},
         {"Float64", NewRaw("false"), float64(0), nil},
         {"Float64", NewRaw("\"1.0\""), float64(1.0), nil},
@@ -318,12 +321,14 @@ func TestTypeCast(t *testing.T) {
         {"Float64", NewAny(float32(0)), float64(0), nil},
         {"Float64", NewAny(float64(0)), float64(0), nil},
         {"Float64", NewRaw("0.0"), float64(0.0), nil},
+        {"Float64", NewRaw("null"), float64(0.0), nil},
         {"StrictFloat64", NewRaw("true"), float64(0), ErrUnsupportType},
         {"StrictFloat64", NewRaw("false"), float64(0), ErrUnsupportType},
         {"StrictFloat64", Node{}, float64(0), ErrUnsupportType},
         {"StrictFloat64", NewAny(float32(0)), float64(0), nil},
         {"StrictFloat64", NewAny(float64(0)), float64(0), nil},
         {"StrictFloat64", NewRaw("0.0"), float64(0.0), nil},
+        {"StrictFloat64", NewRaw("null"), float64(0.0), ErrUnsupportType},
         {"Number", Node{}, json.Number(""), ErrUnsupportType},
         {"Number", NewAny(json.Number("0")), json.Number("0"), nil},
         {"Number", NewRaw("0.0"), json.Number("0.0"), nil},
@@ -332,27 +337,29 @@ func TestTypeCast(t *testing.T) {
         {"Number", NewRaw("\"0.x0\""), json.Number(""), nonEmptyErr},
         {"Number", NewRaw("true"), json.Number("1"), nil},
         {"Number", NewRaw("false"), json.Number("0"), nil},
+        {"Number", NewRaw("null"), json.Number("0"), nil},
         {"StrictNumber", NewRaw("true"), json.Number(""), ErrUnsupportType},
         {"StrictNumber", NewRaw("false"), json.Number(""), ErrUnsupportType},
         {"StrictNumber", Node{}, json.Number(""), ErrUnsupportType},
         {"StrictNumber", NewAny(json.Number("0")), json.Number("0"), nil},
         {"StrictNumber", NewRaw("0.0"), json.Number("0.0"), nil},
+        {"StrictNumber", NewRaw("null"), json.Number(""), ErrUnsupportType},
         {"String", Node{}, "", ErrUnsupportType},
         {"String", NewAny(`\u263a`), `\u263a`, nil},
         {"String", NewRaw(`"\u263a"`), `☺`, nil},
         {"String", NewString(`\u263a`), `\u263a`, nil},
         {"String", NewRaw(`0.0`), "0.0", nil},
-        {"String", NewRaw(`null`), "null", nil},
         {"String", NewRaw(`true`), "true", nil},
         {"String", NewRaw(`false`), "false", nil},
+        {"String", NewRaw(`null`), "", nil},
         {"StrictString", Node{}, "", ErrUnsupportType},
         {"StrictString", NewAny(`\u263a`), `\u263a`, nil},
         {"StrictString", NewRaw(`"\u263a"`), `☺`, nil},
         {"StrictString", NewString(`\u263a`), `\u263a`, nil},
         {"StrictString", NewRaw(`0.0`), "", ErrUnsupportType},
-        {"StrictString", NewRaw(`null`), "", nil},
         {"StrictString", NewRaw(`true`), "", ErrUnsupportType},
         {"StrictString", NewRaw(`false`), "", ErrUnsupportType},
+        {"StrictString", NewRaw(`null`), "", ErrUnsupportType},
         {"Len", Node{}, 0, nil},
         {"Len", NewAny(0), 0, ErrUnsupportType},
         {"Len", NewNull(), 0, nil},