Skip to content

Commit

Permalink
biscuit: update parser to conform to the EBNF grammar
Browse files Browse the repository at this point in the history
The EBNF grammar defined in biscuit-auth/biscuit#82
makes the parsing rules a bit more explicit:

- fact names can only start with a letter
  • Loading branch information
divarvel committed Oct 31, 2021
1 parent 90fb058 commit 2eb00fc
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 8 deletions.
23 changes: 15 additions & 8 deletions biscuit/src/Auth/Biscuit/Datalog/Parser.hs
Expand Up @@ -38,14 +38,15 @@ module Auth.Biscuit.Datalog.Parser
import Control.Applicative (liftA2, optional, (<|>))
import qualified Control.Monad.Combinators.Expr as Expr
import Data.Attoparsec.Text
import qualified Data.Attoparsec.Text as A
import Data.ByteString (ByteString)
import Data.ByteString.Base16 as Hex
import Data.Char (isAlphaNum, isSpace)
import Data.Char (isAlphaNum, isLetter, isSpace)
import Data.Either (partitionEithers)
import Data.Foldable (fold)
import Data.Functor (void, ($>))
import qualified Data.Set as Set
import Data.Text (Text, pack, unpack)
import Data.Text (Text, pack, singleton, unpack)
import Data.Text.Encoding (encodeUtf8)
import Data.Time (UTCTime, defaultTimeLocale,
parseTimeM)
Expand Down Expand Up @@ -85,9 +86,15 @@ type HasTermParsers inSet pof ctx =
)
type HasParsers pof ctx = HasTermParsers 'NotWithinSet pof ctx

-- | Parser for an identifier (predicate name, variable name, …)
nameParser :: Parser Text
nameParser = takeWhile1 $ \c -> c == '_' || c == ':' || isAlphaNum c
-- | Parser for a datalog predicate name
predicateNameParser :: Parser Text
predicateNameParser = do
first <- satisfy isLetter
rest <- A.takeWhile $ \c -> c == '_' || c == ':' || isAlphaNum c
pure $ singleton first <> rest

variableNameParser :: Parser Text
variableNameParser = char '$' *> takeWhile1 (\c -> c == '_' || c == ':' || isAlphaNum c)

delimited :: Parser x
-> Parser y
Expand All @@ -109,7 +116,7 @@ commaList0 p =
predicateParser :: HasParsers pof ctx => Parser (Predicate' pof ctx)
predicateParser = do
skipSpace
name <- nameParser
name <- predicateNameParser
skipSpace
terms <- parens (commaList termParser)
pure Predicate{name,terms}
Expand Down Expand Up @@ -240,7 +247,7 @@ termParser :: forall inSet pof ctx
=> Parser (Term' inSet pof ctx)
termParser = skipSpace *> choice
[ Antiquote <$> ifPresent "slice" (Slice <$> (string "${" *> many1 letter <* char '}'))
, Variable <$> ifPresent "var" (char '$' *> nameParser)
, Variable <$> ifPresent "var" variableNameParser
, TermSet <$> parseSet @inSet @ctx
, LBytes <$> hexBsParser
, LDate <$> rfc3339DateParser
Expand All @@ -256,7 +263,7 @@ termParser = skipSpace *> choice
ruleHeadParser :: HasParsers 'InPredicate ctx => Parser (Predicate' 'InPredicate ctx)
ruleHeadParser = do
skipSpace
name <- nameParser
name <- predicateNameParser
skipSpace
terms <- parens (commaList0 termParser)
pure Predicate{name,terms}
Expand Down
6 changes: 6 additions & 0 deletions biscuit/test/Spec/Parser.hs
Expand Up @@ -43,6 +43,7 @@ specs :: TestTree
specs = testGroup "datalog parser"
[ factWithDate
, simpleFact
, oneLetterFact
, simpleRule
, multilineRule
, termsGroup
Expand Down Expand Up @@ -82,6 +83,11 @@ simpleFact = testCase "Parse simple fact" $
parsePredicate "right(\"file1\", \"read\")" @?=
Right (Predicate "right" [LString "file1", LString "read"])

oneLetterFact :: TestTree
oneLetterFact = testCase "Parse one-letter fact" $
parsePredicate "a(12)" @?=
Right (Predicate "a" [LInteger 12])

factWithDate :: TestTree
factWithDate = testCase "Parse fact containing a date" $
parsePredicate "date(2019-12-02T13:49:53Z)" @?=
Expand Down

0 comments on commit 2eb00fc

Please sign in to comment.