{-# LANGUAGE NoImplicitPrelude #-} {- BNF Converter: C++ Skeleton generation Copyright (C) 2004 Author: Michael Pellauer 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA -} {- ************************************************************** BNF Converter Module Description : This module generates the C++ Skeleton functions. The generated files use the Visitor design pattern. Author : Michael Pellauer (pellauer@cs.chalmers.se) License : GPL (GNU General Public License) Created : 9 August, 2003 Modified : 12 August, 2003 ************************************************************** -} module BNFC.Backend.CPP.NoSTL.CFtoCVisitSkel (cf2CVisitSkel) where import Prelude' import BNFC.CF import BNFC.Utils ((+++)) import BNFC.Backend.Common.NamedVariables import BNFC.Backend.CPP.Naming (mkVariable) import Data.List import Data.Char(toLower, toUpper) import Data.Either (lefts) import BNFC.PrettyPrint --Produces (.H file, .C file) cf2CVisitSkel :: CF -> (String, String) cf2CVisitSkel cf = (mkHFile cf groups, mkCFile cf groups) where groups = fixCoercions (ruleGroups cf) {- **** Header (.H) File Functions **** -} --Generates the Header File mkHFile :: CF -> [(Cat,[Rule])] -> String mkHFile cf groups = unlines [ header, concatMap prDataH groups, concatMap (prUserH.show) user, footer ] where user = fst (unzip (tokenPragmas cf)) header = unlines [ "#ifndef SKELETON_HEADER", "#define SKELETON_HEADER", "/* You might want to change the above name. */", "", "#include \"Absyn.H\"", "", "class Skeleton : public Visitor", "{", " public:" ] prUserH u = " void visit" ++ u' ++ "(" ++ u ++ " p);" where u' = ((toUpper (head u)) : (map toLower (tail u))) --this is a hack to fix a potential capitalization problem. footer = unlines [ " void visitIdent(String s);", " void visitInteger(Integer i);", " void visitDouble(Double d);", " void visitChar(Char c);", " void visitString(String s);", "};", "", "#endif" ] --Prints out visit functions for a category prDataH :: (Cat, [Rule]) -> String prDataH (cat, rules) = if isList cat then concat [" void visit", cl, "(", cl, " *", vname, ");"] else abstract ++ concatMap prRuleH rules where cl = identCat (normCat cat) vname = mkVariable cl abstract = case lookupRule (show cat) rules of Just _ -> "" Nothing -> " void visit" ++ cl ++ "(" ++ cl +++ "*" ++ vname ++ "); /* abstract class */\n" --Visit functions for a rule. prRuleH :: Rule -> String prRuleH (Rule fun _ _) | not (isCoercion fun) = concat [" void visit", fun, "(", fun, " *", fnm, ");\n"] where fnm = mkVariable fun prRuleH _ = "" {- **** Implementation (.C) File Functions **** -} --Makes the .C File mkCFile :: CF -> [(Cat,[Rule])] -> String mkCFile cf groups = concat [ header, concatMap (prData user) groups, concatMap (prUser.show) user, footer ] where user = fst (unzip (tokenPragmas cf)) header = unlines [ "/*** BNFC-Generated Visitor Design Pattern Skeleton. ***/", "/* This implements the common visitor design pattern.", " Note that this method uses Visitor-traversal of lists, so", " List->accept() does NOT traverse the list. This allows different", " algorithms to use context information differently. */", "", "#include \"Skeleton.H\"", "" ] prUser x = unlines [ "void Skeleton::visit" ++ x' ++ "(" ++ x ++ " p)", "{", " /* Code for " ++ x ++ " Goes Here */", "}", "" ] where x' = ((toUpper (head x)) : (map toLower (tail x))) --this is a hack to fix a potential capitalization problem. footer = unlines [ "void Skeleton::visitIdent(Ident i)", "{", " /* Code for Ident Goes Here */", "}", "", "void Skeleton::visitInteger(Integer i)", "{", " /* Code for Integers Goes Here */", "}", "", "void Skeleton::visitDouble(Double d)", "{", " /* Code for Doubles Goes Here */", "}", "", "void Skeleton::visitChar(Char c)", "{", " /* Code for Chars Goes Here */", "}", "", "void Skeleton::visitString(String s)", "{", " /* Code for Strings Goes Here */", "}", "" ] --Visit functions for a category. prData :: [UserDef] -> (Cat, [Rule]) -> String prData user (cat, rules) = if isList cat then unlines [ "void Skeleton::visit" ++ cl ++ "("++ cl +++ "*" ++ vname ++ ")", "{", " while(" ++ vname +++ "!= 0)", " {", " /* Code For " ++ cl ++ " Goes Here */", visitMember, " " ++ vname ++ " = " ++ vname ++ "->" ++ vname' ++ "_;", " }", "}", "" ] --Not a list: else abstract ++ (concatMap (render . prRule) rules) where cl = identCat (normCat cat) vname = mkVariable cl vname' = map toLower cl ecl = identCat (normCatOfList cat) member = map toLower ecl ++ "_" visitMember = if isBasic user member then " visit" ++ (funName member) ++ "(" ++ vname ++ "->" ++ member ++ ");" else " " ++ vname ++ "->" ++ member ++ "->accept(this);" abstract = case lookupRule (show cat) rules of Just _ -> "" Nothing -> "void Skeleton::visit" ++ cl ++ "(" ++ cl +++ "*" ++ vname ++ ") {} //abstract class\n\n" -- | Visits all the instance variables of a category. -- >>> prRule (Rule "F" (Cat "S") [Right "X", Left (TokenCat "A"), Left (Cat "B")]) -- void Skeleton::visitF(F *f) -- { -- /* Code For F Goes Here */ -- -- visitA(f->a_); -- f->b_->accept(this); -- } -- prRule :: Rule -> Doc prRule (Rule fun _ cats) | not (isCoercion fun) = vcat [ text ("void Skeleton::visit" ++ fun ++ "(" ++ fun +++ "*" ++ fnm ++ ")") , codeblock 2 [ text ("/* Code For " ++ fun ++ " Goes Here */") , "" , cats' ] , "" ] where cats' = vcat (map (prCat fnm) (lefts (numVars cats))) fnm = mkVariable fun prRule _ = "" -- | Prints the actual instance-variable visiting. -- >>> prCat "Myfun" (TokenCat "Integer", "integer_") -- visitInteger(Myfun->integer_); -- >>> prCat "Myfun" (ListCat (Cat "A"), "lista_") -- if (Myfun->lista_) {Myfun->lista_->accept(this);} -- >>> prCat "Myfun" (Cat "A", "a_") -- Myfun->a_->accept(this); prCat :: String -> (Cat, Doc) -> Doc prCat fnm (cat, nt) | isTokenCat cat = "visit" <> text (funName (render nt)) <> parens (fname <> "->" <> nt) <> ";" | isList cat = "if" <+> parens (fname <> "->" <> nt) <+> braces accept | otherwise = accept where accept = fname <> "->" <> nt <> "->accept(this);" fname = text fnm --Just checks if something is a basic or user-defined type. --This is because you don't -> a basic non-pointer type. isBasic :: [UserDef] -> String -> Bool isBasic user v = if elem (init v) user' then True else if "integer_" `isPrefixOf` v then True else if "char_" `isPrefixOf` v then True else if "string_" `isPrefixOf` v then True else if "double_" `isPrefixOf` v then True else if "ident_" `isPrefixOf` v then True else False where user' = map (map toLower.show) user --The visit-function name of a basic type funName :: String -> String funName v = if "integer_" `isPrefixOf` v then "Integer" else if "char_" `isPrefixOf` v then "Char" else if "string_" `isPrefixOf` v then "String" else if "double_" `isPrefixOf` v then "Double" else if "ident_" `isPrefixOf` v then "Ident" else (toUpper (head v)) : (init (tail v)) --User-defined type