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

157 lines
3.7 KiB
Go
Raw Normal View History

2022-04-01 00:23:19 +02:00
package query
import (
"fmt"
"text/scanner"
"github.com/graph-gophers/graphql-go/errors"
"github.com/graph-gophers/graphql-go/internal/common"
"github.com/graph-gophers/graphql-go/types"
)
const (
Query types.OperationType = "QUERY"
Mutation types.OperationType = "MUTATION"
Subscription types.OperationType = "SUBSCRIPTION"
)
func Parse(queryString string) (*types.ExecutableDefinition, *errors.QueryError) {
l := common.NewLexer(queryString, false)
var execDef *types.ExecutableDefinition
err := l.CatchSyntaxError(func() { execDef = parseExecutableDefinition(l) })
if err != nil {
return nil, err
}
return execDef, nil
}
func parseExecutableDefinition(l *common.Lexer) *types.ExecutableDefinition {
ed := &types.ExecutableDefinition{}
l.ConsumeWhitespace()
for l.Peek() != scanner.EOF {
if l.Peek() == '{' {
op := &types.OperationDefinition{Type: Query, Loc: l.Location()}
op.Selections = parseSelectionSet(l)
ed.Operations = append(ed.Operations, op)
continue
}
loc := l.Location()
switch x := l.ConsumeIdent(); x {
case "query":
op := parseOperation(l, Query)
op.Loc = loc
ed.Operations = append(ed.Operations, op)
case "mutation":
ed.Operations = append(ed.Operations, parseOperation(l, Mutation))
case "subscription":
ed.Operations = append(ed.Operations, parseOperation(l, Subscription))
case "fragment":
frag := parseFragment(l)
frag.Loc = loc
ed.Fragments = append(ed.Fragments, frag)
default:
l.SyntaxError(fmt.Sprintf(`unexpected %q, expecting "fragment"`, x))
}
}
return ed
}
func parseOperation(l *common.Lexer, opType types.OperationType) *types.OperationDefinition {
op := &types.OperationDefinition{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) *types.FragmentDefinition {
f := &types.FragmentDefinition{}
f.Name = l.ConsumeIdentWithLoc()
l.ConsumeKeyword("on")
f.On = types.TypeName{Ident: l.ConsumeIdentWithLoc()}
f.Directives = common.ParseDirectives(l)
f.Selections = parseSelectionSet(l)
return f
}
func parseSelectionSet(l *common.Lexer) []types.Selection {
var sels []types.Selection
l.ConsumeToken('{')
for l.Peek() != '}' {
sels = append(sels, parseSelection(l))
}
l.ConsumeToken('}')
return sels
}
func parseSelection(l *common.Lexer) types.Selection {
if l.Peek() == '.' {
return parseSpread(l)
}
return parseFieldDef(l)
}
func parseFieldDef(l *common.Lexer) *types.Field {
f := &types.Field{}
f.Alias = l.ConsumeIdentWithLoc()
f.Name = f.Alias
if l.Peek() == ':' {
l.ConsumeToken(':')
f.Name = l.ConsumeIdentWithLoc()
}
if l.Peek() == '(' {
f.Arguments = common.ParseArgumentList(l)
}
f.Directives = common.ParseDirectives(l)
if l.Peek() == '{' {
f.SelectionSetLoc = l.Location()
f.SelectionSet = parseSelectionSet(l)
}
return f
}
func parseSpread(l *common.Lexer) types.Selection {
loc := l.Location()
l.ConsumeToken('.')
l.ConsumeToken('.')
l.ConsumeToken('.')
f := &types.InlineFragment{Loc: loc}
if l.Peek() == scanner.Ident {
ident := l.ConsumeIdentWithLoc()
if ident.Name != "on" {
fs := &types.FragmentSpread{
Name: ident,
Loc: loc,
}
fs.Directives = common.ParseDirectives(l)
return fs
}
f.On = types.TypeName{Ident: l.ConsumeIdentWithLoc()}
}
f.Directives = common.ParseDirectives(l)
f.Selections = parseSelectionSet(l)
return f
}