github.com/cayleygraph/cayley@v0.7.7/query/sexp/parser.go (about) 1 // Copyright 2014 The Cayley Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sexp 16 17 import ( 18 "github.com/badgerodon/peg" 19 20 "github.com/cayleygraph/cayley/graph" 21 "github.com/cayleygraph/cayley/graph/iterator" 22 "github.com/cayleygraph/cayley/graph/shape" 23 "github.com/cayleygraph/quad" 24 ) 25 26 func BuildIteratorTreeForQuery(qs graph.QuadStore, query string) graph.Iterator { 27 s, err := BuildShape(query) 28 if err != nil { 29 return iterator.NewError(err) 30 } 31 return shape.BuildIterator(qs, s) 32 } 33 34 func BuildShape(query string) (shape.Shape, error) { 35 tree := parseQuery(query) 36 s, _ := buildShape(tree) 37 s, _ = shape.Optimize(s, nil) 38 return s, nil 39 } 40 41 func ParseString(input string) string { 42 return parseQuery(input).String() 43 } 44 45 func newParser() *peg.Parser { 46 parser := peg.NewParser() 47 48 start := parser.NonTerminal("Start") 49 whitespace := parser.NonTerminal("Whitespace") 50 quotedString := parser.NonTerminal("QuotedString") 51 rootConstraint := parser.NonTerminal("RootConstraint") 52 53 constraint := parser.NonTerminal("Constraint") 54 colonIdentifier := parser.NonTerminal("ColonIdentifier") 55 variable := parser.NonTerminal("Variable") 56 identifier := parser.NonTerminal("Identifier") 57 fixedNode := parser.NonTerminal("FixedNode") 58 nodeIdent := parser.NonTerminal("NodeIdentifier") 59 predIdent := parser.NonTerminal("PredIdentifier") 60 reverse := parser.NonTerminal("Reverse") 61 predKeyword := parser.NonTerminal("PredicateKeyword") 62 optional := parser.NonTerminal("OptionalKeyword") 63 64 start.Expression = rootConstraint 65 66 whitespace.Expression = parser.OneOrMore( 67 parser.OrderedChoice( 68 parser.Terminal(' '), 69 parser.Terminal('\t'), 70 parser.Terminal('\n'), 71 parser.Terminal('\r'), 72 ), 73 ) 74 75 quotedString.Expression = parser.Sequence( 76 parser.Terminal('"'), 77 parser.OneOrMore( 78 parser.OrderedChoice( 79 parser.Range('0', '9'), 80 parser.Range('a', 'z'), 81 parser.Range('A', 'Z'), 82 parser.Terminal('_'), 83 parser.Terminal('/'), 84 parser.Terminal(':'), 85 parser.Terminal(' '), 86 parser.Terminal('\''), 87 ), 88 ), 89 parser.Terminal('"'), 90 ) 91 92 predKeyword.Expression = parser.OrderedChoice( 93 optional, 94 ) 95 96 optional.Expression = parser.Sequence( 97 parser.Terminal('o'), 98 parser.Terminal('p'), 99 parser.Terminal('t'), 100 parser.Terminal('i'), 101 parser.Terminal('o'), 102 parser.Terminal('n'), 103 parser.Terminal('a'), 104 parser.Terminal('l'), 105 ) 106 107 identifier.Expression = parser.OneOrMore( 108 parser.OrderedChoice( 109 parser.Range('0', '9'), 110 parser.Range('a', 'z'), 111 parser.Range('A', 'Z'), 112 parser.Terminal('_'), 113 parser.Terminal('.'), 114 parser.Terminal('/'), 115 parser.Terminal(':'), 116 parser.Terminal('#'), 117 ), 118 ) 119 120 reverse.Expression = parser.Terminal('!') 121 122 variable.Expression = parser.Sequence( 123 parser.Terminal('$'), 124 identifier, 125 ) 126 127 colonIdentifier.Expression = parser.Sequence( 128 parser.Terminal(':'), 129 identifier, 130 ) 131 132 fixedNode.Expression = parser.OrderedChoice( 133 colonIdentifier, 134 quotedString, 135 ) 136 137 nodeIdent.Expression = parser.OrderedChoice( 138 variable, 139 fixedNode, 140 ) 141 142 predIdent.Expression = parser.Sequence( 143 parser.Optional(reverse), 144 parser.OrderedChoice( 145 nodeIdent, 146 constraint, 147 ), 148 ) 149 150 constraint.Expression = parser.Sequence( 151 parser.Terminal('('), 152 parser.Optional(whitespace), 153 predIdent, 154 parser.Optional(whitespace), 155 parser.Optional(predKeyword), 156 parser.Optional(whitespace), 157 parser.OrderedChoice( 158 nodeIdent, 159 rootConstraint, 160 ), 161 parser.Optional(whitespace), 162 parser.Terminal(')'), 163 ) 164 165 rootConstraint.Expression = parser.Sequence( 166 parser.Terminal('('), 167 parser.Optional(whitespace), 168 nodeIdent, 169 parser.Optional(whitespace), 170 parser.ZeroOrMore(parser.Sequence( 171 constraint, 172 parser.Optional(whitespace), 173 )), 174 parser.Terminal(')'), 175 ) 176 return parser 177 } 178 179 func parseQuery(input string) *peg.ExpressionTree { 180 return newParser().Parse(input) 181 } 182 183 func getIdentString(tree *peg.ExpressionTree) string { 184 out := "" 185 if len(tree.Children) > 0 { 186 for _, child := range tree.Children { 187 out += getIdentString(child) 188 } 189 } else { 190 if tree.Value != '"' { 191 out += string(tree.Value) 192 } 193 } 194 return out 195 } 196 197 func lookup(s string) shape.Shape { 198 return shape.Lookup{quad.StringToValue(s)} 199 } 200 201 func buildShape(tree *peg.ExpressionTree) (_ shape.Shape, opt bool) { 202 switch tree.Name { 203 case "Start": 204 return buildShape(tree.Children[0]) 205 case "NodeIdentifier": 206 var out shape.Shape 207 nodeID := getIdentString(tree) 208 if tree.Children[0].Name == "Variable" { 209 out = shape.Save{ 210 From: shape.AllNodes{}, 211 Tags: []string{nodeID}, 212 } 213 } else { 214 n := nodeID 215 if tree.Children[0].Children[0].Name == "ColonIdentifier" { 216 n = nodeID[1:] 217 } 218 out = lookup(n) 219 } 220 return out, false 221 case "PredIdentifier": 222 i := 0 223 if tree.Children[0].Name == "Reverse" { 224 //Taken care of below 225 i++ 226 } 227 it, _ := buildShape(tree.Children[i]) 228 return shape.Quads{ 229 {Dir: quad.Predicate, Values: it}, 230 }, false 231 case "RootConstraint": 232 var and shape.IntersectOpt 233 for _, c := range tree.Children { 234 switch c.Name { 235 case "NodeIdentifier": 236 fallthrough 237 case "Constraint": 238 it, opt := buildShape(c) 239 if opt { 240 and.AddOptional(it) 241 } else { 242 and.Add(it) 243 } 244 continue 245 default: 246 continue 247 } 248 } 249 return and, false 250 case "Constraint": 251 topLevelDir := quad.Subject 252 subItDir := quad.Object 253 var subAnd shape.IntersectOpt 254 isOptional := false 255 for _, c := range tree.Children { 256 switch c.Name { 257 case "PredIdentifier": 258 if c.Children[0].Name == "Reverse" { 259 topLevelDir = quad.Object 260 subItDir = quad.Subject 261 } 262 it, opt := buildShape(c) 263 if opt { 264 subAnd.AddOptional(it) 265 } else { 266 subAnd.Add(it) 267 } 268 continue 269 case "PredicateKeyword": 270 switch c.Children[0].Name { 271 case "OptionalKeyword": 272 isOptional = true 273 } 274 case "NodeIdentifier": 275 fallthrough 276 case "RootConstraint": 277 it, opt := buildShape(c) 278 l := shape.Quads{ 279 {Dir: subItDir, Values: it}, 280 } 281 if opt { 282 subAnd.AddOptional(l) 283 } else { 284 subAnd.Add(l) 285 } 286 continue 287 default: 288 continue 289 } 290 } 291 return shape.NodesFrom{ 292 Dir: topLevelDir, 293 Quads: subAnd, 294 }, isOptional 295 default: 296 return shape.Null{}, false 297 } 298 }