Skip to content

Commit

Permalink
Add to/bytes/bits[range]
Browse files Browse the repository at this point in the history
  • Loading branch information
wader committed Oct 17, 2021
1 parent a050adc commit 7f36f70
Show file tree
Hide file tree
Showing 10 changed files with 449 additions and 52 deletions.
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Copyright (c) 2021 Mattias Wadman
colorjson fork and various code in gojqextra package Copyright (c) 2019-2021 itchyny

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
Expand Down
14 changes: 10 additions & 4 deletions doc/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,22 @@ notable is support for arbitrary-precision integers.
- `format_root/0` return root value of format for value
- `parent/0` return parent value
- `parents/0` output parents of value
- `find` and `grep` all take 1 or 2 arguments. First is a scalar to match, where a string is
treated as a regexp. A buffer will be matches exact bytes. Second argument is regexp
flags with addition to "b" which will treat each byte in the input buffer as a rune, this
- All `find` and `grep` functions take 1 or 2 arguments. First is a scalar to match, where a string is
treated as a regexp. A buffer scalar will be matches exact bytes. Second argument are regexp
flags with addition that "b" will treat each byte in the input buffer as a code point, this
makes it possible to match exact bytes, ex: `find("\u00ff"; b")` will match the byte `0xff` and not
the UTF-8 codepoint `0xff`.
the UTF-8 encoded codepoint for 255.
- `find/1`, `find/2` match in buffer and output match buffers
- `grep/1`, `grep/2` recursively match value and buffer
- `vgrep/1`, `vgrep/2` recursively match value
- `bgrep/1`, `bgrep/2` recursively match buffer
- `fgrep/1`, `fgrep/2` recursively match field name
- Buffers:
- `tobits` - Transform input into a bits buffer not preserving source range, will start at zero.
- `tobitsrange` - Transform input into a bits buffer preserving source range if possible.
- `tobytes` - Transform input into a bytes buffer not preserving source range, will start at zero.
- `tobytesrange` - Transform input into a byte buffer preserving source range if possible.
- `buffer[start:end]`, `buffer[:end]`, `buffer[start:]` - Create a sub buffer from start to end in buffer units preserving source range.
- `open` open file for reading
- `probe` or `decode` probe format and decode
- `mp3`, `matroska`, ..., `<name>`, `decode([name])` force decode as format
Expand Down
50 changes: 47 additions & 3 deletions internal/gojqextra/error.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,40 @@
package gojqextra

import "fmt"
import (
"fmt"
"math/big"

// many of these based on errors form gojq
"github.com/wader/gojq"
)

// many of these based on errors from gojq
// TODO: refactor to use errors from gojq?
// TODO: preview from gojq?

type NonUpdatableTypeError struct {
Typ string
Key string
}

func (err NonUpdatableTypeError) Error() string {
return fmt.Sprintf("update key %v cannot be applied to: %s", err.Key, err.Typ)
}

type FuncTypeError struct {
Name string
V interface{}
}

func (err FuncTypeError) Error() string { return err.Name + " cannot be applied to: " + typeof(err.V) }

type FuncTypeNameError struct {
Name string
Typ string
}

func (err FuncTypeError) Error() string { return err.Name + " cannot be applied to: " + err.Typ }
func (err FuncTypeNameError) Error() string {
return err.Name + " cannot be applied to: " + err.Typ
}

type ExpectedObjectError struct {
Typ string
Expand Down Expand Up @@ -70,3 +93,24 @@ type ArrayIndexTooLargeError struct {
func (err *ArrayIndexTooLargeError) Error() string {
return fmt.Sprintf("array index too large: %v", err.V)
}

func typeof(v interface{}) string {
switch v := v.(type) {
case nil:
return "null"
case bool:
return "boolean"
case int, float64, *big.Int:
return "number"
case string:
return "string"
case []interface{}:
return "array"
case map[string]interface{}:
return "object"
case gojq.JQValue:
return fmt.Sprintf("JQValue(%s)", v.JQValueType())
default:
panic(fmt.Sprintf("invalid value: %v", v))
}
}
12 changes: 12 additions & 0 deletions internal/gojqextra/math.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// from gojq
// The MIT License (MIT)
// Copyright (c) 2019-2021 itchyny

package gojqextra

import "math/bits"

const (
maxInt = 1<<(bits.UintSize-1) - 1 // math.MaxInt64 or math.MaxInt32
minInt = -maxInt - 1 // math.MinInt64 or math.MinInt32
)
127 changes: 127 additions & 0 deletions internal/gojqextra/totype.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Some of these functions are based on gojq func.go functions
// TODO: maybe should be exported from gojq fq branch instead?
// The MIT License (MIT)
// Copyright (c) 2019-2021 itchyny

package gojqextra

import (
"math"
"math/big"
"strconv"

"github.com/wader/gojq"
)

func ToString(x interface{}) (string, bool) {
switch x := x.(type) {
case string:
return x, true
case gojq.JQValue:
return ToString(x.JQValueToGoJQ())
default:
return "", false
}
}

func ToObject(x interface{}) (map[string]interface{}, bool) {
switch x := x.(type) {
case map[string]interface{}:
return x, true
case gojq.JQValue:
return ToObject(x.JQValueToGoJQ())
default:
return nil, false
}
}

func ToArray(x interface{}) ([]interface{}, bool) {
switch x := x.(type) {
case []interface{}:
return x, true
case gojq.JQValue:
return ToArray(x.JQValueToGoJQ())
default:
return nil, false
}
}

func ToBoolean(x interface{}) (bool, bool) {
switch x := x.(type) {
case bool:
return x, true
case gojq.JQValue:
return ToBoolean(x.JQValueToGoJQ())
default:
return false, false
}
}

func IsNull(x interface{}) bool {
switch x := x.(type) {
case nil:
return true
case gojq.JQValue:
return IsNull(x.JQValueToGoJQ())
default:
return false
}
}

func ToInt(x interface{}) (int, bool) {
switch x := x.(type) {
case int:
return x, true
case float64:
return floatToInt(x), true
case *big.Int:
if x.IsInt64() {
if i := x.Int64(); minInt <= i && i <= maxInt {
return int(i), true
}
}
if x.Sign() > 0 {
return maxInt, true
}
return minInt, true
case gojq.JQValue:
return ToInt(x.JQValueToGoJQ())
default:
return 0, false
}
}

func floatToInt(x float64) int {
if minInt <= x && x <= maxInt {
return int(x)
}
if x > 0 {
return maxInt
}
return minInt
}

func ToFloat(x interface{}) (float64, bool) {
switch x := x.(type) {
case int:
return float64(x), true
case float64:
return x, true
case *big.Int:
return bigToFloat(x), true
case gojq.JQValue:
return ToFloat(x.JQValueToGoJQ())
default:
return 0.0, false
}
}

func bigToFloat(x *big.Int) float64 {
if x.IsInt64() {
return float64(x.Int64())
}
if f, err := strconv.ParseFloat(x.String(), 64); err == nil {
return f
}
return math.Inf(x.Sign())
}
60 changes: 28 additions & 32 deletions internal/gojqextra/gojqextra.go → internal/gojqextra/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,6 @@ func expectedArrayOrObject(key interface{}, typ string) error {
}
}

type NonUpdatableTypeError struct {
Typ string
Key string
}

func (err NonUpdatableTypeError) Error() string {
return fmt.Sprintf("update key %v cannot be applied to: %s", err.Key, err.Typ)
}

// array

var _ gojq.JQValue = Array{}
Expand Down Expand Up @@ -86,7 +77,8 @@ func (v Array) JQValueUpdate(key interface{}, u interface{}, delpath bool) inter
if delpath {
return v
}
return FuncTypeError{Name: "setpath", Typ: "number"}
// TODO: wrong error?
return FuncTypeNameError{Name: "setpath", Typ: "number"}
} else if intKey < 0 {
intKey += len(v)
}
Expand All @@ -111,10 +103,10 @@ func (v Array) JQValueHas(key interface{}) interface{} {
}
func (v Array) JQValueType() string { return "array" }
func (v Array) JQValueToNumber() interface{} {
return FuncTypeError{Name: "tonumber", Typ: "array"}
return FuncTypeNameError{Name: "tonumber", Typ: "array"}
}
func (v Array) JQValueToString() interface{} {
return FuncTypeError{Name: "tostring", Typ: "array"}
return FuncTypeNameError{Name: "tostring", Typ: "array"}
}
func (v Array) JQValueToGoJQ() interface{} { return []interface{}(v) }

Expand Down Expand Up @@ -177,10 +169,10 @@ func (v Object) JQValueHas(key interface{}) interface{} {
}
func (v Object) JQValueType() string { return "object" }
func (v Object) JQValueToNumber() interface{} {
return FuncTypeError{Name: "tonumber", Typ: "object"}
return FuncTypeNameError{Name: "tonumber", Typ: "object"}
}
func (v Object) JQValueToString() interface{} {
return FuncTypeError{Name: "tostring", Typ: "object"}
return FuncTypeNameError{Name: "tostring", Typ: "object"}
}
func (v Object) JQValueToGoJQ() interface{} { return map[string]interface{}(v) }

Expand All @@ -203,9 +195,9 @@ func (v Number) JQValueUpdate(key interface{}, u interface{}, delpath bool) inte
return expectedArrayOrObject(key, "number")
}
func (v Number) JQValueEach() interface{} { return IteratorError{Typ: "number"} }
func (v Number) JQValueKeys() interface{} { return FuncTypeError{Name: "keys", Typ: "number"} }
func (v Number) JQValueKeys() interface{} { return FuncTypeNameError{Name: "keys", Typ: "number"} }
func (v Number) JQValueHas(key interface{}) interface{} {
return FuncTypeError{Name: "has", Typ: "number"}
return FuncTypeNameError{Name: "has", Typ: "number"}
}
func (v Number) JQValueType() string { return "number" }
func (v Number) JQValueToNumber() interface{} { return v.V }
Expand Down Expand Up @@ -240,9 +232,9 @@ func (v String) JQValueUpdate(key interface{}, u interface{}, delpath bool) inte
return expectedArrayOrObject(key, "string")
}
func (v String) JQValueEach() interface{} { return IteratorError{Typ: "string"} }
func (v String) JQValueKeys() interface{} { return FuncTypeError{Name: "keys", Typ: "string"} }
func (v String) JQValueKeys() interface{} { return FuncTypeNameError{Name: "keys", Typ: "string"} }
func (v String) JQValueHas(key interface{}) interface{} {
return FuncTypeError{Name: "has", Typ: "string"}
return FuncTypeNameError{Name: "has", Typ: "string"}
}
func (v String) JQValueType() string { return "string" }
func (v String) JQValueToNumber() interface{} { return gojq.NormalizeNumbers(string(v)) }
Expand All @@ -255,7 +247,9 @@ var _ gojq.JQValue = Boolean(true)

type Boolean bool

func (v Boolean) JQValueLength() interface{} { return FuncTypeError{Name: "length", Typ: "boolean"} }
func (v Boolean) JQValueLength() interface{} {
return FuncTypeNameError{Name: "length", Typ: "boolean"}
}
func (v Boolean) JQValueSliceLen() interface{} { return ExpectedArrayError{Typ: "boolean"} }
func (v Boolean) JQValueIndex(index int) interface{} { return ExpectedArrayError{Typ: "boolean"} }
func (v Boolean) JQValueSlice(start int, end int) interface{} {
Expand All @@ -266,13 +260,13 @@ func (v Boolean) JQValueUpdate(key interface{}, u interface{}, delpath bool) int
return expectedArrayOrObject(key, "boolean")
}
func (v Boolean) JQValueEach() interface{} { return IteratorError{Typ: "boolean"} }
func (v Boolean) JQValueKeys() interface{} { return FuncTypeError{Name: "keys", Typ: "boolean"} }
func (v Boolean) JQValueKeys() interface{} { return FuncTypeNameError{Name: "keys", Typ: "boolean"} }
func (v Boolean) JQValueHas(key interface{}) interface{} {
return FuncTypeError{Name: "has", Typ: "boolean"}
return FuncTypeNameError{Name: "has", Typ: "boolean"}
}
func (v Boolean) JQValueType() string { return "boolean" }
func (v Boolean) JQValueToNumber() interface{} {
return FuncTypeError{Name: "tonumber", Typ: "boolean"}
return FuncTypeNameError{Name: "tonumber", Typ: "boolean"}
}
func (v Boolean) JQValueToString() interface{} {
if v {
Expand All @@ -296,13 +290,15 @@ func (v Null) JQValueKey(name string) interface{} { return ExpectedObje
func (v Null) JQValueUpdate(key interface{}, u interface{}, delpath bool) interface{} {
return expectedArrayOrObject(key, "null")
}
func (v Null) JQValueEach() interface{} { return IteratorError{Typ: "null"} }
func (v Null) JQValueKeys() interface{} { return FuncTypeError{Name: "keys", Typ: "null"} }
func (v Null) JQValueHas(key interface{}) interface{} { return FuncTypeError{Name: "has", Typ: "null"} }
func (v Null) JQValueType() string { return "null" }
func (v Null) JQValueToNumber() interface{} { return FuncTypeError{Name: "tonumber", Typ: "null"} }
func (v Null) JQValueToString() interface{} { return "null" }
func (v Null) JQValueToGoJQ() interface{} { return nil }
func (v Null) JQValueEach() interface{} { return IteratorError{Typ: "null"} }
func (v Null) JQValueKeys() interface{} { return FuncTypeNameError{Name: "keys", Typ: "null"} }
func (v Null) JQValueHas(key interface{}) interface{} {
return FuncTypeNameError{Name: "has", Typ: "null"}
}
func (v Null) JQValueType() string { return "null" }
func (v Null) JQValueToNumber() interface{} { return FuncTypeNameError{Name: "tonumber", Typ: "null"} }
func (v Null) JQValueToString() interface{} { return "null" }
func (v Null) JQValueToGoJQ() interface{} { return nil }

// Base

Expand All @@ -325,11 +321,11 @@ func (v Base) JQValueUpdate(key interface{}, u interface{}, delpath bool) interf
return expectedArrayOrObject(key, v.Typ)
}
func (v Base) JQValueEach() interface{} { return IteratorError{Typ: v.Typ} }
func (v Base) JQValueKeys() interface{} { return FuncTypeError{Name: "keys", Typ: v.Typ} }
func (v Base) JQValueKeys() interface{} { return FuncTypeNameError{Name: "keys", Typ: v.Typ} }
func (v Base) JQValueHas(key interface{}) interface{} {
return HasKeyTypeError{L: "array", R: fmt.Sprintf("%v", key)}
}
func (v Base) JQValueType() string { return v.Typ }
func (v Base) JQValueToNumber() interface{} { return FuncTypeError{Name: "tonumber", Typ: v.Typ} }
func (v Base) JQValueToString() interface{} { return FuncTypeError{Name: "tostring", Typ: v.Typ} }
func (v Base) JQValueToNumber() interface{} { return FuncTypeNameError{Name: "tonumber", Typ: v.Typ} }
func (v Base) JQValueToString() interface{} { return FuncTypeNameError{Name: "tostring", Typ: v.Typ} }
func (v Base) JQValueToGoJQ() interface{} { return nil }
2 changes: 1 addition & 1 deletion pkg/interp/buffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func (bv BufferView) JQValueType() string {
return "buffer"
}
func (bv BufferView) JQValueKeys() interface{} {
return gojqextra.FuncTypeError{Name: "keys", Typ: "buffer"}
return gojqextra.FuncTypeNameError{Name: "keys", Typ: "buffer"}
}
func (bv BufferView) JQValueHas(key interface{}) interface{} {
return gojqextra.HasKeyTypeError{L: "buffer", R: fmt.Sprintf("%v", key)}
Expand Down

0 comments on commit 7f36f70

Please sign in to comment.