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.
943 lines
16 KiB
Go
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
|
|
}
|