Files
kyle 9444a35322 p1: /v2/scrape, /v1/fetch, auth, CLI, contract tests
Single Fetcher.Fetch on the Guard that holds DenyPrefixes. skipTlsVerification
is ignored. No 502. keys add writes the secret only to --out. Release binary
has no -test-allow-prefix.
2026-09-22 21:54:01 -07:00

943 lines
16 KiB
Go

package gojq
import (
"context"
"strings"
)
// Parse a query string, and returns the query struct.
//
// If parsing failed, it returns an error of type [*ParseError], which has
// the byte offset and the invalid token. The byte offset is the scanned bytes
// when the error occurred. The token is empty if the error occurred after
// scanning the entire query string.
func Parse(src string) (*Query, error) {
l := newLexer(src)
if yyParse(l) > 0 {
return nil, l.err
}
return l.result, nil
}
// Query represents the abstract syntax tree of a jq query.
type Query struct {
Meta *ConstObject
Imports []*Import
FuncDefs []*FuncDef
Term *Term
Left *Query
Right *Query
Patterns []*Pattern
Op Operator
}
// Run the query.
//
// It is safe to call this method in goroutines, to reuse a parsed [*Query].
func (e *Query) Run(v any) Iter {
return e.RunWithContext(context.Background(), v)
}
// RunWithContext runs the query with context.
func (e *Query) RunWithContext(ctx context.Context, v any) Iter {
code, err := Compile(e)
if err != nil {
return NewIter(err)
}
return code.RunWithContext(ctx, v)
}
func (e *Query) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *Query) writeTo(s *strings.Builder) {
if e.Meta != nil {
s.WriteString("module ")
e.Meta.writeTo(s)
s.WriteString(";\n")
}
for _, im := range e.Imports {
im.writeTo(s)
}
for _, fd := range e.FuncDefs {
fd.writeTo(s)
s.WriteByte(' ')
}
if e.Term != nil {
e.Term.writeTo(s)
} else if e.Right != nil {
e.Left.writeTo(s)
if e.Op != OpComma {
s.WriteByte(' ')
}
for i, p := range e.Patterns {
if i == 0 {
s.WriteString("as ")
} else {
s.WriteString("?// ")
}
p.writeTo(s)
s.WriteByte(' ')
}
s.WriteString(e.Op.String())
s.WriteByte(' ')
e.Right.writeTo(s)
}
}
func (e *Query) toIndexKey() any {
if e.Term == nil {
return nil
}
return e.Term.toIndexKey()
}
func (e *Query) toIndices(xs []any) []any {
if e.Term == nil {
return nil
}
return e.Term.toIndices(xs)
}
// Import ...
type Import struct {
ImportPath string
ImportAlias string
IncludePath string
Meta *ConstObject
}
func (e *Import) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *Import) writeTo(s *strings.Builder) {
if e.ImportPath != "" {
s.WriteString("import ")
jsonEncodeString(s, e.ImportPath)
s.WriteString(" as ")
s.WriteString(e.ImportAlias)
} else {
s.WriteString("include ")
jsonEncodeString(s, e.IncludePath)
}
if e.Meta != nil {
s.WriteByte(' ')
e.Meta.writeTo(s)
}
s.WriteString(";\n")
}
// FuncDef ...
type FuncDef struct {
Name string
Args []string
Body *Query
}
func (e *FuncDef) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *FuncDef) writeTo(s *strings.Builder) {
s.WriteString("def ")
s.WriteString(e.Name)
if len(e.Args) > 0 {
s.WriteByte('(')
for i, e := range e.Args {
if i > 0 {
s.WriteString("; ")
}
s.WriteString(e)
}
s.WriteByte(')')
}
s.WriteString(": ")
e.Body.writeTo(s)
s.WriteByte(';')
}
// Term ...
type Term struct {
Type TermType
Index *Index
Func *Func
Object *Object
Array *Array
Number string
Unary *Unary
Format string
Str *String
If *If
Try *Try
Reduce *Reduce
Foreach *Foreach
Label *Label
Break string
Query *Query
SuffixList []*Suffix
}
func (e *Term) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *Term) writeTo(s *strings.Builder) {
switch e.Type {
case TermTypeIdentity:
s.WriteByte('.')
case TermTypeRecurse:
s.WriteString("..")
case TermTypeNull:
s.WriteString("null")
case TermTypeTrue:
s.WriteString("true")
case TermTypeFalse:
s.WriteString("false")
case TermTypeIndex:
e.Index.writeTo(s)
case TermTypeFunc:
e.Func.writeTo(s)
case TermTypeObject:
e.Object.writeTo(s)
case TermTypeArray:
e.Array.writeTo(s)
case TermTypeNumber:
s.WriteString(e.Number)
case TermTypeUnary:
e.Unary.writeTo(s)
case TermTypeFormat:
s.WriteString(e.Format)
if e.Str != nil {
s.WriteByte(' ')
e.Str.writeTo(s)
}
case TermTypeString:
e.Str.writeTo(s)
case TermTypeIf:
e.If.writeTo(s)
case TermTypeTry:
e.Try.writeTo(s)
case TermTypeReduce:
e.Reduce.writeTo(s)
case TermTypeForeach:
e.Foreach.writeTo(s)
case TermTypeLabel:
e.Label.writeTo(s)
case TermTypeBreak:
s.WriteString("break ")
s.WriteString(e.Break)
case TermTypeQuery:
s.WriteByte('(')
e.Query.writeTo(s)
s.WriteByte(')')
}
for _, e := range e.SuffixList {
e.writeTo(s)
}
}
func (e *Term) toIndexKey() any {
switch e.Type {
case TermTypeNumber:
return toNumber(e.Number)
case TermTypeUnary:
return e.Unary.toNumber()
case TermTypeString:
if e.Str.Queries == nil {
return e.Str.Str
}
return nil
default:
return nil
}
}
func (e *Term) toIndices(xs []any) []any {
switch e.Type {
case TermTypeIndex:
if xs = e.Index.toIndices(xs); xs == nil {
return nil
}
case TermTypeQuery:
if xs = e.Query.toIndices(xs); xs == nil {
return nil
}
default:
return nil
}
for _, s := range e.SuffixList {
if xs = s.toIndices(xs); xs == nil {
return nil
}
}
return xs
}
func (e *Term) toNumber() any {
if e.Type == TermTypeNumber {
return toNumber(e.Number)
}
return nil
}
// Unary ...
type Unary struct {
Op Operator
Term *Term
}
func (e *Unary) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *Unary) writeTo(s *strings.Builder) {
s.WriteString(e.Op.String())
e.Term.writeTo(s)
}
func (e *Unary) toNumber() any {
v := e.Term.toNumber()
if v != nil && e.Op == OpSub {
v = funcOpNegate(v)
}
return v
}
// Pattern ...
type Pattern struct {
Name string
Array []*Pattern
Object []*PatternObject
}
func (e *Pattern) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *Pattern) writeTo(s *strings.Builder) {
if e.Name != "" {
s.WriteString(e.Name)
} else if len(e.Array) > 0 {
s.WriteByte('[')
for i, e := range e.Array {
if i > 0 {
s.WriteString(", ")
}
e.writeTo(s)
}
s.WriteByte(']')
} else if len(e.Object) > 0 {
s.WriteByte('{')
for i, e := range e.Object {
if i > 0 {
s.WriteString(", ")
}
e.writeTo(s)
}
s.WriteByte('}')
}
}
// PatternObject ...
type PatternObject struct {
Key string
KeyString *String
KeyQuery *Query
Val *Pattern
}
func (e *PatternObject) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *PatternObject) writeTo(s *strings.Builder) {
if e.Key != "" {
s.WriteString(e.Key)
} else if e.KeyString != nil {
e.KeyString.writeTo(s)
} else if e.KeyQuery != nil {
s.WriteByte('(')
e.KeyQuery.writeTo(s)
s.WriteByte(')')
}
if e.Val != nil {
s.WriteString(": ")
e.Val.writeTo(s)
}
}
// Index ...
type Index struct {
Name string
Str *String
Start *Query
End *Query
IsSlice bool
}
func (e *Index) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *Index) writeTo(s *strings.Builder) {
if l := s.Len(); l > 0 {
// ". .x" != "..x" and "0 .x" != "0.x"
if c := s.String()[l-1]; c == '.' || '0' <= c && c <= '9' {
s.WriteByte(' ')
}
}
s.WriteByte('.')
e.writeSuffixTo(s)
}
func (e *Index) writeSuffixTo(s *strings.Builder) {
if e.Name != "" {
s.WriteString(e.Name)
} else if e.Str != nil {
e.Str.writeTo(s)
} else {
s.WriteByte('[')
if e.IsSlice {
if e.Start != nil {
e.Start.writeTo(s)
}
s.WriteByte(':')
if e.End != nil {
e.End.writeTo(s)
}
} else {
e.Start.writeTo(s)
}
s.WriteByte(']')
}
}
func (e *Index) toIndexKey() any {
if e.Name != "" {
return e.Name
} else if e.Str != nil {
if e.Str.Queries == nil {
return e.Str.Str
}
} else if !e.IsSlice {
return e.Start.toIndexKey()
} else {
var start, end any
ok := true
if e.Start != nil {
start = e.Start.toIndexKey()
ok = start != nil
}
if e.End != nil && ok {
end = e.End.toIndexKey()
ok = end != nil
}
if ok {
return map[string]any{"start": start, "end": end}
}
}
return nil
}
func (e *Index) toIndices(xs []any) []any {
if k := e.toIndexKey(); k != nil {
return append(xs, k)
}
return nil
}
// Func ...
type Func struct {
Name string
Args []*Query
}
func (e *Func) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *Func) writeTo(s *strings.Builder) {
s.WriteString(e.Name)
if len(e.Args) > 0 {
s.WriteByte('(')
for i, e := range e.Args {
if i > 0 {
s.WriteString("; ")
}
e.writeTo(s)
}
s.WriteByte(')')
}
}
// String ...
type String struct {
Str string
Queries []*Query
}
func (e *String) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *String) writeTo(s *strings.Builder) {
if e.Queries == nil {
jsonEncodeString(s, e.Str)
return
}
s.WriteByte('"')
for _, e := range e.Queries {
if e.Term.Str == nil {
s.WriteString(`\`)
e.writeTo(s)
} else {
es := e.String()
s.WriteString(es[1 : len(es)-1])
}
}
s.WriteByte('"')
}
// Object ...
type Object struct {
KeyVals []*ObjectKeyVal
}
func (e *Object) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *Object) writeTo(s *strings.Builder) {
if len(e.KeyVals) == 0 {
s.WriteString("{}")
return
}
s.WriteString("{ ")
for i, kv := range e.KeyVals {
if i > 0 {
s.WriteString(", ")
}
kv.writeTo(s)
}
s.WriteString(" }")
}
// ObjectKeyVal ...
type ObjectKeyVal struct {
Key string
KeyString *String
KeyQuery *Query
Val *Query
}
func (e *ObjectKeyVal) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *ObjectKeyVal) writeTo(s *strings.Builder) {
if e.Key != "" {
s.WriteString(e.Key)
} else if e.KeyString != nil {
e.KeyString.writeTo(s)
} else if e.KeyQuery != nil {
s.WriteByte('(')
e.KeyQuery.writeTo(s)
s.WriteByte(')')
}
if e.Val != nil {
s.WriteString(": ")
e.Val.writeTo(s)
}
}
// Array ...
type Array struct {
Query *Query
}
func (e *Array) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *Array) writeTo(s *strings.Builder) {
s.WriteByte('[')
if e.Query != nil {
e.Query.writeTo(s)
}
s.WriteByte(']')
}
// Suffix ...
type Suffix struct {
Index *Index
Iter bool
Optional bool
}
func (e *Suffix) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *Suffix) writeTo(s *strings.Builder) {
if e.Index != nil {
if e.Index.Name != "" || e.Index.Str != nil {
e.Index.writeTo(s)
} else {
e.Index.writeSuffixTo(s)
}
} else if e.Iter {
s.WriteString("[]")
} else if e.Optional {
s.WriteByte('?')
}
}
func (e *Suffix) toTerm() *Term {
if e.Index != nil {
return &Term{Type: TermTypeIndex, Index: e.Index}
} else if e.Iter {
return &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}}}
} else {
return nil
}
}
func (e *Suffix) toIndices(xs []any) []any {
if e.Index == nil {
return nil
}
return e.Index.toIndices(xs)
}
// If ...
type If struct {
Cond *Query
Then *Query
Elif []*IfElif
Else *Query
}
func (e *If) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *If) writeTo(s *strings.Builder) {
s.WriteString("if ")
e.Cond.writeTo(s)
s.WriteString(" then ")
e.Then.writeTo(s)
for _, e := range e.Elif {
s.WriteByte(' ')
e.writeTo(s)
}
if e.Else != nil {
s.WriteString(" else ")
e.Else.writeTo(s)
}
s.WriteString(" end")
}
// IfElif ...
type IfElif struct {
Cond *Query
Then *Query
}
func (e *IfElif) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *IfElif) writeTo(s *strings.Builder) {
s.WriteString("elif ")
e.Cond.writeTo(s)
s.WriteString(" then ")
e.Then.writeTo(s)
}
// Try ...
type Try struct {
Body *Query
Catch *Query
}
func (e *Try) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *Try) writeTo(s *strings.Builder) {
s.WriteString("try ")
e.Body.writeTo(s)
if e.Catch != nil {
s.WriteString(" catch ")
e.Catch.writeTo(s)
}
}
// Reduce ...
type Reduce struct {
Query *Query
Pattern *Pattern
Start *Query
Update *Query
}
func (e *Reduce) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *Reduce) writeTo(s *strings.Builder) {
s.WriteString("reduce ")
e.Query.writeTo(s)
s.WriteString(" as ")
e.Pattern.writeTo(s)
s.WriteString(" (")
e.Start.writeTo(s)
s.WriteString("; ")
e.Update.writeTo(s)
s.WriteByte(')')
}
// Foreach ...
type Foreach struct {
Query *Query
Pattern *Pattern
Start *Query
Update *Query
Extract *Query
}
func (e *Foreach) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *Foreach) writeTo(s *strings.Builder) {
s.WriteString("foreach ")
e.Query.writeTo(s)
s.WriteString(" as ")
e.Pattern.writeTo(s)
s.WriteString(" (")
e.Start.writeTo(s)
s.WriteString("; ")
e.Update.writeTo(s)
if e.Extract != nil {
s.WriteString("; ")
e.Extract.writeTo(s)
}
s.WriteByte(')')
}
// Label ...
type Label struct {
Ident string
Body *Query
}
func (e *Label) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *Label) writeTo(s *strings.Builder) {
s.WriteString("label ")
s.WriteString(e.Ident)
s.WriteString(" | ")
e.Body.writeTo(s)
}
// ConstTerm ...
type ConstTerm struct {
Object *ConstObject
Array *ConstArray
Number string
Str string
Null bool
True bool
False bool
}
func (e *ConstTerm) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *ConstTerm) writeTo(s *strings.Builder) {
if e.Object != nil {
e.Object.writeTo(s)
} else if e.Array != nil {
e.Array.writeTo(s)
} else if e.Number != "" {
s.WriteString(e.Number)
} else if e.Null {
s.WriteString("null")
} else if e.True {
s.WriteString("true")
} else if e.False {
s.WriteString("false")
} else {
jsonEncodeString(s, e.Str)
}
}
func (e *ConstTerm) toValue() any {
if e.Object != nil {
return e.Object.ToValue()
} else if e.Array != nil {
return e.Array.toValue()
} else if e.Number != "" {
return toNumber(e.Number)
} else if e.Null {
return nil
} else if e.True {
return true
} else if e.False {
return false
} else {
return e.Str
}
}
func (e *ConstTerm) toString() (string, bool) {
if e.Object != nil || e.Array != nil ||
e.Number != "" || e.Null || e.True || e.False {
return "", false
}
return e.Str, true
}
// ConstObject ...
type ConstObject struct {
KeyVals []*ConstObjectKeyVal
}
func (e *ConstObject) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *ConstObject) writeTo(s *strings.Builder) {
if len(e.KeyVals) == 0 {
s.WriteString("{}")
return
}
s.WriteString("{ ")
for i, kv := range e.KeyVals {
if i > 0 {
s.WriteString(", ")
}
kv.writeTo(s)
}
s.WriteString(" }")
}
// ToValue converts the object to map[string]any.
func (e *ConstObject) ToValue() map[string]any {
if e == nil {
return nil
}
v := make(map[string]any, len(e.KeyVals))
for _, e := range e.KeyVals {
key := e.Key
if key == "" {
key = e.KeyString
}
v[key] = e.Val.toValue()
}
return v
}
// ConstObjectKeyVal ...
type ConstObjectKeyVal struct {
Key string
KeyString string
Val *ConstTerm
}
func (e *ConstObjectKeyVal) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *ConstObjectKeyVal) writeTo(s *strings.Builder) {
if e.Key != "" {
s.WriteString(e.Key)
} else {
jsonEncodeString(s, e.KeyString)
}
s.WriteString(": ")
e.Val.writeTo(s)
}
// ConstArray ...
type ConstArray struct {
Elems []*ConstTerm
}
func (e *ConstArray) String() string {
var s strings.Builder
e.writeTo(&s)
return s.String()
}
func (e *ConstArray) writeTo(s *strings.Builder) {
s.WriteByte('[')
for i, e := range e.Elems {
if i > 0 {
s.WriteString(", ")
}
e.writeTo(s)
}
s.WriteByte(']')
}
func (e *ConstArray) toValue() []any {
v := make([]any, len(e.Elems))
for i, e := range e.Elems {
v[i] = e.toValue()
}
return v
}