@@ -17,6 +17,8 @@ module Database.Persist.Quasi.Internal.ModelParser
1717 , EntityField (.. )
1818 , entityField
1919 , entityFieldContent
20+ , FieldName (.. )
21+ , fieldName
2022 , ParsedEntityDef (.. )
2123 , parseSource
2224 , memberEntityFields
@@ -257,7 +259,7 @@ data SourceLoc = SourceLoc
257259 }
258260 deriving (Show , Lift )
259261
260- -- | An attribute of an entity field definition.
262+ -- | An attribute of an entity field definition or a directive .
261263--
262264-- @since 2.17.1.0
263265data Attribute
@@ -268,16 +270,6 @@ data Attribute
268270 Quotation Text
269271 deriving (Eq , Ord , Show )
270272
271- -- | An argument to an entity field directive.
272- --
273- -- @since 2.17.1.0
274- data DirectiveArgument
275- = -- This is too unstructured. We should rework directive parsing and make this smarter.
276- DText Text
277- | -- | Quoted directive arguments are deprecated since 2.17.1.0.
278- DQuotation Text
279- deriving (Eq , Ord , Show )
280-
281273-- | The name of an entity block or extra block.
282274--
283275-- @since 2.17.1.0
@@ -308,7 +300,9 @@ attributeContent = \case
308300--
309301-- @since 2.17.1.0
310302directiveContent :: Directive -> [Text ]
311- directiveContent d = directiveArgumentContent <$> directiveArguments d
303+ directiveContent d =
304+ [directiveNameContent $ directiveName d]
305+ <> (attributeContent <$> directiveAttributes d)
312306
313307entityFieldContent :: EntityField -> [Text ]
314308entityFieldContent f =
@@ -320,6 +314,9 @@ entityFieldContent f =
320314blockKeyContent :: BlockKey -> Text
321315blockKeyContent (BlockKey t) = t
322316
317+ directiveNameContent :: DirectiveName -> Text
318+ directiveNameContent (DirectiveName t) = t
319+
323320-- | Generates the field name of an EntityField, accompanied by
324321-- its strictness sigil, if one is present.
325322-- This is only needed temporarily, and can eventually be refactored away.
@@ -342,7 +339,7 @@ commentContent = \case
342339 DocComment s -> s
343340
344341quotedAttributeErrorMessage :: String
345- quotedAttributeErrorMessage = " Unexpected quotation mark in entity field attribute"
342+ quotedAttributeErrorMessage = " Unexpected quotation mark in field or directive attribute"
346343
347344attribute :: Parser Attribute
348345attribute = do
@@ -501,13 +498,18 @@ assignment = label "assignment expression" $ do
501498 [ quotation
502499 , sqlLiteral
503500 , parentheticalInner
501+ , try sqlFunctionApplication
504502 , some $ contentChar <|> char ' (' <|> char ' )'
505503 ]
506504 pure $ Assignment (Text. pack lhs) (Text. pack rhs)
507505 where
508506 parentheticalInner = do
509507 str <- parenthetical'
510508 pure . init . drop 1 $ str
509+ sqlFunctionApplication = do
510+ fn <- some contentChar
511+ argString <- parentheticalInner
512+ pure $ mconcat [fn, " (" , argString, " )" ]
511513
512514sqlTypeName :: Parser String
513515sqlTypeName =
@@ -565,15 +567,18 @@ fieldStrictness = label "strictness sigil" $ do
565567fieldName :: Parser FieldName
566568fieldName = label " field name" $ do
567569 fl <- lowerChar
568- rl <- many alphaNumChar
570+ rl <- many fieldNameChar
569571 pure . FieldName . Text. pack $ fl : rl
572+ where
573+ fieldNameChar =
574+ choice
575+ [ alphaNumChar
576+ , char ' _'
577+ ]
570578
571579ptext :: Parser Attribute
572580ptext = label " plain attribute" $ do
573- str <- L. lexeme spaceConsumer $ do
574- first <- alphaNumChar
575- rest <- many contentChar
576- pure (first : rest)
581+ str <- L. lexeme spaceConsumer $ some contentChar
577582 pure . PText . Text. pack $ str
578583
579584data ParsedEntityDef = ParsedEntityDef
@@ -659,7 +664,10 @@ data FieldStrictness = Strict | Lazy
659664 deriving (Show )
660665
661666newtype FieldName = FieldName Text
662- deriving (Show )
667+ deriving (Show , Eq )
668+
669+ newtype DirectiveName = DirectiveName Text
670+ deriving (Show , Eq )
663671
664672data EntityField = EntityField
665673 { entityFieldDocCommentBlock :: Maybe DocCommentBlock
@@ -673,7 +681,8 @@ data EntityField = EntityField
673681
674682data Directive = Directive
675683 { directiveDocCommentBlock :: Maybe DocCommentBlock
676- , directiveArguments :: [DirectiveArgument ]
684+ , directiveName :: DirectiveName
685+ , directiveAttributes :: [Attribute ]
677686 , directivePos :: SourcePos
678687 }
679688 deriving (Show )
@@ -829,92 +838,34 @@ entityField = do
829838 , entityFieldPos = pos
830839 }
831840
832- directiveName :: Parser String
833- directiveName =
841+ directiveNameP :: Parser DirectiveName
842+ directiveNameP =
834843 label " directive name" $
835- choice
836- [ string " deriving"
837- , directiveName'
838- ]
844+ DirectiveName . Text. pack
845+ <$> choice
846+ [ string " deriving"
847+ , directiveName'
848+ ]
839849 where
840850 directiveName' = do
841851 fl <- upperChar
842852 rl <- many alphaNumChar
843853 pure (fl : rl)
844854
845- quotedArgumentErrorMessage :: String
846- quotedArgumentErrorMessage = " Unexpected quotation mark in directive argument"
847-
848- -- Parses an argument to an entity definition directive. It's somewhat naive about it,
849- -- and we should refine this in the future.
850- directiveArgument :: Parser DirectiveArgument
851- directiveArgument = do
852- quotedArgumentErrorLevel <- asks psQuotedArgumentErrorLevel
853- tryOrReport
854- quotedArgumentErrorLevel
855- " Quoted directive arguments are deprecated since 2.17.1.0, and will be removed in or after 2.18.0.0"
856- isQuotedArgumentError
857- directiveArgument'
858- (DQuotation . Text. pack <$> quotation)
859- where
860- isQuotedArgumentError (FancyError _ s) = s == Set. singleton (ErrorFail quotedArgumentErrorMessage)
861- isQuotedArgumentError _ = False
862- directiveArgument' = do
863- q <- lookAhead (optional $ char ' "' )
864- case q of
865- Just _ -> fail quotedArgumentErrorMessage
866- Nothing ->
867- choice
868- [ DText . Text. pack <$> parenthetical'
869- , DText . Text. pack <$> some directiveArgumentChar
870- ]
871- -- This big random-looking character class is a sign that the parser is too lax.
872- -- When we improve directive parsing, we'll eliminate this.
873- -- Note that this character class is not identical to the class parsed by the top-level function `contentChar`.
874- directiveArgumentChar =
875- choice
876- [ alphaNumChar
877- , char ' .'
878- , char ' ['
879- , char ' ]'
880- , char ' _'
881- , char ' \' '
882- , char ' !'
883- , char ' ~'
884- , char ' -'
885- , char ' :'
886- , char ' ,'
887- , char ' ='
888- , do
889- backslash <- char ' \\ '
890- nextChar <- lookAhead anySingle
891- if nextChar == ' (' || nextChar == ' )'
892- then single nextChar
893- else pure backslash
894- ]
895-
896- -- | Converts a directive argument into a Text representation for second-stage
897- -- parsing or presentation to the user
898- --
899- -- @since 2.17.1.0
900- directiveArgumentContent :: DirectiveArgument -> Text
901- directiveArgumentContent = \ case
902- DText t -> t
903- DQuotation t -> t
904-
905855directive :: Parser Member
906856directive = do
907857 dcb <- getDcb
908858 pos <- getSourcePos
909- dn <- L. lexeme spaceConsumer directiveName
910- args <- some $ L. lexeme spaceConsumer directiveArgument
859+ dn <- L. lexeme spaceConsumer directiveNameP
860+ args <- some $ L. lexeme spaceConsumer attribute
911861 _ <- setLastDocumentablePosition
912862 lookAhead (void newline <|> eof)
913863 pure $
914864 MemberDirective
915865 Directive
916866 { directiveDocCommentBlock = dcb
917- , directiveArguments = DText (Text. pack dn) : args
867+ , directiveName = dn
868+ , directiveAttributes = args
918869 , directivePos = pos
919870 }
920871
0 commit comments