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.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.logging.Level; //-----------------------------------------------------------------------// // Main class: //-----------------------------------------------------------------------// /** * Provides a <code>driver</code> to translate a Bali source file into a * set of Jak source files. * * @layer<bali2jak> */ public class Main { final private static String PROPERTIES = "bali2jak.properties" ; /** * Handles argument parsing, source parsing, parse-tree collection, * and code generation. * * @return {@link $TEqn.Collector} with collected data from parse tree. * * @layer<bali2jak> */ public Object driver( String[] args ) throws Throwable { setVersion( "v2002.09.03" ) ; Files.setProgram( "bali2jak" ) ; Files.setVersion( getVersion() ) ; loadProperties( PROPERTIES ) ; try { parseArguments( Arrays.asList( args ) ) ; } catch ( Exception exception ) { usage( exception.getMessage() ) ; throw exception ; } Main.DEBUG.info( "-layer " + String.valueOf( argLayer ) ) ; Main.DEBUG.info( "-directory " + String.valueOf( argDirectory ) ) ; Main.DEBUG.info( "source " + String.valueOf( argSourceFile ) ) ; Collector collector = collectSource( argSourceFile ) ; generateObject( collector ) ; Main.DEBUG.exiting( "bali2jak.Main", "driver", "collector" ) ; return collector ; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /** * Given an {@link Object}, returns a {@link String} containing the * normalized {@link Class} name for the object. * * @layer<bali2jak> */ 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<bali2jak> */ private Collector collectSource( File inpFile ) throws IOException, ParseException { Main.DEBUG.entering( "bali2jak.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( "bali2jak.Main", "collectSource", collector ) ; return collector ; } /** * Generates the object code {@link File} objects (Jak class files for * parse tree nodes) from a {@link $TEqn.Collector} argument. * * @layer<bali2jak> */ public void generateObject( Collector collector ) throws IOException { Main.DEBUG.entering( "bali2jak.Main", "generateObject" ) ; collector.setLayer( argLayer ) ; // Generate syntax tree node classes for Bali grammar rules: // collector.generateNonterminals( argDirectory ) ; Main.DEBUG.exiting( "bali2jak.Main", "generateObject" ) ; } public void loadProperties( String source ) { Main.DEBUG.entering( "bali2jak.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( "bali2jak.Main", "loadProperties" ) ; } /** * Processes a {@link List} of {@link String} arguments that specify * the input files and output file. * * @see #usage() * * @layer<bali2jak> */ private void parseArguments( List args ) throws IOException { // Set default values: // argLayer = null ; // Generated layer name. argDebug = Level.OFF ; // How much debugging output. argDirectory = null ; // Output directory. 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( "-layer" ) && p.hasNext() ) argLayer = parseLayer( ( String ) p.next() ) ; else if ( arg.equals( "-a" ) && p.hasNext() ) argLayer = parseLayer( ( String ) p.next() ) ; else if ( arg.equals( "-directory" ) && p.hasNext() ) argDirectory = parseDirectory( ( String ) p.next() ) ; else if ( arg.equals( "-d" ) && p.hasNext() ) argDirectory = parseDirectory( ( 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 ( argDirectory == null ) argDirectory = parseDirectory( "." ) ; if ( argLayer == null ) { File directory ; try { directory = argDirectory.getCanonicalFile() ; } catch ( IOException exception ) { directory = argDirectory.getAbsoluteFile() ; } argLayer = parseLayer( directory.getName() ) ; } return ; } private String parseLayer( String layerName ) { if ( argLayer != null ) throw new IllegalArgumentException( "option \"-layer\" appears more than once" ) ; if ( ! layerName.matches( "^[\\p{Alpha}_$][\\p{Alnum}_$]*$" ) ) throw new IllegalArgumentException( "option \"-layer\" doesn't specify an identifier" ) ; return layerName ; } private File parseDirectory( String directoryName ) { if ( argDirectory != null ) throw new IllegalArgumentException( "option \"-directory\" appears more than once" ) ; File directory = new File( directoryName ) ; if ( ! directory.exists() ) directory.mkdirs() ; if ( ! directory.isDirectory() ) throw new IllegalArgumentException( "file \"" + directory + "\" is not a directory" ) ; return directory ; } 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 + " [-layer <layer-name>]" + " [-directory <output-directory>]" + " <Bali-source-file>" ) ; } private Level argDebug = Level.OFF ; // How many debug messages. private String argLayer = null ; // Generated layer name. private File argDirectory = null ; // Output directory. private File argSourceFile = null ; // Bali source file. }