Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support decimal numbers in Query.Run #1

Draft
wants to merge 9 commits into
base: fq
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ import (
"fmt"
"log"

"github.com/itchyny/gojq"
"github.com/wader/gojq"
)

func main() {
Expand Down
2 changes: 1 addition & 1 deletion _tools/gen_builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"strings"

"github.com/itchyny/astgen-go"
"github.com/itchyny/gojq"
"github.com/wader/gojq"
)

const fileFormat = `// Code generated by _tools/gen_builtin.go; DO NOT EDIT.
Expand Down
2 changes: 1 addition & 1 deletion _tools/print_builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"sort"
"strings"

"github.com/itchyny/gojq"
"github.com/wader/gojq"
)

func main() {
Expand Down
2 changes: 1 addition & 1 deletion cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

"github.com/mattn/go-isatty"

"github.com/itchyny/gojq"
"github.com/wader/gojq"
)

const name = "gojq"
Expand Down
6 changes: 6 additions & 0 deletions cli/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"sort"
"strconv"
"unicode/utf8"

"github.com/wader/gojq"
)

type encoder struct {
Expand Down Expand Up @@ -66,6 +68,10 @@ func (e *encoder) encode(v any) error {
if err := e.encodeObject(v); err != nil {
return err
}
case gojq.JQValue:
if err := e.encode(v.JQValueToGoJQ()); err != nil {
return err
}
default:
panic(fmt.Sprintf("invalid type: %[1]T (%[1]v)", v))
}
Expand Down
2 changes: 1 addition & 1 deletion cli/inputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

"gopkg.in/yaml.v3"

"github.com/itchyny/gojq"
"github.com/wader/gojq"
)

type inputReader struct {
Expand Down
2 changes: 1 addition & 1 deletion cmd/gojq/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package main
import (
"os"

"github.com/itchyny/gojq/cli"
"github.com/wader/gojq/cli"
)

func main() {
Expand Down
5 changes: 5 additions & 0 deletions compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package gojq
import (
"math"
"math/big"

"github.com/shopspring/decimal"
)

// Compare l and r, and returns jq-flavored comparison value.
Expand All @@ -25,6 +27,9 @@ func compare(l, r any) int {
return 1
}
},
func(l, r decimal.Decimal) any {
return l.Cmp(r)
},
func(l, r *big.Int) any {
return l.Cmp(r)
},
Expand Down
13 changes: 12 additions & 1 deletion compare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import (
"math/big"
"testing"

"github.com/itchyny/gojq"
"github.com/shopspring/decimal"
"github.com/wader/gojq"
)

func TestCompare(t *testing.T) {
Expand Down Expand Up @@ -35,6 +36,16 @@ func TestCompare(t *testing.T) {
{1.00, 1.01, -1},
{1.01, 1.00, 1},
{1.01, 1.01, 0},
{decimal.New(1, 0), decimal.New(1, 0), 0},
{decimal.NewFromFloat(1.00), decimal.NewFromFloat(1.01), -1},
{decimal.NewFromFloat(1.01), decimal.NewFromFloat(1.00), 1},
{1, decimal.New(1, 0), 0},
{decimal.New(1, 0), 1, 0},
{1.00, decimal.NewFromFloat(1.01), -1},
{decimal.NewFromFloat(1.01), 1.00, 1},
{1.01, decimal.NewFromFloat(1.00), 1},
{big.NewInt(1), decimal.NewFromFloat(1.00), 0},
{decimal.NewFromFloat(1.01), big.NewInt(1), 1},
{1, big.NewInt(0), 1},
{big.NewInt(0), 1, -1},
{0, big.NewInt(0), 0},
Expand Down
58 changes: 58 additions & 0 deletions compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,13 @@ func (c *compiler) compileFunc(e *Func) error {
true,
-1,
)
case "scope":
return c.compileCallInternal(
[3]any{c.funcScope, 0, e.Name},
e.Args,
true,
-1,
)
case "input":
if c.inputIter == nil {
return &inputNotAllowedError{}
Expand All @@ -990,6 +997,20 @@ func (c *compiler) compileFunc(e *Func) error {
true,
-1,
)
case "scopedump":
c.append(&code{op: oppop})
n := 0
for i := len(c.scopes) - 1; i >= 0; i-- {
s := c.scopes[i]
for j := len(s.variables) - 1; j >= 0; j-- {
v := s.variables[j]
c.append(&code{op: oppush, v: v.name})
c.append(&code{op: opload, v: v.index})
n++
}
}
c.append(&code{op: opobject, v: n})
return nil
default:
return c.compileCall(e.Name, e.Args)
}
Expand Down Expand Up @@ -1161,6 +1182,43 @@ func (c *compiler) funcBuiltins(any, []any) any {
return ys
}

func (c *compiler) funcScope(any, []any) any {
var xs []any
for _, fds := range builtinFuncDefs {
for _, fd := range fds {
xs = append(xs, fmt.Sprintf("%s/%d", fd.Name, len(fd.Args)))
}
}
for name, f := range internalFuncs {
for _, a := range f.arities() {
xs = append(xs, fmt.Sprintf("%s/%d", name, a))
}
}
for name, f := range c.customFuncs {
for _, a := range f.arities() {
xs = append(xs, fmt.Sprintf("%s/%d", name, a))
}
}
if c.environLoader != nil {
xs = append(xs, "$ENV")
}
for i := len(c.scopes) - 1; i >= 0; i-- {
s := c.scopes[i]
for j := len(s.variables) - 1; j >= 0; j-- {
v := s.variables[j]
xs = append(xs, v.name)
}
for j := len(s.funcs) - 1; j >= 0; j-- {
f := s.funcs[j]
xs = append(xs, fmt.Sprintf("%s/%d", f.name, f.argcnt))
}
}
sort.Slice(xs, func(i, j int) bool {
return xs[i].(string) < xs[j].(string)
})
return xs
}

func (c *compiler) funcInput(any, []any) any {
v, ok := c.inputIter.Next()
if !ok {
Expand Down
2 changes: 1 addition & 1 deletion compiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"time"
"unsafe"

"github.com/itchyny/gojq"
"github.com/wader/gojq"
)

func ExampleCompile() {
Expand Down
2 changes: 2 additions & 0 deletions debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ func debugValue(v any) string {
return fmt.Sprintf("gojq.Iter(%#v)", v)
case []pathValue:
return fmt.Sprintf("[]gojq.pathValue(%v)", v)
case JQValue:
return fmt.Sprintf("gojq.JQValue(%s)", Preview(v))
case [2]int:
return fmt.Sprintf("[%d,%d]", v[0], v[1])
case [3]int:
Expand Down
6 changes: 6 additions & 0 deletions encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"strconv"
"strings"
"unicode/utf8"

"github.com/shopspring/decimal"
)

// Marshal returns the jq-flavored JSON encoding of v.
Expand Down Expand Up @@ -60,6 +62,8 @@ func (e *encoder) encode(v any) {
e.w.Write(strconv.AppendInt(e.buf[:0], int64(v), 10))
case float64:
e.encodeFloat64(v)
case decimal.Decimal:
e.w.WriteString(v.String())
case *big.Int:
e.w.Write(v.Append(e.buf[:0], 10))
case string:
Expand All @@ -68,6 +72,8 @@ func (e *encoder) encode(v any) {
e.encodeArray(v)
case map[string]any:
e.encodeObject(v)
case JQValue:
e.encode(v.JQValueToGoJQ())
default:
panic(fmt.Sprintf("invalid type: %[1]T (%[1]v)", v))
}
Expand Down
2 changes: 1 addition & 1 deletion encoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"math/big"
"testing"

"github.com/itchyny/gojq"
"github.com/wader/gojq"
)

func TestMarshal(t *testing.T) {
Expand Down
39 changes: 38 additions & 1 deletion execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ loop:
m := make(map[string]any, n)
for i := 0; i < n; i++ {
v, k := env.pop(), env.pop()
if jv, ok := k.(JQValue); ok {
k = jv.JQValueToString()
}
s, ok := k.(string)
if !ok {
err = &objectKeyNotStringError{k}
Expand Down Expand Up @@ -147,7 +150,9 @@ loop:
pc = code.v.(int)
goto loop
case opjumpifnot:
if v := env.pop(); v == nil || v == false {
v := env.pop()
b, bOk := toBoolean(v)
if isNull(v) || (bOk && !b) {
pc = code.v.(int)
goto loop
}
Expand All @@ -157,6 +162,9 @@ loop:
}
p, v := code.v, env.pop()
if code.op == opindexarray && v != nil {
if jqv, ok := v.(JQValue); ok {
v = jqv.JQValueToGoJQ()
}
if _, ok := v.([]any); !ok {
err = &expectedArrayError{v}
break loop
Expand Down Expand Up @@ -320,6 +328,32 @@ loop:
continue
}
break loop
case JQValue:
if !env.paths.empty() && env.expdepth == 0 && !env.pathIntact(v) {
err = &invalidPathIterError{v}
break loop
}
xsv := v.JQValueEach()
if e, ok := xsv.(error); ok {
err = e
break loop
}
switch xsv := xsv.(type) {
case []PathValue:
// convert from external PathValue to internal pathValue to make it easier to follow upstream
xs = make([]pathValue, len(xsv))
if len(xsv) == 0 {
break loop
}
for i, pv := range xsv {
xs[i] = pathValue{path: pv.Path, value: pv.Value}
}
case nil:
break loop
default:
err = &iteratorError{xsv}
break loop
}
default:
err = &iteratorError{v}
env.push(emptyIter{})
Expand Down Expand Up @@ -431,6 +465,9 @@ func (env *env) pathIntact(v any) bool {
if w, ok := w.(float64); ok {
return v == w || math.IsNaN(v) && math.IsNaN(w)
}
case JQValue:
// TODO: JQValue: should understand this better
return true
}
return v == w
}
Expand Down