github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/schemadsl/parser/parser.go (about) 1 // parser package defines the parser for the Authzed Schema DSL. 2 package parser 3 4 import ( 5 "strings" 6 7 "github.com/authzed/spicedb/pkg/schemadsl/dslshape" 8 "github.com/authzed/spicedb/pkg/schemadsl/input" 9 "github.com/authzed/spicedb/pkg/schemadsl/lexer" 10 ) 11 12 // Parse parses the given Schema DSL source into a parse tree. 13 func Parse(builder NodeBuilder, source input.Source, input string) AstNode { 14 lx := lexer.Lex(source, input) 15 parser := buildParser(lx, builder, source, input) 16 defer parser.close() 17 return parser.consumeTopLevel() 18 } 19 20 // ignoredTokenTypes are those tokens ignored when parsing. 21 var ignoredTokenTypes = map[lexer.TokenType]bool{ 22 lexer.TokenTypeWhitespace: true, 23 lexer.TokenTypeNewline: true, 24 lexer.TokenTypeSinglelineComment: true, 25 lexer.TokenTypeMultilineComment: true, 26 } 27 28 // consumeTopLevel attempts to consume the top-level definitions. 29 func (p *sourceParser) consumeTopLevel() AstNode { 30 rootNode := p.startNode(dslshape.NodeTypeFile) 31 defer p.mustFinishNode() 32 33 // Start at the first token. 34 p.consumeToken() 35 36 if p.currentToken.Kind == lexer.TokenTypeError { 37 p.emitErrorf("%s", p.currentToken.Value) 38 return rootNode 39 } 40 41 Loop: 42 for { 43 if p.isToken(lexer.TokenTypeEOF) { 44 break Loop 45 } 46 47 // Consume a statement terminator if one was found. 48 p.tryConsumeStatementTerminator() 49 50 if p.isToken(lexer.TokenTypeEOF) { 51 break Loop 52 } 53 54 // The top level of the DSL is a set of definitions and caveats: 55 // definition foobar { ... } 56 // caveat somecaveat (...) { ... } 57 58 switch { 59 case p.isKeyword("definition"): 60 rootNode.Connect(dslshape.NodePredicateChild, p.consumeDefinition()) 61 62 case p.isKeyword("caveat"): 63 rootNode.Connect(dslshape.NodePredicateChild, p.consumeCaveat()) 64 65 default: 66 p.emitErrorf("Unexpected token at root level: %v", p.currentToken.Kind) 67 break Loop 68 } 69 } 70 71 return rootNode 72 } 73 74 // consumeCaveat attempts to consume a single caveat definition. 75 // ```caveat somecaveat(param1 type, param2 type) { ... }``` 76 func (p *sourceParser) consumeCaveat() AstNode { 77 defNode := p.startNode(dslshape.NodeTypeCaveatDefinition) 78 defer p.mustFinishNode() 79 80 // caveat ... 81 p.consumeKeyword("caveat") 82 caveatName, ok := p.consumeTypePath() 83 if !ok { 84 return defNode 85 } 86 87 defNode.MustDecorate(dslshape.NodeCaveatDefinitionPredicateName, caveatName) 88 89 // Parameters: 90 // ( 91 _, ok = p.consume(lexer.TokenTypeLeftParen) 92 if !ok { 93 return defNode 94 } 95 96 for { 97 paramNode, ok := p.consumeCaveatParameter() 98 if !ok { 99 return defNode 100 } 101 102 defNode.Connect(dslshape.NodeCaveatDefinitionPredicateParameters, paramNode) 103 if _, ok := p.tryConsume(lexer.TokenTypeComma); !ok { 104 break 105 } 106 } 107 108 // ) 109 _, ok = p.consume(lexer.TokenTypeRightParen) 110 if !ok { 111 return defNode 112 } 113 114 // { 115 _, ok = p.consume(lexer.TokenTypeLeftBrace) 116 if !ok { 117 return defNode 118 } 119 120 exprNode, ok := p.consumeCaveatExpression() 121 if !ok { 122 return defNode 123 } 124 125 defNode.Connect(dslshape.NodeCaveatDefinitionPredicateExpession, exprNode) 126 127 // } 128 _, ok = p.consume(lexer.TokenTypeRightBrace) 129 if !ok { 130 return defNode 131 } 132 133 return defNode 134 } 135 136 func (p *sourceParser) consumeCaveatExpression() (AstNode, bool) { 137 exprNode := p.startNode(dslshape.NodeTypeCaveatExpression) 138 defer p.mustFinishNode() 139 140 // Special Logic Note: Since CEL is its own language, we consume here until we have a matching 141 // close brace, and then pass ALL the found tokens to CEL's own parser to attach the expression 142 // here. 143 braceDepth := 1 // Starting at 1 from the open brace above 144 var startToken *commentedLexeme 145 var endToken *commentedLexeme 146 consumer: 147 for { 148 currentToken := p.currentToken 149 150 switch currentToken.Kind { 151 case lexer.TokenTypeLeftBrace: 152 braceDepth++ 153 154 case lexer.TokenTypeRightBrace: 155 if braceDepth == 1 { 156 break consumer 157 } 158 159 braceDepth-- 160 161 case lexer.TokenTypeError: 162 break consumer 163 164 case lexer.TokenTypeEOF: 165 break consumer 166 } 167 168 if startToken == nil { 169 startToken = ¤tToken 170 } 171 172 endToken = ¤tToken 173 p.consumeToken() 174 } 175 176 if startToken == nil { 177 p.emitErrorf("missing caveat expression") 178 return exprNode, false 179 } 180 181 caveatExpression := p.input[startToken.Position : int(endToken.Position)+len(endToken.Value)] 182 exprNode.MustDecorate(dslshape.NodeCaveatExpressionPredicateExpression, caveatExpression) 183 return exprNode, true 184 } 185 186 // consumeCaveatParameter attempts to consume a caveat parameter. 187 // ```(paramName paramtype)``` 188 func (p *sourceParser) consumeCaveatParameter() (AstNode, bool) { 189 paramNode := p.startNode(dslshape.NodeTypeCaveatParameter) 190 defer p.mustFinishNode() 191 192 name, ok := p.consumeIdentifier() 193 if !ok { 194 return paramNode, false 195 } 196 197 paramNode.MustDecorate(dslshape.NodeCaveatParameterPredicateName, name) 198 paramNode.Connect(dslshape.NodeCaveatParameterPredicateType, p.consumeCaveatTypeReference()) 199 return paramNode, true 200 } 201 202 // consumeCaveatTypeReference attempts to consume a caveat type reference. 203 // ```typeName<childType>``` 204 func (p *sourceParser) consumeCaveatTypeReference() AstNode { 205 typeRefNode := p.startNode(dslshape.NodeTypeCaveatTypeReference) 206 defer p.mustFinishNode() 207 208 name, ok := p.consumeIdentifier() 209 if !ok { 210 return typeRefNode 211 } 212 213 typeRefNode.MustDecorate(dslshape.NodeCaveatTypeReferencePredicateType, name) 214 215 // Check for child type(s). 216 // < 217 if _, ok := p.tryConsume(lexer.TokenTypeLessThan); !ok { 218 return typeRefNode 219 } 220 221 for { 222 childTypeRef := p.consumeCaveatTypeReference() 223 typeRefNode.Connect(dslshape.NodeCaveatTypeReferencePredicateChildTypes, childTypeRef) 224 if _, ok := p.tryConsume(lexer.TokenTypeComma); !ok { 225 break 226 } 227 } 228 229 // > 230 p.consume(lexer.TokenTypeGreaterThan) 231 return typeRefNode 232 } 233 234 // consumeDefinition attempts to consume a single schema definition. 235 // ```definition somedef { ... }``` 236 func (p *sourceParser) consumeDefinition() AstNode { 237 defNode := p.startNode(dslshape.NodeTypeDefinition) 238 defer p.mustFinishNode() 239 240 // definition ... 241 p.consumeKeyword("definition") 242 definitionName, ok := p.consumeTypePath() 243 if !ok { 244 return defNode 245 } 246 247 defNode.MustDecorate(dslshape.NodeDefinitionPredicateName, definitionName) 248 249 // { 250 _, ok = p.consume(lexer.TokenTypeLeftBrace) 251 if !ok { 252 return defNode 253 } 254 255 // Relations and permissions. 256 for { 257 // } 258 if _, ok := p.tryConsume(lexer.TokenTypeRightBrace); ok { 259 break 260 } 261 262 // relation ... 263 // permission ... 264 switch { 265 case p.isKeyword("relation"): 266 defNode.Connect(dslshape.NodePredicateChild, p.consumeRelation()) 267 268 case p.isKeyword("permission"): 269 defNode.Connect(dslshape.NodePredicateChild, p.consumePermission()) 270 } 271 272 ok := p.consumeStatementTerminator() 273 if !ok { 274 break 275 } 276 } 277 278 return defNode 279 } 280 281 // consumeRelation consumes a relation. 282 // ```relation foo: sometype``` 283 func (p *sourceParser) consumeRelation() AstNode { 284 relNode := p.startNode(dslshape.NodeTypeRelation) 285 defer p.mustFinishNode() 286 287 // relation ... 288 p.consumeKeyword("relation") 289 relationName, ok := p.consumeIdentifier() 290 if !ok { 291 return relNode 292 } 293 294 relNode.MustDecorate(dslshape.NodePredicateName, relationName) 295 296 // : 297 _, ok = p.consume(lexer.TokenTypeColon) 298 if !ok { 299 return relNode 300 } 301 302 // Relation allowed type(s). 303 relNode.Connect(dslshape.NodeRelationPredicateAllowedTypes, p.consumeTypeReference()) 304 305 return relNode 306 } 307 308 // consumeTypeReference consumes a reference to a type or types of relations. 309 // ```sometype | anothertype | anothertype:* ``` 310 func (p *sourceParser) consumeTypeReference() AstNode { 311 refNode := p.startNode(dslshape.NodeTypeTypeReference) 312 defer p.mustFinishNode() 313 314 for { 315 refNode.Connect(dslshape.NodeTypeReferencePredicateType, p.consumeSpecificTypeWithCaveat()) 316 if _, ok := p.tryConsume(lexer.TokenTypePipe); !ok { 317 break 318 } 319 } 320 321 return refNode 322 } 323 324 // tryConsumeWithCaveat tries to consume a caveat `with` expression. 325 func (p *sourceParser) tryConsumeWithCaveat() (AstNode, bool) { 326 if !p.isKeyword("with") { 327 return nil, false 328 } 329 330 caveatNode := p.startNode(dslshape.NodeTypeCaveatReference) 331 defer p.mustFinishNode() 332 333 if ok := p.consumeKeyword("with"); !ok { 334 return nil, ok 335 } 336 337 consumed, ok := p.consumeTypePath() 338 if !ok { 339 return caveatNode, true 340 } 341 342 caveatNode.MustDecorate(dslshape.NodeCaveatPredicateCaveat, consumed) 343 return caveatNode, true 344 } 345 346 // consumeSpecificTypeWithCaveat consumes an identifier as a specific type reference, with optional caveat. 347 func (p *sourceParser) consumeSpecificTypeWithCaveat() AstNode { 348 specificNode := p.consumeSpecificTypeWithoutFinish() 349 defer p.mustFinishNode() 350 351 caveatNode, ok := p.tryConsumeWithCaveat() 352 if ok { 353 specificNode.Connect(dslshape.NodeSpecificReferencePredicateCaveat, caveatNode) 354 } 355 356 return specificNode 357 } 358 359 // consumeSpecificTypeOpen consumes an identifier as a specific type reference. 360 func (p *sourceParser) consumeSpecificTypeWithoutFinish() AstNode { 361 specificNode := p.startNode(dslshape.NodeTypeSpecificTypeReference) 362 363 typeName, ok := p.consumeTypePath() 364 if !ok { 365 return specificNode 366 } 367 368 specificNode.MustDecorate(dslshape.NodeSpecificReferencePredicateType, typeName) 369 370 // Check for a wildcard 371 if _, ok := p.tryConsume(lexer.TokenTypeColon); ok { 372 _, ok := p.consume(lexer.TokenTypeStar) 373 if !ok { 374 return specificNode 375 } 376 377 specificNode.MustDecorate(dslshape.NodeSpecificReferencePredicateWildcard, "true") 378 return specificNode 379 } 380 381 // Check for a relation specified. 382 if _, ok := p.tryConsume(lexer.TokenTypeHash); !ok { 383 return specificNode 384 } 385 386 // Consume an identifier or an ellipsis. 387 consumed, ok := p.consume(lexer.TokenTypeIdentifier, lexer.TokenTypeEllipsis) 388 if !ok { 389 return specificNode 390 } 391 392 specificNode.MustDecorate(dslshape.NodeSpecificReferencePredicateRelation, consumed.Value) 393 return specificNode 394 } 395 396 func (p *sourceParser) consumeTypePath() (string, bool) { 397 var segments []string 398 399 for { 400 segment, ok := p.consumeIdentifier() 401 if !ok { 402 return "", false 403 } 404 405 segments = append(segments, segment) 406 407 _, ok = p.tryConsume(lexer.TokenTypeDiv) 408 if !ok { 409 break 410 } 411 } 412 413 return strings.Join(segments, "/"), true 414 } 415 416 // consumePermission consumes a permission. 417 // ```permission foo = bar + baz``` 418 func (p *sourceParser) consumePermission() AstNode { 419 permNode := p.startNode(dslshape.NodeTypePermission) 420 defer p.mustFinishNode() 421 422 // permission ... 423 p.consumeKeyword("permission") 424 permissionName, ok := p.consumeIdentifier() 425 if !ok { 426 return permNode 427 } 428 429 permNode.MustDecorate(dslshape.NodePredicateName, permissionName) 430 431 // = 432 _, ok = p.consume(lexer.TokenTypeEquals) 433 if !ok { 434 return permNode 435 } 436 437 permNode.Connect(dslshape.NodePermissionPredicateComputeExpression, p.consumeComputeExpression()) 438 return permNode 439 } 440 441 // ComputeExpressionOperators defines the binary operators in precedence order. 442 var ComputeExpressionOperators = []binaryOpDefinition{ 443 {lexer.TokenTypeMinus, dslshape.NodeTypeExclusionExpression}, 444 {lexer.TokenTypeAnd, dslshape.NodeTypeIntersectExpression}, 445 {lexer.TokenTypePlus, dslshape.NodeTypeUnionExpression}, 446 } 447 448 // consumeComputeExpression consumes an expression for computing a permission. 449 func (p *sourceParser) consumeComputeExpression() AstNode { 450 // Compute expressions consist of a set of binary operators, so build a tree with proper 451 // precedence. 452 binaryParser := p.buildBinaryOperatorExpressionFnTree(ComputeExpressionOperators) 453 found, ok := binaryParser() 454 if !ok { 455 return p.createErrorNodef("Expected compute expression for permission") 456 } 457 return found 458 } 459 460 // tryConsumeComputeExpression attempts to consume a nested compute expression. 461 func (p *sourceParser) tryConsumeComputeExpression(subTryExprFn tryParserFn, binaryTokenType lexer.TokenType, nodeType dslshape.NodeType) (AstNode, bool) { 462 rightNodeBuilder := func(leftNode AstNode, operatorToken lexer.Lexeme) (AstNode, bool) { 463 rightNode, ok := subTryExprFn() 464 if !ok { 465 return nil, false 466 } 467 468 // Create the expression node representing the binary expression. 469 exprNode := p.createNode(nodeType) 470 exprNode.Connect(dslshape.NodeExpressionPredicateLeftExpr, leftNode) 471 exprNode.Connect(dslshape.NodeExpressionPredicateRightExpr, rightNode) 472 return exprNode, true 473 } 474 return p.performLeftRecursiveParsing(subTryExprFn, rightNodeBuilder, nil, binaryTokenType) 475 } 476 477 // tryConsumeArrowExpression attempts to consume an arrow expression. 478 // ```foo->bar->baz->meh``` 479 func (p *sourceParser) tryConsumeArrowExpression() (AstNode, bool) { 480 rightNodeBuilder := func(leftNode AstNode, operatorToken lexer.Lexeme) (AstNode, bool) { 481 rightNode, ok := p.tryConsumeBaseExpression() 482 if !ok { 483 return nil, false 484 } 485 486 // Create the expression node representing the binary expression. 487 exprNode := p.createNode(dslshape.NodeTypeArrowExpression) 488 exprNode.Connect(dslshape.NodeExpressionPredicateLeftExpr, leftNode) 489 exprNode.Connect(dslshape.NodeExpressionPredicateRightExpr, rightNode) 490 return exprNode, true 491 } 492 return p.performLeftRecursiveParsing(p.tryConsumeIdentifierLiteral, rightNodeBuilder, nil, lexer.TokenTypeRightArrow) 493 } 494 495 // tryConsumeBaseExpression attempts to consume base compute expressions (identifiers, parenthesis). 496 // ```(foo + bar)``` 497 // ```(foo)``` 498 // ```foo``` 499 // ```nil``` 500 func (p *sourceParser) tryConsumeBaseExpression() (AstNode, bool) { 501 switch { 502 // Nested expression. 503 case p.isToken(lexer.TokenTypeLeftParen): 504 comments := p.currentToken.comments 505 506 p.consume(lexer.TokenTypeLeftParen) 507 exprNode := p.consumeComputeExpression() 508 p.consume(lexer.TokenTypeRightParen) 509 510 // Attach any comments found to the consumed expression. 511 p.decorateComments(exprNode, comments) 512 513 return exprNode, true 514 515 // Nil expression. 516 case p.isKeyword("nil"): 517 return p.tryConsumeNilExpression() 518 519 // Identifier. 520 case p.isToken(lexer.TokenTypeIdentifier): 521 return p.tryConsumeIdentifierLiteral() 522 } 523 524 return nil, false 525 } 526 527 // tryConsumeIdentifierLiteral attempts to consume an identifier as a literal 528 // expression. 529 // 530 // ```foo``` 531 func (p *sourceParser) tryConsumeIdentifierLiteral() (AstNode, bool) { 532 if !p.isToken(lexer.TokenTypeIdentifier) { 533 return nil, false 534 } 535 536 identNode := p.startNode(dslshape.NodeTypeIdentifier) 537 defer p.mustFinishNode() 538 539 identifier, _ := p.consumeIdentifier() 540 identNode.MustDecorate(dslshape.NodeIdentiferPredicateValue, identifier) 541 return identNode, true 542 } 543 544 func (p *sourceParser) tryConsumeNilExpression() (AstNode, bool) { 545 if !p.isKeyword("nil") { 546 return nil, false 547 } 548 549 node := p.startNode(dslshape.NodeTypeNilExpression) 550 p.consumeKeyword("nil") 551 defer p.mustFinishNode() 552 return node, true 553 }