diff --git a/library/PostgresqlSyntax/Parsing.hs b/library/PostgresqlSyntax/Parsing.hs index 94b5d03..376a166 100644 --- a/library/PostgresqlSyntax/Parsing.hs +++ b/library/PostgresqlSyntax/Parsing.hs @@ -60,6 +60,7 @@ import qualified Text.Megaparsec.Char.Lexer as MegaparsecLexer import qualified PostgresqlSyntax.KeywordSet as KeywordSet import qualified PostgresqlSyntax.Predicate as Predicate import qualified PostgresqlSyntax.Validation as Validation +import qualified Data.Char as Char import qualified Data.Text as Text import qualified Data.List.NonEmpty as NonEmpty import qualified Text.Builder as TextBuilder @@ -357,7 +358,7 @@ selectWithParens = inParens selectNoParens = withSelectNoParens <|> simpleSelectNoParens sharedSelectNoParens _with = do - _select <- selectClause + _select <- selectClauseNoParens _sort <- optional (space1 *> sortClause) _limit <- optional (space1 *> selectLimit) _forLocking <- optional (space1 *> forLockingClause) @@ -383,6 +384,10 @@ selectClause = suffixRec base suffix where base = asum [Right <$> selectWithParens, Left <$> baseSimpleSelect] suffix a = Left <$> extensionSimpleSelect a +selectClauseNoParens = suffixRec base suffix where + base = Left <$> baseSimpleSelect + suffix a = Left <$> extensionSimpleSelect a + baseSimpleSelect = asum [ do keyword "select" @@ -1062,96 +1067,106 @@ customizedAExpr cExpr = suffixRec base suffix where base = asum [ DefaultAExpr <$ keyword "default" , UniqueAExpr <$> (keyword "unique" *> space1 *> selectWithParens) - , OverlapsAExpr - <$> wrapToHead row - <*> (space1 *> keyword "overlaps" *> space1 *> endHead *> row) , qualOpExpr aExpr PrefixQualOpAExpr , PlusAExpr <$> plusedExpr aExpr , MinusAExpr <$> minusedExpr aExpr , NotAExpr <$> (keyword "not" *> space1 *> aExpr) - , CExprAExpr <$> cExpr + , char '(' *> space *> asum + [ CExprAExpr <$> cExprContParenNoExpr + , do + a <- aExpr + endHead + asum + [ CExprAExpr <$> cExprContParenExpr a + , do + b <- wrapToHead $ ImplicitRowRow <$> implicitRowCont a + space1 + keyword "overlaps" + space1 + endHead + c <- row + return $ OverlapsAExpr b c + ] + ] + , CExprAExpr <$> cExprNoCont ] suffix a = asum - [ do - space1 - b <- wrapToHead subqueryOp - space1 - c <- wrapToHead subType - space - d <- Left <$> wrapToHead selectWithParens <|> Right <$> inParens aExpr - return (SubqueryAExpr a b c d) - , typecastExpr a TypecastAExpr - , CollateAExpr a - <$> (space1 *> keyword "collate" *> space1 *> endHead *> anyName) - , AtTimeZoneAExpr a - <$> (space1 *> keyphrase "at time zone" *> space1 *> endHead *> aExpr) - , symbolicBinOpExpr a aExpr SymbolicBinOpAExpr + [ typecastExpr a TypecastAExpr + , symbolicBinOpExpr a base SymbolicBinOpAExpr , SuffixQualOpAExpr a <$> (space *> qualOp) - , AndAExpr a <$> (space1 *> keyword "and" *> space1 *> endHead *> aExpr) - , OrAExpr a <$> (space1 *> keyword "or" *> space1 *> endHead *> aExpr) - , do - space1 - b <- trueIfPresent (keyword "not" *> space1) - c <- asum - [ LikeVerbalExprBinOp <$ keyword "like" - , IlikeVerbalExprBinOp <$ keyword "ilike" - , SimilarToVerbalExprBinOp <$ keyphrase "similar to" - ] - space1 - endHead - d <- aExpr - e <- optional (space1 *> keyword "escape" *> space1 *> endHead *> aExpr) - return (VerbalExprBinOpAExpr a b c d e) - , do - space1 - keyword "is" - space1 - endHead - b <- trueIfPresent (keyword "not" *> space1) - c <- asum - [ NullAExprReversableOp <$ keyword "null" - , TrueAExprReversableOp <$ keyword "true" - , FalseAExprReversableOp <$ keyword "false" - , UnknownAExprReversableOp <$ keyword "unknown" - , DistinctFromAExprReversableOp - <$> ( keyword "distinct" - *> space1 - *> keyword "from" - *> space1 - *> endHead - *> aExpr - ) - , OfAExprReversableOp - <$> (keyword "of" *> space1 *> endHead *> inParens typeList) - , DocumentAExprReversableOp <$ keyword "document" - ] - return (ReversableOpAExpr a b c) - , do - space1 - b <- trueIfPresent (keyword "not" *> space1) - keyword "between" - space1 - endHead - c <- asum - [ BetweenSymmetricAExprReversableOp <$ (keyword "symmetric" *> space1) - , BetweenAExprReversableOp True <$ (keyword "asymmetric" *> space1) - , pure (BetweenAExprReversableOp False) - ] - d <- bExpr - space1 - keyword "and" - space1 - e <- aExpr - return (ReversableOpAExpr a b (c d e)) - , do - space1 - b <- trueIfPresent (keyword "not" *> space1) - keyword "in" - space - c <- InAExprReversableOp <$> inExpr - return (ReversableOpAExpr a b c) - , IsnullAExpr a <$ (space1 *> keyword "isnull") - , NotnullAExpr a <$ (space1 *> keyword "notnull") + , space1 *> asum + [ do + b <- wrapToHead subqueryOp + space1 + c <- wrapToHead subType + space + d <- Left <$> wrapToHead selectWithParens <|> Right <$> inParens aExpr + return (SubqueryAExpr a b c d) + , CollateAExpr a <$> (keyword "collate" *> space1 *> endHead *> anyName) + , AtTimeZoneAExpr a + <$> (keyphrase "at time zone" *> space1 *> endHead *> aExpr) + , AndAExpr a <$> (keyword "and" *> space1 *> endHead *> aExpr) + , OrAExpr a <$> (keyword "or" *> space1 *> endHead *> aExpr) + , do + b <- trueIfPresent (keyword "not" *> space1) + c <- asum + [ LikeVerbalExprBinOp <$ keyword "like" + , IlikeVerbalExprBinOp <$ keyword "ilike" + , SimilarToVerbalExprBinOp <$ keyphrase "similar to" + ] + space1 + endHead + d <- aExpr + e <- optional (space1 *> keyword "escape" *> space1 *> endHead *> aExpr) + return (VerbalExprBinOpAExpr a b c d e) + , do + keyword "is" + space1 + endHead + b <- trueIfPresent (keyword "not" *> space1) + c <- asum + [ NullAExprReversableOp <$ keyword "null" + , TrueAExprReversableOp <$ keyword "true" + , FalseAExprReversableOp <$ keyword "false" + , UnknownAExprReversableOp <$ keyword "unknown" + , DistinctFromAExprReversableOp + <$> ( keyword "distinct" + *> space1 + *> keyword "from" + *> space1 + *> endHead + *> aExpr + ) + , OfAExprReversableOp + <$> (keyword "of" *> space1 *> endHead *> inParens typeList) + , DocumentAExprReversableOp <$ keyword "document" + ] + return (ReversableOpAExpr a b c) + , do + b <- trueIfPresent (keyword "not" *> space1) + keyword "between" + space1 + endHead + c <- asum + [ BetweenSymmetricAExprReversableOp <$ (keyword "symmetric" *> space1) + , BetweenAExprReversableOp True <$ (keyword "asymmetric" *> space1) + , pure (BetweenAExprReversableOp False) + ] + d <- bExpr + space1 + keyword "and" + space1 + e <- aExpr + return (ReversableOpAExpr a b (c d e)) + , do + b <- trueIfPresent (keyword "not" *> space1) + keyword "in" + space + c <- InAExprReversableOp <$> inExpr + return (ReversableOpAExpr a b c) + , IsnullAExpr a <$ (keyword "isnull") + , NotnullAExpr a <$ (keyword "notnull") + ] ] bExpr = customizedBExpr cExpr @@ -1167,7 +1182,7 @@ customizedBExpr cExpr = suffixRec base suffix where ] suffix a = asum [ typecastExpr a TypecastBExpr - , symbolicBinOpExpr a bExpr SymbolicBinOpBExpr + , symbolicBinOpExpr a base SymbolicBinOpBExpr , do space1 keyword "is" @@ -1184,13 +1199,29 @@ customizedBExpr cExpr = suffixRec base suffix where return (IsOpBExpr a b c) ] -cExpr = customizedCExpr columnref +cExpr :: HeadedParsec Void Text CExpr +cExpr = asum [cExprNoCont, char '(' *> space *> cExprContParen] + +cExprNoCont = asum + [ cExprCommon + , FuncCExpr <$> funcExprNoCont + , do + a <- colId + endHead + asum [FuncCExpr <$> funcExprCont a, ColumnrefCExpr <$> columnrefCont a] + ] customizedCExpr columnref = asum + [ cExprCommon + , char '(' *> space *> cExprContParen + , FuncCExpr <$> funcExpr + , ColumnrefCExpr <$> columnref + ] + +cExprCommon = asum [ ParamCExpr <$> (char '$' *> decimal <* endHead) <*> optional (space *> indirection) , CaseCExpr <$> caseExpr - , ImplicitRowCExpr <$> implicitRow , ExplicitRowCExpr <$> explicitRow , inParensWithClause (keyword "grouping") (GroupingCExpr <$> sep1 commaSeparator aExpr) @@ -1202,19 +1233,34 @@ customizedCExpr columnref = asum [ fmap (fmap (ArrayCExpr . Right)) arrayExprCont , fmap (fmap (ArrayCExpr . Left) . pure) selectWithParens ] + , AexprConstCExpr <$> wrapToHead aexprConst + ] + +-- cExpr following a '(' +cExprContParen = asum + [ cExprContParenNoExpr , do - a <- wrapToHead selectWithParens + a <- aExpr endHead - b <- optional (space *> indirection) - return (SelectWithParensCExpr a b) - , InParensCExpr <$> (inParens aExpr <* endHead) <*> optional - (space *> indirection) - , AexprConstCExpr <$> wrapToHead aexprConst - , FuncCExpr <$> funcExpr - , ColumnrefCExpr <$> columnref + cExprContParenExpr a + ] + +cExprContParenNoExpr = do + a <- selectNoParens <* endHead <* space <* char ')' + b <- optional (space *> indirection) + return (SelectWithParensCExpr (NoParensSelectWithParens a) b) + +-- cExpr following a '(' plus an aExpr. +cExprContParenExpr :: AExpr -> HeadedParsec Void Text CExpr +cExprContParenExpr a = asum + [ ImplicitRowCExpr <$> (implicitRowCont a <* space <* char ')') + , InParensCExpr a <$> (space *> char ')' *> optional (space *> indirection)) ] +openParenAExpr :: HeadedParsec Void Text AExpr +openParenAExpr = char '(' *> space *> aExpr <* endHead + -- * ------------------------- @@ -1264,8 +1310,9 @@ row = ExplicitRowRow <$> explicitRow <|> ImplicitRowRow <$> implicitRow explicitRow = keyword "row" *> space *> inParens (optional exprList) -implicitRow = inParens $ do - a <- wrapToHead aExpr +implicitRow = inParens (wrapToHead aExpr >>= implicitRowCont) + +implicitRowCont a = do commaSeparator b <- exprList return $ case NonEmpty.consAndUnsnoc a b of @@ -1307,16 +1354,28 @@ elseClause = do space1 return a -funcExpr = asum +funcExpr :: HeadedParsec Void Text FuncExpr +funcExpr = funcExprNoCont <|> (wrapToHead colId >>= funcExprCont) + +funcExprNoCont :: HeadedParsec Void Text FuncExpr +funcExprNoCont = asum [ SubexprFuncExpr <$> funcExprCommonSubexpr , do - a <- funcApplication - endHead - b <- optional (space1 *> withinGroupClause) - c <- optional (space1 *> filterClause) - d <- optional (space1 *> overClause) - return (ApplicationFuncExpr a b c d) + app <- funcApplicationNoCont + appFuncExprCont app ] +funcExprCont :: Ident -> HeadedParsec Void Text FuncExpr +funcExprCont ident = do + a <- funcApplicationContIdent ident + endHead + appFuncExprCont a + +appFuncExprCont a = do + b <- optional (space1 *> withinGroupClause) + c <- optional (space1 *> filterClause) + d <- optional (space1 *> overClause) + return (ApplicationFuncExpr a b c d) + funcExprWindowless = asum [ CommonSubexprFuncExprWindowless <$> funcExprCommonSubexpr @@ -1469,8 +1528,29 @@ trimList = asum , ExprListTrimList <$> exprList ] -funcApplication = - inParensWithLabel FuncApplication funcName (optional funcApplicationParams) +funcApplication = colId >>= funcApplicationContIdent + +funcApplicationNoCont :: HeadedParsec Void Text FuncApplication +funcApplicationNoCont = do + label <- wrapToHead funcNameNoCont + funcApplicationContFuncName label + +funcApplicationContIdent :: Ident -> HeadedParsec Void Text FuncApplication +funcApplicationContIdent ident = do + label <- wrapToHead $ funcNameCont ident + funcApplicationContFuncName label + +funcApplicationContFuncName + :: FuncName -> HeadedParsec Void Text FuncApplication +funcApplicationContFuncName label = do + space + char '(' + endHead + space + content <- optional funcApplicationParams + space + char ')' + pure (FuncApplication label content) funcApplicationParams = asum [ starFuncApplicationParams @@ -2003,12 +2083,15 @@ qualifiedName = <$> colId columnref = customizedColumnref colId +columnrefCont = customizedColumnrefCont filteredColumnref _keywords = customizedColumnref (filteredColId _keywords) customizedColumnref colId = do a <- wrapToHead colId - endHead + customizedColumnrefCont a + +customizedColumnrefCont a = do b <- optional (space *> indirection) return (Columnref a b) @@ -2034,11 +2117,13 @@ func_name: | ColId indirection -} funcName = - IndirectedFuncName - <$> wrapToHead colId - <*> (space *> indirection) - <|> TypeFuncName - <$> typeFunctionName + (wrapToHead colId >>= funcNameCont) <|> TypeFuncName <$> typeFunctionName + +funcNameCont :: Ident -> HeadedParsec Void Text FuncName +funcNameCont a = IndirectedFuncName a <$> (space *> indirection) +funcNameNoCont :: HeadedParsec Void Text FuncName +funcNameNoCont = TypeFuncName <$> typeFunctionName + {- type_function_name: @@ -2122,7 +2207,14 @@ anyKeyword = parse $ Megaparsec.label "keyword" $ do return (Text.toLower (Text.cons _firstChar _remainder)) {-| Expected keyword -} -keyword a = mfilter (a ==) anyKeyword +-- keyword a = mfilter (a ==) anyKeyword +keyword a = parse $ Megaparsec.label "keyword" $ do + _firstChar <- Megaparsec.satisfy Predicate.firstIdentifierChar + guard (Char.toLower _firstChar == Text.head a) + _remainder <- Megaparsec.takeWhileP Nothing Predicate.notFirstIdentifierChar + let r = Text.toLower (Text.cons _firstChar _remainder) + guard (r == a) + return r {-| Consume a keyphrase, ignoring case and types of spaces between words.