Skip to content

Commit

Permalink
Merge pull request #2103 from authzed/local-imports
Browse files Browse the repository at this point in the history
Implement parsing of local imports
  • Loading branch information
tstirrat15 authored Oct 28, 2024
2 parents 9692e58 + f54b2c3 commit 98a8473
Show file tree
Hide file tree
Showing 20 changed files with 668 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
with:
distribution: "goreleaser-pro"
version: "2.3.2"
args: "release --clean --split --snapshot --single-target --skip=chocolatey"
args: "release --clean --split --snapshot --single-target"
env:
GORELEASER_KEY: "${{ secrets.GORELEASER_KEY }}"
- name: "Obtain container image to scan"
Expand Down
10 changes: 10 additions & 0 deletions pkg/composableschemadsl/dslshape/dslshape.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ const (
NodeTypeNilExpression // A nil keyword

NodeTypeCaveatTypeReference // A type reference for a caveat parameter.

NodeTypeImport
)

const (
Expand Down Expand Up @@ -188,4 +190,12 @@ const (
//
NodeExpressionPredicateLeftExpr = "left-expr"
NodeExpressionPredicateRightExpr = "right-expr"

//
// NodeTypeImport
//
// TODO: still need to figure out what form this should take - full path? relative path?
NodeImportPredicateSource = "import-source"
NodeImportPredicatePathSegment = "path-segment"
NodeImportPredicateDefinitionName = "imported-definition"
)

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pkg/composableschemadsl/lexer/lex_def.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ var keywords = map[string]struct{}{
"permission": {},
"nil": {},
"with": {},
"from": {},
"import": {},
}

// IsKeyword returns whether the specified input string is a reserved keyword.
Expand Down
72 changes: 72 additions & 0 deletions pkg/composableschemadsl/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ Loop:
case p.isKeyword("caveat"):
rootNode.Connect(dslshape.NodePredicateChild, p.consumeCaveat())

case p.isKeyword("from"):
rootNode.Connect(dslshape.NodePredicateChild, p.consumeImport())

default:
p.emitErrorf("Unexpected token at root level: %v", p.currentToken.Kind)
break Loop
Expand Down Expand Up @@ -574,6 +577,20 @@ func (p *sourceParser) tryConsumeIdentifierLiteral() (AstNode, bool) {
return identNode, true
}

// consumeIdentifierLiteral is similar to the above, but attempts and errors
// rather than checking the token type beforehand
func (p *sourceParser) consumeIdentifierLiteral() (AstNode, bool) {
identNode := p.startNode(dslshape.NodeTypeIdentifier)
defer p.mustFinishNode()

identifier, ok := p.consumeIdentifier()
if !ok {
return identNode, false
}
identNode.MustDecorate(dslshape.NodeIdentiferPredicateValue, identifier)
return identNode, true
}

func (p *sourceParser) tryConsumeNilExpression() (AstNode, bool) {
if !p.isKeyword("nil") {
return nil, false
Expand All @@ -584,3 +601,58 @@ func (p *sourceParser) tryConsumeNilExpression() (AstNode, bool) {
defer p.mustFinishNode()
return node, true
}

func (p *sourceParser) consumeImport() AstNode {
importNode := p.startNode(dslshape.NodeTypeImport)
defer p.mustFinishNode()

// from ...
// NOTE: error handling isn't necessary here because this function is only
// invoked if the `from` keyword is found in the function above.
p.consumeKeyword("from")

// Consume alternating periods and identifiers
for {
if _, ok := p.consume(lexer.TokenTypePeriod); !ok {
return importNode
}

segmentNode, ok := p.consumeIdentifierLiteral()
// We connect the node so that the error information is retained, then break the loop
// so that we aren't continuing to attempt to consume.
importNode.Connect(dslshape.NodeImportPredicatePathSegment, segmentNode)
if !ok {
break
}

if !p.isToken(lexer.TokenTypePeriod) {
// If we don't have a period as our next token, we move
// to the next step of parsing.
break
}
}

if ok := p.consumeKeyword("import"); !ok {
return importNode
}

// Consume alternating identifiers and commas until we reach the end of the import statement
for {
definitionNode, ok := p.consumeIdentifierLiteral()
// We connect the node so that the error information is retained, then break the loop
// so that we aren't continuing to attempt to consume.
importNode.Connect(dslshape.NodeImportPredicateDefinitionName, definitionNode)
if !ok {
break
}

if _, ok := p.tryConsumeStatementTerminator(); ok {
break
}
if _, ok := p.consume(lexer.TokenTypeComma); !ok {
return importNode
}
}

return importNode
}
7 changes: 7 additions & 0 deletions pkg/composableschemadsl/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ func TestParser(t *testing.T) {
{"arrow illegal operations test", "arrowillegalops"},
{"arrow illegal function test", "arrowillegalfunc"},
{"caveat with keyword parameter test", "caveatwithkeywordparam"},
{"local imports test", "localimport"},
{"local imports with keyword in import path test", "localimport_import_path_with_keyword"},
{"local imports with keyword in identifiers test", "localimport_keyword_in_identifiers"},
{"local imports with malformed identifiers set test", "localimport_malformed_identifier_set"},
{"local imports with malformed import path test", "localimport_malformed_import_path"},
{"local imports with path missing leading period test", "localimport_path_missing_leading_period"},
{"local imports with typo in import separator test", "localimport_typo_in_import_separator"},
}

for _, test := range parserTests {
Expand Down
7 changes: 7 additions & 0 deletions pkg/composableschemadsl/parser/tests/localimport.zed
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from .path.to.user import user, persona

definition resource {
relation user: user
relation persona: persona
permission view = user + persona
}
96 changes: 96 additions & 0 deletions pkg/composableschemadsl/parser/tests/localimport.zed.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
NodeTypeFile
end-rune = 155
input-source = local imports test
start-rune = 0
child-node =>
NodeTypeImport
end-rune = 39
input-source = local imports test
start-rune = 0
imported-definition =>
NodeTypeIdentifier
end-rune = 29
identifier-value = user
input-source = local imports test
start-rune = 26
NodeTypeIdentifier
end-rune = 38
identifier-value = persona
input-source = local imports test
start-rune = 32
path-segment =>
NodeTypeIdentifier
end-rune = 9
identifier-value = path
input-source = local imports test
start-rune = 6
NodeTypeIdentifier
end-rune = 12
identifier-value = to
input-source = local imports test
start-rune = 11
NodeTypeIdentifier
end-rune = 17
identifier-value = user
input-source = local imports test
start-rune = 14
NodeTypeDefinition
definition-name = resource
end-rune = 154
input-source = local imports test
start-rune = 41
child-node =>
NodeTypeRelation
end-rune = 85
input-source = local imports test
relation-name = user
start-rune = 67
allowed-types =>
NodeTypeTypeReference
end-rune = 85
input-source = local imports test
start-rune = 82
type-ref-type =>
NodeTypeSpecificTypeReference
end-rune = 85
input-source = local imports test
start-rune = 82
type-name = user
NodeTypeRelation
end-rune = 115
input-source = local imports test
relation-name = persona
start-rune = 91
allowed-types =>
NodeTypeTypeReference
end-rune = 115
input-source = local imports test
start-rune = 109
type-ref-type =>
NodeTypeSpecificTypeReference
end-rune = 115
input-source = local imports test
start-rune = 109
type-name = persona
NodeTypePermission
end-rune = 152
input-source = local imports test
relation-name = view
start-rune = 121
compute-expression =>
NodeTypeUnionExpression
end-rune = 152
input-source = local imports test
start-rune = 139
left-expr =>
NodeTypeIdentifier
end-rune = 142
identifier-value = user
input-source = local imports test
start-rune = 139
right-expr =>
NodeTypeIdentifier
end-rune = 152
identifier-value = persona
input-source = local imports test
start-rune = 146
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from .path.definition.user import user, persona

definition resource {
relation user: user
relation persona: persona
permission view = user + persona
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
NodeTypeFile
end-rune = 20
input-source = local imports with keyword in import path test
start-rune = 0
child-node =>
NodeTypeImport
end-rune = 10
input-source = local imports with keyword in import path test
start-rune = 0
child-node =>
NodeTypeError
end-rune = 10
error-message = Expected keyword import, found token TokenTypeKeyword
error-source = definition
input-source = local imports with keyword in import path test
start-rune = 11
path-segment =>
NodeTypeIdentifier
end-rune = 9
identifier-value = path
input-source = local imports with keyword in import path test
start-rune = 6
NodeTypeIdentifier
end-rune = 10
input-source = local imports with keyword in import path test
start-rune = 11
child-node =>
NodeTypeError
end-rune = 10
error-message = Expected identifier, found token TokenTypeKeyword
error-source = definition
input-source = local imports with keyword in import path test
start-rune = 11
NodeTypeDefinition
end-rune = 20
input-source = local imports with keyword in import path test
start-rune = 11
child-node =>
NodeTypeError
end-rune = 20
error-message = Expected identifier, found token TokenTypePeriod
error-source = .
input-source = local imports with keyword in import path test
start-rune = 21
NodeTypeError
end-rune = 20
error-message = Unexpected token at root level: TokenTypePeriod
error-source = .
input-source = local imports with keyword in import path test
start-rune = 21
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from .path.to.user import user, caveat

definition resource {
relation user: user
relation persona: persona
permission view = user + persona
}
Loading

0 comments on commit 98a8473

Please sign in to comment.