go-ethereum/vendor/github.com/graph-gophers/graphql-go/internal/query/query.go

235 lines
4.8 KiB
Go

package query
import (
"fmt"
"text/scanner"
"github.com/graph-gophers/graphql-go/errors"
"github.com/graph-gophers/graphql-go/internal/common"
)
type Document struct {
Operations OperationList
Fragments FragmentList
}
type OperationList []*Operation
func (l OperationList) Get(name string) *Operation {
for _, f := range l {
if f.Name.Name == name {
return f
}
}
return nil
}
type FragmentList []*FragmentDecl
func (l FragmentList) Get(name string) *FragmentDecl {
for _, f := range l {
if f.Name.Name == name {
return f
}
}
return nil
}
type Operation struct {
Type OperationType
Name common.Ident
Vars common.InputValueList
Selections []Selection
Directives common.DirectiveList
Loc errors.Location
}
type OperationType string
const (
Query OperationType = "QUERY"
Mutation = "MUTATION"
Subscription = "SUBSCRIPTION"
)
type Fragment struct {
On common.TypeName
Selections []Selection
}
type FragmentDecl struct {
Fragment
Name common.Ident
Directives common.DirectiveList
Loc errors.Location
}
type Selection interface {
isSelection()
}
type Field struct {
Alias common.Ident
Name common.Ident
Arguments common.ArgumentList
Directives common.DirectiveList
Selections []Selection
SelectionSetLoc errors.Location
}
type InlineFragment struct {
Fragment
Directives common.DirectiveList
Loc errors.Location
}
type FragmentSpread struct {
Name common.Ident
Directives common.DirectiveList
Loc errors.Location
}
func (Field) isSelection() {}
func (InlineFragment) isSelection() {}
func (FragmentSpread) isSelection() {}
func Parse(queryString string) (*Document, *errors.QueryError) {
l := common.NewLexer(queryString)
var doc *Document
err := l.CatchSyntaxError(func() { doc = parseDocument(l) })
if err != nil {
return nil, err
}
return doc, nil
}
func parseDocument(l *common.Lexer) *Document {
d := &Document{}
l.Consume()
for l.Peek() != scanner.EOF {
if l.Peek() == '{' {
op := &Operation{Type: Query, Loc: l.Location()}
op.Selections = parseSelectionSet(l)
d.Operations = append(d.Operations, op)
continue
}
loc := l.Location()
switch x := l.ConsumeIdent(); x {
case "query":
op := parseOperation(l, Query)
op.Loc = loc
d.Operations = append(d.Operations, op)
case "mutation":
d.Operations = append(d.Operations, parseOperation(l, Mutation))
case "subscription":
d.Operations = append(d.Operations, parseOperation(l, Subscription))
case "fragment":
frag := parseFragment(l)
frag.Loc = loc
d.Fragments = append(d.Fragments, frag)
default:
l.SyntaxError(fmt.Sprintf(`unexpected %q, expecting "fragment"`, x))
}
}
return d
}
func parseOperation(l *common.Lexer, opType OperationType) *Operation {
op := &Operation{Type: opType}
op.Name.Loc = l.Location()
if l.Peek() == scanner.Ident {
op.Name = l.ConsumeIdentWithLoc()
}
op.Directives = common.ParseDirectives(l)
if l.Peek() == '(' {
l.ConsumeToken('(')
for l.Peek() != ')' {
loc := l.Location()
l.ConsumeToken('$')
iv := common.ParseInputValue(l)
iv.Loc = loc
op.Vars = append(op.Vars, iv)
}
l.ConsumeToken(')')
}
op.Selections = parseSelectionSet(l)
return op
}
func parseFragment(l *common.Lexer) *FragmentDecl {
f := &FragmentDecl{}
f.Name = l.ConsumeIdentWithLoc()
l.ConsumeKeyword("on")
f.On = common.TypeName{Ident: l.ConsumeIdentWithLoc()}
f.Directives = common.ParseDirectives(l)
f.Selections = parseSelectionSet(l)
return f
}
func parseSelectionSet(l *common.Lexer) []Selection {
var sels []Selection
l.ConsumeToken('{')
for l.Peek() != '}' {
sels = append(sels, parseSelection(l))
}
l.ConsumeToken('}')
return sels
}
func parseSelection(l *common.Lexer) Selection {
if l.Peek() == '.' {
return parseSpread(l)
}
return parseField(l)
}
func parseField(l *common.Lexer) *Field {
f := &Field{}
f.Alias = l.ConsumeIdentWithLoc()
f.Name = f.Alias
if l.Peek() == ':' {
l.ConsumeToken(':')
f.Name = l.ConsumeIdentWithLoc()
}
if l.Peek() == '(' {
f.Arguments = common.ParseArguments(l)
}
f.Directives = common.ParseDirectives(l)
if l.Peek() == '{' {
f.SelectionSetLoc = l.Location()
f.Selections = parseSelectionSet(l)
}
return f
}
func parseSpread(l *common.Lexer) Selection {
loc := l.Location()
l.ConsumeToken('.')
l.ConsumeToken('.')
l.ConsumeToken('.')
f := &InlineFragment{Loc: loc}
if l.Peek() == scanner.Ident {
ident := l.ConsumeIdentWithLoc()
if ident.Name != "on" {
fs := &FragmentSpread{
Name: ident,
Loc: loc,
}
fs.Directives = common.ParseDirectives(l)
return fs
}
f.On = common.TypeName{Ident: l.ConsumeIdentWithLoc()}
}
f.Directives = common.ParseDirectives(l)
f.Selections = parseSelectionSet(l)
return f
}