From e3be820dfb4a4d433e0adc310c77750a8c418280 Mon Sep 17 00:00:00 2001 From: Jake Wheat Date: Fri, 13 Dec 2013 15:05:32 +0200 Subject: [PATCH] pretty printer, minor clean ups clean up some warnings update the cabal file add pretty printer add pretty printing tests --- Parser.lhs | 11 ++-- Pretty.lhs | 114 ++++++++++++++++++++++++++++++++++++++-- TODO | 19 +++---- Tests.lhs | 22 ++++++-- simple-sql-parser.cabal | 67 ++++++----------------- 5 files changed, 157 insertions(+), 76 deletions(-) diff --git a/Parser.lhs b/Parser.lhs index 28f6039..294644d 100644 --- a/Parser.lhs +++ b/Parser.lhs @@ -2,15 +2,12 @@ > module Parser (parseQueryExpr > ,parseScalarExpr -> ,ParseError(..)) where +> ,ParseError) where > import Text.Groom > import Text.Parsec > import Control.Monad.Identity > import Control.Applicative hiding (many, (<|>), optional) -> import Debug.Trace -> import Data.List -> import Text.Parsec.Expr > import qualified Language.Haskell.Exts.Syntax as HSE > import qualified Language.Haskell.Exts.Fixity as HSE > import Data.Maybe @@ -200,9 +197,9 @@ to be. > tref = choice [try (JoinQueryExpr <$> parens queryExpr) > ,JoinParens <$> parens tref > ,SimpleTableRef <$> identifierString] -> >>= optionSuffix join +> >>= optionSuffix pjoin > >>= optionSuffix alias -> join tref0 = +> pjoin tref0 = > choice > [try (keyword_ "natural") *> keyword_ "inner" > *> conditionlessSuffix tref0 Inner (Just JoinNatural) @@ -217,7 +214,7 @@ to be. > ,try (keyword_ "cross") > *> conditionlessSuffix tref0 Cross Nothing > ] -> >>= optionSuffix join +> >>= optionSuffix pjoin > outerJoinSuffix tref0 jt = > optional (keyword_ "outer") *> conditionSuffix tref0 jt > conditionSuffix tref0 jt = diff --git a/Pretty.lhs b/Pretty.lhs index ca2bc71..3838072 100644 --- a/Pretty.lhs +++ b/Pretty.lhs @@ -2,12 +2,118 @@ This is the pretty printer code which takes AST values and turns them back into SQL source text. It attempts to format the output nicely. -> module Pretty (pretty, prettyScalarExpr) where +> module Pretty (prettyQueryExpr, prettyScalarExpr) where > import Syntax +> import Text.PrettyPrint +> import Data.Maybe -> pretty :: QueryExpr -> String -> pretty = undefined +> prettyQueryExpr :: QueryExpr -> String +> prettyQueryExpr = render . queryExpr > prettyScalarExpr :: ScalarExpr -> String -> prettyScalarExpr = undefined +> prettyScalarExpr = render . scalarExpr + + += scalar expressions + +> scalarExpr :: ScalarExpr -> Doc +> scalarExpr (Literal s) = quotes $ text s +> scalarExpr (Identifier i) = text i +> scalarExpr (Identifier2 q i) = text q <> text "." <> text i +> scalarExpr Star = text "*" +> scalarExpr (Star2 q) = text q <> text "." <> text "*" + +> scalarExpr (App f es) = text f <> parens (commaSep (map scalarExpr es)) +> scalarExpr (Op f [e]) = text f <+> scalarExpr e +> scalarExpr (Op f [e0,e1]) = +> sep [scalarExpr e0, text f, scalarExpr e1] + +> scalarExpr (Op f es) = +> -- TODO: how to handle this? error or either seems poor +> text f <> parens (commaSep (map scalarExpr es)) + +> scalarExpr (Case t ws els) = +> sep [text "case" <+> (maybe empty scalarExpr t) +> ,nest 4 (sep ((map w ws) +> ++ maybeToList (fmap e els))) +> ,text "end"] +> where +> w (t0,t1) = sep [text "when" <+> scalarExpr t0 +> ,text "then" <+> scalarExpr t1] +> e el = text "else" <+> scalarExpr el +> scalarExpr (Parens e) = parens $ scalarExpr e + += query expressions + +> queryExpr :: QueryExpr -> Doc +> queryExpr (Select sl fr wh gb hv od) = +> sep [text "select" +> ,nest 4 $ sep [selectList sl] +> ,from fr +> ,whr wh +> ,grpBy gb +> ,having hv +> ,orderBy od] + +> selectList :: [(Maybe String, ScalarExpr)] -> Doc +> selectList is = commaSep $ map si is +> where +> si (al,e) = scalarExpr e <+> maybe empty alias al +> alias al = text "as" <+> text al + +> from :: [TableRef] -> Doc +> from [] = empty +> from ts = +> sep [text "from" +> ,nest 4 $ commaSep $ map tr ts] +> where +> tr (SimpleTableRef t) = text t +> tr (JoinAlias t a) = tr t <+> text "as" <+> text a +> tr (JoinParens t) = parens $ tr t +> tr (JoinQueryExpr q) = parens $ queryExpr q +> tr (JoinTableRef jt t0 t1 jc) = +> sep [tr t0 +> ,joinText jt jc +> ,tr t1 +> ,joinCond jc] +> joinText jt jc = +> sep [case jc of +> Just JoinNatural -> text "natural" +> _ -> empty +> ,case jt of +> Inner -> text "inner" +> JLeft -> text "left" +> JRight -> text "right" +> Full -> text "full" +> Cross -> text "cross" +> ,text "join"] +> joinCond (Just (JoinOn e)) = text "on" <+> scalarExpr e +> joinCond (Just (JoinUsing es)) = text "using" <+> parens (commaSep $ map text es) +> joinCond Nothing = empty +> joinCond (Just JoinNatural) = empty + +> whr :: Maybe ScalarExpr -> Doc +> whr = maybe empty +> (\w -> sep [text "where" +> ,nest 4 $ scalarExpr w]) + +> grpBy :: [ScalarExpr] -> Doc +> grpBy [] = empty +> grpBy gs = sep [text "group by" +> ,nest 4 $ commaSep $ map scalarExpr gs] + +> having :: Maybe ScalarExpr -> Doc +> having = maybe empty +> (\w -> sep [text "having" +> ,nest 4 $ scalarExpr w]) +> orderBy :: [ScalarExpr] -> Doc +> orderBy [] = empty +> orderBy os = sep [text "order by" +> ,nest 4 $ commaSep $ map scalarExpr os] + + += utils + +> commaSep :: [Doc] -> Doc +> commaSep ds = sep $ punctuate comma ds diff --git a/TODO b/TODO index 81039ea..9854ed6 100644 --- a/TODO +++ b/TODO @@ -1,24 +1,24 @@ -add tests -left factor parsing code +add tests to cabal -implement pretty printer - -reimplement the fixity thing natively +haddock dialect switching -fix lexing wrt suffixes + +left factor parsing code + +reimplement the fixity thing natively + +fix lexing wrt suffixes 1/2 done position annotation emacs parse error formatting -haddock = sql support -count(*) decimal literals, split string and number literals order by directions distinct/all @@ -59,12 +59,13 @@ group by (), grouping sets(), cube, rollup lateral corresponding named windows +table, values cte apply, pivot full tableref aliases collate - within group aggregate syntax + try to do full review of sql2003 query syntax maybe later: other dml \ No newline at end of file diff --git a/Tests.lhs b/Tests.lhs index ef8bf50..57529e4 100644 --- a/Tests.lhs +++ b/Tests.lhs @@ -1,5 +1,5 @@ -> module Tests (testData) where +> module Tests (testData, runTests) where > import Syntax > import Pretty @@ -239,12 +239,24 @@ > itemToTest (Group nm ts) = > H.TestLabel nm $ H.TestList $ map itemToTest ts > itemToTest (TestScalarExpr str expected) = -> toTest parseScalarExpr str expected +> toTest parseScalarExpr prettyScalarExpr str expected > itemToTest (TestQueryExpr str expected) = -> toTest parseQueryExpr str expected +> toTest parseQueryExpr prettyQueryExpr str expected -> toTest parser str expected = H.TestLabel str $ H.TestCase $ do +> toTest :: (Eq a, Show a, Show e) => +> (String -> Maybe (Int,Int) -> String -> Either e a) +> -> (a -> String) +> -> String +> -> a +> -> H.Test +> toTest parser pp str expected = H.TestLabel str $ H.TestCase $ do > let egot = parser "" Nothing str > case egot of > Left e -> H.assertFailure $ show e -> Right got -> H.assertEqual "" expected got +> Right got -> do +> H.assertEqual "" expected got +> let str' = pp got +> let egot' = parser "" Nothing str' +> case egot' of +> Left e' -> H.assertFailure $ "pp roundtrip " ++ show e' +> Right got' -> H.assertEqual "pp roundtrip" expected got' diff --git a/simple-sql-parser.cabal b/simple-sql-parser.cabal index dec1997..f311d3c 100644 --- a/simple-sql-parser.cabal +++ b/simple-sql-parser.cabal @@ -1,70 +1,35 @@ --- Initial simple-sql-parser.cabal generated by cabal init. For further --- documentation, see http://haskell.org/cabal/users-guide/ - --- The name of the package. name: simple-sql-parser - --- The package version. See the Haskell package versioning policy (PVP) --- for standards guiding when and how versions should be incremented. --- http://www.haskell.org/haskellwiki/Package_versioning_policy --- PVP summary: +-+------- breaking API changes --- | | +----- non-breaking API additions --- | | | +--- code changes with no API change version: 0.1.0.0 - --- A short (one-line) description of the package. synopsis: A parser for SQL queries --- A longer description of the package. --- description: +description: A parser for SQL queries, using Parsec. Also includes pretty printer. Aims to support most of SQL2003 queries. --- URL for the project homepage or repository. -homepage: xx - --- The license under which the package is released. +homepage: https://github.com/JakeWheat/simple_sql_parser license: BSD3 - --- The file containing the license text. license-file: LICENSE - --- The package author(s). author: Jake Wheat - --- An email address to which users can send suggestions, bug reports, and --- patches. maintainer: jakewheatmail@gmail.com - --- A copyright notice. --- copyright: - -category: Database - +copyright: Copyright Jake Wheat 2013 +category: Database,Language build-type: Simple - --- Extra files to be distributed with the package, such as examples or a --- README. --- extra-source-files: - --- Constraint on the version of Cabal needed to build this package. +extra-source-files: README,LICENSE cabal-version: >=1.10 +bug-reports: https://github.com/JakeWheat/simple_sql_parser/issues +source-repository head + type: git + location: https://github.com/JakeWheat/simple_sql_parser.git library - -- Modules exported by the library. exposed-modules: Pretty, Parser, Syntax - - -- Modules included in this library but not exported. -- other-modules: - - -- LANGUAGE extensions used by modules in this package. -- other-extensions: - - -- Other library packages from which modules are imported. - build-depends: base >=4.6 && <4.7, groom >=0.1 && <0.2, parsec >=3.1 && <3.2, mtl >=2.1 && <2.2, haskell-src-exts >=1.14 && <1.15 - - -- Directories containing source files. + build-depends: base >=4.6 && <4.7, + groom >=0.1 && <0.2, + parsec >=3.1 && <3.2, + mtl >=2.1 && <2.2, + haskell-src-exts >=1.14 && <1.15, + pretty >= 1.1 && < 1.2 -- hs-source-dirs: - - -- Base language which the package is written in. default-language: Haskell2010 - \ No newline at end of file + ghc-options: -Wall \ No newline at end of file