🌱 Bump github.com/itchyny/gojq from 0.12.18 to 0.12.19 (#1472)

Bumps [github.com/itchyny/gojq](https://github.com/itchyny/gojq) from 0.12.18 to 0.12.19.
- [Release notes](https://github.com/itchyny/gojq/releases)
- [Changelog](https://github.com/itchyny/gojq/blob/main/CHANGELOG.md)
- [Commits](https://github.com/itchyny/gojq/compare/v0.12.18...v0.12.19)

---
updated-dependencies:
- dependency-name: github.com/itchyny/gojq
  dependency-version: 0.12.19
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
dependabot[bot]
2026-04-20 07:48:54 +00:00
committed by GitHub
co-authored by lnx01
parent c6648845f6
commit cdd00ce6b8
21 changed files with 560 additions and 481 deletions
+2 -2
View File
@@ -14,7 +14,7 @@ require (
github.com/ghodss/yaml v1.0.0
github.com/google/cel-go v0.28.0
github.com/google/go-cmp v0.7.0
github.com/itchyny/gojq v0.12.18
github.com/itchyny/gojq v0.12.19
github.com/onsi/ginkgo/v2 v2.28.1
github.com/onsi/gomega v1.39.1
github.com/openshift/api v0.0.0-20251125174858-5cf710f68a92
@@ -129,7 +129,7 @@ require (
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/itchyny/timefmt-go v0.1.7 // indirect
github.com/itchyny/timefmt-go v0.1.8 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
+4 -4
View File
@@ -266,10 +266,10 @@ github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/itchyny/gojq v0.12.18 h1:gFGHyt/MLbG9n6dqnvlliiya2TaMMh6FFaR2b1H6Drc=
github.com/itchyny/gojq v0.12.18/go.mod h1:4hPoZ/3lN9fDL1D+aK7DY1f39XZpY9+1Xpjz8atrEkg=
github.com/itchyny/timefmt-go v0.1.7 h1:xyftit9Tbw+Dc/huSSPJaEmX1TVL8lw5vxjJLK4GMMA=
github.com/itchyny/timefmt-go v0.1.7/go.mod h1:5E46Q+zj7vbTgWY8o5YkMeYb4I6GeWLFnetPy5oBrAI=
github.com/itchyny/gojq v0.12.19 h1:ttXA0XCLEMoaLOz5lSeFOZ6u6Q3QxmG46vfgI4O0DEs=
github.com/itchyny/gojq v0.12.19/go.mod h1:5galtVPDywX8SPSOrqjGxkBeDhSxEW1gSxoy7tn1iZY=
github.com/itchyny/timefmt-go v0.1.8 h1:1YEo1JvfXeAHKdjelbYr/uCuhkybaHCeTkH8Bo791OI=
github.com/itchyny/timefmt-go v0.1.8/go.mod h1:5E46Q+zj7vbTgWY8o5YkMeYb4I6GeWLFnetPy5oBrAI=
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
+15
View File
@@ -1,4 +1,19 @@
# Changelog
## [v0.12.19](https://github.com/itchyny/gojq/compare/v0.12.18..v0.12.19) (2026-04-01)
* fix `gsub` and `sub` when the replacement emits multiple values
* fix `fmax`, `fmin`, `modf` functions against NaN and infinities
* fix `join/1` to use `add/0` implementation and handle null separator
* fix `del` and `delpaths` on null to emit null
* fix arithmetic operations on the minimum integer
* fix `significand` function against subnormal numbers
* fix handling of `--` in cli flag parsing for jq compatibility
* fix `flatten/1` to emit error when the depth is NaN
* fix array slice update to validate index types
* fix string repetition boundary check to match jq behavior
* implement `splits/2` using `match/2` for better jq compatibility
* implement `to_entries` and `from_entries` in jq for simplicity
* improve performance of regexp functions by caching compiled regexps
## [v0.12.18](https://github.com/itchyny/gojq/compare/v0.12.17..v0.12.18) (2025-12-02)
* implement `trimstr/1`, `toboolean/0` function
* fix `last/1` to be included in `builtins/0`
+1 -1
View File
@@ -1,4 +1,4 @@
FROM golang:1.25 AS builder
FROM golang:1.26 AS builder
WORKDIR /app
COPY go.* ./
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2019-2025 itchyny
Copyright (c) 2019-2026 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
+1 -1
View File
@@ -75,7 +75,7 @@ lint: $(GOBIN)/staticcheck
staticcheck -checks all -tags gojq_debug ./...
$(GOBIN)/staticcheck:
go install honnef.co/go/tools/cmd/staticcheck@latest
go install honnef.co/go/tools/cmd/staticcheck@v0.6.1
.PHONY: check-tools
check-tools:
+5
View File
@@ -56,6 +56,11 @@ gojq: invalid json: <stdin>
brew install gojq
```
### mise
```sh
mise use -g gojq@latest
```
### Zero Install
```sh
0install add gojq https://apps.0install.net/utils/gojq.xml
+6 -4
View File
@@ -15,11 +15,12 @@ func init() {
"any": {{Name: "any", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{{Term: &Term{Type: TermTypeIdentity}}}}}}}, {Name: "any", Args: []string{"y"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "y"}}}}}}}}, {Name: "any", Args: []string{"g", "y"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "isempty", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "y"}}}}}}}, Op: OpPipe}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "not"}}}, Op: OpPipe}}},
"arrays": {{Name: "arrays", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "type"}}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}, Op: OpEq}}}}}}},
"booleans": {{Name: "booleans", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "type"}}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "boolean"}}}, Op: OpEq}}}}}}},
"capture": {{Name: "capture", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "capture", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeNull}}}}}}}, {Name: "capture", Args: []string{"$re", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$flags"}}}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_capture"}}}, Op: OpPipe}}},
"capture": {{Name: "capture", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "capture", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeNull}}}}}}}, {Name: "capture", Args: []string{"$re", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$flags"}}}}}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "captures"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_captures"}}}, Op: OpPipe}, Op: OpPipe}}},
"combinations": {{Name: "combinations", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "length"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, Op: OpEq}, Then: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}, Else: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, SuffixList: []*Suffix{{Iter: true}}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$x"}}}}}}, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}, IsSlice: true}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "combinations"}}}, Op: OpPipe}}}, Op: OpAdd}, Patterns: []*Pattern{{Name: "$x"}}, Op: OpPipe}}}}}, {Name: "combinations", Args: []string{"n"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "limit", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "n"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "repeat", Args: []*Query{{Term: &Term{Type: TermTypeIdentity}}}}}}}}}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "combinations"}}}, Op: OpPipe}}},
"del": {{Name: "del", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "delpaths", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}}}}}}}}}}}}}},
"finites": {{Name: "finites", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "isfinite"}}}}}}}}},
"first": {{Name: "first", Body: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}, {Name: "first", Args: []string{"g"}, Body: &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{Ident: "$out", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}, Op: OpComma}, Op: OpPipe}}}}}},
"from_entries": {{Name: "from_entries", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{{KeyQuery: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "key"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "Key"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "name"}}}, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "Name"}}}, Op: OpAlt}, Op: OpAlt}, Op: OpAlt}, Val: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "has", Args: []*Query{{Term: &Term{Type: TermTypeString, Str: &String{Str: "value"}}}}}}}, Then: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "value"}}}, Else: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "Value"}}}}}}}}}}}}}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "add"}}}, Right: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{}}}, Op: OpAlt}, Op: OpPipe}}},
"fromdate": {{Name: "fromdate", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "fromdateiso8601"}}}}},
"fromdateiso8601": {{Name: "fromdateiso8601", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "strptime", Args: []*Query{{Term: &Term{Type: TermTypeString, Str: &String{Str: "%Y-%m-%dT%H:%M:%S%z"}}}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "mktime"}}}, Op: OpPipe}}},
"fromstream": {{Name: "fromstream", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}, Pattern: &Pattern{Name: "$pv"}, Start: &Query{Term: &Term{Type: TermTypeNull}}, Update: &Query{Left: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "e"}}}, Then: &Query{Term: &Term{Type: TermTypeNull}}}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$pv"}}}, Right: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$pv"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "length"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "2"}}, Op: OpEq}, Op: OpPipe}, Then: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "v"}}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$p"}}}, Op: OpAdd}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$v"}}}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "e"}}}}}}, {Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$p"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "length"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, Op: OpEq}, Op: OpPipe}}}}}, Op: OpPipe}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "e"}}}}}}, {Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$p"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "length"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}, Op: OpEq}, Op: OpPipe}}}}}}}}, Patterns: []*Pattern{{Array: []*Pattern{{Name: "$p"}, {Name: "$v"}}}}, Op: OpPipe}, Op: OpPipe}, Extract: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "e"}}}, Then: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "v"}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "empty"}}}}}}}}}}},
@@ -27,7 +28,6 @@ func init() {
"gsub": {{Name: "gsub", Args: []string{"$re", "str"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sub", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "str"}}}, {Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}}}}}}, {Name: "gsub", Args: []string{"$re", "str", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sub", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "str"}}}, {Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$flags"}}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}, Op: OpAdd}}}}}}},
"in": {{Name: "in", Args: []string{"xs"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "xs"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "has", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$x"}}}}}}}, Op: OpPipe}, Patterns: []*Pattern{{Name: "$x"}}, Op: OpPipe}}},
"inputs": {{Name: "inputs", Body: &Query{Term: &Term{Type: TermTypeTry, Try: &Try{Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "repeat", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "input"}}}}}}}, Catch: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "break"}}}, Op: OpEq}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "empty"}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "error"}}}}}}}}}}},
"inside": {{Name: "inside", Args: []string{"xs"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "xs"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "contains", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$x"}}}}}}}, Op: OpPipe}, Patterns: []*Pattern{{Name: "$x"}}, Op: OpPipe}}},
"isempty": {{Name: "isempty", Args: []string{"g"}, Body: &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{Ident: "$out", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFalse}}, Right: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}, Op: OpComma}, Op: OpPipe}}}, Right: &Query{Term: &Term{Type: TermTypeTrue}}, Op: OpComma}}}}}},
"iterables": {{Name: "iterables", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "type"}}}, Right: &Query{Left: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}, Op: OpEq}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "object"}}}, Op: OpEq}, Op: OpOr}, Op: OpPipe}}}}}}},
"last": {{Name: "last", Body: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}}, {Name: "last", Args: []string{"g"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_last", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}}}}}}}},
@@ -53,10 +53,12 @@ func init() {
"select": {{Name: "select", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}, Then: &Query{Term: &Term{Type: TermTypeIdentity}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "empty"}}}}}}}},
"skip": {{Name: "skip", Args: []string{"$n", "g"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$n"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, Op: OpGt}, Then: &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}}, Pattern: &Pattern{Name: "$item"}, Start: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$n"}}}, Update: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}, Op: OpSub}, Extract: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, Op: OpLt}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$item"}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "empty"}}}}}}}}}, Elif: []*IfElif{{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$n"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, Op: OpEq}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "error", Args: []*Query{{Term: &Term{Type: TermTypeString, Str: &String{Str: "skip doesn't support negative count"}}}}}}}}}}}},
"sort_by": {{Name: "sort_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_sort_by", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}}}}}}}}}}}}}},
"splits": {{Name: "splits", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "splits", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeNull}}}}}}}, {Name: "splits", Args: []string{"$re", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "split", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$flags"}}}}}, SuffixList: []*Suffix{{Iter: true}}}}}},
"split": {{Name: "split", Args: []string{"$re", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "splits", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$flags"}}}}}}}}}}}},
"splits": {{Name: "splits", Args: []string{"$re", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{Query: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$flags"}}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}, Op: OpAdd}}}}}, Right: &Query{Term: &Term{Type: TermTypeNull}}, Op: OpComma}}}, Pattern: &Pattern{Object: []*PatternObject{{Key: "$offset"}, {Key: "$length"}}}, Start: &Query{Term: &Term{Type: TermTypeNull}}, Update: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{{Key: "start", Val: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "next"}}}}, {Key: "end", Val: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$offset"}}}}, {Key: "next", Val: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$offset"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$length"}}}, Op: OpAdd}}}}}}}}}}}}}, {Name: "splits", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "splits", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeNull}}}}}}}},
"strings": {{Name: "strings", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "type"}}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}, Op: OpEq}}}}}}},
"sub": {{Name: "sub", Args: []string{"$re", "str"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sub", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "str"}}}, {Term: &Term{Type: TermTypeNull}}}}}}}, {Name: "sub", Args: []string{"$re", "str", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{FuncDefs: []*FuncDef{{Name: "_sub", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "matches"}}}, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}, Op: OpEq}, Then: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$str"}, SuffixList: []*Suffix{{Index: &Index{End: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "offset"}}}, IsSlice: true}}}}}, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}, Op: OpAdd}, Else: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "matches"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{{Key: "string", Val: &Query{Left: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_capture"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "str"}}}, Op: OpPipe}, Op: OpPipe}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$str"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{{Index: &Index{Name: "offset"}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{{Index: &Index{Name: "length"}}}}}, Op: OpAdd}, End: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "offset"}}}, IsSlice: true}}}}}, Op: OpAdd}, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}, Op: OpAdd}}, {Key: "offset", Val: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{{Index: &Index{Name: "offset"}}}}}}, {Key: "matches", Val: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "matches"}, SuffixList: []*Suffix{{Index: &Index{End: &Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeNumber, Number: "1"}}}}, IsSlice: true}}}}}}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_sub"}}}, Op: OpPipe}, Patterns: []*Pattern{{Name: "$r"}}, Op: OpPipe}}}}}}, Left: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{{Key: "string", Val: &Query{Term: &Term{Type: TermTypeString, Str: &String{}}}}, {Key: "matches", Val: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$flags"}}}}}}}}}}}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_sub"}}}, Op: OpPipe}, Patterns: []*Pattern{{Name: "$str"}}, Op: OpPipe}}},
"sub": {{Name: "sub", Args: []string{"$re", "str"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sub", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "str"}}}, {Term: &Term{Type: TermTypeNull}}}}}}}, {Name: "sub", Args: []string{"$re", "str", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$flags"}}}}}}}, Pattern: &Pattern{Object: []*PatternObject{{Key: "$offset"}, {Key: "$length"}, {Key: "$captures"}}}, Start: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{{Key: "s", Val: &Query{Term: &Term{Type: TermTypeIdentity}}}, {Key: "r", Val: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}}}}}}, Update: &Query{Left: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Query: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$captures"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_captures"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "str"}}}, Op: OpPipe}, Op: OpPipe}}}, Pattern: &Pattern{Name: "$s"}, Start: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "i"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, Op: OpAssign}, Update: &Query{Left: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "r"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "i"}}}}}}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "s"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "next"}}}, End: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$offset"}}}, IsSlice: true}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$s"}}}, Op: OpAdd}, Op: OpUpdateAdd}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "i"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}, Op: OpUpdateAdd}, Op: OpPipe}}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "next"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$offset"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$length"}}}, Op: OpAdd}, Op: OpAssign}, Op: OpPipe}}}}, Right: &Query{Left: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "r"}, SuffixList: []*Suffix{{Iter: true}}}}, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "s"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "next"}}}, IsSlice: true}}}}}, Op: OpAdd}, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "s"}}}, Op: OpAlt}, Op: OpPipe}}},
"test": {{Name: "test", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "test", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeNull}}}}}}}, {Name: "test", Args: []string{"$re", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_match", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$flags"}}}, {Term: &Term{Type: TermTypeTrue}}}}}}}},
"to_entries": {{Name: "to_entries", Body: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "keys"}, SuffixList: []*Suffix{{Iter: true}}}}, Right: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{{Key: "key", Val: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$k"}}}}, {Key: "value", Val: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$k"}}}}}}}}}}}, Patterns: []*Pattern{{Name: "$k"}}, Op: OpPipe}}}}}},
"todate": {{Name: "todate", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "todateiso8601"}}}}},
"todateiso8601": {{Name: "todateiso8601", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "strftime", Args: []*Query{{Term: &Term{Type: TermTypeString, Str: &String{Str: "%Y-%m-%dT%H:%M:%SZ"}}}}}}}}},
"tostream": {{Name: "tostream", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{{FuncDefs: []*FuncDef{{Name: "r", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}, {Optional: true}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "r"}}}, Op: OpPipe}}}, Right: &Query{Term: &Term{Type: TermTypeIdentity}}, Op: OpComma}}}, Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "r"}}}}}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "getpath", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$p"}}}}}}}, Right: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}, {Optional: true}}}}}}}}, Pattern: &Pattern{Name: "$q"}, Start: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$p"}}}, Right: &Query{Term: &Term{Type: TermTypeIdentity}}, Op: OpComma}}}}, Update: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$p"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$q"}}}, Op: OpAdd}}}}}}}, Op: OpPipe}, Patterns: []*Pattern{{Name: "$p"}}, Op: OpPipe}}},
+22 -22
View File
@@ -1,12 +1,19 @@
def not: if . then false else true end;
def in(xs): . as $x | xs | has($x);
def map(f): [.[] | f];
def with_entries(f): to_entries | map(f) | from_entries;
def add(f): [f] | add;
def map_values(f): .[] |= f;
def in(xs): . as $x | xs | has($x);
def not: if . then false else true end;
def select(f): if f then . else empty end;
def recurse: recurse(.[]?);
def recurse(f): def r: ., (f | r); r;
def recurse(f; cond): def r: ., (f | select(cond) | r); r;
def to_entries: [keys[] as $k | {key: $k, value: .[$k]}];
def from_entries: map({ (.key // .Key // .name // .Name):
if has("value") then .value else .Value end }) | add // {};
def with_entries(f): to_entries | map(f) | from_entries;
def while(cond; update):
def _while: if cond then ., (update | _while) else empty end;
_while;
@@ -20,7 +27,6 @@ def range($end): _range(0; $end; 1);
def range($start; $end): _range($start; $end; 1);
def range($start; $end; $step): _range($start; $end; $step);
def add(f): [f] | add;
def min_by(f): _min_by(map([f]));
def max_by(f): _max_by(map([f]));
def sort_by(f): _sort_by(map([f]));
@@ -39,7 +45,6 @@ def nulls: select(. == null);
def values: select(. != null);
def scalars: select(type | . != "array" and . != "object");
def inside(xs): . as $x | xs | contains($x);
def combinations:
if length == 0 then
[]
@@ -47,6 +52,7 @@ def combinations:
.[0][] as $x | [$x] + (.[1:] | combinations)
end;
def combinations(n): [limit(n; repeat(.))] | combinations;
def walk(f):
def _walk:
if type == "array" then
@@ -67,6 +73,7 @@ def all(g; y): isempty(g | select(y | not));
def any: any(.);
def any(y): any(.[]; y);
def any(g; y): isempty(g | select(y)) | not;
def limit($n; g):
if $n > 0 then
label $out |
@@ -121,7 +128,6 @@ def tostream:
getpath($p) |
reduce path(.[]?) as $q ([$p, .]; [$p + $q]);
def map_values(f): .[] |= f;
def del(f): delpaths([path(f)]);
def paths: path(..) | select(. != []);
def paths(f): path(.. | select(f)) | select(. != []);
@@ -138,7 +144,7 @@ def match($re; $flags): _match($re; $flags; false)[];
def test($re): test($re; null);
def test($re; $flags): _match($re; $flags; true);
def capture($re): capture($re; null);
def capture($re; $flags): match($re; $flags) | _capture;
def capture($re; $flags): match($re; $flags) | .captures | _captures;
def scan($re): scan($re; null);
def scan($re; $flags):
match($re; $flags + "g") |
@@ -147,24 +153,18 @@ def scan($re; $flags):
else
[.captures[].string]
end;
def splits($re; $flags):
.[foreach (match($re; $flags + "g"), null) as {$offset, $length}
(null; {start: .next, end: $offset, next: $offset + $length})];
def splits($re): splits($re; null);
def splits($re; $flags): split($re; $flags)[];
def split($re; $flags): [splits($re; $flags)];
def sub($re; str): sub($re; str; null);
def sub($re; str; $flags):
. as $str |
def _sub:
if .matches == [] then
$str[:.offset] + .string
else
.matches[-1] as $r |
{
string: ($r | _capture | str) + $str[$r.offset+$r.length:.offset] + .string,
offset: $r.offset,
matches: .matches[:-1],
} |
_sub
end;
{ string: "", matches: [match($re; $flags)] } | _sub;
reduce match($re; $flags) as {$offset, $length, $captures}
({s: ., r: []};
reduce ($captures | _captures | str) as $s
(.i = 0; .r[.i] += .s[.next:$offset] + $s | .i += 1) |
.next = $offset + $length) | .r[] + .s[.next:] // .s;
def gsub($re; str): sub($re; str; "g");
def gsub($re; str; $flags): sub($re; str; $flags + "g");
+5 -1
View File
@@ -15,7 +15,7 @@ func Compare(l, r any) int {
cmp.Compare,
func(l, r float64) int {
switch {
case l < r || math.IsNaN(l):
case lt(l, r):
return -1
case l == r:
return 0
@@ -51,6 +51,10 @@ func Compare(l, r any) int {
)
}
func lt(l, r float64) bool {
return l < r || math.IsNaN(l)
}
func typeIndex(v any) int {
switch v := v.(type) {
default:
+13
View File
@@ -7,6 +7,7 @@ import (
"sort"
"strconv"
"strings"
"sync"
)
type compiler struct {
@@ -20,6 +21,7 @@ type compiler struct {
builtinScope *scopeinfo
scopes []*scopeinfo
scopecnt int
regexpCache sync.Map
}
// Code is a compiled jq query.
@@ -996,6 +998,13 @@ func (c *compiler) compileFunc(e *Func) error {
c.append(&code{op: opbacktrack})
setfork()
return nil
case "_match":
return c.compileCallInternal(
[3]any{c.funcMatch, len(e.Args), e.Name},
e.Args,
true,
-1,
)
default:
return c.compileCall(e.Name, e.Args)
}
@@ -1279,6 +1288,10 @@ func listModuleDeps(q *Query) []any {
return deps
}
func (c *compiler) funcMatch(v any, args []any) any {
return funcMatch(v, args[0], args[1], args[2], &c.regexpCache)
}
func (c *compiler) compileObject(e *Object) error {
c.appendCodeInfo(e)
if len(e.KeyVals) == 0 {
-8
View File
@@ -221,14 +221,6 @@ func (err *flattenDepthError) Error() string {
return "flatten depth should not be negative: " + Preview(err.v)
}
type joinTypeError struct {
v any
}
func (err *joinTypeError) Error() string {
return "join cannot be applied to an array including: " + typeErrorPreview(err.v)
}
type timeArrayError struct{}
func (*timeArrayError) Error() string {
+129 -158
View File
@@ -6,15 +6,18 @@ import (
"errors"
"fmt"
"io"
"iter"
"maps"
"math"
"math/big"
"net/url"
"reflect"
"regexp"
"slices"
"sort"
"strconv"
"strings"
"sync"
"time"
"unicode"
"unicode/utf8"
@@ -58,8 +61,6 @@ func init() {
"utf8bytelength": argFunc0(funcUtf8ByteLength),
"keys": argFunc0(funcKeys),
"has": argFunc1(funcHas),
"to_entries": argFunc0(funcToEntries),
"from_entries": argFunc0(funcFromEntries),
"add": argFunc0(funcAdd),
"toboolean": argFunc0(funcToBoolean),
"tonumber": argFunc0(funcToNumber),
@@ -67,6 +68,7 @@ func init() {
"type": argFunc0(funcType),
"reverse": argFunc0(funcReverse),
"contains": argFunc1(funcContains),
"inside": argFunc1(funcInside),
"indices": argFunc1(funcIndices),
"index": argFunc1(funcIndex),
"rindex": argFunc1(funcRindex),
@@ -80,7 +82,8 @@ func init() {
"trim": argFunc0(funcTrim),
"explode": argFunc0(funcExplode),
"implode": argFunc0(funcImplode),
"split": {argcount1 | argcount2, false, funcSplit},
"split": argFunc1(funcSplit),
"join": argFunc1(funcJoin),
"ascii_downcase": argFunc0(funcASCIIDowncase),
"ascii_upcase": argFunc0(funcASCIIUpcase),
"tojson": argFunc0(funcToJSON),
@@ -121,7 +124,6 @@ func init() {
"_group_by": argFunc1(funcGroupBy),
"unique": argFunc0(funcUnique),
"_unique_by": argFunc1(funcUniqueBy),
"join": argFunc1(funcJoin),
"sin": mathFunc("sin", math.Sin),
"cos": mathFunc("cos", math.Cos),
"tan": mathFunc("tan", math.Tan),
@@ -168,8 +170,8 @@ func init() {
"copysign": mathFunc2("copysign", math.Copysign),
"drem": mathFunc2("drem", funcDrem),
"fdim": mathFunc2("fdim", math.Dim),
"fmax": mathFunc2("fmax", math.Max),
"fmin": mathFunc2("fmin", math.Min),
"fmax": mathFunc2("fmax", funcFmax),
"fmin": mathFunc2("fmin", funcFmin),
"fmod": mathFunc2("fmod", math.Mod),
"hypot": mathFunc2("hypot", math.Hypot),
"jn": mathFunc2("jn", funcJn),
@@ -200,8 +202,8 @@ func init() {
"strflocaltime": argFunc1(funcStrflocaltime),
"strptime": argFunc1(funcStrptime),
"now": argFunc0(funcNow),
"_match": argFunc3(funcMatch),
"_capture": argFunc0(funcCapture),
"_match": argFunc3(nil),
"_captures": argFunc0(funcCaptures),
"error": {argcount0 | argcount1, false, funcError},
"halt": argFunc0(funcHalt),
"halt_error": {argcount0 | argcount1, false, funcHaltError},
@@ -288,7 +290,7 @@ func funcAbs(v any) any {
if v >= 0 {
return v
}
return -v
return negate(v)
case float64:
return math.Abs(v)
case *big.Int:
@@ -314,7 +316,7 @@ func funcLength(v any) any {
if v >= 0 {
return v
}
return -v
return negate(v)
case float64:
return math.Abs(v)
case *big.Int:
@@ -408,70 +410,17 @@ func funcHas(v, x any) any {
return &func1TypeError{"has", v, x}
}
func funcToEntries(v any) any {
switch v := v.(type) {
case []any:
w := make([]any, len(v))
for i, x := range v {
w[i] = map[string]any{"key": i, "value": x}
}
return w
case map[string]any:
w := make([]any, len(v))
for i, k := range keys(v) {
w[i] = map[string]any{"key": k, "value": v[k]}
}
return w
default:
return &func0TypeError{"to_entries", v}
}
}
func funcFromEntries(v any) any {
vs, ok := v.([]any)
if !ok {
return &func0TypeError{"from_entries", v}
}
w := make(map[string]any, len(vs))
for _, v := range vs {
switch v := v.(type) {
case map[string]any:
var (
key string
value any
ok bool
)
for _, k := range [4]string{"key", "Key", "name", "Name"} {
if k := v[k]; k != nil && k != false {
if key, ok = k.(string); !ok {
return &func0WrapError{"from_entries", vs, &objectKeyNotStringError{k}}
}
break
}
}
if !ok {
return &func0WrapError{"from_entries", vs, &objectKeyNotStringError{nil}}
}
for _, k := range [2]string{"value", "Value"} {
if value, ok = v[k]; ok {
break
}
}
w[key] = value
default:
return &func0TypeError{"from_entries", v}
}
}
return w
}
func funcAdd(v any) any {
vs, ok := values(v)
if !ok {
return &func0TypeError{"add", v}
}
v = nil
for _, x := range vs {
return add(slices.Values(vs))
}
func add(xs iter.Seq[any]) any {
var v any
for x := range xs {
switch x := x.(type) {
case nil:
continue
@@ -618,6 +567,10 @@ func funcContains(v, x any) any {
)
}
func funcInside(v, x any) any {
return funcContains(x, v)
}
func funcIndices(v, x any) any {
return indexFunc("indices", v, x, indices)
}
@@ -807,33 +760,16 @@ func funcImplode(v any) any {
return sb.String()
}
func funcSplit(v any, args []any) any {
func funcSplit(v, x any) any {
s, ok := v.(string)
if !ok {
return &func0TypeError{"split", v}
}
x, ok := args[0].(string)
t, ok := x.(string)
if !ok {
return &func0TypeError{"split", x}
}
var ss []string
if len(args) == 1 {
ss = strings.Split(s, x)
} else {
var flags string
if args[1] != nil {
v, ok := args[1].(string)
if !ok {
return &func0TypeError{"split", args[1]}
}
flags = v
}
r, err := compileRegexp(x, flags)
if err != nil {
return err
}
ss = r.Split(s, -1)
}
ss := strings.Split(s, t)
xs := make([]any, len(ss))
for i, s := range ss {
xs[i] = s
@@ -841,6 +777,34 @@ func funcSplit(v any, args []any) any {
return xs
}
func funcJoin(v, x any) any {
vs, ok := values(v)
if !ok {
return &func1TypeError{"join", v, x}
}
if len(vs) == 0 {
return ""
}
return add(func(yield func(any) bool) {
for i, v := range vs {
s := x
if i == 0 {
s = ""
}
if !yield(s) {
return
}
switch w := v.(type) {
case bool, int, float64, *big.Int, json.Number:
v = jsonMarshal(w)
}
if !yield(v) {
return
}
}
})
}
func funcASCIIDowncase(v any) any {
s, ok := v.(string)
if !ok {
@@ -1203,7 +1167,7 @@ func funcFlatten(v any, args []any) any {
if !ok {
return &func0TypeError{"flatten", args[0]}
}
if depth < 0 {
if lt(depth, 0) {
return &flattenDepthError{depth}
}
}
@@ -1393,44 +1357,9 @@ func uniqueBy(name string, v, x any) any {
return rs
}
func funcJoin(v, x any) any {
vs, ok := values(v)
if !ok {
return &func1TypeError{"join", v, x}
}
if len(vs) == 0 {
return ""
}
sep, ok := x.(string)
if len(vs) > 1 && !ok {
return &func1TypeError{"join", v, x}
}
ss := make([]string, len(vs))
for i, v := range vs {
switch v := v.(type) {
case nil:
case string:
ss[i] = v
case bool:
if v {
ss[i] = "true"
} else {
ss[i] = "false"
}
case int, float64, *big.Int, json.Number:
ss[i] = jsonMarshal(v)
default:
return &joinTypeError{v}
}
}
return strings.Join(ss, sep)
}
func funcSignificand(v float64) float64 {
if math.IsNaN(v) || math.IsInf(v, 0) || v == 0.0 {
return v
}
return math.Float64frombits((math.Float64bits(v) & 0x800fffffffffffff) | 0x3ff0000000000000)
frac, _ := math.Frexp(v)
return frac * 2
}
func funcExp10(v float64) float64 {
@@ -1451,6 +1380,9 @@ func funcModf(v any) any {
if !ok {
return &func0TypeError{"modf", v}
}
if math.IsInf(x, 0) {
return []any{math.Copysign(0, x), x}
}
i, f := math.Modf(x)
return []any{f, i}
}
@@ -1468,6 +1400,26 @@ func funcDrem(l, r float64) float64 {
return x
}
func funcFmax(l, r float64) float64 {
if math.IsNaN(l) {
return r
}
if math.IsNaN(r) {
return l
}
return max(l, r)
}
func funcFmin(l, r float64) float64 {
if math.IsNaN(l) {
return r
}
if math.IsNaN(r) {
return l
}
return min(l, r)
}
func funcJn(l, r float64) float64 {
return math.Jn(int(l), r)
}
@@ -1511,7 +1463,7 @@ func funcIsnan(v any) any {
func funcIsnormal(v any) any {
if v, ok := toFloat(v); ok {
e := math.Float64bits(v) & 0x7ff0000000000000 >> 52
e := (math.Float64bits(v) & 0x7ff0000000000000) >> 52
return 0 < e && e < 0x7ff
}
return false
@@ -1658,6 +1610,9 @@ func update(v any, path []any, n any, a allocator) (any, error) {
func updateObject(v map[string]any, k string, path []any, n any, a allocator) (any, error) {
x, ok := v[k]
if !ok && n == struct{}{} {
if v == nil {
return nil, nil
}
return v, nil
}
u, err := update(x, path, n, a)
@@ -1678,6 +1633,9 @@ func updateArrayIndex(v []any, i int, path []any, n any, a allocator) (any, erro
var x any
if j := clampIndex(i, -1, len(v)); j < 0 {
if n == struct{}{} {
if v == nil {
return nil, nil
}
return v, nil
}
return nil, &arrayIndexNegativeError{i}
@@ -1686,6 +1644,9 @@ func updateArrayIndex(v []any, i int, path []any, n any, a allocator) (any, erro
x = v[i]
} else {
if n == struct{}{} {
if v == nil {
return nil, nil
}
return v, nil
}
if i >= 0x20000000 {
@@ -1726,15 +1687,26 @@ func updateArraySlice(v []any, m map[string]any, path []any, n any, a allocator)
return nil, &expectedStartEndError{m}
}
var start, end int
if i, ok := toInt(s); ok {
start = clampIndex(i, 0, len(v))
if s != nil {
if i, ok := toInt(s); ok {
start = clampIndex(i, 0, len(v))
} else {
return nil, &arrayIndexNotNumberError{s}
}
}
if i, ok := toInt(e); ok {
end = clampIndex(i, start, len(v))
if e != nil {
if i, ok := toIntCeil(e); ok {
end = clampIndex(i, start, len(v))
} else {
return nil, &arrayIndexNotNumberError{e}
}
} else {
end = len(v)
}
if start == end && n == struct{}{} {
if v == nil {
return nil, nil
}
return v, nil
}
u, err := update(v[start:end], path, n, a)
@@ -1962,8 +1934,7 @@ func funcStrptime(v, x any) any {
if err != nil {
return &func1WrapError{"strptime", v, x, err}
}
var u time.Time
if t == u {
if t.Equal(time.Time{}) {
return &func1TypeError{"strptime", v, x}
}
return epochToArray(timeToEpoch(t), time.UTC)
@@ -2001,18 +1972,20 @@ func funcNow(any) any {
return timeToEpoch(time.Now())
}
func funcMatch(v, re, fs, testing any) any {
name := "match"
func funcMatch(v, re, fs, testing any, cache *sync.Map) any {
var name string
if testing == true {
name = "test"
} else {
name = "match"
}
var flags string
if fs != nil {
v, ok := fs.(string)
var ok bool
flags, ok = fs.(string)
if !ok {
return &func2TypeError{name, v, re, fs}
}
flags = v
}
s, ok := v.(string)
if !ok {
@@ -2022,22 +1995,20 @@ func funcMatch(v, re, fs, testing any) any {
if !ok {
return &func2TypeError{name, v, re, fs}
}
r, err := compileRegexp(restr, flags)
r, err := compileRegexp(restr, flags, cache)
if err != nil {
return err
}
var xs [][]int
if strings.ContainsRune(flags, 'g') && testing != true {
xs = r.FindAllStringSubmatchIndex(s, -1)
} else {
got := r.FindStringSubmatchIndex(s)
if testing == true {
return got != nil
}
if got != nil {
xs = [][]int{got}
}
if testing == true {
return r.MatchString(s)
}
var n int
if strings.ContainsRune(flags, 'g') {
n = -1
} else {
n = 1
}
xs := r.FindAllStringSubmatchIndex(s, n)
res, names := make([]any, len(xs)), r.SubexpNames()
for i, x := range xs {
captures := make([]any, (len(x)-2)/2)
@@ -2072,7 +2043,11 @@ func funcMatch(v, re, fs, testing any) any {
return res
}
func compileRegexp(re, flags string) (*regexp.Regexp, error) {
func compileRegexp(re, flags string, cache *sync.Map) (*regexp.Regexp, error) {
key := [2]string{re, flags}
if r, ok := cache.Load(key); ok {
return r.(*regexp.Regexp), nil
}
if strings.IndexFunc(flags, func(r rune) bool {
return r != 'g' && r != 'i' && r != 'm'
}) >= 0 {
@@ -2088,15 +2063,11 @@ func compileRegexp(re, flags string) (*regexp.Regexp, error) {
if err != nil {
return nil, fmt.Errorf("invalid regular expression %q: %s", re, err)
}
cache.Store(key, r)
return r, nil
}
func funcCapture(v any) any {
vs, ok := v.(map[string]any)
if !ok {
return &expectedObjectError{v}
}
v = vs["captures"]
func funcCaptures(v any) any {
captures, ok := v.([]any)
if !ok {
return &expectedArrayError{v}
@@ -2165,7 +2136,7 @@ func toIntCeil(x any) (int, bool) {
}
func floatToInt(x float64) int {
if math.MinInt <= x && x <= math.MaxInt {
if math.MinInt <= x && x < math.MaxInt {
return int(x)
}
if x > 0 {
+1 -1
View File
@@ -4,5 +4,5 @@ go 1.24.0
require (
github.com/itchyny/astgen-go v0.0.0-20250520171007-4331c963041e // indirect
github.com/itchyny/timefmt-go v0.1.7 // indirect
github.com/itchyny/timefmt-go v0.1.8 // indirect
)
+2 -2
View File
@@ -1,4 +1,4 @@
github.com/itchyny/astgen-go v0.0.0-20250520171007-4331c963041e h1:Pu7Ev38D+75aWdsHzULyVZfMP3WrgIHNUaAUH81CTqE=
github.com/itchyny/astgen-go v0.0.0-20250520171007-4331c963041e/go.mod h1:8EXgnGrgW1p0qVnOF1A/4ORQEUAi8TvxQdIR+zJfKxM=
github.com/itchyny/timefmt-go v0.1.7 h1:xyftit9Tbw+Dc/huSSPJaEmX1TVL8lw5vxjJLK4GMMA=
github.com/itchyny/timefmt-go v0.1.7/go.mod h1:5E46Q+zj7vbTgWY8o5YkMeYb4I6GeWLFnetPy5oBrAI=
github.com/itchyny/timefmt-go v0.1.8 h1:1YEo1JvfXeAHKdjelbYr/uCuhkybaHCeTkH8Bo791OI=
github.com/itchyny/timefmt-go v0.1.8/go.mod h1:5E46Q+zj7vbTgWY8o5YkMeYb4I6GeWLFnetPy5oBrAI=
+33 -17
View File
@@ -279,10 +279,17 @@ func funcOpPlus(v any) any {
}
}
func negate(v int) any {
if v == math.MinInt {
return new(big.Int).Neg(big.NewInt(int64(v)))
}
return -v
}
func funcOpNegate(v any) any {
switch v := v.(type) {
case int:
return -v
return negate(v)
case float64:
return -v
case *big.Int:
@@ -378,6 +385,9 @@ func funcOpSub(_, l, r any) any {
func funcOpMul(_, l, r any) any {
return binopTypeSwitch(l, r,
func(l, r int) any {
if r == -1 {
return negate(l)
}
if v := l * r; r == 0 || v/r == l {
return v
}
@@ -422,28 +432,30 @@ func deepMergeObjects(l, r map[string]any) any {
}
func repeatString(s string, n float64) any {
if n < 0.0 || math.IsNaN(n) {
if lt(n, 0) {
return nil
}
if s == "" {
return ""
}
if n >= float64(math.MaxInt32/len(s)) {
c := int(min(n, math.MaxInt32))
if uint64(len(s))*uint64(c) >= math.MaxInt32 {
return &repeatStringTooLargeError{s, n}
}
return strings.Repeat(s, int(n))
return strings.Repeat(s, c)
}
func funcOpDiv(_, l, r any) any {
return binopTypeSwitch(l, r,
func(l, r int) any {
if r == 0 {
switch r {
case 0:
return &zeroDivisionError{l, r}
case -1:
return negate(l)
default:
if l%r == 0 {
return l / r
}
return float64(l) / float64(r)
}
if l%r == 0 {
return l / r
}
return float64(l) / float64(r)
},
func(l, r float64) any {
if r == 0.0 {
@@ -481,19 +493,23 @@ func funcOpDiv(_, l, r any) any {
func funcOpMod(_, l, r any) any {
return binopTypeSwitch(l, r,
func(l, r int) any {
if r == 0 {
switch r {
case 0:
return &zeroModuloError{l, r}
case -1:
return 0
default:
return l % r
}
return l % r
},
func(l, r float64) any {
if math.IsNaN(l) || math.IsNaN(r) {
return math.NaN()
}
ri := floatToInt(r)
if ri == 0 {
return &zeroModuloError{l, r}
}
if math.IsNaN(l) || math.IsNaN(r) {
return math.NaN()
}
return floatToInt(l) % ri
},
func(l, r *big.Int) any {
+114 -119
View File
@@ -154,140 +154,135 @@ var yyExca = [...]int16{
-1, 1,
1, -1,
-2, 0,
-1, 138,
-1, 139,
5, 0,
-2, 27,
-1, 141,
-1, 142,
7, 0,
-2, 30,
-1, 198,
-1, 194,
59, 114,
-2, 49,
}
const yyPrivate = 57344
const yyLast = 781
const yyLast = 733
var yyAct = [...]int16{
78, 133, 174, 185, 103, 10, 210, 194, 175, 130,
108, 102, 5, 134, 48, 228, 158, 50, 73, 74,
123, 6, 32, 93, 73, 74, 243, 14, 110, 246,
204, 227, 157, 203, 115, 279, 122, 264, 278, 120,
135, 98, 97, 245, 148, 149, 136, 73, 74, 104,
179, 180, 181, 226, 114, 242, 73, 74, 179, 180,
181, 281, 116, 117, 118, 73, 74, 225, 177, 253,
178, 255, 73, 74, 127, 128, 177, 99, 178, 234,
237, 207, 233, 240, 206, 131, 230, 162, 229, 160,
182, 73, 74, 73, 74, 219, 6, 153, 182, 159,
156, 126, 125, 247, 6, 277, 161, 252, 137, 138,
139, 140, 141, 142, 143, 144, 145, 146, 151, 150,
124, 73, 74, 188, 45, 73, 74, 239, 183, 184,
73, 74, 73, 74, 50, 244, 147, 192, 173, 213,
91, 195, 92, 272, 168, 73, 74, 76, 199, 200,
73, 74, 75, 202, 119, 205, 270, 90, 84, 196,
83, 269, 95, 94, 214, 96, 165, 212, 216, 217,
209, 190, 218, 80, 81, 129, 84, 82, 83, 43,
44, 104, 266, 167, 25, 221, 3, 211, 211, 24,
223, 220, 224, 215, 131, 222, 73, 74, 231, 96,
85, 86, 87, 88, 89, 236, 176, 232, 238, 47,
80, 81, 189, 84, 82, 83, 43, 44, 85, 86,
87, 88, 89, 85, 86, 87, 88, 89, 248, 73,
74, 250, 251, 195, 186, 187, 249, 101, 254, 87,
88, 89, 164, 261, 262, 258, 256, 257, 241, 263,
273, 196, 163, 155, 265, 85, 86, 87, 88, 89,
267, 268, 84, 82, 83, 121, 271, 211, 211, 193,
274, 275, 84, 191, 276, 132, 73, 74, 73, 74,
79, 129, 174, 195, 103, 10, 210, 190, 175, 227,
135, 32, 5, 158, 102, 108, 48, 93, 6, 123,
243, 234, 50, 246, 233, 226, 225, 242, 110, 157,
98, 264, 15, 99, 115, 122, 97, 245, 104, 120,
224, 148, 149, 114, 37, 21, 22, 23, 33, 207,
74, 253, 206, 34, 130, 35, 36, 39, 41, 40,
42, 19, 20, 28, 31, 43, 44, 116, 117, 118,
240, 229, 228, 92, 127, 160, 133, 29, 30, 159,
126, 131, 18, 73, 75, 27, 136, 132, 38, 43,
44, 26, 125, 95, 94, 204, 96, 153, 203, 124,
156, 252, 198, 45, 239, 186, 161, 151, 74, 96,
88, 89, 90, 150, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 137, 179, 180, 181, 183, 184,
43, 44, 77, 188, 179, 180, 181, 191, 199, 50,
173, 73, 75, 177, 192, 178, 185, 76, 74, 200,
74, 119, 177, 202, 178, 205, 74, 80, 86, 87,
88, 89, 90, 266, 214, 182, 91, 212, 216, 217,
104, 6, 218, 209, 182, 165, 74, 74, 74, 219,
6, 73, 75, 73, 75, 221, 222, 74, 223, 73,
75, 134, 211, 211, 230, 74, 255, 281, 215, 3,
279, 196, 197, 278, 136, 236, 231, 74, 238, 73,
75, 73, 75, 170, 273, 171, 169, 74, 25, 74,
73, 75, 74, 24, 237, 220, 162, 247, 73, 75,
249, 250, 191, 176, 277, 248, 47, 17, 254, 192,
73, 75, 251, 261, 262, 9, 256, 257, 101, 263,
73, 75, 73, 75, 265, 73, 75, 259, 260, 78,
267, 268, 164, 272, 244, 258, 271, 112, 113, 213,
274, 275, 211, 211, 276, 74, 241, 155, 121, 74,
280, 52, 53, 54, 55, 56, 57, 58, 59, 60,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
71, 72, 106, 107, 85, 86, 87, 88, 89, 9,
43, 44, 259, 260, 85, 86, 87, 88, 89, 170,
17, 171, 169, 77, 16, 201, 15, 37, 21, 22,
23, 33, 7, 105, 8, 4, 34, 208, 35, 36,
71, 72, 106, 107, 189, 187, 128, 201, 73, 75,
43, 44, 73, 75, 7, 8, 4, 2, 74, 1,
111, 0, 168, 270, 16, 13, 14, 37, 21, 22,
23, 33, 0, 105, 13, 0, 34, 208, 35, 36,
39, 41, 40, 42, 19, 20, 28, 31, 43, 44,
112, 113, 2, 80, 81, 1, 84, 82, 83, 0,
29, 30, 0, 166, 0, 18, 0, 0, 27, 0,
0, 73, 75, 81, 82, 0, 85, 83, 84, 0,
29, 30, 269, 167, 85, 18, 84, 0, 27, 0,
154, 38, 0, 152, 26, 52, 53, 54, 55, 56,
57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
67, 68, 69, 70, 71, 72, 106, 107, 85, 86,
87, 88, 89, 0, 43, 44, 0, 0, 0, 0,
0, 0, 0, 0, 111, 0, 11, 12, 16, 13,
15, 37, 21, 22, 23, 33, 0, 105, 13, 0,
67, 68, 69, 70, 71, 72, 106, 107, 86, 87,
88, 89, 90, 0, 43, 44, 86, 87, 88, 89,
90, 0, 74, 0, 0, 0, 11, 12, 16, 0,
14, 37, 21, 22, 23, 33, 0, 105, 0, 0,
34, 100, 35, 36, 39, 41, 40, 42, 19, 20,
28, 31, 43, 44, 0, 0, 0, 0, 80, 81,
0, 84, 82, 83, 29, 30, 0, 0, 79, 18,
0, 0, 27, 0, 0, 38, 0, 16, 26, 15,
28, 31, 43, 44, 0, 73, 75, 0, 81, 82,
0, 85, 83, 84, 29, 30, 232, 0, 166, 18,
0, 0, 27, 0, 0, 38, 0, 16, 26, 14,
37, 21, 22, 23, 33, 0, 0, 0, 0, 34,
0, 35, 36, 39, 41, 40, 42, 19, 20, 28,
31, 43, 44, 85, 86, 87, 88, 89, 0, 0,
0, 0, 0, 29, 30, 0, 0, 0, 18, 0,
31, 43, 44, 86, 87, 88, 89, 90, 0, 0,
0, 0, 0, 29, 30, 85, 83, 84, 18, 0,
0, 27, 0, 0, 38, 0, 235, 26, 16, 0,
15, 37, 21, 22, 23, 33, 0, 0, 0, 0,
14, 37, 21, 22, 23, 33, 0, 0, 0, 0,
34, 0, 35, 36, 39, 41, 40, 42, 19, 20,
28, 31, 43, 44, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 29, 30, 0, 0, 0, 18,
28, 31, 43, 44, 0, 74, 0, 86, 87, 88,
89, 90, 163, 0, 29, 30, 0, 0, 0, 18,
0, 0, 27, 0, 0, 38, 0, 109, 26, 16,
0, 15, 37, 21, 22, 23, 33, 0, 0, 0,
0, 14, 37, 21, 22, 23, 33, 0, 73, 75,
0, 34, 0, 35, 36, 39, 41, 40, 42, 19,
20, 28, 31, 43, 44, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 29, 30, 0, 0, 0,
20, 28, 31, 43, 44, 0, 0, 81, 82, 0,
85, 83, 84, 0, 0, 29, 30, 0, 0, 0,
18, 0, 0, 27, 0, 0, 38, 0, 0, 26,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
72, 49, 0, 0, 0, 0, 0, 0, 0, 51,
72, 49, 86, 87, 88, 89, 90, 0, 0, 51,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
72, 49, 0, 0, 0, 0, 172, 0, 0, 51,
37, 21, 22, 23, 33, 0, 0, 0, 0, 34,
0, 35, 36, 39, 41, 40, 42, 19, 20, 28,
31, 43, 44, 0, 0, 0, 46, 0, 0, 0,
0, 0, 0, 29, 30, 0, 0, 0, 18, 0,
0, 27, 0, 0, 38, 0, 0, 26, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 106,
198, 0, 0, 0, 0, 0, 0, 43, 44, 0,
72, 49, 85, 0, 0, 0, 172, 0, 0, 51,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
72, 106, 194, 0, 0, 0, 46, 0, 0, 43,
44, 0, 0, 0, 86, 87, 88, 89, 90, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
197,
0, 0, 193,
}
var yyPact = [...]int16{
176, -1000, -1000, -42, 405, 66, 642, -1000, -1000, -1000,
182, 115, 110, 556, 444, 127, 111, 108, 142, -1000,
-1000, -1000, -1000, -1000, 17, -1000, 367, 505, -1000, 664,
664, 179, -1000, 556, 664, 664, 664, 124, 556, -1000,
-1000, -1000, -1000, -1000, -1000, -1000, -1000, -28, -1000, 61,
43, 42, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000,
189, -1000, -1000, -45, 405, 45, 642, -1000, -1000, -1000,
94, 110, 95, 556, 127, 593, 137, 39, 52, -1000,
-1000, -1000, -1000, -1000, -27, -1000, 367, 505, -1000, 28,
28, 93, -1000, 556, 28, 28, 28, 121, 556, -1000,
-1000, -1000, -1000, -1000, -1000, -1000, -1000, -29, -1000, 40,
33, 21, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000,
-1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000,
-1000, -1000, -1000, 556, 556, 161, -42, -1000, 182, -17,
664, 664, 664, 664, 664, 664, 664, 664, 664, 664,
89, -15, -1000, -1000, -1000, 142, 311, -1000, -1000, 556,
-1000, -32, -1000, 40, 30, 556, -1000, -1000, -1000, -1000,
25, 556, 108, 108, -1000, 231, 140, 349, 169, -1000,
83, 282, -1000, 612, 41, 41, 41, 182, -1000, 205,
65, -1000, 165, -1000, -1000, -17, 720, 206, 255, 151,
265, 174, 188, 188, -1000, -1000, -1000, 556, 556, 205,
-1000, -1000, -1000, -29, 556, 23, 182, -1000, 273, 664,
664, 78, -1000, 556, -1000, 664, -17, -17, -1000, -1000,
-1000, -1000, -1000, 556, 24, 556, 177, -45, -1000, 94,
77, 28, 28, 28, 28, 28, 28, 28, 28, 28,
28, -18, -1000, -1000, -1000, 52, 311, -1000, -1000, 556,
-1000, -35, -1000, 20, 16, 556, -1000, -1000, -1000, -1000,
164, 556, 39, 39, -1000, 531, 149, 444, 349, -1000,
261, 176, -1000, 612, 108, 108, 108, 94, 99, -1000,
-1000, 24, 672, 163, 172, 44, -1000, 556, 593, 498,
357, 665, 109, 59, 59, -1000, -1000, -1000, 556, 172,
-1000, -1000, -1000, 36, 556, -9, 94, -1000, 273, 28,
28, 208, -1000, 556, -1000, 28, 24, 24, -1000, -1000,
-1000, 556, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000,
-1000, -1000, 33, -1000, -1000, -42, -1000, -1000, -1000, 556,
-17, 5, -1000, -33, -1000, 29, 27, 556, -1000, 182,
149, 21, -1000, -1000, 454, 18, -1000, 556, -1000, -1000,
80, 206, 80, 24, 182, -1000, -5, -34, 74, -1000,
-19, -1000, 45, 182, -1000, -1000, -17, -1000, 720, -17,
-17, 46, -1000, 10, 205, -1000, 9, -1000, 182, 664,
664, 290, 556, 556, -1000, -1000, 41, -1000, -1000, -1000,
-1000, -1000, -22, 556, -1000, -1000, 80, 80, 158, 556,
556, 103, 98, -1000, -17, 85, -1000, 229, 182, 556,
556, -1000, -1000, 556, 44, -23, 182, -1000, -1000, 556,
0, -1000,
-1000, -1000, 117, -1000, -1000, 556, 24, -22, -1000, -39,
-1000, 13, 12, 556, -1000, -45, -1000, -1000, -1000, 94,
398, -37, -1000, -1000, 454, 162, -1000, 556, -1000, -1000,
57, 593, 57, 11, 94, -1000, -33, -40, 203, -1000,
-25, -1000, 94, -1000, -1000, 24, -1000, 672, 24, 24,
181, 43, -1000, -8, 172, -1000, 134, -1000, 94, 28,
28, 235, 556, 556, -1000, -1000, 108, -1000, -1000, -1000,
-1000, -28, -1000, 556, -1000, -1000, 57, 57, 139, 556,
556, 304, 265, -1000, 24, 205, -1000, 193, 94, 556,
556, -1000, -1000, 556, 173, 142, 94, -1000, -1000, 556,
136, -1000,
}
var yyPgo = [...]int16{
0, 355, 352, 335, 334, 9, 332, 309, 414, 325,
0, 275, 1, 273, 269, 7, 27, 320, 22, 265,
23, 253, 248, 245, 242, 237, 11, 6, 2, 8,
209, 14, 206, 191, 3, 189, 184, 10, 4,
0, 319, 317, 316, 315, 10, 314, 245, 320, 307,
0, 306, 1, 305, 304, 7, 32, 237, 11, 278,
17, 277, 276, 265, 262, 248, 14, 6, 2, 8,
236, 16, 233, 225, 3, 223, 218, 15, 4,
}
var yyR1 = [...]int8{
@@ -330,31 +325,31 @@ var yyR2 = [...]int8{
var yyChk = [...]int16{
-1000, -1, -2, 10, -3, -29, 63, -6, -4, -7,
-10, 11, 12, -8, -16, 15, 13, -17, 54, 33,
-10, 11, 12, -8, 15, -16, 13, -17, 54, 33,
34, 17, 18, 19, -35, -36, 63, 57, 35, 49,
50, 36, -18, 20, 25, 27, 28, 16, 60, 29,
31, 30, 32, 37, 38, 58, 64, -30, -31, 29,
-37, 37, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 47, 48, 37, 37, -7, -10, 14,
4, 5, 8, 9, 7, 49, 50, 51, 52, 53,
30, 29, 34, -20, 55, 54, 57, -20, -18, 60,
26, 27, 28, 47, 14, 48, 37, 37, -7, -10,
30, 4, 5, 8, 9, 7, 49, 50, 51, 52,
53, 29, 34, -20, 55, 54, 57, -20, -18, 60,
64, -25, -26, -38, -18, 60, 29, 30, -37, 62,
-10, -8, -17, -17, -18, -10, -16, -16, -16, 30,
-10, -19, 64, 48, 59, 59, 59, -10, -10, 14,
-5, -29, -11, -12, 30, 57, 63, -16, -16, -16,
-16, -16, -16, -16, -16, -16, -16, 47, 59, 60,
-10, -19, 64, 48, 59, 59, 59, -10, -11, -12,
30, 57, 63, -10, 14, -5, -29, 47, -16, -16,
-16, -16, -16, -16, -16, -16, -16, -16, 59, 60,
-20, -18, 62, -10, 59, -21, -10, 64, 48, 59,
59, -10, 62, 21, -24, 26, 14, 14, 61, 40,
37, 39, 64, -31, -28, -29, -32, 35, 37, 17,
18, 19, 57, -28, -28, -34, 29, 30, 58, 47,
6, -13, -12, -14, -15, -38, -18, 60, 30, -10,
18, 19, 57, -28, -28, 47, 6, -13, -12, -14,
-15, -38, -18, 60, 30, -34, 29, 30, 58, -10,
-10, -9, -34, 62, 59, -10, 61, 58, 64, -26,
-27, -16, -27, 61, -10, -16, -12, -12, -10, 62,
-33, -28, -5, -10, -12, 62, 48, 64, 48, 59,
59, -10, 58, 61, 58, 62, -10, 62, -10, 47,
59, -22, 60, 60, 61, 62, 48, 58, -12, -15,
-12, -12, 61, 59, -34, 62, -27, -27, -23, 22,
-33, -28, -10, -12, 62, 48, 64, 48, 59, 59,
-10, -5, 58, 61, 58, 62, -10, 62, -10, 47,
59, -22, 60, 60, 61, 62, 48, -12, -15, -12,
-12, 61, 58, 59, -34, 62, -27, -27, -23, 22,
23, -10, -10, -28, 59, -10, 24, -10, -10, 58,
58, -12, 58, 21, -10, -10, -10, 61, 61, 58,
-10, 61,
@@ -362,31 +357,31 @@ var yyChk = [...]int16{
var yyDef = [...]int16{
2, -2, 4, 0, 12, 0, 0, 1, 5, 10,
11, 0, 0, 12, 25, 0, 0, 36, 50, 51,
11, 0, 0, 12, 0, 25, 0, 36, 50, 51,
52, 55, 56, 57, 58, 60, 0, 0, 66, 0,
0, 69, 71, 0, 0, 0, 0, 0, 0, 89,
90, 91, 92, 84, 86, 3, 125, 0, 128, 0,
0, 0, 137, 138, 139, 140, 141, 142, 143, 144,
145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
155, 156, 157, 0, 0, 0, 8, 13, 20, 0,
155, 156, 157, 0, 0, 0, 0, 8, 13, 20,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 79, 80, 81, 0, 0, 53, 54, 0,
61, 0, 106, 111, 112, 0, 113, 114, 115, 64,
0, 0, 67, 68, 70, 0, 104, 0, 0, 77,
0, 0, 126, 0, 0, 0, 0, 21, 24, 0,
0, 9, 0, 37, 39, 0, 0, 26, -2, 28,
29, -2, 31, 32, 33, 34, 35, 0, 0, 0,
0, 0, 126, 0, 0, 0, 0, 21, 0, 37,
39, 0, 0, 24, 0, 0, 9, 0, 26, -2,
28, 29, -2, 31, 32, 33, 34, 35, 0, 0,
82, 83, 93, 0, 0, 0, 98, 62, 0, 0,
0, 0, 65, 0, 73, 0, 0, 0, 78, 85,
87, 0, 127, 129, 130, 118, 119, 120, 121, 122,
123, 124, 0, 131, 132, 8, 18, 19, 7, 0,
0, 0, 42, 0, 44, 0, 0, 0, -2, 23,
123, 124, 0, 131, 132, 0, 0, 0, 42, 0,
44, 0, 0, 0, -2, 8, 18, 19, 7, 23,
0, 0, 16, 94, 0, 0, 59, 0, 63, 107,
108, 117, 109, 0, 100, 105, 0, 0, 0, 133,
0, 135, 0, 22, 38, 40, 0, 41, 0, 0,
0, 135, 22, 38, 40, 0, 41, 0, 0, 0,
0, 0, 14, 0, 0, 95, 0, 96, 99, 0,
0, 102, 0, 0, 88, 134, 0, 6, 43, 45,
46, 47, 0, 0, 17, 97, 116, 110, 0, 0,
0, 102, 0, 0, 88, 134, 0, 43, 45, 46,
47, 0, 6, 0, 17, 97, 116, 110, 0, 0,
0, 0, 0, 136, 0, 0, 72, 0, 103, 0,
0, 48, 15, 0, 0, 0, 101, 74, 75, 0,
0, 76,
+8
View File
@@ -1,4 +1,12 @@
# Changelog
## [v0.1.8](https://github.com/itchyny/timefmt-go/compare/v0.1.7..v0.1.8) (2026-04-01)
* fix parsing negative year and Unix time (`%Y`, `%G`, `%s`)
* fix formatting negative year, century, Unix time (`%Y`, `%G`, `%C`, `%y`, `%g`, `%s`)
* fix `%g` parsing to use the same two-digit year threshold 69 as `%y`
* fix `%s` formatting and parsing on 32-bit platforms
* support parsing time zone offset with `%:::z`
* improve performance of parsing/formatting compound directives
## [v0.1.7](https://github.com/itchyny/timefmt-go/compare/v0.1.6..v0.1.7) (2025-10-01)
* refactor code using built-in `min` and `max` functions
+102 -83
View File
@@ -85,12 +85,20 @@ func AppendFormat(buf []byte, t time.Time, format string) []byte {
case 'Y':
buf = appendInt(buf, year, or(width, 4), padding)
case 'y':
buf = appendInt(buf, year%100, max(width, 2), padding)
buf = appendInt(buf, abs(year%100), max(width, 2), padding)
case 'C':
buf = appendInt(buf, year/100, max(width, 2), padding)
c := year / 100
z := year < 0 && c == 0
if z {
c = -1
}
buf = appendInt(buf, c, max(width, 2), padding)
if z {
buf[len(buf)-1] = '0'
}
case 'g':
year, _ := t.ISOWeek()
buf = appendInt(buf, year%100, max(width, 2), padding)
buf = appendInt(buf, abs(year%100), max(width, 2), padding)
case 'G':
year, _ := t.ISOWeek()
buf = appendInt(buf, year, or(width, 4), padding)
@@ -161,7 +169,7 @@ func AppendFormat(buf []byte, t time.Time, format string) []byte {
if padding < ^paddingMask {
padding = ' '
}
buf = appendInt(buf, int(t.Unix()), width, padding)
buf = appendInt64(buf, t.Unix(), width, padding)
case 'f':
buf = appendInt(buf, t.Nanosecond()/1000, or(width, 6), padding)
case 'Z', 'z':
@@ -237,13 +245,24 @@ func AppendFormat(buf []byte, t time.Time, format string) []byte {
buf = appendString(buf, "\n", width, padding, false, false)
case '%':
buf = appendString(buf, "%", width, padding, false, false)
case 'c':
pending, swap = "a b e H:M:S Y", false
case '+':
pending, swap = "a b e H:M:S Z Y", false
case 'v':
pending, swap = "e-b-Y", false
case 'r':
pending, swap = "I:M:S p", false
case 'F':
pending = "Y-m-d"
case 'D', 'x':
pending = "m/d/y"
case 'T', 'X':
pending = "H:M:S"
case 'R':
pending = "H:M"
default:
if pending == "" {
var ok bool
if pending, ok = compositions[b]; ok {
swap = false
break
}
buf = appendLast(buf, format[:i], width-1, padding)
}
buf = append(buf, b)
@@ -261,73 +280,79 @@ K:
return appendLast(buf, format, width, padding)
}
const smalls = "" +
"00010203040506070809" +
"10111213141516171819" +
"20212223242526272829" +
"30313233343536373839" +
"40414243444546474849" +
"50515253545556575859" +
"60616263646566676869" +
"70717273747576777879" +
"80818283848586878889" +
"90919293949596979899"
func appendInt(buf []byte, num, width int, padding byte) []byte {
var digits int
switch {
default:
fallthrough
case num < 0:
return appendInt64(buf, int64(num), width, padding)
case num < 100 && width == 2 && padding == '0':
return append(buf, smalls[num*2:num*2+2]...)
case num < 10:
digits = 1
case num < 100:
digits = 2
case num < 1000:
digits = 3
case num < 10000:
digits = 4
}
if padding != ^paddingMask {
padding &= paddingMask
switch width {
case 2:
if num < 10 {
buf = append(buf, padding)
goto L1
} else if num < 100 {
goto L2
} else if num < 1000 {
goto L3
} else if num < 10000 {
goto L4
}
case 4:
if num < 1000 {
buf = append(buf, padding)
if num < 100 {
buf = append(buf, padding)
if num < 10 {
buf = append(buf, padding)
goto L1
}
goto L2
}
goto L3
} else if num < 10000 {
goto L4
}
default:
i := len(buf)
for ; width > 1; width-- {
buf = append(buf, padding)
}
j := len(buf)
buf = strconv.AppendInt(buf, int64(num), 10)
if k := min(len(buf)-j-1, j-i); k > 0 {
copy(buf[j-k:], buf[j:])
buf = buf[:len(buf)-k]
}
return buf
for ; width > digits; width-- {
buf = append(buf, padding)
}
}
if num < 100 {
if num < 10 {
goto L1
}
goto L2
} else if num < 10000 {
if num < 1000 {
goto L3
}
goto L4
switch digits {
case 4:
buf = append(buf, byte(num/1000)|'0')
num %= 1000
fallthrough
case 3:
buf = append(buf, byte(num/100)|'0')
num %= 100
fallthrough
case 2:
buf = append(buf, byte(num/10)|'0')
num %= 10
fallthrough
default:
return append(buf, byte(num)|'0')
}
return strconv.AppendInt(buf, int64(num), 10)
L4:
buf = append(buf, byte(num/1000)|'0')
num %= 1000
L3:
buf = append(buf, byte(num/100)|'0')
num %= 100
L2:
buf = append(buf, byte(num/10)|'0')
num %= 10
L1:
return append(buf, byte(num)|'0')
}
func appendInt64(buf []byte, num int64, width int, padding byte) []byte {
if padding == ^paddingMask {
return strconv.AppendInt(buf, num, 10)
}
padding &= paddingMask
i := len(buf)
for ; width > 1; width-- {
buf = append(buf, padding)
}
j := len(buf)
buf = strconv.AppendInt(buf, num, 10)
if k := min(len(buf)-j-1, j-i); k > 0 {
copy(buf[j-k:], buf[j:])
buf = buf[:len(buf)-k]
if j -= k; buf[i] == '0' && buf[j] == '-' {
buf[i], buf[j] = '-', '0'
}
}
return buf
}
func appendString(buf []byte, str string, width int, padding byte, upper, swap bool) []byte {
@@ -377,6 +402,13 @@ func or(x, y int) int {
return y
}
func abs(x int) int {
if x >= 0 {
return x
}
return -x
}
const paddingMask byte = 0x7F
var longMonthNames = []string{
@@ -428,16 +460,3 @@ var shortWeekNames = []string{
"Fri",
"Sat",
}
var compositions = map[byte]string{
'c': "a b e H:M:S Y",
'+': "a b e H:M:S Z Y",
'F': "Y-m-d",
'D': "m/d/y",
'x': "m/d/y",
'v': "e-b-Y",
'T': "H:M:S",
'X': "H:M:S",
'r': "I:M:S p",
'R': "H:M",
}
+94 -55
View File
@@ -3,7 +3,6 @@ package timefmt
import (
"errors"
"fmt"
"math"
"time"
)
@@ -25,26 +24,33 @@ func parse(source, format string, loc, base *time.Location) (t time.Time, err er
err = fmt.Errorf("failed to parse %q with %q: %w", source, format, err)
}
}()
var j, week, weekday, yday, colons int
var j, week, weekday, yday, colons, sign int
century, weekstart := -1, time.Weekday(-1)
var pm, hasISOYear, hasZoneName, hasZoneOffset bool
var pending string
for i, l := 0, len(source); i < len(format); i++ {
if b := format[i]; b == '%' {
i++
if i == len(format) {
if i++; i == len(format) {
err = errors.New(`stray "%"`)
return
}
b = format[i]
L:
switch b {
case 'G':
hasISOYear = true
fallthrough
case 'Y':
if year, j, err = parseNumber(source, j, 4, 0, 9999, 'Y'); err != nil {
sign, j = parseSign(source, j, l)
if year, j, err = parseInt(source, j, 4, 0, 9999, b); err != nil {
return
}
year *= sign
case 'g':
hasISOYear = true
fallthrough
case 'y':
if year, j, err = parseNumber(source, j, 2, 0, 99, 'y'); err != nil {
if year, j, err = parseInt(source, j, 2, 0, 99, b); err != nil {
return
}
if year < 69 {
@@ -53,22 +59,16 @@ func parse(source, format string, loc, base *time.Location) (t time.Time, err er
year += 1900
}
case 'C':
if century, j, err = parseNumber(source, j, 2, 0, 99, 'C'); err != nil {
sign, j = parseSign(source, j, l)
if sign < 0 {
err = errors.New(`negative century is not supported for "%C"`)
return
}
case 'g':
if year, j, err = parseNumber(source, j, 2, 0, 99, b); err != nil {
if century, j, err = parseInt(source, j, 2, 0, 99, 'C'); err != nil {
return
}
year += 2000
hasISOYear = true
case 'G':
if year, j, err = parseNumber(source, j, 4, 0, 9999, b); err != nil {
return
}
hasISOYear = true
case 'm':
if month, j, err = parseNumber(source, j, 2, 1, 12, 'm'); err != nil {
if month, j, err = parseInt(source, j, 2, 1, 12, 'm'); err != nil {
return
}
case 'B':
@@ -88,29 +88,29 @@ func parse(source, format string, loc, base *time.Location) (t time.Time, err er
return
}
case 'w':
if weekday, j, err = parseNumber(source, j, 1, 0, 6, 'w'); err != nil {
if weekday, j, err = parseInt(source, j, 1, 0, 6, 'w'); err != nil {
return
}
weekday++
case 'u':
if weekday, j, err = parseNumber(source, j, 1, 1, 7, 'u'); err != nil {
if weekday, j, err = parseInt(source, j, 1, 1, 7, 'u'); err != nil {
return
}
weekday = weekday%7 + 1
case 'V':
if week, j, err = parseNumber(source, j, 2, 1, 53, b); err != nil {
if week, j, err = parseInt(source, j, 2, 1, 53, b); err != nil {
return
}
weekstart = time.Thursday
weekday = or(weekday, 2)
case 'U':
if week, j, err = parseNumber(source, j, 2, 0, 53, b); err != nil {
if week, j, err = parseInt(source, j, 2, 0, 53, b); err != nil {
return
}
weekstart = time.Sunday
weekday = or(weekday, 1)
case 'W':
if week, j, err = parseNumber(source, j, 2, 0, 53, b); err != nil {
if week, j, err = parseInt(source, j, 2, 0, 53, b); err != nil {
return
}
weekstart = time.Monday
@@ -121,11 +121,11 @@ func parse(source, format string, loc, base *time.Location) (t time.Time, err er
}
fallthrough
case 'd':
if day, j, err = parseNumber(source, j, 2, 1, 31, b); err != nil {
if day, j, err = parseInt(source, j, 2, 1, 31, b); err != nil {
return
}
case 'j':
if yday, j, err = parseNumber(source, j, 3, 1, 366, 'j'); err != nil {
if yday, j, err = parseInt(source, j, 3, 1, 366, 'j'); err != nil {
return
}
case 'k':
@@ -134,7 +134,7 @@ func parse(source, format string, loc, base *time.Location) (t time.Time, err er
}
fallthrough
case 'H':
if hour, j, err = parseNumber(source, j, 2, 0, 23, b); err != nil {
if hour, j, err = parseInt(source, j, 2, 0, 23, b); err != nil {
return
}
case 'l':
@@ -143,7 +143,7 @@ func parse(source, format string, loc, base *time.Location) (t time.Time, err er
}
fallthrough
case 'I':
if hour, j, err = parseNumber(source, j, 2, 1, 12, b); err != nil {
if hour, j, err = parseInt(source, j, 2, 1, 12, b); err != nil {
return
}
if hour == 12 {
@@ -156,26 +156,27 @@ func parse(source, format string, loc, base *time.Location) (t time.Time, err er
}
pm = ampm == 2
case 'M':
if minute, j, err = parseNumber(source, j, 2, 0, 59, 'M'); err != nil {
if minute, j, err = parseInt(source, j, 2, 0, 59, 'M'); err != nil {
return
}
case 'S':
if second, j, err = parseNumber(source, j, 2, 0, 60, 'S'); err != nil {
if second, j, err = parseInt(source, j, 2, 0, 60, 'S'); err != nil {
return
}
case 's':
var unix int
if unix, j, err = parseNumber(source, j, 10, 0, math.MaxInt, 's'); err != nil {
sign, j = parseSign(source, j, l)
var unix int64
if unix, j, err = parseInt64(source, j, 19, 's'); err != nil {
return
}
t = time.Unix(int64(unix), 0).In(time.UTC)
t = time.Unix(int64(sign)*unix, 0).In(time.UTC)
var mon time.Month
year, mon, day = t.Date()
hour, minute, second = t.Clock()
month = int(mon)
case 'f':
microsecond, i := 0, j
if microsecond, j, err = parseNumber(source, j, 6, 0, 999999, 'f'); err != nil {
if microsecond, j, err = parseInt(source, j, 6, 0, 999999, 'f'); err != nil {
return
}
for i = j - i; i < 6; i++ {
@@ -207,19 +208,19 @@ func parse(source, format string, loc, base *time.Location) (t time.Time, err er
err = parseZFormatError(colons)
return
}
sign := 1
sign = 1
switch source[j] {
case '-':
sign = -1
fallthrough
case '+':
hour, minute, second, i := 0, 0, 0, j
if hour, j, _ = parseNumber(source, j+1, 2, 0, 23, 'z'); j != i+3 {
hour, minute, second, i := 0, 0, 0, j+1
if hour, j, _ = parseInt(source, i, 2, 0, 23, 'z'); j != i+2 {
err = parseZFormatError(colons)
return
}
if j >= l || source[j] != ':' {
if colons > 0 {
if colons > 0 && colons < 3 {
err = expectedColonForZFormatError(colons)
return
}
@@ -227,26 +228,26 @@ func parse(source, format string, loc, base *time.Location) (t time.Time, err er
colons = 4
}
i = j
if minute, j, _ = parseNumber(source, j, 2, 0, 59, 'z'); j != i+2 {
if colons > 0 {
if minute, j, _ = parseInt(source, i, 2, 0, 59, 'z'); j != i+2 {
if colons > 0 && colons != 3 {
err = parseZFormatError(colons & 3)
return
}
j = i
} else if colons > 1 {
if j >= l || source[j] != ':' {
if colons == 2 {
if colons < 3 {
err = expectedColonForZFormatError(colons)
return
}
} else {
i = j
if second, j, _ = parseNumber(source, j+1, 2, 0, 59, 'z'); j != i+3 {
if colons == 2 {
i = j + 1
if second, j, _ = parseInt(source, i, 2, 0, 59, 'z'); j != i+2 {
if colons < 3 {
err = parseZFormatError(colons)
return
}
j = i
j = i - 1
}
}
}
@@ -270,12 +271,12 @@ func parse(source, format string, loc, base *time.Location) (t time.Time, err er
}
j++
} else {
for colons = 1; colons <= 2; colons++ {
for colons = 1; colons <= 3; colons++ {
if i++; i == len(format) {
break
} else if b = format[i]; b == 'z' {
goto L
} else if b != ':' || colons == 2 {
} else if b != ':' || colons == 3 {
break
}
}
@@ -302,12 +303,24 @@ func parse(source, format string, loc, base *time.Location) (t time.Time, err er
return
}
j++
case 'c':
pending = "a b e H:M:S Y"
case '+':
pending = "a b e H:M:S Z Y"
case 'v':
pending = "e-b-Y"
case 'r':
pending = "I:M:S p"
case 'F':
pending = "Y-m-d"
case 'D', 'x':
pending = "m/d/y"
case 'T', 'X':
pending = "H:M:S"
case 'R':
pending = "H:M"
default:
if pending == "" {
var ok bool
if pending, ok = compositions[b]; ok {
break
}
err = fmt.Errorf(`unexpected format "%%%c"`, b)
return
}
@@ -386,27 +399,37 @@ func (err expectedFormatError) Error() string {
type parseZFormatError int
func (err parseZFormatError) Error() string {
return `cannot parse "%` + `::z"`[2-err:]
return `cannot parse "%` + `:::z"`[3-err:]
}
type expectedColonForZFormatError int
func (err expectedColonForZFormatError) Error() string {
return `expected ':' for "%` + `::z"`[2-err:]
return `expected ':' for "%` + `:::z"`[3-err:]
}
type expectedZAfterColonError int
func (err expectedZAfterColonError) Error() string {
return `expected 'z' after "%` + `::"`[2-err:]
return `expected 'z' after "%` + `:::"`[3-err:]
}
func parseNumber(source string, index, size, minimum, maximum int, format byte) (int, int, error) {
func parseSign(source string, index, l int) (int, int) {
if index < l && source[index] == '-' {
return -1, index + 1
}
return 1, index
}
// parseInt parses an integer from source. This is intentionally not a
// wrapper of parseInt64 to avoid the overhead of int64 arithmetic and
// function call indirection in the hot path (~20% slower in benchmarks).
func parseInt(source string, index, size, minimum, maximum int, format byte) (int, int, error) {
var value int
i := index
for size = min(i+size, len(source)); i < size; i++ {
if b := source[i]; '0' <= b && b <= '9' {
value = value*10 + int(b&0x0F)
if b := source[i] - '0'; b < 10 {
value = value*10 + int(b)
} else {
break
}
@@ -417,6 +440,22 @@ func parseNumber(source string, index, size, minimum, maximum int, format byte)
return value, i, nil
}
func parseInt64(source string, index, size int, format byte) (int64, int, error) {
var value int64
i := index
for size = min(i+size, len(source)); i < size; i++ {
if b := source[i] - '0'; b < 10 {
value = value*10 + int64(b)
} else {
break
}
}
if i == index {
return 0, 0, parseFormatError(format)
}
return value, i, nil
}
func parseAny(source string, index int, candidates []string, format byte) (int, int, error) {
L:
for i, xs := range candidates {
+2 -2
View File
@@ -461,10 +461,10 @@ github.com/huandu/xstrings
# github.com/inconshreveable/mousetrap v1.1.0
## explicit; go 1.18
github.com/inconshreveable/mousetrap
# github.com/itchyny/gojq v0.12.18
# github.com/itchyny/gojq v0.12.19
## explicit; go 1.24.0
github.com/itchyny/gojq
# github.com/itchyny/timefmt-go v0.1.7
# github.com/itchyny/timefmt-go v0.1.8
## explicit; go 1.24
github.com/itchyny/timefmt-go
# github.com/json-iterator/go v1.1.12