main.beh.html

Program{
	(@
    static final String version = "0.6.4 with HTML";
    static void version() {
      System.err.println("Demeter/Java version " + version);
    }
    static void usage() {
      System.err.println(
 "Usage: demjava [-code] [-aspect] [-grammar] [-parser] [-compile]\n" +
 "               [-printvisitor] [-copyvisitor] [-displayvisitor]\n" +
 "               [-equalvisitor] [-tracevisitor] [-tie] [-tao]\n" +
 "		 [-outputdir dir] [-classdir dir] [-htmldir dir] prog.cd\n" +
 "               [ prog.beh ... ] [ prog.input ... ]\n" +
 "       demjava -version");
    }

    static Program prog;
    static PrintWriter out;
    static File gendir = new File("gen");
    static File classdir = new File("gen");
    static File ckfile = new File(gendir, "checksums");
    static PrintWriter log = new PrintWriter(System.out, true);
    public static boolean dbg=false;
    static boolean html = false;  // is html output desired?

    /** The main entry point. */
    public static void main(String args[]) {
      try {
	parseOptions(args);
	ensureOutputDirectory();
	readChecksums(ckfile);
	readClassDictionary();
	addGenericVisitorsToClassDictionary();
	simplifyClassDictionary();
	readBehaviorFilesAndMarkDerivedParts();
	makeGenericVisitorsAndGenerateCode();
	generateGrammarFile();
	writeChecksums(ckfile);
	generateParser();
	compile();
        generateHtml();
	log.println("Done.");
      } catch (IOException exc) {
	System.exit(-1);      
      } catch (Throwable err) {
	err.printStackTrace();
	System.exit(-1);
      }
    }

    private static boolean arg_err = false;
    private static boolean no_opts = true,
		   gen_code, gen_aspect, gen_grammar, gen_parser, compile,
		   gen_TAO, gen_univis, gen_printvis, gen_copyvis,
		   gen_equalvis, gen_displayvis, gen_tracevis, got_beh;
    private static File cdfile;
    private static Vector behfiles = new Vector();
    private static String grammar = "grammar.jj";
  
    /** Parse the command line options, setting global static variables.
	This should probably be converted to use JavaCC or something.
	Returns true iff the command line is valid.
	Sets several global flags and filenames.
     */
    static private void parseOptions(String args[]) throws IOException {
      for (int arg = 0; arg < args.length; arg++) {
	String curarg = args[arg];
	if (curarg.charAt(0) == '-') {
	  switch (curarg.charAt(1)) {
	  case 'a': gen_aspect = true; no_opts = false; break;
	  case 'c': 
	    switch (curarg.charAt(2)) {
	    case 'l':
	      if (++arg >= args.length) {
		System.err.println("Please specify a class directory.");
		arg_err = true;
	      } else {
		classdir = new File(args[arg]);
	      }
	      break;
	    case 'o':
	      switch (curarg.charAt(3)) {
	      case 'd': gen_code = true; no_opts = false; break;
	      case 'm': compile = true; break;
	      case 'p': gen_copyvis = true; break;
	      default: invalidOption(curarg); break;
	      }
	      break;
	    default: invalidOption(curarg); break;
	    }
	    break;
          case 'd': 
            switch (curarg.charAt(2)) {
            case 'i': gen_displayvis = true; break;
            case 'e': dbg = true; break;
            default: invalidOption(curarg); break;
            }
            break;
	  case 'e': gen_equalvis = true; break;
	  case 'g': gen_grammar = true; no_opts = false; break;
          case 'h':
            if (++arg >= args.length)
            {
              System.err.println("Please specify an output directory.");
              arg_err = true;
            }
            else
            {
              html = true;
              htmldir = new File(args[arg]);
            }
            break;
	  case 'o':
	    if (++arg >= args.length) {
	      System.err.println("Please specify an output directory.");
	      arg_err = true;
	    } else {
	      gendir = new File(args[arg]);
	    }
	    break;
	  case 'p':
	    switch (curarg.charAt(2)) {
	    case 'a': gen_parser = true; break;
	    case 'r': gen_printvis = true; break;
	    default: invalidOption(curarg); break;
	    }
	    break;
	  case 't':
	    switch (curarg.charAt(2)) {
	    case 'a': gen_TAO = true; break;
	    case 'i': System.setErr(System.out); break;
	    case 'r': gen_tracevis = true; break;
	    default: invalidOption(curarg); break;
	    }
	    break;
	  case 'v':
	    version();
	    System.exit(0);
	  default:
	    invalidOption(curarg); break;
	  }
	} else if (curarg.endsWith(".cd")) {
	  if (cdfile != null) {
	    System.err.println("Please specify only one class dictionary " +
			       "file (prog.cd).");
	    arg_err = true;
	  } else {
	    cdfile = new File(curarg);
	  }
	} else if (curarg.endsWith(".beh")) {
          File behfile = new File(curarg);
	  behfiles.addElement(behfile);
	  origbehfiles.addElement(behfile); // for HTML, since behfiles reused
	} else if (curarg.endsWith(".input")) {
          // HTML: optional list of input files
          File inputfile = new File(curarg);
          inputfiles.addElement(inputfile);
	} else if (cdfile != null || behfiles.size() != 0) {
          arg_err = true;
	} else {
	  // Figure out the filenames, old-style syntax
	  cdfile = new File(curarg + ".cd");
	  behfiles.addElement(new File(curarg + ".beh"));
          grammar = curarg + ".jack";
	}
      }
      if (arg_err || cdfile == null) {
	usage();
	// We should really use our own class of Exceptions...
	throw new IOException();
      }
    }

    /** Print an error message for an invalid option. */
    private static void invalidOption(String option) {
      System.err.println("Invalid option: " + option);
      arg_err = true;
    }

    /** Make sure the output directory exists and is writable. */
    private static void ensureOutputDirectory() throws IOException {
      if (!gendir.exists()) {
	log.println("Output directory " + gendir +
		    " does not exist, will create.");
	if (!gendir.mkdirs()) {
	  System.err.println("Error: could not create output directory.");
	  throw new IOException();
	}
      }
      if (gendir.exists()) {
	if (!gendir.isDirectory()) {
	  System.err.println("Error: " + gendir +
			     " exists but is not a directory.");
	} else if (!gendir.canWrite()) {
	  System.err.println("Error: " + gendir +
			     " exists but is not writable.");
	} else return;
	throw new IOException();
      }
    }

    /** Read the cd file and build the classdef table.
	Uses: cdfile
	Sets: prog
    */
    private static void readClassDictionary()
     throws ParseError, IOException
    {
      InputStream in = makeInputStream(cdfile);

      log.println("Parsing " + cdfile + "...");
      prog = parse(in);

      log.println("Building hashtable of classes...");
      prog.buildClassDefTable();

    }

    static ClassName univis = ClassName.parse("UniversalVisitor");
    static String printvis = "PrintVisitor";
    static String copyvis = "CopyVisitor";
    static String equalvis = "EqualVisitor";
    static String displayvis = "DisplayVisitor";
    static String tracevis = "TraceVisitor";

    /** Add generic visitors.
	Uses: gen_*vis
	Sets: gen_univis
    */
    private static void addGenericVisitorsToClassDictionary() {
      log.println("Adding generic visitors:");

      if (gen_printvis) {
	log.print(" " + printvis); log.flush();
	prog.addPrintVisitor(printvis);
      }

      if (gen_copyvis) {
	log.print(" " + copyvis); log.flush();
	prog.addCopyVisitor(copyvis);
      }

      if (gen_equalvis) {
	log.print(" " + equalvis); log.flush();
	prog.addEqualVisitor(equalvis);
      }

      if (gen_displayvis) {
	log.print(" " + displayvis); log.flush();
	prog.addDisplayVisitor(displayvis);
      }

      if (gen_tracevis) {
	log.print(" " + tracevis); log.flush();
	prog.addTraceVisitor(tracevis);
      }

      if (gen_printvis || gen_copyvis || gen_equalvis ||
	  gen_displayvis || gen_tracevis)
	gen_univis = true;

      if (gen_univis) {
	log.println(" " + univis);
	prog.addUniversalVisitor(univis);
      }
    }


    /** Simplify the class dictionary by various transformations. */
    private static void simplifyClassDictionary() {

      if (gen_TAO) {
	log.println("TAO1 things being done...");
	Program.genTAO1();
      }

      log.println("Marking not-parsed classes...");
      prog.markNotParsed();

      log.println("Marking visitor classes...");
      prog.markVisitors();

      log.println("Expanding parameterized classes...");
      prog.expandParamDefs();

      log.println("Converting repetition classes...");
      prog.convertRepetition();

      log.println("Filling in part names...");
      prog.fillInPartNames();

      log.println("Setting inheritance links...");
      prog.setInheritanceLinks();

      // Replace ".cd" with ".xcd" for writing out the expanded CD.
      // Just append ".xcd" if the filename doesn't end with ".cd".
      String xcdname = cdfile.toString();
      if (xcdname.endsWith(".cd"))
	xcdname = xcdname.substring(0, xcdname.length() - 3);
      File xcdfile = new File(gendir, xcdname + ".xcd");

      log.println("Saving expanded CD to " + xcdfile + "...");
      openOutputFile(xcdfile);
      prog.toAll(new PrintVisitor(out));
      closeOutputFile();
    }

    /** Read the given behavior files, attaching behavior to
	appropriate class defs and recording derived parts.  This has
	to happen whether we're generating code or grammar, and before
	we generate the generic visitors.
	Uses: behfiles
	Sets: got_beh
    */
    private static void readBehaviorFilesAndMarkDerivedParts()
      throws ParseError
    {
      readBehaviorFiles();

      log.println("Filling in visitor names...");
      prog.fillInVisitorNames();

      log.println("Marking derived parts...");
      prog.markDerivedParts();
    }

    /** Read the behavior files and attach the behavior to
        appropriate class defs.
	Uses: behfiles
	Sets: got_beh
    */
    private static void readBehaviorFiles() throws ParseError {
      Enumeration behfileEnum = behfiles.elements();
      boolean newline = false;
      if (behfileEnum.hasMoreElements()) {
	log.println("Reading behavior files:");
	newline = true;
      }
      while (behfileEnum.hasMoreElements()) {
	File behfile = (File) behfileEnum.nextElement();
	try {
	  InputStream in = makeInputStream(behfile);

	  log.print(" " + behfile); log.flush();
	  ProgramBehavior beh = ProgramBehavior.parse(in);

	  beh.collectBehavior(behfile.toString());
	  got_beh = true;
	} catch (IOException exc) {
	  // Just skip over unreadable files.
	}
      }
      if (newline) log.println();
    }

    /** Make generic visitors, expand traversals, and generate the
	.java and core aspect files.
	Uses: gen_code no_opts gen_univis got_beh gen_TAO gendir
    */
    private static void makeGenericVisitorsAndGenerateCode()
      throws ParseError
    {
      if (gen_code || gen_aspect || no_opts) {
	makeGenericVisitorBehaviorFiles();
	readBehaviorFiles();
	expandTraversals();

        log.println("Adding Enumeration thingies to Repetition classes");
        prog.addEnumerationStuff(); // in repetition.beh     

	log.println("Adding Subtraversal class.");
	prog.addSubtraversalClass(); // in wrapper.beh

	if (gen_aspect) {
	  log.println("Generating core aspect code to " + gendir + "...");
	  prog.generateCode(true, false);
	}

	if (gen_code) {
	  log.println("Generating Java code to " + gendir + "...");
	  prog.generateCode(false, false);
	}
      }
    }

    /** Generate a .beh file for each generic visitor.
	Uses: gendir, gen_*vis, *vis
	Sets: behfiles
    */
    private static void makeGenericVisitorBehaviorFiles() {
      log.println("Making generic visitor behavior files...");
      behfiles = new Vector();
 
      if (gen_univis) {
	File univisfile = new File(gendir, univis + ".beh");
	prog.generateUniversalVisitor(univis, univisfile);
	behfiles.addElement(univisfile);
      }

      if (gen_printvis) {
	File printvisfile = new File(gendir, printvis + ".beh");
	prog.generatePrintVisitor(printvis, printvisfile);
	behfiles.addElement(printvisfile);
      }

      if (gen_copyvis) {
	File copyvisfile = new File(gendir, copyvis + ".beh");
	prog.generateCopyVisitor(copyvis, copyvisfile);
	behfiles.addElement(copyvisfile);
      }

      if (gen_equalvis) {
	File equalvisfile = new File(gendir, equalvis + ".beh");
	prog.generateEqualVisitor(equalvis, equalvisfile);
	behfiles.addElement(equalvisfile);
      }

      if (gen_displayvis) {
	File displayvisfile = new File(gendir, displayvis + ".beh");
	prog.generateDisplayVisitor(displayvis, displayvisfile);
	behfiles.addElement(displayvisfile);
      }

      if (gen_tracevis) {
	File tracevisfile = new File(gendir, tracevis + ".beh");
	prog.generateTraceVisitor(tracevis, tracevisfile);
	behfiles.addElement(tracevisfile);
      }
    }

    private static String unitrav = "universal";

    /** Expand traversals.
	Uses: gen_univis got_beh gen_TAO
    */
    private static void expandTraversals() {
      log.println("Building traversal graph...");
      prog.buildTraversalGraph();

      log.println("Converting adaptive methods...");
      prog.convertAdaptiveMethods();

      log.println("Building visitor tables...");
      prog.buildVisitorTables();

      if (gen_univis) {
	log.println("Expanding universal traversal...");
	prog.expandUniversalTraversal(unitrav, univis);
      }

      if (got_beh) {
	log.println("Expanding static traversals...");
	prog.expandStaticTraversals();
      }

      if (gen_TAO) {
	log.println("TAO2 things being done...");
	Program.genTAO2();
      }
    }

    /** Generate the JavaCC grammar file.
	Uses: gen_grammar no_opts gendir grammar
    */
    private static void generateGrammarFile() {
      if (gen_grammar || no_opts) {
	File gramfile = new File(gendir, grammar);
	log.println("Generating grammar to " + gramfile + "...");
	prog.generateGrammar(gramfile);
      }
    }

    /** Generate the parser by invoking JavaCC on the grammar file.
        Uses: gen_parser gendir grammar
    */
    private static void generateParser() throws Exception {
      if (gen_parser) {
	File gramfile = new File(gendir, grammar);
	log.println("Generating parser from " + gramfile +
		    " to " + gendir + "...");
	// HACK: temporarily change the current working directory,
	// because MacMain ignores the -OUTPUT_DIRECTORY option. (!)
	Properties p = System.getProperties();
	String cwd = p.getProperty("user.dir");
	p.put("user.dir", gendir.toString());
	COM.sun.labs.javacc.MacMain.main(new String[]
		   { "-OUTPUT_DIRECTORY="+gendir, gramfile.toString() });
	p.put("user.dir", cwd);
      }
    }

    /** Compile the Java files by invoking javac on them.
	Uses: compile classdir javaFiles (defined in utils.beh)
    */
    private static void compile() {
      if (compile) {
	log.println("Compiling to " + classdir + "...");
	Vector changedFileNames = new Vector();	// vector of Strings
	Enumeration e = javaFiles.elements();
	while (e.hasMoreElements()) {
	  File file = (File) e.nextElement();
	  /* should check for changed... */
	  changedFileNames.addElement(file.toString());
	}
	int numfiles = changedFileNames.size();
	String options[] = {
	  "-d", classdir.toString(),
	  "-deprecation", "-g",
	  "-classpath", "" + classdir + File.pathSeparatorChar +
			System.getProperty("java.class.path")
	};
	String args[] = new String[numfiles + options.length];
	changedFileNames.copyInto(args);
	System.arraycopy(options, 0, args, numfiles, options.length);
	sun.tools.javac.Main compiler = new sun.tools.javac.Main(System.err, "javac");
	compiler.compile(args);
      }
    }

    /** Make sure file exists and is readable. */
    private static InputStream makeInputStream(File file)
      throws IOException
    {
      if (!file.exists()) {
	System.err.println("Error: Input file " + file + " does not exist.");
      } else if (!file.canRead()) {
	System.err.println("Error: Input file " + file +
			   " exists but is not readable.");
      } else if (!file.isFile()) {
	System.err.println("Error: Input file " + file +
			   " is not a plain file.");
      } else try {
	FileInputStream in = new FileInputStream(file);
	return in;
      } catch (FileNotFoundException exc) {
	// This shouldn't happen...
	throw new RuntimeException(exc.toString());
      }
      throw new IOException();
    }	

  @)
}