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
Manish R Jain
committed
Children []*GraphQuery
}
type Mutation struct {
Set string
Del string
}
type pair struct {
Key string
Val 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
}
}
}
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
143
144
145
146
147
148
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
}
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
func parseArguments(l *lex.Lexer) (result []pair, rerr error) {
for {
var p pair
// Get key.
item := <-l.Items
if item.Typ == itemArgName {
p.Key = item.Val
} else if item.Typ == itemRightRound {
break
} else {
return result, fmt.Errorf("Expecting argument name. Got: %v", item)
}
// Get value.
item = <-l.Items
if item.Typ == itemArgVal {
p.Val = item.Val
} else {
return result, fmt.Errorf("Expecting argument value. Got: %v", item)
}
result = append(result, p)
}
return result, nil
}
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
args, err := parseArguments(l)
if err != nil {
return nil, err
}
for _, p := range args {
if p.Key == "_uid_" {
uid, rerr = strconv.ParseUint(p.Val, 0, 64)
Manish R Jain
committed
if rerr != nil {
return nil, rerr
}
} else if p.Key == "_xid_" {
xid = p.Val
Manish R Jain
committed
} else {
return nil, fmt.Errorf("Expecting _uid_ or _xid_. Got: %+v", p)
Manish R Jain
committed
}
}
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 {
var key, val string
item = <-l.Items
if item.Typ == itemArgName {
key = item.Val
} else if item.Typ == itemRightRound {
break
} else {
return nil, fmt.Errorf("Expecting argument name. Got: %v", item)
}
}
*/
Manish R Jain
committed
for ti := range l.Items {
Manish R Jain
committed
if ti.Typ == itemRightRound || ti.Typ == lex.ItemEOF {
return nil
Manish R Jain
committed
}
}
}
}
Manish R Jain
committed
return nil