import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.Reader; import java.util.Arrays; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.TreeMap; import java.util.Properties; import java.util.Random; import java.util.Set; import java.util.logging.Level; //-----------------------------------------------------------------------// // Main class: //-----------------------------------------------------------------------// /** * Provides a <code>driver</code> to translate a Bali source file into a * Javacc source file. * * @layer<bali2javacc> */ public class Main { final private static String PROPERTIES = "bale2javacc.properties" ; /** * Handles argument parsing, source parsing, parse-tree collection, * and code generation. * * @return {@link $TEqn.Collector} with collected data from parse tree. * * @layer<bali2javacc> */ public Object driver( String[] args ) throws Throwable { setVersion( "v2002.09.04" ) ; Files.setProgram( "bali2javacc" ) ; Files.setVersion( getVersion() ) ; loadProperties( PROPERTIES ) ; try { parseArguments( Arrays.asList( args ) ) ; } catch ( Exception exception ) { usage( exception.getMessage() ) ; throw exception ; } Main.DEBUG.info( "-output " + String.valueOf( argOutputFile ) ) ; Main.DEBUG.info( "-package " + String.valueOf( argPackage ) ) ; Main.DEBUG.info( "source " + String.valueOf( argSourceFile ) ) ; Collector collector = collectSource( argSourceFile ) ; collector.setPackage( argPackage ) ; generateObject( collector ) ; Main.DEBUG.exiting( "bali2javacc.Main", "driver", "collector" ) ; return collector ; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /** * Given an {@link Object}, returns a {@link String} containing the * normalized {@link Class} name for the object. * * @layer<bali2javacc> */ private static String className( Object object ) { return ( object != null ) ? object.getClass().getName() : "null" ; } /** * Processes an input {@link File} (with Bali source code) into a parse * tree, then running a {@link $TEqn.Collector} over the tree to gather * all necessary data for later code generation. * * @return {@link $TEqn.Collector} with collected data from parse tree. * * @layer<bali2javacc> */ private Collector collectSource( File inpFile ) throws IOException, ParseException { Main.DEBUG.entering( "bali2javacc.Main", "collectSource", inpFile ) ; Reader reader = new BufferedReader( new InputStreamReader( new FileInputStream( inpFile ), "ISO-8859-1" ) ) ; Parser parser = Parser.getInstance( reader ) ; BaliParse tree = (BaliParse) parser.parseAll () ; reader.close() ; Collector collector = new Collector() ; collector.dispatch( tree ) ; Main.DEBUG.exiting( "bali2javacc.Main", "collectSource" ) ; return collector ; } /** * Generates the object code {@link File} objects (Jak class files for * parse tree nodes) from a {@link $TEqn.Collector} argument. * * @layer<bali2javacc> */ public void generateObject( Collector collector ) throws IOException { Main.DEBUG.entering( "bali2javacc.Main", "generateObject" ) ; Files.toFile( collector.toString(), argOutputFile ) ; Main.DEBUG.exiting( "bali2javacc.Main", "generateObject" ) ; } public void loadProperties( String source ) { Main.DEBUG.entering( "bali2javacc.Main", "loadProperties", source ) ; // Allow users to rename resource via system properties: // String resource = System.getProperty( source, source ) ; // Load properties from properties resource: // Properties properties = new Properties() ; try { properties.load( ClassLoader.getSystemResourceAsStream( resource ) ) ; Main.DEBUG.info( "properties loaded from resource " + resource ) ; } catch ( NullPointerException thrown ) { return ; // Resource not found is ok. } catch ( IOException thrown ) { IllegalStateException exception = new IllegalStateException( "resource error " + resource ) ; exception.initCause( thrown ) ; throw exception ; } // Create a new system Properties object containing the current // system properties, but using the application Properties as // defaults. This way, properties defined on the command line // (which show up as system properties) override application // properties defined via relatively static sources such as // resources and files. // Properties newSystem = new Properties( properties ) ; newSystem.putAll( System.getProperties() ) ; System.setProperties( newSystem ) ; Main.DEBUG.exiting( "bali2javacc.Main", "loadProperties" ) ; } /** * Processes a {@link List} of {@link String} arguments that specify * the input files and output file. * * @see #usage() * * @layer<bali2javacc> */ private void parseArguments( List args ) throws IOException { // Set default values: // argDebug = Level.OFF ; // How much debugging output. argOutputFile = null ; // JavaCC output file. argPackage = null ; // Generated package name. argSourceFile = null ; // Bali source file. for ( ListIterator p = args.listIterator() ; p.hasNext() ; ) { String arg = ( String ) p.next() ; if ( arg.equals( "-debug" ) ) { argDebug = Level.INFO ; if ( p.hasNext() ) { String peek = ( String ) p.next() ; if ( peek.charAt( 0 ) != '-' ) argDebug = Level.parse( peek.toUpperCase() ) ; else p.previous() ; } continue ; } if ( arg.equals( "-output" ) && p.hasNext() ) argOutputFile = parseOutputFile( ( String ) p.next() ) ; else if ( arg.equals( "-o" ) && p.hasNext() ) argOutputFile = parseOutputFile( ( String ) p.next() ) ; else if ( arg.equals( "-package" ) && p.hasNext() ) argPackage = parsePackage( ( String ) p.next() ) ; else if ( arg.equals( "-p" ) && p.hasNext() ) argPackage = parsePackage( ( String ) p.next() ) ; else if ( arg.charAt( 0 ) == '-' ) throw new IllegalArgumentException( "invalid: " + arg ) ; else argSourceFile = parseSourceFile( arg ) ; } Main.DEBUG.setLevel( argDebug ) ; if ( argSourceFile == null ) { String message = "no Bali source file specified" ; throw new IllegalArgumentException( message ) ; } if ( argOutputFile == null ) { String name = argSourceFile.getName() ; int dot = name.lastIndexOf( '.' ) ; name = ( ( dot < 0 ) ? name : name.substring( 0, dot ) ) + ".jj" ; argOutputFile = new File( argSourceFile.getParentFile(),name ) ; argOutputFile = validateOutputFile( argOutputFile ) ; } } private File parseOutputFile( String fileName ) { if ( argOutputFile != null ) throw new IllegalArgumentException( "option \"-output\" appears more than once" ) ; return validateOutputFile( new File( fileName ) ) ; } private String parsePackage( String packageName ) { if ( argPackage != null ) throw new IllegalArgumentException( "option \"-package\" appears more than once" ) ; if ( ! packageName.matches( "^[\\p{Alpha}_$][\\p{Alnum}_$]*$" ) ) throw new IllegalArgumentException( "option \"-package\" doesn't specify an identifier" ) ; return packageName ; } private File parseSourceFile( String fileName ) { if ( argSourceFile != null ) throw new IllegalArgumentException( "more than one Bali source file specified" ) ; File file = new File( fileName ) ; if ( ! file.exists() ) throw new IllegalArgumentException( "file doesn't exist: " + fileName ) ; if ( ! file.canRead() ) throw new IllegalArgumentException( "file can't be read: " + fileName ) ; return file ; } private void usage( String message ) { String program = className( this ) ; if ( message != null ) System.err.println( program + ": " + message ) ; System.err.println( "Usage: java " + program + " [-output <JavaCC-output-file>]" + " [-package <Java-package-name>]" + " <Bali-source-file>" ) ; } private File validateOutputFile( final File outputFile ) { if ( ! outputFile.exists() ) return outputFile ; if ( ! outputFile.canWrite() ) throw new IllegalArgumentException( "file \"" + outputFile + "\" cannot be written" ) ; if ( outputFile.isDirectory() ) throw new IllegalArgumentException( "file \"" + outputFile + "\" is not a normal file" ) ; return outputFile ; } private Level argDebug = Level.OFF ; // How many debug messages. private String argPackage = null ; // Generated package name. private File argOutputFile = null ; // Output file. private File argSourceFile = null ; // Bali source file. }