package edu.neu.ccs.demeterf.demfgen;

import edu.neu.ccs.demeterf.lib.*;
import edu.neu.ccs.demeterf.demfgen.classes.*;
import edu.neu.ccs.demeterf.demfgen.dgp.DGPFunc;
import edu.neu.ccs.demeterf.demfgen.pcdgp.PCDGPFunc;
import edu.neu.ccs.demeterf.demfgen.traversals.Travs;
import edu.neu.ccs.demeterf.dispatch.*;
import edu.neu.ccs.demeterf.*;
import edu.neu.ccs.demeterf.demfgen.ClassGen.ArityPair;
import edu.neu.ccs.demeterf.demfgen.ClassHier.InhrtPair;
import edu.neu.ccs.demeterf.util.Util;
import edu.neu.ccs.demeterf.util.CLI;

/** DemFGen Main program/class */
public class DemFGenMain{
    /** Simple (shorter) Print to Std-Error */
    static void p(String s){ if(!Diff.optionSet(Diff.quiet))System.err.print(s); }
    /** Create a String of spaces with the given width */
    static String gap(int len){
       if(len <= 0)return "";
       return " "+gap(len-1);
    /** Quit/Exit due to some error (print before calling) */
    public static void abort(){ System.exit(1); }
    /** Print the DemFGen Header for the given language (e.g., "Java" or "C#") */
    public static void header(String lang){
      p("\n                 \u25e4------------------------------\u25e5"+
        "\n                 |         DemeterF "+lang+gap(12-lang.length())+"|"+
        "\n                 |          "+Diff.buildDate+"          |"+
        "\n                 \u25e3------------------------------\u25e2\n\n");
    static void timing(String what, long start){
        //System.out.println(pad(what+": ",30)+(System.currentTimeMillis()-start));
    /** Main entry point for DemFGen. Parses the CD and BEH files, manages arguments,
     *    and generates classes.  All the real stuff is implemented below, and in separate
     *    classes in the "demeterf.demfgen.*" classes. */
    public static void main(String args[], String lang) throws Exception {
      long time = System.currentTimeMillis();
      List<String>[] all = CLI.splitArgs(args);
      List<String> opts = all[CLI.OPTS];
      String nonOpt[] = all[CLI.ARGS].toArray(new String[all[CLI.ARGS].length()]);
      if(nonOpt.length != 3)usage(false,"Not enough arguments");
      List<String> unknown = CLI.invalidOptions(opts, Diff.validOptions);
      if(!unknown.isEmpty())usage(false,"Unknown Option(s): "+unknown.toString(", ", ""));
          cdFile = nonOpt[0],
          behFile = nonOpt[1],
          outDir = nonOpt[2];
      // Trim annoying final separator
      if(outDir.lastIndexOf(File.separatorChar) == outDir.length()-1)
          outDir = outDir.substring(0,outDir.length()-1);
      final String dir = outDir;
          long starter = System.currentTimeMillis();
          final List<CDFile> CDs = resolveCDFile(cdFile);
          final List<BehDef> BEHs = resolveBEHFile(behFile);
          final PackageDef basepkg =;
          boolean parser = !Diff.optionSet(Diff.noparse);
              p("\n !! Warning: No Root Package Specified\n"+
                      "     Parser/DGP classes may not be accessible\n\n");
          List<ArityPair> arities = CDs.fold(new List.Fold<CDFile,List<ArityPair>>(){
              public List<ArityPair> fold(CDFile f, List<ArityPair> r){
                  return TypeCheck.defsToList(f.getTypes()).append(r);
              }}, ClassGen.Primitives);
          TypeCheck tc = new TypeCheck();
          Travs.TheFactory.makeTypeCheckTrav(tc).traverse(CDs, new TypeCheck.Env(arities));
              p("\n ** Full Class Dictionary:\n     "+Print.PrintM(CDs).replace("\n","\n     "));
              p("\n ** Generating DOT Graph...\n");
              GraphGen.genGraph(CDs, dir);
              List<InhrtPair> inhrt = DemFGenMain.subtypes(CDs);
              String dgpMeths = "";
              int dgp = OptStart.index(opts, Diff.dgp+":");
              if(dgp >= 0){
                  p(" ** Loading DGP Functions...\n     [");
                  List<DGPFunc> funcs = loadDGP(CLI.separateOption("dgp",opts), new DGPLoad(),"DGP");
                  p("]\n ** Generating DGP Functions...\n     [");
                  dgpMeths = funcs.foldl(new List.Fold<DGPFunc, String>(){
                      public String fold(DGPFunc f, String s){
                          return (f.method(;
                  starter = System.currentTimeMillis();

                  List<PCDGPFunc> funcs = List.create();
                  if(OptStart.index(opts, Diff.pcdgp+":") >= 0){
                      p(" ** Loading PCDGP Functions...\n     [");
                      funcs = loadDGP(CLI.separateOption("pcdgp",opts),new PCDGPLoad(),"PCDGP");
                  p(" ** Generating Classes...\n");
                  starter = System.currentTimeMillis();
                  ClassGen.genClasses(CDs, BEHs, dir, dgpMeths, opts, inhrt, funcs, basepkg);
                  p(" ** Generating Parser...\n");
                  ParseGen.genParser(CDs, dir, inhrt, basepkg);
                  int lib = OptStart.index(opts, Diff.lib+":");
                  List<String> pkgdirs = CDs.foldr(new List.Fold<CDFile,List<String>>(){
                     public List<String> fold(CDFile cd, List<String> r){
                         return (cd.getTypes().anyGen())?
                                 r.push(DemFGenMain.pkgdir(dir, cd.getPkg(), true)):r;
                  }, List.<String>create());
                  Make.make(dir, basepkg, parser && !Diff.optionSet(Diff.noparsecc), opts, lib, pkgdirs);
              p(" ** Finished ["+seconds(System.currentTimeMillis()-time)+" sec.]\n");
      }catch(RE re){ error(re,"Runtime");
      }catch(RTParseException pe){ error(pe,"Parse");
      }catch( fe){ error(fe,"File");
      }catch(RTFileNotFound fe){ error(fe,"File");
      }catch(TE te){ error(te,"Type"); }
    /** Pad the given string out to the give size (on the right) */
    public static String pad(String s, int i){
        if(s.length() >= i)return s;
        return pad(s+" ",i);
    /** Return the number of Seconds from the given Milliseconds */
    public static double seconds(long mil){
        return ((int)(mil/10.0))/100.0;
    /** Does the given argument start with the given option prefix (usually "--")? */
    static class OptStart extends List.Pred<String>{
        String start;
        OptStart(String s){ start = s; }
        public boolean huh(String s){ return s.startsWith(start); }
        static int index(List<String> l, String start){
           return l.index(new OptStart(start));
    /** Print an Error Message (default) */
    static void error(Throwable t, String type){
      p("\n\n !! "+type+" Error:\n"+t.getMessage()+"\n\n");
     ***   Some of these methods are quite useful...  ***
    /** Recursively resolve any included CD files, starting with the given file name. */
    public static List<CDFile> resolveCDFile(String file) throws FileNotFoundException{
           return resolveCDFile(new FileInputStream(file), file);
        }catch(ParseException pe){
           throw new RTParseException(" ** CD File: \""+file+"\"\n"+pe.getMessage());
        }catch(TokenMgrError te){
           throw new RTParseException(" ** CD File: \""+file+"\"\n"+te.getMessage());
    /** Recursively resolve any included CD files from the given InputStream, starting
     *    with the given file name. The name is kept around for ParseError feedback
     *    to the user. */
    public static List<CDFile> resolveCDFile(InputStream in, String name) throws ParseException{
        CDFile main = CDFile.parse(in);
        List<CDFile> CDs = IncludeCDs.resolveCDs(main.getIncl(),name);
        ImportList imports = IncludeCDs.allImports(CDs.push(main));
        return CDs.push(main.updateImports(imports));
    /** Recursively resolve any included BEH files, starting with the given file name. */
    public static List<BehDef> resolveBEHFile(String file) throws FileNotFoundException{
           return resolveBEHFile(new FileInputStream(file), file);
        }catch(ParseException pe){
           throw new RTParseException(" ** BEH File: \""+file+"\"\n"+pe.getMessage());
        }catch(TokenMgrError te){
           throw new RTParseException(" ** BEH File: \""+file+"\"\n"+te.getMessage());
    /** Recursively resolve any included BEH files from the given InputStream, starting
     *    with the given file name. The name is kept around for ParseError feedback
     *    to the user. */
    public static List<BehDef> resolveBEHFile(InputStream in, String name) throws ParseException{
        BehFile beh = BehFile.parse(in);
        return beh.getBehs().toList().append(IncludeCDs.resolveBEHs(beh.getIncl(),name));
    /** Print the full usage information for DemFGen */
    static void usage(boolean help,String bad){
        (help?"":" !! "+bad+"\n\n")+
        " ** Usage: DemFGen [Options] <CD-File> <BEH-File> <Output-Dir>\n"+
        (!help?" ** Use --help for a full description\n":
        " The order/placement of options doesn\'t matter, but the relative\n"+
        "    order of the manditory ones must be as shown.\n\n"+
        " CD-File contains the CD to be generated,\n"+
        " BEH-File contains the behavior to be used with classes,\n"+
        " Output-Dir is the directory to place generated files\n\n"+
        " Options can be: \n"+
        "   --help            : Print this usage information.\n"+
        "   --build           : "+Diff.d.makeInfo+"\n"+
        "   --lib:FILE        : Implies --build, Create a library named FILE from the\n"+
        "                         compiled/generated code (JAR for Java, DLL for C#)\n"+
        "   --windows         : Use Windows based command launches and CSC to compile\n"+
        "                         C# instead of GMCS. System PATH and CLASSPATH\n"+
        "                         variables need to be set correctly. Works with both\n"+
        "                         '--build' and '--lib'\n"+
        "   --noshell         : Use Java to run JavaCC, instead of trying to run commands\n"+
        "                         through BASH or Windows CMD\n"+
        "   --nogen           : Don\'t generate the classes... just DGP and Parser\n"+
        "   --noparse         : Don\'t generate the parser files or methods\n"+
        "   --noequals        : Don\'t generate the equals() methods\n"+
        "   --mutable         : Generate 'mutable' fields... not final/readonly\n"+
        "   --graph           : Just print a Dot file to StdOut\n"+
        "   --show            : Print the final deep parsed CD(s)\n"+
        "   --dgp:C1:...:Cn   : A Colon seperated List of Data-generic function\n"+
        "                         classes to be run/generated. Each class must be a\n"+
        "                         subclass of ...demeterf.demfgen.dgp.DGPFunc\n"+
        "                         and accessible in the current classpath.\n"+
        "       * Predefined classes are:\n"+
        "         - Objects to Strings, Each one can be used as a \"toString\" method\n"+
        "           by using the _ToString version\n"+
        "              Print, PrintToString         : in complete CD syntax\n"+
  	    "              PrintHeap, PrintHeapToString : Uses very little Java stack\n"+
  	    "                                             Same method Names as above\n"+
        "              PrintIter                    : Use iteration when possible\n"+
        "              Display, DisplayToString     : nested field/type information\n"+
        "              ToStr, ToString              : simple nested constructors\n"+
        "              ToXML                        : simple XML output\n"+
        "         - Useful methods\n"+
        "              HashCode                     : add deep hashcode methods\n"+        
        "         - Static Code Generation:\n"+
        "              StaticTrav     : Create Static Traversal Class/Methods\n"+
        "                 use --nocontrol to eliminate Traversal control overhead\n"+
        "              StaticTravCtx  : Static Traversal with a context/argument\n"+
        "              StaticTP       : Create a static TP Class for the CD\n"+
        "              StaticTU       : Create a static TU Class for the CD\n"+
        "       * See the demfgen.dgp package source for more details.\n\n"+
        "   --pcdgp:C1:...:Cn : A Colon seperated List of Per-Class Data-generic function\n"+
        "                         classes to be run on each TypeDef. Classes must be a\n"+
        "                         subclass of ...demeterf.demfgen.pcdgp.PCDGPFunc\n"+
        "                         and accessible in the current classpath.\n"+
        "       * Predefined classes are:\n"+
        "         - Getters  : Add getters for all fields. For 'field' the getter is\n"+
        "                        created as 'getField()'.\n"+
        "         - Updaters : Adds an Update method for each field that returns an\n"+
        "                        updated instance; all other fields are the same, but\n"+
        "                        the selected field is replaced.\n"+
        "         - Creator  : Addes a static Creator method that mimics the constructor.\n"+
        "         - Setters  : Add setters for all fields.  ** The '--mutable' option\n"+
        "                        must be used in order for this to work.  Otherwise, try\n"+
        "                        using the Updaters instead.\n"+
        "       * See the demfgen.pcdgp package source for more details.\n")+
    /** Load/Instantiation Exception */
    static class LoadException extends RuntimeException{
        public LoadException(String s){ super(s); }
        public LoadException(Throwable e){ super(e); }
    /** Load a specific instance of the given class, and check/cast if needed */
    static abstract class Loader<X> extends List.Map<String, X>{
        public X map(String n){
            try{Class<?> c = Type.classForName(n); return instance(c);
            }catch(Exception e){throw new RuntimeException(e); }
        abstract X instance(Class<?> c) throws Exception;
    static class DGPLoad extends Loader<DGPFunc>{
        DGPFunc instance(Class<?> c) throws Exception{
                p(""+c.getSimpleName()+", ");
                return (DGPFunc)(c.newInstance());
            throw new LoadException("Unknown DGPFunc");
    static class PCDGPLoad extends Loader<PCDGPFunc>{
        PCDGPFunc instance(Class<?> c) throws Exception{
                p(""+c.getSimpleName()+", ");
                java.lang.reflect.Constructor<?> constr = c.getConstructor(new Class[]{List.class});
                return (PCDGPFunc)(constr.newInstance(new Object[]{List.<String>create()}));
            throw new LoadException("Unknown PCDGPFunc");
    /** Lookup the given DGP function classes */
    static <X> List<X> loadDGP(String names[], final Loader<X> load, final String typ){
       return List.create(names).reverse().map(new List.Map<String, X>(){
           public X map(String name){
               try{ return;
               }catch(TypeSearchException e)
               { p("\n !! DGP Error: Couldn\'t Find function class: \'"+name+"\'\n\n"); }
               catch(LoadException e)
               { p("\n !! DGP Error: Class \'"+name+"\' isn\'t a subclass of "+typ+"Func\n\n");
               }catch(Exception e)
               { p("\n !! DGP Error: Couldn\'t create function: \'"+name+"\'\n\n"); }
               return null;
    /** When traversing just return a list of TypeDefs */
    static class JustTypes extends TU<List<TypeDef>>{
      public List<TypeDef> combine(){ return List.create(); }
      public List<TypeDef> fold(List<TypeDef> a, List<TypeDef> b){ return a.append(b); }
      List<TypeDef> combine(TypeDef t){ return combine().push(t); }
    /** Get just the TypeDefs from a List of DemFGenMains */
    public static List<TypeDef> justTypes(List<CDFile> CD){
        List<TypeDef> types = new JustTypes().traverse(CD);
        return types;
    /** Flatten the classes so that fields are represented in all concrete
     *    classes (i.e., push common fields down to subtypes) */
    public static List<TypeDef> flatten(List<CDFile> CD, final List<InhrtPair> inhrt){
        List<TypeDef> types = new Traversal(new ID(){
            TypeDef combine(TypeDef td){ return td; }
            ClassDef combine(ClassDef cd, DoGen g, ident n, TypeDefParams ps, PESubtypeList sts, FieldList fs, Impl i){
                return new ClassDef(g,n,ps,sts,
                        fs.append(ClassHier.superFieldsAndSyntax(ps.toList(), inhrt, ""+n)
                                .foldr(new List.Fold<FieldOrSyntax, FieldList>(){
                                    public FieldList fold(FieldOrSyntax f, FieldList r){ return r.push(f); }
                                }, new FieldEmpty())), i);    
            Empty<TypeDef> combine(Empty<TypeDef> e){ return e; }
            List<TypeDef> combine(List<TypeDef> l, TypeDef f, List<TypeDef> r){ return r.push(f); }
        return types;
    /** Remove all the syntax froma given CD */
    public static List<TypeDef> removeSyntax(List<TypeDef> types){
        return Factory.newTraversal(new TP(){
            FieldList combine(FieldList l, Syntax s, FieldList r){ return r; }
    /** DGP generation function class */
    public static class DGPGen extends FC{
        final List<TypeDef> types;
        final List<BehDef> BEHs;
        final String dir, imports, suff;
        final PackageDef pkg;
        public DGPGen(List<CDFile> CD, List<BehDef> Bs, String d, List<InhrtPair> inhrt){
            types = flatten(CD, inhrt);
            BEHs = Bs;
            imports = ""+IncludeCDs.allImports(CD);
            pkg =;
            suff ="";
            dir = DemFGenMain.pkgdir(d, pkg, true);
        public String combine(List<DGPFunc> e, String f, String r){ return f+r; }
        public String combine(Empty<DGPFunc> e){ return ""; }
        public String combine(DGPFunc f){
            long starter = System.currentTimeMillis();
            String name = f.fileName();
            String extra = behBody(BEHs, name);
            DGPFunc.Trav trav = f.traversalObj(extra);
            String ret = trav.traverseOption_List_TypeDef__(Option.some(types));
            // Removed to support inlined DGP traversals
            //   DFGTrav trav = Factory.newTraversal(f.functionObj(extra), f.control());
            //   String ret = trav.<String>traverseOption_List_TypeDef__(Option.some(types));
            // Make sure the package dir exists if needed
            Util.writeFile(name, "."+f.fileSuffix(), ret+suff+"\n", dir,
            timing("    DGP-"+name+": ",starter);
            return (""+name+", ");
    /** Calculate the directory for the package based on command line args  */
    public static String pkgdir(String dir, PackageDef pkg, boolean create){
        if(pkg.hasPkg() && !Diff.optionSet(Diff.flatdirs)){
            String d = dir+File.separator+pkg.dir();
            // Make sure it exists...
            return d;
        return dir;
    /** Run each of the DGP function classes and allow them to create files */
    static void doDGPGen(List<DGPFunc> funcs, List<CDFile> CDs, List<BehDef> BEHs, String dir, List<InhrtPair> inhrt){
        DGPGen gen = new DGPGen(CDs,BEHs,dir,inhrt);
        String names = Travs.TheFactory.makeDGPGenTrav(gen).traverse(funcs);
    /** Find BEH attached to a giv en Name */
    static class FindBeh extends List.Pred<BehDef>{
        String name;
        FindBeh(String n){ name = n; }
        public boolean huh(BehDef b){ return name.equals(""+b.getName()); }
    /** Find the BEH body for the given class/interface */
    public static String behBody(List<BehDef> behs, String c){
        List<BehDef> match = behs.filter(new FindBeh(c));
        return match.fold(new List.Fold<BehDef, String>(){
            public String fold(BehDef b, String r){
                return b.getBody().getText()+r;
    /** Instantiate a type definition with the given environment */
    private static TypeDef instantiate(TypeDef td, List<StrLTrip.StrPair> env){
        return new Traversal(new Subst()).<TypeDef>traverse(td,env);
    /** Type instantiation Function Class */
    public static List<FieldOrSyntax> instantiate(List<FieldOrSyntax> fs, List<String> bnds, List<String> uses){
        List<StrLTrip.StrPair> env = List.Zip<String,String,StrLTrip.StrPair>(){
            public StrLTrip.StrPair zip(String b, String u){ return new StrLTrip.StrPair(b,u); }
        }, uses);
        return new Traversal(new Subst()).<List<FieldOrSyntax>>traverse(fs,env);
    /** Instantiate a generic type definition.  Basically replace all occurences of the
     *    TypeParameters using the environment implied by the given TypeUse */
    public static TypeDef instantiate(TypeUse tu, List<TypeDef> aT){
        TypeDef def = aT.find(new FindType(""+tu.getName()));
            dpar[] = def.typeParams().toArray(),
            upar[] = tu.getTparams().toArray();
        if(dpar.length != upar.length){
            throw new RuntimeException("Wrong number of Type parameters for "+
          "\n  - Given "+upar.length+" in "+tu.print()+"!"+
                    "\n Use: "+tu.print()+
                    "\n Def: "+def.print()+"\n");
        List<StrLTrip.StrPair> env = makeEnv(dpar,upar,0);
        return instantiate(def,env);
    /**  Predicate to search for a type definition by String name */
    static class FindType extends List.Pred<TypeDef>{
        String name;
        FindType(String n){ name = n; }
        public boolean huh(TypeDef t){ return name.equals(""; }
        public String toString(){ return "typedef["+name+"]"; }
    /** Find a type by Ident name */
    static class FIdent extends List.Pred<StrLTrip.StrPair>{
        String name;
        FIdent(ident look){ name = ""+look; }
        public boolean huh(StrLTrip.StrPair t){ return name.equals(t.n);}
    /** Substitution class, replaces TypeUses and DefParams using the given 
     *    Environment context */
    public static class Subst extends StaticTP{
        String lookup(ident name, List<StrLTrip.StrPair> env){
            FIdent find = new FIdent(name);
                return env.find(find).b;
            return name.toString();
        TypeUse combine(TypeUse t, ident name, EmptyUseParams ps, List<StrLTrip.StrPair> env){
            return TypeUse.makeType(lookup(name,env));
        NameDef combine(NameDef d, ident i, Bound b, List<StrLTrip.StrPair> env){
            return new NameDef(new ident(ClassGen.unlocal(lookup(i,env))), b);
    /** Create an environment from definition variables and actual uses */
    public static List<StrLTrip.StrPair> makeEnv(String[] d, String[] u, int i){
        if(i == d.length)return List.<StrLTrip.StrPair>create();
        return makeEnv(d,u,i+1).push(new StrLTrip.StrPair(d[i],u[i]));
    /** Return a list of all the suptypes from the given CDFiles... */
    public static List<ClassHier.InhrtPair> subtypes(List<CDFile> CD){
        return CD.fold(new List.Fold<CDFile,List<ClassHier.InhrtPair>>(){
            public List<ClassHier.InhrtPair> fold(CDFile f, List<ClassHier.InhrtPair> r){
                return CollectInherit.inheritPairs(f.getTypes()).append(r);
            } }, List.<ClassHier.InhrtPair>create());