Newer
Older
Manish R Jain
committed
/*
* Copyright 2015 Manish R Jain <manishrjain@gmaicom>
Manish R Jain
committed
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package gql
import (
"errors"
"fmt"
"strconv"
"github.com/dgraph-io/dgraph/lex"
Manish R Jain
committed
)
var glog = x.Log("gql")
Manish R Jain
committed
// GraphQuery stores the parsed Query in a tree format. This gets
// converted to internally used query.SubGraph before processing the query.
type GraphQuery struct {
UID uint64
XID string
Attr string
Children []*GraphQuery
}
type Mutation struct {
Set string
Del string
}
func run(l *lex.Lexer) {
for state := lexText; state != nil; {
state = state(l)
}
close(l.Items) // No more tokens.
}
func Parse(input string) (gq *GraphQuery, mu *Mutation, rerr error) {
go run(l)
Manish R Jain
committed
gq = nil
for item := range l.Items {
if item.Typ == itemText {
Manish R Jain
committed
continue
}
if item.Typ == itemOpType {
if item.Val == "mutation" {
if mu != nil {
return nil, nil, errors.New("Only one mutation block allowed.")
}
mu, rerr = getMutation(l)
if rerr != nil {
return nil, nil, rerr
}
Manish R Jain
committed
}
}
if item.Typ == itemLeftCurl {
Manish R Jain
committed
if gq == nil {
gq, rerr = getRoot(l)
Manish R Jain
committed
if rerr != nil {
x.Err(glog, rerr).Error("While retrieving subgraph root")
Manish R Jain
committed
}
} else {
Manish R Jain
committed
if err := godeep(l, gq); err != nil {
Manish R Jain
committed
}
Manish R Jain
committed
}
}
}
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
return gq, mu, nil
}
func getMutation(l *lex.Lexer) (mu *Mutation, rerr error) {
for item := range l.Items {
if item.Typ == itemText {
continue
}
if item.Typ == itemLeftCurl {
mu = new(Mutation)
}
if item.Typ == itemRightCurl {
return mu, nil
}
if item.Typ == itemMutationOp {
if err := parseMutationOp(l, item.Val, mu); err != nil {
return nil, err
}
}
}
return nil, errors.New("Invalid mutation.")
}
func parseMutationOp(l *lex.Lexer, op string, mu *Mutation) error {
if mu == nil {
return errors.New("Mutation is nil.")
}
parse := false
for item := range l.Items {
if item.Typ == itemText {
continue
}
if item.Typ == itemLeftCurl {
if parse {
return errors.New("Too many left curls in set mutation.")
}
parse = true
}
if item.Typ == itemMutationContent {
if !parse {
return errors.New("Mutation syntax invalid.")
}
if op == "set" {
mu.Set = item.Val
} else if op == "delete" {
mu.Del = item.Val
} else {
return errors.New("Invalid mutation operation.")
}
}
if item.Typ == itemRightCurl {
return nil
}
}
return errors.New("Invalid mutation formatting.")
Manish R Jain
committed
}
Manish R Jain
committed
func getRoot(l *lex.Lexer) (gq *GraphQuery, rerr error) {
item := <-l.Items
if item.Typ != itemName {
Manish R Jain
committed
return nil, fmt.Errorf("Expected some name. Got: %v", item)
}
// ignore itemName for now.
item = <-l.Items
if item.Typ != itemLeftRound {
Manish R Jain
committed
return nil, fmt.Errorf("Expected variable start. Got: %v", item)
}
var uid uint64
var xid string
for {
var key, val string
// Get key or close bracket
item = <-l.Items
if item.Typ == itemArgName {
key = item.Val
} else if item.Typ == itemRightRound {
Manish R Jain
committed
break
} else {
return nil, fmt.Errorf("Expecting argument name. Got: %v", item)
}
// Get corresponding value.
item = <-l.Items
if item.Typ == itemArgVal {
val = item.Val
Manish R Jain
committed
} else {
return nil, fmt.Errorf("Expecting argument va Got: %v", item)
Manish R Jain
committed
}
Manish R Jain
committed
if key == "_uid_" {
Manish R Jain
committed
uid, rerr = strconv.ParseUint(val, 0, 64)
if rerr != nil {
return nil, rerr
}
Manish R Jain
committed
} else if key == "_xid_" {
Manish R Jain
committed
xid = val
} else {
Manish R Jain
committed
return nil, fmt.Errorf("Expecting _uid_ or _xid_. Got: %v", item)
Manish R Jain
committed
}
}
if item.Typ != itemRightRound {
Manish R Jain
committed
return nil, fmt.Errorf("Unexpected token. Got: %v", item)
}
Manish R Jain
committed
gq = new(GraphQuery)
gq.UID = uid
gq.XID = xid
return gq, nil
Manish R Jain
committed
}
Manish R Jain
committed
func godeep(l *lex.Lexer, gq *GraphQuery) error {
curp := gq // Used to track current node, for nesting.
Manish R Jain
committed
for item := range l.Items {
if item.Typ == lex.ItemError {
return errors.New(item.Val)
} else if item.Typ == lex.ItemEOF {
return nil
} else if item.Typ == itemName {
Manish R Jain
committed
child := new(GraphQuery)
child.Attr = item.Val
Manish R Jain
committed
gq.Children = append(gq.Children, child)
Manish R Jain
committed
curp = child
Manish R Jain
committed
} else if item.Typ == itemLeftCurl {
if err := godeep(l, curp); err != nil {
return err
}
} else if item.Typ == itemRightCurl {
return nil
} else if item.Typ == itemLeftRound {
// absorb all these, we don't use them right now.
for ti := range l.Items {
if ti.Typ == itemRightRound || ti.Typ == lex.ItemEOF {
return nil
Manish R Jain
committed
}
}
}
}
Manish R Jain
committed
return nil