// main.beh -- Main function and global variables. // $Id: main.beh,v 1.13 2000/10/13 19:36:21 dougo Exp $ Main { /** The main entry point. */ public static Vector main(String args[]) (@ return Program.main(args); @) } Program { (@ static void usage() { System.err.println( "Usage: demeterj [-code] [-weaver] [-grammar] [-javacc|-mparse]\n" + " [-printvisitor] [-copyvisitor] [-displayvisitor]\n" + " [-subgraphvisitor] [-equalvisitor] [-tracevisitor]\n" + " [-tie] [-tao] [-outputdir dir] prog.cd [ prog.beh ... ]\n" ); } static Program prog; static PrintWriter out; static File gendir = null; static File classdir = null; static File ckfile = null; static PrintWriter log = new PrintWriter(System.out, true); public static boolean dbg=false; /** Produce aspects, grammar, and/or code. Return a vector of weaver files produced, relative to the current directory. */ public static Vector main(String args[]) { Vector aspectfiles = null; try { parseOptions(args); ensureOutputDirectory(); readChecksums(ckfile); readClassDictionary(); addGenericVisitorsToClassDictionary(); simplifyClassDictionary(); if (!sanityCheckClassDictionary()) { System.err.println("Errors in class dictionary, aborting..."); System.exit(-1); } readBehaviorFilesAndMarkDerivedParts(); aspectfiles = makeGenericVisitorsAndGenerateCode(); if (aspectfiles == null) { System.err.println("Errors while generating code, aborting..."); System.exit(-1); } generateGrammarFile(); writeChecksums(ckfile); log.println("Done."); } catch (IOException exc) { System.exit(-1); } catch (Throwable err) { err.printStackTrace(); System.exit(-1); } prog = null; // don't hang onto all the data! return aspectfiles; } private static boolean arg_err = false; private static boolean no_opts = true, gen_code, gen_aspect, gen_grammar, gen_parser, gen_TAO, gen_univis, gen_printvis, gen_copyvis, gen_subgraphvis, gen_equalvis, gen_displayvis, gen_tracevis, got_beh; private static File cdfile; private static Vector behfiles = new Vector(); private static String grammar = "Parser.jj"; static final int NONE = 0, JAVACC = 1, MPARSE = 2; static int grammar_type = NONE; /** 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 '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 '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 'j': if (grammar_type != NONE) { System.err.println("Please specify only one of " + "-javacc or -mparse."); arg_err = true; } else { grammar_type = JAVACC; } break; case 'm': if (grammar_type != NONE) { System.err.println("Please specify only one of " + "-javacc or -mparse."); arg_err = true; } else { grammar_type = MPARSE; } 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 's': gen_subgraphvis = true; 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 'w': gen_aspect = true; no_opts = false; break; 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")) { behfiles.addElement(new File(curarg)); } 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(); } // Set defaults. if (gendir == null) gendir = new File("gen"); if (classdir == null) classdir = gendir; if (ckfile == null) ckfile = new File(gendir, "checksums"); if (grammar_type == NONE) grammar_type = JAVACC; // MPARSE; } /** 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 ParseException, 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 subgraphvis = "SubgraphVisitor"; 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_subgraphvis || gen_equalvis) { log.print(" " + subgraphvis); log.flush(); prog.addSubgraphVisitor(subgraphvis); } 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_subgraphvis || 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(); } /** Check the class dictionary for obvious problems that would otherwise lead to compilation errors. Return false if there are any problems. */ private static boolean sanityCheckClassDictionary() { boolean success = true; log.println("Checking for duplicate part names..."); success = success && prog.checkForDuplicatePartNames(); return success; } /** 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 ParseException { 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 ParseException { 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. Return vector of aspect files if successful; otherwise, return null. Uses: gen_code no_opts gen_univis got_beh gen_TAO gendir */ private static Vector makeGenericVisitorsAndGenerateCode() throws ParseException { Vector aspectfiles = null; if (gen_code || gen_aspect || no_opts) { makeGenericVisitorBehaviorFiles(); readBehaviorFiles(); if (!expandTraversals()) return null; 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 + "..."); aspectfiles = prog.generateCode(true); } if (gen_code) { log.println("Generating Java code to " + gendir + "..."); prog.generateCode(false); aspectfiles = new Vector(); } } return aspectfiles; } /** 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_subgraphvis || gen_equalvis) { File subgraphvisfile = new File(gendir, subgraphvis + ".beh"); prog.generateSubgraphVisitor(subgraphvis, subgraphvisfile); behfiles.addElement(subgraphvisfile); } 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 boolean expandTraversals() { log.println("Building cyclic class graph..."); prog.buildCyclicClassGraph(); 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..."); if (!prog.expandStaticTraversals()) { return false; } } if (gen_TAO) { log.println("TAO2 things being done..."); Program.genTAO2(); } return true; } /** 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); } } /** 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(); } @) }