{- BNF Converter: C Main file 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} module BNFC.Backend.C (makeC) where import BNFC.Utils import BNFC.CF import BNFC.Options import BNFC.Backend.Base import BNFC.Backend.C.CFtoCAbs import BNFC.Backend.C.CFtoFlexC import BNFC.Backend.C.CFtoBisonC import BNFC.Backend.C.CFtoCSkel import BNFC.Backend.C.CFtoCPrinter import Data.Char import qualified BNFC.Backend.Common.Makefile as Makefile makeC :: SharedOptions -> CF -> MkFiles () makeC opts cf = do let (hfile, cfile) = cf2CAbs prefix cf mkfile "Absyn.h" hfile mkfile "Absyn.c" cfile let (flex, env) = cf2flex prefix cf mkfile (name ++ ".l") flex let bison = cf2Bison prefix cf env mkfile (name ++ ".y") bison let header = mkHeaderFile cf (allCats cf) (allEntryPoints cf) env mkfile "Parser.h" header let (skelH, skelC) = cf2CSkel cf mkfile "Skeleton.h" skelH mkfile "Skeleton.c" skelC let (prinH, prinC) = cf2CPrinter cf mkfile "Printer.h" prinH mkfile "Printer.c" prinC mkfile "Test.c" (ctest cf) Makefile.mkMakefile opts (makefile name prefix) where prefix :: String -- The prefix is a string used by flex and bison -- that is prepended to generated function names. -- In most cases we want the grammar name as the prefix -- but in a few specific cases, this can create clashes -- with existing functions prefix = if name `elem` ["m","c","re","std","str"] then name ++ "_" else name name = lang opts makefile :: String -> String -> String makefile name prefix = (unlines [ "CC = gcc", "CCFLAGS = -g -W -Wall", "", "FLEX = flex", "FLEX_OPTS = -P" ++ prefix, "", "BISON = bison", "BISON_OPTS = -t -p" ++ prefix, "", "OBJS = Absyn.o Lexer.o Parser.o Printer.o", ""] ++) $ Makefile.mkRule ".PHONY" ["clean", "distclean"] [] $ Makefile.mkRule "all" [testName] [] $ Makefile.mkRule "clean" [] -- peteg: don't nuke what we generated - move that to the "vclean" target. [ "rm -f *.o " ++ testName ++ " " ++ unwords [ name ++ e | e <- [".aux", ".log", ".pdf",".dvi", ".ps", ""]] ] $ Makefile.mkRule "distclean" ["clean"] [ "rm -f " ++ unwords [ "Absyn.h", "Absyn.c", "Test.c", "Parser.c", "Parser.h", "Lexer.c", "Skeleton.c", "Skeleton.h", "Printer.c", "Printer.h", "Makefile " ] ++ name ++ ".l " ++ name ++ ".y " ++ name ++ ".tex "] $ Makefile.mkRule testName ["${OBJS}", "Test.o"] [ "@echo \"Linking " ++ testName ++ "...\"" , "${CC} ${CCFLAGS} ${OBJS} Test.o -o " ++ testName ] $ Makefile.mkRule "Absyn.o" [ "Absyn.c", "Absyn.h"] [ "${CC} ${CCFLAGS} -c Absyn.c" ] $ Makefile.mkRule "Lexer.c" [ name ++ ".l" ] [ "${FLEX} ${FLEX_OPTS} -oLexer.c " ++ name ++ ".l" ] $ Makefile.mkRule "Parser.c" [ name ++ ".y" ] [ "${BISON} ${BISON_OPTS} " ++ name ++ ".y -o Parser.c" ] $ Makefile.mkRule "Lexer.o" [ "Lexer.c", "Parser.h" ] [ "${CC} ${CCFLAGS} -c Lexer.c " ] $ Makefile.mkRule "Parser.o" ["Parser.c", "Absyn.h" ] [ "${CC} ${CCFLAGS} -c Parser.c" ] $ Makefile.mkRule "Printer.o" [ "Printer.c", "Printer.h", "Absyn.h" ] [ "${CC} ${CCFLAGS} -c Printer.c" ] $ Makefile.mkRule "Test.o" [ "Test.c", "Parser.h", "Printer.h", "Absyn.h" ] [ "${CC} ${CCFLAGS} -c Test.c" ] "" where testName = "Test" ++ name -- | Generate a test program that parses stdin and prints the AST and it's -- linearization ctest :: CF -> String ctest cf = unlines [ "/*** Compiler Front-End Test automatically generated by the BNF Converter ***/", "/* */", "/* This test will parse a file, print the abstract syntax tree, and then */", "/* pretty-print the result. */", "/* */", "/****************************************************************************/", "", "#include ", "#include ", "", "#include \"Parser.h\"", "#include \"Printer.h\"", "#include \"Absyn.h\"", "", "int main(int argc, char ** argv)", "{", " FILE *input;", " " ++ def ++ " parse_tree;", " if (argc > 1) ", " {", " input = fopen(argv[1], \"r\");", " if (!input)", " {", " fprintf(stderr, \"Error opening input file.\\n\");", " exit(1);", " }", " }", " else input = stdin;", " /* The default entry point is used. For other options see Parser.h */", " parse_tree = p" ++ def ++ "(input);", " if (parse_tree)", " {", " printf(\"\\nParse Succesful!\\n\");", " printf(\"\\n[Abstract Syntax]\\n\");", " printf(\"%s\\n\\n\", show" ++ def ++ "(parse_tree));", " printf(\"[Linearized Tree]\\n\");", " printf(\"%s\\n\\n\", print" ++ def ++ "(parse_tree));", " return 0;", " }", " return 1;", "}", "" ] where def = show $ head (allEntryPoints cf) mkHeaderFile :: CF -> [Cat] -> [Cat] -> [(a, String)] -> String mkHeaderFile cf cats eps env = unlines [ "#ifndef PARSER_HEADER_FILE", "#define PARSER_HEADER_FILE", "", "#include \"Absyn.h\"", "", "typedef union", "{", " int int_;", " char char_;", " double double_;", " char* string_;", (concatMap mkVar cats) ++ "} YYSTYPE;", "", "#define _ERROR_ 258", mkDefines (259::Int) env, "extern YYSTYPE yylval;", concatMap mkFunc eps, "", "#endif" ] where mkVar s | (normCat s == s) = " " ++ (identCat s) +++ (map toLower (identCat s)) ++ "_;\n" mkVar _ = "" mkDefines n [] = mkString n mkDefines n ((_,s):ss) = ("#define " ++ s +++ (show n) ++ "\n") ++ (mkDefines (n+1) ss) mkString n = if isUsedCat cf catString then ("#define _STRING_ " ++ show n ++ "\n") ++ mkChar (n+1) else mkChar n mkChar n = if isUsedCat cf catChar then ("#define _CHAR_ " ++ show n ++ "\n") ++ mkInteger (n+1) else mkInteger n mkInteger n = if isUsedCat cf catInteger then ("#define _INTEGER_ " ++ show n ++ "\n") ++ mkDouble (n+1) else mkDouble n mkDouble n = if isUsedCat cf catDouble then ("#define _DOUBLE_ " ++ show n ++ "\n") ++ mkIdent(n+1) else mkIdent n mkIdent n = if isUsedCat cf catIdent then ("#define _IDENT_ " ++ show n ++ "\n") else "" mkFunc s | normCat s == s = identCat s ++ " p" ++ identCat s ++ "(FILE *inp);\n" mkFunc _ = ""