You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@etch.apache.org by di...@apache.org on 2008/11/16 23:35:38 UTC
svn commit: r718128 [4/6] - in /incubator/etch/branches/etch-python: ./
binding-csharp/compiler/src/main/java/etch/bindings/csharp/compiler/
binding-csharp/compiler/src/main/resources/etch/bindings/csharp/compiler/
binding-csharp/runtime/ binding-cshar...
Modified: incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/EtchCompiler.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/EtchCompiler.java?rev=718128&r1=718127&r2=718128&view=diff
==============================================================================
--- incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/EtchCompiler.java (original)
+++ incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/EtchCompiler.java Sun Nov 16 14:35:35 2008
@@ -17,644 +17,389 @@
package etch.compiler;
+import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
import java.util.HashSet;
-import java.util.LinkedList;
import java.util.List;
-import java.util.Scanner;
+import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
-import java.util.regex.Pattern;
import etch.compiler.ast.Module;
-
+import etch.compiler.ast.Name;
+import etch.compiler.ast.Opt;
+import etch.compiler.ast.Service;
+
+/**
+ * The main driver of the compiler.
+ */
public class EtchCompiler
{
-
- private static String ETCH_INCLUDE_PATH = "ETCH_INCLUDE_PATH";
- private static Integer ETCH_OUTPUT_COUNTER = 1;
-
/**
* Instantiate a new compiler
+ * @param cl the class loader to use to load the binding
*/
public EtchCompiler( ClassLoader cl )
{
this.cl = cl;
-
- // Default options
- ignoreGlobal = false;
- ignoreLocal = false;
- ignoreEnvIncludePath = false;
-
- initLogHandler();
- initIncludePath();
}
private final ClassLoader cl;
-
- private File getTempOutputDirectory() throws Exception
- {
- // TODO check
- String suffix = String.format("%d", ETCH_OUTPUT_COUNTER++);
- File tmpOutputDir = File.createTempFile("etch-outputdir", suffix);
- if (!tmpOutputDir.delete())
- {
- String msg = String.format("Could not initialize temporary output directory (delete): %s \n", tmpOutputDir);
- throw new Exception(msg);
- }
- if (!tmpOutputDir.mkdirs())
- {
- String msg = String.format("Could not initialize temporary output directory (create): %s \n", tmpOutputDir);
- throw new Exception(msg);
- }
- tmpOutputDir.deleteOnExit();
- String msg = String.format("Created temporary output directory: %s\n", tmpOutputDir);
- logHandler.logMessage( LogHandler.LEVEL_INFO, null, msg);
- return tmpOutputDir;
- }
-
- // logHandler
-
- private LogHandler logHandler;
- private void initLogHandler()
- {
- logHandler = new CompilerLogHandler( "Command" );
- }
-
- public void setQuiet(boolean value)
+ /**
+ * Runs the compiler using the specified input options.
+ * @param clo
+ * @throws Exception
+ */
+ public void run( CmdLineOptions clo ) throws Exception
{
- logHandler.setQuiet(value);
+ clo.cl = cl;
+
+ if (clo.lh == null)
+ {
+ clo.lh = new ConsoleLogHandler( "Command" );
+ clo.lh.setLevel(
+ clo.quiet ? LogHandler.LEVEL_WARNING : LogHandler.LEVEL_INFO );
+ }
+
+ if (!initBindingClass( clo ))
+ return;
+
+ if (!checkSourceFile( clo ))
+ return;
+
+ initOutput( clo );
+ clo.lh.push( clo.sourceFile.getName(), null );
+
+ try
+ {
+ compile( clo );
+
+ if (!clo.lh.hasError())
+ {
+ clo.lh.report( LogHandler.LEVEL_INFO, "Saving Resources..." );
+
+ saveOutput( clo );
+
+ if (!clo.lh.hasError())
+ clo.lh.report( LogHandler.LEVEL_INFO, "Saving Resources Done." );
+ else
+ clo.lh.report( LogHandler.LEVEL_INFO, "Saving Resources Failed." );
+ }
+ else
+ clo.lh.report( LogHandler.LEVEL_INFO, "Compile Failed." );
+ }
+ catch ( Exception e )
+ {
+ clo.lh.report( LogHandler.LEVEL_ERROR, "caught exception: %s", e );
+ e.printStackTrace();
+ }
+ finally
+ {
+ clo.lh.pop( clo.sourceFile.getName() );
+ }
}
-
- // includePath
-
- private List<File> includePath;
- private List<File> envIncludePath;
- private void initIncludePath()
- {
- includePath = new LinkedList<File>();
- envIncludePath = new LinkedList<File>();
+ private static boolean initBindingClass( CmdLineOptions clo )
+ {
+ if (clo.bindingClass != null)
+ return true;
+
+ String n = clo.binding;
+
+ if (n == null || n.length() == 0)
+ {
+ clo.lh.report( LogHandler.LEVEL_ERROR,
+ "Binding not specified." );
+ return false;
+ }
+
+ if (n.equalsIgnoreCase( "help" ))
+ {
+ // TODO find some way to list the bindings?
+ clo.lh.report( LogHandler.LEVEL_ERROR,
+ "Binding help not yet implemented." );
+ return false;
+ }
+
+ if (n.equalsIgnoreCase( "null" ))
+ {
+ clo.bindingClass = NullCompiler.class;
+ return true;
+ }
+
+ String cn = String.format( BINDING_FORMAT, n );
+
+ try
+ {
+ clo.bindingClass = clo.cl.loadClass( cn );
+ return true;
+ }
+ catch ( ClassNotFoundException e )
+ {
+ clo.lh.report( LogHandler.LEVEL_ERROR,
+ "Binding '%s' could not be loaded; class '%s' not in classpath.",
+ n, cn );
+ return false;
+ }
+ catch ( RuntimeException e )
+ {
+ clo.lh.report( LogHandler.LEVEL_ERROR,
+ "Binding '%s' could not be loaded; class '%s' threw exception %s",
+ n, cn, e );
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ private static boolean initBackend( CmdLineOptions clo )
+ {
+ try
+ {
+ clo.backend = (Backend) clo.bindingClass.newInstance();
+ return true;
+ }
+ catch ( Exception e )
+ {
+ clo.lh.report( LogHandler.LEVEL_ERROR,
+ "Binding '%s' could not be initialized; caught exception %s.",
+ clo.binding, e );
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ private final static String BINDING_FORMAT = "etch.bindings.%s.compiler.Compiler";
+
+ private static boolean checkSourceFile( CmdLineOptions clo )
+ {
+ File f = clo.sourceFile;
+
+ if (!f.isFile())
+ {
+ clo.lh.report( LogHandler.LEVEL_ERROR,
+ "Source file '%s' does not exist or is not a file.", f );
+ return false;
+ }
+
+ if (!f.getName().endsWith( ".etch" ))
+ {
+ clo.lh.report( LogHandler.LEVEL_ERROR,
+ "Source file '%s' must have .etch extension.", f );
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param etchHome command line specified location for the etch.
+ * @return sets up the class loader based on information from
+ * our environment.
+ * @throws Exception
+ */
+ public static ClassLoader setupClassLoader( File etchHome )
+ throws Exception
+ {
+ // get the current class path...
+
+ Set<String> mainClassPath = new HashSet<String>();
- // Harvest include path from envvar 'ETCH_INCLUDE_PATH'
-
- String tempIncludePathString = System.getenv(ETCH_INCLUDE_PATH);
- if (tempIncludePathString != null)
+ ClassLoader cl = EtchMain.class.getClassLoader();
+ if (cl instanceof URLClassLoader)
{
- envIncludePath = new LinkedList<File>();
- StringTokenizer tempPath = new StringTokenizer(tempIncludePathString, File.pathSeparator);
- while (tempPath.hasMoreTokens())
+ for (URL u: ((URLClassLoader) cl).getURLs())
{
- File temp = new File(tempPath.nextToken());
- if (temp.exists() && temp.isDirectory())
+ String s = u.toString();
+ if (s.startsWith( "file:" ) && s.endsWith( ".jar" ))
{
- try
- {
- envIncludePath.add(temp.getCanonicalFile());
- }
- catch ( IOException e )
+ File f = new File( u.toURI() );
+ if (f.isFile() && f.canRead())
{
- // ignore
+ s = f.getCanonicalPath();
+ mainClassPath.add( s );
+// System.out.println( "mainClassPath.add: "+s );
}
}
}
}
- }
-
- /**
- * add a directory to the ETCH_INCLUDE_PATH
- *
- * @param value path to add
- */
- public void addIncludePath(File value)
- {
- if (value.exists() && value.isDirectory())
- {
- try
- {
- includePath.add(value.getCanonicalFile());
- }
- catch ( IOException e )
- {
- // ignore
- }
- }
- }
-
- /**
- * return a list of the directories in the Etch include path
- *
- */
- public List<File> getIncludePath()
- {
- return getIncludePath(null);
- }
-
- /**
- * return a list of the directories in the Etch include path, inserting 'workingDirectory' at the beginning
- *
- * @param workingDirectory use this value as the workingDirectory path
- */
- public List<File> getIncludePath(File workingDirectory)
- {
- // Setup Etch include path from the environment variable, commandline options
- // and the working directory of the etch file itself.
- List<File> effectiveIncludePath = new LinkedList<File>();
- if (!ignoreEnvIncludePath)
- effectiveIncludePath.addAll(envIncludePath);
+ // search etch.home (if specified) for more jar files to add
+ // to a secondary class loader. exclude jar files on the main
+ // class path.
- effectiveIncludePath.addAll(includePath);
- if (workingDirectory != null)
- {
- try
- {
- effectiveIncludePath.add(0, workingDirectory.getCanonicalFile());
- }
- catch ( IOException e )
- {
- String msg = String.format("Could not add working directory %s to the includePath \n", workingDirectory);
- logHandler.logMessage ( LogHandler.LEVEL_WARNING, null, msg );
- }
- }
+ String s = etchHome != null ? etchHome.getCanonicalPath() : null;
- return effectiveIncludePath;
- }
-
- // outputDirectory
-
- private File outputDir;
-
- /**
- * set the output directory for generated source
- *
- * @param value directory to output source
- */
- public void setOutputDirectory(File value) throws Exception
- {
- if (value == null)
- {
- outputDir = null;
- return;
- }
-
- if (value.exists() && (!value.isDirectory() || !value.canWrite()))
- {
- // Not a directory, not writable
- // TODO better exception name
- throw new Exception("Specified output directory: '" + value.toString() + "' is not a directory or not writable \n");
- }
- outputDir = value.getCanonicalFile();
- }
-
- /**
- * return the name of the output directory
- *
- */
- public File getOutputDirectory()
- {
- return outputDir;
- }
-
- // templateOutputDirectory
-
- private File templateOutputDir;
-
- /**
- * set an alternative output directory for generated template source, i.e. IMPL and MAIN files
- *
- * @param value directory to output template source
- */
- public void setTemplateOutputDirectory(File value) throws Exception
- {
- if (value == null)
+ if (s == null || s.length() == 0)
{
- templateOutputDir = null;
- return;
+ // from java -Detch.home=...
+ s = System.getProperty( "etch.home" );
}
-
- if (!value.isDirectory() || !value.canWrite())
- {
- // Not a directory, not writable
- // TODO better exception name
- throw new Exception("Specified output directory: '" + value.toString() + "' is not a directory or not writable \n");
- }
- templateOutputDir = value.getCanonicalFile();
- }
-
- /**
- * return the name of the template output directory
- *
- */
- public File getTemplateOutputDirectory()
- {
- return templateOutputDir;
- }
-
- private boolean overwriteTemplate;
-
- /**
- * set the value of the OverwriteTemplate flag
- *
- * @param value
- */
- public void setOverwriteTemplate(boolean value)
- {
- overwriteTemplate = value;
- }
- /**
- * return the value of the OverwriteTemplate flag
- */
- public boolean getOverwriteTemplate()
- {
- return overwriteTemplate;
- }
-
- // binding
-
- private String bindingName;
- private Class<?> bindingClass;
-
- /**
- * set the name of the binding
- * The compiler will assume the binding class is 'etch.bindings.<name>.compiler.Compiler'
- *
- * @param value name of the binding (e.g., java, csharp, xml, ...)
- */
- public void setBindingName(String value)
- {
- bindingName = value;
- }
-
- /**
- * get the name of the binding
- *
- */
- public String getBindingName()
- {
- return bindingName;
- }
-
- /**
- * get the fully-qualified class name of the binding
- *
- */
- public String getBindingClassName()
- {
- if (bindingName == null)
- return null;
-
- // TODO perhaps allow user to specifiy a fully qualified classname for the binding as an option?
- return String.format("etch.bindings.%s.compiler.Compiler", bindingName);
- }
-
-
- private boolean initBindingClass() throws Exception
- {
-
- if (bindingClass != null)
- return true;
-
- try
- {
- bindingClass = cl.loadClass(getBindingClassName());
- // bindingClass = Class.forName(getBindingClassName());
- return true;
- }
- catch ( ClassNotFoundException e )
+ if (s == null || s.length() == 0)
{
- String msg = String.format("Binding '" + getBindingName() + "' could not be loaded; class '" + getBindingClassName() + "' not in classpath.\n");
- // TODO better exception
- e.printStackTrace();
- throw new Exception(msg);
+ s = System.getenv( "ETCH_HOME" );
}
- }
-
- private Class<?> getBindingClass() throws Exception
- {
- initBindingClass();
- return bindingClass;
- }
-
- private Backend getBindingClassInstance() throws Exception
- {
- initBindingClass();
- return (Backend) bindingClass.newInstance();
- }
-
- // what
-
- private Set<String> what = new HashSet<String>();
-
- /**
- * add to the list of what the compiler should produce, (e.g. 'client','server','helper')
- *
- * @param what a valid compiler product
- */
- public void addWhat(String value) throws Exception
- {
- what.add(value);
- if (value == "FORCE")
- overwriteTemplate = true;
- }
-
- /**
- * return the set of compiler products to be produced
- *
- */
- public Set<String> getWhat()
- {
- return what;
- }
-
- // ignoreGlobal
-
- private boolean ignoreGlobal;
-
- /**
- * set the value of the IgnoreGlobal flag
- *
- * @param value
- */
- public void setIgnoreGlobal(boolean value)
- {
- ignoreGlobal = value;
- }
-
- /**
- * return the value of the IgnoreGlobal flag
- */
- public boolean getIgnoreGlobal()
- {
- return ignoreGlobal;
- }
-
- // ignoreLocal
-
- private boolean ignoreLocal;
-
- /**
- * set the value of the IgnoreLocal flag
- *
- * @param value
- */
- public void setIgnoreLocal(boolean value)
- {
- ignoreLocal = value;
- }
-
- /**
- * return the value of the IgnoreLocal flag
- */
- public boolean getIgnoreLocal()
- {
- return ignoreLocal;
- }
-
- // ignoreEnvironmentIncludePath
-
- private boolean ignoreEnvIncludePath;
-
- /**
- * set the value of the IgnoreEnvironmentIncludePath flag
- *
- * @param value
- */
- public void setIgnoreEnvironmentIncludePath(boolean value)
- {
- ignoreEnvIncludePath = value;
- }
-
- /**
- * return value of the IgnoreEnvironmentIncludePath flag
- */
- public boolean getIgnoreEnvironmentIncludePath()
- {
- return ignoreEnvIncludePath;
- }
-
- // userWordsList
-
- private String userWordsList;
+
+ if (s != null && s.length() > 0)
+ s = s + File.separator + "lib";
- /**
- * specify a Users Words list
- *
- * @param value path to Users Words list file
- */
- public void setUserWordsList(File value) throws Exception
- {
- if (!value.isFile() || !value.canRead())
+ // System.out.println( "etch.home.lib = "+s );
+ if (s != null && s.length() > 0)
{
- // Not a file or not readable
- // TODO better Exception
- throw new Exception("The user words list specified: '" + value.toString() + "' is not a file or is not readable");
- }
- userWordsList = value.getCanonicalFile().getPath();
- }
-
- /**
- * return the path to the User Words list file
- */
- public String getUserWordsList()
- {
- return userWordsList;
- }
-
- // isMixinSuppressed
-
- private boolean isMixinSuppressed = false;
-
- /**
- * set the value of the MixinSuppressed flag
- *
- * @param value
- */
- public void setMixinSuppressed(boolean value)
- {
- isMixinSuppressed = value;
- }
-
- /**
- * return the value of the MixinSuppressed flag
- */
- public boolean getNoMixinArtifacts()
- {
- return isMixinSuppressed;
- }
-
- // mixinOutputDir
-
- private File mixinOutputDir;
+ File d = new File( s );
+
+ if (!d.isDirectory() || !d.canRead())
+ throw new IOException( String.format(
+ "Specified $ETCH_HOME/lib is not a directory: %s\n", s ) );
- /**
- * set the path for mixin outputs
- *
- * @param value path
- */
- public void setMixinOutputDirectory(File value) throws Exception
- {
- if (!value.isDirectory() || !value.canWrite())
- {
- // Not a directory or cannot write
- throw new Exception(String.format("Mixin output directory %s is not found or is not writable", value));
+ d = d.getCanonicalFile();
+
+ MyURLClassLoader ncl = new MyURLClassLoader( new URL[] {}, cl );
+ for (File f: d.listFiles())
+ {
+ if (!f.isFile())
+ continue;
+ String x = f.getCanonicalPath();
+ if (!x.endsWith( ".jar" ))
+ continue;
+ if (!mainClassPath.contains( x ))
+ ncl.addURL( f.toURI().toURL() );
+ }
+ cl = ncl;
}
- mixinOutputDir = value.getCanonicalFile();
- }
-
- /**
- * get the value of the mixin output directory
- *
- */
- public File getMixinOutputDirectory()
- {
- return mixinOutputDir;
+ return cl;
}
-
- // flattenPackages
-
- private boolean flattenPackages = true;
-
- /**
- * set the value of the FlattenPackages flag
- *
- * @param value
- */
- public void setFlattenPackages(boolean value)
- {
- flattenPackages = value;
- }
-
- /**
- * get the value of the FlattenPackages attribute
- */
- public boolean getFlattenPackages()
- {
- return flattenPackages;
- }
-
- // sourceFiles
- /**
- * validate that the path specified is a .etch file
- *
- * @param value filename
- */
- public void validateEtchFile(File value) throws Exception
+ private static class MyURLClassLoader extends URLClassLoader
{
-
- if (!value.isFile() || !value.canRead())
+ /**
+ * This is just a class loader where we can hang more urls to
+ * search.
+ * @param urls
+ * @param parent
+ */
+ public MyURLClassLoader( URL[] urls, ClassLoader parent )
{
- String msg = String.format("File '%s' does not exist or cannot be read.", value);
- // TODO better exception
- throw new Exception(msg);
+ super( urls, parent );
}
- String s = value.getName();
- if (!s.endsWith(".etch"))
+ @Override
+ public void addURL( URL url )
{
- String msg = String.format("File '%s' must have .etch extension", value);
- // TODO better exception
- throw new Exception(msg);
+ super.addURL( url );
}
}
- /****************************/
-
- /**
- * Compile an .etch file, assume workingDirectory is the same as the file's parent directory
- *
- * @param name filename
- */
- public void compile(File name) throws Exception
- {
- name = name.getCanonicalFile();
- validateEtchFile(name);
-
- FileInputStream is = new java.io.FileInputStream(name);
-
- String msg = String.format("Compiling %s ...\n", name);
- logHandler.logMessage( LogHandler.LEVEL_INFO, null, msg);
- if (name.getParentFile() != null)
- compile((InputStream)is, name.getParentFile());
- else
- compile((InputStream)is, name.getAbsoluteFile().getParentFile());
- }
+ // includePath
/**
- * Compile an .etch input stream
- *
- * @param is stream
+ * Initialize {@link CmdLineOptions#effectiveIncludePath} given the
+ * compilation we are about to perform.
+ * @param clo
*/
- public void compile(InputStream is) throws Exception
+ private static void initIncludePath( CmdLineOptions clo ) throws IOException
{
- // TODO null is not valid, pass a legitimate dir or change the other method
- compile(is, null);
- }
-
- /**
- * Compile an .etch input stream, specify the workingDirectory
- *
- * @param is stream
- * @param workingDirectory working directory
- */
- public void compile(InputStream is, File workingDirectory) throws Exception
- {
-
- CmdLineOptions options = new CmdLineOptions();
- workingDirectory = workingDirectory.getCanonicalFile();
-
- // Configure EtchHelper
- // TODO really need to re-factor how compiler-directive stuff in CmdLineOptions is passed around
-// EtchHelper.cl = options;
- options.lh = logHandler;
-
- // Setup options
- // if there are any problems, inconsistencies in the option set, Exceptions will be thrown
- options.includePath = getIncludePath(workingDirectory);
- options.bindingClass = getBindingClass();
- options.what = getWhat();
- options.outputDir = getTempOutputDirectory();
- // options.outputDir = getOutputDirectory();
- options.mixinOutputDir = getMixinOutputDirectory();
- options.ignoreGlobal = getIgnoreGlobal();
- options.ignoreLocal = getIgnoreLocal();
- options.userWordsList = getUserWordsList();
- options.noMixinArtifacts = getNoMixinArtifacts();
- options.noFlattenPackages = !getFlattenPackages();
-
- // Instantiate a new backend and parser
- final Backend b = getBindingClassInstance();
-
- final EtchGrammar parser = new EtchGrammar(b, is);
+// System.out.println( "initIncludePath: sourceFile = "+clo.sourceFile );
+// System.out.println( "initIncludePath: includePath = "+clo.includePath );
+
+ // includePath is composed of the source file's directory, then
+ // the paths specifies by -I command line directives, then the
+ // paths specified by the ETCH_INCLUDE_PATH environment variable.
+
+ clo.effectiveIncludePath = new ArrayList<File>( );
+
+ // add directory of source file.
+ if (clo.sourceFile != null)
+ clo.effectiveIncludePath.add(
+ clo.sourceFile.getCanonicalFile().getParentFile() );
+
+ for (File f: clo.includePath)
+ {
+ if (f.isDirectory())
+ clo.effectiveIncludePath.add( f );
+ else
+ clo.lh.report( LogHandler.LEVEL_WARNING,
+ "Include path element doesn't exist: %s", f );
+ }
+
+ if (!clo.ignoreIncludePath)
+ {
+ String s = System.getenv( CmdLineOptions.ETCH_INCLUDE_PATH );
+ if (s != null)
+ {
+ StringTokenizer st = new StringTokenizer( s, File.pathSeparator );
+ while (st.hasMoreTokens())
+ {
+ File f = new File( st.nextToken() );
+ if (f.isDirectory())
+ clo.effectiveIncludePath.add( f );
+ else
+ clo.lh.report( LogHandler.LEVEL_WARNING,
+ "Environment include path element doesn't exist: %s", f );
+ }
+ }
+ }
+// System.out.println( "initIncludePath: effectiveIncludePath = "+clo.effectiveIncludePath );
+ }
+
+ private static Module compile( CmdLineOptions clo ) throws Exception
+ {
+ InputStream is = new BufferedInputStream( new FileInputStream( clo.sourceFile ) );
+ try
+ {
+ return compile( clo, is );
+ }
+ finally
+ {
+ is.close();
+ }
+ }
+
+ private static Module compile( CmdLineOptions clo, InputStream is ) throws Exception
+ {
+ initIncludePath( clo );
+
+ if (!initBackend( clo ))
+ return null;
+
+ if (clo.sourceFile != null)
+ clo.lh.report( LogHandler.LEVEL_INFO, "Compiling %s...", clo.sourceFile );
+ else
+ clo.lh.report( LogHandler.LEVEL_INFO, "Compiling..." );
+
+ final EtchGrammar parser = new EtchGrammar( clo.backend, is );
final Module m;
// Parse .etch source
try
{
- m = parser.module( options );
- logHandler.logMessage( LogHandler.LEVEL_INFO, null, " Parsed Ok.\n");
+ m = parser.module( clo );
+ clo.lh.report( LogHandler.LEVEL_INFO, "Parsed Ok." );
+
m.check();
- logHandler.logMessage( LogHandler.LEVEL_INFO, null, " Checked Ok.\n");
+ clo.lh.report( LogHandler.LEVEL_INFO, "Checked Ok." );
}
catch ( ParseException e )
{
String fmt = e.getMessage();
- logHandler.logMessage( LogHandler.LEVEL_ERROR, e.currentToken, fmt);
- // TODO better exception
+ clo.lh.report( LogHandler.LEVEL_ERROR,
+ e.currentToken != null ? e.currentToken.beginLine : null,
+ e.getMessage() );
+ // TODO better report of a ParseException
throw new Exception(e.currentToken + " " + fmt);
}
- catch ( Exception e)
+ catch ( Exception e )
{
- String fmt = e.getMessage();
- logHandler.logMessage( LogHandler.LEVEL_ERROR, null, fmt);
- // TODO better exception
+ clo.lh.report( LogHandler.LEVEL_ERROR, "caught exception: %s", e );
+ // TODO better report of an Exception
throw e;
}
@@ -663,158 +408,211 @@
try
{
// TODO report work lists?
- logHandler.logMessage( LogHandler.LEVEL_INFO, null, " Generating Resources... \n");
+ clo.lh.report( LogHandler.LEVEL_INFO, "Generating Resources..." );
// TODO integrate includePath with code generation.
- b.generate(m, options, logHandler);
+ clo.backend.generate( m, clo );
+
+ // Move generated code to target location
+ // the parser will set 'isMixinPresent' if a "mixin" directive is in the .etch file
+ // TODO would be nice if the backend just put things in the right place from the start .. sigh re-factor later
+ // moveGeneratedCode(options.outputDir, options.isMixinPresent, workingDirectory);
}
catch ( Exception e )
{
String fmt = e.getMessage();
- // TODO better exception
+ // TODO better report of an Exception
e.printStackTrace();
throw new Exception(fmt);
}
- logHandler.logMessage( LogHandler.LEVEL_INFO, null, " Generating Complete. \n");
-
- // Move generated code to target location
- // the parser will set 'isMixinPresent' if a "mixin" directive is in the .etch file
- // TODO would be nice if the backend just put things in the right place from the start .. sigh re-factor later
- moveGeneratedCode(options.outputDir, options.isMixinPresent, workingDirectory);
// Complete
- logHandler.logMessage( LogHandler.LEVEL_INFO, null, " Compile Done.\n");
-
- // return m;
- }
-
- private static class GeneratedFiles
- {
- private Pattern rest = Pattern.compile(".*");
- private String IMPL_PATTERN = ".*Impl.+(Server|Client)..+$";
- private String MAIN_PATTERN = ".*Main.+(Client|Listener)..+$";
-
- private File basedir;
-
- public GeneratedFiles(File basedir)
- {
- this.basedir = basedir;
- }
-
- public List<String> relativeFiles()
- {
- return strip(find(null));
- }
-
- public List<String> templateFiles()
- {
- List<String> r = new LinkedList<String>();
-
- for (String f: relativeFiles())
- {
- if (f.matches(IMPL_PATTERN) || f.matches(MAIN_PATTERN))
- r.add(f);
- }
- return r;
- }
-
- public List<String> nonTemplateFiles()
- {
- List<String> r = new LinkedList<String>();
-
- for (String f: relativeFiles())
- {
- if (!f.matches(IMPL_PATTERN) && !f.matches(MAIN_PATTERN))
- r.add(f);
- }
- return r;
- }
-
- private List<String> strip(List<File> files)
- {
- List<String> s = new LinkedList<String>();
-
- for (File n: files)
- {
- Scanner t = new Scanner(n.getPath());
- t.skip(Pattern.quote(basedir.getPath()));
- s.add(t.next(rest));
- }
- return s;
- }
-
- private List<File> find(File root)
- {
- List<File> lst = new LinkedList<File>();
-
- if (root == null)
- root = basedir;
-
- for (File n: root.listFiles())
- {
- if (n.isDirectory())
- {
- lst.addAll(find(n));
- }
- else
- {
- lst.add(n);
- }
- }
- return lst;
- }
- }
-
- private void moveFiles(File srcDir, File destDir, List<String> files, boolean overwrite) throws Exception
- {
- //
- for (String f: files)
- {
- File srcFile = new File(srcDir, f);
- File destFile = new File(destDir, f);
- if (destFile.exists() && overwrite)
- destFile.delete();
-
- if (!destFile.exists())
- {
- File parent = new File(destFile.getParent());
- parent.mkdirs();
- srcFile.renameTo(destFile);
- logHandler.logMessage( LogHandler.LEVEL_INFO, null, " Writing " + f + "\n");
- }
- else
- {
- logHandler.logMessage( LogHandler.LEVEL_INFO, null, " No overwrite, skipping " + f + "\n");
- srcFile.delete();
- }
- }
+ clo.lh.report( LogHandler.LEVEL_INFO, "Compile Done." );
+ return m;
}
- private void moveGeneratedCode(File srcDir, boolean mixinPresent, File workingDirectory) throws Exception
- {
-
- File target = outputDir;
- File templateTarget = templateOutputDir;
-
- // override output directory if mixin detected
- if (mixinPresent && mixinOutputDir != null)
- target = mixinOutputDir;
-
- if (target == null)
- target = workingDirectory;
-
- if (templateOutputDir == null)
- templateTarget = target;
-
- // move files
- GeneratedFiles gf = new GeneratedFiles(srcDir);
-
- logHandler.logMessage( LogHandler.LEVEL_INFO, null, " Moving generated code to " + target.toString() + "\n");
- moveFiles(srcDir, target, gf.nonTemplateFiles(), true);
-
- logHandler.logMessage( LogHandler.LEVEL_INFO, null, " Moving starter template code to " + templateTarget.toString() + "\n");
- moveFiles(srcDir, templateTarget, gf.templateFiles(), overwriteTemplate);
-
- }
+ private static void initOutput( CmdLineOptions clo )
+ {
+ clo.output = new FileOutput();
+ clo.templateOutput = new FileOutput();
+ clo.mixinOutput = clo.noMixinArtifacts ? new NullOutput() : new FileOutput();
+ }
+
+ private static void saveOutput( CmdLineOptions clo ) throws IOException
+ {
+ // TODO implement saveOutput
+// clo.output.report( "output" );
+// clo.templateOutput.report( "templateOutput" );
+// clo.mixinOutput.report( "mixinOutput" );
+
+ if (clo.testing)
+ return;
+
+ // Destination directory of generated artifacts. Use source file's
+ // parent directory if not specified.
+
+ File outputDir = clo.outputDir;
+ boolean noQualOutputDir = false;
+ if (outputDir == null)
+ {
+ outputDir = clo.sourceFile.getCanonicalFile().getParentFile();
+ noQualOutputDir = true;
+ }
+
+ // Destination directory of generated user-editable artifacts. Use
+ // outputDir if not specified.
+
+ File templateOutputDir = clo.templateOutputDir;
+ boolean noQualTemplateOutputDir = false;
+ if (templateOutputDir == null)
+ {
+ templateOutputDir = outputDir;
+ noQualTemplateOutputDir = noQualOutputDir;
+ }
+
+ // Destination directory of mixin generated artifacts. Use outputDir
+ // if not specified.
+
+ File mixinOutputDir = clo.mixinOutputDir;
+ boolean noQualMixinOutputDir = false;
+ if (mixinOutputDir == null)
+ {
+ mixinOutputDir = outputDir;
+ noQualMixinOutputDir = noQualOutputDir;
+ }
+
+ boolean force = clo.what.contains( Backend.WHAT_FORCE );
+
+ saveFiles( clo.output, outputDir, noQualOutputDir, clo.noFlattenPackages, true );
+ saveFiles( clo.templateOutput, templateOutputDir, noQualTemplateOutputDir, clo.noFlattenPackages, force );
+ saveFiles( clo.mixinOutput, mixinOutputDir, noQualMixinOutputDir, clo.noFlattenPackages, true );
+ }
+
+ private static void saveFiles( Output output, File outputDir,
+ boolean noQualOutputDir, boolean noFlattenPackages, boolean force )
+ throws IOException
+ {
+ output.saveFiles( outputDir, noQualOutputDir, noFlattenPackages, force );
+ }
+
+ /**
+ * Parses a mixin which is within the specified service.
+ * @param intf
+ * @param n
+ * @param opts
+ * @return the constructed Mixin object, or null.
+ * @throws Exception
+ */
+ public static Module parseModule( Service intf, Name n, Map<String,
+ Opt> opts ) throws Exception
+ {
+ CmdLineOptions clo = new CmdLineOptions( intf.getCmdLineOptions() );
+
+ // find the mixin file
+
+ String fn = n.name;
+
+ // fn is something like a.b.c.d or perhaps just plain d.
+ //
+ // we will look for d.etch, a/b/c/d.etch, and a.b.c/d.etch.
+ //
+ // we can look in the same directory as the original source we are
+ // compiling (the working dir), or any of the include path directories.
+
+ String module;
+ String file;
+ int i = fn.lastIndexOf( '.' );
+ if (i >= 0)
+ {
+ module = fn.substring( 0, i );
+ file = fn.substring( i+1 );
+ }
+ else
+ {
+ module = null;
+ file = fn;
+ }
+
+ file = file + ".etch";
+
+// System.out.printf( "searching for mixin '%s' module '%s'\n", file, module );
+// System.out.println( "includePath = "+clo.includePath );
+// System.out.println( "effectiveIncludePath = "+clo.effectiveIncludePath );
+
+ List<File> list = new ArrayList<File>();
+
+ // look for the unqualified name
+ find( list, clo, null, file );
+
+ if (module != null)
+ {
+ // look for the qualified name
+ find( list, clo, module, file );
+ find( list, clo, module.replace( '.', '/' ), file );
+ }
+
+ if (list.isEmpty())
+ return null;
+
+ File f = list.get( 0 );
+// System.out.println( "mixing in = "+f );
+
+ return parseModule0( clo, n, f );
+ }
+
+ private static void find( List<File> list, CmdLineOptions clo, String path,
+ String file ) throws IOException
+ {
+ for (File d: clo.effectiveIncludePath)
+ {
+ File f = d;
+
+ if (path != null)
+ f = new File( f, path );
+
+ f = new File( f, file ).getCanonicalFile();
+
+ if (f.isFile() && !list.contains( f ))
+ list.add( f );
+ }
+ }
+
+ /**
+ * @param clo
+ * @param n
+ * @param f
+ * @return the parsed module of the mixin.
+ * @throws Exception
+ */
+ public static Module parseModule0( CmdLineOptions clo, Name n, File f )
+ throws Exception
+ {
+ clo.sourceFile = f;
+ clo.output = clo.mixinOutput;
+
+ clo.templateOutput = new NullOutput();
+ clo.what.remove( Backend.WHAT_MAIN );
+ clo.what.remove( Backend.WHAT_IMPL );
+
+ if (clo.noMixinArtifacts)
+ {
+ clo.what.clear();
+ clo.what.add( Backend.WHAT_NONE );
+ }
+
+ clo.lh.report( LogHandler.LEVEL_INFO, n.token.beginLine,
+ "Compiling mixin file: %s", n.name );
+ clo.lh.push( f.getName(), n.token.beginLine );
+ try
+ {
+ return compile( clo );
+ }
+ finally
+ {
+ clo.lh.pop( f.getName() );
+ clo.lh.report( LogHandler.LEVEL_INFO, n.token.beginLine,
+ "Done compiling mixin file: %s", n.name );
+ }
+ }
}
Modified: incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/EtchHelper.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/EtchHelper.java?rev=718128&r1=718127&r2=718128&view=diff
==============================================================================
--- incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/EtchHelper.java (original)
+++ incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/EtchHelper.java Sun Nov 16 14:35:35 2008
@@ -614,7 +614,7 @@
//Get the Gramar Obj
EtchGrammar oGramar = (EtchGrammar) this;
- servobj.getCmdLineOptions().lh.push( fileName.toString(), new Integer(fileName.beginLine) );
+ servobj.getCmdLineOptions().lh.push( fileName.toString(), fileName.beginLine );
//System.out.println("------Start Do Include-------");
@@ -622,13 +622,13 @@
String oStr = fileName.toString().replaceAll("\"", "");
// System.out.println("Including " + oStr);
- servobj.getCmdLineOptions().lh.logMessage( LogHandler.LEVEL_INFO, null,"Including " + oStr + "\n");
+ servobj.getCmdLineOptions().lh.report( LogHandler.LEVEL_INFO, "Including %s", oStr );
//Create the input string and read it in
InputStream oStream = null;
// Search the etch path for the file
- for (File f : servobj.getCmdLineOptions().includePath)
+ for (File f : servobj.getCmdLineOptions().effectiveIncludePath)
{
try
{
@@ -676,7 +676,7 @@
//Put the old data back
oGramar.ReInit( oOldTM );
oGramar.token = token;
- servobj.getCmdLineOptions().lh.pop();
+ servobj.getCmdLineOptions().lh.pop( fileName.toString() );
//System.out.println("------End Do Include---------");
}
}
Modified: incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/EtchMain.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/EtchMain.java?rev=718128&r1=718127&r2=718128&view=diff
==============================================================================
--- incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/EtchMain.java (original)
+++ incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/EtchMain.java Sun Nov 16 14:35:35 2008
@@ -18,12 +18,6 @@
package etch.compiler;
import java.io.File;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
import java.util.StringTokenizer;
import etch.util.cmd.CommandParser;
@@ -36,131 +30,32 @@
*/
public class EtchMain extends Program
{
- private static String WHO_DELIMETER = ",";
-
- /**
+ /**
* @param args
* @throws Exception
*/
public static void main( String[] args ) throws Exception
{
- try
- {
- main( new EtchMain(), args );
- }
- catch ( Throwable e )
- {
- System.out.println(e.getMessage());
- e.printStackTrace();
- System.exit( 3 );
- return;
- }
+ new EtchMain().doMain( args );
}
/**
- * @param etchHome command line specified location for the etch.
- * @return sets up the class loader based on information from
- * our environment.
+ * Runs the main program, parsing command line arguments and performing
+ * the action.
+ * @param args
* @throws Exception
*/
- public static ClassLoader setupClassLoader( String etchHome )
- throws Exception
+ public void doMain( String[] args ) throws Exception
{
- // get the current class path...
-
- Set<String> mainClassPath = new HashSet<String>();
-
- ClassLoader cl = EtchMain.class.getClassLoader();
- if (cl instanceof URLClassLoader)
- {
- for (URL u: ((URLClassLoader) cl).getURLs())
- {
- String s = u.toString();
- if (s.startsWith( "file:/" ) && s.endsWith( ".jar" ))
- {
- s = s.substring( 6 );
- File f = new File( s );
- if (f.isFile() && f.canRead())
- {
- s = f.getCanonicalPath();
- mainClassPath.add( s );
-// System.out.println( "mainClassPath.add: "+s );
- }
- }
- }
- }
-
- // search etch.home.lib (if specified) for more jar files to add
- // to a secondary class loader. exclude jar files on the main
- // class path.
-
- String s = etchHome;
-
- if (s == null || s.length() == 0)
- s = System.getProperty( "etch.home.lib" );
-
- if (s == null || s.length() == 0)
- {
- s = System.getProperty( "etch.home" );
- if (s != null && s.length() > 0)
- s = s + File.separator + "lib";
- }
-
- if (s == null || s.length() == 0)
- {
- s = System.getenv( "ETCH_HOME" );
- if (s != null && s.length() > 0)
- s = s + File.separator + "lib";
- }
-
-// System.out.println( "etch.home.lib = "+s );
- if (s != null && s.length() > 0)
- {
- File d = new File( s );
- if (d.isDirectory() && d.canRead())
- {
- MyURLClassLoader ncl = new MyURLClassLoader( new URL[] {}, cl );
- for (File f: d.listFiles())
- {
- if (!f.isFile())
- continue;
- if (!f.getName().endsWith( ".jar" ))
- continue;
- // s = f.getCanonicalPath();
- if (!mainClassPath.contains( f.getCanonicalPath() ))
- {
- ncl.addURL(f.toURL());
-// System.out.println( "+++ "+s.getName() );
-
- }
- else
- {
-// System.out.println( "--- "+s.getName() );
- }
- }
- cl = ncl;
- }
- }
- return cl;
- }
-
- private static class MyURLClassLoader extends URLClassLoader
- {
- /**
- * This is just a class loader where we can hang more urls to
- * search.
- * @param urls
- * @param parent
- */
- public MyURLClassLoader( URL[] urls, ClassLoader parent )
- {
- super( urls, parent );
- }
-
- @Override
- public void addURL( URL url )
+ try
+ {
+ main( this, args );
+ }
+ catch ( Throwable e )
{
- super.addURL( url );
+ e.printStackTrace();
+ exit( 3, toString(), e.toString(), true );
+ return;
}
}
@@ -169,9 +64,7 @@
*/
public EtchMain()
{
- sourceFiles = new LinkedList<File>();
- what = new LinkedList<String>();
- includePath = new LinkedList<File>();
+ // nothing to do.
}
@Override
@@ -184,57 +77,51 @@
protected void defineOptionsAndParameters( CommandParser cp )
throws Exception
{
- cp.defineFileOption( "-t|--template-out", "templateDir", "setTemplateDir",
- "specifies the output directory for Impl* and Main* files",
- Option.SINGLETON, null, null );
-
cp.defineFileOption( "-d|--output-dir", "outputDir", "setOutputDir",
- "specifies the output directory",
+ "output directory for compiler generated files",
+ Option.SINGLETON, null, null );
+
+ cp.defineFileOption( "-m|--mixin-output-dir", "outputDir", "setMixinOutputDir",
+ "output directory for compiler generated files for mixins",
Option.SINGLETON, null, null );
+ cp.defineFileOption( "-t|--template-output-dir", "outputDir", "setTemplateOutputDir",
+ "output directory for compiler generated user editable template files",
+ Option.SINGLETON, null, null );
+
cp.defineStringOption( "-b|--binding", "binding", "setBinding",
"specifies a target language binding to generate",
Option.REQUIRED|Option.SINGLETON, null, null );
- cp.defineStringOption( "-w|--what|--who", "what", "setWhat",
- "specifies what files to generate: all,server,client,impl,main,helper (--who is deprecated)",
- Option.SINGLETON, "BOTH", null );
+ cp.defineStringOption( "-w|--what", "what", "addWhat",
+ "specifies what files to generate (depends upon binding, try -w help for more info; separate with '"+CmdLineOptions.WHAT_DELIMETER+"')",
+ Option.NONE, null, null );
- cp.defineNullOption( "-g|--ignore-global", "ignoreGlobalWordsList",
+ cp.defineNullOption( "-g|--ignore-global-words-list", "ignoreGlobalWordsList",
"ignore the global reserved words list",
Option.SINGLETON );
- cp.defineNullOption( "-l|--ignore-local", "ignoreLocalWordsList",
- "ignore the local reserved words list",
+ cp.defineNullOption( "-l|--ignore-local-words-list", "ignoreLocalWordsList",
+ "ignore the local (binding-specific) reserved words list",
Option.SINGLETON );
- cp.defineFileOption( "-W|--word-list", "wordList", "setUserWordsList",
- "specifies a reserved words list",
+ cp.defineFileOption( "-W|--user-words-list", "wordList", "setUserWordsList",
+ "file name of a user-specified reserved words list",
Option.SINGLETON, null, null );
- cp.defineStringOption( "-I|--include-path", "includePath", "setIncludePath",
- "a search directory for included etch files",
+ cp.defineStringOption( "-I|--include-path", "includePath", "addIncludePath",
+ "adds search directories for included or mixed in etch files (separate elements with system path separator '"+File.pathSeparator+"')",
Option.NONE, null, null );
- /*
- cp.defineStringOption( "-L|--etch-lib", "etchLib", "setEtchLib",
- "the fully qualified path of ETCH_HOME/lib",
- Option.SINGLETON, null, null );
- */
-
- cp.defineNullOption( "-i|--ignore-include-path", "ignoreEnvIncludePath",
- "ignore the include path environment variable",
+ cp.defineNullOption( "-i|--ignore-include-path", "ignoreIncludePath",
+ "ignore the "+CmdLineOptions.ETCH_INCLUDE_PATH+" environment variable",
Option.SINGLETON );
- cp.defineFileOption( "-m|--dir-mixin", "mixinOutputDir", "setMixinOutputDir",
- "the output dir for mixin artifacts",
- Option.SINGLETON, null, null );
-
- cp.defineNullOption( "-n|--no-mixin-artifacts", "setNoMixinArtifacts",
+ cp.defineNullOption( "-n|--no-mixin-artifacts", "noMixinArtifacts",
"mixin artifacts should not be generated",
Option.SINGLETON );
- cp.defineNullOption( "-q|--quiet", "setQuiet",
+ cp.defineNullOption( "-q|--quiet", "quiet",
"only report problems",
Option.SINGLETON );
@@ -242,130 +129,117 @@
"show the version and exit",
Option.SINGLETON );
- cp.defineNullOption( "-f|--no-flatten", "setNoFlattenPackages",
- "the namespace directory tree should not be flattened",
+ cp.defineNullOption( "-f|--no-flatten-packages", "noFlattenPackages",
+ "namespace directory tree should not be flattened",
Option.SINGLETON );
+
+ cp.defineNullOption( "--testing", "setTesting",
+ "",
+ Option.HIDDEN );
- cp.defineFileParameter( "file", "setFile",
- "Etch source to compile", true, true, null );
+ cp.defineFileParameter( "sourceFile", "setSourceFile",
+ "etch source file to compile", true, false, null );
}
+
+ private final CmdLineOptions clo = new CmdLineOptions();
/**
- * Command parser method to set the output directory.
+ * Sets the output directory of compiler generated files.
* @param cp
* @param option
* @param token
* @param value the output directory
- * @return true if it worked, false otherwise.
*/
- public boolean setOutputDir( CommandParser cp, Option option, String token,
+ public void setOutputDir( CommandParser cp, Option option, String token,
File value )
{
- outputDirectory = value;
- return true;
+ clo.outputDir = value;
}
- private File outputDirectory = null;
/**
- * Command parser method to set the output directory for template files Impl* and Main*.
+ * Sets the output directory for user editable template files.
* @param cp
* @param option
* @param token
* @param value the template output directory
- * @return true if it worked, false otherwise.
*/
- public boolean setTemplateDir( CommandParser cp, Option option, String token,
+ public void setTemplateOutputDir( CommandParser cp, Option option, String token,
File value )
{
- templateOutputDirectory = value;
- return true;
+ clo.templateOutputDir = value;
}
- private File templateOutputDirectory = null;
/**
- * Command parser method to set the binding.
+ * Sets the binding.
* @param cp
* @param option
* @param token
* @param value the binding
- * @return true if it worked, false otherwise.
*/
- public boolean setBinding( CommandParser cp, Option option, String token,
+ public void setBinding( CommandParser cp, Option option, String token,
String value )
{
- bindingName = value;
- return true;
+ clo.binding = value;
}
- private String bindingName = null;
/**
- * Command parser method to set for whom/what we are generating code.
+ * Adds to the set of what needs to be generated.
* @param cp
* @param option
* @param token
* @param value the list of what needs to be generated.
- * @return true if it worked, false otherwise.
*/
- public boolean setWhat( CommandParser cp, Option option, String token,
+ public void addWhat( CommandParser cp, Option option, String token,
String value )
{
- StringTokenizer st = new StringTokenizer(value,WHO_DELIMETER);
-
+ StringTokenizer st = new StringTokenizer( value, CmdLineOptions.WHAT_DELIMETER );
while (st.hasMoreElements())
- what.add(st.nextToken().toUpperCase());
-
- return true;
+ clo.what.add(st.nextToken().toUpperCase());
}
- private List<String> what;
/**
- * Command parser method to ignore the globally reserved words list.
+ * Sets the ignore the globally reserved words list flag.
* @param cp
* @param option
* @param token
*/
public void ignoreGlobalWordsList(CommandParser cp, Option option, String token)
{
- ignoreGlobal = true;
+ clo.ignoreGlobalWordsList = true;
}
- private boolean ignoreGlobal = true;
/**
- * Command parser method to ignore the locally reserved words list.
+ * Sets the ignore the locally reserved words list flag.
* @param cp
* @param option
* @param token
*/
public void ignoreLocalWordsList(CommandParser cp, Option option, String token)
{
- ignoreLocal = true;
+ clo.ignoreLocalWordsList = true;
}
- private boolean ignoreLocal = false;
/**
- * Command parser method to set the user-defined reserved words list.
+ * Sets the file name of the user-defined reserved words list.
* @param cp
* @param option
* @param token
- * @param value the reserved words list
- * @return true if it worked, false otherwise.
+ * @param value the file name of the user-defined reserved words list
*/
- public boolean setUserWordsList(CommandParser cp, Option option, String token,
+ public void setUserWordsList(CommandParser cp, Option option, String token,
File value)
{
- userWordList = value;
- return true;
+ clo.userWordsList = value;
}
- private File userWordList = null;
/**
- * Command parser method to set the include path.
+ * Adds to the include path.
* @param cp
* @param option
* @param token
- * @param value path to append to the current include path
+ * @param value path list to append to the current include path.
*/
- public void setIncludePath( CommandParser cp, Option option, String token,
+ public void addIncludePath( CommandParser cp, Option option, String token,
String value )
{
StringTokenizer includeTokens = new StringTokenizer(value,
@@ -374,38 +248,35 @@
while(includeTokens.hasMoreTokens())
{
File temp = new File(includeTokens.nextToken());
- includePath.add(temp);
+ clo.includePath.add(temp);
}
}
- private List<File> includePath;
/**
- * Command parser method to ignore the include path from the environment.
+ * Sets the ignore the include path from the environment flag.
* @param cp
* @param option
* @param token
*/
- public void ignoreEnvIncludePath(CommandParser cp, Option option,
+ public void ignoreIncludePath(CommandParser cp, Option option,
String token)
{
- ignoreEnvironmentIncludePath = true;
+ clo.ignoreIncludePath = true;
}
- private boolean ignoreEnvironmentIncludePath = false;
/**
- * Command parser method to set the quiet flag.
+ * Sets the quiet flag.
* @param cp
* @param option
* @param token
*/
- public void setQuiet( CommandParser cp, Option option, String token )
+ public void quiet( CommandParser cp, Option option, String token )
{
- quiet = true;
+ clo.quiet = true;
}
- private boolean quiet = false;
/**
- * Command parser method to show the version and exit.
+ * Shows the version and exits.
* @param cp
* @param option
* @param token
@@ -418,125 +289,80 @@
}
/**
- * Command parser method to suppress mixin artifacts.
+ * Sets the suppress mixin artifacts flag.
* @param cp
* @param option
* @param token
*/
- public void setNoMixinArtifacts( CommandParser cp, Option option, String token )
+ public void noMixinArtifacts( CommandParser cp, Option option, String token )
{
- mixinSuppressed = true;
+ clo.noMixinArtifacts = true;
}
- private boolean mixinSuppressed = false;
/**
- * Command parser method to set the output directory for mixins.
+ * Sets the output directory for mixins.
* @param cp
* @param option
* @param token
* @param value the output directory for mixin
- * @return true if it worked, false otherwise.
*/
- public boolean setMixinOutputDir( CommandParser cp, Option option, String token,
+ public void setMixinOutputDir( CommandParser cp, Option option, String token,
File value )
{
- mixinOutputDirectory = value;
- return true;
+ clo.mixinOutputDir = value;
}
-
- private File mixinOutputDirectory = null;
/**
+ * Sets the no flatten packages flag.
* @param cp
* @param option
* @param token
*/
- public void setNoFlattenPackages( CommandParser cp, Option option, String token)
+ public void noFlattenPackages( CommandParser cp, Option option, String token )
{
- noFlattenPackages = true;
+ clo.noFlattenPackages = true;
}
- private boolean noFlattenPackages;
-
/**
- * Command parser method to set the etch file to compile.
+ * Sets the hidden testing flag.
* @param cp
+ * @param option
+ * @param token
+ */
+ public void setTesting( CommandParser cp, Option option, String token )
+ {
+ clo.testing = true;
+ clo.lh = new ListLogHandler();
+ testingClo = clo;
+ }
+
+ /**
+ * If --testing is on the command line, the CmdLineOptions is saved
+ * here for reference by unit testing programs.
+ */
+ public CmdLineOptions testingClo;
+
+ /**
+ * Sets the etch source file to compile.
+ * @param cp the command parser
* @param param
* @param value path of etch file to compile
- * @return true if file added to list, false otherwise.
*/
- public boolean setFile( CommandParser cp, Parameter param, File value )
+ public void setSourceFile( CommandParser cp, Parameter param, File value )
{
- sourceFiles.add(value);
- return true;
+ clo.sourceFile = value;
}
- private List<File> sourceFiles;
@Override
protected void run() throws Exception
{
- if (bindingName == null)
- throw new Exception ("No binding specified.");
-
- // Instantiate a new compiler instance
- EtchCompiler etchCompiler = new EtchCompiler( setupClassLoader( null ) );
- etchCompiler.setQuiet( quiet );
-
- // VALIDATE
-
- try
- {
- etchCompiler.setBindingName(bindingName);
- etchCompiler.setIgnoreEnvironmentIncludePath(ignoreEnvironmentIncludePath);
- etchCompiler.setMixinSuppressed(mixinSuppressed);
- etchCompiler.setFlattenPackages(!noFlattenPackages);
- etchCompiler.setOverwriteTemplate(false);
- etchCompiler.setIgnoreLocal(ignoreLocal);
- etchCompiler.setIgnoreGlobal(ignoreGlobal);
-
- for (File f: includePath)
- etchCompiler.addIncludePath(f);
-
- for (String w: what)
- etchCompiler.addWhat(w);
-
- if (outputDirectory != null)
- etchCompiler.setOutputDirectory(outputDirectory);
-
- if (userWordList != null)
- etchCompiler.setUserWordsList(userWordList);
+ // Instantiate a new compiler instance and run it.
+ ClassLoader cl = EtchCompiler.setupClassLoader( null );
+ EtchCompiler etchCompiler = new EtchCompiler( cl );
- if (mixinOutputDirectory != null)
- etchCompiler.setMixinOutputDirectory(mixinOutputDirectory);
+ etchCompiler.run( clo );
- if (templateOutputDirectory != null)
- etchCompiler.setTemplateOutputDirectory(templateOutputDirectory);
- }
- catch (Exception e)
- {
- throw new Exception("Invalid parameter passed to the compiler:\n" + e.getMessage());
- }
-
- // EXECUTE
-
- boolean status = true;
- for (File name: sourceFiles)
- {
- try
- {
- etchCompiler.validateEtchFile(name);
- etchCompiler.compile(name);
- }
- catch ( Exception e )
- {
- System.out.println(String.format("Error Compiling %s: %s", name.getName(), e.getMessage()));
- e.printStackTrace();
- status = false;
- }
- }
- if (!status)
- {
- throw new Exception("One or more .etch files failed to compile.");
- }
+ if (clo.lh.hasError())
+ exit( 3, "EtchMain", "errors during compile", false );
}
}
Modified: incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/LogHandler.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/LogHandler.java?rev=718128&r1=718127&r2=718128&view=diff
==============================================================================
--- incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/LogHandler.java (original)
+++ incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/LogHandler.java Sun Nov 16 14:35:35 2008
@@ -26,73 +26,197 @@
public interface LogHandler
{
/**
- * Constant to specify an Error Message
+ * A message which is always printed, but doesn't affect compilation.
+ * Also usually formatted without level, phase, source, line number, or
+ * history information.
+ */
+ public static final int LEVEL_IMPORTANT = 0;
+
+ /**
+ * An error level message. Also causes compiler to exit with an error code.
*/
public static final int LEVEL_ERROR = 1;
+
/**
- * Constant to specify an Warning Message
+ * A warning level message.
*/
public static final int LEVEL_WARNING = 2;
+
/**
- * Constant to specify an Info Message
+ * An information level message.
*/
public static final int LEVEL_INFO = 3;
+
/**
- * Constant to specify an unformatted message to be printed always.
+ * Formats and reports a message without a line number.
+ * @param level
+ * @param fmt
+ * @param params
+ */
+ public void report( int level, String fmt, Object ... params );
+
+ /**
+ * Formats and reports a message with perhaps a line number.
+ * @param level
+ * @param lineNumber
+ * @param fmt
+ * @param params
*/
- public static final int LEVEL_MESSAGE = 4;
+ public void report( int level, Integer lineNumber, String fmt,
+ Object ... params );
/**
* This method is used to either store or print messages
* @param level indicates ERROR, WARNING or INFO
- * @param token indicates the token at which the error occured
+ * @param token indicates the token at which the error occurred
* @param msg String message
+ * @deprecated use {@link #report(int, Integer, String)} instead.
+ */
+ @Deprecated
+ public void logMessage( int level, Token token, String msg );
+
+ /**
+ * Reports a pre-formatted message perhaps with a line number.
+ * @param level
+ * @param lineNumber
+ * @param msg
*/
- public void logMessage(int level, Token token, String msg );
+ public void report( int level, Integer lineNumber, String msg );
/**
- * This method is used to specify the source of the Message
- * @param str File name where the Message occured
- * @param num Line Number where the Message occured
+ * Determines if a message is interesting given its level.
+ * @param level
+ * @return true if a message is interesting given its level.
*/
+ public boolean isInteresting( int level );
- public void push(String str, Integer num);
+ /**
+ * Pushes a new source on the stack.
+ * @param newSource new source name being read.
+ * @param lineNumberInOldSource line number in old source which caused the new
+ * source to be read. This should be null for the top level source.
+ */
+ public void push( String newSource, Integer lineNumberInOldSource );
/**
- * This method removes the source that was added by the push method.
+ * Removes the source that was added by the last push method.
* It behaves exactly like the pop method in Stacks
- *
+ * @param oldSource the source name we just finished reading. This was
+ * previously passed as the argument to the last push.
*/
+ public void pop( String oldSource );
- public void pop();
+ /**
+ * @return true if an error has been reported.
+ */
+ public boolean hasError();
/**
- * Indicates whether an error was encountered
- * @return flag to indicate whether an error occured
+ * @return count of errors that have been reported.
*/
+ public int errorCount();
- public boolean hasError();
+ /**
+ * @return true if a warning has been reported.
+ */
+ public boolean hasWarning();
/**
- * Sets the quiet flag.
- * @param quiet
+ * @return count of warnings that have been reported.
*/
- public void setQuiet( boolean quiet );
+ public int warningCount();
/**
- * This method gets the isQuiet Variable
- * @return whether Compiler is printing info and warning messages
+ * Gets the level of logging.
+ * @return the current level of logging. Only messages with level less than
+ * or equal to this value will be logged.
+ */
+ public int getLevel();
+
+ /**
+ * Sets the level of logging. Only messages with level less than
+ * or equal to this value will be logged.
+ * @param level the level of logging
+ */
+ public void setLevel( int level );
+
+ /**
+ * @return the current phase of processing. The initial value is null.
*/
- public boolean getQuiet();
+ public String getPhase();
/**
* Sets the current phase of processing.
- * @param phase
+ * @param phase may be null.
*/
public void setPhase( String phase );
/**
- * @return the current phase of processing.
+ * Final condensation of the information of a report.
*/
- public String getPhase();
+ public static class Message
+ {
+ /**
+ * @param level
+ * @param phase
+ * @param source
+ * @param lineNumber
+ * @param msg
+ * @param history
+ */
+ public Message( int level, String phase, String source,
+ Integer lineNumber, String msg, History[] history )
+ {
+ this.level = level;
+ this.phase = phase;
+ this.source = source;
+ this.lineNumber = lineNumber;
+ this.msg = msg;
+ this.history = history;
+ }
+
+ /** the level of the report */
+ public final int level;
+
+ /** the phase of processing when the report was made */
+ public final String phase;
+
+ /** the current input source */
+ public final String source;
+
+ /** the line number of the source which caused the report (if any) */
+ public final Integer lineNumber;
+
+ /** the text of the report */
+ public final String msg;
+
+ /** the history of sources previous to source / lineNumber above */
+ public final History[] history;
+ }
+
+ /**
+ * Record of the input streams previous to the last one pushed.
+ */
+ public static class History
+ {
+ /**
+ * @param source
+ * @param lineNumber
+ */
+ public History( String source, int lineNumber )
+ {
+ this.source = source;
+ this.lineNum = lineNumber;
+ }
+
+ /**
+ * The source of the input stream (file, stdin, etc.)
+ */
+ public final String source;
+
+ /**
+ * The line number of the source which caused a new source to be pushed.
+ */
+ public final int lineNum;
+ }
}
Modified: incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/ast/ReservedWordChecker.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/ast/ReservedWordChecker.java?rev=718128&r1=718127&r2=718128&view=diff
==============================================================================
--- incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/ast/ReservedWordChecker.java (original)
+++ incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/ast/ReservedWordChecker.java Sun Nov 16 14:35:35 2008
@@ -154,9 +154,9 @@
{
String msg = String.format( "Etch: %s '%s' is a reserved word at line %d\n", what, token.image, token.beginLine);
if (rewrite)
- lh.logMessage( LogHandler.LEVEL_WARNING, token,msg );
+ lh.report( LogHandler.LEVEL_WARNING, token.beginLine, msg );
else
- lh.logMessage( LogHandler.LEVEL_ERROR, token,msg );
+ lh.report( LogHandler.LEVEL_ERROR, token.beginLine, msg );
ok = rewrite;
}
}
Modified: incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/ast/Service.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/ast/Service.java?rev=718128&r1=718127&r2=718128&view=diff
==============================================================================
--- incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/ast/Service.java (original)
+++ incubator/etch/branches/etch-python/compiler/src/main/java/etch/compiler/ast/Service.java Sun Nov 16 14:35:35 2008
@@ -17,22 +17,15 @@
package etch.compiler.ast;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import etch.compiler.Backend;
-import etch.compiler.CmdLineOptions;
-import etch.compiler.Etch2;
+import etch.compiler.EtchCompiler;
import etch.compiler.EtchGrammarConstants;
-import etch.compiler.LogHandler;
import etch.compiler.ParseException;
import etch.compiler.Token;
@@ -85,125 +78,30 @@
{
optsCheck( Mixin.class, nOpts );
- try {
-
-
- getCmdLineOptions().lh.logMessage( LogHandler.LEVEL_INFO, n.token,
- " Checking Mixin file: " + n.name + " at line : "
- + n.token.beginLine + "\n" );
- String filename = n.name;
- filename = filename.replace( '.', '/' );
- filename = filename + ".etch";
- List<File> list = getCmdLineOptions().includePath;
- String searchPath = null;
- File etchFile = null;
- boolean mixinFileExist = false;
-
-
- if (list != null)
+ try
{
- for (int i = 0; i < list.size(); i++)
- {
- File f = list.get( i );
- if (f != null)
- {
- searchPath = f.getAbsolutePath() + "/" + filename;
- etchFile = new File( searchPath );
- if (etchFile.exists())
- {
- mixinFileExist = true;
- break;
- }
- }
- }
- if (mixinFileExist)
- {
-
- getCmdLineOptions().lh.logMessage( LogHandler.LEVEL_INFO, n.token,
- " Found mixin file " + etchFile.getAbsolutePath()
- + " in Include Path \n" );
- // System.out.println(" We found the file " + etchFile.getName()
- // + "
- // Now lets try to generate the parsed tree");
-
- // Does -d or -dm option exist. If not return
- if (!getCmdLineOptions().noMixinArtifacts)
- {
- if (getCmdLineOptions().outputDir == null
- && getCmdLineOptions().mixinOutputDir == null)
- {
- getCmdLineOptions().lh
- .logMessage(
- LogHandler.LEVEL_ERROR,
- n.token,
- "-d or -m option is not specified. Please specify one of these"
- + " options. Mixin artifacts will only be generated when atleast one of these options"
- + " is present \n" );
-
- return null;
- }
- }
-
- InputStream is = null;
- try
- {
- is = new java.io.FileInputStream( etchFile );
- }
- catch ( FileNotFoundException e1 )
- {
- e1.printStackTrace();
-
- }
-
- getCmdLineOptions().lh.logMessage( LogHandler.LEVEL_INFO, null,
- "Parsing file " + etchFile.getAbsolutePath() + "\n" );
- getCmdLineOptions().lh.push( etchFile.getName(), new Integer(
- n.token.beginLine ) );
-
- CmdLineOptions cmdLineObject = new CmdLineOptions(getCmdLineOptions());
- HashSet<String> set = new HashSet<String>();
- if (getCmdLineOptions().noMixinArtifacts)
- {
- cmdLineObject.isMixinPresent = false;
- set.add(Backend.WHAT_NONE);
- }
- else
- {
- cmdLineObject.isMixinPresent = true;
- set = populateWhat(getCmdLineOptions().what);
- }
- cmdLineObject.what = set;
-
-
- Module retMod = Etch2.doCompile( cmdLineObject, is,
- getCmdLineOptions().lh );
- getCmdLineOptions().lh.pop();
- Mixin mixin = new Mixin( this, n, nOpts, retMod );
- nameList.add( n, mixin );
- return mixin;
- }
- getCmdLineOptions().lh.logMessage( LogHandler.LEVEL_ERROR, n.token,
- " Mixin file does not exist in Include Path. \n" );
- }
- else
- {
- getCmdLineOptions().lh.logMessage( LogHandler.LEVEL_ERROR, n.token,
- " No Include Path defined for Mixin File \n" );
+ Module module = EtchCompiler.parseModule( this, n, nOpts );
+ if (module == null)
+ throw new ParseException( String.format(
+ "could not find mixin '%s' at line %d",
+ n.name, n.token.beginLine ) );
+ Mixin mixin = new Mixin( this, n, nOpts, module );
+ nameList.add( n, mixin );
+ return mixin;
}
+ catch ( ParseException e )
+ {
+ throw e;
}
- catch (Exception e) {
- getCmdLineOptions().lh.logMessage( LogHandler.LEVEL_ERROR, n.token,
- " Unexpected Error occured during parsing mixin \n" );
- e.printStackTrace();
-
+ catch ( Exception e )
+ {
+ throw new ParseException( String.format(
+ "could not find mixin '%s' at line %d: %s",
+ n.name, n.token.beginLine, e ) );
}
-
- return null;
}
-
-
/**
* Adds a constant declaration.
* @param n
@@ -661,35 +559,4 @@
walker.postService( this );
}
-
- private HashSet<String> populateWhat(Set<String> set) {
- HashSet<String> retSet = new HashSet<String>();
- if (set.contains( Backend.WHAT_ALL ) || set.contains( Backend.WHAT_BOTH )) {
- retSet.add( Backend.WHAT_BOTH );
- }
- if (set.contains( Backend.WHAT_CLIENT)) {
- retSet.add( Backend.WHAT_CLIENT );
- }
- if (set.contains( Backend.WHAT_SERVER)) {
- retSet.add( Backend.WHAT_SERVER );
- }
- if (set.contains( Backend.WHAT_MAIN ) || set.contains( Backend.WHAT_IMPL ) ||
- set.contains( Backend.WHAT_HELPER )) {
- if (retSet.isEmpty()) {
- retSet.add( Backend.WHAT_BOTH );
- }
- }
- if (set.contains( Backend.WHAT_FORCE ) ) {
-
- retSet.add( Backend.WHAT_FORCE );
-
- }
- if (set.contains( Backend.WHAT_NONE)) {
- retSet.clear();
- retSet.add( Backend.WHAT_NONE );
- }
-
- return retSet;
-
- }
}
Modified: incubator/etch/branches/etch-python/compiler/src/main/scripts/etch.bat
URL: http://svn.apache.org/viewvc/incubator/etch/branches/etch-python/compiler/src/main/scripts/etch.bat?rev=718128&r1=718127&r2=718128&view=diff
==============================================================================
--- incubator/etch/branches/etch-python/compiler/src/main/scripts/etch.bat (original)
+++ incubator/etch/branches/etch-python/compiler/src/main/scripts/etch.bat Sun Nov 16 14:35:35 2008
@@ -12,7 +12,6 @@
set ETCH_CP=
for %%i in ("%ETCH_HOME%\lib\etch-compiler-*.jar") do set ETCH_CP=%ETCH_CP%;%%i
-rem for %%i in ("%ETCH_HOME%\lib\etch-util-*.jar") do set ETCH_CP=%ETCH_CP%;%%i
rem add clover to classpath if defined and exists
@@ -21,4 +20,4 @@
set ETCH_CP=%ETCH_CP%;%CLOVER_HOME%\lib\clover.jar
:skip_clover
-java -cp "%ETCH_CP%" -Detch.home="%ETCH_HOME%" etch.compiler.EtchMain %*
+java -cp "%ETCH_CP%" etch.compiler.EtchMain %*
Modified: incubator/etch/branches/etch-python/compiler/src/test/java/etch/compiler/TestMapWords.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/etch-python/compiler/src/test/java/etch/compiler/TestMapWords.java?rev=718128&r1=718127&r2=718128&view=diff
==============================================================================
--- incubator/etch/branches/etch-python/compiler/src/test/java/etch/compiler/TestMapWords.java (original)
+++ incubator/etch/branches/etch-python/compiler/src/test/java/etch/compiler/TestMapWords.java Sun Nov 16 14:35:35 2008
@@ -91,11 +91,10 @@
}
@Override
- public void generate( Module module, CmdLineOptions options, LogHandler lh )
+ public void generate( Module module, CmdLineOptions options )
throws Exception
{
// Not needed for testing
-
}
@Override
Modified: incubator/etch/branches/etch-python/examples/build.xml
URL: http://svn.apache.org/viewvc/incubator/etch/branches/etch-python/examples/build.xml?rev=718128&r1=718127&r2=718128&view=diff
==============================================================================
--- incubator/etch/branches/etch-python/examples/build.xml (original)
+++ incubator/etch/branches/etch-python/examples/build.xml Sun Nov 16 14:35:35 2008
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<project name="etch-examples" basedir="." default="help">
<description>Etch Examples</description>
+ <target name="help"><echo>Please select a target...</echo></target>
<property name="Etch.basedir" location="${basedir}/.." />
<import file="${Etch.basedir}/build-support/etch.common.xml" />
@@ -55,10 +56,10 @@
<target name="component-all" >
<mkdir dir="${basedir}/target" />
- <build_example dir="perf" logdir="${basedir}/target/logs" />
- <build_example dir="distmap" logdir="${basedir}/target/logs" />
<build_example dir="chat" logdir="${basedir}/target/logs" />
+ <build_example dir="distmap" logdir="${basedir}/target/logs" />
<build_example dir="example" logdir="${basedir}/target/logs" />
+ <build_example dir="perf" logdir="${basedir}/target/logs" />
</target>
</project>
Modified: incubator/etch/branches/etch-python/examples/chat/README.txt
URL: http://svn.apache.org/viewvc/incubator/etch/branches/etch-python/examples/chat/README.txt?rev=718128&r1=718127&r2=718128&view=diff
==============================================================================
--- incubator/etch/branches/etch-python/examples/chat/README.txt (original)
+++ incubator/etch/branches/etch-python/examples/chat/README.txt Sun Nov 16 14:35:35 2008
@@ -1,10 +1,26 @@
-
To build:
> ant debug
-To Run:
+The debug ant target will build both the java and csharp
+programs.
+
+To run java chat, open a cmd window and run these commands:
+
+> cd target\bin
+> start java -cp chat.jar etch.examples.chat.MainChatListener
+> start java -cp chat.jar etch.examples.chat.MainChatClient
+> start java -cp chat.jar etch.examples.chat.MainChatClient
+
+Two chat clients will be started; there is a help command;
+login as a different user in each with any password; they
+can chat with each other!
+
+To run csharp chat:
+
+> cd target\bin
+> start ChatListener.exe
+> start ChatClient.exe
+> start ChatClient.exe
-> cd target/bin
-> java -cp chat.jar etch.examples.chat.MainChatListener
-> java -cp chat.jar etch.examples.chat.MainChatClient
+You can mix and match the various clients and listeners.