%{ package gojq // Parse a query string, and returns the query struct. // // If parsing failed, the returned error has the method Token() (string, int), // which reports the invalid token and the byte offset in the query string. The // token is empty if the error occurred after scanning the entire query string. // The byte offset is the scanned bytes when the error occurred. func Parse(src string) (*Query, error) { l := newLexer(src) if yyParse(l) > 0 { return nil, l.err } return l.result, nil } func reverseFuncDef(xs []*FuncDef) []*FuncDef { for i, j := 0, len(xs)-1; i < j; i, j = i+1, j-1 { xs[i], xs[j] = xs[j], xs[i] } return xs } func prependFuncDef(xs []*FuncDef, x *FuncDef) []*FuncDef { xs = append(xs, nil) copy(xs[1:], xs) xs[0] = x return xs } %} %union { value any token string operator Operator } %type program moduleheader programbody imports import metaopt funcdefs funcdef funcdefargs query %type bindpatterns pattern arraypatterns objectpatterns objectpattern %type term string stringparts suffix args ifelifs ifelse trycatch %type objectkeyvals objectkeyval objectval %type constterm constobject constobjectkeyvals constobjectkeyval constarray constarrayelems %type tokIdentVariable tokIdentModuleIdent tokVariableModuleVariable tokKeyword objectkey %token tokAltOp tokUpdateOp tokDestAltOp tokOrOp tokAndOp tokCompareOp %token tokModule tokImport tokInclude tokDef tokAs tokLabel tokBreak %token tokNull tokTrue tokFalse %token tokIdent tokVariable tokModuleIdent tokModuleVariable %token tokIndex tokNumber tokFormat %token tokString tokStringStart tokStringQuery tokStringEnd %token tokIf tokThen tokElif tokElse tokEnd %token tokTry tokCatch tokReduce tokForeach %token tokRecurse tokFuncDefPost tokTermPost tokEmptyCatch %token tokInvalid tokInvalidEscapeSequence tokUnterminatedString %nonassoc tokFuncDefPost tokTermPost %right '|' %left ',' %right tokAltOp %nonassoc tokUpdateOp %left tokOrOp %left tokAndOp %nonassoc tokCompareOp %left '+' '-' %left '*' '/' '%' %nonassoc tokAs tokIndex '.' '?' tokEmptyCatch %nonassoc '[' tokTry tokCatch %% program : moduleheader programbody { if $1 != nil { $2.(*Query).Meta = $1.(*ConstObject) } yylex.(*lexer).result = $2.(*Query) } moduleheader : { $$ = nil } | tokModule constobject ';' { $$ = $2; } programbody : imports funcdefs { $$ = &Query{Imports: $1.([]*Import), FuncDefs: reverseFuncDef($2.([]*FuncDef)), Term: &Term{Type: TermTypeIdentity}} } | imports query { if $1 != nil { $2.(*Query).Imports = $1.([]*Import) } $$ = $2 } imports : { $$ = []*Import(nil) } | imports import { $$ = append($1.([]*Import), $2.(*Import)) } import : tokImport tokString tokAs tokIdentVariable metaopt ';' { $$ = &Import{ImportPath: $2, ImportAlias: $4, Meta: $5.(*ConstObject)} } | tokInclude tokString metaopt ';' { $$ = &Import{IncludePath: $2, Meta: $3.(*ConstObject)} } metaopt : { $$ = (*ConstObject)(nil) } | constobject {} funcdefs : { $$ = []*FuncDef(nil) } | funcdef funcdefs { $$ = append($2.([]*FuncDef), $1.(*FuncDef)) } funcdef : tokDef tokIdent ':' query ';' { $$ = &FuncDef{Name: $2, Body: $4.(*Query)} } | tokDef tokIdent '(' funcdefargs ')' ':' query ';' { $$ = &FuncDef{$2, $4.([]string), $7.(*Query)} } funcdefargs : tokIdentVariable { $$ = []string{$1} } | funcdefargs ';' tokIdentVariable { $$ = append($1.([]string), $3) } tokIdentVariable : tokIdent {} | tokVariable {} query : funcdef query %prec tokFuncDefPost { $2.(*Query).FuncDefs = prependFuncDef($2.(*Query).FuncDefs, $1.(*FuncDef)) $$ = $2 } | query '|' query { $$ = &Query{Left: $1.(*Query), Op: OpPipe, Right: $3.(*Query)} } | term tokAs bindpatterns '|' query { $1.(*Term).SuffixList = append($1.(*Term).SuffixList, &Suffix{Bind: &Bind{$3.([]*Pattern), $5.(*Query)}}) $$ = &Query{Term: $1.(*Term)} } | tokReduce term tokAs pattern '(' query ';' query ')' { $$ = &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{$2.(*Term), $4.(*Pattern), $6.(*Query), $8.(*Query)}}} } | tokForeach term tokAs pattern '(' query ';' query ')' { $$ = &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{$2.(*Term), $4.(*Pattern), $6.(*Query), $8.(*Query), nil}}} } | tokForeach term tokAs pattern '(' query ';' query ';' query ')' { $$ = &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{$2.(*Term), $4.(*Pattern), $6.(*Query), $8.(*Query), $10.(*Query)}}} } | tokIf query tokThen query ifelifs ifelse tokEnd { $$ = &Query{Term: &Term{Type: TermTypeIf, If: &If{$2.(*Query), $4.(*Query), $5.([]*IfElif), $6.(*Query)}}} } | tokTry query trycatch { $$ = &Query{Term: &Term{Type: TermTypeTry, Try: &Try{$2.(*Query), $3.(*Query)}}} } | tokLabel tokVariable '|' query { $$ = &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{$2, $4.(*Query)}}} } | query '?' { if t := $1.(*Query).Term; t != nil { t.SuffixList = append(t.SuffixList, &Suffix{Optional: true}) } else { $$ = &Query{Term: &Term{Type: TermTypeQuery, Query: $1.(*Query), SuffixList: []*Suffix{{Optional: true}}}} } } | query ',' query { $$ = &Query{Left: $1.(*Query), Op: OpComma, Right: $3.(*Query)} } | query tokAltOp query { $$ = &Query{Left: $1.(*Query), Op: $2, Right: $3.(*Query)} } | query tokUpdateOp query { $$ = &Query{Left: $1.(*Query), Op: $2, Right: $3.(*Query)} } | query tokOrOp query { $$ = &Query{Left: $1.(*Query), Op: OpOr, Right: $3.(*Query)} } | query tokAndOp query { $$ = &Query{Left: $1.(*Query), Op: OpAnd, Right: $3.(*Query)} } | query tokCompareOp query { $$ = &Query{Left: $1.(*Query), Op: $2, Right: $3.(*Query)} } | query '+' query { $$ = &Query{Left: $1.(*Query), Op: OpAdd, Right: $3.(*Query)} } | query '-' query { $$ = &Query{Left: $1.(*Query), Op: OpSub, Right: $3.(*Query)} } | query '*' query { $$ = &Query{Left: $1.(*Query), Op: OpMul, Right: $3.(*Query)} } | query '/' query { $$ = &Query{Left: $1.(*Query), Op: OpDiv, Right: $3.(*Query)} } | query '%' query { $$ = &Query{Left: $1.(*Query), Op: OpMod, Right: $3.(*Query)} } | term %prec tokTermPost { $$ = &Query{Term: $1.(*Term)} } bindpatterns : pattern { $$ = []*Pattern{$1.(*Pattern)} } | bindpatterns tokDestAltOp pattern { $$ = append($1.([]*Pattern), $3.(*Pattern)) } pattern : tokVariable { $$ = &Pattern{Name: $1} } | '[' arraypatterns ']' { $$ = &Pattern{Array: $2.([]*Pattern)} } | '{' objectpatterns '}' { $$ = &Pattern{Object: $2.([]*PatternObject)} } arraypatterns : pattern { $$ = []*Pattern{$1.(*Pattern)} } | arraypatterns ',' pattern { $$ = append($1.([]*Pattern), $3.(*Pattern)) } objectpatterns : objectpattern { $$ = []*PatternObject{$1.(*PatternObject)} } | objectpatterns ',' objectpattern { $$ = append($1.([]*PatternObject), $3.(*PatternObject)) } objectpattern : objectkey ':' pattern { $$ = &PatternObject{Key: $1, Val: $3.(*Pattern)} } | string ':' pattern { $$ = &PatternObject{KeyString: $1.(*String), Val: $3.(*Pattern)} } | '(' query ')' ':' pattern { $$ = &PatternObject{KeyQuery: $2.(*Query), Val: $5.(*Pattern)} } | tokVariable { $$ = &PatternObject{Key: $1} } term : '.' { $$ = &Term{Type: TermTypeIdentity} } | tokRecurse { $$ = &Term{Type: TermTypeRecurse} } | tokIndex { $$ = &Term{Type: TermTypeIndex, Index: &Index{Name: $1}} } | '.' suffix { if $2.(*Suffix).Iter { $$ = &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{$2.(*Suffix)}} } else { $$ = &Term{Type: TermTypeIndex, Index: $2.(*Suffix).Index} } } | '.' string { $$ = &Term{Type: TermTypeIndex, Index: &Index{Str: $2.(*String)}} } | tokNull { $$ = &Term{Type: TermTypeNull} } | tokTrue { $$ = &Term{Type: TermTypeTrue} } | tokFalse { $$ = &Term{Type: TermTypeFalse} } | tokIdentModuleIdent { $$ = &Term{Type: TermTypeFunc, Func: &Func{Name: $1}} } | tokIdentModuleIdent '(' args ')' { $$ = &Term{Type: TermTypeFunc, Func: &Func{Name: $1, Args: $3.([]*Query)}} } | tokVariableModuleVariable { $$ = &Term{Type: TermTypeFunc, Func: &Func{Name: $1}} } | tokNumber { $$ = &Term{Type: TermTypeNumber, Number: $1} } | tokFormat { $$ = &Term{Type: TermTypeFormat, Format: $1} } | tokFormat string { $$ = &Term{Type: TermTypeFormat, Format: $1, Str: $2.(*String)} } | string { $$ = &Term{Type: TermTypeString, Str: $1.(*String)} } | '(' query ')' { $$ = &Term{Type: TermTypeQuery, Query: $2.(*Query)} } | '+' term { $$ = &Term{Type: TermTypeUnary, Unary: &Unary{OpAdd, $2.(*Term)}} } | '-' term { $$ = &Term{Type: TermTypeUnary, Unary: &Unary{OpSub, $2.(*Term)}} } | '{' '}' { $$ = &Term{Type: TermTypeObject, Object: &Object{}} } | '{' objectkeyvals '}' { $$ = &Term{Type: TermTypeObject, Object: &Object{$2.([]*ObjectKeyVal)}} } | '{' objectkeyvals ',' '}' { $$ = &Term{Type: TermTypeObject, Object: &Object{$2.([]*ObjectKeyVal)}} } | '[' ']' { $$ = &Term{Type: TermTypeArray, Array: &Array{}} } | '[' query ']' { $$ = &Term{Type: TermTypeArray, Array: &Array{$2.(*Query)}} } | tokBreak tokVariable { $$ = &Term{Type: TermTypeBreak, Break: $2} } | term tokIndex { $1.(*Term).SuffixList = append($1.(*Term).SuffixList, &Suffix{Index: &Index{Name: $2}}) } | term suffix { $1.(*Term).SuffixList = append($1.(*Term).SuffixList, $2.(*Suffix)) } | term '?' { $1.(*Term).SuffixList = append($1.(*Term).SuffixList, &Suffix{Optional: true}) } | term '.' suffix { $1.(*Term).SuffixList = append($1.(*Term).SuffixList, $3.(*Suffix)) } | term '.' string { $1.(*Term).SuffixList = append($1.(*Term).SuffixList, &Suffix{Index: &Index{Str: $3.(*String)}}) } string : tokString { $$ = &String{Str: $1} } | tokStringStart stringparts tokStringEnd { $$ = &String{Queries: $2.([]*Query)} } stringparts : { $$ = []*Query{} } | stringparts tokString { $$ = append($1.([]*Query), &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: $2}}}) } | stringparts tokStringQuery query ')' { yylex.(*lexer).inString = true $$ = append($1.([]*Query), &Query{Term: &Term{Type: TermTypeQuery, Query: $3.(*Query)}}) } tokIdentModuleIdent : tokIdent {} | tokModuleIdent {} tokVariableModuleVariable : tokVariable {} | tokModuleVariable {} suffix : '[' ']' { $$ = &Suffix{Iter: true} } | '[' query ']' { $$ = &Suffix{Index: &Index{Start: $2.(*Query)}} } | '[' query ':' ']' { $$ = &Suffix{Index: &Index{Start: $2.(*Query), IsSlice: true}} } | '[' ':' query ']' { $$ = &Suffix{Index: &Index{End: $3.(*Query), IsSlice: true}} } | '[' query ':' query ']' { $$ = &Suffix{Index: &Index{Start: $2.(*Query), End: $4.(*Query), IsSlice: true}} } args : query { $$ = []*Query{$1.(*Query)} } | args ';' query { $$ = append($1.([]*Query), $3.(*Query)) } ifelifs : { $$ = []*IfElif(nil) } | ifelifs tokElif query tokThen query { $$ = append($1.([]*IfElif), &IfElif{$3.(*Query), $5.(*Query)}) } ifelse : { $$ = (*Query)(nil) } | tokElse query { $$ = $2 } trycatch : %prec tokEmptyCatch { $$ = (*Query)(nil) } | tokCatch query { $$ = $2 } objectkeyvals : objectkeyval { $$ = []*ObjectKeyVal{$1.(*ObjectKeyVal)} } | objectkeyvals ',' objectkeyval { $$ = append($1.([]*ObjectKeyVal), $3.(*ObjectKeyVal)) } objectkeyval : objectkey ':' objectval { $$ = &ObjectKeyVal{Key: $1, Val: $3.(*ObjectVal)} } | string ':' objectval { $$ = &ObjectKeyVal{KeyString: $1.(*String), Val: $3.(*ObjectVal)} } | '(' query ')' ':' objectval { $$ = &ObjectKeyVal{KeyQuery: $2.(*Query), Val: $5.(*ObjectVal)} } | objectkey { $$ = &ObjectKeyVal{Key: $1} } | string { $$ = &ObjectKeyVal{KeyString: $1.(*String)} } objectkey : tokIdent {} | tokVariable {} | tokKeyword {} objectval : term { $$ = &ObjectVal{[]*Query{{Term: $1.(*Term)}}} } | objectval '|' term { $$ = &ObjectVal{append($1.(*ObjectVal).Queries, &Query{Term: $3.(*Term)})} } constterm : constobject { $$ = &ConstTerm{Object: $1.(*ConstObject)} } | constarray { $$ = &ConstTerm{Array: $1.(*ConstArray)} } | tokNumber { $$ = &ConstTerm{Number: $1} } | tokString { $$ = &ConstTerm{Str: $1} } | tokNull { $$ = &ConstTerm{Null: true} } | tokTrue { $$ = &ConstTerm{True: true} } | tokFalse { $$ = &ConstTerm{False: true} } constobject : '{' '}' { $$ = &ConstObject{} } | '{' constobjectkeyvals '}' { $$ = &ConstObject{$2.([]*ConstObjectKeyVal)} } | '{' constobjectkeyvals ',' '}' { $$ = &ConstObject{$2.([]*ConstObjectKeyVal)} } constobjectkeyvals : constobjectkeyval { $$ = []*ConstObjectKeyVal{$1.(*ConstObjectKeyVal)} } | constobjectkeyvals ',' constobjectkeyval { $$ = append($1.([]*ConstObjectKeyVal), $3.(*ConstObjectKeyVal)) } constobjectkeyval : tokIdent ':' constterm { $$ = &ConstObjectKeyVal{Key: $1, Val: $3.(*ConstTerm)} } | tokKeyword ':' constterm { $$ = &ConstObjectKeyVal{Key: $1, Val: $3.(*ConstTerm)} } | tokString ':' constterm { $$ = &ConstObjectKeyVal{KeyString: $1, Val: $3.(*ConstTerm)} } constarray : '[' ']' { $$ = &ConstArray{} } | '[' constarrayelems ']' { $$ = &ConstArray{$2.([]*ConstTerm)} } constarrayelems : constterm { $$ = []*ConstTerm{$1.(*ConstTerm)} } | constarrayelems ',' constterm { $$ = append($1.([]*ConstTerm), $3.(*ConstTerm)) } tokKeyword : tokOrOp {} | tokAndOp {} | tokModule {} | tokImport {} | tokInclude {} | tokDef {} | tokAs {} | tokLabel {} | tokBreak {} | tokNull {} | tokTrue {} | tokFalse {} | tokIf {} | tokThen {} | tokElif {} | tokElse {} | tokEnd {} | tokTry {} | tokCatch {} | tokReduce {} | tokForeach {} %%