{- BNF Converter: Java Top File Copyright (C) 2004 Author: Markus Forsberg, Peter Gammie, Michael Pellauer, Bjorn 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 : JavaTop -- Copyright : (C)opyright 2003, {markus, aarne, pellauer, peteg, bringert} at cs dot chalmers dot se -- License : GPL (see COPYING for details) -- -- Maintainer : {markus, aarne} at cs dot chalmers dot se -- Stability : alpha -- Portability : Haskell98 -- -- Top-level for the Java back end. -- -- > $Id: JavaTop15.hs,v 1.12 2007/01/08 18:20:23 aarne Exp $ ------------------------------------------------------------------- module BNFC.Backend.Java ( makeJava ) where ------------------------------------------------------------------- -- Dependencies. ------------------------------------------------------------------- import System.Directory ( createDirectory ) import System.IO.Error ( isAlreadyExistsError ) import System.Exit ( exitFailure ) import System.FilePath (pathSeparator) import BNFC.Utils import BNFC.CF import BNFC.Options as Options import BNFC.Backend.Base import BNFC.Backend.Java.CFtoCup15 ( cf2Cup ) import BNFC.Backend.Java.CFtoJLex15 import BNFC.Backend.Java.CFtoJavaAbs15 ( cf2JavaAbs ) import BNFC.Backend.Java.CFtoJavaPrinter15 import BNFC.Backend.Java.CFtoVisitSkel15 import BNFC.Backend.Java.CFtoComposVisitor import BNFC.Backend.Java.CFtoAbstractVisitor import BNFC.Backend.Java.CFtoFoldVisitor import BNFC.Backend.Java.CFtoAllVisitor import Data.Char import Data.List(intersperse) import qualified BNFC.Backend.Common.Makefile as Makefile ------------------------------------------------------------------- -- | Build the Java output. -- FIXME: get everything to put the files in the right places. -- Adapt Makefile to do the business. ------------------------------------------------------------------- makeJava :: SharedOptions -> CF -> MkFiles () makeJava options cf = do -- Create the package directories if necessary. let packageBase = case inPackage of Nothing -> name Just p -> p ++ "." ++ name packageAbsyn = packageBase ++ "." ++ "Absyn" dirBase = pkgToDir packageBase dirAbsyn = pkgToDir packageAbsyn let absynFiles = remDups $ cf2JavaAbs packageBase packageAbsyn cf absynBaseNames = map fst absynFiles absynFileNames = map (dirAbsyn ++) absynBaseNames absynFuns = [ f | (_,ps) <- cf2data cf, (f,_) <- ps ] let writeAbsyn (filename, contents) = mkfile (dirAbsyn ++ filename ++ ".java") contents mapM writeAbsyn absynFiles mkfile (dirBase ++ "PrettyPrinter.java") $ cf2JavaPrinter packageBase packageAbsyn cf mkfile (dirBase ++ "VisitSkel.java") $ cf2VisitSkel packageBase packageAbsyn cf mkfile (dirBase ++ "ComposVisitor.java") $ cf2ComposVisitor packageBase packageAbsyn cf mkfile (dirBase ++ "AbstractVisitor.java") $ cf2AbstractVisitor packageBase packageAbsyn cf mkfile (dirBase ++ "FoldVisitor.java") $ cf2FoldVisitor packageBase packageAbsyn cf mkfile (dirBase ++ "AllVisitor.java") $ cf2AllVisitor packageBase packageAbsyn cf mkfile (dirBase ++ "Test.java") $ javaTest packageBase packageAbsyn cf --- mkfile ("Test" ++ name) $ "java " ++ dirBase ++ "Test $(1)" let (lex, env) = cf2jlex packageBase packageAbsyn cf mkfile (dirBase ++ "Yylex") lex liftIO $ putStrLn " (Tested with JLex 1.2.6.)" mkfile (dirBase ++ name ++ ".cup") $ cf2Cup packageBase packageAbsyn cf env -- FIXME: put in a doc directory? liftIO $ putStrLn $ " (Parser created for category " ++ show (firstEntry cf) ++ ")" liftIO $ putStrLn " (Tested with CUP 0.10k)" Makefile.mkMakefile options $ makefile name dirBase dirAbsyn absynFileNames where remDups [] = [] remDups ((a,b):as) = case lookup a as of Just {} -> remDups as Nothing -> (a, b) : (remDups as) pkgToDir :: String -> FilePath pkgToDir s = replace '.' pathSeparator s ++ [pathSeparator] make = Options.make options inPackage = Options.inPackage options name = Options.lang options -- FIXME get filenames right. -- FIXME It's almost certainly better to just feed all the Java source -- files to javac in one go. -- Replace with an ANT script? makefile :: String -> FilePath -> FilePath -> [String] -> String makefile name dirBase dirAbsyn absynFileNames = (++) (unlines [ "JAVAC = javac", "JAVAC_FLAGS = -sourcepath .", "", "JAVA = java", "JAVA_FLAGS =", "", "CUP = java_cup.Main", "CUPFLAGS = -nopositions -expect 100", "", "JLEX = JLex.Main", "" ]) $ Makefile.mkRule "all" [ "test" ] [] $ Makefile.mkRule "test" ("absyn":(map (dirBase ++) [ "Yylex.class", "PrettyPrinter.class", "Test.class", "ComposVisitor.class", "AbstractVisitor.class", "FoldVisitor.class", "AllVisitor.class", "parser.class", "sym.class", "Test.class"])) [] $ Makefile.mkRule ".PHONY" ["absyn"] [] $ Makefile.mkRule "%.class" [ "%.java" ] [ "${JAVAC} ${JAVAC_FLAGS} $^" ] $ Makefile.mkRule "absyn" [absynJavaSrc] [ "${JAVAC} ${JAVAC_FLAGS} $^" ] $ Makefile.mkRule (dirBase ++ "Yylex.java") [ dirBase ++ "Yylex" ] [ "${JAVA} ${JAVA_FLAGS} ${JLEX} " ++ dirBase ++ "Yylex" ] $ Makefile.mkRule (dirBase ++ "sym.java " ++ dirBase ++ "parser.java") [ dirBase ++ name ++ ".cup" ] [ "${JAVA} ${JAVA_FLAGS} ${CUP} ${CUPFLAGS} " ++ dirBase ++ name ++ ".cup" , "mv sym.java parser.java " ++ dirBase ] $ Makefile.mkRule (dirBase ++ "Yylex.class") [ dirBase ++ "Yylex.java", dirBase ++ "sym.java" ] [] $ Makefile.mkRule (dirBase ++ "sym.class") [ dirBase ++ "sym.java" ] [] $ Makefile.mkRule (dirBase ++ "parser.class") [ dirBase ++ "parser.java" , dirBase ++ "sym.java" ] [] $ Makefile.mkRule (dirBase ++ "PrettyPrinter.class") [ dirBase ++ "PrettyPrinter.java" ] [] -- FIXME $ Makefile.mkRule "clean" [] [ "rm -f " ++ dirAbsyn ++ "*.class" ++ " " ++ dirBase ++ "*.class" , "rm -f " ++ ".dvi " ++ name ++ ".aux " ++ name ++ ".log " ++ name ++ ".ps " ++ " *.class" ] $ Makefile.mkRule "distclean" [ "vclean" ] [] $ Makefile.mkRule "vclean" [] [ " rm -f " ++ absynJavaSrc ++ " " ++ absynJavaClass , " rm -f " ++ dirAbsyn ++ "*.class" -- , "rm -f " ++ "Test" ++ name , " rmdir " ++ dirAbsyn , " rm -f " ++ unwords (map (dirBase ++) [ "Yylex", name ++ ".cup", "Yylex.java", "VisitSkel.java", "ComposVisitor.java", "AbstractVisitor.java", "FoldVisitor.java", "AllVisitor.java", "PrettyPrinter.java", "Skeleton.java", "Test.java", "sym.java", "parser.java", "*.class"]) , "rm -f Makefile" , "rmdir -p " ++ dirBase ] "" where absynJavaSrc = unwords (map (++ ".java") absynFileNames) absynJavaClass = unwords (map (++ ".class") absynFileNames) javaTest :: String -> String -> CF -> String javaTest packageBase packageAbsyn cf = unlines [ "package" +++ packageBase ++ ";", "import java_cup.runtime.*;", "import" +++ packageBase ++ ".*;", "import" +++ packageAbsyn ++ ".*;", "import java.io.*;", "", "public class Test", "{", " public static void main(String args[]) throws Exception", " {", " Yylex l = null;", " parser p;", " try", " {", " if (args.length == 0) l = new Yylex(System.in);", " else l = new Yylex(new FileReader(args[0]));", " }", " catch(FileNotFoundException e)", " {", " System.err.println(\"Error: File not found: \" + args[0]);", " System.exit(1);", " }", " p = new parser(l);", " /* The default parser is the first-defined entry point. */", " /* You may want to change this. Other options are: */", " /* " ++ (concat (intersperse ", " (showOpts (tail eps)))) ++ " */", " try", " {", " " ++ packageAbsyn ++ "." ++ show def +++ "parse_tree = p.p" ++ show def ++ "();", " System.out.println();", " System.out.println(\"Parse Succesful!\");", " System.out.println();", " System.out.println(\"[Abstract Syntax]\");", " System.out.println();", " System.out.println(PrettyPrinter.show(parse_tree));", " System.out.println();", " System.out.println(\"[Linearized Tree]\");", " System.out.println();", " System.out.println(PrettyPrinter.print(parse_tree));", " }", " catch(Throwable e)", " {", " System.err.println(\"At line \" + String.valueOf(l.line_num()) + \", near \\\"\" + l.buff() + \"\\\" :\");", " System.err.println(\" \" + e.getMessage());", " System.exit(1);", " }", " }", "}" ] where eps = allEntryPoints cf def = head eps showOpts [] = [] showOpts (x:[]) = if normCat x /= x then [] else ['p' : (identCat x)] showOpts (x:xs) = if normCat x /= x then (showOpts xs) else ('p' : (identCat x)) : (showOpts xs)