Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
782bd29
feat: Add support for C-style block comments in lexer
devin-ai-integration[bot] Jan 29, 2025
8a4854f
feat: Implement JSONata 2.0.6 features
devin-ai-integration[bot] Jan 29, 2025
c34c29d
fix: Fix string literal parsing in lexer
devin-ai-integration[bot] Jan 29, 2025
fab3b51
chore: Update CI workflow and fix string parsing
devin-ai-integration[bot] Jan 29, 2025
81d51f1
chore: Update staticcheck and goimports versions
devin-ai-integration[bot] Jan 29, 2025
a53f0e4
chore: Update staticcheck to latest version
devin-ai-integration[bot] Jan 29, 2025
c651310
fix: Remove unused code and simplify type assertions
devin-ai-integration[bot] Jan 29, 2025
5e0906b
fix: Remove unused variable in PathNode.Evaluate
devin-ai-integration[bot] Jan 29, 2025
763270a
fix: Replace fmt.Errorf with errors.New for simple error messages
devin-ai-integration[bot] Jan 29, 2025
fd9c92b
fix: Add errors package import
devin-ai-integration[bot] Jan 29, 2025
8a5dfa2
style: Format code with goimports
devin-ai-integration[bot] Jan 29, 2025
52ab795
fix: Improve goimports check in CI
devin-ai-integration[bot] Jan 29, 2025
7903b90
chore: Format jsonata-test/main.go
devin-ai-integration[bot] Jan 29, 2025
df6a01d
fix: Handle leading zeros and decimal numbers correctly in lexer
devin-ai-integration[bot] Jan 29, 2025
36db271
fix: Handle single zero and decimal numbers correctly in lexer
devin-ai-integration[bot] Jan 29, 2025
9bc63b2
style: Format lexer.go with goimports
devin-ai-integration[bot] Jan 29, 2025
5047694
fix: Improve number parsing in lexer
devin-ai-integration[bot] Jan 29, 2025
6a016ee
fix: Improve number parsing in lexer and node.go
devin-ai-integration[bot] Jan 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ jobs:
test:
strategy:
matrix:
go-version: [1.16.x]
go-version: [1.21.x]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
Expand All @@ -16,11 +16,17 @@ jobs:
uses: actions/checkout@v2
- name: Install dependencies
run: |
go get -u honnef.co/go/tools/cmd/staticcheck@latest
go get -u golang.org/x/tools/cmd/goimports
go install honnef.co/go/tools/cmd/staticcheck@latest
go install golang.org/x/tools/cmd/goimports@latest
- name: Run staticcheck
run: staticcheck ./...
- name: Check code formatting
run: test -z $(goimports -l .)
run: |
files=$(goimports -l .)
if [ -n "$files" ]; then
echo "The following files need formatting:"
echo "$files"
exit 1
fi
- name: Run Test
run: go test ./...
28 changes: 22 additions & 6 deletions env.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package jsonata

import (
"errors"
"math"

"reflect"
"strings"
"unicode/utf8"
Expand Down Expand Up @@ -140,7 +140,13 @@ var baseEnv = initBaseEnv(map[string]Extension{
EvalContextHandler: contextHandlerReplace,
},
"formatNumber": {
Func: jlib.FormatNumber,
Func: func(x float64, picture string, options ...interface{}) (string, error) {
var opt jtypes.OptionalValue
if len(options) > 0 {
opt = jtypes.OptionalValue{Value: reflect.ValueOf(options[0])}
}
return jlib.FormatNumber(x, picture, opt)
},
UndefinedHandler: defaultUndefinedHandler,
EvalContextHandler: contextHandlerFormatNumber,
},
Expand Down Expand Up @@ -188,17 +194,27 @@ var baseEnv = initBaseEnv(map[string]Extension{
EvalContextHandler: defaultContextHandler,
},
"abs": {
Func: math.Abs,
Func: jlib.Abs,
UndefinedHandler: defaultUndefinedHandler,
EvalContextHandler: defaultContextHandler,
},
"ceil": {
Func: jlib.Ceil,
UndefinedHandler: defaultUndefinedHandler,
EvalContextHandler: defaultContextHandler,
},
"floor": {
Func: math.Floor,
Func: jlib.Floor,
UndefinedHandler: defaultUndefinedHandler,
EvalContextHandler: defaultContextHandler,
},
"ceil": {
Func: math.Ceil,
"formatInteger": {
Func: jlib.FormatInteger,
UndefinedHandler: defaultUndefinedHandler,
EvalContextHandler: defaultContextHandler,
},
"parseInteger": {
Func: jlib.ParseInteger,
UndefinedHandler: defaultUndefinedHandler,
EvalContextHandler: defaultContextHandler,
},
Expand Down
103 changes: 97 additions & 6 deletions jlib/number.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,19 @@ import (
"github.com/blues/jsonata-go/jtypes"
)

var reNumber = regexp.MustCompile(`^-?(([0-9]+))(\.[0-9]+)?([Ee][-+]?[0-9]+)?$`)
var (
reNumber = regexp.MustCompile(`^-?(([0-9]+))(\.[0-9]+)?([Ee][-+]?[0-9]+)?$`)
reBinary = regexp.MustCompile(`^0[bB][01]+$`)
reOctal = regexp.MustCompile(`^0[oO][0-7]+$`)
reHex = regexp.MustCompile(`^0[xX][0-9a-fA-F]+$`)
)

// Number converts values to numbers. Numeric values are returned
// unchanged. Strings in legal JSON number format are converted
// to the number they represent. Boooleans are converted to 0 or 1.
// to the number they represent. Booleans are converted to 0 or 1.
// All other types trigger an error.
func Number(value StringNumberBool) (float64, error) {
v := reflect.Value(value)
func Number(value interface{}) (float64, error) {
v := reflect.ValueOf(value)
if b, ok := jtypes.AsBool(v); ok {
if b {
return 1, nil
Expand All @@ -36,7 +41,24 @@ func Number(value StringNumberBool) (float64, error) {
}

s, ok := jtypes.AsString(v)
if ok && reNumber.MatchString(s) {
if !ok {
return 0, fmt.Errorf("unable to cast value to a number")
}
s = strings.TrimSpace(s)

if reBinary.MatchString(s) {
n, _ := strconv.ParseInt(s[2:], 2, 64)
return float64(n), nil
}
if reOctal.MatchString(s) {
n, _ := strconv.ParseInt(s[2:], 8, 64)
return float64(n), nil
}
if reHex.MatchString(s) {
n, _ := strconv.ParseInt(s[2:], 16, 64)
return float64(n), nil
}
if reNumber.MatchString(s) {
if n, err := strconv.ParseFloat(s, 64); err == nil {
return n, nil
}
Expand Down Expand Up @@ -112,11 +134,80 @@ func Random() float64 {
return rand.Float64()
}

// Abs returns the absolute value of x.
func Abs(x float64) float64 {
return math.Abs(x)
}

// Ceil returns the least integer value greater than or equal to x.
func Ceil(x float64) float64 {
return math.Ceil(x)
}

// Floor returns the greatest integer value less than or equal to x.
func Floor(x float64) float64 {
return math.Floor(x)
}

// FormatBase formats a number using the specified base (2-36).
func FormatBase(x float64, base jtypes.OptionalFloat64) (string, error) {
radix := 10
if base.IsSet() {
radix = int(Round(base.Float64, jtypes.OptionalInt{}))
}

if radix < 2 || radix > 36 {
return "", fmt.Errorf("the second argument to formatBase must be between 2 and 36")
}
n := int64(Round(x, jtypes.OptionalInt{}))
return strconv.FormatInt(n, radix), nil
}

// FormatInteger formats an integer using the specified picture string.
func FormatInteger(x float64, picture string) (string, error) {
if picture == "" {
return strconv.FormatInt(int64(Round(x, jtypes.OptionalInt{})), 10), nil
}
return formatNumberWithPicture(x, picture, jtypes.OptionalValue{})
}

// FormatNumber formats a number using the specified picture string and options.
func FormatNumber(x float64, picture string, options jtypes.OptionalValue) (string, error) {
if picture == "" {
return strconv.FormatFloat(x, 'f', -1, 64), nil
}
return formatNumberWithPicture(x, picture, options)
}

// ParseInteger parses a string as an integer using the specified base.
func ParseInteger(value interface{}, base jtypes.OptionalFloat64) (float64, error) {
s, ok := jtypes.AsString(reflect.ValueOf(value))
if !ok {
return 0, fmt.Errorf("first argument of parseInteger must be a string")
}
s = strings.TrimSpace(s)

radix := 10
if base.IsSet() {
radix = int(Round(base.Float64, jtypes.OptionalInt{}))
}

if radix < 0 || radix == 1 || radix > 36 {
return 0, fmt.Errorf("invalid base: %d", radix)
}

n, err := strconv.ParseInt(s, radix, 64)
if err != nil {
return 0, err
}
return float64(n), nil
}

// multByPow10 multiplies a number by 10 to the power of n.
// It does this by converting back and forth to strings to
// avoid floating point rounding errors, e.g.
//
// 4.525 * math.Pow10(2) returns 452.50000000000006
// 4.525 * math.Pow10(2) returns 452.50000000000006
func multByPow10(x float64, n int) float64 {
if n == 0 || math.IsNaN(x) || math.IsInf(x, 0) {
return x
Expand Down
Loading
Loading