BNFC-2.4.1.1: formats/haskell-gadt/CFtoPrinterGADT.hs
{-
BNF Converter: GADT Pretty-printer generator
Copyright (C) 2004-2005 Author: Aarne Ranta, Björn Bringert
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 CFtoPrinterGADT (cf2Printer) where
import CF
import Utils
import CFtoTemplate
import List (intersperse)
import Char(toLower)
import HaskellGADTCommon
-- derive pretty-printer from a BNF grammar. AR 15/2/2002
cf2Printer :: String -> String -> CF -> String
cf2Printer name absMod cf = unlines $ [
prologue name absMod,
integerRule cf,
doubleRule cf]
++ prPrt cf
++ [""]
++ concatMap (prPrtList cf) (filter isList (allCats cf))
prologue :: String -> String -> String
prologue name absMod = unlines [
"{-# OPTIONS_GHC -fglasgow-exts #-}",
"module " ++ name +++ "where\n",
"-- pretty-printer generated by the BNF converter\n",
"import " ++ absMod,
"import Data.Char",
"import Data.List (intersperse)",
"",
"-- 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",
"",
"unwordsD :: [Doc] -> Doc",
"unwordsD = concatD . intersperse (doc (showChar ' '))",
"",
"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",
"",
"instance Print Char where",
" prt _ s = doc (showChar '\\'' . mkEsc '\\'' s . showChar '\\'')",
"",
"instance Print String where",
" prt _ 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<i then parenth else id",
""
]
integerRule cf = showsPrintRule cf "Integer"
doubleRule cf = showsPrintRule cf "Double"
showsPrintRule cf t = unlines $ [
"instance Print " ++ t ++ " where",
" prt _ x = doc (shows x)",
""] ++ ifList cf t
ownPrintRule cf own = unlines $ [
"instance Print " ++ own ++ " where",
" prt _ (" ++ own ++ posn ++ ") = doc (showString i)",
""] ++ ifList cf own
where
posn = if isPositionCat cf own then " (_,i)" else " i"
ifList :: CF -> Cat -> [String]
ifList cf cat = prPrtList cf ("["++cat++"]") -- FIXME: hackish
prPrt :: CF -> [String]
prPrt cf = ["instance Print (Tree c) where",
" prt _i e = case e of"
] ++ map prPrtCons (cf2cons cf)
where
prPrtCons c = " " ++ consFun c +++ unwords (vars c) +++ "->" +++
"prPrec _i" +++ show (consPrec c) +++ rhs
-- for token rules, just print the string argument unquoted
where rhs | isToken c = let [v] = vars c in "(doc (showString " ++ v ++ "))"
| otherwise = mkRhs (vars c) (consRhs c)
vars = map snd . consVars
isToken c = consCat c `elem` specialCats cf
prPrtList :: CF -> Cat -> [String]
prPrtList cf cat = mkListRule (nil ++ one ++ cons)
where
nil = [" [] -> " ++ mkRhs [] its |
(f,(_,its)) <- rules, isNilFun f]
one = [" [x] -> " ++ mkRhs ["x"] its |
(f,(_,its)) <- rules, isOneFun f]
cons = [" x:xs -> " ++ mkRhs ["x","xs"] its |
(f,(_,its)) <- rules, isConsFun f]
mkListRule [] = []
mkListRule rs = ["instance Print" +++ cat +++ "where",
" prt _" +++ "es = case es of"] ++ rs
rules = rulesForCat cf cat
mkRhs :: [String] -> [Either Cat String] -> String
mkRhs args its =
"(concatD [" ++ unwords (intersperse "," (mk args its)) ++ "])"
where
mk args (Left "#" : items) = mk args items
mk (arg:args) (Left c : items) = (prt c +++ arg) : mk args items
mk args (Right s : items) = ("doc (showString" +++ show s ++ ")") : mk args items
mk _ _ = []
prt c = "prt" +++ show (precCat c)