{-# LANGUAGE LambdaCase #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE OverloadedStrings #-} {- BNF Converter: C Abstract syntax Copyright (C) 2004 Author: Michael Pellauer Description : This module generates the C Abstract Syntax tree classes. It generates both a Header file and an Implementation file, and Appel's C method. Author : Michael Pellauer Created : 15 September, 2003 -} module BNFC.Backend.C.CFtoCAbs (cf2CAbs) where import Prelude hiding ((<>)) import Data.Char (toLower) import Data.Function (on) import Data.List (groupBy, intercalate, nub, sort) import BNFC.CF import BNFC.PrettyPrint import BNFC.Options (RecordPositions(..)) import BNFC.Utils ((+++), uncurry3, unless) import BNFC.Backend.Common.NamedVariables -- | The result is two files (.H file, .C file) cf2CAbs :: RecordPositions -> String -- ^ Ignored. -> CF -- ^ Grammar. -> (String, String) -- ^ @.H@ file, @.C@ file. cf2CAbs rp _ cf = (mkHFile rp cf, mkCFile cf) {- **** Header (.H) File Functions **** -} -- | Makes the Header file. mkHFile :: RecordPositions -> CF -> String mkHFile rp cf = unlines $ concat [ [ "#ifndef ABSYN_HEADER" , "#define ABSYN_HEADER" , "" , "/* C++ Abstract Syntax Interface generated by the BNF Converter.*/" , "" , prTypeDefs user , "/******************** Forward Declarations ***********************/" ] , map prForward classes , [ "/******************** Abstract Syntax Classes ********************/" , "" ] , map (prDataH rp) $ getAbstractSyntax cf , unless (null definedConstructors) [ "/******************** Defined Constructors ***********************/" , "" ] , map (uncurry3 prDefH) definedConstructors , [ "" , "#endif" ] ] where user = map fst $ tokenPragmas cf rules :: [String] rules = getRules cf classes = nub (rules ++ getClasses (allCatsNorm cf)) prForward s | not (isCoercion s) = unlines [ "struct " ++ s ++ "_;" , "typedef struct " ++ s ++ "_ *" ++ s ++ ";" ] prForward _ = "" getRules cf = map testRule (cfgRules cf) getClasses = map show . filter isDataCat testRule (Rule f (WithPosition _ c) _ _) | isList c && isConsFun f = identCat (normCat c) | otherwise = "_" definedConstructors = [ (funName f, xs, e) | FunDef f xs e <- cfgPragmas cf ] -- | For @define@d constructors, make a CPP definition. -- -- >>> prDefH "iSg" ["i"] (App "ICons" [Var "i", App "INil" []]) -- "#define make_iSg(i) make_ICons(i,make_INil())" -- -- >>> prDefH "snoc" ["xs","x"] (App "Cons" [Var "x", Var "xs"]) -- "#define make_snoc(xs,x) make_Cons(x,xs)" -- prDefH :: String -- ^ Name of the defined constructors. -> [String] -- ^ Names of the arguments. -> Exp -- ^ Definition (referring to arguments and rule labels). -> String prDefH f xs e = concat [ "#define make_", f, "(", intercalate "," xs, ") ", prExp e ] where prExp :: Exp -> String prExp = \case Var x -> x App g es -> concat [ "make_", g, "(", intercalate "," (map prExp es), ")" ] LitInt i -> show i LitDouble d -> show d LitChar c -> show c LitString s -> show s -- | Prints struct definitions for all categories. prDataH :: RecordPositions -> Data -> String prDataH rp (cat, rules) | isList cat = unlines [ "struct " ++ c' ++ "_" , "{" , " " ++ mem +++ varName mem ++ ";" , " " ++ c' +++ varName c' ++ ";" , "};" , "" , c' ++ " make_" ++ c' ++ "(" ++ mem ++ " p1, " ++ c' ++ " p2);" ] | otherwise = unlines $ concat [ [ "struct " ++ show cat ++ "_" , "{" ] , [ " int line_number, char_number;" | rp == RecordPositions ] , [ " enum { " ++ intercalate ", " (map prKind rules) ++ " } kind;" , " union" , " {" , concatMap prUnion rules ++ " } u;" , "};" , "" ] , concatMap (prRuleH cat) rules ] where c' = identCat (normCat cat) mem = identCat (normCatOfList cat) prKind (fun, _) = "is_" ++ fun prUnion (_, []) = "" prUnion (fun, cats) = " struct { " ++ (render $ prInstVars (getVars cats)) ++ " } " ++ (memName fun) ++ ";\n" -- | Interface definitions for rules vary on the type of rule. prRuleH :: Cat -> (Fun, [Cat]) -> [String] prRuleH c (fun, cats) | isNilFun fun || isOneFun fun || isConsFun fun = [] -- these are not represented in the AbSyn | otherwise = return $ concat [ catToStr c, " make_", fun, "(", prParamsH (getVars cats), ");" ] where prParamsH :: [(String, a)] -> String prParamsH [] = "void" prParamsH ps = intercalate ", " $ zipWith par ps [0..] where par (t, _) n = t ++ " p" ++ show n -- typedefs in the Header make generation much nicer. prTypeDefs user = unlines $ concat [ [ "/******************** TypeDef Section ********************/" , "" , "typedef int Integer;" , "typedef char Char;" , "typedef double Double;" , "typedef char* String;" , "typedef char* Ident;" ] , map prUserDef user ] where prUserDef s = "typedef char* " ++ s ++ ";" -- | A class's instance variables. Print the variables declaration by grouping -- together the variables of the same type. -- >>> prInstVars [("A", 1)] -- A a_1; -- >>> prInstVars [("A",1),("A",2),("B",1)] -- A a_1, a_2; B b_1; prInstVars :: [IVar] -> Doc prInstVars = hsep . map prInstVarsOneType . groupBy ((==) `on` fst) . sort where prInstVarsOneType ivars = text (fst (head ivars)) <+> hsep (punctuate comma (map prIVar ivars)) <> semi prIVar (s, i) = text (varName s) <> text (showNum i) {- **** Implementation (.C) File Functions **** -} -- | Makes the .C file mkCFile :: CF -> String mkCFile cf = concat [ header , render $ vsep $ concatMap prDataC $ getAbstractSyntax cf ] where header = unlines [ "/* C Abstract Syntax Implementation generated by the BNF Converter. */" , "" , "#include " , "#include " , "#include \"Absyn.h\"" , "" ] prDataC :: Data -> [Doc] prDataC (cat, rules) = map (prRuleC cat) rules -- | Classes for rules vary based on the type of rule. -- -- * Empty list constructor, these are not represented in the AbSyn -- -- >>> prRuleC (ListCat (Cat "A")) ("[]", [Cat "A", Cat "B", Cat "B"]) -- -- -- * Linked list case. These are all built-in list functions. -- Later we could include things like lookup, insert, delete, etc. -- -- >>> prRuleC (ListCat (Cat "A")) ("(:)", [Cat "A", Cat "B", Cat "B"]) -- /******************** ListA ********************/ -- -- ListA make_ListA(A p1, ListA p2) -- { -- ListA tmp = (ListA) malloc(sizeof(*tmp)); -- if (!tmp) -- { -- fprintf(stderr, "Error: out of memory when allocating ListA!\n"); -- exit(1); -- } -- tmp->a_ = p1; -- tmp->lista_ = p2; -- return tmp; -- } -- -- * Standard rule -- -- >>> prRuleC (Cat "A") ("funa", [Cat "A", Cat "B", Cat "B"]) -- /******************** funa ********************/ -- -- A make_funa(A p1, B p2, B p3) -- { -- A tmp = (A) malloc(sizeof(*tmp)); -- if (!tmp) -- { -- fprintf(stderr, "Error: out of memory when allocating funa!\n"); -- exit(1); -- } -- tmp->kind = is_funa; -- tmp->u.funa_.a_ = p1; -- tmp->u.funa_.b_1 = p2; -- tmp->u.funa_.b_2 = p3; -- return tmp; -- } prRuleC :: Cat -> (String, [Cat]) -> Doc prRuleC _ (fun, _) | isNilFun fun || isOneFun fun = empty prRuleC cat (fun, _) | isConsFun fun = vcat' [ "/******************** " <> c <> " ********************/" , "" , c <+> "make_" <> c <> parens (text m <+> "p1" <> "," <+> c <+> "p2") , lbrace , nest 4 $ vcat' [ c <+> "tmp = (" <> c <> ") malloc(sizeof(*tmp));" , "if (!tmp)" , lbrace , nest 4 $ vcat' [ "fprintf(stderr, \"Error: out of memory when allocating " <> c <> "!\\n\");" , "exit(1);" ] , rbrace , text $ "tmp->" ++ m' ++ " = " ++ "p1;" , "tmp->" <> v <+> "=" <+> "p2;" , "return tmp;" ] , rbrace ] where icat = identCat (normCat cat) c = text icat v = text (map toLower icat ++ "_") ListCat c' = cat -- We're making a list constructor, so we -- expect a list category m = identCat (normCat c') m' = map toLower m ++ "_" prRuleC c (fun, cats) = vcat' [ text $ "/******************** " ++ fun ++ " ********************/" , "" , prConstructorC c fun vs cats ] where vs = getVars cats -- | The constructor just assigns the parameters to the corresponding instance -- variables. -- >>> prConstructorC (Cat "A") "funa" [("A",1),("B",2)] [Cat "O", Cat "E"] -- A make_funa(O p1, E p2) -- { -- A tmp = (A) malloc(sizeof(*tmp)); -- if (!tmp) -- { -- fprintf(stderr, "Error: out of memory when allocating funa!\n"); -- exit(1); -- } -- tmp->kind = is_funa; -- tmp->u.funa_.a_ = p1; -- tmp->u.funa_.b_2 = p2; -- return tmp; -- } prConstructorC :: Cat -> String -> [IVar] -> [Cat] -> Doc prConstructorC cat c vs cats = vcat' [ text (cat' ++ " make_" ++ c) <> parens args , lbrace , nest 4 $ vcat' [ text $ cat' ++ " tmp = (" ++ cat' ++ ") malloc(sizeof(*tmp));" , text "if (!tmp)" , lbrace , nest 4 $ vcat' [ text ("fprintf(stderr, \"Error: out of memory when allocating " ++ c ++ "!\\n\");") , text "exit(1);" ] , rbrace , text $ "tmp->kind = is_" ++ c ++ ";" , prAssigns c vs params , text "return tmp;" ] , rbrace ] where cat' = identCat (normCat cat) (types, params) = unzip (prParams cats) args = hsep $ punctuate comma $ zipWith (<+>) types params -- | Prints the constructor's parameters. Returns pairs of type * name -- >>> prParams [Cat "O", Cat "E"] -- [(O,p1),(E,p2)] prParams :: [Cat] -> [(Doc, Doc)] prParams = zipWith prParam [1..] where prParam n c = (text (identCat c), text ("p" ++ show n)) -- | Prints the assignments of parameters to instance variables. -- >>> prAssigns "A" [("A",1),("B",2)] [text "abc", text "def"] -- tmp->u.a_.a_ = abc; -- tmp->u.a_.b_2 = def; prAssigns :: String -> [IVar] -> [Doc] -> Doc prAssigns c vars params = vcat $ zipWith prAssign vars params where prAssign (t,n) p = text ("tmp->u." ++ c' ++ "_." ++ vname t n) <+> char '=' <+> p <> semi vname t n | n == 1, [_] <- filter ((t ==) . fst) vars = varName t | otherwise = varName t ++ showNum n c' = map toLower c {- **** Helper Functions **** -} memName s = map toLower s ++ "_"