Skip to content

Commit

Permalink
Merge pull request #2105 from authzed/make-all-and-any-into-keywords
Browse files Browse the repository at this point in the history
Make all and any into keywords
  • Loading branch information
tstirrat15 authored Oct 28, 2024
2 parents 98a8473 + a7c8332 commit 45c8f10
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 28 deletions.
2 changes: 2 additions & 0 deletions pkg/composableschemadsl/lexer/lex_def.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ var keywords = map[string]struct{}{
"with": {},
"from": {},
"import": {},
"all": {},
"any": {},
}

// IsKeyword returns whether the specified input string is a reserved keyword.
Expand Down
10 changes: 9 additions & 1 deletion pkg/composableschemadsl/lexer/lex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ var lexerTests = []lexerTest{
{"minus", "-", []Lexeme{{TokenTypeMinus, 0, "-", ""}, tEOF}},

{"keyword", "definition", []Lexeme{{TokenTypeKeyword, 0, "definition", ""}, tEOF}},
{"keyword", "caveat", []Lexeme{{TokenTypeKeyword, 0, "caveat", ""}, tEOF}},
{"keyword", "relation", []Lexeme{{TokenTypeKeyword, 0, "relation", ""}, tEOF}},
{"keyword", "permission", []Lexeme{{TokenTypeKeyword, 0, "permission", ""}, tEOF}},
{"keyword", "nil", []Lexeme{{TokenTypeKeyword, 0, "nil", ""}, tEOF}},
{"keyword", "with", []Lexeme{{TokenTypeKeyword, 0, "with", ""}, tEOF}},
{"keyword", "from", []Lexeme{{TokenTypeKeyword, 0, "from", ""}, tEOF}},
{"keyword", "import", []Lexeme{{TokenTypeKeyword, 0, "import", ""}, tEOF}},
{"keyword", "all", []Lexeme{{TokenTypeKeyword, 0, "all", ""}, tEOF}},
{"keyword", "nil", []Lexeme{{TokenTypeKeyword, 0, "nil", ""}, tEOF}},
{"identifier", "define", []Lexeme{{TokenTypeIdentifier, 0, "define", ""}, tEOF}},
{"typepath", "foo/bar", []Lexeme{
Expand Down Expand Up @@ -251,7 +259,7 @@ var lexerTests = []lexerTest{
{"dot access", "foo.all(something)", []Lexeme{
{TokenTypeIdentifier, 0, "foo", ""},
{TokenTypePeriod, 0, ".", ""},
{TokenTypeIdentifier, 0, "all", ""},
{TokenTypeKeyword, 0, "all", ""},
{TokenTypeLeftParen, 0, "(", ""},
{TokenTypeIdentifier, 0, "something", ""},
{TokenTypeRightParen, 0, ")", ""},
Expand Down
25 changes: 17 additions & 8 deletions pkg/composableschemadsl/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ func (p *sourceParser) consumeCaveatTypeReference() AstNode {
typeRefNode := p.startNode(dslshape.NodeTypeCaveatTypeReference)
defer p.mustFinishNode()

name, ok := p.consumeIdentifier()
name, ok := p.consumeCaveatTypeIdentifier()
if !ok {
return typeRefNode
}
Expand All @@ -234,6 +234,21 @@ func (p *sourceParser) consumeCaveatTypeReference() AstNode {
return typeRefNode
}

// "any" is both a keyword and a valid caveat type, so a caveat type identifier
// can either be a keyword or an identifier. This wraps around that.
func (p *sourceParser) consumeCaveatTypeIdentifier() (string, bool) {
if ok := p.tryConsumeKeyword("any"); ok {
return "any", true
}

identifier, ok := p.tryConsume(lexer.TokenTypeIdentifier)
if !ok {
p.emitErrorf("Expected keyword \"any\" or a valid identifier, found token %v", p.currentToken.Kind)
return "", false
}
return identifier.Value, true
}

// consumeDefinition attempts to consume a single schema definition.
// ```definition somedef { ... }```
func (p *sourceParser) consumeDefinition() AstNode {
Expand Down Expand Up @@ -483,17 +498,11 @@ func (p *sourceParser) tryConsumeArrowExpression() (AstNode, bool) {
rightNodeBuilder := func(leftNode AstNode, operatorToken lexer.Lexeme) (AstNode, bool) {
// Check for an arrow function.
if operatorToken.Kind == lexer.TokenTypePeriod {
functionName, ok := p.consumeIdentifier()
functionName, ok := p.consumeKeywords("any", "all")
if !ok {
return nil, false
}

// TODO(jschorr): Change to keywords in schema v2.
if functionName != "any" && functionName != "all" {
p.emitErrorf("Expected 'any' or 'all' for arrow function, found: %s", functionName)
return nil, false
}

if _, ok := p.consume(lexer.TokenTypeLeftParen); !ok {
return nil, false
}
Expand Down
22 changes: 22 additions & 0 deletions pkg/composableschemadsl/parser/parser_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package parser

import (
"fmt"
"strings"

"github.com/authzed/spicedb/pkg/composableschemadsl/dslshape"
"github.com/authzed/spicedb/pkg/composableschemadsl/input"
Expand Down Expand Up @@ -197,6 +198,27 @@ func (p *sourceParser) tryConsumeKeyword(keyword string) bool {
return true
}

// consumeKeywords consumes any of a set of keywords or adds an error node
func (p *sourceParser) consumeKeywords(keywords ...string) (string, bool) {
keyword, ok := p.tryConsumeKeywords(keywords...)
if !ok {
p.emitErrorf("Expected one of keywords %s; found token %v", strings.Join(keywords, ", "), p.currentToken.Kind)
return "", false
}
return keyword, true
}

// tryConsumeKeyword attempts to consume one of a set of expected keyword tokens.
func (p *sourceParser) tryConsumeKeywords(keywords ...string) (string, bool) {
for _, keyword := range keywords {
if p.isKeyword(keyword) {
p.consumeToken()
return keyword, true
}
}
return "", false
}

// cosumeIdentifier consumes an expected identifier token or adds an error node.
func (p *sourceParser) consumeIdentifier() (string, bool) {
token, ok := p.tryConsume(lexer.TokenTypeIdentifier)
Expand Down
38 changes: 19 additions & 19 deletions pkg/composableschemadsl/parser/tests/arrowillegalfunc.zed.expected
Original file line number Diff line number Diff line change
@@ -1,47 +1,47 @@
NodeTypeFile
end-rune = 48
end-rune = 45
input-source = arrow illegal function test
start-rune = 0
child-node =>
NodeTypeDefinition
definition-name = resource
end-rune = 48
end-rune = 45
input-source = arrow illegal function test
start-rune = 0
child-node =>
NodeTypePermission
end-rune = 48
end-rune = 45
input-source = arrow illegal function test
relation-name = view
start-rune = 26
child-node =>
NodeTypeError
end-rune = 48
error-message = Expected 'any' or 'all' for arrow function, found: foo
error-source = (
end-rune = 45
error-message = Expected one of keywords any, all; found token TokenTypeIdentifier
error-source = foo
input-source = arrow illegal function test
start-rune = 49
start-rune = 46
NodeTypeError
end-rune = 48
error-message = Expected right hand expression, found: TokenTypeLeftParen
error-source = (
end-rune = 45
error-message = Expected right hand expression, found: TokenTypeIdentifier
error-source = foo
input-source = arrow illegal function test
start-rune = 49
start-rune = 46
compute-expression =>
NodeTypeIdentifier
end-rune = 44
identifier-value = a
input-source = arrow illegal function test
start-rune = 44
NodeTypeError
end-rune = 48
error-message = Expected end of statement or definition, found: TokenTypeLeftParen
error-source = (
end-rune = 45
error-message = Expected end of statement or definition, found: TokenTypeIdentifier
error-source = foo
input-source = arrow illegal function test
start-rune = 49
start-rune = 46
NodeTypeError
end-rune = 48
error-message = Unexpected token at root level: TokenTypeLeftParen
error-source = (
end-rune = 45
error-message = Unexpected token at root level: TokenTypeIdentifier
error-source = foo
input-source = arrow illegal function test
start-rune = 49
start-rune = 46

0 comments on commit 45c8f10

Please sign in to comment.