diff --git a/cli/printer/mql.go b/cli/printer/mql.go index 4594070f64..164c089e2d 100644 --- a/cli/printer/mql.go +++ b/cli/printer/mql.go @@ -503,7 +503,7 @@ func (print *Printer) autoExpand(blockRef uint64, data interface{}, bundle *llx. } func (print *Printer) Data(typ types.Type, data interface{}, codeID string, bundle *llx.CodeBundle, indent string) string { - if typ.IsEmpty() { + if typ.NotSet() { return "no data available" } diff --git a/llx/builtin.go b/llx/builtin.go index 7f346ebf9c..3bd26b052e 100644 --- a/llx/builtin.go +++ b/llx/builtin.go @@ -42,6 +42,8 @@ func init() { string("!=" + types.Time): {f: chunkNeqTrueV2, Label: "!="}, string("==" + types.Dict): {f: chunkEqFalseV2, Label: "=="}, string("!=" + types.Dict): {f: chunkNeqTrueV2, Label: "!="}, + string("==" + types.Empty): {f: chunkEqTrueV2, Label: "=="}, + string("!=" + types.Empty): {f: chunkNeqTrueV2, Label: "!="}, string("==" + types.ArrayLike): {f: chunkEqFalseV2, Label: "=="}, string("!=" + types.ArrayLike): {f: chunkNeqTrueV2, Label: "!="}, string("==" + types.MapLike): {f: chunkEqFalseV2, Label: "=="}, @@ -69,6 +71,8 @@ func init() { string("!=" + types.Time): {f: chunkNeqTrueV2, Label: "!="}, string("==" + types.Dict): {f: boolCmpDictV2, Label: "=="}, string("!=" + types.Dict): {f: boolNotDictV2, Label: "!="}, + string("==" + types.Empty): {f: boolCmpNilV2, Label: "=="}, + string("!=" + types.Empty): {f: boolNotNilV2, Label: "!="}, string("==" + types.ArrayLike): {f: chunkEqFalseV2, Label: "=="}, string("!=" + types.ArrayLike): {f: chunkNeqTrueV2, Label: "!="}, string("==" + types.Array(types.Bool)): {f: boolCmpBoolarrayV2, Label: "=="}, @@ -115,6 +119,8 @@ func init() { string("!=" + types.Regex): {f: intNotRegexV2, Label: "!="}, string("==" + types.Dict): {f: intCmpDictV2, Label: "=="}, string("!=" + types.Dict): {f: intNotDictV2, Label: "!="}, + string("==" + types.Empty): {f: intCmpNilV2, Label: "=="}, + string("!=" + types.Empty): {f: intNotNilV2, Label: "!="}, string("==" + types.ArrayLike): {f: chunkEqFalseV2, Label: "=="}, string("!=" + types.ArrayLike): {f: chunkNeqTrueV2, Label: "!="}, string("==" + types.Array(types.Int)): {f: intCmpIntarrayV2, Label: "=="}, @@ -183,6 +189,8 @@ func init() { string("!=" + types.Regex): {f: floatNotRegexV2, Label: "!="}, string("==" + types.Dict): {f: floatCmpDictV2, Label: "=="}, string("!=" + types.Dict): {f: floatNotDictV2, Label: "!="}, + string("==" + types.Empty): {f: floatCmpNilV2, Label: "=="}, + string("!=" + types.Empty): {f: floatNotNilV2, Label: "!="}, string("==" + types.ArrayLike): {f: chunkEqFalseV2, Label: "=="}, string("!=" + types.ArrayLike): {f: chunkNeqTrueV2, Label: "!="}, string("==" + types.Array(types.Int)): {f: floatCmpIntarrayV2, Label: "=="}, @@ -243,6 +251,8 @@ func init() { // == / != string("==" + types.Nil): {f: stringCmpNilV2, Label: "=="}, string("!=" + types.Nil): {f: stringNotNilV2, Label: "!="}, + string("==" + types.Empty): {f: stringCmpEmptyV2, Label: "=="}, + string("!=" + types.Empty): {f: stringNotEmptyV2, Label: "!="}, string("==" + types.String): {f: stringCmpStringV2, Label: "=="}, string("!=" + types.String): {f: stringNotStringV2, Label: "!="}, string("==" + types.Regex): {f: stringCmpRegexV2, Label: "=="}, @@ -338,6 +348,8 @@ func init() { // == / != string("==" + types.Nil): {f: stringCmpNilV2, Label: "=="}, string("!=" + types.Nil): {f: stringNotNilV2, Label: "!="}, + string("==" + types.Empty): {f: stringCmpEmptyV2, Label: "=="}, + string("!=" + types.Empty): {f: stringNotEmptyV2, Label: "!="}, string("==" + types.Regex): {f: stringCmpStringV2, Label: "=="}, string("!=" + types.Regex): {f: stringNotStringV2, Label: "!="}, string("==" + types.Bool): {f: chunkEqFalseV2, Label: "=="}, @@ -382,6 +394,8 @@ func init() { types.Time: { string("==" + types.Nil): {f: timeCmpNilV2, Label: "=="}, string("!=" + types.Nil): {f: timeNotNilV2, Label: "!="}, + string("==" + types.Empty): {f: timeCmpNilV2, Label: "=="}, + string("!=" + types.Empty): {f: timeNotNilV2, Label: "!="}, string("==" + types.Time): {f: timeCmpTimeV2, Label: "=="}, string("!=" + types.Time): {f: timeNotTimeV2, Label: "!="}, string("<" + types.Time): {f: timeLTTimeV2, Label: "<"}, @@ -421,6 +435,8 @@ func init() { types.Dict: { string("==" + types.Nil): {f: dictCmpNilV2, Label: "=="}, string("!=" + types.Nil): {f: dictNotNilV2, Label: "!="}, + string("==" + types.Empty): {f: dictCmpEmpty, Label: "=="}, + string("!=" + types.Empty): {f: dictNotEmpty, Label: "!="}, string("==" + types.Bool): {f: dictCmpBoolV2, Label: "=="}, string("!=" + types.Bool): {f: dictNotBoolV2, Label: "!="}, string("==" + types.Int): {f: dictCmpIntV2, Label: "=="}, @@ -523,33 +539,35 @@ func init() { "notEmpty": {f: dictNotEmptyV2}, }, types.ArrayLike: { - "[]": {f: arrayGetIndexV2}, - "first": {f: arrayGetFirstIndexV2}, - "last": {f: arrayGetLastIndexV2}, - "{}": {f: arrayBlockListV2}, - "${}": {f: arrayBlockV2}, - "length": {f: arrayLengthV2}, - "where": {f: arrayWhereV2}, - "$whereNot": {f: arrayWhereNotV2}, - "$all": {f: arrayAllV2}, - "$none": {f: arrayNoneV2}, - "$any": {f: arrayAnyV2}, - "$one": {f: arrayOneV2}, - "map": {f: arrayMapV2}, - "flat": {f: arrayFlat}, - "duplicates": {f: arrayDuplicatesV2}, - "fieldDuplicates": {f: arrayFieldDuplicatesV2}, - "unique": {f: arrayUniqueV2}, - "difference": {f: arrayDifferenceV2}, - "containsNone": {f: arrayContainsNoneV2}, - "==": {Compiler: compileArrayOpArray("=="), f: tarrayCmpTarrayV2, Label: "=="}, - "!=": {Compiler: compileArrayOpArray("!="), f: tarrayNotTarrayV2, Label: "!="}, - "==" + string(types.Nil): {f: arrayCmpNilV2}, - "!=" + string(types.Nil): {f: arrayNotNilV2}, - "&&": {Compiler: compileLogicalArrayOp(types.ArrayLike, "&&")}, - "||": {Compiler: compileLogicalArrayOp(types.ArrayLike, "||")}, - "+": {Compiler: compileArrayOpArray("+"), f: tarrayConcatTarrayV2, Label: "+"}, - "-": {Compiler: compileArrayOpArray("-"), f: tarrayDeleteTarrayV2, Label: "-"}, + "[]": {f: arrayGetIndexV2}, + "first": {f: arrayGetFirstIndexV2}, + "last": {f: arrayGetLastIndexV2}, + "{}": {f: arrayBlockListV2}, + "${}": {f: arrayBlockV2}, + "length": {f: arrayLengthV2}, + "where": {f: arrayWhereV2}, + "$whereNot": {f: arrayWhereNotV2}, + "$all": {f: arrayAllV2}, + "$none": {f: arrayNoneV2}, + "$any": {f: arrayAnyV2}, + "$one": {f: arrayOneV2}, + "map": {f: arrayMapV2}, + "flat": {f: arrayFlat}, + "duplicates": {f: arrayDuplicatesV2}, + "fieldDuplicates": {f: arrayFieldDuplicatesV2}, + "unique": {f: arrayUniqueV2}, + "difference": {f: arrayDifferenceV2}, + "containsNone": {f: arrayContainsNoneV2}, + "==": {Compiler: compileArrayOpArray("=="), f: tarrayCmpTarrayV2, Label: "=="}, + "!=": {Compiler: compileArrayOpArray("!="), f: tarrayNotTarrayV2, Label: "!="}, + "==" + string(types.Nil): {f: arrayCmpNilV2}, + "!=" + string(types.Nil): {f: arrayNotNilV2}, + "==" + string(types.Empty): {f: arrayCmpEmpty}, + "!=" + string(types.Empty): {f: arrayNotEmpty}, + "&&": {Compiler: compileLogicalArrayOp(types.ArrayLike, "&&")}, + "||": {Compiler: compileLogicalArrayOp(types.ArrayLike, "||")}, + "+": {Compiler: compileArrayOpArray("+"), f: tarrayConcatTarrayV2, Label: "+"}, + "-": {Compiler: compileArrayOpArray("-"), f: tarrayDeleteTarrayV2, Label: "-"}, // logical operations [] -- K string(types.Any + "&&" + types.Bool): {f: arrayAndBoolV2, Label: "&&"}, string(types.Any + "||" + types.Bool): {f: arrayOrBoolV2, Label: "||"}, @@ -618,13 +636,17 @@ func init() { "notEmpty": {f: arrayNotEmptyV2}, }, types.MapLike: { - "[]": {f: mapGetIndexV2}, - "length": {f: mapLengthV2}, - "where": {f: mapWhereV2}, - "$whereNot": {f: mapWhereNotV2}, - "{}": {f: mapBlockCallV2}, - "keys": {f: mapKeysV2, Label: "keys"}, - "values": {f: mapValuesV2, Label: "values"}, + "[]": {f: mapGetIndexV2}, + "length": {f: mapLengthV2}, + "where": {f: mapWhereV2}, + "$whereNot": {f: mapWhereNotV2}, + "{}": {f: mapBlockCallV2}, + "keys": {f: mapKeysV2, Label: "keys"}, + "values": {f: mapValuesV2, Label: "values"}, + "==" + string(types.Nil): {f: mapCmpNil}, + "!=" + string(types.Nil): {f: mapNotNil}, + "==" + string(types.Empty): {f: mapCmpEmpty}, + "!=" + string(types.Empty): {f: mapNotEmpty}, // {}T -- T string("&&" + types.Bool): {f: chunkEqFalseV2, Label: "&&"}, string("||" + types.Bool): {f: chunkNeqTrueV2, Label: "||"}, @@ -647,8 +669,11 @@ func init() { }, types.ResourceLike: { // == / != - string("==" + types.Nil): {f: chunkEqFalseV2, Label: "=="}, - string("!=" + types.Nil): {f: chunkNeqTrueV2, Label: "!="}, + string("==" + types.Nil): {f: bindingEqNil, Label: "=="}, + string("!=" + types.Nil): {f: bindingNeqNil, Label: "!="}, + // TODO: correctly implement this for list type resources + string("==" + types.Empty): {f: bindingEqNil, Label: "=="}, + string("!=" + types.Empty): {f: bindingNeqNil, Label: "!="}, // fields "where": {f: resourceWhereV2}, "$whereNot": {f: resourceWhereNotV2}, diff --git a/llx/builtin_array.go b/llx/builtin_array.go index 50ab3b8848..1a742ae897 100644 --- a/llx/builtin_array.go +++ b/llx/builtin_array.go @@ -1037,6 +1037,28 @@ func arrayNotNilV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (* return BoolTrue, 0, nil } +func arrayCmpEmpty(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { + if bind.Value == nil { + return BoolTrue, 0, nil + } + v := bind.Value.([]interface{}) + if v == nil { + return BoolTrue, 0, nil + } + return BoolData(len(v) == 0), 0, nil +} + +func arrayNotEmpty(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { + if bind.Value == nil { + return BoolFalse, 0, nil + } + v := bind.Value.([]interface{}) + if v == nil { + return BoolFalse, 0, nil + } + return BoolData(len(v) != 0), 0, nil +} + func boolarrayCmpBoolV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { return rawboolOpV2(e, bind, chunk, ref, func(left *RawData, right *RawData) bool { return cmpArrayOne(left, right, opBoolCmpBool) diff --git a/llx/builtin_map.go b/llx/builtin_map.go index 6961632b5e..59035ba436 100644 --- a/llx/builtin_map.go +++ b/llx/builtin_map.go @@ -56,6 +56,50 @@ func mapGetIndexV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (* }, 0, nil } +func mapCmpNil(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { + if bind.Value == nil { + return BoolTrue, 0, nil + } + v, ok := bind.Value.(map[string]interface{}) + if !ok || v == nil { + return BoolTrue, 0, nil + } + return BoolFalse, 0, nil +} + +func mapNotNil(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { + if bind.Value == nil { + return BoolFalse, 0, nil + } + v, ok := bind.Value.(map[string]interface{}) + if !ok || v == nil { + return BoolFalse, 0, nil + } + return BoolTrue, 0, nil +} + +func mapCmpEmpty(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { + if bind.Value == nil { + return BoolTrue, 0, nil + } + v, ok := bind.Value.(map[string]interface{}) + if !ok || v == nil { + return BoolTrue, 0, nil + } + return BoolData(len(v) == 0), 0, nil +} + +func mapNotEmpty(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { + if bind.Value == nil { + return BoolFalse, 0, nil + } + v, ok := bind.Value.(map[string]interface{}) + if !ok || v == nil { + return BoolFalse, 0, nil + } + return BoolData(len(v) != 0), 0, nil +} + func mapLengthV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { if bind.Value == nil { return &RawData{Type: types.Int}, 0, nil @@ -357,6 +401,41 @@ func dictLengthV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*R } } +func dictCmpEmpty(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { + if bind.Value == nil { + return BoolTrue, 0, nil + } + + switch x := bind.Value.(type) { + case string: + return BoolData(len(x) == 0), 0, nil + case []interface{}: + return BoolData(len(x) == 0), 0, nil + case map[string]interface{}: + return BoolData(len(x) == 0), 0, nil + default: + return BoolFalse, 0, nil + } +} + +func dictNotEmpty(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { + if bind.Value == nil { + return BoolFalse, 0, nil + } + + switch x := bind.Value.(type) { + case string: + return BoolData(len(x) != 0), 0, nil + case []interface{}: + return BoolData(len(x) != 0), 0, nil + case map[string]interface{}: + return BoolData(len(x) != 0), 0, nil + default: + return BoolTrue, 0, nil + } +} + +// Deprecated: replace with calls to the empty type func dictNotEmptyV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { if bind.Value == nil { return BoolFalse, 0, nil diff --git a/llx/builtin_simple.go b/llx/builtin_simple.go index ee50e0e867..f921e39013 100644 --- a/llx/builtin_simple.go +++ b/llx/builtin_simple.go @@ -199,6 +199,14 @@ func chunkNeqTrueV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) ( }) } +func bindingEqNil(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { + return BoolData(bind == nil), 0, nil +} + +func bindingNeqNil(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { + return BoolData(bind != nil), 0, nil +} + // raw operator handling // == != @@ -320,6 +328,20 @@ func timeNotTimeV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (* return boolNotOpV2(e, bind, chunk, ref, opTimeCmpTime) } +func stringCmpEmptyV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { + if bind.Value == nil { + return BoolTrue, 0, nil + } + return BoolData(bind.Value.(string) == ""), 0, nil +} + +func stringNotEmptyV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { + if bind.Value == nil { + return BoolFalse, 0, nil + } + return BoolData(bind.Value.(string) != ""), 0, nil +} + // int arithmetic func intPlusIntV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { diff --git a/llx/data_conversions.go b/llx/data_conversions.go index 8f12a92fc0..25fc0aac41 100644 --- a/llx/data_conversions.go +++ b/llx/data_conversions.go @@ -37,6 +37,7 @@ func init() { types.Time: time2result, types.Dict: dict2result, types.Score: score2result, + types.Empty: empty2result, types.Block: block2result, types.ArrayLike: array2result, types.MapLike: map2result, @@ -55,6 +56,7 @@ func init() { types.Time: ptime2raw, types.Dict: pdict2raw, types.Score: pscore2raw, + types.Empty: pempty2raw, types.Block: pblock2rawV2, types.ArrayLike: parray2raw, types.MapLike: pmap2raw, @@ -226,6 +228,10 @@ func score2result(value interface{}, typ types.Type) (*Primitive, error) { }, nil } +func empty2result(value interface{}, typ types.Type) (*Primitive, error) { + return EmptyPrimitive, nil +} + func block2result(value interface{}, typ types.Type) (*Primitive, error) { m, ok := value.(map[string]interface{}) if !ok { @@ -465,7 +471,7 @@ func (r *Result) RawData() *RawData { data := &RawData{} if r.Data != nil { // The type can be empty, when we do not have data - if r.Data.IsNil() || types.Type(r.Data.Type).IsEmpty() { + if r.Data.IsNil() || types.Type(r.Data.Type).NotSet() { data.Type = types.Nil } else { data = r.Data.RawData() @@ -562,6 +568,10 @@ func pscore2raw(p *Primitive) *RawData { return &RawData{Value: p.Value, Type: types.Score} } +func pempty2raw(p *Primitive) *RawData { + return &RawData{Type: types.Type(p.Type)} +} + func pblock2rawV2(p *Primitive) *RawData { d, err := primitive2rawdataMapV2(p.Map) return &RawData{Value: d, Error: err, Type: types.Type(p.Type)} diff --git a/llx/primitives.go b/llx/primitives.go index ea27db36d7..d214defadc 100644 --- a/llx/primitives.go +++ b/llx/primitives.go @@ -138,6 +138,9 @@ func RefPrimitiveV2(v uint64) *Primitive { } } +// EmptyPrimitive is the empty value indicator +var EmptyPrimitive = &Primitive{Type: string(types.Empty)} + // ArrayPrimitive creates a primitive from a list of primitives func ArrayPrimitive(v []*Primitive, childType types.Type) *Primitive { return &Primitive{ @@ -578,7 +581,7 @@ const mapOverhead = 4 + 1 + 1 + 2 + 4 func (p *Primitive) Size() int { typ := types.Type(p.Type) - if typ.IsEmpty() { + if typ.NotSet() { return 0 } diff --git a/llx/rawdata.go b/llx/rawdata.go index c19dbeb2f0..257ed86606 100644 --- a/llx/rawdata.go +++ b/llx/rawdata.go @@ -221,7 +221,7 @@ func (r *RawData) Score() (int, bool) { func isTruthy(data interface{}, typ types.Type) (bool, bool) { if data == nil && - (typ.IsEmpty() || !typ.IsResource()) { + (typ.NotSet() || !typ.IsResource()) { return false, true } @@ -353,7 +353,7 @@ func (r *RawData) IsSuccess() (bool, bool) { func isSuccess(data interface{}, typ types.Type) (bool, bool) { if data == nil && - (typ.IsEmpty() || !typ.IsResource()) { + (typ.NotSet() || !typ.IsResource()) { return false, false } diff --git a/llx/rawdata_deref.go b/llx/rawdata_deref.go index 167591d62d..5a35b00130 100644 --- a/llx/rawdata_deref.go +++ b/llx/rawdata_deref.go @@ -98,7 +98,7 @@ func dereferenceStringMap(typ types.Type, data map[string]interface{}, codeID st } func dereference(raw *RawData, codeID string, bundle *CodeBundle) (*RawData, error) { - if raw.Type.IsEmpty() || raw.Value == nil { + if raw.Type.NotSet() || raw.Value == nil { return raw, nil } diff --git a/llx/rawdata_json.go b/llx/rawdata_json.go index 3b3cb2b470..e025277242 100644 --- a/llx/rawdata_json.go +++ b/llx/rawdata_json.go @@ -273,7 +273,7 @@ func rawIntMapJSON(typ types.Type, data map[int]interface{}, codeID string, bund // possible for now, because our type system provides most of the information we need, // allowing us to avoid more costly reflection calls. func rawDataJSON(typ types.Type, data interface{}, codeID string, bundle *CodeBundle, buf *bytes.Buffer) error { - if typ.IsEmpty() { + if typ.NotSet() { return errors.New("type information is missing") } diff --git a/mqlc/operators.go b/mqlc/operators.go index c17683093d..640a66a948 100644 --- a/mqlc/operators.go +++ b/mqlc/operators.go @@ -42,9 +42,19 @@ func init() { "typeof": compileTypeof, "switch": compileSwitch, "Never": compileNever, + "empty": compileEmpty, } } +func compileEmpty(c *compiler, id string, call *parser.Call) (types.Type, error) { + c.addChunk(&llx.Chunk{ + Call: llx.Chunk_PRIMITIVE, + Primitive: llx.EmptyPrimitive, + }) + + return types.Time, nil +} + // compile the operation between two operands A and B // examples: A && B, A - B, ... func compileABOperation(c *compiler, id string, call *parser.Call) (uint64, *llx.Chunk, *llx.Primitive, *llx.AssertionMessage, error) { @@ -253,7 +263,7 @@ func compileTransformation(c *compiler, id string, call *parser.Call) (types.Typ } returnType := h.Typ - if returnType == types.Empty { + if returnType == types.NoType { returnType = lt } diff --git a/providers/core/resources/mql_test.go b/providers/core/resources/mql_test.go index ac0caecedf..71ff625006 100644 --- a/providers/core/resources/mql_test.go +++ b/providers/core/resources/mql_test.go @@ -296,6 +296,42 @@ func TestOperations_Equality(t *testing.T) { x.TestSimple(t, simpleTests) } +func TestEmpty(t *testing.T) { + empty := []string{ + "null", + "''", + "[]", + "{}", + } + nonEmpty := []string{ + "true", "false", + "0", "1.0", + "'a'", + "/a/", + "[null]", "[1]", + "{a: 1}", + } + + tests := []testutils.SimpleTest{} + for i := range empty { + tests = append(tests, testutils.SimpleTest{ + Code: empty[i] + " == empty", + ResultIndex: 1, + Expectation: true, + }) + } + + for i := range nonEmpty { + tests = append(tests, testutils.SimpleTest{ + Code: nonEmpty[i] + " == empty", + ResultIndex: 1, + Expectation: false, + }) + } + + x.TestSimple(t, tests) +} + func TestNumber_Methods(t *testing.T) { x.TestSimple(t, []testutils.SimpleTest{ { diff --git a/types/types.go b/types/types.go index 39f689808b..001a44e143 100644 --- a/types/types.go +++ b/types/types.go @@ -46,7 +46,8 @@ const ( byteDict byteScore byteBlock - byteArray = 1<<4 + iota - 4 // set to 25 to avoid breaking changes + byteEmpty + byteArray = 1<<4 + iota - 5 // set to 25 to avoid breaking changes byteMap byteResource byteFunction @@ -54,8 +55,8 @@ const ( byteRange ) -// Empty type is one whose type information is not available at all -const Empty Type = "" +// NoType type is one whose type information is not available at all +const NoType Type = "" const ( // Unset type indicates that the type has not yet been set @@ -84,6 +85,8 @@ const ( Score = Type(rune(byteScore)) // Block evaluation results Block = Type(rune(byteBlock)) + // Empty value + Empty = Type(rune(byteEmpty)) // ArrayLike is the underlying type of all arrays ArrayLike = Type(rune(byteArray)) // MapLike is the underlying type of all maps @@ -109,8 +112,8 @@ const ( Range = Type(rune(byteRange)) ) -// IsEmpty returns true if the type has no information -func (typ Type) IsEmpty() bool { +// NotSet returns true if the type has no information +func (typ Type) NotSet() bool { return typ == "" } @@ -144,7 +147,7 @@ func Resource(name string) Type { // IsResource checks if this type is a map func (typ Type) IsResource() bool { - if typ.IsEmpty() { + if typ.NotSet() { return false } return typ[0] == byteResource @@ -251,6 +254,7 @@ var labels = map[byte]string{ byteDict: "dict", byteScore: "score", byteBlock: "block", + byteEmpty: "empty", byteStringSlice: "stringslice", byteRange: "range", }