mirror of
https://github.com/MichaelMure/git-bug.git
synced 2024-12-16 11:00:42 +03:00
349 lines
7.4 KiB
Go
349 lines
7.4 KiB
Go
package parser
|
|
|
|
import (
|
|
"github.com/vektah/gqlparser/gqlerror"
|
|
"github.com/vektah/gqlparser/lexer"
|
|
|
|
. "github.com/vektah/gqlparser/ast"
|
|
)
|
|
|
|
func ParseQuery(source *Source) (*QueryDocument, *gqlerror.Error) {
|
|
p := parser{
|
|
lexer: lexer.New(source),
|
|
}
|
|
return p.parseQueryDocument(), p.err
|
|
}
|
|
|
|
func (p *parser) parseQueryDocument() *QueryDocument {
|
|
var doc QueryDocument
|
|
for p.peek().Kind != lexer.EOF {
|
|
if p.err != nil {
|
|
return &doc
|
|
}
|
|
doc.Position = p.peekPos()
|
|
switch p.peek().Kind {
|
|
case lexer.Name:
|
|
switch p.peek().Value {
|
|
case "query", "mutation", "subscription":
|
|
doc.Operations = append(doc.Operations, p.parseOperationDefinition())
|
|
case "fragment":
|
|
doc.Fragments = append(doc.Fragments, p.parseFragmentDefinition())
|
|
default:
|
|
p.unexpectedError()
|
|
}
|
|
case lexer.BraceL:
|
|
doc.Operations = append(doc.Operations, p.parseOperationDefinition())
|
|
default:
|
|
p.unexpectedError()
|
|
}
|
|
}
|
|
|
|
return &doc
|
|
}
|
|
|
|
func (p *parser) parseOperationDefinition() *OperationDefinition {
|
|
if p.peek().Kind == lexer.BraceL {
|
|
return &OperationDefinition{
|
|
Position: p.peekPos(),
|
|
Operation: Query,
|
|
SelectionSet: p.parseRequiredSelectionSet(),
|
|
}
|
|
}
|
|
|
|
var od OperationDefinition
|
|
od.Position = p.peekPos()
|
|
od.Operation = p.parseOperationType()
|
|
|
|
if p.peek().Kind == lexer.Name {
|
|
od.Name = p.next().Value
|
|
}
|
|
|
|
od.VariableDefinitions = p.parseVariableDefinitions()
|
|
od.Directives = p.parseDirectives(false)
|
|
od.SelectionSet = p.parseRequiredSelectionSet()
|
|
|
|
return &od
|
|
}
|
|
|
|
func (p *parser) parseOperationType() Operation {
|
|
tok := p.next()
|
|
switch tok.Value {
|
|
case "query":
|
|
return Query
|
|
case "mutation":
|
|
return Mutation
|
|
case "subscription":
|
|
return Subscription
|
|
}
|
|
p.unexpectedToken(tok)
|
|
return ""
|
|
}
|
|
|
|
func (p *parser) parseVariableDefinitions() VariableDefinitionList {
|
|
var defs []*VariableDefinition
|
|
p.many(lexer.ParenL, lexer.ParenR, func() {
|
|
defs = append(defs, p.parseVariableDefinition())
|
|
})
|
|
|
|
return defs
|
|
}
|
|
|
|
func (p *parser) parseVariableDefinition() *VariableDefinition {
|
|
var def VariableDefinition
|
|
def.Position = p.peekPos()
|
|
def.Variable = p.parseVariable()
|
|
|
|
p.expect(lexer.Colon)
|
|
|
|
def.Type = p.parseTypeReference()
|
|
|
|
if p.skip(lexer.Equals) {
|
|
def.DefaultValue = p.parseValueLiteral(true)
|
|
}
|
|
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseVariable() string {
|
|
p.expect(lexer.Dollar)
|
|
return p.parseName()
|
|
}
|
|
|
|
func (p *parser) parseOptionalSelectionSet() SelectionSet {
|
|
var selections []Selection
|
|
p.some(lexer.BraceL, lexer.BraceR, func() {
|
|
selections = append(selections, p.parseSelection())
|
|
})
|
|
|
|
return SelectionSet(selections)
|
|
}
|
|
|
|
func (p *parser) parseRequiredSelectionSet() SelectionSet {
|
|
if p.peek().Kind != lexer.BraceL {
|
|
p.error(p.peek(), "Expected %s, found %s", lexer.BraceL, p.peek().Kind.String())
|
|
return nil
|
|
}
|
|
|
|
var selections []Selection
|
|
p.some(lexer.BraceL, lexer.BraceR, func() {
|
|
selections = append(selections, p.parseSelection())
|
|
})
|
|
|
|
return SelectionSet(selections)
|
|
}
|
|
|
|
func (p *parser) parseSelection() Selection {
|
|
if p.peek().Kind == lexer.Spread {
|
|
return p.parseFragment()
|
|
}
|
|
return p.parseField()
|
|
}
|
|
|
|
func (p *parser) parseField() *Field {
|
|
var field Field
|
|
field.Position = p.peekPos()
|
|
field.Alias = p.parseName()
|
|
|
|
if p.skip(lexer.Colon) {
|
|
field.Name = p.parseName()
|
|
} else {
|
|
field.Name = field.Alias
|
|
}
|
|
|
|
field.Arguments = p.parseArguments(false)
|
|
field.Directives = p.parseDirectives(false)
|
|
if p.peek().Kind == lexer.BraceL {
|
|
field.SelectionSet = p.parseOptionalSelectionSet()
|
|
}
|
|
|
|
return &field
|
|
}
|
|
|
|
func (p *parser) parseArguments(isConst bool) ArgumentList {
|
|
var arguments ArgumentList
|
|
p.many(lexer.ParenL, lexer.ParenR, func() {
|
|
arguments = append(arguments, p.parseArgument(isConst))
|
|
})
|
|
|
|
return arguments
|
|
}
|
|
|
|
func (p *parser) parseArgument(isConst bool) *Argument {
|
|
arg := Argument{}
|
|
arg.Position = p.peekPos()
|
|
arg.Name = p.parseName()
|
|
p.expect(lexer.Colon)
|
|
|
|
arg.Value = p.parseValueLiteral(isConst)
|
|
return &arg
|
|
}
|
|
|
|
func (p *parser) parseFragment() Selection {
|
|
p.expect(lexer.Spread)
|
|
|
|
if peek := p.peek(); peek.Kind == lexer.Name && peek.Value != "on" {
|
|
return &FragmentSpread{
|
|
Position: p.peekPos(),
|
|
Name: p.parseFragmentName(),
|
|
Directives: p.parseDirectives(false),
|
|
}
|
|
}
|
|
|
|
var def InlineFragment
|
|
def.Position = p.peekPos()
|
|
if p.peek().Value == "on" {
|
|
p.next() // "on"
|
|
|
|
def.TypeCondition = p.parseName()
|
|
}
|
|
|
|
def.Directives = p.parseDirectives(false)
|
|
def.SelectionSet = p.parseRequiredSelectionSet()
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseFragmentDefinition() *FragmentDefinition {
|
|
var def FragmentDefinition
|
|
def.Position = p.peekPos()
|
|
p.expectKeyword("fragment")
|
|
|
|
def.Name = p.parseFragmentName()
|
|
def.VariableDefinition = p.parseVariableDefinitions()
|
|
|
|
p.expectKeyword("on")
|
|
|
|
def.TypeCondition = p.parseName()
|
|
def.Directives = p.parseDirectives(false)
|
|
def.SelectionSet = p.parseRequiredSelectionSet()
|
|
return &def
|
|
}
|
|
|
|
func (p *parser) parseFragmentName() string {
|
|
if p.peek().Value == "on" {
|
|
p.unexpectedError()
|
|
return ""
|
|
}
|
|
|
|
return p.parseName()
|
|
}
|
|
|
|
func (p *parser) parseValueLiteral(isConst bool) *Value {
|
|
token := p.peek()
|
|
|
|
var kind ValueKind
|
|
switch token.Kind {
|
|
case lexer.BracketL:
|
|
return p.parseList(isConst)
|
|
case lexer.BraceL:
|
|
return p.parseObject(isConst)
|
|
case lexer.Dollar:
|
|
if isConst {
|
|
p.unexpectedError()
|
|
return nil
|
|
}
|
|
return &Value{Position: &token.Pos, Raw: p.parseVariable(), Kind: Variable}
|
|
case lexer.Int:
|
|
kind = IntValue
|
|
case lexer.Float:
|
|
kind = FloatValue
|
|
case lexer.String:
|
|
kind = StringValue
|
|
case lexer.BlockString:
|
|
kind = BlockValue
|
|
case lexer.Name:
|
|
switch token.Value {
|
|
case "true", "false":
|
|
kind = BooleanValue
|
|
case "null":
|
|
kind = NullValue
|
|
default:
|
|
kind = EnumValue
|
|
}
|
|
default:
|
|
p.unexpectedError()
|
|
return nil
|
|
}
|
|
|
|
p.next()
|
|
|
|
return &Value{Position: &token.Pos, Raw: token.Value, Kind: kind}
|
|
}
|
|
|
|
func (p *parser) parseList(isConst bool) *Value {
|
|
var values ChildValueList
|
|
pos := p.peekPos()
|
|
p.many(lexer.BracketL, lexer.BracketR, func() {
|
|
values = append(values, &ChildValue{Value: p.parseValueLiteral(isConst)})
|
|
})
|
|
|
|
return &Value{Children: values, Kind: ListValue, Position: pos}
|
|
}
|
|
|
|
func (p *parser) parseObject(isConst bool) *Value {
|
|
var fields ChildValueList
|
|
pos := p.peekPos()
|
|
p.many(lexer.BraceL, lexer.BraceR, func() {
|
|
fields = append(fields, p.parseObjectField(isConst))
|
|
})
|
|
|
|
return &Value{Children: fields, Kind: ObjectValue, Position: pos}
|
|
}
|
|
|
|
func (p *parser) parseObjectField(isConst bool) *ChildValue {
|
|
field := ChildValue{}
|
|
field.Position = p.peekPos()
|
|
field.Name = p.parseName()
|
|
|
|
p.expect(lexer.Colon)
|
|
|
|
field.Value = p.parseValueLiteral(isConst)
|
|
return &field
|
|
}
|
|
|
|
func (p *parser) parseDirectives(isConst bool) []*Directive {
|
|
var directives []*Directive
|
|
|
|
for p.peek().Kind == lexer.At {
|
|
if p.err != nil {
|
|
break
|
|
}
|
|
directives = append(directives, p.parseDirective(isConst))
|
|
}
|
|
return directives
|
|
}
|
|
|
|
func (p *parser) parseDirective(isConst bool) *Directive {
|
|
p.expect(lexer.At)
|
|
|
|
return &Directive{
|
|
Position: p.peekPos(),
|
|
Name: p.parseName(),
|
|
Arguments: p.parseArguments(isConst),
|
|
}
|
|
}
|
|
|
|
func (p *parser) parseTypeReference() *Type {
|
|
var typ Type
|
|
|
|
if p.skip(lexer.BracketL) {
|
|
typ.Position = p.peekPos()
|
|
typ.Elem = p.parseTypeReference()
|
|
p.expect(lexer.BracketR)
|
|
} else {
|
|
typ.Position = p.peekPos()
|
|
typ.NamedType = p.parseName()
|
|
}
|
|
|
|
if p.skip(lexer.Bang) {
|
|
typ.Position = p.peekPos()
|
|
typ.NonNull = true
|
|
}
|
|
return &typ
|
|
}
|
|
|
|
func (p *parser) parseName() string {
|
|
token := p.expect(lexer.Name)
|
|
|
|
return token.Value
|
|
}
|