From e356b1f4393b814873635254fd9df7b84b9b0001 Mon Sep 17 00:00:00 2001 From: Manish R Jain <manishrjain@gmail.com> Date: Wed, 28 Oct 2015 17:28:02 +1100 Subject: [PATCH] Add parser to generate SubGraph. Should be a joyous moment, don't feel it though :-). --- gql/lexer.go | 16 ++++++ gql/lexer_test.go | 16 ++++++ gql/parser.go | 131 ++++++++++++++++++++++++++++++++++++++++++++ gql/parser_test.go | 134 +++++++++++++++++++++++++++++++++++++++++++++ gql/state.go | 16 ++++++ 5 files changed, 313 insertions(+) create mode 100644 gql/parser.go create mode 100644 gql/parser_test.go diff --git a/gql/lexer.go b/gql/lexer.go index 4b1be185..3a30e4a7 100644 --- a/gql/lexer.go +++ b/gql/lexer.go @@ -1,3 +1,19 @@ +/* + * Copyright 2015 Manish R Jain <manishrjain@gmail.com> + * + * 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 ( diff --git a/gql/lexer_test.go b/gql/lexer_test.go index 91ea5be5..0564b0c7 100644 --- a/gql/lexer_test.go +++ b/gql/lexer_test.go @@ -1,3 +1,19 @@ +/* + * Copyright 2015 Manish R Jain <manishrjain@gmail.com> + * + * 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 ( diff --git a/gql/parser.go b/gql/parser.go new file mode 100644 index 00000000..5b3f2753 --- /dev/null +++ b/gql/parser.go @@ -0,0 +1,131 @@ +/* + * Copyright 2015 Manish R Jain <manishrjain@gmail.com> + * + * 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/manishrjain/dgraph/query" + "github.com/manishrjain/dgraph/x" +) + +func Parse(input string) (sg *query.SubGraph, rerr error) { + l := newLexer(input) + sg = nil + for item := range l.items { + if item.typ == itemText { + continue + } + if item.typ == itemOpType { + if item.val == "mutation" { + return nil, errors.New("Mutations not supported") + } + } + if item.typ == itemLeftCurl { + if sg == nil { + sg, rerr = getRoot(l) + if rerr != nil { + x.Err(glog, rerr).Error("While retrieving subgraph root") + return nil, rerr + } + } else { + godeep(l, sg) + } + } + } + return sg, nil +} + +func getRoot(l *lexer) (sg *query.SubGraph, rerr error) { + item := <-l.items + if item.typ != itemName { + return nil, fmt.Errorf("Expected some name. Got: %v", item) + } + // ignore itemName for now. + item = <-l.items + if item.typ != itemLeftRound { + 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 { + 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 + } else { + return nil, fmt.Errorf("Expecting argument val. Got: %v", item) + } + + if key == "uid" { + uid, rerr = strconv.ParseUint(val, 0, 64) + if rerr != nil { + return nil, rerr + } + } else if key == "xid" { + xid = val + } else { + return nil, fmt.Errorf("Expecting uid or xid. Got: %v", item) + } + } + if item.typ != itemRightRound { + return nil, fmt.Errorf("Unexpected token. Got: %v", item) + } + return query.NewGraph(uid, xid) +} + +func godeep(l *lexer, sg *query.SubGraph) { + curp := sg // stores current pointer. + for { + switch item := <-l.items; { + case item.typ == itemName: + child := new(query.SubGraph) + child.Attr = item.val + sg.Children = append(sg.Children, child) + curp = child + case item.typ == itemLeftCurl: + godeep(l, curp) // recursive iteration + case item.typ == itemRightCurl: + return + case item.typ == itemLeftRound: + // absorb all these, we don't care right now. + for { + item = <-l.items + if item.typ == itemRightRound || item.typ == itemEOF { + break + } + } + default: + // continue + } + } + +} diff --git a/gql/parser_test.go b/gql/parser_test.go new file mode 100644 index 00000000..e4dab4ee --- /dev/null +++ b/gql/parser_test.go @@ -0,0 +1,134 @@ +/* + * Copyright 2015 Manish R Jain <manishrjain@gmail.com> + * + * 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 ( + "fmt" + "testing" + + "github.com/manishrjain/dgraph/query" +) + +func checkAttr(g *query.SubGraph, attr string) error { + if g.Attr != attr { + return fmt.Errorf("Expected: %v. Got: %v", attr, g.Attr) + } + return nil +} + +func TestParse(t *testing.T) { + query := ` + query { + me(uid:0x0a) { + friends { + name + } + gender + age + hometown + } + } +` + + sg, err := Parse(query) + if err != nil { + t.Error(err) + } + if len(sg.Children) != 4 { + t.Errorf("Expected 4 children. Got: %v", len(sg.Children)) + } + if err := checkAttr(sg.Children[0], "friends"); err != nil { + t.Error(err) + } + if err := checkAttr(sg.Children[1], "gender"); err != nil { + t.Error(err) + } + if err := checkAttr(sg.Children[2], "age"); err != nil { + t.Error(err) + } + if err := checkAttr(sg.Children[3], "hometown"); err != nil { + t.Error(err) + } + child := sg.Children[0] + if len(child.Children) != 1 { + t.Errorf("Expected 1 child of friends. Got: %v", len(child.Children)) + } + if err := checkAttr(child.Children[0], "name"); err != nil { + t.Error(err) + } +} + +func TestParse_error1(t *testing.T) { + query := ` + mutation { + me(uid:0x0a) { + name + } + } + ` + var err error + _, err = Parse(query) + t.Log(err) + if err == nil { + t.Error("Expected error") + } +} + +func TestParse_error2(t *testing.T) { + query := ` + query { + me { + name + } + } + ` + var err error + _, err = Parse(query) + t.Log(err) + if err == nil { + t.Error("Expected error") + } +} + +func TestParse_pass1(t *testing.T) { + query := ` + { + me(uid:0x0a) { + name, + friends(xid:what) { # xid would be ignored. + } + } + } + ` + sg, err := Parse(query) + if err != nil { + t.Error(err) + } + if len(sg.Children) != 2 { + t.Errorf("Expected 2. Got: %v", len(sg.Children)) + } + if err := checkAttr(sg.Children[0], "name"); err != nil { + t.Error(err) + } + if err := checkAttr(sg.Children[1], "friends"); err != nil { + t.Error(err) + } + f := sg.Children[1] + if len(f.Children) != 0 { + t.Errorf("Expected 0. Got: %v", len(sg.Children)) + } +} diff --git a/gql/state.go b/gql/state.go index 01e967ea..f5e07c04 100644 --- a/gql/state.go +++ b/gql/state.go @@ -1,3 +1,19 @@ +/* + * Copyright 2015 Manish R Jain <manishrjain@gmail.com> + * + * 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 const ( -- GitLab