{- BNF Converter: Pretty-printer generator Copyright (C) 2004 Author: Aarne Ranta This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} module BNFC.Backend.Haskell.CFtoPrinter (cf2Printer, compareRules) where import BNFC.Backend.Haskell.Utils (hsReservedWords) import BNFC.CF import BNFC.Utils import Data.Char(toLower) import Data.Either (lefts) import Data.List (sortBy) import Data.Maybe (fromJust) import Text.PrettyPrint -- derive pretty-printer from a BNF grammar. AR 15/2/2002 cf2Printer :: Bool -> Bool -> Bool -> String -> String -> CF -> String cf2Printer byteStrings functor useGadt name absMod cf = unlines [ prologue byteStrings useGadt name absMod, integerRule cf, doubleRule cf, if hasIdent cf then identRule byteStrings cf else "", unlines [ownPrintRule byteStrings cf own | (own,_) <- tokenPragmas cf], rules functor cf ] prologue :: Bool -> Bool -> String -> String -> String prologue byteStrings useGadt name absMod = unlines $ ["{-# LANGUAGE GADTs, TypeSynonymInstances, FlexibleInstances #-}" | useGadt] ++ [ "{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}", "module " ++ name +++ "where\n", "-- pretty-printer generated by the BNF converter\n", "import " ++ absMod, "import Data.Char", (if byteStrings then "import qualified Data.ByteString.Char8 as BS" else ""), "", "-- the top-level printing method", "printTree :: Print a => a -> String", "printTree = render . prt 0", "", "type Doc = [ShowS] -> [ShowS]", "", "doc :: ShowS -> Doc", "doc = (:)", "", "render :: Doc -> String", "render d = rend 0 (map ($ \"\") $ d []) \"\" where", " rend i ss = case ss of", " \"[\" :ts -> showChar '[' . rend i ts", " \"(\" :ts -> showChar '(' . rend i ts", " \"{\" :ts -> showChar '{' . new (i+1) . rend (i+1) ts", " \"}\" : \";\":ts -> new (i-1) . space \"}\" . showChar ';' . new (i-1) . rend (i-1) ts", " \"}\" :ts -> new (i-1) . showChar '}' . new (i-1) . rend (i-1) ts", " \";\" :ts -> showChar ';' . new i . rend i ts", " t : \",\" :ts -> showString t . space \",\" . rend i ts", " t : \")\" :ts -> showString t . showChar ')' . rend i ts", " t : \"]\" :ts -> showString t . showChar ']' . rend i ts", " t :ts -> space t . rend i ts", " _ -> id", " new i = showChar '\\n' . replicateS (2*i) (showChar ' ') . dropWhile isSpace", " space t = showString t . (\\s -> if null s then \"\" else (' ':s))", "", "parenth :: Doc -> Doc", "parenth ss = doc (showChar '(') . ss . doc (showChar ')')", "", "concatS :: [ShowS] -> ShowS", "concatS = foldr (.) id", "", "concatD :: [Doc] -> Doc", "concatD = foldr (.) id", "", "replicateS :: Int -> ShowS -> ShowS", "replicateS n f = concatS (replicate n f)", "", "-- the printer class does the job", "class Print a where", " prt :: Int -> a -> Doc", " prtList :: Int -> [a] -> Doc", " prtList i = concatD . map (prt i)", "", "instance Print a => Print [a] where", " prt = prtList", "", "instance Print Char where", " prt _ s = doc (showChar '\\'' . mkEsc '\\'' s . showChar '\\'')", " prtList _ s = doc (showChar '\"' . concatS (map (mkEsc '\"') s) . showChar '\"')", "", "mkEsc :: Char -> Char -> ShowS", "mkEsc q s = case s of", " _ | s == q -> showChar '\\\\' . showChar s", " '\\\\'-> showString \"\\\\\\\\\"", " '\\n' -> showString \"\\\\n\"", " '\\t' -> showString \"\\\\t\"", " _ -> showChar s", "", "prPrec :: Int -> Int -> Doc -> Doc", "prPrec i j = if j CF -> String rules functor cf = unlines $ map (\(s,xs) -> render (case_fun functor s (map toArgs xs)) ++++ ifList cf s) $ cf2data cf where toArgs (cons,_) = (cons, ruleOf cons) ruleOf s = fromJust $ lookupRule s (rulesOfCF cf) -- | -- >>> case_fun False (Cat "A") [("AA", (Cat "AB", [Right "xxx"]))] -- instance Print A where -- prt i e = case e of -- AA -> prPrec i 0 (concatD [doc (showString "xxx")]) case_fun :: Bool -> Cat -> [(String, (Cat, [Either Cat String]))] -> Doc case_fun functor cat xs = vcat [ "instance Print" <+> type_ <+> "where" , nest 2 $ vcat [ "prt i e = case e of" , nest 2 $ vcat (map (mkPrintCase functor) xs) ] ] where type_ | functor = parens (text (show cat) <+> "a") | otherwise = text (show cat) -- When writing the Print instance for a category (in case_fun), we have -- a different case for each constructor for this category. -- >>> mkPrintCase False ("AA", (Cat "A", [Right "xxx"])) -- AA -> prPrec i 0 (concatD [doc (showString "xxx")]) -- -- Coercion levels are passed to prPrec -- >>> mkPrintCase False ("EInt", (CoercCat "Expr" 2, [Left (TokenCat "Integer")])) -- EInt n -> prPrec i 2 (concatD [prt 0 n]) -- >>> mkPrintCase False ("EPlus", (CoercCat "Expr" 1, [Left (Cat "Expr"), Right "+", Left (Cat "Expr")])) -- EPlus expr0 expr -> prPrec i 1 (concatD [prt 0 expr0, doc (showString "+"), prt 0 expr]) -- -- If the AST is a functor, ignore first argument -- >>> mkPrintCase True ("EInt", (CoercCat "Expr" 2, [Left (TokenCat "Integer")])) -- EInt _ n -> prPrec i 2 (concatD [prt 0 n]) -- -- Skip intertal categories -- >>> mkPrintCase True ("EInternal", (Cat "Expr", [Left InternalCat, Left (Cat "Expr")])) -- EInternal _ expr -> prPrec i 0 (concatD [prt 0 expr]) mkPrintCase :: Bool -> (Fun, (Cat, [Either Cat String])) -> Doc mkPrintCase functor (f, (cat, rhs)) = text f <+> (if functor then "_" else empty) <+> hsep variables <+> "->" <+> "prPrec i" <+> integer (precCat cat) <+> mkRhs (map render variables) rhs where -- Creating variables names used in pattern matching. In addition to -- haskell's reserved words, `e` and `i` are used in the printing function -- and should be avoided names = map var (filter (/=InternalCat) $ lefts rhs) variables = map text $ mkNames ("e":"i":hsReservedWords) LowerCase names var (ListCat c) = var c ++ "s" var (TokenCat "Ident") = "id" var (TokenCat "Integer") = "n" var (TokenCat "String") = "str" var (TokenCat "Char") = "c" var (TokenCat "Double") = "d" var xs = map toLower $ show xs ifList :: CF -> Cat -> String ifList cf cat = render $ nest 2 $ vcat [ mkPrtListCase r | r <- rules ] where rules = sortBy compareRules $ rulesForNormalizedCat cf (ListCat cat) -- | Pattern match on the list constructor and the coercion level -- >>> mkPrtListCase (Rule "[]" (ListCat (Cat "Foo")) []) -- prtList _ [] = (concatD []) -- >>> mkPrtListCase (Rule "(:[])" (ListCat (Cat "Foo")) [Left (Cat "FOO")]) -- prtList _ [x] = (concatD [prt 0 x]) -- >>> mkPrtListCase (Rule "(:)" (ListCat (Cat "Foo")) [Left (Cat "Foo"), Left (ListCat (Cat "Foo"))]) -- prtList _ (x:xs) = (concatD [prt 0 x, prt 0 xs]) -- >>> mkPrtListCase (Rule "[]" (ListCat (CoercCat "Foo" 2)) []) -- prtList 2 [] = (concatD []) -- >>> mkPrtListCase (Rule "(:[])" (ListCat (CoercCat "Foo" 2)) [Left (CoercCat "Foo" 2)]) -- prtList 2 [x] = (concatD [prt 2 x]) -- >>> mkPrtListCase (Rule "(:)" (ListCat (CoercCat "Foo" 2)) [Left (CoercCat "Foo" 2), Left (ListCat (CoercCat "Foo" 2))]) -- prtList 2 (x:xs) = (concatD [prt 2 x, prt 2 xs]) mkPrtListCase :: Rule -> Doc mkPrtListCase (Rule f (ListCat c) rhs) | isNilFun f = "prtList" <+> precPattern <+> "[]" <+> "=" <+> body | isOneFun f = "prtList" <+> precPattern <+> "[x]" <+> "=" <+> body | isConsFun f = "prtList" <+> precPattern <+> "(x:xs)" <+> "=" <+> body | otherwise = empty -- (++) constructor where precPattern = case precCat c of 0 -> "_" ; p -> integer p body = mkRhs ["x", "xs"] rhs -- | Define an ordering on lists' rules with the following properties: -- - rules with a higher coercion level should come first, i.e. the rules for -- [Foo3] are before rules for [Foo1] and they are both lower than rules for -- [Foo]. -- - [] < [_] < _:_ -- This is desiged to correctly order the rules in the prtList function so that -- the pattern matching works as expectd. -- -- >>> compareRules (Rule "[]" (ListCat (CoercCat "Foo" 3)) []) (Rule "[]" (ListCat (CoercCat "Foo" 1)) []) -- LT -- >>> compareRules (Rule "[]" (ListCat (CoercCat "Foo" 3)) []) (Rule "[]" (ListCat (Cat "Foo")) []) -- LT -- >>> compareRules (Rule "[]" (ListCat (Cat "Foo")) []) (Rule "(:[])" (ListCat (Cat "Foo")) []) -- LT -- >>> compareRules (Rule "(:[])" (ListCat (Cat "Foo")) []) (Rule "(:)" (ListCat (Cat "Foo")) []) -- LT compareRules :: Rule -> Rule -> Ordering compareRules r1 r2 | precRule r1 > precRule r2 = LT compareRules r1 r2 | precRule r1 < precRule r2 = GT compareRules (Rule "[]" _ _) (Rule "[]" _ _) = EQ compareRules (Rule "[]" _ _) _ = LT compareRules (Rule "(:[])" _ _) (Rule "[]" _ _) = GT compareRules (Rule "(:[])" _ _) (Rule "(:[])" _ _) = EQ compareRules (Rule "(:[])" _ _) (Rule "(:)" _ _) = LT compareRules (Rule "(:)" _ _) (Rule "(:)" _ _) = EQ compareRules (Rule "(:)" _ _) _ = GT compareRules _ _ = EQ -- | -- >>> mkRhs ["expr1", "n", "expr2"] [Left (Cat "Expr"), Right "-", Left (TokenCat "Integer"), Left (Cat "Expr")] -- (concatD [prt 0 expr1, doc (showString "-"), prt 0 n, prt 0 expr2]) -- -- Coercions on the right hand side should be passed to prt: -- >>> mkRhs ["expr1"] [Left (CoercCat "Expr" 2)] -- (concatD [prt 2 expr1]) -- >>> mkRhs ["expr2s"] [Left (ListCat (CoercCat "Expr" 2))] -- (concatD [prt 2 expr2s]) mkRhs :: [String] -> [Either Cat String] -> Doc mkRhs args its = parens ("concatD" <+> brackets (hsep (punctuate "," (mk args its)))) where mk args (Left InternalCat : items) = mk args items mk (arg:args) (Left c : items) = (prt c <+> text arg) : mk args items mk args (Right s : items) = ("doc (showString" <+> text (show s) <> ")") : mk args items mk _ _ = [] prt c = "prt" <+> integer (precCat c)