% GenProg.lhs - program generation module for the HPG % @(#)GenProg.lhs 1.20 dated 92/07/20 at 17:30:37 % Crown Copyright 1992 \section{Generating the program} \label{genprog} This module gathers the others together to generate and print the program. \begin{haskell} > module Main ( > main > ) where > import Config > import Types > import Env > import Utils > import GenType > import GenVal > import GenExp > import System -- 1.3 > import IO -- 1.3 \end{haskell} \prog{main} is the name of the main \HPG\ function. The Haskell report requires that the entry point to the program executable is called \prog{main} and is of type \prog{IO ()e}. \begin{haskell} > main :: IO () > main = do > argv <- getArgs > parse_args defaultArgs (unlines argv) \end{haskell} The data type \prog{Args} is used to package up the command line arguments and give their types. \begin{haskell} > data Args = MkArgs (Int,Int,Int) Int Int Int Int Int String Output > deriving () \end{haskell} The parameters to the program generator, and their command line flags, are: \begin{enumerate} \item A 3-tuple of seeds for the random number generator --- \prog{s}. \item The number of type declarations to be generated and their depth --- \prog{nt} and \prog{dt} respectively. \item The number of value declarations to be generated and their maximum depth --- \prog{nv} and \prog{dv} respectively. \item The depth of the expressions to be generated --- \prog{de}. \item A module name, \prog{mn}, for the output program. \item A stream, \prog{op}, to which the program will be written. \end{enumerate} The default values of these parameters are given by \prog{defaultArgs}. \begin{haskell} > defaultArgs :: Args > defaultArgs = MkArgs (9807,65,32975) 4 4 4 4 4 "Main" default_output \end{haskell} \prog{parse\_args} parses values passed to \prog{hpg} from the command line. It is edited from output produced by \prog{mkhprog}, a command line parser generator (see~\cite{north} for further details). \begin{haskell} > parse_args :: Args -> String -> IO () > parse_args (MkArgs x1 x2 x3 x4 x5 x6 x7 x8) ('-':'s':rest) > = readval reads > (\val -> parse_args (MkArgs val x2 x3 x4 x5 x6 x7 x8)) rest > parse_args (MkArgs x1 x2 x3 x4 x5 x6 x7 x8) ('-':'n':'t':rest) > = readval reads > (\val -> parse_args (MkArgs x1 val x3 x4 x5 x6 x7 x8)) rest > parse_args (MkArgs x1 x2 x3 x4 x5 x6 x7 x8) ('-':'d':'t':rest) > = readval reads > (\val -> parse_args (MkArgs x1 x2 val x4 x5 x6 x7 x8)) rest > parse_args (MkArgs x1 x2 x3 x4 x5 x6 x7 x8) ('-':'n':'v':rest) > = readval reads > (\val -> parse_args (MkArgs x1 x2 x3 val x5 x6 x7 x8)) rest > parse_args (MkArgs x1 x2 x3 x4 x5 x6 x7 x8) ('-':'d':'v':rest) > = readval reads > (\val -> parse_args (MkArgs x1 x2 x3 x4 val x6 x7 x8)) rest > parse_args (MkArgs x1 x2 x3 x4 x5 x6 x7 x8) ('-':'d':'e':rest) > = readval reads > (\val -> parse_args (MkArgs x1 x2 x3 x4 x5 val x7 x8)) rest > parse_args (MkArgs x1 x2 x3 x4 x5 x6 x7 x8) ('-':'m':rest) > = readstring (\str -> parse_args (MkArgs x1 x2 x3 x4 x5 x6 str x8)) rest > parse_args (MkArgs x1 x2 x3 x4 x5 x6 x7 x8) ('-':'o':rest) > = readstring (\str -> parse_args > (MkArgs x1 x2 x3 x4 x5 x6 x7 (set_output str))) rest > parse_args (MkArgs x1 x2 x3 x4 x5 x6 x7 x8) "" > = hpg x1 x2 x3 x4 x5 x6 x7 x8 > parse_args (MkArgs x1 x2 x3 x4 x5 x6 x7 x8) _ > = usage defaultArgs \end{haskell} \prog{hpg s nt dt nv dv de mn} generates a program with features as described above. It prints to the given output stream first a header giving the \HPG\ version number and the supplied parameters, and then the generated program. \begin{haskell} > hpg :: (Int, Int, Int) -> Int -> Int -> Int -> Int -> Int -> String > -> Output -> Answer > hpg s nt dt nv dv de mn op > = print_str (head "") > (gen_types max_vnts max_flds nt dt c1) (make_Env s op) > where > c1 = gen_vals nv dv c2 > c2 = gen_exps de print_program > head = showString "-- HPG version " . version > . newline > . showString "-- Output from hpg " > . sep_list space id > [shows s, shows nt, shows dt, shows nv, shows dv, > shows de] > . newline > . sep_list newline id [shead, thead, vhead, ehead] > . newline . newline . mhead . newline . newline > shead = showString "-- Random number generator seeds: " . shows s > thead = showString "-- " . shows nt . showString " types, of depth " > . shows dt > vhead = showString "-- " . shows nv . showString " values, of depth " > . shows dv > ehead = showString "-- Expression depth: " . shows de > mhead = mod_name . space . showString mn . space . lbrack > . main_name . rbrack . space . where_name . space . lbrace \end{haskell} \subsection{Printing programs} This section deals with printing of the generated program. \prog{print\_program vnes} prints a program consisting of the type and value declarations in the environment, followed by a test of the equivalence of the (value name, expression) pairs in \prog{vnes}. \begin{haskell} > print_program :: Xscont (Val_name, Expression) > print_program vnes > = get_all_type_decls (\tds -> get_all_val_decls (\vds -> > print_str (split_str line_len > ((sep_list dsep id (map showsType_decl tds > ++ map showsVal_decl vds) > . dsep ) "")) > (print_test vnes))) > where > dsep = decl_sep . newline \end{haskell} \prog{print\_test vnes} prints a function, called \prog{main}, which prints a list of \prog{bool}, one for each (value name, expression) pair in \prog{vnes}. The value of the \prog{bool} corresponding to \prog{(vn,e)} is \prog{vn = e}. \begin{haskell} > print_test :: Xscont (Val_name, Expression) > print_test vnes > = print_str (split_str line_len > ((main_name . val_def . showString print_name > . space . lsq > . sep_list list_separator showspair vnes . rsq > . newline . rbrace) > "")) finish > where > showspair (vn, e) = showString vn . space . showString eq_name > . space . shows e \end{haskell} \subsection{Auxiliary functions} This section contains the auxiliary functions used in parsing command line arguments. The functions are generated by \prog{mkhprog} (see~\cite{north} for further details). \prog{readstring} reads a string from the command line. \begin{haskell} > readstring :: (String -> String -> IO ()) -> String -> IO () > readstring f "" = f "" "" > readstring f cs@(c:cs') > = f s t > where > st = if c == '\n' then cs' else cs > (s,t1) = span ((/=) '\n') st > t = if t1 == "" then t1 else (tail t1) \end{haskell} \prog{readval} reads a value of arbitrary type from the command line. It is used for reading integers and the random number generator seed values. \begin{haskell} > readval :: (Read a) => ReadS a -> (a -> String -> IO ()) -> String > -> IO () > readval readsfn f str > = case thing of > [] -> usage defaultArgs > (_:_) -> f val (if str' == "" then str' else (tail str')) > where > thing = readsfn str > (val, str') = head thing \end{haskell} \prog{usage} is called if the command line contains invalid flags or values. It prints a message giving a template for usage of \prog{hpg}. \begin{haskell} > usage :: Args -> IO () > usage (MkArgs s nt dt nv dv de mn _) > = hPutStr stderr > ("Usage: hpg [-s (Int,Int,Int)] [-nt Int] [-dt Int] \ > \[-nv Int] [-dv Int] [-de Int] [-m String] [-o String]\n\ > \ -s random number generator seeds (default " ++ show s ++ ")\n\ > \ -nt number of types to be generated (" ++ show nt ++ ")\n\ > \ -dt depth of generated types (" ++ show dt ++ ")\n\ > \ -nv number of values to be generated (" ++ show nv ++ ")\n\ > \ -dv depth of values to be generated (" ++ show dv ++ ")\n\ > \ -de depth of expressions to be generated (" ++ show de ++ ")\n\ > \ -m output module name (" ++ mn ++ ")\n\ > \ -o output file name (stdout)\n") \end{haskell}