[jboss-svn-commits] JBL Code SVN: r22809 - in labs/jbosstm/workspace/adinn/orchestration: dd/grammar and 12 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Tue Sep 16 08:42:51 EDT 2008


Author: adinn
Date: 2008-09-16 08:42:51 -0400 (Tue, 16 Sep 2008)
New Revision: 22809

Added:
   labs/jbosstm/workspace/adinn/orchestration/src/TestJar.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/RuleAdapter.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/RuleCheckAdapter.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Rule.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/RuleElement.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/exception/
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/exception/CompileException.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/exception/ExecuteException.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/exception/ParseException.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/exception/TypeException.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/type/TypeHelper.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/synchronization/
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/synchronization/CountDown.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/synchronization/Waiter.java
   labs/jbosstm/workspace/adinn/orchestration/testrule.txt
   labs/jbosstm/workspace/adinn/orchestration/testrule2.txt
Modified:
   labs/jbosstm/workspace/adinn/orchestration/README
   labs/jbosstm/workspace/adinn/orchestration/build.xml
   labs/jbosstm/workspace/adinn/orchestration/dd/grammar/ECAGrammar.g
   labs/jbosstm/workspace/adinn/orchestration/dd/grammar/ECAToken.g
   labs/jbosstm/workspace/adinn/orchestration/ext/asm-all-3.0.jar
   labs/jbosstm/workspace/adinn/orchestration/src/TestRule.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/Transformer.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Action.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Condition.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Event.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/binding/Binding.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/binding/Bindings.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ArithmeticExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ArrayExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BinaryOperExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BitExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BooleanExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ComparisonExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ConditionalEvalExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/DollarExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/Expression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ExpressionHelper.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/FieldExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/LogicalExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/MethodExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/NotExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/NumericLiteral.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/OperExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/PlusExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StaticExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StringLiteral.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StringPlusExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/TernaryOperExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/TwiddleExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/UnaryOperExpression.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/Variable.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/grammar/ECAGrammarParser.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/grammar/ECATokenLexer.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/type/Type.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/type/TypeGroup.java
   labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/test/HandlerClass.java
Log:
first working version of rule triggering and rule execution (via interpretation)

Modified: labs/jbosstm/workspace/adinn/orchestration/README
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/README	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/README	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,4 +1,4 @@
-Type 'build jar' to create the orchestration package library which
+Type 'ant jar' to create the orchestration package library which
 supports definition of Event Condition Action rules for orchestrating
 JBossTS test scenarios and insertion of event triggers into com.arjuna
 methods, either at start/end of method or at specific line numbers.
@@ -76,11 +76,11 @@
 be referenced during evaluaton of the rule condition and execution of
 the rule actions. Bindings are established in the context of the
 target method. By default, the target method arguments are pre-bound
-using the special syntax $0 (for this) and $1...$N for arguments 1..N
-of the target method. Further bindings, using symbolic names for the
-bound variables, can be established by assigning a symbolic constant
-to an expression which may refer to previously bound variables. For
-example,
+using the special syntax $0 (for this, when the target method is not
+static) and $1...$N for arguments 1..N of the target method. Further
+bindings, using symbolic names for the bound variables, can be
+established by assigning a symbolic constant to an expression which
+may refer to previously bound variables. For example,
 
   'coordinator:Coordinator = $0,
    recovered:boolean = coordinator.recovered,
@@ -96,24 +96,25 @@
 possible. The RHS of a binding is a Java exprssion which can include
 various builtin expressions plus most Java expressions such as static
 or instance field accesses and invocations, array dereferences,
-operator expressions, etc (but *not* assignment expressions). The RHS
-of a binding may refer to variables bound in earlier bindings but not
-to its own variable or later bound variables. Rebinding of variables
-is not permitted.
+operator expressions, method invocations, etc (but *not* assignment
+expressions). The RHS of a binding may refer to method argument
+variables and to variables bound in earlier bindings but not to its
+own variable or later bound variables. Rebinding of variables is not
+permitted.
 
 A condition is merely an expression with a boolean result. Expressions
 may be complex using the usual operators plus a few syntactically
-sugared variants. Conditions may also include references to bound
-variables. So, for example:
+sugared variants. Conditions may also include method invocations and
+references to bound variables. So, for example:
 
-  'recovered AND findCounter(instanceIdentifier)'
+  'engine.isRecovered() AND getCountdown(instanceIdentifier)'
 
-will succeed if recovered is true and the counter identified
-by instanceIdentifer is active
+will succeed if engine.isRecovered() returns true and a countdown
+identified by instanceIdentifer is active
 
-  'NOT recovered && decrementCounter(instanceIdentifier)'
+  'NOT recovered && countDown(instanceIdentifier)'
 
-will suceed if recovered is false and decrementing the counter
+will succeed if recovered is false and decrementing the countdown
 identified by instanceIdentifier renders it inactive.
 
 An action is a series of expressions which are either method

Modified: labs/jbosstm/workspace/adinn/orchestration/build.xml
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/build.xml	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/build.xml	2008-09-16 12:42:51 UTC (rev 22809)
@@ -36,6 +36,14 @@
 The JBoss installation directory must be specified with the JBOSS_HOME environment variable or the jboss.home property.
     </fail>
 
+    <condition property="xts.home" value="${env.XTS_HOME}">
+      <isset property="env.XTS_HOME"/>
+    </condition>
+
+    <fail unless="xts.home">
+The XTS installation directory must be specified with the XTS_HOME environment variable or the xts.home property.
+    </fail>
+
     <property name="src.dir"           value="src"/>
     <property name="dd.dir"            value="dd"/>
     <property name="dd.grammar.dir"    value="${dd.dir}/grammar"/>
@@ -46,6 +54,21 @@
 
     <property name="ext.antlr.jars" value="antlr-3.0.1.jar antlr-2.7.7.jar antlr-runtime-3.0.1.jar stringtemplate-3.1b1.jar"/>
 
+    <!-- we need to add the XTS, TS and various jboss jars to the test code path so we can
+    load XTS code -->
+
+    <property name="xts.lib.dir"     value="${xts.home}/lib"/>
+    <property name="jboss.lib.dir"     value="${jboss.home}/lib"/>
+    <property name="jboss.client.dir"     value="${jboss.home}/client"/>
+    <property name="jboss.server.lib.dir"     value="${jboss.home}/server/default/lib"/>
+    <property name="jboss.server.deploy.dir"     value="${jboss.home}/server/default/deploy"/>
+
+    <property name="xts.lib.jars" value="ws-c10.jar wscf10.jar wscf.jar ws-t10.jar ws-t.jar wstx11.jar wsas.jar ws-c11.jar wscf11.jar ws-c.jar ws-t11.jar wstx10.jar wstx.jar"/>
+    <property name="jboss.lib.jars" value="stax-api.jar"/>
+    <property name="jboss.client.jars" value="jbossall-client.jar"/>
+    <property name="jboss.server.lib.jars" value="jbossjta.jar jbossws-common.jar jbossws-framework.jar jbossws-spi.jar jbossws-native-saaj.jar jbossws-native-jaxws.jar jbossws-native-jaxws-ext.jar jbossws-native-jaxrpc.jar jboss-javaee.jar"/>
+    <property name="jboss.server.deploy.jars" value="jbossws.sar"/>
+
     <!-- enable debugging of XTS service code -->
 
     <property name="javac.debug" value="on"/>
@@ -108,7 +131,7 @@
        <delete dir="${dd.grammar.dir}" includes="*.java *.tokens"/>
     </target>
 
-    <target name="TestParse.compile" depends="jar">
+    <target name="TestParse.compile">
         <!-- build the TestParse class and then run it to check parsing of rule elements -->
         <javac srcdir="${src.dir}" destdir="${build.classes.dir}" debug="${javac.debug}" source="TestParse.java">
             <classpath>
@@ -131,7 +154,7 @@
         </java>
     </target>
 
-    <target name="TestRule.compile" depends="jar">
+    <target name="TestRule.compile">
         <!-- build the TestRule class and then run it to check parsing of rule elements -->
         <javac srcdir="${src.dir}" destdir="${build.classes.dir}" debug="${javac.debug}" source="TestRule.java">
             <classpath>
@@ -147,7 +170,37 @@
                 <fileset dir="${build.lib.dir}" includes="orchestration.jar"/>
                 <pathelement location="${asm.home}/${asm.jar}"/>
                 <fileset dir="${ext.lib.dir}" includes="${ext.antlr.jars}"/>
+                <!-- xts, jta and jboss as jars-->
+                <fileset dir="${xts.lib.dir}" includes="${xts.lib.jars}"/>
+                <fileset dir="${jboss.lib.dir}" includes="${jboss.lib.jars}"/>
+                <fileset dir="${jboss.client.dir}" includes="${jboss.client.jars}"/>
+                <fileset dir="${jboss.server.lib.dir}" includes="${jboss.server.lib.jars}"/>
+                <fileset dir="${jboss.server.deploy.dir}" includes="${jboss.server.deploy.jars}"/>
             </classpath>
+            <arg value='-event'/>
+            <arg value='coordinator:Coordinator = $1, recovered : boolean = coordinator.recovered, identifier : String = coordinator.getInstanceIdentifier()'/>
+            <arg value='-condition'/>
+            <arg value='recovered AND getCounter(identifier)'/>
+            <arg value='-action'/>
+            <arg value='debug("killing prepare"),  killThread()'/>
+	    <arg value = "-rule"/>
+	    <arg value='WHEN coordinator:Coordinator = $1, recovered : boolean = coordinator.recovered, identifier : String = coordinator.getInstanceIdentifier() IF recovered AND getCounter(identifier) DO debug("killing prepare"), killThread()'/>
+        </java>
+    </target>
+
+    <target name="TestRuleDebug" depends="TestRule.compile">
+        <java classname="TestRule" fork="true">
+            <classpath>
+                <fileset dir="${build.lib.dir}" includes="orchestration.jar"/>
+                <pathelement location="${asm.home}/${asm.jar}"/>
+                <fileset dir="${ext.lib.dir}" includes="${ext.antlr.jars}"/>
+                <!-- xts, jta and jboss as jars-->
+                <fileset dir="${xts.lib.dir}" includes="${xts.lib.jars}"/>
+                <fileset dir="${jboss.lib.dir}" includes="${jboss.lib.jars}"/>
+                <fileset dir="${jboss.client.dir}" includes="${jboss.client.jars}"/>
+                <fileset dir="${jboss.server.lib.dir}" includes="${jboss.server.lib.jars}"/>
+                <fileset dir="${jboss.server.deploy.dir}" includes="${jboss.server.deploy.jars}"/>
+            </classpath>
             <jvmarg value="-Xdebug"/>
             <jvmarg value="-Xrunjdwp:transport=dt_socket,server=n,address=5005,suspend=y"/>
             <arg value='-event'/>
@@ -156,6 +209,145 @@
             <arg value='recovered AND getCounter(identifier)'/>
             <arg value='-action'/>
             <arg value='debug("killing prepare"),  killThread()'/>
+	    <arg value = "-rule"/>
+	    <arg value='WHEN coordinator:Coordinator = $1, recovered : boolean = coordinator.recovered, identifier : String = coordinator.getInstanceIdentifier() IF recovered AND getCounter(identifier) DO debug("killing prepare"), killThread()'/>
         </java>
     </target>
+
+    <target name="TestRuleFile" depends="TestRule.compile">
+        <java classname="TestRule" fork="true">
+            <classpath>
+                <fileset dir="${build.lib.dir}" includes="orchestration.jar"/>
+                <pathelement location="${asm.home}/${asm.jar}"/>
+                <fileset dir="${ext.lib.dir}" includes="${ext.antlr.jars}"/>
+                <!-- xts, jta and jboss as jars-->
+                <fileset dir="${xts.lib.dir}" includes="${xts.lib.jars}"/>
+                <fileset dir="${jboss.lib.dir}" includes="${jboss.lib.jars}"/>
+                <fileset dir="${jboss.client.dir}" includes="${jboss.client.jars}"/>
+                <fileset dir="${jboss.server.lib.dir}" includes="${jboss.server.lib.jars}"/>
+                <fileset dir="${jboss.server.deploy.dir}" includes="${jboss.server.deploy.jars}"/>
+            </classpath>
+            <arg value='-class'/>
+            <arg value='com.arjuna.wst11.messaging.engines.CoordinatorEngine'/>
+            <arg value='-method'/>
+            <arg value='commit'/>
+            <arg value='-file'/>
+            <arg value='testrule.txt'/>
+        </java>
+    </target>
+
+    <target name="TestRuleFileDebug" depends="TestRule.compile">
+        <java classname="TestRule" fork="true">
+            <classpath>
+                <fileset dir="${build.lib.dir}" includes="orchestration.jar"/>
+                <pathelement location="${asm.home}/${asm.jar}"/>
+                <fileset dir="${ext.lib.dir}" includes="${ext.antlr.jars}"/>
+                <!-- xts, jta and jboss as jars-->
+                <fileset dir="${xts.lib.dir}" includes="${xts.lib.jars}"/>
+                <fileset dir="${jboss.lib.dir}" includes="${jboss.lib.jars}"/>
+                <fileset dir="${jboss.client.dir}" includes="${jboss.client.jars}"/>
+                <fileset dir="${jboss.server.lib.dir}" includes="${jboss.server.lib.jars}"/>
+                <fileset dir="${jboss.server.deploy.dir}" includes="${jboss.server.deploy.jars}"/>
+            </classpath>
+            <jvmarg value="-Xdebug"/>
+            <jvmarg value="-Xrunjdwp:transport=dt_socket,server=n,address=5005,suspend=y"/>
+            <arg value='-class'/>
+            <arg value='com.arjuna.wst11.messaging.engines.CoordinatorEngine'/>
+            <arg value='-method'/>
+            <arg value='commit'/>
+            <arg value='-file'/>
+            <arg value='testrule.txt'/>
+        </java>
+    </target>
+    <target name="TestRuleFile2" depends="TestRule.compile">
+        <java classname="TestRule" fork="true">
+            <classpath>
+                <fileset dir="${build.lib.dir}" includes="orchestration.jar"/>
+                <pathelement location="${asm.home}/${asm.jar}"/>
+                <fileset dir="${ext.lib.dir}" includes="${ext.antlr.jars}"/>
+                <!-- xts, jta and jboss as jars-->
+                <fileset dir="${xts.lib.dir}" includes="${xts.lib.jars}"/>
+                <fileset dir="${jboss.lib.dir}" includes="${jboss.lib.jars}"/>
+                <fileset dir="${jboss.client.dir}" includes="${jboss.client.jars}"/>
+                <fileset dir="${jboss.server.lib.dir}" includes="${jboss.server.lib.jars}"/>
+                <fileset dir="${jboss.server.deploy.dir}" includes="${jboss.server.deploy.jars}"/>
+            </classpath>
+            <arg value='-class'/>
+            <arg value='com.arjuna.wst11.messaging.CoordinatorProcessorImpl'/>
+            <arg value='-method'/>
+            <arg value='committed(Notification, AddressingProperties, ArjunaContext)'/>
+            <arg value='-file'/>
+            <arg value='testrule2.txt'/>
+        </java>
+    </target>
+    <target name="TestRuleFileDebug2" depends="TestRule.compile">
+        <java classname="TestRule" fork="true">
+            <classpath>
+                <fileset dir="${build.lib.dir}" includes="orchestration.jar"/>
+                <pathelement location="${asm.home}/${asm.jar}"/>
+                <fileset dir="${ext.lib.dir}" includes="${ext.antlr.jars}"/>
+                <!-- xts, jta and jboss as jars-->
+                <fileset dir="${xts.lib.dir}" includes="${xts.lib.jars}"/>
+                <fileset dir="${jboss.lib.dir}" includes="${jboss.lib.jars}"/>
+                <fileset dir="${jboss.client.dir}" includes="${jboss.client.jars}"/>
+                <fileset dir="${jboss.server.lib.dir}" includes="${jboss.server.lib.jars}"/>
+                <fileset dir="${jboss.server.deploy.dir}" includes="${jboss.server.deploy.jars}"/>
+            </classpath>
+            <jvmarg value="-Xdebug"/>
+            <jvmarg value="-Xrunjdwp:transport=dt_socket,server=n,address=5005,suspend=y"/>
+            <arg value='-class'/>
+            <arg value='com.arjuna.wst11.messaging.CoordinatorProcessorImpl'/>
+            <arg value='-method'/>
+            <arg value='committed(Notification, AddressingProperties, ArjunaContext)'/>
+            <arg value='-file'/>
+            <arg value='testrule2.txt'/>
+        </java>
+    </target>
+
+    <target name="TestJar.compile">
+        <!-- build the TestJar class and then run it to check parsing of rule elements -->
+        <javac srcdir="${src.dir}" destdir="${build.classes.dir}" debug="${javac.debug}" source="TestJar.java">
+            <classpath>
+                <pathelement location="${asm.home}/${asm.jar}"/>
+                <fileset dir="${ext.lib.dir}" includes="${ext.antlr.jars}"/>
+            </classpath>
+        </javac>
+    </target>
+
+    <target name="TestJar" depends="TestJar.compile">
+        <java classname="TestJar" fork="true">
+            <classpath>
+                <fileset dir="${build.lib.dir}" includes="orchestration.jar"/>
+                <pathelement location="${asm.home}/${asm.jar}"/>
+                <fileset dir="${ext.lib.dir}" includes="${ext.antlr.jars}"/>
+                <!-- xts, jta and jboss as jars-->
+                <fileset dir="${xts.lib.dir}" includes="${xts.lib.jars}"/>
+                <fileset dir="${jboss.lib.dir}" includes="${jboss.lib.jars}"/>
+                <fileset dir="${jboss.client.dir}" includes="${jboss.client.jars}"/>
+                <fileset dir="${jboss.server.lib.dir}" includes="${jboss.server.lib.jars}"/>
+                <fileset dir="${jboss.server.deploy.dir}" includes="${jboss.server.deploy.jars}"/>
+            </classpath>
+	    <arg value="${build.lib.dir}/orchestration.jar"/>
+        </java>
+    </target>
+
+    <target name="TestJarDebug" depends="TestJar.compile">
+        <java classname="TestJar" fork="true">
+            <classpath>
+                <fileset dir="${build.lib.dir}" includes="orchestration.jar"/>
+                <pathelement location="${asm.home}/${asm.jar}"/>
+                <fileset dir="${ext.lib.dir}" includes="${ext.antlr.jars}"/>
+                <!-- xts, jta and jboss as jars-->
+                <fileset dir="${xts.lib.dir}" includes="${xts.lib.jars}"/>
+                <fileset dir="${jboss.lib.dir}" includes="${jboss.lib.jars}"/>
+                <fileset dir="${jboss.client.dir}" includes="${jboss.client.jars}"/>
+                <fileset dir="${jboss.server.lib.dir}" includes="${jboss.server.lib.jars}"/>
+                <fileset dir="${jboss.server.deploy.dir}" includes="${jboss.server.deploy.jars}"/>
+            </classpath>
+            <jvmarg value="-Xdebug"/>
+            <jvmarg value="-Xrunjdwp:transport=dt_socket,server=n,address=5005,suspend=y"/>
+	    <arg value="${build.lib.dir}/orchestration.jar"/>
+        </java>
+    </target>
+
 </project>

Modified: labs/jbosstm/workspace/adinn/orchestration/dd/grammar/ECAGrammar.g
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/dd/grammar/ECAGrammar.g	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/dd/grammar/ECAGrammar.g	2008-09-16 12:42:51 UTC (rev 22809)
@@ -23,13 +23,23 @@
 package org.jboss.jbossts.orchestration.rule.grammar;
 }
 
-eca_rule	:	eca EOF ;
+eca_rule	:	eca EOF -> ^(eca)
+	;
+	
+eca_event	:	event EOF -> ^(event)
+	;
 
-eca	:	WHEN event	-> ^(WHEN event)
-	|	IF condition 	-> ^(IF condition)
-	|	DO action	-> ^(DO action)
+eca_condition	:	condition EOF -> ^(condition)
 	;
 
+eca_action	:	action EOF -> ^(action)
+	;
+
+eca	:	BIND e=event
+		IF c=condition
+		DO a=action	-> ^(BIND $e $c $a)
+	;
+
 // event specifications -- for now events are just a list of bindings
 
 event	:	bindings

Modified: labs/jbosstm/workspace/adinn/orchestration/dd/grammar/ECAToken.g
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/dd/grammar/ECAToken.g	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/dd/grammar/ECAToken.g	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,4 +1,4 @@
-lexer grammar ECAToken;
+klexer grammar ECAToken;
 
 @header {
 package org.jboss.jbossts.orchestration.rule.grammar;
@@ -44,7 +44,7 @@
 
 // builtin symbols -- need to add these before addign the rules for adding any old symbol
 
-WHEN	:	'WHEN'
+BIND	:	'BIND'
 	;
 
 IF	:	'IF'

Modified: labs/jbosstm/workspace/adinn/orchestration/ext/asm-all-3.0.jar
===================================================================
(Binary files differ)

Added: labs/jbosstm/workspace/adinn/orchestration/src/TestJar.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/TestJar.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/src/TestJar.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -0,0 +1,218 @@
+import org.jboss.jbossts.orchestration.annotation.EventHandlerClass;
+import org.jboss.jbossts.orchestration.annotation.EventHandler;
+import org.jboss.jbossts.orchestration.rule.type.TypeHelper;
+import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.exception.ParseException;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.CompileException;
+import org.objectweb.asm.Opcodes;
+
+import java.util.jar.JarFile;
+import java.util.jar.JarEntry;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.ArrayList;
+import java.io.File;
+import java.io.IOException;
+import java.net.URLClassLoader;
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Constructor;
+
+public class TestJar
+{
+    public static void main(String[] args)
+    {
+        TestJar testJar = new TestJar();
+        testJar.testJar(args);
+    }
+
+    public void testJar(String[] jarFiles)
+    {
+        for (String ruleJarPath : jarFiles) {
+            try {
+                JarFile jarFile = new JarFile(new File(ruleJarPath));
+                List<Class> ruleClasses = processRules(jarFile);
+                System.out.println("checking classes in " + ruleJarPath);
+                checkRules(ruleClasses);
+            } catch (IOException ioe) {
+                System.err.println("TestJar: unable to open rule jar file : " + ruleJarPath);
+            }
+        }
+    }
+
+
+
+    private List<Class> processRules(JarFile jarFile)
+    {
+        try {
+            URLClassLoader classLoader = new URLClassLoader(new URL[] { new URL("file:" + jarFile.getName()) });
+            Enumeration<JarEntry> jarEntries = jarFile.entries();
+            List<Class> ruleClasses = new ArrayList<Class>();
+
+            while (jarEntries.hasMoreElements()) {
+                JarEntry jarEntry = jarEntries.nextElement();
+                String entryName = jarEntry.getName();
+
+                if (entryName.endsWith(CLASS_FILE_SUFFIX)) {
+                    // try to load the rule class
+                    int classNameLength = entryName.length() - CLASS_FILE_SUFFIX.length();
+                    String className = entryName.substring(0, classNameLength).replaceAll("/", ".");
+                    try {
+                        Class clazz = classLoader.loadClass(className);
+                        Annotation a = clazz.getAnnotation(EventHandlerClass.class);
+                        if (a != null) {
+                            ruleClasses.add(clazz);
+                        }
+                    } catch (ClassNotFoundException e) {
+                        System.err.println("org.jboss.jbossts.orchestration.agent.Main: unable to load class " + className + " from : " + jarFile.getName());
+                    }
+                }
+            }
+            return ruleClasses;
+        } catch (MalformedURLException mue) {
+            System.err.println("org.jboss.jbossts.orchestration.agent.Main: unable to load classes from : " + jarFile.getName());
+        }
+        
+        return null;
+    }
+
+    private void checkRules(List<Class> ruleClasses)
+    {
+        ClassLoader loader = this.getClass().getClassLoader();
+
+        for (Class ruleClass : ruleClasses) {
+            Annotation classAnnotation = ruleClass.getAnnotation(EventHandlerClass.class);
+            for (Method method : ruleClass.getDeclaredMethods()) {
+                EventHandler eventHandler = method.getAnnotation(EventHandler.class);
+                if (eventHandler != null) {
+                    String targetClassName = eventHandler.targetClass();
+                    String targetMethodName = eventHandler.targetMethod();
+                    int targetLine = eventHandler.targetLine();
+                    String ruleName = targetClassName + "::" + targetMethodName + (targetLine < 0 ? "" : "@" + targetLine);
+                    try {
+                        Class targetClass = loader.loadClass(targetClassName);
+                        Method[] candidates = targetClass.getDeclaredMethods();
+                        boolean found = false;
+                        boolean multiple = false;
+                        for (Method candidate : candidates) {
+                            String targetName = TypeHelper.parseMethodName(targetMethodName);
+                            String targetDesc = TypeHelper.parseMethodDescriptor(targetMethodName);
+                            String candidateName = candidate.getName();
+                            String candidateDesc = makeDescriptor(candidate);
+                            if (targetName.equals(candidateName)) {
+                                if (targetDesc.equals("") || TypeHelper.equalDescriptors(targetDesc, candidateDesc)) {
+                                    System.err.println("TestJar: checking rule " + ruleName);
+                                    if (found) {
+                                        multiple = true;
+                                    }
+                                    found = true;
+                                    int access = 0;
+                                    if ((candidate.getModifiers() & Modifier.STATIC) != 0) {
+                                        access = Opcodes.ACC_STATIC;
+                                    }
+                                    String event = eventHandler.event();
+                                    String condition = eventHandler.condition();
+                                    String action = eventHandler.action();
+                                    Rule rule = Rule.create(ruleName, event, condition, action, loader);
+                                    System.err.println("TestJar: parsed rule " + ruleName);
+                                    System.err.println(rule);
+                                    rule.setTypeInfo(targetClassName, access, candidateName, candidateDesc);
+                                    rule.typeCheck();
+                                    System.err.println("TestJar: type checked rule " + ruleName);
+                                }
+                            }
+                        }
+                        if (!found) {
+                            Constructor[] constructors = targetClass.getConstructors();
+                            for (Constructor constructor : constructors) {
+                                String targetName = TypeHelper.parseMethodName(targetMethodName);
+                                String targetDesc = TypeHelper.parseMethodDescriptor(targetMethodName);
+                                String candidateName = constructor.getName();
+                                String candidateDesc = makeDescriptor(constructor);
+                                if (targetName.equals("<init>")) {
+                                    if (targetDesc.equals("") || TypeHelper.equalDescriptors(targetDesc, candidateDesc)) {
+                                        System.err.println("TestJar: checking rule " + ruleName);
+                                        if (found) {
+                                            multiple = true;
+                                        }
+                                        found = true;
+                                        int access = 0;
+                                        if ((constructor.getModifiers() & Modifier.STATIC) != 0) {
+                                            access = Opcodes.ACC_STATIC;
+                                        }
+                                        String event = eventHandler.event();
+                                        String condition = eventHandler.condition();
+                                        String action = eventHandler.action();
+                                        Rule rule = Rule.create(ruleName, event, condition, action, loader);
+                                        System.err.println("TestJar: parsed rule " + ruleName);
+                                        System.err.println(rule);
+                                        rule.setTypeInfo(targetClassName, access, candidateName, candidateDesc);
+                                        rule.typeCheck();
+                                        System.err.println("TestJar: type checked rule " + ruleName);
+                                    }
+                                }
+                            }
+                        }
+                        if (!found) {
+                            System.err.println("TestJar: no matching method for rule " + ruleName);
+                        } else if (multiple) {
+                            System.err.println("TestJar: multiple matching methods for rule " + ruleName);
+                        }
+                    } catch (ParseException e) {
+                        System.err.println("TestJar: parse exception for rule " + ruleName + " : " + e);
+                        e.printStackTrace(System.err);
+                    } catch (TypeException e) {
+                        System.err.println("TestJar: type exception for rule " + ruleName + " : " + e);
+                        e.printStackTrace(System.err);
+                    } catch (CompileException e) {
+                        System.err.println("TestJar: compile exception for rule " + " : " + ruleName + e);
+                        e.printStackTrace(System.err);
+                    } catch(ClassNotFoundException cfe) {
+                        System.err.println("TestJar: unable to load class " + targetClassName);
+                    }
+                }
+            }
+        }
+    }
+
+    static String makeDescriptor(Method method)
+    {
+        Class<?> paramTypes[] = method.getParameterTypes();
+        Class<?> retType = method.getReturnType();
+        String desc = "(";
+
+        for (Class<?> paramType : paramTypes) {
+            String name = paramType.getCanonicalName();
+            desc += TypeHelper.externalizeType(name);
+        }
+        desc += ")";
+        desc += TypeHelper.externalizeType(retType.getCanonicalName());
+
+        return desc;
+    }
+
+    static String makeDescriptor(Constructor constructor)
+    {
+        Class<?> paramTypes[] = constructor.getParameterTypes();
+        String desc = "(";
+
+        for (Class<?> paramType : paramTypes) {
+            String name = paramType.getCanonicalName();
+            desc += TypeHelper.externalizeType(name);
+        }
+        desc += ")";
+
+        return desc;
+    }
+
+    /**
+     * suffix found on end of .class files (doh :-)
+     */
+
+    private static final String CLASS_FILE_SUFFIX = ".class";
+}
\ No newline at end of file

Modified: labs/jbosstm/workspace/adinn/orchestration/src/TestRule.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/TestRule.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/TestRule.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,54 +1,190 @@
-import org.antlr.runtime.tree.CommonTree;
-import org.antlr.runtime.ANTLRStringStream;
-import org.antlr.runtime.CommonTokenStream;
-import org.antlr.runtime.RecognitionException;
-import org.jboss.jbossts.orchestration.rule.grammar.ECATokenLexer;
-import org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser;
 import org.jboss.jbossts.orchestration.rule.Condition;
 import org.jboss.jbossts.orchestration.rule.Event;
 import org.jboss.jbossts.orchestration.rule.Action;
+import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeHelper;
+import org.objectweb.asm.Opcodes;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
 public class TestRule
 {
     public static void main(String[] args)
     {
+        ClassLoader loader = TestRule.class.getClassLoader();
         Event event = null;
         Condition condition = null;
         Action action = null;
+        Rule rule = null;
+        String className = null;
+        String methodName = null;
+        String descriptor = null;
         for (int i = 0; i < args.length ; i++) {
-            if ("-event".equals(args[i])) {
+            if ("-class".equals(args[i])) {
+                System.out.println("Using class name " + args[++i]);
+                className = args[i];
+            } else if ("-method".equals(args[i])) {
+                System.out.println("Using method name " + args[++i]);
+                methodName = args[i];
+            } else if ("-descriptor".equals(args[i])) {
+                System.out.println("Using method descriptor " + args[++i]);
+                descriptor = args[i];
+            } else if ("-rule".equals(args[i])) {
+                System.out.println("Creating rule from " + args[++i]);
+                try {
+                    rule = Rule.create("TestRule" + i, args[i], loader);
+                    System.out.print(rule);
+                    if (methodName != null &&
+                            className != null) {
+                        maybeTypeCheck(rule, className, methodName, descriptor);
+                    }
+                } catch (Throwable th) {
+                    // bad rule event
+                    System.err.println(": error " + th);
+                    th.printStackTrace(System.err);
+                }
+            } else if ("-event".equals(args[i])) {
                 System.out.println("Creating event from " + args[++i]);
                 try {
-                    event = Event.create(args[i]);
+                    rule = Rule.create("TestRule" + i, loader);
+                    rule.setEvent(args[i]);
+                    System.out.print(rule);
                 } catch (Throwable th) {
                     // bad rule event
                     System.err.println(": error " + th);
+                    th.printStackTrace(System.err);
                 }
             } else if ("-condition".equals(args[i])) {
-                if (event != null) {
-                    System.out.println("Creating condition from " + args[++i]);
-                    try {
-                        condition = Condition.create(event.getTypeGroup(), event.getBindings(), args[i]);
-                    } catch (Throwable th) {
-                        // bad rule event
-                        System.err.println(": error " + th);
+                System.out.println("Creating condition from " + args[++i]);
+                try {
+                    if (rule == null || rule.getCondition() != null) {
+                        rule = Rule.create("TestRule" + i, loader);
                     }
-                } else {
-                     System.out.println("No event to create condition from " + args[++i]);
+                    if (rule.getEvent() == null) {
+                        rule.setEvent("");
+                    }
+                    rule.setCondition(args[i]);
+                    System.out.print(rule);
+                } catch (Throwable th) {
+                    // bad rule event
+                    System.err.println(": error " + th);
+                    th.printStackTrace(System.err);
                 }
             } else if ("-action".equals(args[i])) {
-                if (event != null) {
-                    System.out.println("Creating action from " + args[++i]);
-                    try {
-                        action = Action.create(event.getTypeGroup(), event.getBindings(), args[i]);
-                    } catch (Throwable th) {
-                        // bad rule event
-                        System.err.println(": error " + th);
+                System.out.println("Creating action from " + args[++i]);
+                try {
+                    if (rule == null || rule.getAction() != null) {
+                        rule = Rule.create("TestRule" + i, loader);
                     }
-                } else {
-                     System.out.println("No event to create action from " + args[++i]);
+                    if (rule.getEvent() == null) {
+                        rule.setEvent("");
+                    }
+                    if (rule.getCondition() == null) {
+                        rule.setCondition("");
+                    }
+                    rule.setAction(args[i]);
+                    System.out.print(rule);
+                    if (methodName != null &&
+                            className != null) {
+                        maybeTypeCheck(rule, className, methodName, descriptor);
+                    }
+                } catch (Throwable th) {
+                    // bad rule event
+                    System.err.println(": error " + th);
+                    th.printStackTrace(System.err);
                 }
+            } else if ("-file".equals(args[i])) {
+                String fileName = args[++i];
+                System.out.println("Creating rule from file " + fileName);
+                try {
+                    byte[] bytes;
+                    String text;
+                    FileInputStream fis = new FileInputStream(fileName);
+                    bytes = new byte[fis.available()];
+                    fis.read(bytes);
+                    text = new String(bytes);
+                    rule = Rule.create("TestRule" + i, text, loader);
+                    System.out.print(rule);
+                    if (methodName != null &&
+                            className != null) {
+                        maybeTypeCheck(rule, className, methodName, descriptor);
+                    }
+                } catch (Throwable th) {
+                    // bad rule event
+                    System.err.println(": error " + th);
+                    th.printStackTrace(System.err);
+                }
             }
         }
     }
+
+    public static void maybeTypeCheck(Rule rule, String className, String methodName, String descriptor)
+    {
+        Type type = rule.getTypeGroup().create(className);
+
+        if (type != null && !type.isUndefined()) {
+            Class clazz = type.getClass();
+            Method[] methods = type.getTargetClass().getMethods();
+            Method method = null;
+
+            for (int i = 0; i < methods.length; i++) {
+                if (methods[i].getName().equals(methodName)) {
+                    if (descriptor != null) {
+                        Class<?>[] paramTypes = methods[i].getParameterTypes();
+                        Class<?> retType = methods[i].getReturnType();
+                        String methDescriptor = makeDescriptor(paramTypes, retType);
+                        if (TypeHelper.equalDescriptors(descriptor, methDescriptor)) {
+                            method = methods[i];
+                            break;
+                        }
+                    } else if (method != null) {
+                            // hmm, cannot resolve alternatives
+                            System.out.println("ambiguous method name " + className + "." + methodName);
+                            method = null;
+                            break;
+                    } else {
+                        method = methods[i];
+                        // carry on in case the method name is ambiguous
+                    }
+                }
+            }
+            if (method != null) {
+                if (descriptor == null) {
+                    Class<?>[] paramTypes = method.getParameterTypes();
+                    Class<?> retType = method.getReturnType();
+                    descriptor = makeDescriptor(paramTypes, retType);
+                }
+                try {
+                    int modifiers = method.getModifiers();
+                    int access = 0;
+                    if ((modifiers & Modifier.STATIC) != 0) {
+                        access |= Opcodes.ACC_STATIC;
+                    }
+                    rule.setTypeInfo(className, access, methodName,  descriptor);
+                    rule.typeCheck();
+                } catch (TypeException te) {
+                    System.out.println("error typechecking against " + className + "." + methodName + " : " + te);
+                }
+            }
+        }
+    }
+
+    public static String makeDescriptor(Class<?>[] paramTypes, Class<?> retType)
+    {
+        String descriptor = "(";
+
+        for (int i = 0; i < paramTypes.length; i++) {
+            descriptor += TypeHelper.externalizeType(paramTypes[i].getCanonicalName());
+        }
+        descriptor += ") ";
+
+        descriptor += TypeHelper.externalizeType(retType.getCanonicalName());
+
+        return descriptor;
+    }
 }
\ No newline at end of file

Added: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/RuleAdapter.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/RuleAdapter.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/RuleAdapter.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -0,0 +1,136 @@
+package org.jboss.jbossts.orchestration.agent;
+
+import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.type.TypeHelper;
+import org.objectweb.asm.*;
+import org.objectweb.asm.commons.GeneratorAdapter;
+import org.objectweb.asm.commons.Method;
+
+/**
+ * asm Adapter class used to add a rule event trigger call to a method of som egiven class
+ */
+public class RuleAdapter extends ClassAdapter
+{
+    RuleAdapter(ClassVisitor cv, Rule rule, String targetClass, String handlerMethod, int handlerLine)
+    {
+        super(cv);
+        this.rule = rule;
+        this.targetClass = targetClass;
+        this.targetMethod = TypeHelper.parseMethodName(handlerMethod);
+        this.targetDescriptor = TypeHelper.parseMethodDescriptor(handlerMethod);
+        this.targetLine = handlerLine;
+        this.visitedLine = false;
+    }
+
+    void visitClass(
+            final int version,
+            final int access,
+            final String name,
+            final String signature,
+            final String superName,
+            final String[] interfaces)
+    {
+        super.visit(version, access, name, signature, superName, interfaces);
+    }
+
+    public FieldVisitor visitField(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final Object value)
+    {
+        FieldVisitor fv = super.visitField(access, name, desc, signature, value);
+        return fv;
+    }
+
+    public MethodVisitor visitMethod(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final String[] exceptions)
+    {
+        MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
+        if (targetMethod.equals(name)) {
+            if (targetDescriptor.equals("") || TypeHelper.equalDescriptors(targetDescriptor, desc))
+            {
+                return new RuleMethodAdapter(mv, access, name, desc, signature, exceptions);
+            }
+        }
+
+        return mv;
+    }
+
+    /**
+     * a method visitor used to add a rule event trigger call to a method
+     */
+
+    private class RuleMethodAdapter extends GeneratorAdapter
+    {
+        private int access;
+        private String name;
+        private String descriptor;
+        private String signature;
+        private String[] exceptions;
+        private Label startLabel;
+        private Label endLabel;
+
+        RuleMethodAdapter(MethodVisitor mv, int access, String name, String descriptor, String signature, String[] exceptions)
+        {
+            super(mv, access, name, descriptor);
+            this.access = access;
+            this.name = name;
+            this.descriptor = descriptor;
+            this.signature = signature;
+            this.exceptions = exceptions;
+            startLabel = null;
+            endLabel = null;
+        }
+
+        // somewhere we need to add a catch exception block
+        // super.catchException(startLabel, endLabel, new Type("org.jboss.jbossts.orchestration.rule.exception.ExecuteException")));
+
+        public void visitLineNumber(final int line, final Label start) {
+            if (!visitedLine && (targetLine < 0 || targetLine == line)) {
+                rule.setTypeInfo(targetClass, access, name, descriptor);
+                String key = rule.getKey();
+                Type ruleType = Type.getType(TypeHelper.externalizeType("org.jboss.jbossts.orchestration.rule.Rule"));
+                Method method = Method.getMethod("void execute(String, Object, Object[])");
+                // we are at the relevant line in the method -- so add a trigger call here
+                System.out.println("RuleMethodAdapter.visitLineNumber : inserting trigger for " + rule.getName());
+                startLabel = super.newLabel();
+                endLabel = super.newLabel();
+                super.visitLabel(startLabel);
+                super.push(key);
+                if ((access & Opcodes.ACC_STATIC) == 0) {
+                    super.loadThis();
+                } else {
+                    super.push((Type)null);
+                }
+                super.loadArgArray();
+                super.invokeStatic(ruleType, method);
+                super.visitLabel(endLabel);
+                visitedLine = true;
+            }
+            super.visitLineNumber(line, start);
+        }
+
+        public void visitEnd()
+        {
+            Type exceptionType = Type.getType(TypeHelper.externalizeType("org.jboss.jbossts.orchestration.rule.exception.ExecuteException"));
+            super.catchException(startLabel, endLabel, exceptionType);
+            super.throwException(exceptionType, rule.getName() + " execution exception ");
+            super.visitEnd();
+        }
+    }
+
+    private Rule rule;
+    private String targetClass;
+    private String targetMethod;
+    private String targetDescriptor;
+    private int targetLine;
+    private String fieldName;
+    private boolean visitedLine;
+}

Added: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/RuleCheckAdapter.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/RuleCheckAdapter.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/RuleCheckAdapter.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -0,0 +1,91 @@
+package org.jboss.jbossts.orchestration.agent;
+
+import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.type.TypeHelper;
+import org.objectweb.asm.*;
+
+/**
+ * asm Adapter class used to check that the target method for a rule exists in a class
+ */
+public class RuleCheckAdapter extends ClassAdapter
+{
+    RuleCheckAdapter(ClassVisitor cv, String targetClass, String handlerMethod, int handlerLine)
+    {
+        super(cv);
+        this.targetClass = targetClass;
+        this.targetMethod = TypeHelper.parseMethodName(handlerMethod);
+        this.targetDescriptor = TypeHelper.parseMethodDescriptor(handlerMethod);
+        this.targetLine = handlerLine;
+        this.visitOk = false;
+    }
+
+    public MethodVisitor visitMethod(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final String[] exceptions)
+    {
+        MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
+        if (targetMethod.equals(name)) {
+            if (targetDescriptor.equals("") || TypeHelper.equalDescriptors(targetDescriptor, desc))
+            {
+                return new RuleCheckMethodAdapter(mv, access, name, desc, signature, exceptions);
+            }
+        }
+
+        return mv;
+    }
+
+    public boolean isVisitOk()
+    {
+        return visitOk;
+    }
+
+    /**
+     * a method visitor used to add a rule event trigger call to a method
+     */
+
+    private class RuleCheckMethodAdapter extends MethodAdapter
+    {
+        private int access;
+        private String name;
+        private String descriptor;
+        private String signature;
+        private String[] exceptions;
+        private boolean visited;
+
+        RuleCheckMethodAdapter(MethodVisitor mv, int access, String name, String descriptor, String signature, String[] exceptions)
+        {
+            super(mv);
+            this.access = access;
+            this.name = name;
+            this.descriptor = descriptor;
+            this.signature = signature;
+            this.exceptions = exceptions;
+            this.visited = false;
+        }
+
+        public void visitLineNumber(final int line, final Label start) {
+            if (!visited && (targetLine < 0 || targetLine == line)) {
+                // the relevant line occurs in the called method
+                visited = true;
+                visitOk = true;
+                String name = targetClass + "." + targetMethod + targetDescriptor;
+                if (targetLine >= 0) {
+                    name += targetLine;
+                }
+            }
+            mv.visitLineNumber(line, start);
+        }
+
+    }
+
+    private Rule rule;
+    private String targetClass;
+    private String targetMethod;
+    private String targetDescriptor;
+    private int targetLine;
+    private boolean visitOk;
+}
\ No newline at end of file

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/Transformer.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/Transformer.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/agent/Transformer.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -2,18 +2,13 @@
 
 import org.jboss.jbossts.orchestration.annotation.EventHandler;
 import org.jboss.jbossts.orchestration.annotation.EventHandlerClass;
-import org.jboss.jbossts.orchestration.rule.grammar.ECATokenLexer;
-import org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser;
-import org.jboss.jbossts.orchestration.rule.Event;
-import org.jboss.jbossts.orchestration.rule.Condition;
-import org.jboss.jbossts.orchestration.rule.Action;
-import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
-import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.Rule;
+import org.jboss.jbossts.orchestration.rule.type.TypeHelper;
+import org.jboss.jbossts.orchestration.rule.exception.ParseException;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.objectweb.asm.*;
-import org.antlr.runtime.tree.CommonTree;
-import org.antlr.runtime.ANTLRStringStream;
-import org.antlr.runtime.CommonTokenStream;
-import org.antlr.runtime.RecognitionException;
+import org.objectweb.asm.commons.EmptyVisitor;
+import org.objectweb.asm.util.TraceClassVisitor;
 
 import java.lang.instrument.ClassFileTransformer;
 import java.lang.instrument.IllegalClassFormatException;
@@ -24,6 +19,9 @@
 import java.util.List;
 import java.util.HashMap;
 import java.util.ArrayList;
+import java.io.PrintWriter;
+import java.io.FileOutputStream;
+import java.io.IOException;
 
 /**
  * byte code transformer used to introduce orchestration events into JBoss code
@@ -49,7 +47,7 @@
             for (Method method : ruleClass.getDeclaredMethods()) {
                 EventHandler eventHandler = method.getAnnotation(EventHandler.class);
                 if (eventHandler != null) {
-                    String target = externalize(eventHandler.targetClass());
+                    String target = eventHandler.targetClass();
                     if (isTransformable(target)) {
                         List<Annotation> clazzes = targetToHandlerClassMap.get(target);
                         if (clazzes == null) {
@@ -135,245 +133,158 @@
                             byte[] classfileBuffer)
             throws IllegalClassFormatException
     {
+        byte[] newBuffer = classfileBuffer;
         // we only transform certain classes -- in  particular, we exclude bootstrap classes whose loader is null
         // and we exclude orchestration classes
-        if (loader == null || className.startsWith("org/jboss/jbossts/orchestration/") || !isTransformable(className)) {
-            return classfileBuffer;
+        String internalClassName = TypeHelper.internalizeClass(className);
+
+        if (loader == null || isOrchestrationClass(internalClassName) || !isTransformable(internalClassName)) {
+            return null;
         }
 
         // ok, we need to check whether there are any event handlers associated with this class and if so
         // we will consider transforming the byte code
 
-        List<Method> handlerMethods = targetToHandlerMethodMap.get(className);
+        List<Method> handlerMethods = targetToHandlerMethodMap.get(internalClassName);
 
         if (handlerMethods != null) {
             for (Method handlerMethod : handlerMethods) {
                 try {
-                    transform(handlerMethod.getAnnotation(EventHandler.class), loader, className,  classBeingRedefined, classfileBuffer);
+                    newBuffer = transform(handlerMethod.getAnnotation(EventHandler.class), loader, internalClassName,  classBeingRedefined, newBuffer);
                 } catch (Throwable th) {
                     System.err.println("transform  : caught throwable " + th);
+                    th.printStackTrace(System.err);
                 }
             }
         }
 
-        return classfileBuffer;
-    }
+        // if the class is not in the defautl package then we also need to look for handlers
+        // which specify the class without the package qualification
 
-    private byte[] transform(EventHandler handler, ClassLoader loader, String className, Class targetClass, byte[] targetClassBytes)
-    {
-        // set up trees for parse;
-        CommonTree eventTree = null;
-        CommonTree conditionTree = null;
-        CommonTree actionTree = null;
+        int dotIdx = internalClassName.lastIndexOf('.');
 
-        System.out.println("org.jboss.jbossts.orchestration.agent.Transformer: Inserting trigger event");
-        System.out.println("                                                 : class " + className);
-        System.out.println("                                                 : method " + handler.targetMethod());
-        System.out.println("                                                 : line " + handler.targetLine());
-        Event event;
-        TypeGroup typeGroup;
-        Bindings bindings;
-        Condition condition;
-        Action action;
-        if ("".equals(handler.event())) {
-            System.out.println("                                                 : WHEN []");
-        } else {
-            System.out.println("                                                : " + handler.event());
+        if (dotIdx >= 0) {
+            handlerMethods = targetToHandlerMethodMap.get(internalClassName.substring(dotIdx + 1));
+            if (handlerMethods != null) {
+                for (Method handlerMethod : handlerMethods) {
+                    try {
+                        newBuffer = transform(handlerMethod.getAnnotation(EventHandler.class), loader, internalClassName,  classBeingRedefined, newBuffer);
+                    } catch (Throwable th) {
+                        System.err.println("transform  : caught throwable " + th);
+                        th.printStackTrace(System.err);
+                    }
+                }
+            }
         }
-        event = Event.create(handler.event());
-        typeGroup = event.getTypeGroup();
-        bindings = event.getBindings();
-        if ("".equals(handler.condition())) {
-            System.out.println("                                                 : IF true");
+
+        if (newBuffer != classfileBuffer) {
+            if (false) {
+                String name = (dotIdx < 0 ? internalClassName : internalClassName.substring(dotIdx + 1));
+                name += ".class";
+                System.out.println("Saving transformed bytes to " + name);
+                try {
+                    FileOutputStream fio = new FileOutputStream(name);
+                    fio.write(newBuffer);
+                    fio.close();
+                } catch (IOException ioe) {
+                    System.out.println("Error saving transformed bytes to" + name);
+                    ioe.printStackTrace(System.out);
+                }
+            }
+            return newBuffer;
         } else {
-            System.out.println("                                                : " + handler.condition());
+            return null;
         }
-        condition = Condition.create(typeGroup, bindings, handler.condition());
-        if ("".equals(handler.action())) {
-            System.out.println("                                                 : DO nothing");
-        } else {
-            System.out.println("                                                 : " + handler.action());
+    }
+
+    private byte[] transform(EventHandler handler, ClassLoader loader, String className, Class classBeingRedefined, byte[] targetClassBytes)
+    {
+        final String handlerClass = handler.targetClass();
+        final String handlerMethod = handler.targetMethod();
+        final int handlerLine = handler.targetLine();
+        System.out.println("org.jboss.jbossts.orchestration.agent.Transformer: Inserting trigger event");
+        System.out.println("  class " + handlerClass);
+        System.out.println("  method " + handlerMethod);
+        System.out.println("  line " + handlerLine);
+        final Rule rule;
+        String ruleName = handlerClass + "::" + handlerMethod;
+        if (handlerLine >= 0) {
+            ruleName += "@" + handlerLine;
         }
-        action = Action.create(typeGroup, bindings, handler.action());
-        final String finalClassName = className;
-        final EventHandler finalHandler = handler;
-        final ClassLoader finalLoader = loader;
-        final Class finalClass = targetClass;
-        final String targetMethod = handler.targetMethod();
-        final int targetLine = handler.targetLine();
-        final String targetName = parseMethodName(targetMethod);
-        final String targetSignature = parseMethodSignature(targetMethod);
-        final String ecaRuleFieldName = generateFieldName(targetName, targetSignature);
+        try {
+            rule = Rule.create(ruleName, handler.event(), handler.condition(), handler.action(), loader);
+        } catch (ParseException pe) {
+            System.out.println("Transformer : error parsing rule : " + pe);
+            return targetClassBytes;
+        } catch (TypeException te) {
+            System.out.println("Transformer : error checking rule : " + te);
+            return targetClassBytes;
+        } catch (Throwable th) {
+            System.out.println("Transformer : error processing rule : " + th);
+            return targetClassBytes;
+        }
+        System.out.println(rule);
 
-        final ClassReader cr = new ClassReader(targetClassBytes);
-        ClassWriter cw = new ClassWriter(0);
-        ClassAdapter adapter = new ClassAdapter(cw) {
-            public void visit(final int version,
-                    final int access,
-                    final String name,
-                    final String signature,
-                    final String superName,
-                    final String[] interfaces)
-            {
-                // create a class derived from ECARule to implement the event handler and
-                // instantiate it with a singleton instance
-                //
-                // n.b. we have to wait until here to do this because we need to resolve type names
-                // in the rule dynamically aginst types which are only available via the target class
-                // and its class loader.
-                //
-                // TODO see if we need to throw up if we try to resolve types which are not yet loaded
+        // ok, we have a rule with a matchingclass and a candidiate method and line number
+        // we need to see if the class has a matching method and, if so, add a call to
+        // execute the rule when we hit the relevant line
 
-                Class handlerClazz = generateHandlerClass(finalHandler, finalLoader, finalClassName, finalClass);
-                Object handlerSingleton = null;
-                try {
-                    // TODO do we want to give the handler class some arguments?
-                    handlerSingleton = handlerClazz.newInstance();
-                } catch (InstantiationException e) {
-                    // should not happen!!!
-                } catch (IllegalAccessException e) {
-                    // should not happen!!!
-                }
-
-                // add a static field to the class to hold the singleton
-/*                FieldVisitor visitor = visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL,
-                        ecaRuleFieldName,
-                        handlerClazz.getName(),
-                        null,
-                        handlerSingleton);
-*/
+        ClassReader cr = new ClassReader(targetClassBytes);
+        // ClassWriter cw = new ClassWriter(0);
+        ClassVisitor empty = new EmptyVisitor();
+        RuleCheckAdapter checkAdapter = new RuleCheckAdapter(empty, className, handlerMethod, handlerLine);
+        // PrintWriter pw = new PrintWriter(System.out);
+        // ClassVisitor traceAdapter = new TraceClassVisitor(cw, pw);
+        // RuleCheckAdapter adapter = new RuleAdapter(traceAdapter, rule, className, handlerMethod, handlerLine, loader);
+        try {
+            cr.accept(checkAdapter, 0);
+        } catch (Throwable th) {
+            System.out.println("Transformer : error applying rule " + rule.getName() + " to class " + className + th);
+            th.printStackTrace(System.out);
+            return targetClassBytes;
+        }
+        // only insert the rule trigger call if there is a suitable line in the target method
+        if (checkAdapter.isVisitOk()) {
+            cr = new ClassReader(targetClassBytes);
+            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+            // PrintWriter pw = new PrintWriter(System.out);
+            // ClassVisitor traceAdapter = new TraceClassVisitor(cw, pw);
+            // RuleAdapter adapter = new RuleAdapter(traceAdapter, rule, className, handlerMethod, handlerLine);
+            RuleAdapter adapter = new RuleAdapter(cw, rule, className, handlerMethod, handlerLine);
+            try {
+                cr.accept(adapter, 0);
+            } catch (Throwable th) {
+                System.out.println("Transformer : error compiling rule " + rule.getName() + " for class " + className + th);
+                th.printStackTrace(System.out);
+                return targetClassBytes;
             }
+            // hand back the transformed byte code
+            return cw.toByteArray();
+        }
 
-            public MethodVisitor visitMethod(final int access,
-                                             final String name,
-                                             final String desc,
-                                             final String signature,
-                                             final String[] exceptions) {
-                MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
-                if (name.equals(targetName) &&
-                        (targetSignature.length() == 0 || signature.equals(targetSignature))) {
-                    return new MethodAdapter(mv) {
-                        public void visitCode()
-                        {
-                            // generate try catch block for kill thread/JVM
-                            System.out.println("                                                 : // generate wrapper");
-                            System.out.println("                                                 : try { ");
-                            super.visitCode();
-                            System.out.println("                                                 : } catch (KillThreadException kte) { ");
-                            System.out.println("                                                 :   killThread();");
-                            System.out.println("                                                 : } catch (KillJVMException kje) { ");
-                            System.out.println("                                                 :   killJVM();");
-                            System.out.println("                                                 : }");
-                            // and the rest
-                        }
-                        public void visitLineNumber(final int line, final Label start)
-                        {
-                            if (line == targetLine) {
-                                // insert call to relevant event handler instance
-                                System.out.println("                                                 : // generate event notification");
-                                System.out.println("                                                 :    " + finalClassName + "." + ecaRuleFieldName + ".event(this, . . .);");
-                                System.out.println("                                                 : }");
-                            }
-                            super.visitLineNumber(line, start);
-                        }
-                    };
-                }
-                return mv;
-            }
-        };
-
-        cr.accept(adapter, 0);
         return targetClassBytes;
     }
 
     /**
-     * convert a classname from canonical form to the form used to represent it externally i.e. replace
-     * all dots with slashes
-     *
+     * test whether a class with a given name is located in the orchestration package
      * @param className
-     * @return
+     * @return true if a class is located in the orchestration package otherwise return false
      */
-    private String externalize(String className)
+    private boolean isOrchestrationClass(String className)
     {
-        return className.replaceAll("\\.", "/");
+        return className.startsWith("org.jboss.jbossts.orchestration.");
     }
 
     /**
-     * test whether a class witha given name is a potential candidate for insertion of event notifications
+     * test whether a class with a given name is a potential candidate for insertion of event notifications
      * @param className
      * @return true if a class is a potential candidate for insertion of event notifications otherwise return false
      */
     private boolean isTransformable(String className)
     {
-        return (className.startsWith("com/arjuna/"));
+        return (className.startsWith("com.arjuna."));
     }
-
     /**
-     * split off the method name preceding the signature and return it
-     * @param targetMethod - the unqualified method name, possibly including signature
-     * @return
-     */
-    private String parseMethodName(String targetMethod) {
-        int sigIdx = targetMethod.indexOf("(");
-        if (sigIdx > 0) {
-            return targetMethod.substring(0, sigIdx).trim();
-        } else {
-            return targetMethod;
-        }
-    }
-
-    /**
-     * split off the signature following the method name and return it
-     * @param targetMethod - the unqualified method name, possibly including signature
-     * @return
-     */
-    private String parseMethodSignature(String targetMethod) {
-        int sigIdx = targetMethod.indexOf("(");
-        if (sigIdx >= 0) {
-            return targetMethod.substring(sigIdx, targetMethod.length()).trim();
-        } else {
-            return "";
-        }
-    }
-
-    /**
-     * split off the signature following the method name and return it
-     * @param targetName the unqualified method name, not including signature
-     * @param targetSignature the method signature including brackets types and return type
-     * @return
-     */
-    private String generateFieldName(String targetName, String targetSignature) {
-        String result = targetName;
-        int startIdx = targetSignature.indexOf("(");
-        int endIdx = targetSignature.indexOf(")");
-        if (startIdx < 0) {
-            startIdx = 0;
-        }
-        if (endIdx < 0) {
-            endIdx = targetSignature.length() - 1;
-        }
-
-        String args = targetSignature.substring(startIdx, endIdx + 1);
-
-        // remove any brackets, commas, spaces, semi-colons, slashes and '[' characters
-        args = args.replaceAll("\\(", "\\$_\\$");
-        args = args.replaceAll("\\)", "\\$_\\$");
-        args = args.replaceAll(",", "\\$1\\$");
-        args = args.replaceAll(" ", "\\$2\\$");
-        args = args.replaceAll(";", "\\$3\\$");
-        args = args.replaceAll("\\/", "\\$4\\$");
-        args = args.replaceAll("\\[", "\\$5\\$");
-
-        return result + args;
-    }
-
-    private Class generateHandlerClass(EventHandler handler, ClassLoader loader, String targetClassName, Class targetClass)
-    {
-        // TODO -- write this but use Object for now
-        return Object.class;
-    }
-
-    /**
      * the instrumentation interface to the JVM
      */
     private final Instrumentation inst;

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Action.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Action.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Action.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -4,57 +4,92 @@
 import org.antlr.runtime.ANTLRStringStream;
 import org.antlr.runtime.CommonTokenStream;
 import org.antlr.runtime.RecognitionException;
-import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
 import org.jboss.jbossts.orchestration.rule.type.Type;
-import org.jboss.jbossts.orchestration.rule.binding.Bindings;
 import org.jboss.jbossts.orchestration.rule.expression.ExpressionHelper;
 import org.jboss.jbossts.orchestration.rule.expression.Expression;
 import org.jboss.jbossts.orchestration.rule.grammar.ECATokenLexer;
 import org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser;
+import org.jboss.jbossts.orchestration.rule.exception.ParseException;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 
 import java.util.List;
+import java.io.StringWriter;
 
 /**
- * Created by IntelliJ IDEA.
- * User: adinn
- * Date: 17-Jul-2008
- * Time: 15:20:18
- * To change this template use File | Settings | File Templates.
+ * class which represents a rule action comprising a void expression
  */
-public class Action
+public class Action extends RuleElement
 {
-    public static Action create(TypeGroup typeGroup, Bindings bindings, String text)
+    public static Action create(Rule rule, CommonTree actionTree)
+            throws TypeException
     {
+        Action action = new Action(rule, actionTree);
+        return action;
+    }
+
+    public static Action create(Rule rule, String text)
+            throws ParseException, TypeException
+    {
         if ("".equals(text)) {
-            return new Action(typeGroup, bindings);
+            return new Action(rule);
         }
         try {
             ECATokenLexer lexer = new ECATokenLexer(new ANTLRStringStream(text));
             CommonTokenStream tokenStream = new CommonTokenStream(lexer);
             ECAGrammarParser parser = new ECAGrammarParser(tokenStream);
-            ECAGrammarParser.action_return parse = parser.action();
+            ECAGrammarParser.eca_action_return parse = parser.eca_action();
             CommonTree actionTree = (CommonTree) parse.getTree();
-            Action action = new Action(typeGroup, bindings, actionTree);
+            Action action = new Action(rule, actionTree);
             return action;
         } catch (RecognitionException e) {
-            System.err.println("org.jboss.jbossts.orchestration.rule.event : error parsing action " + text);
-            return new Action(typeGroup, bindings);
+            throw new ParseException("org.jboss.jbossts.orchestration.rule.Action : error parsing action " + text);
         }
     }
-    protected Action(TypeGroup typeGroup, Bindings bindings, CommonTree actionTree)
+    protected Action(Rule rule, CommonTree actionTree) throws TypeException
     {
-        this.typeGroup = typeGroup;
-        this.bindings = bindings;
-        this.action = ExpressionHelper.createExpressionList(this.bindings, actionTree, Type.VOID);
+        super(rule);
+        this.action = ExpressionHelper.createExpressionList(this.getBindings(), actionTree, Type.VOID);
     }
 
-    protected Action(TypeGroup typeGroup, Bindings bindings)
+    protected Action(Rule rule)
     {
-        this.typeGroup = typeGroup;
-        this.bindings = bindings;
+        super(rule);
+        this.action = null;
     }
 
+    public void typeCheck() throws TypeException {
+        if (action != null) {
+            for (Expression expr : action) {
+                expr.typeCheck(getBindings(), getTypeGroup(), Type.VOID);
+            }
+        }
+    }
+
+    public void interpret(Rule.BasicHelper helper)
+            throws ExecuteException
+    {
+        if (action != null) {
+            for (Expression expr : action) {
+                expr.interpret(helper);
+            }
+        }
+    }
+
+    public void writeTo(StringWriter stringWriter)
+    {
+        if (action == null) {
+            stringWriter.write("DO   NOTHING");
+        } else {
+            String prefix = "DO   ";
+            for (Expression expr : action) {
+                stringWriter.write(prefix);
+                expr.writeTo(stringWriter);
+                prefix = ",\n     ";
+            }
+        }
+        stringWriter.write("\n");
+    }
+
     private List<Expression> action;
-    private TypeGroup typeGroup;
-    private Bindings bindings;
 }
\ No newline at end of file

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Condition.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Condition.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Condition.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -11,50 +11,78 @@
 import org.jboss.jbossts.orchestration.rule.expression.Expression;
 import org.jboss.jbossts.orchestration.rule.grammar.ECATokenLexer;
 import org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser;
+import org.jboss.jbossts.orchestration.rule.exception.ParseException;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 
+import java.io.StringWriter;
+
 /**
- * Created by IntelliJ IDEA.
- * User: adinn
- * Date: 17-Jul-2008
- * Time: 15:20:18
- * To change this template use File | Settings | File Templates.
+ * class which represents a rule condition comprising a boolean expression
  */
-public class Condition
+public class Condition extends RuleElement
 {
-    public static Condition create(TypeGroup typeGroup, Bindings bindings, String text)
+    public static Condition create(Rule rule, CommonTree conditionTree) throws TypeException
     {
+        Condition condition = new Condition(rule, conditionTree);
+        return condition;
+    }
+
+    public static Condition create(Rule rule, String text) throws ParseException, TypeException
+    {
         if ("".equals(text)) {
-            return new Condition(typeGroup, bindings);
+            return new Condition(rule);
         }
         try {
             ECATokenLexer lexer = new ECATokenLexer(new ANTLRStringStream(text));
             CommonTokenStream tokenStream = new CommonTokenStream(lexer);
             ECAGrammarParser parser = new ECAGrammarParser(tokenStream);
-            ECAGrammarParser.condition_return parse = parser.condition();
+            ECAGrammarParser.eca_condition_return parse = parser.eca_condition();
             CommonTree conditionTree = (CommonTree) parse.getTree();
-            Condition condition = new Condition(typeGroup,  bindings, conditionTree);
+            Condition condition = new Condition(rule, conditionTree);
             return condition;
         } catch (RecognitionException e) {
-            System.err.println("org.jboss.jbossts.orchestration.rule.event : error parsing condition " + text);
-            return new Condition(typeGroup, bindings);
+            throw new ParseException("org.jboss.jbossts.orchestration.rule.Condition : error parsing condition " + text, e);
         }
     }
 
-    protected Condition(TypeGroup typeGroup, Bindings bindings, CommonTree conditionTree)
+    protected Condition(Rule rule, CommonTree conditionTree)
+            throws TypeException
     {
-        this.typeGroup = typeGroup;
-        this.bindings = bindings;
-        this.condition = ExpressionHelper.createExpression(this.bindings, conditionTree, Type.BOOLEAN);
+        super(rule);
+        this.condition = ExpressionHelper.createExpression(rule.getBindings(), conditionTree, Type.BOOLEAN);
     }
 
-    protected Condition(TypeGroup typeGroup, Bindings bindings)
+    protected Condition(Rule rule)
     {
-        this.typeGroup = typeGroup;
-        this.bindings = bindings;
+        super(rule);
         this.condition = null;
     }
 
+    public void typeCheck() throws TypeException {
+        if (condition != null) {
+            condition.typeCheck(getBindings(), getTypeGroup(), Type.Z);
+        }
+    }
+
+    public boolean interpret(Rule.BasicHelper helper)
+            throws ExecuteException
+    {
+        Boolean result = (Boolean)condition.interpret(helper);
+
+        return result;
+    }
+
+    public void writeTo(StringWriter stringWriter)
+    {
+        if (condition == null) {
+            stringWriter.write("IF   TRUE");
+        } else {
+            stringWriter.write("IF   ");
+            condition.writeTo(stringWriter);
+        }
+        stringWriter.write("\n");
+    }
+
     private Expression condition;
-    private TypeGroup typeGroup;
-    private Bindings bindings;
 }

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Event.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Event.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Event.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,7 +1,6 @@
 package org.jboss.jbossts.orchestration.rule;
 
 import org.antlr.runtime.tree.CommonTree;
-import org.antlr.runtime.tree.Tree;
 import org.antlr.runtime.Token;
 import org.antlr.runtime.ANTLRStringStream;
 import org.antlr.runtime.CommonTokenStream;
@@ -15,43 +14,57 @@
 import org.jboss.jbossts.orchestration.rule.expression.ExpressionHelper;
 import org.jboss.jbossts.orchestration.rule.type.Type;
 import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
+import org.jboss.jbossts.orchestration.rule.exception.ParseException;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
 
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.io.StringWriter;
+
 /**
  * class which represents a rule event comprising of a set of abstract bindings of event variables to
  * evaluable expressions.
  */
-public class Event {
+public class Event extends RuleElement {
 
-    public static Event create(String text)
+    public static Event create(Rule rule, CommonTree eventTree)
+            throws TypeException
     {
+        Event event = new Event(rule, eventTree);
+        return event;
+    }
+
+    public static Event create(Rule rule, String text)
+            throws ParseException, TypeException
+    {
         if ("".equals(text)) {
-            return new Event();
+            return new Event(rule);
         }
 
         try {
             ECATokenLexer lexer = new ECATokenLexer(new ANTLRStringStream(text));
             CommonTokenStream tokenStream = new CommonTokenStream(lexer);
             ECAGrammarParser parser = new ECAGrammarParser(tokenStream);
-            ECAGrammarParser.event_return event_parse = parser.event();
+            ECAGrammarParser.eca_event_return event_parse = parser.eca_event();
             CommonTree eventTree = (CommonTree) event_parse.getTree();
-            Event event = new Event(eventTree);
+            Event event = new Event(rule, eventTree);
             return event;
         } catch (RecognitionException e) {
-            System.err.println("org.jboss.jbossts.orchestration.rule.event : error parsing event " + text);
-            return new Event();
+            throw new ParseException("org.jboss.jbossts.orchestration.rule.Event : error parsing event " + text, e);
         }
     }
 
-    protected Event(CommonTree eventTree)
+    protected Event(Rule rule, CommonTree eventTree) throws TypeException
     {
-        this.typeGroup = new TypeGroup();
+        super(rule);
         createBindings(eventTree);
     }
 
-    protected Event()
+    protected Event(Rule rule)
     {
-        this.typeGroup = new TypeGroup();
-        this.bindings = new Bindings();
+        super(rule);
     }
 
     public boolean compile()
@@ -61,92 +74,117 @@
 
     public Bindings getBindings()
     {
-        return bindings;
+        return rule.getBindings();
     }
 
-    private void createBindings(CommonTree eventTree)
+    public void typeCheck() throws TypeException {
+        Iterator<Binding> iterator = getBindings().iterator();
+        while (iterator.hasNext()) {
+            Binding binding = iterator.next();
+
+            typeCheck(binding);
+        }
+    }
+
+    private void typeCheck(Binding binding)
+            throws TypeException
     {
-        Bindings bindings = new Bindings();
-        boolean success = true;
+        binding.typeCheck(getBindings(), getTypeGroup());
+    }
 
+    private void createBindings(CommonTree eventTree) throws TypeException
+    {
+        Bindings bindings = getBindings();
+
+        // we bundle exceptions from each binding to report more than just the first error
+
+        List<TypeException> exceptions = new ArrayList<TypeException>();
+
         // we expect BINDINGS = (SEPR BINDING BINDINGS) | BINDING
         // where BINDING = (BIND BINDSYM EXPR)
 
         while (eventTree != null) {
-            Token token = eventTree.getToken();
-            int tokenType = token.getType();
-            switch (tokenType) {
-                case SEPR:
-                {
-                    success |= addBinding(bindings, (CommonTree)eventTree.getChild(0));
-                    eventTree = (CommonTree)eventTree.getChild(1);
+            try {
+                Token token = eventTree.getToken();
+                int tokenType = token.getType();
+                switch (tokenType) {
+                    case SEPR:
+                    {
+                        // update before we risk an exception
+                        CommonTree child0 = (CommonTree)eventTree.getChild(0);
+                        eventTree = (CommonTree)eventTree.getChild(1);
+                        addBinding(bindings, child0);
+                    }
+                    break;
+                    case ASSIGN:
+                    {
+                        // update before we risk an exception
+                        CommonTree saveTree = eventTree;
+                        eventTree = null;
+                        addBinding(bindings, saveTree);
+                    }
+                    break;
+                    default:
+                    {
+                        eventTree = null;
+                        String message = "Event.createBindings : unexpected token Type in binding list " + tokenType + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine();
+                        throw new TypeException(message);
+                    }
                 }
-                break;
-                case ASSIGN:
-                {
-                    success |= addBinding(bindings, eventTree);
-                    eventTree = null;
-                }
-                break;
-                default:
-                    System.err.println("Event.createBindings : unexpected token Type in binding list " + tokenType + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
-                    break;
+            } catch (TypeException te) {
+                exceptions.add(te);
             }
         }
 
-        // if we had any errors we leave bindings null to ensure that we don't try to
-        // compile this rule any further
-
-        if (success) {
-            this.bindings = bindings;
+        if (!exceptions.isEmpty()) {
+            if (exceptions.size() == 1) {
+                throw exceptions.get(0);
+            } else {
+                StringBuffer buffer = new StringBuffer();
+                buffer.append("Event.createBindings : invalid event bindings");
+                for (TypeException exception : exceptions) {
+                    buffer.append("\n\t");
+                    buffer.append(exception.getMessage());
+                }
+                throw new TypeException(buffer.toString());
+            }
         }
     }
 
-    private boolean addBinding(Bindings bindings, CommonTree bindingTree)
+    private void addBinding(Bindings bindings, CommonTree bindingTree) throws TypeException
     {
         Token token = bindingTree.getToken();
         int tokenType = token.getType();
 
         if (tokenType != ASSIGN) {
-            System.err.println("Event.createBindings : unexpected token Type in binding " + tokenType + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
-            return false;
+            String message = "Event.createBindings : unexpected token Type in binding " + tokenType + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine();
+            throw new TypeException(message);
         }
 
         CommonTree varTree = (CommonTree)bindingTree.getChild(0);
         CommonTree exprTree = (CommonTree)bindingTree.getChild(1);
+        Binding binding;
 
-        Binding binding = createBinding(varTree);
+        binding = createBinding(varTree);
 
         // don't allow current binding to be used when parsing the expression
         // but do use any type supplied for the binding
 
         Expression expr;
 
-        if (binding == null) {
-            // try expression anyway so we get as manyh errors as possible
-            expr = ExpressionHelper.createExpression(bindings, exprTree);
-        } else {
-            expr = ExpressionHelper.createExpression(bindings, exprTree, binding.getType());
-        }
+        expr = ExpressionHelper.createExpression(bindings, exprTree, binding.getType());
 
-        if (binding == null || expr == null) {
-            // errors wil have been notified already
-            return false;
-        }
-
         if (bindings.lookup(binding.getName()) != null) {
             // oops rebinding not allowed
-            System.err.println("Event.createBindings : rebinding disallowed for variable " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
-            return false;
+            String message = "Event.createBindings : rebinding disallowed for variable " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine();
+            throw new TypeException(message);
         }
 
         binding.setValue(expr);
         bindings.append(binding);
-
-        return true;
     }
 
-    public Binding createBinding(CommonTree varTree)
+    public Binding createBinding(CommonTree varTree) throws TypeException
     {
         Token token = varTree.getToken();
         int tokenType = token.getType();
@@ -162,36 +200,50 @@
                 CommonTree child0 = (CommonTree)varTree.getChild(0);
                 CommonTree child1 = (CommonTree)varTree.getChild(1);
                 if (child0.getToken().getType() != SYMBOL) {
-                    System.err.println("Event.createBindings : unexpected token Type in variable declaration" + child0.getToken().getType() + " for token " + child0.getToken().getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
-                    return null;
+                    throw new TypeException("Event.createBindings : unexpected token Type in variable declaration" + child0.getToken().getType() + " for token " + child0.getToken().getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
                 } else if (child1.getToken().getType() != SYMBOL) {
-                    System.err.println("Event.createBindings : unexpected token Type in variable type declaration" + child1.getToken().getType()  + " for token " + child1.getToken().getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
-                    return null;
+                    throw new TypeException("Event.createBindings : unexpected token Type in variable type declaration" + child1.getToken().getType()  + " for token " + child1.getToken().getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
                 }
                 String typeName = child1.getText();
-                Type type = typeGroup.lookup(typeName);
+                Type type = getTypeGroup().create(typeName);
                 if (type == null) {
-                    type = typeGroup.create(typeName);
-                    if (type == null) {
-                        System.err.println("Event.createBindings : incompatible type in declaration of variable " + child1.getToken().getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
-                        return null;
-                    }
+                    throw new TypeException("Event.createBindings : incompatible type in declaration of variable " + child1.getToken().getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
                 }
                 return new Binding(child0.getText(), type);
             }
             default:
             {
-                System.err.println("Event.createBindings : unexpected token Type in binding variable declaration" + tokenType + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
-                return null;
+                throw new TypeException("Event.createBindings : unexpected token Type in binding variable declaration" + tokenType + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
             }
         }
     }
 
-    public TypeGroup getTypeGroup()
+    public void interpret(Rule.BasicHelper helper)
+            throws ExecuteException
     {
-        return typeGroup;
+        Iterator<Binding> iterator = getBindings().iterator();
+
+        while (iterator.hasNext()) {
+            Binding binding = iterator.next();
+
+            if (binding.isVar()) {
+                Object value = binding.getValue().interpret(helper);
+                helper.bind(binding.getName(), value);
+            }
+        }
+
     }
 
-    private TypeGroup typeGroup;
-    private Bindings bindings;
+    public void writeTo(StringWriter stringWriter)
+    {
+        String prefix = "BIND ";
+        Iterator<Binding> iter = getBindings().iterator();
+        while (iter.hasNext()) {
+            Binding binding = iter.next();
+            stringWriter.write(prefix);
+            binding.writeTo(stringWriter);
+            prefix = ",\n     ";
+        }
+        stringWriter.write("\n");
+    }
 }

Added: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Rule.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Rule.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/Rule.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -0,0 +1,747 @@
+package org.jboss.jbossts.orchestration.rule;
+
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
+import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.exception.ParseException;
+import org.jboss.jbossts.orchestration.rule.exception.CompileException;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.binding.Binding;
+import org.jboss.jbossts.orchestration.rule.grammar.ECATokenLexer;
+import org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser;
+import org.jboss.jbossts.orchestration.synchronization.CountDown;
+import org.jboss.jbossts.orchestration.synchronization.Waiter;
+import org.antlr.runtime.ANTLRStringStream;
+import org.antlr.runtime.CommonTokenStream;
+import org.antlr.runtime.RecognitionException;
+import org.antlr.runtime.tree.CommonTree;
+import org.objectweb.asm.Opcodes;
+
+import java.io.StringWriter;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * A rule ties together an event, condition and action. It also maintains a TypeGroup
+ * identifying type information derived from these components.
+ */
+public class Rule
+{
+    private Event event;
+    private Condition condition;
+    private Action action;
+    Bindings bindings;
+    private TypeGroup typeGroup;
+    private String name;
+    private boolean checked;
+    private boolean checkFailed;
+    private String triggerClass;
+    private String triggerMethod;
+    private String triggerDescriptor;
+    private int triggerAccess;
+
+    private Rule(String name, ClassLoader loader)
+            throws ParseException, TypeException, CompileException
+    {
+        this.name = name;
+        typeGroup = new TypeGroup(loader);
+        bindings = new Bindings();
+        event = null;
+        condition = null;
+        action = null;
+        checked = false;
+        triggerClass = null;
+        triggerMethod = null;
+        triggerDescriptor = null;
+        triggerAccess = 0;
+    }
+
+    private Rule(String name, String eventSpec, String conditionSpec, String actionSpec, ClassLoader loader)
+            throws ParseException, TypeException, CompileException
+    {
+        this.name = name;
+        typeGroup = new TypeGroup(loader);
+        bindings = new Bindings();
+        event = null;
+        event = Event.create(this, eventSpec);
+        condition = Condition.create(this, conditionSpec);
+        action = Action.create(this, actionSpec);
+        checked = false;
+        triggerClass = null;
+        triggerMethod = null;
+        triggerDescriptor = null;
+        triggerAccess = 0;
+    }
+
+    private Rule(String name, String ruleSpec, ClassLoader loader)
+            throws ParseException, TypeException, CompileException
+    {
+        CommonTree ruleTree;
+
+        this.name = name;
+        typeGroup = new TypeGroup(loader);
+        bindings = new Bindings();
+        try {
+            ECATokenLexer lexer = new ECATokenLexer(new ANTLRStringStream(ruleSpec));
+            CommonTokenStream tokenStream = new CommonTokenStream(lexer);
+            ECAGrammarParser parser = new ECAGrammarParser(tokenStream);
+            ECAGrammarParser.eca_rule_return rule_parse = parser.eca_rule();
+            ruleTree = (CommonTree) rule_parse.getTree();
+        } catch (RecognitionException e) {
+            throw new ParseException("org.jboss.jbossts.orchestration.rule.Rule : error parsing rule " + ruleSpec, e);
+        }
+        CommonTree eventTree = (CommonTree)ruleTree.getChild(0);
+        CommonTree conditionTree = (CommonTree)ruleTree.getChild(1);
+        CommonTree actionTree = (CommonTree)ruleTree.getChild(2);
+        event = Event.create(this, eventTree);
+        condition = Condition.create(this, conditionTree);
+        action = Action.create(this, actionTree);
+        checked = false;
+        triggerClass = null;
+        triggerMethod = null;
+        triggerDescriptor = null;
+        triggerAccess = 0;
+    }
+
+    public TypeGroup getTypeGroup()
+    {
+        return typeGroup;
+    }
+
+    public Bindings getBindings()
+    {
+        return bindings;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Event getEvent()
+    {
+        return event;
+    }
+
+    public Condition getCondition() {
+        return condition;
+    }
+
+    public Action getAction() {
+        return action;
+    }
+
+    public static Rule create(String name, String eventSpec, String conditionSpec, String actionSpec, ClassLoader loader)
+            throws ParseException, TypeException, CompileException
+    {
+            return new Rule(name, eventSpec, conditionSpec, actionSpec, loader);
+    }
+
+    public static Rule create(String name, String ruleSpec, ClassLoader loader)
+            throws ParseException, TypeException, CompileException
+    {
+            return new Rule(name, ruleSpec, loader);
+    }
+
+    public static Rule create(String name, ClassLoader loader)
+            throws ParseException, TypeException, CompileException
+    {
+            return new Rule(name, loader);
+    }
+
+    public void setEvent(String eventSpec) throws ParseException, TypeException
+    {
+        if (event == null) {
+            event = Event.create(this, eventSpec);
+        }
+    }
+
+    public void setCondition(String conditionSpec) throws ParseException, TypeException
+    {
+        if (event != null & condition == null) {
+            condition = Condition.create(this, conditionSpec);
+        }
+    }
+
+    public void setAction(String actionSpec) throws ParseException, TypeException
+    {
+        if (event != null & condition != null && action == null) {
+            action = Action.create(this, actionSpec);
+        }
+    }
+
+    public void setTypeInfo(final String className, final int access, final String methodName, final String desc)
+    {
+        triggerClass = className;
+        triggerAccess = access;
+        triggerMethod = methodName;
+        triggerDescriptor = desc;
+    }
+
+    private synchronized boolean ensureTypeCheckedCompiled()
+    {
+        if (checkFailed) {
+            return false;
+        }
+
+        if (!checked) {
+            try {
+                typeCheck();
+                helperClass = BasicHelper.class;
+            } catch (TypeException te) {
+                System.out.println("Rule.typecheck : error typechecking rule " + getName());
+                te.printStackTrace(System.out);
+                checkFailed = true;
+                return false;
+            }
+        }
+        
+        return true;
+    }
+
+
+    public void typeCheck()
+            throws TypeException
+    {
+        // try to resolve all types in the type group to classes
+
+        typeGroup.resolveTypes();
+
+        // use the descriptor to derive the method argument types and install bindings for them
+        // in the bindings list
+
+        installParameters((triggerAccess & Opcodes.ACC_STATIC) != 0, triggerClass, triggerDescriptor);
+        event.typeCheck();
+        condition.typeCheck();
+        action.typeCheck();
+        checked = true;
+    }
+
+    private void installParameters(boolean isStatic, String className, String descriptor)
+            throws TypeException
+    {
+        List<Binding> parameterBindings = new ArrayList<Binding>();
+        Type type;
+        // add a binding for the rule so we can call builting static methods
+        type = typeGroup.create("org.jboss.jbossts.orchestration.rule.Rule$Helper");
+        Binding ruleBinding = new Binding("-1", type);
+        parameterBindings.add(ruleBinding);
+
+        if (!isStatic) {
+            type = typeGroup.create(className);
+            if (type.isUndefined()) {
+                throw new TypeException("Rule.installParameters : Rule " + name + " unable to load class " + className);
+            }
+            Binding binding =  new Binding("0", type);
+            parameterBindings.add(binding);
+        }
+        List<String> parameterTypenames = Type.parseDescriptor(descriptor, false);
+        int paramIdx = 1;
+        if (parameterTypenames != null) {
+            for (String typeName : parameterTypenames) {
+                String[] typeAndArrayBounds = typeName.split("\\[");
+                Type baseType = typeGroup.create(typeAndArrayBounds[0]);
+                Type paramType = baseType;
+                if (baseType.isUndefined()) {
+                    throw new TypeException("Rule.installParameters : Rule " + name + " unable to load class " + baseType);
+                }
+                for (int i = 1; i < typeAndArrayBounds.length ; i++) {
+                    paramType = typeGroup.createArray(baseType);
+                }
+                Binding binding = new Binding(Integer.toString(paramIdx++), paramType);
+                parameterBindings.add(binding);
+            }
+        }
+
+        bindings.addBindings(parameterBindings);
+    }
+
+    /**
+     * forward an execute request a rule identified by its unique key
+     * @param key a string key identifying the rule instance to be fired
+     * @param recipient the recipient of the method from which execution of the rule was
+     * triggered or null if it was a static method
+     * @param args the arguments of the method from which execution of the rule was
+     * triggered
+     */
+    public static void execute(String key, Object recipient, Object[] args) throws ExecuteException
+    {
+        Rule rule = ruleKeyMap.get(key);
+        System.out.println("Rule.execute called for " + key);
+
+        if (rule == null) {
+            throw new ExecuteException("Rule.execute : unable to find rule with key " + key);
+        }
+
+        rule.execute(recipient, args);
+    }
+
+    /**
+     * forward an execute request to a helper instance associated with the rule
+     * @param recipient the recipient of the method from which execution of this rule was
+     * triggered or null if it was a static method
+     * @param args the arguments of the method from which execution of this rule was
+     * triggered
+     */
+
+    private void execute(Object recipient, Object[] args) throws ExecuteException
+    {
+        // type check and compile the rule now if it has not already been done
+
+        if (ensureTypeCheckedCompiled()) {
+
+            // create a helper and get it to execute the rule
+            // eventually we will create a subclass of helper for each rule and compile
+            // an implementation of execute from the rule source. for now we create a generic
+            // helper and call the generic execute method which interprets the rule
+            BasicHelper helper;
+            try {
+                helper = (BasicHelper)helperClass.newInstance();
+                helper.setRule(this);
+            } catch (InstantiationException e) {
+                // should not happen
+                System.out.println("cannot create instance of " + helperClass.getCanonicalName());
+                e.printStackTrace(System.out);
+                return;
+            } catch (IllegalAccessException e) {
+                // should not happen
+                System.out.println("cannot access " + helperClass.getCanonicalName());
+                e.printStackTrace(System.out);
+                return;
+            } catch (ClassCastException e) {
+                // should not happen
+                System.out.println("cast exception " + helperClass.getCanonicalName());
+                e.printStackTrace(System.out);
+                return;
+            }
+            helper.execute(bindings, recipient, args);
+        }
+    }
+
+    /**
+     * called when a trigger is compiled for the rule to provide a String key which can be used
+     * at execution time to obtain a handle on the rule instance
+     *
+     * @return a key which can be used later to obtain a reference to the rule
+     */
+
+    public String getKey()
+    {
+        String key = getName() + "_" + nextId();
+        ruleKeyMap.put(key, this);
+        return key;
+    }
+
+    /**
+     * a hash map used to identify rules from their keys
+     */
+    private static HashMap<String, Rule> ruleKeyMap = new HashMap<String, Rule>();
+
+    /**
+     * a counter used to ensure rule identifiers are unique
+     */
+    private static int nextId = 0;
+
+    /**
+     * a method to return the next available counter for use in constructing a key for the rule
+     * @return
+     */
+    private synchronized static int nextId()
+    {
+        return nextId++;
+    }
+
+    /**
+     * generate a string representation of the rule
+     *
+     * @return a string representation of the rule
+     */
+
+    public String toString()
+    {
+        StringWriter stringWriter = new StringWriter();
+        stringWriter.write("RULE ");
+        stringWriter.write(name);
+        stringWriter.write("\n");
+        if (event != null) {
+            event.writeTo(stringWriter);
+        } else {
+            stringWriter.write("BIND NONE\n");
+        }
+        if (condition != null) {
+            condition.writeTo(stringWriter);
+        } else {
+            stringWriter.write("IF   TRUE\n");
+        }
+        if (action != null) {
+            action.writeTo(stringWriter);
+        } else {
+            stringWriter.write("DO   NOTHING\n");
+        }
+        
+        return stringWriter.toString();
+    }
+
+    /**
+     * a helper class instantiated when the rule is triggered which provides an
+     * implementation of execute for the rule and enables the execution context
+     * to be accessed from the executed code
+     */
+
+    private Class helperClass;
+
+    /**
+     * Methods provided on this class are automatically made available as builtin operations in
+     * expressions appearing in rule event bindings, conditions and actions. Although Helper
+     * methods are all instance methods the message recipient for the method call is implicit
+     * and does not appear in the builtin call. It does, however, appear in the runtime
+     * invocation, giving the builtin operation access to the rule being fired (the Helper
+     * instance has implicit access to the enclosing rule object since class Helper is
+     * not static).
+     */
+    public static class Helper
+    {
+        protected Rule rule;
+
+        protected Helper(Rule rule)
+        {
+            this.rule = rule;
+        }
+        // tracing support
+        /**
+         * builtin to print a message during rule execution. n.b. this always returns true which
+         * means it can be invoked during condition execution
+         * @param text the message to be printed as trace output
+         * @return true
+         */
+        public boolean debug(String text)
+        {
+            System.out.println("rule " + rule.getName() + " : " + text);
+            return true;
+        }
+
+        // countdown support
+        /**
+         * builtin to test test if a countdown has been installed
+         * @param identifier an object which uniquely identifies the countdown in question
+         * @return true if the countdown is currently installed
+         */
+        public boolean getCountDown(Object identifier)
+        {
+            synchronized (countDownMap) {
+                return (countDownMap.get(identifier) != null);
+            }
+        }
+
+        /**
+         * builtin to test add a countdown identified by a specific object and with the specified
+         * count. n.b. this builtin checks if a countdown identified by the supplied object is
+         * currently installed, returning false if so, otherwise atomically adds the countdown
+         * and returns true. This allows the builtin to be used safely in conditions where concurrent
+         * rule firings (including firings of multiple rules) might otherwise lead to a race condition.
+         * @param identifier an object which uniquely identifies the countdown in question
+         * @param count the number of times the countdown needs to be counted down before the
+         * countdown operation returns true. e.g. if count is supplied as 2 then the first two
+         * calls to @link{#countdown(Object)} will return false and the third call will return true.
+         * @return true if a new countdown is installed, false if one already exists.
+         */
+        public boolean addCountDown(Object identifier, int count)
+        {
+            synchronized (countDownMap) {
+                if (countDownMap.get(identifier) == null) {
+                    countDownMap.put(identifier, new CountDown(count));
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        /**
+         * builtin to decrement the countdown identified by a specific object, uninstalling it and
+         * returning true only when the count is zero.
+         * @param identifier an object which uniquely identifies the countdown in question
+         * @return true if the countdown is installed and its count is zero, otherwise false
+         */
+        public boolean countDown(Object identifier)
+        {
+            synchronized (countDownMap) {
+                CountDown countDown = countDownMap.get(identifier);
+
+                if (countDown != null) {
+                    boolean result = countDown.decrement();
+                    if (result) {
+                        countDownMap.remove(identifier);
+                    }
+                    return result;
+                }
+            }
+
+            // we must only fire a decrement event once for a given counter
+
+            return false;
+        }
+
+        // wait/notify support
+        /**
+         * wait for another thread to signal an event with no timeout. see
+         * @link{#waitFor(Object, long)} for details and caveats regarding calling this builtin.
+         * @param object an object used to identify the signal that is to be waited on.
+         */
+        public void waitFor(Object object)
+        {
+            waitFor(object, 0);
+        }
+
+        /**
+         * wait for another thread to signal an event with a specific timeout or no timeout if zero
+         * is supplied as the second argument. this may be called in a rule event, condition or action.
+         * it will suspend the current thread pending signalling of the event at which point rule
+         * processing will either continue or abort depending upon the type of signal. if an exception
+         * is thrown it will be an instance of runtime exception which, in normal circumstances, will
+         * cause the thread to exit. The exception may not kill the thread f the trigger method or
+         * calling code contains a catch-all handler so care must be used to ensure that an abort of
+         * waiting threads has the desired effect. n.b. care must also be employed if the current
+         * thread is inside a synchronized block since there is a potential for the waitFor call to
+         * cause deadlock.
+         * @param object an object used to identify the signal that is to be waited on. n.b. the
+         * wait operation is not performed using synchronization on the supplied object as the rule
+         * system cannot safely release and reobtain locks on application data. this argument is used
+         * as a key to identify a synchronization object private to the rule system.
+         */
+        public void waitFor(Object object, long millisecs)
+        {
+            Waiter waiter = getWaiter(object, true);
+
+            waiter.waitFor(millisecs);
+        }
+
+        /**
+         * signal an event identified by the suppied object, causing all waiting threads to resume
+         * rule processing and clearing the event. if there are no threads waiting either because
+         * there has been no call to @link{#waitFor} or because some other thread has sent the signal
+         * then this call returns false, otherwise it returns true. This operation is atomic,
+         * allowing the builtin to be used in rule conditions.
+         * @param object an object used to identify the which waiting threads the signal should
+         * be delivered to. n.b. the operation is not performed using a notify on the supplied object.
+         * this argument is used as a key to identify a synchronization object private to the rule
+         * system.
+         */
+        public boolean signal(Object object)
+        {
+            Waiter waiter = removeWaiter(object);
+
+            if (waiter != null) {
+                return waiter.signal();
+            }
+            
+            return false;
+        }
+
+        /**
+         * signal an event identified by the suppied object, causing all waiting threads to throw an
+         * exception and clearing the event. if there are no objects waiting, either because there has been
+         * no call to @link{#waitFor} or because some other thread has already sent the signal, then this
+         * call returns false, otherwise it returns true. This operation is atomic, allowing the builtin
+         * to be used safely in rule conditions.
+         * @param object an object used to identify the which waiting threads the signal should
+         * be delivered to. n.b. the operation is not performed using a notify on the supplied object.
+         * this argument is used as a key to identify a synchronization object private to the rule
+         * system.
+         */
+        public boolean signalKill(Object object)
+        {
+            Waiter waiter = removeWaiter(object);
+
+            if (waiter != null) {
+                return waiter.signalKill();
+            }
+
+            return false;
+        }
+
+        /**
+         * cause the current thread to throw a runtime exception which will normally cause it to exit.
+         * The exception may not kill the thread if the trigger method or calling code contains a
+         * catch-all handler so care must be employed to ensure that a call to this builtin has the
+         * desired effect.
+         */
+        public void killThread()
+        {
+            throw new ExecuteException("rule " + rule.getName() + " : killing thread " + Thread.currentThread().getName());
+        }
+
+        /**
+         * cause the current JVM to halt immediately, simulating a crash as near as possible. exit code -1
+         * is returned
+         */
+
+        public void killJVM()
+        {
+            java.lang.Runtime.getRuntime().halt(-1);
+        }
+
+        /**
+         * return a unique name for the trigger point associated with this rule. n.b. a single rule may
+         * give rise to more than one trigger point if the rule applies to several methods with the same
+         * name or to several classes with the same (package unqualified) name, or even to several
+         * versions of the same compiled class loaded into distinct class loaders.
+         *
+         * @return a unique name for the trigger point from which this rule was invoked
+         */
+        public String toString()
+        {
+            return rule.getName();
+        }
+
+        /**
+         * lookup the waiter object used to target wait and signal requests associated with a
+         * specific identifying object
+         * @param object the identifer for the waiter
+         * @param createIfAbsent true if the waiter should be (atomically) inserted if it is not present
+         * @return the waiter if it was found or inserted or null if it was not found and createIfAbsent was false
+         */
+        private Waiter getWaiter(Object object, boolean createIfAbsent)
+        {
+            Waiter waiter;
+
+            synchronized(waitMap) {
+                waiter = waitMap.get(object);
+                if (waiter == null && createIfAbsent) {
+                    waiter = new Waiter(object);
+                    waitMap.put(object, waiter);
+                }
+            }
+
+            return waiter;
+        }
+
+        /**
+         * remove the waiter object used to target wait and signal requests associated with a
+         * specific identifying object
+         * @param object the identifer for the waiter
+         * @return the waiter if it was found or inserted or null if it was not found and createIfAbsent was false
+         */
+        private Waiter removeWaiter(Object object)
+        {
+            return waitMap.remove(object);
+        }
+    }
+
+    /**
+     * implementation of helper class which executes a compiled rule. it
+     * can be overridden by a generated subclass in order to enable compiled
+     * rule execution. this version inerprets the rule
+     */
+    public static class BasicHelper extends Helper
+    {
+        protected HashMap<String, Object> bindingMap;
+        private HashMap<String, Type> bindingTypeMap;
+
+        public BasicHelper()
+        {
+            super(null);
+            bindingMap = new HashMap<String, Object>();
+            bindingTypeMap = new HashMap<String, Type>();
+        }
+
+        public void setRule(Rule rule)
+        {
+            this.rule = rule;
+        }
+
+        /**
+         * install values into the bindings map and then call the execute0 method
+         * to actually execute the rule
+         * @param bindings
+         * @param recipient
+         * @param args
+         */
+        public void execute(Bindings bindings, Object recipient, Object[] args)
+                throws ExecuteException
+        {
+            System.out.println(rule.getName() + " execute");
+            Iterator<Binding> iterator = bindings.iterator();
+            while (iterator.hasNext()) {
+                Binding binding = iterator.next();
+                String name = binding.getName();
+                Type type = binding.getType();
+                if (binding.isHelper()) {
+                    bindingMap.put(name, this);
+                } else if (binding.isRecipient()) {
+                    bindingMap.put(name, recipient);
+                } else if (binding.isParam()) {
+                    bindingMap.put(name, args[binding.getIndex() - 1]);
+                }
+                bindingTypeMap.put(name, type);
+            }
+
+            // now do the actual execution
+
+            execute0();
+        }
+
+        /**
+         * basic implementation of rule execution
+         *
+         * @throws ExecuteException
+         */
+        
+        protected void execute0()
+                throws ExecuteException
+        {
+            System.out.println(rule.getName() + " execute0");
+            executeEvent();
+            if (executeCondition()) {
+                executeAction();
+            }
+        }
+
+        public void bind(String name, Object value)
+        {
+            bindingMap.put(name, value);
+        }
+
+        public Object getBinding(String name)
+        {
+            return bindingMap.get(name);
+        }
+
+        private void executeEvent()
+                throws ExecuteException
+        {
+            System.out.println(rule.getName() + " executeEvent");
+            rule.getEvent().interpret(this);
+        }
+
+        private boolean executeCondition()
+                throws ExecuteException
+        {
+            System.out.println(rule.getName() + " executeCondition");
+            return rule.getCondition().interpret(this);
+        }
+        
+        private void executeAction()
+                throws ExecuteException
+        {
+            System.out.println(rule.getName() + " executeAction");
+            rule.getAction().interpret(this);
+        }
+
+        public String getName() {
+            return rule.getName();
+        }
+    }
+    
+    /**
+     * a hash map used to identify countdowns from their identifying objects
+     */
+    private static HashMap<Object, CountDown> countDownMap = new HashMap<Object, CountDown>();
+
+    /**
+     * a hash map used to identify waiters from their identifying objects
+     */
+    private static HashMap<Object, Waiter> waitMap = new HashMap<Object, Waiter>();
+}

Added: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/RuleElement.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/RuleElement.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/RuleElement.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -0,0 +1,41 @@
+package org.jboss.jbossts.orchestration.rule;
+
+import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+
+import java.io.StringWriter;
+
+/**
+ * generic class implemented by rule events, conditions and actions which gives them
+ * access to the rule context and provides them with common behaviours
+ */
+public abstract class RuleElement {
+    protected RuleElement(Rule rule)
+    {
+        this.rule = rule;
+    }
+
+    protected Rule rule;
+
+    protected TypeGroup getTypeGroup()
+    {
+        return rule.getTypeGroup();
+    }
+
+    protected Bindings getBindings()
+    {
+        return rule.getBindings();
+    }
+
+    public abstract void typeCheck() throws TypeException;
+
+    public String toString()
+    {
+        StringWriter stringWriter = new StringWriter();
+        writeTo(stringWriter);
+        return stringWriter.toString();
+    }
+
+    public abstract void writeTo(StringWriter stringWriter);
+}

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/binding/Binding.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/binding/Binding.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/binding/Binding.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,8 +1,12 @@
 package org.jboss.jbossts.orchestration.rule.binding;
 
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
 import org.jboss.jbossts.orchestration.rule.expression.Expression;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 
+import java.io.StringWriter;
+
 /**
  * Class used to store a binding of a named variable to a value of some given type
  */
@@ -24,28 +28,30 @@
         this.name = name;
         this.type = type;
         this.value = value;
+        if (name.equals("-1")) {
+            index = -1;
+        } else if (name.matches("[0-9].*")) {
+            index = Integer.valueOf(name);
+        } else {
+            index = -2;
+        }
     }
 
-    public boolean typeCheck(ClassLoader loader, Type type)
+    public void typeCheck(Bindings bindings, TypeGroup typeGroup)
+            throws TypeException
     {
-        if (this.type.isUndefined()) {
-            // accept the candidate type
+        // value can be null if this is a rule method parameter
+        if (value != null) {
+            // type check the binding expression, using the bound variable's type if it is known
+
+            Type valueType = value.typeCheck(bindings, typeGroup, type);
+
             if (type.isUndefined()) {
-                // !!should never happen
-                System.err.println("Binding.typecheck : shouldn't happen! variable " + getName() + " has undefined tyep and undefined derived type " + type.getName());
-                return false;
-            } else {
-                this.type = type;
-                return true;
+                type = valueType;
             }
-        } else {
-            // TODO check is this the right way round???
-            if (type.isAssignableFrom(this.type)) {
-                return true;
-            } else {
-                System.err.println("Binding.typecheck : current type " + this.type.getName() + " for variable " + getName() + " is incompatible with derived type " + type.getName());
-                return false;
-            }
+        } else if (type.isUndefined()) {
+            // can we have no type for a method parameter?
+            throw new TypeException("Binding.typecheck unknown type for binding " + name);
         }
     }
 
@@ -54,7 +60,7 @@
         return name;
     }
 
-    public Object getValue()
+    public Expression getValue()
     {
         return value;
     }
@@ -72,7 +78,56 @@
         return type;
     }
 
+    public boolean isHelper()
+    {
+        return index == -1;
+    }
+
+    public boolean isRecipient()
+    {
+        return index == 0;
+    }
+
+    public boolean isParam()
+    {
+        return index > 0;
+    }
+
+    public boolean isVar()
+    {
+        return index < -1;
+    }
+
+    public int getIndex()
+    {
+        return index;
+    }
+
+    public void writeTo(StringWriter stringWriter)
+    {
+        if (isHelper()) {
+            stringWriter.write("$$");
+        } else if (isParam()) {
+            stringWriter.write("$" + name);
+            if (type != null && (type.isDefined() || type.isObject())) {
+                stringWriter.write(" : ");
+                stringWriter.write(type.getName());
+            }
+        } else {
+            stringWriter.write(name);
+            if (type != null && (type.isDefined() || type.isObject())) {
+                stringWriter.write(" : ");
+                stringWriter.write(type.getName());
+            }
+        }
+        if (value != null) {
+            stringWriter.write(" = ");
+            value.writeTo(stringWriter);
+        }
+    }
+
     private String name;
     private Type type;
     private Expression value;
+    private int index;
 }

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/binding/Bindings.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/binding/Bindings.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/binding/Bindings.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -30,23 +30,30 @@
     }
 
     /**
-     * add a binding to the end of the list
+     * add the method parameter bindings to the front of the list
      *
-     * n.b. the caller msut ensure that no binding with the same name is already present in
-     * the list
+     * n.b. the caller must ensure that the bindings are only for the rule's
+     * positional parameters and have names constructed from successive non-negative integers
+     * @param bindings
+     */
+    public void addBindings(List<Binding> bindings)
+    {
+        this.bindings.addAll(0, bindings);
+    }
+
+    /**
+     * append a binding to the end of the currrent bindings list
      * @param binding
      * @return
      */
-    public Binding append(Binding binding)
+    public void append(Binding binding)
     {
-        Binding oldBinding = lookup(binding.getName());
-        if (oldBinding!= null) {
-            bindings.remove(binding);
-        }
-
         bindings.add(binding);
+    }
 
-        return oldBinding;
+    public Iterator<Binding> iterator()
+    {
+        return bindings.iterator();
     }
 
     /**

Added: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/exception/CompileException.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/exception/CompileException.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/exception/CompileException.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -0,0 +1,17 @@
+package org.jboss.jbossts.orchestration.rule.exception;
+
+/**
+ * used to notify an exception during rule compilation
+ */
+public class CompileException extends Exception
+{
+    public CompileException(String message)
+    {
+        super(message);
+    }
+
+    public CompileException(String message, Throwable th)
+    {
+        super(message, th);
+    }
+}
\ No newline at end of file

Added: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/exception/ExecuteException.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/exception/ExecuteException.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/exception/ExecuteException.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -0,0 +1,18 @@
+package org.jboss.jbossts.orchestration.rule.exception;
+
+/**
+ * exception class thrown during rule execution. n.b. this extends RuntimeException to avoid
+ * issues with method checking
+ */
+public class ExecuteException extends RuntimeException
+{
+    public ExecuteException(String message)
+    {
+        super(message);
+    }
+
+    public ExecuteException(String message, Throwable th)
+    {
+        super(message, th);
+    }
+}

Added: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/exception/ParseException.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/exception/ParseException.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/exception/ParseException.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -0,0 +1,17 @@
+package org.jboss.jbossts.orchestration.rule.exception;
+
+/**
+ * used to notify an exception during rule parsing
+ */
+public class ParseException extends Exception
+{
+    public ParseException(String message)
+    {
+        super(message);
+    }
+
+    public ParseException(String message, Throwable th)
+    {
+        super(message, th);
+    }
+}

Added: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/exception/TypeException.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/exception/TypeException.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/exception/TypeException.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -0,0 +1,17 @@
+package org.jboss.jbossts.orchestration.rule.exception;
+
+/**
+ * used to notify an exception during rule type checking
+ */
+public class TypeException extends Exception
+{
+    public TypeException(String message)
+    {
+        super(message);
+    }
+
+    public TypeException(String message, Throwable th)
+    {
+        super(message, th);
+    }
+}
\ No newline at end of file

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ArithmeticExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ArithmeticExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ArithmeticExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,6 +1,12 @@
 package org.jboss.jbossts.orchestration.rule.expression;
 
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
+import org.jboss.jbossts.orchestration.rule.type.TypeHelper;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.Rule;
 import org.antlr.runtime.Token;
 
 /**
@@ -12,4 +18,218 @@
     {
         super(oper, Type.promote(left.getType(), right.getType()), token, left, right);
     }
+
+    public Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected) throws TypeException {
+        Type type1 = getOperand(0).typeCheck(bindings, typegroup, Type.N);
+        Type type2 = getOperand(1).typeCheck(bindings, typegroup, Type.N);
+        type = Type.promote(type1, type2);
+        if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type) ) {
+            throw new TypeException("ArithmenticExpression.typeCheck : invalid expected result type " + expected.getName() + getPos());
+        }
+
+        return type;
+    }
+
+    public Object interpret(Rule.BasicHelper helper)
+            throws ExecuteException
+    {
+        try {
+// n.b. be careful with characters here
+            Number value1 = (Number)getOperand(0).interpret(helper);
+            Number value2 = (Number)getOperand(1).interpret(helper);
+            // type is the result of promoting one or other or both of the operands
+            // and they should be converted to this type before doing the arithmetic operation
+            if (type == type.B) {
+                byte b1 = value1.byteValue();
+                byte b2 = value2.byteValue();
+                byte result;
+                // TODO we should probably only respect the byte, short and char types for + and -
+                // TODO also need to decide how to handle divide by zero
+
+                switch (oper)
+                {
+                    case MUL:
+                        result = (byte)(b1 * b2);
+                        break;
+                    case DIV:
+                        result = (byte)(b1 / b2);
+                        break;
+                    case PLUS:
+                        result = (byte)(b1 + b2);
+                        break;
+                    case MINUS:
+                        result = (byte)(b1 - b2);
+                        break;
+                    case MOD:
+                        result = (byte)(b1 % b2);
+                        break;
+                    default:
+                        result = 0;
+                        break;
+                }
+                return new Byte(result);
+            } else if (type == type.S) {
+                short s1 = value1.shortValue();
+                short s2 = value2.shortValue();
+                short result;
+                switch (oper)
+                {
+                    case MUL:
+                        result = (short)(s1 * s2);
+                        break;
+                    case DIV:
+                        result = (short)(s1 / s2);
+                        break;
+                    case PLUS:
+                        result = (short)(s1 + s2);
+                        break;
+                    case MINUS:
+                        result = (short)(s1 - s2);
+                        break;
+                    case MOD:
+                        result = (short)(s1 % s2);
+                        break;
+                    default:
+                        result = 0;
+                        break;
+                }
+                return new Short(result);
+            }  else if (type == type.I) {
+                int i1 = value1.intValue();
+                int i2 = value2.intValue();
+                int result;
+                switch (oper)
+                {
+                    case MUL:
+                        result = (i1 * i2);
+                        break;
+                    case DIV:
+                        result = (i1 / i2);
+                        break;
+                    case PLUS:
+                        result = (i1 + i2);
+                        break;
+                    case MINUS:
+                        result = (i1 - i2);
+                        break;
+                    case MOD:
+                        result = (i1 % i2);
+                        break;
+                    default:
+                        result = 0;
+                        break;
+                }
+                return new Integer(result);
+            }  else if (type == type.J) {
+                long l1 = value1.longValue();
+                long l2 = value2.longValue();
+                long result;
+                switch (oper)
+                {
+                    case MUL:
+                        result = (l1 * l2);
+                        break;
+                    case DIV:
+                        result = (l1 / l2);
+                        break;
+                    case PLUS:
+                        result = (l1 + l2);
+                        break;
+                    case MINUS:
+                        result = (l1 - l2);
+                        break;
+                    case MOD:
+                        result = (l1 % l2);
+                        break;
+                    default:
+                        result = 0;
+                        break;
+                }
+                return new Long(result);
+            }  else if (type == type.F) {
+                float f1 = value1.floatValue();
+                float f2 = value2.floatValue();
+                float result;
+                switch (oper)
+                {
+                    case MUL:
+                        result = (f1 * f2);
+                        break;
+                    case DIV:
+                        result = (f1 / f2);
+                        break;
+                    case PLUS:
+                        result = (f1 + f2);
+                        break;
+                    case MINUS:
+                        result = (f1 - f2);
+                        break;
+                    case MOD:
+                        result = (f1 % f2);
+                        break;
+                    default:
+                        result = 0;
+                        break;
+                }
+                return new Float(result);
+            }  else if (type == type.D) {
+                double d1 = value1.doubleValue();
+                double d2 = value2.doubleValue();
+                double result;
+                switch (oper)
+                {
+                    case MUL:
+                        result = (d1 * d2);
+                        break;
+                    case DIV:
+                        result = (d1 / d2);
+                        break;
+                    case PLUS:
+                        result = (d1 + d2);
+                        break;
+                    case MINUS:
+                        result = (d1 - d2);
+                        break;
+                    case MOD:
+                        result = (d1 % d2);
+                        break;
+                    default:
+                        result = 0;
+                        break;
+                }
+                return new Double(result);
+            }  else { // (type == type.C)
+                // use integers here but be careful about conversions
+                int s1 = value1.intValue();
+                int s2 = value2.intValue();
+                char result;
+                switch (oper)
+                {
+                    case MUL:
+                        result = (char)(s1 * s2);
+                        break;
+                    case DIV:
+                        result = (char)(s1 / s2);
+                        break;
+                    case PLUS:
+                        result = (char)(s1 + s2);
+                        break;
+                    case MINUS:
+                        result = (char)(s1 - s2);
+                        break;
+                    case MOD:
+                        result = (char)(s1 % s2);
+                        break;
+                    default:
+                        result = 0;
+                        break;
+                }
+                return new Integer(result);
+            }
+        } catch (ExecuteException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ExecuteException("ArithmeticExpression.interpret : unexpected exception for operation " + token + getPos() + " in rule " + helper.getName(), e);
+        }
+    }
 }

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ArrayExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ArrayExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ArrayExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,12 +1,18 @@
 package org.jboss.jbossts.orchestration.rule.expression;
 
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
 import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
 import org.antlr.runtime.Token;
 import org.antlr.runtime.tree.CommonTree;
 
 import java.util.List;
 import java.util.Iterator;
+import java.io.StringWriter;
+import java.lang.reflect.Array;
 
 /**
  * an expression which identifies an array reference.
@@ -15,10 +21,10 @@
 public class ArrayExpression extends Expression
 {
 
-    public ArrayExpression(Type type, Token token, List<Expression> idxList)
+    public ArrayExpression(Type type, Token token, Expression arrayRef, List<Expression> idxList)
     {
         super(type, token);
-        this.name = token.getText();
+        this.arrayRef = arrayRef;
         this.idxList = idxList;
     }
 
@@ -32,14 +38,11 @@
      *         been detected during inference/validation.
      */
     public boolean bind(Bindings bindings) {
-        // we  have to make sure that the array name is bound and taht the index expressions
-        // contain valid bindings
+        // we  have to make sure that any names occuring in the array reference are bound
+        // and that the index expressions contain valid bindings
         boolean valid = true;
 
-        if (bindings.lookup(name) == null) {
-            System.err.println("ArrayExpression.bind : unbound symbol " + name + getPos());
-            valid = false;
-        }
+        valid = arrayRef.bind(bindings);
 
         Iterator<Expression> iterator = idxList.iterator();
 
@@ -50,6 +53,63 @@
         return valid;
     }
 
-    String name;
+    public Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected) throws TypeException {
+        Type arrayType = arrayRef.typeCheck(bindings, typegroup, Type.UNDEFINED);
+        Type nextType = arrayType;
+        for (Expression expr : idxList) {
+            if (!nextType.isArray()) {
+                throw new TypeException("ArrayExpression.typeCheck : invalid type for array dereference " + nextType.getName() + getPos());
+            }
+            nextType = nextType.getBaseType();
+            expr.typeCheck(bindings, typegroup, Type.N);
+        }
+        type = nextType;
+        if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
+            throw new TypeException("ArrayExpression.typeCheck : invalid expected result type " + expected.getName() + getPos());
+        }
+
+        return type;
+    }
+
+    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+        // evaluate the array expression then evaluate each index expression in turn and
+        // dereference to access the array element
+
+        try {
+            Object value = arrayRef.interpret(helper);
+            Type nextType = arrayRef.getType();
+            for (Expression expr : idxList) {
+                int idx = ((Number) expr.interpret(helper)).intValue();
+                if (value == null) {
+                    throw new ExecuteException("ArrayExpression.interpret : attempted array indirection through null value " + arrayRef.token.getText() + getPos());
+                }
+                value = Array.get(value, idx);
+                nextType = nextType.getBaseType();
+            }
+
+            return value;
+        } catch (ExecuteException e) {
+            throw e;
+        } catch (IllegalArgumentException e) {
+            throw new ExecuteException("ArrayExpression.interpret : failed to evaluate expression " + arrayRef.token.getText() + getPos(), e);
+        } catch (ArrayIndexOutOfBoundsException e) {
+            throw new ExecuteException("ArrayExpression.interpret : invalid index for array " + arrayRef.token.getText() + getPos(), e);
+        } catch (ClassCastException e) {
+            throw new ExecuteException("ArrayExpression.interpret : invalid index dereferencing array " + arrayRef.token.getText() + getPos(), e);
+        } catch (Exception e) {
+            throw new ExecuteException("ArrayExpression.interpret : unexpected exception dereferencing array " + arrayRef.token.getText() + getPos(), e);
+        }
+    }
+
+    public void writeTo(StringWriter stringWriter) {
+        arrayRef.writeTo(stringWriter);
+        for (Expression expr : idxList) {
+            stringWriter.write("[");
+            expr.writeTo(stringWriter);
+            stringWriter.write("]");
+        }
+    }
+
+    Expression arrayRef;
     List<Expression> idxList;
 }

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BinaryOperExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BinaryOperExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BinaryOperExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -2,12 +2,13 @@
 
 import org.jboss.jbossts.orchestration.rule.type.Type;
 import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.antlr.runtime.Token;
 
 /**
  * binary operators includes arithmetic and comparison operators
  */
-public class BinaryOperExpression extends OperExpression
+public abstract class BinaryOperExpression extends OperExpression
 {
     public BinaryOperExpression(int oper, Type type, Token token, Expression operand1, Expression operand2)
     {

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BitExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BitExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BitExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,6 +1,11 @@
 package org.jboss.jbossts.orchestration.rule.expression;
 
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
+import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
 import org.antlr.runtime.Token;
 
 /**
@@ -14,4 +19,139 @@
 
         super(oper, Type.promote(left.getType(), right.getType()), token, left, right);
     }
+
+    public Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected) throws TypeException {
+        Type type1 = getOperand(0).typeCheck(bindings, typegroup, Type.N);
+        Type type2 = getOperand(1).typeCheck(bindings, typegroup, Type.N);
+        type = Type.promote(type1, type2);
+        // if either arg is float or double we will convert it to long and generate a long
+        // result so correct the promotion here
+        if (type.isFloating()) {
+            type = type.J;
+        }
+        if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
+            throw new TypeException("BitExpression.typeCheck : invalid expected result type " + expected.getName() + getPos());
+        }
+
+        return type;
+    }
+
+    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+        try {
+// n.b. be careful with characters here
+            Number value1 = (Number)getOperand(0).interpret(helper);
+            Number value2 = (Number)getOperand(1).interpret(helper);
+            // type is the result of promoting one or other or both of the operands
+            // and they should be converted to this type before doing the arithmetic operation
+            if (type == type.B) {
+                byte b1 = value1.byteValue();
+                byte b2 = value2.byteValue();
+                byte result;
+                // TODO we should probably only respect the byte, short and char types for + and -
+                // TODO also need to decide how to handle divide by zero
+
+                switch (oper)
+                {
+                    case BOR:
+                        result = (byte)(b1 | b2);
+                        break;
+                    case BAND:
+                        result = (byte)(b1 & b2);
+                        break;
+                    case BXOR:
+                        result = (byte)(b1 ^ b2);
+                        break;
+                    default:
+                        result = 0;
+                        break;
+                }
+                return new Byte(result);
+            } else if (type == type.S) {
+                short s1 = value1.shortValue();
+                short s2 = value2.shortValue();
+                short result;
+                switch (oper)
+                {
+                    case BOR:
+                        result = (short)(s1 | s2);
+                        break;
+                    case BAND:
+                        result = (short)(s1 & s2);
+                        break;
+                    case BXOR:
+                        result = (short)(s1 ^ s2);
+                        break;
+                    default:
+                        result = 0;
+                        break;
+                }
+                return new Short(result);
+            }  else if (type == type.I) {
+                int i1 = value1.intValue();
+                int i2 = value2.intValue();
+                int result;
+                switch (oper)
+                {
+                    case BOR:
+                        result = (i1 | i2);
+                        break;
+                    case BAND:
+                        result = (i1 & i2);
+                        break;
+                    case BXOR:
+                        result = (i1 ^ i2);
+                        break;
+                    default:
+                        result = 0;
+                        break;
+                }
+                return new Integer(result);
+            }  else if (type == type.J || type == type.F || type == type.D) {
+                long l1 = value1.longValue();
+                long l2 = value2.longValue();
+                long result;
+                switch (oper)
+                {
+                    case BOR:
+                        result = (l1 | l2);
+                        break;
+                    case BAND:
+                        result = (l1 & l2);
+                        break;
+                    case BXOR:
+                        result = (l1 ^ l2);
+                        break;
+                    default:
+                        result = 0;
+                        break;
+                }
+                return new Long(result);
+            }  else { // (type == type.C)
+                // use integers here but be careful about conversions
+                int s1 = value1.intValue();
+                int s2 = value2.intValue();
+                char result;
+                switch (oper)
+                {
+                    case BOR:
+                        result = (char)(s1 | s2);
+                        break;
+                    case BAND:
+                        result = (char)(s1 & s2);
+                        break;
+                    case BXOR:
+                        result = (char)(s1 ^ s2);
+                        break;
+                    default:
+                        result = 0;
+                        break;
+                }
+                return new Integer(result);
+            }
+        } catch (ExecuteException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ExecuteException("BitExpression.interpret : unexpected exception for operation " + token + getPos() + " in rule " + helper.getName(), e);
+        }
+    }
 }
\ No newline at end of file

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BooleanExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BooleanExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/BooleanExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -6,10 +6,10 @@
 /**
  * A binary arithmetic operator expression
  */
-public class BooleanExpression extends BinaryOperExpression
+public abstract class BooleanExpression extends BinaryOperExpression
 {
     public BooleanExpression(int oper, Token token, Expression left, Expression right)
     {
-        super(oper, Type.BOOLEAN, token, left, right);
+        super(oper, Type.Z, token, left, right);
     }
 }
\ No newline at end of file

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ComparisonExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ComparisonExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ComparisonExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,6 +1,11 @@
 package org.jboss.jbossts.orchestration.rule.expression;
 
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
+import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
 import org.antlr.runtime.Token;
 
 /**
@@ -12,4 +17,177 @@
     {
         super(oper, token, left, right);
     }
+
+    public Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected) throws TypeException {
+        // TODO allow comparison of non-numeric values
+        Type type1 = getOperand(0).typeCheck(bindings, typegroup, Type.N);
+        Type type2 = getOperand(1).typeCheck(bindings, typegroup, Type.N);
+        type = Type.Z;
+        if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
+            throw new TypeException("ComparisonExpression.typeCheck : invalid expected result type " + expected.getName() + getPos());
+        }
+
+        return type;
+    }
+
+    public Object interpret(Rule.BasicHelper helper) throws ExecuteException
+    {
+        try {
+// n.b. be careful with characters here
+            Number value1 = (Number)getOperand(0).interpret(helper);
+            Number value2 = (Number)getOperand(1).interpret(helper);
+            // type is the result of promoting one or other or both of the operands
+            // and they should be converted to this type before doing the compare operation
+            if (type == type.B || type == type.S || type == type.I) {
+                int i1 = value1.intValue();
+                int i2 = value2.intValue();
+                boolean result;
+                switch (oper)
+                {
+                    case LT:
+                        result = (i1 < i2);
+                        break;
+                    case LEQ:
+                        result = (i1 <= i2);
+                        break;
+                    case GT:
+                        result = (i1 > i2);
+                        break;
+                    case GEQ:
+                        result = (i1 >= i2);
+                        break;
+                    case EQ:
+                        result = (i1 == i2);
+                        break;
+                    case NEQ:
+                        result = (i1 != i2);
+                        break;
+                    default:
+                        result = false;
+                        break;
+                }
+                return result;
+            }  else if (type == type.J) {
+                long l1 = value1.longValue();
+                long l2 = value2.longValue();
+                boolean result;
+                switch (oper)
+                {
+                    case LT:
+                        result = (l1 < l2);
+                        break;
+                    case LEQ:
+                        result = (l1 <= l2);
+                        break;
+                    case GT:
+                        result = (l1 > l2);
+                        break;
+                    case GEQ:
+                        result = (l1 >= l2);
+                        break;
+                    case EQ:
+                        result = (l1 == l2);
+                        break;
+                    case NEQ:
+                        result = (l1 != l2);
+                        break;
+                    default:
+                        result = false;
+                        break;
+                }
+                return result;
+            }  else if (type == type.F) {
+                float f1 = value1.floatValue();
+                float f2 = value2.floatValue();
+                boolean result;
+                switch (oper)
+                {
+                    case LT:
+                        result = (f1 < f2);
+                        break;
+                    case LEQ:
+                        result = (f1 <= f2);
+                        break;
+                    case GT:
+                        result = (f1 > f2);
+                        break;
+                    case GEQ:
+                        result = (f1 >= f2);
+                        break;
+                    case EQ:
+                        result = (f1 == f2);
+                        break;
+                    case NEQ:
+                        result = (f1 != f2);
+                        break;
+                    default:
+                        result = false;
+                        break;
+                }
+                return result;
+            }  else if (type == type.D) {
+                double d1 = value1.doubleValue();
+                double d2 = value2.doubleValue();
+                boolean result;
+                switch (oper)
+                {
+                    case LT:
+                        result = (d1 < d2);
+                        break;
+                    case LEQ:
+                        result = (d1 <= d2);
+                        break;
+                    case GT:
+                        result = (d1 > d2);
+                        break;
+                    case GEQ:
+                        result = (d1 >= d2);
+                        break;
+                    case EQ:
+                        result = (d1 == d2);
+                        break;
+                    case NEQ:
+                        result = (d1 != d2);
+                        break;
+                    default:
+                        result = false;
+                        break;
+                }
+                return result;
+            }  else { // (type == type.C)
+                char c1 = (char)value1.intValue();
+                char c2 = (char)value2.intValue();
+                boolean result;
+                switch (oper)
+                {
+                    case LT:
+                        result = (c1 < c2);
+                        break;
+                    case LEQ:
+                        result = (c1 <= c2);
+                        break;
+                    case GT:
+                        result = (c1 > c2);
+                        break;
+                    case GEQ:
+                        result = (c1 >= c2);
+                        break;
+                    case EQ:
+                        result = (c1 == c2);
+                        break;
+                    case NEQ:
+                        result = (c1 != c2);
+                        break;
+                    default:
+                        result = false;
+                        break;
+                }
+                return result;
+            }
+        } catch (ExecuteException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ExecuteException("ComparisonExpression.interpret : unexpected exception for operation " + token + getPos() + " in rule " + helper.getName(), e);
+        }
+    }
 }
\ No newline at end of file

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ConditionalEvalExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ConditionalEvalExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ConditionalEvalExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,6 +1,11 @@
 package org.jboss.jbossts.orchestration.rule.expression;
 
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
+import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
 import org.antlr.runtime.Token;
 
 /**
@@ -12,4 +17,40 @@
     {
         super(TERNARY, type, token, cond, if_expr, else_expr);
     }
+
+    public Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected) throws TypeException {
+        Type condType = getOperand(0).typeCheck(bindings, typegroup,  Type.Z);
+        Type type1 = getOperand(1).typeCheck(bindings, typegroup, expected);
+        Type type2 = getOperand(2).typeCheck(bindings, typegroup, expected);
+        // type1 must be defined and type2 must be the same as type 1 or assignable
+        // to/from it.
+        if (type2 != type1) {
+            // ok check that the types are interassignable in at least one direction
+            // but we have to treat numerics as special cases because we can assign in
+            // many directions
+            if (type1.isNumeric() && type2.isNumeric()) {
+                type = Type.promote(type1,  type2);
+            } else if (type2.isAssignableFrom(type1)) {
+                type = type2;
+            } else if (type1.isAssignableFrom(type2)) {
+                type = type1;
+            } else {
+                throw new TypeException("ConditionalEvalExpression.typeCheck : incompatible argument types " + type1.getName() + " and " + type2.getName() + getPos());
+            }
+        }
+        if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
+            throw new TypeException("ConditionalEvalExpression.typeCheck : invalid expected result type " + expected.getName() + getPos());
+        }
+        return type;
+    }
+
+    public Object interpret(Rule.BasicHelper helper) throws ExecuteException
+    {
+        Boolean executeFirstBranch = (Boolean)getOperand(0).interpret(helper);
+        if (executeFirstBranch) {
+            return getOperand(1).interpret(helper);
+        } else {
+            return getOperand(2).interpret(helper);
+        }
+    }
 }

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/DollarExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/DollarExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/DollarExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -3,8 +3,14 @@
 import org.jboss.jbossts.orchestration.rule.binding.Bindings;
 import org.jboss.jbossts.orchestration.rule.binding.Binding;
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
 import org.antlr.runtime.Token;
 
+import java.io.StringWriter;
+
 /**
  * an expression which refers either to a builtin variable or to a bound parameter of the
  * triggering method for an ECA rule. builtin variables are written as a dollar sign followed
@@ -35,6 +41,18 @@
         }
     }
 
+    public DollarExpression(Type type, Token token, String text)
+    {
+        super(type, token);
+        this.name = text.substring(1, text.length());
+        try {
+            index = Integer.decode(name);
+        } catch (NumberFormatException nfe) {
+            // oops should not be possible according to tokenizer rules
+            index = -1;
+        }
+    }
+
     /**
      * verify that variables mentioned in this expression are actually available in the supplied
      * bindings list and infer/validate the type of this expression or its subexpressions
@@ -47,15 +65,7 @@
 
     public boolean bind(Bindings bindings) {
         if (index < 0) {
-            // reference to special symbol
-            int l = dollarSymbols.length;
-            int i;
-            for (i = 0; i < l; i++) {
-                if (dollarSymbols[i].equals(name)) {
-                    return true;
-                }
-            }
-            System.err.println("DollarExpression.bind : invalid builtin symbol " + name + getPos());
+            System.err.println("DollarExpression.bind : invalid bound parameter $" + name + getPos());
             return false;
         } else {
             // reference to positional parameter -- name must be a non-signed integer
@@ -64,9 +74,32 @@
         }
     }
 
+    public Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected) throws TypeException {
+        // ensure there is a parameter with the relevant name in the bindings
+        Binding binding = bindings.lookup(Integer.toString(index));
+        if (binding == null) {
+            throw new TypeException("DollarExpression.typeCheck : invalid bound parameter $" + name + getPos());
+        }
+        type = binding.getType();
+        if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
+            throw new TypeException("DollarExpression.typeCheck : invalid expected type " + expected.getName() + " for bound parameter " + name + getPos());            
+        }
+        return type;
+    }
+
+    public Object interpret(Rule.BasicHelper helper) throws ExecuteException
+    {
+        return helper.getBinding(name);
+    }
+
+    public void writeTo(StringWriter stringWriter) {
+        if (name.equals("-1")) {
+            stringWriter.write("$$");
+        } else {
+            stringWriter.write("$" + name);
+        }
+    }
+
     private String name;
     private int index;
-
-    private static String[] dollarSymbols = {
-    };
 }

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/Expression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/Expression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/Expression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -6,7 +6,13 @@
 import org.jboss.jbossts.orchestration.rule.binding.Bindings;
 import org.jboss.jbossts.orchestration.rule.expression.*;
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
 
+import java.io.StringWriter;
+
 /**
  * abstract class representing an evaluable expression. this is used in all 3 elements of ECA rules:
  * as the value part of each of the event bindings in the binding list comprising an ECA rule event;
@@ -22,8 +28,14 @@
     protected Expression(Type type, Token token)
     {
         this.type = type;
-        this.charPos = token.getCharPositionInLine();
-        this.line = token.getLine();
+        this.token = token;
+        if (token != null) {
+            this.charPos = token.getCharPositionInLine();
+            this.line = token.getLine();
+        } else {
+            this.charPos = 0;
+            this.line = 0;
+        }
     }
 
     /**
@@ -48,9 +60,36 @@
     }
 
     /**
-     * the name of the
+     * ensure that all type references in the expression and its component expressions
+     * can be resolved, that the type of the expression is well-defined and that it is
+     * compatible with the type expected in the context in which it occurs.
+     * @param bindings the bound variable in scope at the point where the expression is
+     * to be evaluate
+     * @param typegroup the set of types employed by the rule
+     * @param expected the type expected for the expression in the contxt in which it occurs. this
+     * may be void but shoudl not be undefined at the point where type checking is performed.
+     * @return
+     * @throws TypeException
      */
+    public abstract Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected)
+            throws TypeException;
+
+    /**
+     * evaluate the expression by interpreting the expression tree
+     * @param helper an execution context associated with the rule whcih contains a map of
+     * current bindings for rule variables and another map of their declared types both of which
+     * are indexed by varoable name. This includes entries for the helper (name "-1"), the
+     * recipient if the trigger method is not static (name "0") and the trigger method arguments
+     * (names "1", ...)
+     * @return  the result of evaluation as an Object
+     * @throws org.jboss.jbossts.orchestration.rule.exception.ExecuteException
+     */
+    public abstract Object interpret(Rule.BasicHelper helper) throws ExecuteException;
+
+    public abstract void writeTo(StringWriter stringWriter);
+
     protected Type type;
     protected int charPos;
     protected int line;
+    protected Token token;
 }

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ExpressionHelper.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ExpressionHelper.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/ExpressionHelper.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,8 +1,10 @@
 package org.jboss.jbossts.orchestration.rule.expression;
 
 import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.binding.Binding;
 import static org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.*;
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.antlr.runtime.tree.CommonTree;
 import org.antlr.runtime.Token;
 
@@ -15,11 +17,13 @@
 public class ExpressionHelper
 {
     public static Expression createExpression(Bindings bindings, CommonTree exprTree)
+            throws TypeException
     {
         return createExpression(bindings, exprTree, Type.UNDEFINED);
     }
 
     public static Expression createExpression(Bindings bindings, CommonTree exprTree, Type type)
+            throws TypeException
     {
         // we expect expr = simple_expr |
         //                  (UNARYOP unary_oper expr) |
@@ -37,7 +41,7 @@
 
         Token token = exprTree.getToken();
         int tokenType = token.getType();
-        Expression expr = null;
+        Expression expr;
         switch (tokenType) {
             case DOLLARSYM:
             {
@@ -54,13 +58,13 @@
                     // direct variable reference
                     expr = new Variable(type, token);
                 } else {
-                    // field reference either to an instance named by a field or or a static
+                    // field reference either to an instance named by a binding or or a static
 
                     String[] parts = text.split("\\.");
                     if (parts.length < 2) {
                         // oops malformed symbol either "." ro "foo." or ".foo"
-                        // shoudl nto happen but ...
-                        System.err.println("ExpressionHelper.createExpression : unexpected symbol "  + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
+                        // should not  happen but ...
+                        throw new TypeException("ExpressionHelper.createExpression : unexpected symbol "  + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
                     } else {
                         String prefix = parts[0];
 
@@ -69,14 +73,10 @@
                             // instance field access
                             int l = parts.length - 1;
                             String[] fields = new String[l];
-                            for (int i = 0; i < l; i++) {
-                                fields[i] = parts[i + 1];
-                            }
+                            System.arraycopy(parts, 1, fields, 0, l);
                             expr = new FieldExpression(type, token, prefix, fields);
                         } else {
-                            String clazzName = text.substring(0, dotIdx);
-                            String fieldName = text.substring(dotIdx);
-                            expr = new StaticExpression(type, token, clazzName, fieldName);
+                            expr = new StaticExpression(type, token, parts);
                         }
                     }
                 }
@@ -88,15 +88,45 @@
                 CommonTree child1 = (CommonTree) exprTree.getChild(1);
                 token = child0.getToken();
                 if (token.getType() != SYMBOL) {
-                    System.err.println("ExpressionHelper.createExpression : unexpected token Type in array expression tree " + tokenType + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
+                    throw new TypeException("ExpressionHelper.createExpression : unexpected token Type in array expression tree " + tokenType + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
+                }
+                Expression arrayRef;
+                // check for embedded dots
+
+                String text = token.getText();
+                int dotIdx = text.lastIndexOf('.');
+                if (dotIdx < 0) {
+                    // array name is direct variable reference
+                    arrayRef = new Variable(type, token);
                 } else {
-                    List<Expression> indices = createExpressionList(bindings, child1, Type.INTEGER);
-                    if (indices != null) {
-                        expr = new ArrayExpression(type, token, indices);
+                    // field reference either to an instance named by a binding or or a static
+
+                    String[] parts = text.split("\\.");
+                    if (parts.length < 2) {
+                        // oops malformed symbol either "." ro "foo." or ".foo"
+                        // should not  happen but ...
+                        throw new TypeException("ExpressionHelper.createExpression : unexpected symbol "  + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
                     } else {
-                        System.err.println("ExpressionHelper.createExpression : invalid array index expression @ " + token.getLine() + "." + token.getCharPositionInLine());
+                        String prefix = parts[0];
+
+                        if (bindings.lookup(prefix) != null) {
+                            // intitial segment of text identifies a bound variable so treat as
+                            // instance field access
+                            int l = parts.length - 1;
+                            String[] fields = new String[l];
+                            System.arraycopy(parts, 1, fields, 0, l);
+                            arrayRef = new FieldExpression(type, token, prefix, fields);
+                        } else {
+                            arrayRef = new StaticExpression(type, token, parts);
+                        }
                     }
                 }
+                List<Expression> indices = createExpressionList(bindings, child1, Type.INTEGER);
+                if (indices != null) {
+                    expr = new ArrayExpression(type, token, arrayRef, indices);
+                } else {
+                    throw new TypeException("ExpressionHelper.createExpression : invalid array index expression @ " + token.getLine() + "." + token.getCharPositionInLine());
+                }
             }
             break;
             case METH:
@@ -110,18 +140,9 @@
                 }
                 token = child0.getToken();
                 if (token.getType() != SYMBOL) {
-                    System.err.println("ExpressionHelper.createExpression : unexpected token Type in method expression tree " + tokenType + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
-                } else if (child1 == null) {
-                    expr = new MethodExpression(type, token, new ArrayList<Expression>());
+                    throw new TypeException("ExpressionHelper.createExpression : unexpected token Type in method expression tree " + tokenType + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
                 } else {
-                    List<Expression> args = createExpressionList(bindings, child1);
-                    if (args != null) {
-                        // need to separate out builtins, instance methdo calls and
-                        // static method calls
-                        expr = new MethodExpression(type, token, args);
-                    } else {
-                        System.err.println("ExpressionHelper.createExpression : method argument @ " + token.getLine() + "." + token.getCharPositionInLine());
-                    }
+                    expr = createCallExpression(bindings, token, child1, type);
                 }
             }
             break;
@@ -152,35 +173,96 @@
             break;
             default:
             {
-                System.err.println("ExpressionHelper.createExpression : unexpected token Type in expression tree " + tokenType + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
+                throw new TypeException("ExpressionHelper.createExpression : unexpected token Type in expression tree " + tokenType + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
             }
-            break;
         }
 
-        if (expr != null) {
-            Type exprType = Type.dereference(expr.getType());
-            Type targetType = Type.dereference(type);
-            if (exprType.isDefined() && targetType.isDefined() && !targetType.isAssignableFrom(exprType)) {
-                // we already know this is an invalid type so notify an error and return null
-                System.err.println("ExpressionHelper.createExpression : invalid expression type " + exprType.getName() + " expecting " + targetType.getName() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
-                return null;
-            } else if (targetType == Type.NUMBER && !exprType.isNumeric()) {
-                // we already know this is an invalid type so notify ane rror and return null
-                System.err.println("ExpressionHelper.createExpression : invalid expression type " + exprType.getName() + " expecting " + targetType.getName() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
-                return null;
-            }
-            if (expr.bind(bindings)) {
-                return expr;
-            } else {
-                System.err.println("ExpressionHelper.createExpression : unknown reference in expression @ " + token.getLine() + "." + token.getCharPositionInLine());
-                return null;
-            }
-        } else {
-            return null;
+        Type exprType = Type.dereference(expr.getType());
+        Type targetType = Type.dereference(type);
+        if (exprType.isDefined() && targetType.isDefined() && !targetType.isAssignableFrom(exprType)) {
+            // we already know this is an invalid type so notify an error and return null
+            throw new TypeException("ExpressionHelper.createExpression : invalid expression type " + exprType.getName() + " expecting " + targetType.getName() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
+        } else if (targetType.isNumeric() && !exprType.isNumeric()) {
+            // we already know this is an invalid type so notify an error and return null
+            throw new TypeException("ExpressionHelper.createExpression : invalid expression type " + exprType.getName() + " expecting " + targetType.getName() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
         }
+        if (!expr.bind(bindings)) {
+            throw new TypeException("ExpressionHelper.createExpression : unknown reference in expression @ " + token.getLine() + "." + token.getCharPositionInLine());
+        }
+
+        return expr;
     }
 
+     public static Expression createCallExpression(Bindings bindings, Token token, CommonTree argTree, Type type)
+             throws TypeException
+     {
+         Expression expr;
+         String callName;
+         Expression recipient;
+         List<Expression> args;
+
+         // we need to factor off the path from the method/builtin name
+
+         String text = token.getText();
+         int dotIdx = text.lastIndexOf('.');
+         if (dotIdx < 0) {
+             // a builtin call
+
+             recipient = null;
+             callName = text;
+         } else {
+             // prefix must be either a bound varibale, a field reference via a bound
+             // variable, or a static field reference
+
+             String[] path = text.split("\\.");
+             if (path.length < 2) {
+                 // oops malformed symbol either "." ro "foo." or ".foo"
+                 // should not happen but ...
+                 throw new TypeException("ExpressionHelper.createCallExpression : unexpected symbol "  + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
+             } else {
+                 // save the method name and only consider path preceding it
+                 int pathLength = path.length - 1;
+                 callName = path[pathLength];
+
+                 // see if the path starts with a bound variable
+                 String prefix = path[0];
+                 Binding binding = bindings.lookup(prefix);
+
+                 if (binding != null) {
+                     // intitial segment of text identifies a bound variable so treat as
+                     // a variable access or an instance field access depending upon how
+                     // many extra path elenments there are
+                     if (pathLength == 1) {
+                         // method call on bound variable
+                         recipient = new Variable(binding.getType(), token, binding.getName());
+                     } else {
+                         // method call on field of bound variable
+                         String[] fields = new String[pathLength - 1];
+                         System.arraycopy(path, 1, fields, 0, pathLength - 1);
+                         recipient = new FieldExpression(Type.UNDEFINED, token, prefix, fields);
+                     }
+                 } else {
+                     // ok, we need a version of the path without the method name on the end
+                     String[] realPath = new String[pathLength];
+                     System.arraycopy(path, 0, realPath, 0, pathLength);
+                     recipient = new StaticExpression(Type.UNDEFINED, token, realPath);
+                 }
+             }
+         }
+
+         if (argTree == null) {
+             args = new ArrayList<Expression>();
+         } else {
+             args = createExpressionList(bindings, argTree);
+         }
+
+         expr = new MethodExpression(type, token, callName, recipient, args);
+
+         return expr;
+     }
+
     public static Expression createUnaryExpression(Bindings bindings, CommonTree exprTree, Type type)
+            throws TypeException
     {
         // we expect ^(UNOP unary_oper expr)
 
@@ -194,37 +276,34 @@
             case TWIDDLE:
             {
                 // the argument must be a numeric expression
+                if (!type.isUndefined() && !type.isVoid() && !type.isNumeric()) {
+                    throw new TypeException("ExpressionHelper.createUnaryExpression : invalid numeric expression @ " + token.getLine() + "." + token.getCharPositionInLine());
+                }
                 Expression operand = createExpression(bindings, child1, Type.NUMBER);
-                if (operand != null) {
-                    expr = new TwiddleExpression(token, operand);
-                } else {
-                    expr = null;
-                }
+                expr = new TwiddleExpression(token, operand);
             }
             break;
             case NOT:
             {
                 // the argument must be a boolean expression
+                if (!type.isUndefined() && !type.isVoid() && !type.isBoolean()) {
+                    throw new TypeException("ExpressionHelper.createUnaryExpression : invalid boolean expression @ " + token.getLine() + "." + token.getCharPositionInLine());
+                }
                 Expression operand = createExpression(bindings, child1, Type.BOOLEAN);
-                if (operand != null) {
-                    expr = new NotExpression(token, operand);
-                } else {
-                    expr = null;
-                }
+                expr = new NotExpression( token, operand);
             }
             break;
             default:
             {
-                System.err.println("ExpressionHelper.createUnaryExpression : unexpected token Type in expression tree " + token.getType() + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
-                expr = null;
+                throw new TypeException("ExpressionHelper.createUnaryExpression : unexpected token Type in expression tree " + token.getType() + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
             }
-            break;
         }
 
         return expr;
     }
 
     public static Expression createBinaryExpression(Bindings bindings, CommonTree exprTree, Type type)
+            throws TypeException
     {
         // we expect ^(BINOP infix_oper simple_expr expr)
 
@@ -233,8 +312,9 @@
         CommonTree child2 = (CommonTree) exprTree.getChild(2);
         Expression expr;
         Token token = child0.getToken();
+        int oper = token.getType();
 
-        switch (token.getType())
+        switch (oper)
         {
             case PLUS:
             {
@@ -244,53 +324,29 @@
                 if (type == Type.STRING) {
                     // must be doing String concatenation
                     operand1 = createExpression(bindings, child1, Type.STRING);
-                    operand2 = createExpression(bindings, child1, Type.STRING);
-                    if (operand1 != null && operand2 != null) {
-                        expr = new StringPlusExpression(token, operand1,  operand2);
-                    } else {
-                        expr = null;
-                    }
+                    operand2 = createExpression(bindings, child2, Type.STRING);
+                    expr = new StringPlusExpression(token, operand1,  operand2);
                 } else if (type.isNumeric()) {
                     // must be doing arithmetic
                     operand1 = createExpression(bindings, child1, Type.NUMBER);
-                    operand2 = createExpression(bindings, child1, Type.NUMBER);
-                    if (operand1 != null && operand2 != null) {
-                        expr = new ArithmeticExpression(PLUS, token, operand1,  operand2);
-                    } else {
-                        expr = null;
-                    }
+                    operand2 = createExpression(bindings, child2, Type.NUMBER);
+                    int convertedOper = OperExpression.convertOper(oper);
+                    expr = new ArithmeticExpression(convertedOper, token, operand1,  operand2);
                 } else {
                     // see if the operand gives us any type info
                     operand1 = createExpression(bindings, child1, Type.UNDEFINED);
-                    if (operand1 != null) {
-                        if (operand1.getType().isNumeric()) {
-                            operand2 = createExpression(bindings, child1, Type.NUMBER);
-                            if (operand2 != null) {
-                                expr = new ArithmeticExpression(PLUS, token, operand1, operand2);
-                            } else {
-                                expr = null;
-                            }
-                        } else if (operand1.getType() == Type.STRING) {
-                            operand2 = createExpression(bindings, child1, Type.STRING);
-                            if (operand2 != null) {
-                                expr = new StringPlusExpression(token, operand1,  operand2);
-                            } else {
-                                expr = null;
-                            }
-                        } else {
-                            operand2 = createExpression(bindings, child1, Type.UNDEFINED);
-                            if (operand2 != null) {
-                                // create as generic plus expression which we will replace later during type
-                                // checking
-                                expr = new PlusExpression(token, operand1,  operand2);
-                            } else {
-                                expr = null;
-                            }
-                        }
+                    if (operand1.getType().isNumeric()) {
+                        operand2 = createExpression(bindings, child2, Type.NUMBER);
+                        int convertedOper = OperExpression.convertOper(oper);
+                        expr = new ArithmeticExpression(convertedOper, token, operand1, operand2);
+                    } else if (operand1.getType() == Type.STRING) {
+                        operand2 = createExpression(bindings, child2, Type.STRING);
+                        expr = new StringPlusExpression(token, operand1,  operand2);
                     } else {
-                        // generate more errors if we can even though we are giving up
-                        operand2 = createExpression(bindings, child1, Type.UNDEFINED);
-                        expr = null;
+                        operand2 = createExpression(bindings, child2, Type.UNDEFINED);
+                        // create as generic plus expression which we will replace later during type
+                        // checking
+                        expr = new PlusExpression(token, operand1,  operand2);
                     }
                 }
             }
@@ -303,11 +359,8 @@
                 Expression operand1 = createExpression(bindings, child1, Type.NUMBER);
                 Expression operand2 = createExpression(bindings, child2, Type.NUMBER);
 
-                if (operand1 != null & operand2 != null) {
-                    return new ArithmeticExpression(token.getType(), token, operand1, operand2);
-                } else {
-                    expr = null;
-                }
+                int convertedOper = OperExpression.convertOper(oper);
+                expr = new ArithmeticExpression(convertedOper, token, operand1, operand2);
             }
             break;
             case BAND:
@@ -317,11 +370,8 @@
                 Expression operand1 = createExpression(bindings, child1, Type.NUMBER);
                 Expression operand2 = createExpression(bindings, child2, Type.NUMBER);
 
-                if (operand1 != null & operand2 != null) {
-                    return new BitExpression(token.getType(), token, operand1, operand2);
-                } else {
-                    expr = null;
-                }
+                int convertedOper = OperExpression.convertOper(oper);
+                expr = new BitExpression(convertedOper, token, operand1, operand2);
             }
             break;
             case AND:
@@ -330,11 +380,8 @@
                 Expression operand1 = createExpression(bindings, child1, Type.BOOLEAN);
                 Expression operand2 = createExpression(bindings, child2, Type.BOOLEAN);
 
-                if (operand1 != null & operand2 != null) {
-                    expr = new LogicalExpression(token.getType(), token, operand1, operand2);
-                } else {
-                    expr = null;
-                }
+                int convertedOper = OperExpression.convertOper(oper);
+                expr = new LogicalExpression(convertedOper, token, operand1, operand2);
             }
             break;
             case EQ:
@@ -347,24 +394,21 @@
                 Expression operand1 = createExpression(bindings, child1, Type.NUMBER);
                 Expression operand2 = createExpression(bindings, child2, Type.NUMBER);
 
-                if (operand1 != null & operand2 != null) {
-                    expr = new ComparisonExpression(token.getType(), token, operand1, operand2);
-                } else {
-                    expr = null;
-                }
+                int convertedOper = OperExpression.convertOper(oper);
+                expr = new ComparisonExpression(convertedOper, token, operand1, operand2);
             }
+            break;
             default:
             {
-                System.err.println("ExpressionHelper.createBinaryExpression : unexpected token Type in expression tree " + token.getType() + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
-                expr = null;
+                throw new TypeException("ExpressionHelper.createBinaryExpression : unexpected token Type in expression tree " + token.getType() + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
             }
-            break;
         }
 
         return expr;
     }
 
     public static Expression createTernaryExpression(Bindings bindings, CommonTree exprTree, Type type)
+            throws TypeException
     {
         // we expect ^(TERNOP ternary_oper simple_expr expr expr)
 
@@ -381,90 +425,97 @@
             {
                 // the argument must be a numeric expression
                 Expression operand1 = createExpression(bindings, child1, Type.BOOLEAN);
-                Expression operand2 = createExpression(bindings, child1, Type.UNDEFINED);
-                Expression operand3 = createExpression(bindings, child1, Type.UNDEFINED);
-                if (operand1 != null && operand2 != null && operand3 != null) {
-                    Type type2 = Type.dereference(operand2.getType());
-                    Type type3 = Type.dereference(operand3.getType());
-                    if (type2.isNumeric() || type3.isNumeric()) {
-                        expr = new TernaryOperExpression(TERN_IF, Type.promote(type2, type3),  token, operand1,  operand2, operand3);
-                    } else if (type2.isDefined() && type3.isDefined()) {
-                        // since they are not numeric we have to have the same type
-                        if (type2 == type3) {
-                            // use this type
-                            expr = new TernaryOperExpression(TERN_IF, type2,  token, operand1,  operand2, operand3);
-                        } else {
-                            // mismatched types so don't generate a result
-                            System.err.println("ExpressionHelper.createTernaryExpression : mismatched types " + type2.getName() + " and " + type3.getName()  + " in conditional expression " + token.getType() + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
-                            expr = null;
-                        }
+                Expression operand2 = createExpression(bindings, child2, type);
+                Expression operand3 = createExpression(bindings, child3, type);
+                Type type2 = Type.dereference(operand2.getType());
+                Type type3 = Type.dereference(operand3.getType());
+                if (type2.isNumeric() || type3.isNumeric()) {
+                    if (!type.isUndefined() && !type.isVoid() && !type.isNumeric()) {
+                        throw new TypeException("ExpressionHelper.createUnaryExpression : invalid numeric expression @ " + token.getLine() + "." + token.getCharPositionInLine());
+                    }
+                    expr = new ConditionalEvalExpression(Type.promote(type2, type3),  token, operand1,  operand2, operand3);
+                } else if (type2.isDefined() && type3.isDefined()) {
+                    // since they are not numeric we have to have the same type
+                    if (type2 == type3) {
+                        // use this type
+                        expr = new ConditionalEvalExpression(type2,  token, operand1,  operand2, operand3);
                     } else {
-                        // have to wait for type check to resolve types
-                        expr = new TernaryOperExpression(TERN_IF, Type.UNDEFINED,  token, operand1,  operand2, operand3);
+                        // mismatched types so don't generate a result
+                        throw new TypeException("ExpressionHelper.createTernaryExpression : mismatched types " + type2.getName() + " and " + type3.getName()  + " in conditional expression " + token.getType() + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
                     }
                 } else {
-                    expr = null;
+                    // have to wait for type check to resolve types
+                    expr = new ConditionalEvalExpression(Type.UNDEFINED,  token, operand1,  operand2, operand3);
                 }
             }
             break;
             default:
             {
-                System.err.println("ExpressionHelper.createTernaryExpression : unexpected token Type in expression tree " + token.getType() + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
-                expr = null;
+                throw new TypeException("ExpressionHelper.createTernaryExpression : unexpected token Type in expression tree " + token.getType() + " for token " + token.getText() + " @ " + token.getLine() + "." + token.getCharPositionInLine());
             }
-            break;
         }
 
         return expr;
     }
 
     public static List<Expression> createExpressionList(Bindings bindings, CommonTree exprTree)
+            throws TypeException
     {
         return createExpressionList(bindings, exprTree, Type.UNDEFINED);
 
     }
     public static List<Expression> createExpressionList(Bindings bindings, CommonTree exprTree, Type type)
+            throws TypeException
     {
         // we expect expr_list = ^(EXPR) |
         //                       ^(SEPR expr expr_list)
 
         List<Expression> exprList = new ArrayList<Expression>();
-        boolean success = true;
+        List<TypeException> exceptions = new ArrayList<TypeException>();
 
         while (exprTree != null)
         {
-            switch (exprTree.getToken().getType())
-            {
-                case SEPR:
+            try {
+                switch (exprTree.getToken().getType())
                 {
-                    CommonTree child0 = (CommonTree) exprTree.getChild(0);
-                    CommonTree child1 = (CommonTree) exprTree.getChild(1);
-                    Expression expr = createExpression(bindings, child0, type);
-                    if (expr != null) {
+                    case SEPR:
+                    {
+                        CommonTree child0 = (CommonTree) exprTree.getChild(0);
+                        // assign tree before we risk an exception
+                        exprTree = (CommonTree) exprTree.getChild(1);
+                        Expression expr = createExpression(bindings, child0, type);
                         exprList.add(expr);
-                    } else {
-                        success &= false;
                     }
-                    exprTree = child1;
-                }
-                break;
-                default:
-                {
-                    Expression expr = createExpression(bindings, exprTree, type);
-                    if (expr != null) {
+                    break;
+                    default:
+                    {
+                        // assign tree before we risk an exception
+                        CommonTree saveTree = exprTree;
+                        exprTree = null;
+                        Expression expr = createExpression(bindings, saveTree, type);
                         exprList.add(expr);
-                    } else {
-                        success &= false;
                     }
-                    exprTree = null;
+                    break;
                 }
-                break;
+            } catch (TypeException te) {
+                exceptions.add(te);
             }
         }
-        if (success) {
-            return exprList;
-        } else {
-            return null;
+
+        if (!exceptions.isEmpty()) {
+            if (exceptions.size() == 1) {
+                throw exceptions.get(0);
+            } else {
+                StringBuffer buffer = new StringBuffer();
+                buffer.append("ExpressionHelper.createExpressionList : errors checking expression sequence");
+                for (TypeException typeException : exceptions) {
+                    buffer.append("\n");
+                    buffer.append(typeException.toString());
+                }
+                throw new TypeException(buffer.toString());
+            }
         }
+
+        return exprList;
     }
 }

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/FieldExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/FieldExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/FieldExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -3,21 +3,28 @@
 import org.jboss.jbossts.orchestration.rule.binding.Bindings;
 import org.jboss.jbossts.orchestration.rule.binding.Binding;
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
 import org.antlr.runtime.Token;
 
+import java.io.StringWriter;
+import java.lang.reflect.Field;
+
 /**
  * an expression which identifies an instance field reference
  */
 public class FieldExpression extends Expression
 {
-    public FieldExpression(Type type, Token token, String ref, String[] fields) {
+    public FieldExpression(Type type, Token token, String ref, String[] fieldNames) {
         // type is the type of last field
         // ownerType[i] is the type of the owner of field[i]
         // so ownerType[0] is the type of ref;
         super(type, token);
         this.ref = ref;
-        this.fields = fields;
-        int len = fields.length;
+        this.fieldNames = fieldNames;
+        int len = fieldNames.length;
         this.ownerType = new Type[len];
         for (int i = 0; i < len; i++) {
             ownerType[i] = Type.UNDEFINED;
@@ -53,7 +60,81 @@
         return true;
     }
 
+    public Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected) throws TypeException {
+        // check the owner type is defined and then start searching for
+        // the types of each field referenced from it
+
+        ownerType[0] = Type.dereference(ownerType[0]);
+        if (ownerType[0].isUndefined()) {
+            throw new TypeException("FieldExpresssion.typeCheck : unbound instance " + ref + getPos());
+        }
+
+        Class ownerClazz = ownerType[0].getTargetClass();
+        Class valueClass = null;
+        Type valueType = null;
+        int fieldCount = fieldNames.length;
+
+        fields = new Field[fieldCount];
+
+        for (int i = 0; i < fieldCount; i++) {
+            if (i != 0) {
+                ownerType[i] = valueType;
+                ownerClazz = valueType.getTargetClass();
+            }
+            String fieldName = fieldNames[i];
+            try {
+                fields[i]  = ownerClazz.getField(fieldName);
+            } catch (NoSuchFieldException e) {
+                throw new TypeException("FieldExpresssion.typeCheck : invalid field reference " + fieldName + getPos());
+            }
+
+            valueClass = fields[i].getType();
+            valueType = typegroup.ensureType(valueClass);
+        }
+
+        type = valueType;
+
+        if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
+            throw new TypeException("FieldExpresssion.typeCheck : invalid expected type " + expected.getName() + getPos());
+        }
+
+        return type;
+    }
+
+    public Object interpret(Rule.BasicHelper helper) throws ExecuteException
+    {
+        try {
+            // TODO the reference should really be an expression?
+            Object value = helper.getBinding(ref);
+
+            int fieldCount = fields.length;
+            for (int i = 0; i < fieldCount; i++) {
+                if (value == null) {
+                    throw new ExecuteException("FieldExpression.interpret : attempted field indirection through null value " + token.getText() + getPos());
+                }
+                value = fields[i].get(value);
+            }
+
+            return value;
+        } catch (ExecuteException e) {
+            throw e;
+        } catch (IllegalAccessException e) {
+            throw new ExecuteException("FieldExpression.interpret : error accessing field " + token.getText() + getPos(), e);
+        } catch (Exception e) {
+            throw new ExecuteException("FieldExpression.interpret : unexpected exception accessing field " + token.getText() + getPos(), e);
+        }
+    }
+
+    public void writeTo(StringWriter stringWriter) {
+        stringWriter.write(ref);
+        for (String field : fieldNames) {
+            stringWriter.write(".");
+            stringWriter.write(field);
+        }
+    }
+
     private String ref;
-    private String[] fields;
+    private String[] fieldNames;
     private Type[] ownerType;
+    private Field[] fields;
 }
\ No newline at end of file

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/LogicalExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/LogicalExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/LogicalExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,6 +1,11 @@
 package org.jboss.jbossts.orchestration.rule.expression;
 
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
+import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
 import org.antlr.runtime.Token;
 
 /**
@@ -12,4 +17,25 @@
     {
         super(oper, token, left, right);
     }
+
+    public Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected) throws TypeException {
+        Type type1 = getOperand(0).typeCheck(bindings, typegroup, Type.Z);
+        Type type2 = getOperand(1).typeCheck(bindings, typegroup, Type.Z);
+        type = Type.Z;
+        if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
+            throw new TypeException("LogicalExpression.typeCheck : invalid expected result type " + expected.getName() + getPos());
+        }
+
+        return type;
+    }
+
+    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+        Boolean value = (Boolean)getOperand(0).interpret(helper);
+
+        if (oper == AND) {
+            return (value && (Boolean)getOperand(1).interpret(helper));
+        } else { // oper == OR
+            return (value || (Boolean)getOperand(1).interpret(helper));
+        }
+    }
 }
\ No newline at end of file

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/MethodExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/MethodExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/MethodExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,20 +1,30 @@
 package org.jboss.jbossts.orchestration.rule.expression;
 
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
 import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
 import org.antlr.runtime.Token;
 import java.util.List;
 import java.util.Iterator;
+import java.util.ArrayList;
+import java.io.StringWriter;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
 
 /**
  * an expression which identifies a method invocation
  */
 public class MethodExpression extends Expression
 {
-    public MethodExpression(Type type, Token token, List<Expression> arguments) {
+    public MethodExpression(Type type, Token token, String name, Expression recipient, List<Expression> arguments) {
         super(type, token);
-        this.name = token.getText();
+        this.name = name;
+        this.recipient = recipient;
         this.arguments = arguments;
+        this.argumentTypes = null;
     }
 
     /**
@@ -27,19 +37,170 @@
      *         been detected during inference/validation.
      */
     public boolean bind(Bindings bindings) {
-        // we just have to check that the arguemnt expressions ahve valid bindings
+        // check that the recipient and argument expressions have valid bindings
 
         boolean valid = true;
+        if (recipient != null) {
+            valid &= recipient.bind(bindings);
+        }
 
         Iterator<Expression> iterator = arguments.iterator();
 
-        while (iterator.hasNext()) {
+        while (valid && iterator.hasNext()) {
             valid &= iterator.next().bind(bindings);
         }
 
         return valid;
     }
 
+    public Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected) throws TypeException {
+        // if we have no recipient then we use the rule's helper as a target via a binding
+        // to $-1. this means  we can type check the call against methods of class Rule$Helper
+        // without having to do any special case processing.
+
+        if (recipient == null) {
+            Type ruleType = typegroup.create("org.jboss.jbossts.orchestration.rule.Rule$Helper");
+            recipient = new DollarExpression(ruleType, token, "$-1");
+        }
+        // check the recipient type is defined and then look for a relevant method
+
+        Type recipientType = recipient.typeCheck(bindings, typegroup,  Type.UNDEFINED);
+        Class clazz = recipientType.getTargetClass();
+        // if we can find a unique method then we can use it to type the parameters
+        // otherwise we do it the hard way
+        int arity = arguments.size();
+        Method[] methods = clazz.getMethods();
+        List<Method> candidates = new ArrayList<Method>();
+        boolean duplicates = false;
+
+        for (Method method : methods) {
+            if (method.getName().equals(name) &&
+                    method.getParameterTypes().length == arity) {
+                candidates.add(method);
+            }
+        }
+
+        argumentTypes = new ArrayList<Type>();
+        
+        // check each argument in turn -- if all candidates have the same argument type then
+        // use that as the type to check against
+        for (int i = 0; i < arguments.size() ; i++) {
+            if (candidates.isEmpty()) {
+                throw new TypeException("MethodExpression.typeCheck : invalid method " + name + " for target class " + recipientType.getName() + getPos());
+            }
+
+            // TODO get and prune operations do not allow for coercion but type check does!
+            // e.g. the parameter type may be int and the arg type float
+            // or the parameter type may be String and the arg type class Foo
+            // reimplement this using type inter-assignability to do the pruning
+
+            Class candidateClass = getCandidateArgClass(candidates, i);
+            Type candidateType;
+            if (candidateClass != null) {
+                candidateType = typegroup.ensureType(candidateClass);
+            } else {
+                candidateType = Type.UNDEFINED;
+            }
+            Type argType = arguments.get(i).typeCheck(bindings, typegroup, candidateType);
+            argumentTypes.add(argType);
+            if (candidateType == Type.UNDEFINED) {
+                // we had several methods to choose from
+                candidates = pruneCandidates(candidates, i, argType.getTargetClass());
+            }
+        }
+
+        if (candidates.isEmpty()) {
+            throw new TypeException("MethodExpression.typeCheck : invalid method " + name + " for target class " + recipientType.getName() + getPos());
+        }
+        
+        if (candidates.size() > 1) {
+            throw new TypeException("MethodExpression.typeCheck : ambiguous method signature " + name + " for target class " + recipientType.getName() + getPos());
+        }
+
+        method = candidates.get(0);
+
+        type = typegroup.ensureType(method.getReturnType());
+
+        if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
+            throw new TypeException("FieldExpresssion.typeCheck : invalid expected type " + expected.getName() + getPos());
+        }
+
+        return type;
+    }
+
+    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+        Object recipientValue = null;
+        try {
+            if (recipient != null) {
+                recipientValue = recipient.interpret(helper);
+                if (recipientValue == null) {
+                    throw new ExecuteException("MethodExpression.interpret : null recipient for method " + token.getText() + getPos());
+                }
+            }
+            int argCount = arguments.size();
+
+            Object[] argValues = new Object[argCount];
+            for (int i = 0; i < argCount; i++) {
+                argValues[i] = arguments.get(i).interpret(helper);
+            }
+
+            return method.invoke(recipientValue, argValues);
+        } catch (ExecuteException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ExecuteException("MethodExpression.interpret : exception invoking method " + token.getText() + getPos(), e);
+        }
+    }
+
+    public Class getCandidateArgClass(List<Method> candidates, int argIdx)
+    {
+        Class argClazz = null;
+
+        for (Method m : candidates) {
+            Class nextClazz = m.getParameterTypes()[argIdx];
+            if (argClazz == null) {
+                argClazz = nextClazz;
+            } else if (argClazz != nextClazz) {
+                return null;
+            }
+        }
+
+        return argClazz;
+    }
+
+    public List<Method> pruneCandidates(List<Method> candidates, int argIdx, Class argClazz)
+    {
+        for (int i = 0; i < candidates.size();) {
+            Method m = candidates.get(i);
+            Class nextClazz = m.getParameterTypes()[argIdx];
+            if (nextClazz != argClazz) {
+                candidates.remove(i);
+            } else {
+                i++;
+            }
+        }
+        return candidates;
+    }
+
+    public void writeTo(StringWriter stringWriter) {
+        if (recipient != null) {
+            recipient.writeTo(stringWriter);
+            stringWriter.write(".");
+        }
+        stringWriter.write(name);
+        stringWriter.write("(");
+        String sepr = "";
+        for (Expression arg : arguments) {
+            stringWriter.write(sepr);
+            arg.writeTo(stringWriter);
+            sepr = ", ";
+        }
+        stringWriter.write(")");
+    }
+
     private String name;
     private List<Expression> arguments;
+    private List<Type> argumentTypes;
+    private Expression recipient;
+    private Method method;
 }

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/NotExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/NotExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/NotExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,6 +1,11 @@
 package org.jboss.jbossts.orchestration.rule.expression;
 
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
+import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
 import org.antlr.runtime.Token;
 
 /**
@@ -11,4 +16,18 @@
     {
         super(NOT, Type.BOOLEAN, token, operand);
     }
+
+    public Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected)
+    throws TypeException {
+        type = getOperand(0).typeCheck(bindings, typegroup, Type.Z);
+        if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
+            throw new TypeException("NotExpression.typeCheck() : invalid result type : " + expected.getName() + getPos());
+        }
+        return type;
+    }
+
+    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+        Boolean result = (Boolean) getOperand(0).interpret(helper);
+        return !result;
+    }
 }
\ No newline at end of file

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/NumericLiteral.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/NumericLiteral.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/NumericLiteral.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,16 +1,22 @@
 package org.jboss.jbossts.orchestration.rule.expression;
 
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
 import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
 import org.antlr.runtime.Token;
 
+import java.io.StringWriter;
+
 /**
  */
 public class NumericLiteral extends Expression
 {
 
     public NumericLiteral(Token token) {
-        super(checkType(token.getText()), token);
+        super(check(token.getText()), token);
 
         this.text = token.getText();
     }
@@ -43,7 +49,22 @@
         return true;
     }
 
-    private static Type checkType(String text)
+    public Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected) throws TypeException {
+        if (!expected.isNumeric()) {
+            throw new TypeException("NumericLiteral.typeCheck : invalid expected type " + expected.getName() + getPos());            
+        }
+        return type;
+    }
+
+    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+        return value;
+    }
+
+    public void writeTo(StringWriter stringWriter) {
+        stringWriter.write(text);
+    }
+
+    private static Type check(String text)
     {
         if (text.contains("e") | text.contains("E") | text.contains(".")) {
             return checkFloat(text);

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/OperExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/OperExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/OperExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -2,8 +2,11 @@
 
 import org.antlr.runtime.Token;
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ParseException;
 
 import java.util.Iterator;
+import java.io.StringWriter;
 
 /**
  * generic operator expression subsumes unary, binary and ternary operators
@@ -26,6 +29,56 @@
      */
     public abstract Expression getOperand(int index);
 
+    public void writeTo(StringWriter stringWriter) {
+        if ((oper & UNARY) != 0) {
+            stringWriter.write(getOperandString());
+            stringWriter.write("(");
+            getOperand(0).writeTo(stringWriter);
+            stringWriter.write(")");
+        } else if ((oper & BINARY) != 0) {
+            stringWriter.write("(");
+            getOperand(0).writeTo(stringWriter);
+            stringWriter.write(" ");
+            stringWriter.write(getOperandString());
+            stringWriter.write(" ");
+            getOperand(1).writeTo(stringWriter);
+            stringWriter.write(")");
+        } else if (oper == IF) {
+            // we only have one ternary operator
+            stringWriter.write("(");
+            getOperand(0).writeTo(stringWriter);
+            stringWriter.write(" ? ");
+            getOperand(1).writeTo(stringWriter);
+            stringWriter.write(" : ");
+            getOperand(2).writeTo(stringWriter);
+            stringWriter.write(")");
+        } else {
+            stringWriter.write("*** error unknown operator *** " + oper);
+        }
+    }
+
+    private String getOperandString()
+    {
+        for (int i = 0; i < operands.length; i++)
+        {
+            if (operands[i] == oper) {
+                return operandNames[i];
+            }
+        }
+        return "*** error unknown operator ***";
+    }
+
+    public static int convertOper(int parserOper)
+    {
+        for (int i = 0; i < parserOperands.length; i++) {
+            if (parserOperands[i] == parserOper) {
+                return operands[i];
+            }
+        }
+
+        return -1;
+    }
+
     final public static int UNARY       = 0x1000;
     final public static int BINARY      = 0x2000;
     final public static int TERNARY     = 0x4000;
@@ -55,4 +108,72 @@
     final public static int MOD         = 0x0205 | BINARY;
 
     final public static int IF          = 0x0400 | TERNARY;
+
+    final private static int[] operands = {
+            NOT,
+            TWIDDLE,
+            OR,
+            AND,
+            EQ,
+            NEQ,
+            GT,
+            LT,
+            GEQ,
+            LEQ,
+            BOR,
+            BAND,
+            BXOR,
+            MUL,
+            DIV,
+            PLUS,
+            MINUS,
+            MOD,
+            IF
+    };
+
+    /* parser operands ar enot allocated rationally so we convert usingthis table */
+
+    final private static int[] parserOperands = {
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.NOT,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.TWIDDLE,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.OR,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.AND,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.EQ,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.NEQ,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.GT,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.LT,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.GEQ,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.LEQ,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.BOR,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.BAND,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.BXOR,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.MUL,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.DIV,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.PLUS,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.MINUS,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.MOD,
+            org.jboss.jbossts.orchestration.rule.grammar.ECAGrammarParser.IF
+    };
+
+    final private static String[] operandNames = {
+            "!",
+            "~",
+            "||",
+            "&&",
+            "==",
+            "!=",
+            ">",
+            "<",
+            ">=",
+            "<=",
+            "|",
+            "&",
+            "^",
+            "*",
+            "/",
+            "+",
+            "-",
+            "%",
+            "? :"
+    };
 }

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/PlusExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/PlusExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/PlusExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,6 +1,11 @@
 package org.jboss.jbossts.orchestration.rule.expression;
 
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
+import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
 import org.antlr.runtime.Token;
 
 /**
@@ -14,4 +19,68 @@
     {
         super(PLUS, Type.UNDEFINED, token, left, right);
     }
+
+    public Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected) throws TypeException {
+        // if the expected type is numeric then we know this must be an arithmetic plus
+        // if it is string then this could still be an arithmetic plus so we will
+        // have to rely on the type of the first argument to guide us
+
+        Type type1 = getOperand(0).typeCheck(bindings, typegroup, (expected.isNumeric() ? expected : Type.UNDEFINED));
+        Type type2;
+
+        if (type1.isNumeric()) {
+            type2 = getOperand(1).typeCheck(bindings, typegroup, Type.N);
+            type = Type.promote(type1, type2);
+        } else if (type1.isString()) {
+            type2 = getOperand(1).typeCheck(bindings, typegroup, Type.STRING);
+        } else {
+            throw new TypeException("PlusExpression.typeCheck : invalid argument type " + type1.getName() + getPos());
+        }
+
+        return type;
+    }
+
+    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+        Object value1 = getOperand(0).interpret(helper);
+        Object value2 = getOperand(1).interpret(helper);
+        if (type == Type.S) {
+            String s1 = (String)value1;
+            String s2 = (String)value2;
+            return s1 + s2;
+        } else {
+            Number n1 = (Number)value1;
+            Number n2 = (Number)value2;
+            if (type == Type.B) {
+                byte b1 = n1.byteValue();
+                byte b2 = n2.byteValue();
+                byte result = (byte)(b1 + b2);
+                return new Byte(result);
+            } else if (type == Type.S) {
+                short s1 = n1.shortValue();
+                short s2 = n2.shortValue();
+                short result = (short)(s1 + s2);
+                return new Short(result);
+            }  else if (type == Type.I) {
+                int i1 = n1.intValue();
+                int i2 = n2.intValue();
+                int result = (i1 + i2);
+                return new Integer(result);
+            } else if (type == Type.F) {
+                float f1 = n1.floatValue();
+                float f2 = n2.floatValue();
+                float result = (f1 + f2);
+                return new Float(result);
+            } else if (type == Type.D) {
+                double d1 = n1.doubleValue();
+                double d2 = n2.doubleValue();
+                double result = (d1 + d2);
+                return new Double(result);
+            } else { // type == Type.C
+                char c1 = (char)n1.intValue();
+                char c2 = (char)n2.intValue();
+                char result = (char)(c1 + c2);
+                return new Integer(result);
+            }
+        }
+    }
 }
\ No newline at end of file

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StaticExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StaticExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StaticExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,20 +1,27 @@
 package org.jboss.jbossts.orchestration.rule.expression;
 
 import org.jboss.jbossts.orchestration.rule.binding.Bindings;
-import org.jboss.jbossts.orchestration.rule.binding.Binding;
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
 import org.antlr.runtime.Token;
 
+import java.io.StringWriter;
+import java.lang.reflect.Field;
+
 /**
  * an expression which identifies aa static field reference
  */
 public class StaticExpression extends Expression
 {
-    public StaticExpression(Type type, Token token, String clazzName, String fieldName) {
+    public StaticExpression(Type type, Token token, String[] path) {
         // type is the type of static field
         super(type, token);
-        this.clazzName = clazzName;
-        this.fieldName = fieldName;
+        this.path = path;
+        this.fieldNames = null;
+        this.ownerTypes = null;
     }
 
     /**
@@ -32,6 +39,108 @@
         return true;
     }
 
-    private String fieldName;
-    private String clazzName;
+    public Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected) throws TypeException {
+        // look for a class whose name matches some initial segment of path
+        Type rootType = typegroup.match(path);
+        if (rootType == null) {
+            throw new TypeException("StaticExpression.typeCheck : invalid path to static field " + getPath(path.length) + getPos());
+        }
+        // find out which path elements constitute the type name
+        int length = rootType.getName().length();
+        int pathLength = path.length;
+        int idx = 0;
+        int count;
+
+        for (count = 0; count < length;) {
+            if (idx != 0) {
+                count += 1;
+            }
+            count += path[idx++].length();
+        }
+
+        // we need at least one left over element for the field name
+
+        if (idx < pathLength - 1) {
+            throw new TypeException("StaticExpression.typeCheck : invalid static field name " + getPath(path.length) + getPos());
+        }
+
+        int fieldCount = pathLength - idx;
+        fieldNames = new String[fieldCount];
+        fields = new Field[fieldCount];
+        ownerTypes = new Type[fieldCount];
+        int fieldIdx;
+        for (fieldIdx = 0; fieldIdx < fieldCount; fieldIdx++) {
+            fieldNames[fieldIdx] = path[idx++];
+        }
+        Type valueType = rootType;
+        for (idx = 0; idx < fieldCount; idx++)
+        {
+            ownerTypes[idx] = valueType;
+            Class clazz = valueType.getTargetClass();
+            try {
+                fields[idx] = clazz.getField(fieldNames[idx]);
+            } catch (NoSuchFieldException e) {
+                // oops
+                throw new TypeException("StaticExpression.typeCheck : invalid field name " + fieldNames[idx] + getPos());
+            }
+            clazz = fields[idx].getType();
+            valueType = typegroup.ensureType(clazz);
+        }
+        type = valueType;
+        if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
+            throw new TypeException("StaticExpression.typeCheck : invalid expected return type " + expected.getName() + getPos());
+        }
+        return type;
+    }
+
+    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+        try {
+            Object value = fields[0].get(null);
+
+            for (int i = 1; i < ownerTypes.length; i++) {
+                if (value == null) {
+                    throw new ExecuteException("StaticExpression.interpret : attempted field indirection through null value " + token.getText() + getPos());
+                }
+                value = fields[i].get(value);
+            }
+            return value;
+        } catch (ExecuteException e) {
+            throw e;
+        } catch (IllegalAccessException e) {
+            throw new ExecuteException("StaticExpression.interpret : error accessing field " + token.getText() + getPos(), e);
+        } catch (Exception e) {
+            throw new ExecuteException("StaticExpression.interpret : unexpected exception accessing field " + token.getText() + getPos(), e);
+        }
+    }
+
+    public String getPath(int len)
+    {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(path[0]);
+
+        for (int i = 1; i < len; i++) {
+            buffer.append(".");
+            buffer.append(path[i]);
+        }
+        return buffer.toString();
+    }
+
+    public void writeTo(StringWriter stringWriter) {
+        String sepr = "";
+        for (String element : path) {
+            stringWriter.write(sepr);
+            stringWriter.write(element);
+            sepr = ".";
+        }
+    }
+
+    /**
+     * the list of path components which may include package qualifiers, the class name, the
+     * field name and subordinate field references
+     */
+
+    private String path[];
+    private String fieldNames[];
+    private Field fields[];
+    private Type ownerTypes[];
 }
\ No newline at end of file

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StringLiteral.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StringLiteral.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StringLiteral.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,9 +1,15 @@
 package org.jboss.jbossts.orchestration.rule.expression;
 
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
 import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
 import org.antlr.runtime.Token;
 
+import java.io.StringWriter;
+
 /**
  * an expression which identifies a character string.
  */
@@ -29,5 +35,17 @@
         return true;
     }
 
+    public Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected) throws TypeException {
+        return type;
+    }
+
+    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+        return text;
+    }
+
+    public void writeTo(StringWriter stringWriter) {
+        stringWriter.write(text);
+    }
+
     private String text;
 }

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StringPlusExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StringPlusExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/StringPlusExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,6 +1,11 @@
 package org.jboss.jbossts.orchestration.rule.expression;
 
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
+import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
 import org.antlr.runtime.Token;
 
 /**
@@ -12,4 +17,24 @@
     {
         super(PLUS, Type.STRING, token, left, right);
     }
+
+    public Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected) throws TypeException {
+        // first type must be a string -- second may be anything but expect
+        // a string to indicate that it must be assignable evn if only by conversion
+        Type type1 = getOperand(0).typeCheck(bindings, typegroup,  Type.STRING);
+        Type type2 = getOperand(1).typeCheck(bindings, typegroup,  Type.STRING);
+        // result will always be a String
+        if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(Type.STRING)) {
+            throw new TypeException("StringPlusExpression.typeCheck : invalid expected result type " + expected.getName() + getPos());
+        }
+        return type;
+    }
+
+    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+        Object value1 = getOperand(0).interpret(helper);
+        Object value2 = getOperand(1).interpret(helper);
+        String string1 = value1.toString();
+        String string2 = value2.toString();
+        return string1 + string2;
+    }
 }
\ No newline at end of file

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/TernaryOperExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/TernaryOperExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/TernaryOperExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -2,12 +2,13 @@
 
 import org.jboss.jbossts.orchestration.rule.type.Type;
 import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.antlr.runtime.Token;
 
 /**
  * ternary operators includes conditional evaluation operator 'cond ? if_expr : else_expr'
  */
-public class TernaryOperExpression extends OperExpression
+public abstract class TernaryOperExpression extends OperExpression
 {
     public TernaryOperExpression(int oper, Type type, Token token, Expression operand1,
                                  Expression operand2, Expression operand3)

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/TwiddleExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/TwiddleExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/TwiddleExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,6 +1,11 @@
 package org.jboss.jbossts.orchestration.rule.expression;
 
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
+import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
 import org.antlr.runtime.Token;
 
 /**
@@ -11,4 +16,42 @@
     {
         super(TWIDDLE, operand.getType(), token, operand);
     }
+
+    public Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected)
+    throws TypeException {
+        type = getOperand(0).typeCheck(bindings, typegroup, Type.N);
+        if (type == Type.F || type == Type.D) {
+            type = Type.J;
+        }
+        if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
+            throw new TypeException("TwiddleExpression.typeCheck() : invalid result type : " + expected.getName() + getPos());
+        }
+        return type;
+    }
+
+    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+        try {
+            Number value = (Number)getOperand(0).interpret(helper);
+
+            if (type == Type.B) {
+                return (byte)~value.intValue();
+            } else if (type == Type.S) {
+                return (short)~value.intValue();
+            } else if (type == Type.I) {
+                return ~value.intValue();
+            } else if (type == Type.J) {
+                return ~value.longValue();
+            } else if (type == Type.F) {
+                return ~value.longValue();
+            } else if (type == Type.D) {
+                return ~value.longValue();
+            } else { // (type == Type.C)
+                return ~value.intValue();
+            }
+        } catch (ExecuteException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ExecuteException("TwiddleExpression.typeCheck() : unexpected exception : " + token.getText() + getPos(), e);
+        }
+    }
 }

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/UnaryOperExpression.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/UnaryOperExpression.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/UnaryOperExpression.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -2,14 +2,17 @@
 
 import org.jboss.jbossts.orchestration.rule.type.Type;
 import org.jboss.jbossts.orchestration.rule.binding.Bindings;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
 import org.antlr.runtime.Token;
 
+import java.io.StringWriter;
+
 /**
  * unary operators includes boolean NOT and arithmetic TWIDDLE
  * n.b. unary MINUS is not currently supported except as part of number
  * parsing
  */
-public class UnaryOperExpression extends OperExpression
+public abstract class UnaryOperExpression extends OperExpression
 {
     public UnaryOperExpression(int oper, Type type, Token token, Expression operand)
     {

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/Variable.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/Variable.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/expression/Variable.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -3,8 +3,14 @@
 import org.jboss.jbossts.orchestration.rule.binding.Bindings;
 import org.jboss.jbossts.orchestration.rule.binding.Binding;
 import org.jboss.jbossts.orchestration.rule.type.Type;
+import org.jboss.jbossts.orchestration.rule.type.TypeGroup;
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+import org.jboss.jbossts.orchestration.rule.Rule;
 import org.antlr.runtime.Token;
 
+import java.io.StringWriter;
+
 /**
  * an expression which identifies a variable occurring either as an LVALUE on the LHS of an event
  * binding in the rule's event or as an RVALUE mentioned in the RHS of an event binding or in thre
@@ -17,6 +23,11 @@
         this.name = token.getText();
     }
 
+    public Variable(Type type, Token token, String name) {
+        super(type, token);
+        this.name = name;
+    }
+
     /**
      * verify that variables mentioned in this expression are actually available in the supplied
      * bindings list and infer/validate the type of this expression or its subexpressions
@@ -35,17 +46,47 @@
             System.err.println("VarExpresssion.bind : unbound variable " + name + getPos());                
             return false;
         }
-        // if the binding has a defined type and this has an undefined one then adopt it
+        // if this type is undefined then adopt the type of the binding so that this
+        // variable gets typed whenever the binding does otherwise ensure the types are
+        // compatible
 
+        type = Type.dereference(type);
+        
         if (type.isUndefined()) {
+            this.type = binding.getType();
+        } else {
             Type bindingType = binding.getType();
-            if (!bindingType.isUndefined()) {
-                this.type = bindingType;
+
+            if (!bindingType.isAssignableFrom(this.type)) {
+                System.err.println("VarExpresssion.bind : type " + this.type.getName() + " of variable " + name + " conflicts with bound type " + bindingType.getName() + getPos());                
+                return false;
             }
         }
 
         return true;
     }
 
+    public Type typeCheck(Bindings bindings, TypeGroup typegroup, Type expected) throws TypeException {
+        // type must be defined by now or we are in trouble
+
+        type = Type.dereference(type);
+
+        if (type.isUndefined()) {
+            throw new TypeException("Variable.typeCheck : unable to derive type for variable " + name +  getPos());
+        }
+        if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
+            throw new TypeException("Variable.typeCheck() : invalid result type : " + expected.getName() + getPos());
+        }
+        return type;
+    }
+
+    public Object interpret(Rule.BasicHelper helper) throws ExecuteException {
+        return helper.getBinding(name);
+    }
+
+    public void writeTo(StringWriter stringWriter) {
+        stringWriter.write(name);
+    }
+
     private String name;
 }

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/grammar/ECAGrammarParser.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/grammar/ECAGrammarParser.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/grammar/ECAGrammarParser.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,4 +1,4 @@
-// $ANTLR 3.0.1 dd/grammar/ECAGrammar.g 2008-07-18 14:16:03
+// $ANTLR 3.0.1 dd/grammar/ECAGrammar.g 2008-09-15 14:07:44
 
 package org.jboss.jbossts.orchestration.rule.grammar;
 
@@ -14,7 +14,7 @@
 
 public class ECAGrammarParser extends Parser {
     public static final String[] tokenNames = new String[] {
-        "<invalid>", "<EOR>", "<DOWN>", "<UP>", "DIGIT", "POSDIGIT", "SIGN", "BAREINT", "INTEGER", "POINT", "EXPPART", "FLOAT", "NUMBER", "WHEN", "IF", "DO", "LPAREN", "RPAREN", "LSQUARE", "RSQUARE", "LBRACE", "RBRACE", "SEPR", "DOT", "ASSIGN", "OR", "AND", "NOT", "EQ", "NEQ", "GT", "LT", "GEQ", "LEQ", "BOR", "BAND", "BXOR", "TWIDDLE", "MUL", "DIV", "PLUS", "MINUS", "MOD", "TERN_IF", "COLON", "LETTER", "UNDERSCORE", "QUOTE", "DQUOTE", "SPACE", "NEWLINE", "PUNCT", "STRING", "BARESYM", "QUOTSYM", "DOTSYM", "SYMBOL", "DOLLAR", "DOLLARSYM", "WS", "Tokens", "UNOP", "BINOP", "TERNOP", "METH", "ARRAY", "NUM_LIT", "STRING_LIT"
+        "<invalid>", "<EOR>", "<DOWN>", "<UP>", "DIGIT", "POSDIGIT", "SIGN", "BAREINT", "INTEGER", "POINT", "EXPPART", "FLOAT", "NUMBER", "BIND", "IF", "DO", "LPAREN", "RPAREN", "LSQUARE", "RSQUARE", "LBRACE", "RBRACE", "SEPR", "DOT", "ASSIGN", "OR", "AND", "NOT", "EQ", "NEQ", "GT", "LT", "GEQ", "LEQ", "BOR", "BAND", "BXOR", "TWIDDLE", "MUL", "DIV", "PLUS", "MINUS", "MOD", "TERN_IF", "COLON", "LETTER", "UNDERSCORE", "QUOTE", "DQUOTE", "SPACE", "NEWLINE", "PUNCT", "STRING", "BARESYM", "QUOTSYM", "DOTSYM", "SYMBOL", "DOLLAR", "DOLLARSYM", "WS", "Tokens", "UNOP", "BINOP", "TERNOP", "METH", "ARRAY", "NUM_LIT", "STRING_LIT"
     };
     public static final int MINUS=41;
     public static final int ARRAY=65;
@@ -54,7 +54,6 @@
     public static final int LSQUARE=18;
     public static final int DO=15;
     public static final int POINT=9;
-    public static final int WHEN=13;
     public static final int BARESYM=53;
     public static final int SEPR=22;
     public static final int WS=59;
@@ -78,13 +77,14 @@
     public static final int DIV=39;
     public static final int DOTSYM=55;
     public static final int BXOR=36;
+    public static final int BIND=13;
     public static final int NOT=27;
     public static final int UNDERSCORE=46;
     public static final int DOLLARSYM=58;
 
         public ECAGrammarParser(TokenStream input) {
             super(input);
-            ruleMemo = new HashMap[53+1];
+            ruleMemo = new HashMap[54+1];
          }
         
     protected TreeAdaptor adaptor = new CommonTreeAdaptor();
@@ -106,7 +106,7 @@
     };
 
     // $ANTLR start eca_rule
-    // dd/grammar/ECAGrammar.g:26:1: eca_rule : eca EOF ;
+    // dd/grammar/ECAGrammar.g:26:1: eca_rule : eca EOF -> ^( eca ) ;
     public final eca_rule_return eca_rule() throws RecognitionException {
         eca_rule_return retval = new eca_rule_return();
         retval.start = input.LT(1);
@@ -118,27 +118,49 @@
 
 
         Object EOF2_tree=null;
-
+        RewriteRuleTokenStream stream_EOF=new RewriteRuleTokenStream(adaptor,"token EOF");
+        RewriteRuleSubtreeStream stream_eca=new RewriteRuleSubtreeStream(adaptor,"rule eca");
         try {
-            // dd/grammar/ECAGrammar.g:26:10: ( eca EOF )
+            // dd/grammar/ECAGrammar.g:26:10: ( eca EOF -> ^( eca ) )
             // dd/grammar/ECAGrammar.g:26:12: eca EOF
             {
-            root_0 = (Object)adaptor.nil();
-
             pushFollow(FOLLOW_eca_in_eca_rule86);
             eca1=eca();
             _fsp--;
             if (failed) return retval;
-            if ( backtracking==0 ) adaptor.addChild(root_0, eca1.getTree());
+            if ( backtracking==0 ) stream_eca.add(eca1.getTree());
             EOF2=(Token)input.LT(1);
             match(input,EOF,FOLLOW_EOF_in_eca_rule88); if (failed) return retval;
+            if ( backtracking==0 ) stream_EOF.add(EOF2);
+
+
+            // AST REWRITE
+            // elements: eca
+            // token labels: 
+            // rule labels: retval
+            // token list labels: 
+            // rule list labels: 
             if ( backtracking==0 ) {
-            EOF2_tree = (Object)adaptor.create(EOF2);
-            adaptor.addChild(root_0, EOF2_tree);
+            retval.tree = root_0;
+            RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
+
+            root_0 = (Object)adaptor.nil();
+            // 26:20: -> ^( eca )
+            {
+                // dd/grammar/ECAGrammar.g:26:23: ^( eca )
+                {
+                Object root_1 = (Object)adaptor.nil();
+                root_1 = (Object)adaptor.becomeRoot(stream_eca.nextNode(), root_1);
+
+                adaptor.addChild(root_0, root_1);
+                }
+
             }
 
             }
 
+            }
+
             retval.stop = input.LT(-1);
 
             if ( backtracking==0 ) {
@@ -156,194 +178,338 @@
     }
     // $ANTLR end eca_rule
 
-    public static class eca_return extends ParserRuleReturnScope {
+    public static class eca_event_return extends ParserRuleReturnScope {
         Object tree;
         public Object getTree() { return tree; }
     };
 
-    // $ANTLR start eca
-    // dd/grammar/ECAGrammar.g:28:1: eca : ( WHEN event -> ^( WHEN event ) | IF condition -> ^( IF condition ) | DO action -> ^( DO action ) );
-    public final eca_return eca() throws RecognitionException {
-        eca_return retval = new eca_return();
+    // $ANTLR start eca_event
+    // dd/grammar/ECAGrammar.g:29:1: eca_event : event EOF -> ^( event ) ;
+    public final eca_event_return eca_event() throws RecognitionException {
+        eca_event_return retval = new eca_event_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        Token WHEN3=null;
-        Token IF5=null;
-        Token DO7=null;
-        event_return event4 = null;
+        Token EOF4=null;
+        event_return event3 = null;
 
-        condition_return condition6 = null;
 
-        action_return action8 = null;
+        Object EOF4_tree=null;
+        RewriteRuleTokenStream stream_EOF=new RewriteRuleTokenStream(adaptor,"token EOF");
+        RewriteRuleSubtreeStream stream_event=new RewriteRuleSubtreeStream(adaptor,"rule event");
+        try {
+            // dd/grammar/ECAGrammar.g:29:11: ( event EOF -> ^( event ) )
+            // dd/grammar/ECAGrammar.g:29:13: event EOF
+            {
+            pushFollow(FOLLOW_event_in_eca_event105);
+            event3=event();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) stream_event.add(event3.getTree());
+            EOF4=(Token)input.LT(1);
+            match(input,EOF,FOLLOW_EOF_in_eca_event107); if (failed) return retval;
+            if ( backtracking==0 ) stream_EOF.add(EOF4);
 
 
-        Object WHEN3_tree=null;
-        Object IF5_tree=null;
-        Object DO7_tree=null;
-        RewriteRuleTokenStream stream_DO=new RewriteRuleTokenStream(adaptor,"token DO");
-        RewriteRuleTokenStream stream_WHEN=new RewriteRuleTokenStream(adaptor,"token WHEN");
-        RewriteRuleTokenStream stream_IF=new RewriteRuleTokenStream(adaptor,"token IF");
-        RewriteRuleSubtreeStream stream_action=new RewriteRuleSubtreeStream(adaptor,"rule action");
-        RewriteRuleSubtreeStream stream_event=new RewriteRuleSubtreeStream(adaptor,"rule event");
+            // AST REWRITE
+            // elements: event
+            // token labels: 
+            // rule labels: retval
+            // token list labels: 
+            // rule list labels: 
+            if ( backtracking==0 ) {
+            retval.tree = root_0;
+            RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
+
+            root_0 = (Object)adaptor.nil();
+            // 29:23: -> ^( event )
+            {
+                // dd/grammar/ECAGrammar.g:29:26: ^( event )
+                {
+                Object root_1 = (Object)adaptor.nil();
+                root_1 = (Object)adaptor.becomeRoot(stream_event.nextNode(), root_1);
+
+                adaptor.addChild(root_0, root_1);
+                }
+
+            }
+
+            }
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+        catch (RecognitionException re) {
+            reportError(re);
+            recover(input,re);
+        }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end eca_event
+
+    public static class eca_condition_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start eca_condition
+    // dd/grammar/ECAGrammar.g:32:1: eca_condition : condition EOF -> ^( condition ) ;
+    public final eca_condition_return eca_condition() throws RecognitionException {
+        eca_condition_return retval = new eca_condition_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token EOF6=null;
+        condition_return condition5 = null;
+
+
+        Object EOF6_tree=null;
+        RewriteRuleTokenStream stream_EOF=new RewriteRuleTokenStream(adaptor,"token EOF");
         RewriteRuleSubtreeStream stream_condition=new RewriteRuleSubtreeStream(adaptor,"rule condition");
         try {
-            // dd/grammar/ECAGrammar.g:28:5: ( WHEN event -> ^( WHEN event ) | IF condition -> ^( IF condition ) | DO action -> ^( DO action ) )
-            int alt1=3;
-            switch ( input.LA(1) ) {
-            case WHEN:
+            // dd/grammar/ECAGrammar.g:32:15: ( condition EOF -> ^( condition ) )
+            // dd/grammar/ECAGrammar.g:32:17: condition EOF
+            {
+            pushFollow(FOLLOW_condition_in_eca_condition123);
+            condition5=condition();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) stream_condition.add(condition5.getTree());
+            EOF6=(Token)input.LT(1);
+            match(input,EOF,FOLLOW_EOF_in_eca_condition125); if (failed) return retval;
+            if ( backtracking==0 ) stream_EOF.add(EOF6);
+
+
+            // AST REWRITE
+            // elements: condition
+            // token labels: 
+            // rule labels: retval
+            // token list labels: 
+            // rule list labels: 
+            if ( backtracking==0 ) {
+            retval.tree = root_0;
+            RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
+
+            root_0 = (Object)adaptor.nil();
+            // 32:31: -> ^( condition )
+            {
+                // dd/grammar/ECAGrammar.g:32:34: ^( condition )
                 {
-                alt1=1;
+                Object root_1 = (Object)adaptor.nil();
+                root_1 = (Object)adaptor.becomeRoot(stream_condition.nextNode(), root_1);
+
+                adaptor.addChild(root_0, root_1);
                 }
-                break;
-            case IF:
-                {
-                alt1=2;
-                }
-                break;
-            case DO:
-                {
-                alt1=3;
-                }
-                break;
-            default:
-                if (backtracking>0) {failed=true; return retval;}
-                NoViableAltException nvae =
-                    new NoViableAltException("28:1: eca : ( WHEN event -> ^( WHEN event ) | IF condition -> ^( IF condition ) | DO action -> ^( DO action ) );", 1, 0, input);
 
-                throw nvae;
             }
 
-            switch (alt1) {
-                case 1 :
-                    // dd/grammar/ECAGrammar.g:28:7: WHEN event
-                    {
-                    WHEN3=(Token)input.LT(1);
-                    match(input,WHEN,FOLLOW_WHEN_in_eca97); if (failed) return retval;
-                    if ( backtracking==0 ) stream_WHEN.add(WHEN3);
+            }
 
-                    pushFollow(FOLLOW_event_in_eca99);
-                    event4=event();
-                    _fsp--;
-                    if (failed) return retval;
-                    if ( backtracking==0 ) stream_event.add(event4.getTree());
+            }
 
-                    // AST REWRITE
-                    // elements: event, WHEN
-                    // token labels: 
-                    // rule labels: retval
-                    // token list labels: 
-                    // rule list labels: 
-                    if ( backtracking==0 ) {
-                    retval.tree = root_0;
-                    RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
+            retval.stop = input.LT(-1);
 
-                    root_0 = (Object)adaptor.nil();
-                    // 28:18: -> ^( WHEN event )
-                    {
-                        // dd/grammar/ECAGrammar.g:28:21: ^( WHEN event )
-                        {
-                        Object root_1 = (Object)adaptor.nil();
-                        root_1 = (Object)adaptor.becomeRoot(stream_WHEN.next(), root_1);
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+        catch (RecognitionException re) {
+            reportError(re);
+            recover(input,re);
+        }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end eca_condition
 
-                        adaptor.addChild(root_1, stream_event.next());
+    public static class eca_action_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
 
-                        adaptor.addChild(root_0, root_1);
-                        }
+    // $ANTLR start eca_action
+    // dd/grammar/ECAGrammar.g:35:1: eca_action : action EOF -> ^( action ) ;
+    public final eca_action_return eca_action() throws RecognitionException {
+        eca_action_return retval = new eca_action_return();
+        retval.start = input.LT(1);
 
-                    }
+        Object root_0 = null;
 
-                    }
+        Token EOF8=null;
+        action_return action7 = null;
 
-                    }
-                    break;
-                case 2 :
-                    // dd/grammar/ECAGrammar.g:29:4: IF condition
-                    {
-                    IF5=(Token)input.LT(1);
-                    match(input,IF,FOLLOW_IF_in_eca112); if (failed) return retval;
-                    if ( backtracking==0 ) stream_IF.add(IF5);
 
-                    pushFollow(FOLLOW_condition_in_eca114);
-                    condition6=condition();
-                    _fsp--;
-                    if (failed) return retval;
-                    if ( backtracking==0 ) stream_condition.add(condition6.getTree());
+        Object EOF8_tree=null;
+        RewriteRuleTokenStream stream_EOF=new RewriteRuleTokenStream(adaptor,"token EOF");
+        RewriteRuleSubtreeStream stream_action=new RewriteRuleSubtreeStream(adaptor,"rule action");
+        try {
+            // dd/grammar/ECAGrammar.g:35:12: ( action EOF -> ^( action ) )
+            // dd/grammar/ECAGrammar.g:35:14: action EOF
+            {
+            pushFollow(FOLLOW_action_in_eca_action141);
+            action7=action();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) stream_action.add(action7.getTree());
+            EOF8=(Token)input.LT(1);
+            match(input,EOF,FOLLOW_EOF_in_eca_action143); if (failed) return retval;
+            if ( backtracking==0 ) stream_EOF.add(EOF8);
 
-                    // AST REWRITE
-                    // elements: IF, condition
-                    // token labels: 
-                    // rule labels: retval
-                    // token list labels: 
-                    // rule list labels: 
-                    if ( backtracking==0 ) {
-                    retval.tree = root_0;
-                    RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
 
-                    root_0 = (Object)adaptor.nil();
-                    // 29:18: -> ^( IF condition )
-                    {
-                        // dd/grammar/ECAGrammar.g:29:21: ^( IF condition )
-                        {
-                        Object root_1 = (Object)adaptor.nil();
-                        root_1 = (Object)adaptor.becomeRoot(stream_IF.next(), root_1);
+            // AST REWRITE
+            // elements: action
+            // token labels: 
+            // rule labels: retval
+            // token list labels: 
+            // rule list labels: 
+            if ( backtracking==0 ) {
+            retval.tree = root_0;
+            RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
 
-                        adaptor.addChild(root_1, stream_condition.next());
+            root_0 = (Object)adaptor.nil();
+            // 35:25: -> ^( action )
+            {
+                // dd/grammar/ECAGrammar.g:35:28: ^( action )
+                {
+                Object root_1 = (Object)adaptor.nil();
+                root_1 = (Object)adaptor.becomeRoot(stream_action.nextNode(), root_1);
 
-                        adaptor.addChild(root_0, root_1);
-                        }
+                adaptor.addChild(root_0, root_1);
+                }
 
-                    }
+            }
 
-                    }
+            }
 
-                    }
-                    break;
-                case 3 :
-                    // dd/grammar/ECAGrammar.g:30:4: DO action
-                    {
-                    DO7=(Token)input.LT(1);
-                    match(input,DO,FOLLOW_DO_in_eca128); if (failed) return retval;
-                    if ( backtracking==0 ) stream_DO.add(DO7);
+            }
 
-                    pushFollow(FOLLOW_action_in_eca130);
-                    action8=action();
-                    _fsp--;
-                    if (failed) return retval;
-                    if ( backtracking==0 ) stream_action.add(action8.getTree());
+            retval.stop = input.LT(-1);
 
-                    // AST REWRITE
-                    // elements: DO, action
-                    // token labels: 
-                    // rule labels: retval
-                    // token list labels: 
-                    // rule list labels: 
-                    if ( backtracking==0 ) {
-                    retval.tree = root_0;
-                    RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+        catch (RecognitionException re) {
+            reportError(re);
+            recover(input,re);
+        }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end eca_action
 
-                    root_0 = (Object)adaptor.nil();
-                    // 30:14: -> ^( DO action )
-                    {
-                        // dd/grammar/ECAGrammar.g:30:17: ^( DO action )
-                        {
-                        Object root_1 = (Object)adaptor.nil();
-                        root_1 = (Object)adaptor.becomeRoot(stream_DO.next(), root_1);
+    public static class eca_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
 
-                        adaptor.addChild(root_1, stream_action.next());
+    // $ANTLR start eca
+    // dd/grammar/ECAGrammar.g:38:1: eca : BIND e= event IF c= condition DO a= action -> ^( BIND $e $c $a) ;
+    public final eca_return eca() throws RecognitionException {
+        eca_return retval = new eca_return();
+        retval.start = input.LT(1);
 
-                        adaptor.addChild(root_0, root_1);
-                        }
+        Object root_0 = null;
 
-                    }
+        Token BIND9=null;
+        Token IF10=null;
+        Token DO11=null;
+        event_return e = null;
 
-                    }
+        condition_return c = null;
 
-                    }
-                    break;
+        action_return a = null;
 
+
+        Object BIND9_tree=null;
+        Object IF10_tree=null;
+        Object DO11_tree=null;
+        RewriteRuleTokenStream stream_DO=new RewriteRuleTokenStream(adaptor,"token DO");
+        RewriteRuleTokenStream stream_BIND=new RewriteRuleTokenStream(adaptor,"token BIND");
+        RewriteRuleTokenStream stream_IF=new RewriteRuleTokenStream(adaptor,"token IF");
+        RewriteRuleSubtreeStream stream_action=new RewriteRuleSubtreeStream(adaptor,"rule action");
+        RewriteRuleSubtreeStream stream_event=new RewriteRuleSubtreeStream(adaptor,"rule event");
+        RewriteRuleSubtreeStream stream_condition=new RewriteRuleSubtreeStream(adaptor,"rule condition");
+        try {
+            // dd/grammar/ECAGrammar.g:38:5: ( BIND e= event IF c= condition DO a= action -> ^( BIND $e $c $a) )
+            // dd/grammar/ECAGrammar.g:38:7: BIND e= event IF c= condition DO a= action
+            {
+            BIND9=(Token)input.LT(1);
+            match(input,BIND,FOLLOW_BIND_in_eca159); if (failed) return retval;
+            if ( backtracking==0 ) stream_BIND.add(BIND9);
+
+            pushFollow(FOLLOW_event_in_eca163);
+            e=event();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) stream_event.add(e.getTree());
+            IF10=(Token)input.LT(1);
+            match(input,IF,FOLLOW_IF_in_eca167); if (failed) return retval;
+            if ( backtracking==0 ) stream_IF.add(IF10);
+
+            pushFollow(FOLLOW_condition_in_eca171);
+            c=condition();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) stream_condition.add(c.getTree());
+            DO11=(Token)input.LT(1);
+            match(input,DO,FOLLOW_DO_in_eca175); if (failed) return retval;
+            if ( backtracking==0 ) stream_DO.add(DO11);
+
+            pushFollow(FOLLOW_action_in_eca179);
+            a=action();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) stream_action.add(a.getTree());
+
+            // AST REWRITE
+            // elements: e, BIND, c, a
+            // token labels: 
+            // rule labels: a, c, retval, e
+            // token list labels: 
+            // rule list labels: 
+            if ( backtracking==0 ) {
+            retval.tree = root_0;
+            RewriteRuleSubtreeStream stream_a=new RewriteRuleSubtreeStream(adaptor,"token a",a!=null?a.tree:null);
+            RewriteRuleSubtreeStream stream_c=new RewriteRuleSubtreeStream(adaptor,"token c",c!=null?c.tree:null);
+            RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
+            RewriteRuleSubtreeStream stream_e=new RewriteRuleSubtreeStream(adaptor,"token e",e!=null?e.tree:null);
+
+            root_0 = (Object)adaptor.nil();
+            // 40:15: -> ^( BIND $e $c $a)
+            {
+                // dd/grammar/ECAGrammar.g:40:18: ^( BIND $e $c $a)
+                {
+                Object root_1 = (Object)adaptor.nil();
+                root_1 = (Object)adaptor.becomeRoot(stream_BIND.next(), root_1);
+
+                adaptor.addChild(root_1, stream_e.next());
+                adaptor.addChild(root_1, stream_c.next());
+                adaptor.addChild(root_1, stream_a.next());
+
+                adaptor.addChild(root_0, root_1);
+                }
+
             }
+
+            }
+
+            }
+
             retval.stop = input.LT(-1);
 
             if ( backtracking==0 ) {
@@ -367,28 +533,28 @@
     };
 
     // $ANTLR start event
-    // dd/grammar/ECAGrammar.g:35:1: event : bindings ;
+    // dd/grammar/ECAGrammar.g:45:1: event : bindings ;
     public final event_return event() throws RecognitionException {
         event_return retval = new event_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        bindings_return bindings9 = null;
+        bindings_return bindings12 = null;
 
 
 
         try {
-            // dd/grammar/ECAGrammar.g:35:7: ( bindings )
-            // dd/grammar/ECAGrammar.g:35:9: bindings
+            // dd/grammar/ECAGrammar.g:45:7: ( bindings )
+            // dd/grammar/ECAGrammar.g:45:9: bindings
             {
             root_0 = (Object)adaptor.nil();
 
-            pushFollow(FOLLOW_bindings_in_event150);
-            bindings9=bindings();
+            pushFollow(FOLLOW_bindings_in_event206);
+            bindings12=bindings();
             _fsp--;
             if (failed) return retval;
-            if ( backtracking==0 ) adaptor.addChild(root_0, bindings9.getTree());
+            if ( backtracking==0 ) adaptor.addChild(root_0, bindings12.getTree());
 
             }
 
@@ -415,43 +581,43 @@
     };
 
     // $ANTLR start bindings
-    // dd/grammar/ECAGrammar.g:40:1: bindings : ( binding SEPR bindings -> ^( SEPR binding bindings ) | binding );
+    // dd/grammar/ECAGrammar.g:50:1: bindings : ( binding SEPR bindings -> ^( SEPR binding bindings ) | binding );
     public final bindings_return bindings() throws RecognitionException {
         bindings_return retval = new bindings_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        Token SEPR11=null;
-        binding_return binding10 = null;
+        Token SEPR14=null;
+        binding_return binding13 = null;
 
-        bindings_return bindings12 = null;
+        bindings_return bindings15 = null;
 
-        binding_return binding13 = null;
+        binding_return binding16 = null;
 
 
-        Object SEPR11_tree=null;
+        Object SEPR14_tree=null;
         RewriteRuleTokenStream stream_SEPR=new RewriteRuleTokenStream(adaptor,"token SEPR");
         RewriteRuleSubtreeStream stream_bindings=new RewriteRuleSubtreeStream(adaptor,"rule bindings");
         RewriteRuleSubtreeStream stream_binding=new RewriteRuleSubtreeStream(adaptor,"rule binding");
         try {
-            // dd/grammar/ECAGrammar.g:40:10: ( binding SEPR bindings -> ^( SEPR binding bindings ) | binding )
-            int alt2=2;
-            int LA2_0 = input.LA(1);
+            // dd/grammar/ECAGrammar.g:50:10: ( binding SEPR bindings -> ^( SEPR binding bindings ) | binding )
+            int alt1=2;
+            int LA1_0 = input.LA(1);
 
-            if ( (LA2_0==SYMBOL) ) {
-                int LA2_1 = input.LA(2);
+            if ( (LA1_0==SYMBOL) ) {
+                int LA1_1 = input.LA(2);
 
-                if ( (synpred3()) ) {
-                    alt2=1;
+                if ( (synpred1()) ) {
+                    alt1=1;
                 }
                 else if ( (true) ) {
-                    alt2=2;
+                    alt1=2;
                 }
                 else {
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("40:1: bindings : ( binding SEPR bindings -> ^( SEPR binding bindings ) | binding );", 2, 1, input);
+                        new NoViableAltException("50:1: bindings : ( binding SEPR bindings -> ^( SEPR binding bindings ) | binding );", 1, 1, input);
 
                     throw nvae;
                 }
@@ -459,31 +625,31 @@
             else {
                 if (backtracking>0) {failed=true; return retval;}
                 NoViableAltException nvae =
-                    new NoViableAltException("40:1: bindings : ( binding SEPR bindings -> ^( SEPR binding bindings ) | binding );", 2, 0, input);
+                    new NoViableAltException("50:1: bindings : ( binding SEPR bindings -> ^( SEPR binding bindings ) | binding );", 1, 0, input);
 
                 throw nvae;
             }
-            switch (alt2) {
+            switch (alt1) {
                 case 1 :
-                    // dd/grammar/ECAGrammar.g:40:12: binding SEPR bindings
+                    // dd/grammar/ECAGrammar.g:50:12: binding SEPR bindings
                     {
-                    pushFollow(FOLLOW_binding_in_bindings162);
-                    binding10=binding();
+                    pushFollow(FOLLOW_binding_in_bindings218);
+                    binding13=binding();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) stream_binding.add(binding10.getTree());
-                    SEPR11=(Token)input.LT(1);
-                    match(input,SEPR,FOLLOW_SEPR_in_bindings164); if (failed) return retval;
-                    if ( backtracking==0 ) stream_SEPR.add(SEPR11);
+                    if ( backtracking==0 ) stream_binding.add(binding13.getTree());
+                    SEPR14=(Token)input.LT(1);
+                    match(input,SEPR,FOLLOW_SEPR_in_bindings220); if (failed) return retval;
+                    if ( backtracking==0 ) stream_SEPR.add(SEPR14);
 
-                    pushFollow(FOLLOW_bindings_in_bindings166);
-                    bindings12=bindings();
+                    pushFollow(FOLLOW_bindings_in_bindings222);
+                    bindings15=bindings();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) stream_bindings.add(bindings12.getTree());
+                    if ( backtracking==0 ) stream_bindings.add(bindings15.getTree());
 
                     // AST REWRITE
-                    // elements: SEPR, binding, bindings
+                    // elements: bindings, SEPR, binding
                     // token labels: 
                     // rule labels: retval
                     // token list labels: 
@@ -493,9 +659,9 @@
                     RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
 
                     root_0 = (Object)adaptor.nil();
-                    // 40:34: -> ^( SEPR binding bindings )
+                    // 50:34: -> ^( SEPR binding bindings )
                     {
-                        // dd/grammar/ECAGrammar.g:40:37: ^( SEPR binding bindings )
+                        // dd/grammar/ECAGrammar.g:50:37: ^( SEPR binding bindings )
                         {
                         Object root_1 = (Object)adaptor.nil();
                         root_1 = (Object)adaptor.becomeRoot(stream_SEPR.next(), root_1);
@@ -513,15 +679,15 @@
                     }
                     break;
                 case 2 :
-                    // dd/grammar/ECAGrammar.g:41:4: binding
+                    // dd/grammar/ECAGrammar.g:51:4: binding
                     {
                     root_0 = (Object)adaptor.nil();
 
-                    pushFollow(FOLLOW_binding_in_bindings181);
-                    binding13=binding();
+                    pushFollow(FOLLOW_binding_in_bindings237);
+                    binding16=binding();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) adaptor.addChild(root_0, binding13.getTree());
+                    if ( backtracking==0 ) adaptor.addChild(root_0, binding16.getTree());
 
                     }
                     break;
@@ -550,44 +716,44 @@
     };
 
     // $ANTLR start binding
-    // dd/grammar/ECAGrammar.g:44:1: binding : bind_sym ASSIGN expr -> ^( ASSIGN bind_sym expr ) ;
+    // dd/grammar/ECAGrammar.g:54:1: binding : bind_sym ASSIGN expr -> ^( ASSIGN bind_sym expr ) ;
     public final binding_return binding() throws RecognitionException {
         binding_return retval = new binding_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        Token ASSIGN15=null;
-        bind_sym_return bind_sym14 = null;
+        Token ASSIGN18=null;
+        bind_sym_return bind_sym17 = null;
 
-        expr_return expr16 = null;
+        expr_return expr19 = null;
 
 
-        Object ASSIGN15_tree=null;
+        Object ASSIGN18_tree=null;
         RewriteRuleTokenStream stream_ASSIGN=new RewriteRuleTokenStream(adaptor,"token ASSIGN");
         RewriteRuleSubtreeStream stream_expr=new RewriteRuleSubtreeStream(adaptor,"rule expr");
         RewriteRuleSubtreeStream stream_bind_sym=new RewriteRuleSubtreeStream(adaptor,"rule bind_sym");
         try {
-            // dd/grammar/ECAGrammar.g:44:9: ( bind_sym ASSIGN expr -> ^( ASSIGN bind_sym expr ) )
-            // dd/grammar/ECAGrammar.g:44:11: bind_sym ASSIGN expr
+            // dd/grammar/ECAGrammar.g:54:9: ( bind_sym ASSIGN expr -> ^( ASSIGN bind_sym expr ) )
+            // dd/grammar/ECAGrammar.g:54:11: bind_sym ASSIGN expr
             {
-            pushFollow(FOLLOW_bind_sym_in_binding191);
-            bind_sym14=bind_sym();
+            pushFollow(FOLLOW_bind_sym_in_binding247);
+            bind_sym17=bind_sym();
             _fsp--;
             if (failed) return retval;
-            if ( backtracking==0 ) stream_bind_sym.add(bind_sym14.getTree());
-            ASSIGN15=(Token)input.LT(1);
-            match(input,ASSIGN,FOLLOW_ASSIGN_in_binding193); if (failed) return retval;
-            if ( backtracking==0 ) stream_ASSIGN.add(ASSIGN15);
+            if ( backtracking==0 ) stream_bind_sym.add(bind_sym17.getTree());
+            ASSIGN18=(Token)input.LT(1);
+            match(input,ASSIGN,FOLLOW_ASSIGN_in_binding249); if (failed) return retval;
+            if ( backtracking==0 ) stream_ASSIGN.add(ASSIGN18);
 
-            pushFollow(FOLLOW_expr_in_binding195);
-            expr16=expr();
+            pushFollow(FOLLOW_expr_in_binding251);
+            expr19=expr();
             _fsp--;
             if (failed) return retval;
-            if ( backtracking==0 ) stream_expr.add(expr16.getTree());
+            if ( backtracking==0 ) stream_expr.add(expr19.getTree());
 
             // AST REWRITE
-            // elements: bind_sym, ASSIGN, expr
+            // elements: expr, bind_sym, ASSIGN
             // token labels: 
             // rule labels: retval
             // token list labels: 
@@ -597,9 +763,9 @@
             RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
 
             root_0 = (Object)adaptor.nil();
-            // 44:32: -> ^( ASSIGN bind_sym expr )
+            // 54:32: -> ^( ASSIGN bind_sym expr )
             {
-                // dd/grammar/ECAGrammar.g:44:35: ^( ASSIGN bind_sym expr )
+                // dd/grammar/ECAGrammar.g:54:35: ^( ASSIGN bind_sym expr )
                 {
                 Object root_1 = (Object)adaptor.nil();
                 root_1 = (Object)adaptor.becomeRoot(stream_ASSIGN.next(), root_1);
@@ -639,7 +805,7 @@
     };
 
     // $ANTLR start bind_sym
-    // dd/grammar/ECAGrammar.g:48:1: bind_sym : (v= SYMBOL COLON t= SYMBOL -> ^( COLON $v $t) | SYMBOL );
+    // dd/grammar/ECAGrammar.g:58:1: bind_sym : (v= SYMBOL COLON t= SYMBOL -> ^( COLON $v $t) | SYMBOL );
     public final bind_sym_return bind_sym() throws RecognitionException {
         bind_sym_return retval = new bind_sym_return();
         retval.start = input.LT(1);
@@ -648,34 +814,34 @@
 
         Token v=null;
         Token t=null;
-        Token COLON17=null;
-        Token SYMBOL18=null;
+        Token COLON20=null;
+        Token SYMBOL21=null;
 
         Object v_tree=null;
         Object t_tree=null;
-        Object COLON17_tree=null;
-        Object SYMBOL18_tree=null;
+        Object COLON20_tree=null;
+        Object SYMBOL21_tree=null;
         RewriteRuleTokenStream stream_COLON=new RewriteRuleTokenStream(adaptor,"token COLON");
         RewriteRuleTokenStream stream_SYMBOL=new RewriteRuleTokenStream(adaptor,"token SYMBOL");
 
         try {
-            // dd/grammar/ECAGrammar.g:48:10: (v= SYMBOL COLON t= SYMBOL -> ^( COLON $v $t) | SYMBOL )
-            int alt3=2;
-            int LA3_0 = input.LA(1);
+            // dd/grammar/ECAGrammar.g:58:10: (v= SYMBOL COLON t= SYMBOL -> ^( COLON $v $t) | SYMBOL )
+            int alt2=2;
+            int LA2_0 = input.LA(1);
 
-            if ( (LA3_0==SYMBOL) ) {
-                int LA3_1 = input.LA(2);
+            if ( (LA2_0==SYMBOL) ) {
+                int LA2_1 = input.LA(2);
 
-                if ( (LA3_1==COLON) ) {
-                    alt3=1;
+                if ( (LA2_1==COLON) ) {
+                    alt2=1;
                 }
-                else if ( (LA3_1==ASSIGN) ) {
-                    alt3=2;
+                else if ( (LA2_1==ASSIGN) ) {
+                    alt2=2;
                 }
                 else {
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("48:1: bind_sym : (v= SYMBOL COLON t= SYMBOL -> ^( COLON $v $t) | SYMBOL );", 3, 1, input);
+                        new NoViableAltException("58:1: bind_sym : (v= SYMBOL COLON t= SYMBOL -> ^( COLON $v $t) | SYMBOL );", 2, 1, input);
 
                     throw nvae;
                 }
@@ -683,29 +849,29 @@
             else {
                 if (backtracking>0) {failed=true; return retval;}
                 NoViableAltException nvae =
-                    new NoViableAltException("48:1: bind_sym : (v= SYMBOL COLON t= SYMBOL -> ^( COLON $v $t) | SYMBOL );", 3, 0, input);
+                    new NoViableAltException("58:1: bind_sym : (v= SYMBOL COLON t= SYMBOL -> ^( COLON $v $t) | SYMBOL );", 2, 0, input);
 
                 throw nvae;
             }
-            switch (alt3) {
+            switch (alt2) {
                 case 1 :
-                    // dd/grammar/ECAGrammar.g:48:12: v= SYMBOL COLON t= SYMBOL
+                    // dd/grammar/ECAGrammar.g:58:12: v= SYMBOL COLON t= SYMBOL
                     {
                     v=(Token)input.LT(1);
-                    match(input,SYMBOL,FOLLOW_SYMBOL_in_bind_sym218); if (failed) return retval;
+                    match(input,SYMBOL,FOLLOW_SYMBOL_in_bind_sym274); if (failed) return retval;
                     if ( backtracking==0 ) stream_SYMBOL.add(v);
 
-                    COLON17=(Token)input.LT(1);
-                    match(input,COLON,FOLLOW_COLON_in_bind_sym220); if (failed) return retval;
-                    if ( backtracking==0 ) stream_COLON.add(COLON17);
+                    COLON20=(Token)input.LT(1);
+                    match(input,COLON,FOLLOW_COLON_in_bind_sym276); if (failed) return retval;
+                    if ( backtracking==0 ) stream_COLON.add(COLON20);
 
                     t=(Token)input.LT(1);
-                    match(input,SYMBOL,FOLLOW_SYMBOL_in_bind_sym224); if (failed) return retval;
+                    match(input,SYMBOL,FOLLOW_SYMBOL_in_bind_sym280); if (failed) return retval;
                     if ( backtracking==0 ) stream_SYMBOL.add(t);
 
 
                     // AST REWRITE
-                    // elements: t, v, COLON
+                    // elements: t, COLON, v
                     // token labels: t, v
                     // rule labels: retval
                     // token list labels: 
@@ -717,9 +883,9 @@
                     RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
 
                     root_0 = (Object)adaptor.nil();
-                    // 48:36: -> ^( COLON $v $t)
+                    // 58:36: -> ^( COLON $v $t)
                     {
-                        // dd/grammar/ECAGrammar.g:48:39: ^( COLON $v $t)
+                        // dd/grammar/ECAGrammar.g:58:39: ^( COLON $v $t)
                         {
                         Object root_1 = (Object)adaptor.nil();
                         root_1 = (Object)adaptor.becomeRoot(stream_COLON.next(), root_1);
@@ -737,15 +903,15 @@
                     }
                     break;
                 case 2 :
-                    // dd/grammar/ECAGrammar.g:49:4: SYMBOL
+                    // dd/grammar/ECAGrammar.g:59:4: SYMBOL
                     {
                     root_0 = (Object)adaptor.nil();
 
-                    SYMBOL18=(Token)input.LT(1);
-                    match(input,SYMBOL,FOLLOW_SYMBOL_in_bind_sym242); if (failed) return retval;
+                    SYMBOL21=(Token)input.LT(1);
+                    match(input,SYMBOL,FOLLOW_SYMBOL_in_bind_sym298); if (failed) return retval;
                     if ( backtracking==0 ) {
-                    SYMBOL18_tree = (Object)adaptor.create(SYMBOL18);
-                    adaptor.addChild(root_0, SYMBOL18_tree);
+                    SYMBOL21_tree = (Object)adaptor.create(SYMBOL21);
+                    adaptor.addChild(root_0, SYMBOL21_tree);
                     }
 
                     }
@@ -775,28 +941,28 @@
     };
 
     // $ANTLR start condition
-    // dd/grammar/ECAGrammar.g:56:1: condition : expr ;
+    // dd/grammar/ECAGrammar.g:66:1: condition : expr ;
     public final condition_return condition() throws RecognitionException {
         condition_return retval = new condition_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        expr_return expr19 = null;
+        expr_return expr22 = null;
 
 
 
         try {
-            // dd/grammar/ECAGrammar.g:56:11: ( expr )
-            // dd/grammar/ECAGrammar.g:56:13: expr
+            // dd/grammar/ECAGrammar.g:66:11: ( expr )
+            // dd/grammar/ECAGrammar.g:66:13: expr
             {
             root_0 = (Object)adaptor.nil();
 
-            pushFollow(FOLLOW_expr_in_condition256);
-            expr19=expr();
+            pushFollow(FOLLOW_expr_in_condition312);
+            expr22=expr();
             _fsp--;
             if (failed) return retval;
-            if ( backtracking==0 ) adaptor.addChild(root_0, expr19.getTree());
+            if ( backtracking==0 ) adaptor.addChild(root_0, expr22.getTree());
 
             }
 
@@ -823,28 +989,28 @@
     };
 
     // $ANTLR start action
-    // dd/grammar/ECAGrammar.g:63:1: action : action_expr_list ;
+    // dd/grammar/ECAGrammar.g:73:1: action : action_expr_list ;
     public final action_return action() throws RecognitionException {
         action_return retval = new action_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        action_expr_list_return action_expr_list20 = null;
+        action_expr_list_return action_expr_list23 = null;
 
 
 
         try {
-            // dd/grammar/ECAGrammar.g:63:8: ( action_expr_list )
-            // dd/grammar/ECAGrammar.g:63:10: action_expr_list
+            // dd/grammar/ECAGrammar.g:73:8: ( action_expr_list )
+            // dd/grammar/ECAGrammar.g:73:10: action_expr_list
             {
             root_0 = (Object)adaptor.nil();
 
-            pushFollow(FOLLOW_action_expr_list_in_action270);
-            action_expr_list20=action_expr_list();
+            pushFollow(FOLLOW_action_expr_list_in_action326);
+            action_expr_list23=action_expr_list();
             _fsp--;
             if (failed) return retval;
-            if ( backtracking==0 ) adaptor.addChild(root_0, action_expr_list20.getTree());
+            if ( backtracking==0 ) adaptor.addChild(root_0, action_expr_list23.getTree());
 
             }
 
@@ -871,43 +1037,43 @@
     };
 
     // $ANTLR start action_expr_list
-    // dd/grammar/ECAGrammar.g:66:1: action_expr_list : ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr );
+    // dd/grammar/ECAGrammar.g:76:1: action_expr_list : ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr );
     public final action_expr_list_return action_expr_list() throws RecognitionException {
         action_expr_list_return retval = new action_expr_list_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        Token SEPR22=null;
-        action_expr_return action_expr21 = null;
+        Token SEPR25=null;
+        action_expr_return action_expr24 = null;
 
-        action_expr_list_return action_expr_list23 = null;
+        action_expr_list_return action_expr_list26 = null;
 
-        action_expr_return action_expr24 = null;
+        action_expr_return action_expr27 = null;
 
 
-        Object SEPR22_tree=null;
+        Object SEPR25_tree=null;
         RewriteRuleTokenStream stream_SEPR=new RewriteRuleTokenStream(adaptor,"token SEPR");
         RewriteRuleSubtreeStream stream_action_expr_list=new RewriteRuleSubtreeStream(adaptor,"rule action_expr_list");
         RewriteRuleSubtreeStream stream_action_expr=new RewriteRuleSubtreeStream(adaptor,"rule action_expr");
         try {
-            // dd/grammar/ECAGrammar.g:67:2: ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr )
-            int alt4=2;
+            // dd/grammar/ECAGrammar.g:77:2: ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr )
+            int alt3=2;
             switch ( input.LA(1) ) {
             case DOLLARSYM:
                 {
-                int LA4_1 = input.LA(2);
+                int LA3_1 = input.LA(2);
 
-                if ( (synpred5()) ) {
-                    alt4=1;
+                if ( (synpred3()) ) {
+                    alt3=1;
                 }
                 else if ( (true) ) {
-                    alt4=2;
+                    alt3=2;
                 }
                 else {
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("66:1: action_expr_list : ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr );", 4, 1, input);
+                        new NoViableAltException("76:1: action_expr_list : ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr );", 3, 1, input);
 
                     throw nvae;
                 }
@@ -915,18 +1081,18 @@
                 break;
             case SYMBOL:
                 {
-                int LA4_2 = input.LA(2);
+                int LA3_2 = input.LA(2);
 
-                if ( (synpred5()) ) {
-                    alt4=1;
+                if ( (synpred3()) ) {
+                    alt3=1;
                 }
                 else if ( (true) ) {
-                    alt4=2;
+                    alt3=2;
                 }
                 else {
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("66:1: action_expr_list : ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr );", 4, 2, input);
+                        new NoViableAltException("76:1: action_expr_list : ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr );", 3, 2, input);
 
                     throw nvae;
                 }
@@ -934,18 +1100,18 @@
                 break;
             case NUMBER:
                 {
-                int LA4_3 = input.LA(2);
+                int LA3_3 = input.LA(2);
 
-                if ( (synpred5()) ) {
-                    alt4=1;
+                if ( (synpred3()) ) {
+                    alt3=1;
                 }
                 else if ( (true) ) {
-                    alt4=2;
+                    alt3=2;
                 }
                 else {
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("66:1: action_expr_list : ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr );", 4, 3, input);
+                        new NoViableAltException("76:1: action_expr_list : ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr );", 3, 3, input);
 
                     throw nvae;
                 }
@@ -953,18 +1119,18 @@
                 break;
             case STRING:
                 {
-                int LA4_4 = input.LA(2);
+                int LA3_4 = input.LA(2);
 
-                if ( (synpred5()) ) {
-                    alt4=1;
+                if ( (synpred3()) ) {
+                    alt3=1;
                 }
                 else if ( (true) ) {
-                    alt4=2;
+                    alt3=2;
                 }
                 else {
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("66:1: action_expr_list : ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr );", 4, 4, input);
+                        new NoViableAltException("76:1: action_expr_list : ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr );", 3, 4, input);
 
                     throw nvae;
                 }
@@ -972,18 +1138,18 @@
                 break;
             case LPAREN:
                 {
-                int LA4_5 = input.LA(2);
+                int LA3_5 = input.LA(2);
 
-                if ( (synpred5()) ) {
-                    alt4=1;
+                if ( (synpred3()) ) {
+                    alt3=1;
                 }
                 else if ( (true) ) {
-                    alt4=2;
+                    alt3=2;
                 }
                 else {
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("66:1: action_expr_list : ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr );", 4, 5, input);
+                        new NoViableAltException("76:1: action_expr_list : ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr );", 3, 5, input);
 
                     throw nvae;
                 }
@@ -992,18 +1158,18 @@
             case NOT:
             case TWIDDLE:
                 {
-                int LA4_6 = input.LA(2);
+                int LA3_6 = input.LA(2);
 
-                if ( (synpred5()) ) {
-                    alt4=1;
+                if ( (synpred3()) ) {
+                    alt3=1;
                 }
                 else if ( (true) ) {
-                    alt4=2;
+                    alt3=2;
                 }
                 else {
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("66:1: action_expr_list : ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr );", 4, 6, input);
+                        new NoViableAltException("76:1: action_expr_list : ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr );", 3, 6, input);
 
                     throw nvae;
                 }
@@ -1012,32 +1178,32 @@
             default:
                 if (backtracking>0) {failed=true; return retval;}
                 NoViableAltException nvae =
-                    new NoViableAltException("66:1: action_expr_list : ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr );", 4, 0, input);
+                    new NoViableAltException("76:1: action_expr_list : ( action_expr SEPR action_expr_list -> ^( SEPR action_expr action_expr_list ) | action_expr );", 3, 0, input);
 
                 throw nvae;
             }
 
-            switch (alt4) {
+            switch (alt3) {
                 case 1 :
-                    // dd/grammar/ECAGrammar.g:67:4: action_expr SEPR action_expr_list
+                    // dd/grammar/ECAGrammar.g:77:4: action_expr SEPR action_expr_list
                     {
-                    pushFollow(FOLLOW_action_expr_in_action_expr_list281);
-                    action_expr21=action_expr();
+                    pushFollow(FOLLOW_action_expr_in_action_expr_list337);
+                    action_expr24=action_expr();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) stream_action_expr.add(action_expr21.getTree());
-                    SEPR22=(Token)input.LT(1);
-                    match(input,SEPR,FOLLOW_SEPR_in_action_expr_list283); if (failed) return retval;
-                    if ( backtracking==0 ) stream_SEPR.add(SEPR22);
+                    if ( backtracking==0 ) stream_action_expr.add(action_expr24.getTree());
+                    SEPR25=(Token)input.LT(1);
+                    match(input,SEPR,FOLLOW_SEPR_in_action_expr_list339); if (failed) return retval;
+                    if ( backtracking==0 ) stream_SEPR.add(SEPR25);
 
-                    pushFollow(FOLLOW_action_expr_list_in_action_expr_list285);
-                    action_expr_list23=action_expr_list();
+                    pushFollow(FOLLOW_action_expr_list_in_action_expr_list341);
+                    action_expr_list26=action_expr_list();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) stream_action_expr_list.add(action_expr_list23.getTree());
+                    if ( backtracking==0 ) stream_action_expr_list.add(action_expr_list26.getTree());
 
                     // AST REWRITE
-                    // elements: action_expr_list, SEPR, action_expr
+                    // elements: action_expr_list, action_expr, SEPR
                     // token labels: 
                     // rule labels: retval
                     // token list labels: 
@@ -1047,9 +1213,9 @@
                     RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
 
                     root_0 = (Object)adaptor.nil();
-                    // 67:38: -> ^( SEPR action_expr action_expr_list )
+                    // 77:38: -> ^( SEPR action_expr action_expr_list )
                     {
-                        // dd/grammar/ECAGrammar.g:67:41: ^( SEPR action_expr action_expr_list )
+                        // dd/grammar/ECAGrammar.g:77:41: ^( SEPR action_expr action_expr_list )
                         {
                         Object root_1 = (Object)adaptor.nil();
                         root_1 = (Object)adaptor.becomeRoot(stream_SEPR.next(), root_1);
@@ -1067,15 +1233,15 @@
                     }
                     break;
                 case 2 :
-                    // dd/grammar/ECAGrammar.g:68:4: action_expr
+                    // dd/grammar/ECAGrammar.g:78:4: action_expr
                     {
                     root_0 = (Object)adaptor.nil();
 
-                    pushFollow(FOLLOW_action_expr_in_action_expr_list300);
-                    action_expr24=action_expr();
+                    pushFollow(FOLLOW_action_expr_in_action_expr_list356);
+                    action_expr27=action_expr();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) adaptor.addChild(root_0, action_expr24.getTree());
+                    if ( backtracking==0 ) adaptor.addChild(root_0, action_expr27.getTree());
 
                     }
                     break;
@@ -1104,28 +1270,28 @@
     };
 
     // $ANTLR start action_expr
-    // dd/grammar/ECAGrammar.g:71:1: action_expr : expr ;
+    // dd/grammar/ECAGrammar.g:81:1: action_expr : expr ;
     public final action_expr_return action_expr() throws RecognitionException {
         action_expr_return retval = new action_expr_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        expr_return expr25 = null;
+        expr_return expr28 = null;
 
 
 
         try {
-            // dd/grammar/ECAGrammar.g:71:13: ( expr )
-            // dd/grammar/ECAGrammar.g:71:15: expr
+            // dd/grammar/ECAGrammar.g:81:13: ( expr )
+            // dd/grammar/ECAGrammar.g:81:15: expr
             {
             root_0 = (Object)adaptor.nil();
 
-            pushFollow(FOLLOW_expr_in_action_expr310);
-            expr25=expr();
+            pushFollow(FOLLOW_expr_in_action_expr366);
+            expr28=expr();
             _fsp--;
             if (failed) return retval;
-            if ( backtracking==0 ) adaptor.addChild(root_0, expr25.getTree());
+            if ( backtracking==0 ) adaptor.addChild(root_0, expr28.getTree());
 
             }
 
@@ -1152,36 +1318,36 @@
     };
 
     // $ANTLR start expr
-    // dd/grammar/ECAGrammar.g:74:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );
+    // dd/grammar/ECAGrammar.g:84:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );
     public final expr_return expr() throws RecognitionException {
         expr_return retval = new expr_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        Token TERN_IF32=null;
-        Token COLON33=null;
+        Token TERN_IF35=null;
+        Token COLON36=null;
         simple_expr_return cond = null;
 
         expr_return iftrue = null;
 
         expr_return iffalse = null;
 
-        simple_expr_return simple_expr26 = null;
+        simple_expr_return simple_expr29 = null;
 
-        infix_oper_return infix_oper27 = null;
+        infix_oper_return infix_oper30 = null;
 
-        expr_return expr28 = null;
+        expr_return expr31 = null;
 
-        simple_expr_return simple_expr29 = null;
+        simple_expr_return simple_expr32 = null;
 
-        unary_oper_return unary_oper30 = null;
+        unary_oper_return unary_oper33 = null;
 
-        expr_return expr31 = null;
+        expr_return expr34 = null;
 
 
-        Object TERN_IF32_tree=null;
-        Object COLON33_tree=null;
+        Object TERN_IF35_tree=null;
+        Object COLON36_tree=null;
         RewriteRuleTokenStream stream_COLON=new RewriteRuleTokenStream(adaptor,"token COLON");
         RewriteRuleTokenStream stream_TERN_IF=new RewriteRuleTokenStream(adaptor,"token TERN_IF");
         RewriteRuleSubtreeStream stream_unary_oper=new RewriteRuleSubtreeStream(adaptor,"rule unary_oper");
@@ -1189,26 +1355,23 @@
         RewriteRuleSubtreeStream stream_expr=new RewriteRuleSubtreeStream(adaptor,"rule expr");
         RewriteRuleSubtreeStream stream_simple_expr=new RewriteRuleSubtreeStream(adaptor,"rule simple_expr");
         try {
-            // dd/grammar/ECAGrammar.g:74:6: ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) )
-            int alt5=4;
+            // dd/grammar/ECAGrammar.g:84:6: ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) )
+            int alt4=4;
             switch ( input.LA(1) ) {
             case DOLLARSYM:
                 {
                 switch ( input.LA(2) ) {
                 case EOF:
+                case IF:
+                case DO:
                 case RPAREN:
                 case RSQUARE:
                 case SEPR:
                 case COLON:
                     {
-                    alt5=2;
+                    alt4=2;
                     }
                     break;
-                case TERN_IF:
-                    {
-                    alt5=4;
-                    }
-                    break;
                 case OR:
                 case AND:
                 case EQ:
@@ -1225,13 +1388,18 @@
                 case PLUS:
                 case MINUS:
                     {
-                    alt5=1;
+                    alt4=1;
                     }
                     break;
+                case TERN_IF:
+                    {
+                    alt4=4;
+                    }
+                    break;
                 default:
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("74:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );", 5, 1, input);
+                        new NoViableAltException("84:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );", 4, 1, input);
 
                     throw nvae;
                 }
@@ -1243,21 +1411,21 @@
                 switch ( input.LA(2) ) {
                 case LPAREN:
                     {
-                    int LA5_10 = input.LA(3);
+                    int LA4_10 = input.LA(3);
 
-                    if ( (synpred6()) ) {
-                        alt5=1;
+                    if ( (synpred4()) ) {
+                        alt4=1;
                     }
-                    else if ( (synpred7()) ) {
-                        alt5=2;
+                    else if ( (synpred5()) ) {
+                        alt4=2;
                     }
                     else if ( (true) ) {
-                        alt5=4;
+                        alt4=4;
                     }
                     else {
                         if (backtracking>0) {failed=true; return retval;}
                         NoViableAltException nvae =
-                            new NoViableAltException("74:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );", 5, 10, input);
+                            new NoViableAltException("84:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );", 4, 10, input);
 
                         throw nvae;
                     }
@@ -1265,26 +1433,42 @@
                     break;
                 case LSQUARE:
                     {
-                    int LA5_11 = input.LA(3);
+                    int LA4_11 = input.LA(3);
 
-                    if ( (synpred6()) ) {
-                        alt5=1;
+                    if ( (synpred4()) ) {
+                        alt4=1;
                     }
-                    else if ( (synpred7()) ) {
-                        alt5=2;
+                    else if ( (synpred5()) ) {
+                        alt4=2;
                     }
                     else if ( (true) ) {
-                        alt5=4;
+                        alt4=4;
                     }
                     else {
                         if (backtracking>0) {failed=true; return retval;}
                         NoViableAltException nvae =
-                            new NoViableAltException("74:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );", 5, 11, input);
+                            new NoViableAltException("84:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );", 4, 11, input);
 
                         throw nvae;
                     }
                     }
                     break;
+                case TERN_IF:
+                    {
+                    alt4=4;
+                    }
+                    break;
+                case EOF:
+                case IF:
+                case DO:
+                case RPAREN:
+                case RSQUARE:
+                case SEPR:
+                case COLON:
+                    {
+                    alt4=2;
+                    }
+                    break;
                 case OR:
                 case AND:
                 case EQ:
@@ -1301,27 +1485,13 @@
                 case PLUS:
                 case MINUS:
                     {
-                    alt5=1;
+                    alt4=1;
                     }
                     break;
-                case TERN_IF:
-                    {
-                    alt5=4;
-                    }
-                    break;
-                case EOF:
-                case RPAREN:
-                case RSQUARE:
-                case SEPR:
-                case COLON:
-                    {
-                    alt5=2;
-                    }
-                    break;
                 default:
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("74:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );", 5, 2, input);
+                        new NoViableAltException("84:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );", 4, 2, input);
 
                     throw nvae;
                 }
@@ -1333,16 +1503,18 @@
                 switch ( input.LA(2) ) {
                 case TERN_IF:
                     {
-                    alt5=4;
+                    alt4=4;
                     }
                     break;
                 case EOF:
+                case IF:
+                case DO:
                 case RPAREN:
                 case RSQUARE:
                 case SEPR:
                 case COLON:
                     {
-                    alt5=2;
+                    alt4=2;
                     }
                     break;
                 case OR:
@@ -1361,13 +1533,13 @@
                 case PLUS:
                 case MINUS:
                     {
-                    alt5=1;
+                    alt4=1;
                     }
                     break;
                 default:
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("74:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );", 5, 3, input);
+                        new NoViableAltException("84:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );", 4, 3, input);
 
                     throw nvae;
                 }
@@ -1377,11 +1549,6 @@
             case STRING:
                 {
                 switch ( input.LA(2) ) {
-                case TERN_IF:
-                    {
-                    alt5=4;
-                    }
-                    break;
                 case OR:
                 case AND:
                 case EQ:
@@ -1398,22 +1565,29 @@
                 case PLUS:
                 case MINUS:
                     {
-                    alt5=1;
+                    alt4=1;
                     }
                     break;
                 case EOF:
+                case IF:
+                case DO:
                 case RPAREN:
                 case RSQUARE:
                 case SEPR:
                 case COLON:
                     {
-                    alt5=2;
+                    alt4=2;
                     }
                     break;
+                case TERN_IF:
+                    {
+                    alt4=4;
+                    }
+                    break;
                 default:
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("74:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );", 5, 4, input);
+                        new NoViableAltException("84:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );", 4, 4, input);
 
                     throw nvae;
                 }
@@ -1422,21 +1596,21 @@
                 break;
             case LPAREN:
                 {
-                int LA5_5 = input.LA(2);
+                int LA4_5 = input.LA(2);
 
-                if ( (synpred6()) ) {
-                    alt5=1;
+                if ( (synpred4()) ) {
+                    alt4=1;
                 }
-                else if ( (synpred7()) ) {
-                    alt5=2;
+                else if ( (synpred5()) ) {
+                    alt4=2;
                 }
                 else if ( (true) ) {
-                    alt5=4;
+                    alt4=4;
                 }
                 else {
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("74:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );", 5, 5, input);
+                        new NoViableAltException("84:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );", 4, 5, input);
 
                     throw nvae;
                 }
@@ -1445,39 +1619,39 @@
             case NOT:
             case TWIDDLE:
                 {
-                alt5=3;
+                alt4=3;
                 }
                 break;
             default:
                 if (backtracking>0) {failed=true; return retval;}
                 NoViableAltException nvae =
-                    new NoViableAltException("74:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );", 5, 0, input);
+                    new NoViableAltException("84:1: expr : ( simple_expr infix_oper expr -> ^( BINOP infix_oper simple_expr expr ) | simple_expr | unary_oper expr -> ^( UNOP unary_oper expr ) | cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr -> ^( TERNOP $cond $iftrue $iffalse) );", 4, 0, input);
 
                 throw nvae;
             }
 
-            switch (alt5) {
+            switch (alt4) {
                 case 1 :
-                    // dd/grammar/ECAGrammar.g:74:8: simple_expr infix_oper expr
+                    // dd/grammar/ECAGrammar.g:84:8: simple_expr infix_oper expr
                     {
-                    pushFollow(FOLLOW_simple_expr_in_expr320);
-                    simple_expr26=simple_expr();
+                    pushFollow(FOLLOW_simple_expr_in_expr376);
+                    simple_expr29=simple_expr();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) stream_simple_expr.add(simple_expr26.getTree());
-                    pushFollow(FOLLOW_infix_oper_in_expr322);
-                    infix_oper27=infix_oper();
+                    if ( backtracking==0 ) stream_simple_expr.add(simple_expr29.getTree());
+                    pushFollow(FOLLOW_infix_oper_in_expr378);
+                    infix_oper30=infix_oper();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) stream_infix_oper.add(infix_oper27.getTree());
-                    pushFollow(FOLLOW_expr_in_expr324);
-                    expr28=expr();
+                    if ( backtracking==0 ) stream_infix_oper.add(infix_oper30.getTree());
+                    pushFollow(FOLLOW_expr_in_expr380);
+                    expr31=expr();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) stream_expr.add(expr28.getTree());
+                    if ( backtracking==0 ) stream_expr.add(expr31.getTree());
 
                     // AST REWRITE
-                    // elements: expr, infix_oper, simple_expr
+                    // elements: infix_oper, simple_expr, expr
                     // token labels: 
                     // rule labels: retval
                     // token list labels: 
@@ -1487,9 +1661,9 @@
                     RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
 
                     root_0 = (Object)adaptor.nil();
-                    // 74:37: -> ^( BINOP infix_oper simple_expr expr )
+                    // 84:37: -> ^( BINOP infix_oper simple_expr expr )
                     {
-                        // dd/grammar/ECAGrammar.g:74:40: ^( BINOP infix_oper simple_expr expr )
+                        // dd/grammar/ECAGrammar.g:84:40: ^( BINOP infix_oper simple_expr expr )
                         {
                         Object root_1 = (Object)adaptor.nil();
                         root_1 = (Object)adaptor.becomeRoot(adaptor.create(BINOP, "BINOP"), root_1);
@@ -1508,31 +1682,31 @@
                     }
                     break;
                 case 2 :
-                    // dd/grammar/ECAGrammar.g:75:4: simple_expr
+                    // dd/grammar/ECAGrammar.g:85:4: simple_expr
                     {
                     root_0 = (Object)adaptor.nil();
 
-                    pushFollow(FOLLOW_simple_expr_in_expr342);
-                    simple_expr29=simple_expr();
+                    pushFollow(FOLLOW_simple_expr_in_expr398);
+                    simple_expr32=simple_expr();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) adaptor.addChild(root_0, simple_expr29.getTree());
+                    if ( backtracking==0 ) adaptor.addChild(root_0, simple_expr32.getTree());
 
                     }
                     break;
                 case 3 :
-                    // dd/grammar/ECAGrammar.g:76:4: unary_oper expr
+                    // dd/grammar/ECAGrammar.g:86:4: unary_oper expr
                     {
-                    pushFollow(FOLLOW_unary_oper_in_expr347);
-                    unary_oper30=unary_oper();
+                    pushFollow(FOLLOW_unary_oper_in_expr403);
+                    unary_oper33=unary_oper();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) stream_unary_oper.add(unary_oper30.getTree());
-                    pushFollow(FOLLOW_expr_in_expr349);
-                    expr31=expr();
+                    if ( backtracking==0 ) stream_unary_oper.add(unary_oper33.getTree());
+                    pushFollow(FOLLOW_expr_in_expr405);
+                    expr34=expr();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) stream_expr.add(expr31.getTree());
+                    if ( backtracking==0 ) stream_expr.add(expr34.getTree());
 
                     // AST REWRITE
                     // elements: expr, unary_oper
@@ -1545,9 +1719,9 @@
                     RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
 
                     root_0 = (Object)adaptor.nil();
-                    // 76:22: -> ^( UNOP unary_oper expr )
+                    // 86:22: -> ^( UNOP unary_oper expr )
                     {
-                        // dd/grammar/ECAGrammar.g:76:25: ^( UNOP unary_oper expr )
+                        // dd/grammar/ECAGrammar.g:86:25: ^( UNOP unary_oper expr )
                         {
                         Object root_1 = (Object)adaptor.nil();
                         root_1 = (Object)adaptor.becomeRoot(adaptor.create(UNOP, "UNOP"), root_1);
@@ -1565,34 +1739,34 @@
                     }
                     break;
                 case 4 :
-                    // dd/grammar/ECAGrammar.g:77:4: cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr
+                    // dd/grammar/ECAGrammar.g:87:4: cond= simple_expr TERN_IF iftrue= expr COLON iffalse= expr
                     {
-                    pushFollow(FOLLOW_simple_expr_in_expr368);
+                    pushFollow(FOLLOW_simple_expr_in_expr424);
                     cond=simple_expr();
                     _fsp--;
                     if (failed) return retval;
                     if ( backtracking==0 ) stream_simple_expr.add(cond.getTree());
-                    TERN_IF32=(Token)input.LT(1);
-                    match(input,TERN_IF,FOLLOW_TERN_IF_in_expr370); if (failed) return retval;
-                    if ( backtracking==0 ) stream_TERN_IF.add(TERN_IF32);
+                    TERN_IF35=(Token)input.LT(1);
+                    match(input,TERN_IF,FOLLOW_TERN_IF_in_expr426); if (failed) return retval;
+                    if ( backtracking==0 ) stream_TERN_IF.add(TERN_IF35);
 
-                    pushFollow(FOLLOW_expr_in_expr374);
+                    pushFollow(FOLLOW_expr_in_expr430);
                     iftrue=expr();
                     _fsp--;
                     if (failed) return retval;
                     if ( backtracking==0 ) stream_expr.add(iftrue.getTree());
-                    COLON33=(Token)input.LT(1);
-                    match(input,COLON,FOLLOW_COLON_in_expr376); if (failed) return retval;
-                    if ( backtracking==0 ) stream_COLON.add(COLON33);
+                    COLON36=(Token)input.LT(1);
+                    match(input,COLON,FOLLOW_COLON_in_expr432); if (failed) return retval;
+                    if ( backtracking==0 ) stream_COLON.add(COLON36);
 
-                    pushFollow(FOLLOW_expr_in_expr380);
+                    pushFollow(FOLLOW_expr_in_expr436);
                     iffalse=expr();
                     _fsp--;
                     if (failed) return retval;
                     if ( backtracking==0 ) stream_expr.add(iffalse.getTree());
 
                     // AST REWRITE
-                    // elements: iffalse, cond, iftrue
+                    // elements: iftrue, iffalse, cond
                     // token labels: 
                     // rule labels: iftrue, cond, iffalse, retval
                     // token list labels: 
@@ -1605,9 +1779,9 @@
                     RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
 
                     root_0 = (Object)adaptor.nil();
-                    // 77:60: -> ^( TERNOP $cond $iftrue $iffalse)
+                    // 87:60: -> ^( TERNOP $cond $iftrue $iffalse)
                     {
-                        // dd/grammar/ECAGrammar.g:77:63: ^( TERNOP $cond $iftrue $iffalse)
+                        // dd/grammar/ECAGrammar.g:87:63: ^( TERNOP $cond $iftrue $iffalse)
                         {
                         Object root_1 = (Object)adaptor.nil();
                         root_1 = (Object)adaptor.becomeRoot(adaptor.create(TERNOP, "TERNOP"), root_1);
@@ -1650,7 +1824,7 @@
     };
 
     // $ANTLR start simple_expr
-    // dd/grammar/ECAGrammar.g:80:1: simple_expr : (v= DOLLARSYM | v= SYMBOL idx= array_idx -> ^( ARRAY $v $idx) | v= SYMBOL LPAREN RPAREN -> ^( METH $v) | v= SYMBOL | v= SYMBOL LPAREN args= expr_list RPAREN -> ^( METH $v $args) | NUMBER | STRING | LPAREN expr RPAREN -> ^( expr ) );
+    // dd/grammar/ECAGrammar.g:90:1: simple_expr : (v= DOLLARSYM | v= SYMBOL idx= array_idx -> ^( ARRAY $v $idx) | v= SYMBOL LPAREN RPAREN -> ^( METH $v) | v= SYMBOL | v= SYMBOL LPAREN args= expr_list RPAREN -> ^( METH $v $args) | NUMBER | STRING | LPAREN expr RPAREN -> ^( expr ) );
     public final simple_expr_return simple_expr() throws RecognitionException {
         simple_expr_return retval = new simple_expr_return();
         retval.start = input.LT(1);
@@ -1658,30 +1832,30 @@
         Object root_0 = null;
 
         Token v=null;
-        Token LPAREN34=null;
-        Token RPAREN35=null;
-        Token LPAREN36=null;
-        Token RPAREN37=null;
-        Token NUMBER38=null;
-        Token STRING39=null;
-        Token LPAREN40=null;
-        Token RPAREN42=null;
+        Token LPAREN37=null;
+        Token RPAREN38=null;
+        Token LPAREN39=null;
+        Token RPAREN40=null;
+        Token NUMBER41=null;
+        Token STRING42=null;
+        Token LPAREN43=null;
+        Token RPAREN45=null;
         array_idx_return idx = null;
 
         expr_list_return args = null;
 
-        expr_return expr41 = null;
+        expr_return expr44 = null;
 
 
         Object v_tree=null;
-        Object LPAREN34_tree=null;
-        Object RPAREN35_tree=null;
-        Object LPAREN36_tree=null;
-        Object RPAREN37_tree=null;
-        Object NUMBER38_tree=null;
-        Object STRING39_tree=null;
-        Object LPAREN40_tree=null;
-        Object RPAREN42_tree=null;
+        Object LPAREN37_tree=null;
+        Object RPAREN38_tree=null;
+        Object LPAREN39_tree=null;
+        Object RPAREN40_tree=null;
+        Object NUMBER41_tree=null;
+        Object STRING42_tree=null;
+        Object LPAREN43_tree=null;
+        Object RPAREN45_tree=null;
         RewriteRuleTokenStream stream_RPAREN=new RewriteRuleTokenStream(adaptor,"token RPAREN");
         RewriteRuleTokenStream stream_LPAREN=new RewriteRuleTokenStream(adaptor,"token LPAREN");
         RewriteRuleTokenStream stream_SYMBOL=new RewriteRuleTokenStream(adaptor,"token SYMBOL");
@@ -1689,12 +1863,12 @@
         RewriteRuleSubtreeStream stream_expr=new RewriteRuleSubtreeStream(adaptor,"rule expr");
         RewriteRuleSubtreeStream stream_array_idx=new RewriteRuleSubtreeStream(adaptor,"rule array_idx");
         try {
-            // dd/grammar/ECAGrammar.g:80:13: (v= DOLLARSYM | v= SYMBOL idx= array_idx -> ^( ARRAY $v $idx) | v= SYMBOL LPAREN RPAREN -> ^( METH $v) | v= SYMBOL | v= SYMBOL LPAREN args= expr_list RPAREN -> ^( METH $v $args) | NUMBER | STRING | LPAREN expr RPAREN -> ^( expr ) )
-            int alt6=8;
+            // dd/grammar/ECAGrammar.g:90:13: (v= DOLLARSYM | v= SYMBOL idx= array_idx -> ^( ARRAY $v $idx) | v= SYMBOL LPAREN RPAREN -> ^( METH $v) | v= SYMBOL | v= SYMBOL LPAREN args= expr_list RPAREN -> ^( METH $v $args) | NUMBER | STRING | LPAREN expr RPAREN -> ^( expr ) )
+            int alt5=8;
             switch ( input.LA(1) ) {
             case DOLLARSYM:
                 {
-                alt6=1;
+                alt5=1;
                 }
                 break;
             case SYMBOL:
@@ -1702,18 +1876,18 @@
                 switch ( input.LA(2) ) {
                 case LPAREN:
                     {
-                    int LA6_6 = input.LA(3);
+                    int LA5_6 = input.LA(3);
 
-                    if ( (LA6_6==RPAREN) ) {
-                        alt6=3;
+                    if ( (LA5_6==RPAREN) ) {
+                        alt5=3;
                     }
-                    else if ( (LA6_6==NUMBER||LA6_6==LPAREN||LA6_6==NOT||LA6_6==TWIDDLE||LA6_6==STRING||LA6_6==SYMBOL||LA6_6==DOLLARSYM) ) {
-                        alt6=5;
+                    else if ( (LA5_6==NUMBER||LA5_6==LPAREN||LA5_6==NOT||LA5_6==TWIDDLE||LA5_6==STRING||LA5_6==SYMBOL||LA5_6==DOLLARSYM) ) {
+                        alt5=5;
                     }
                     else {
                         if (backtracking>0) {failed=true; return retval;}
                         NoViableAltException nvae =
-                            new NoViableAltException("80:1: simple_expr : (v= DOLLARSYM | v= SYMBOL idx= array_idx -> ^( ARRAY $v $idx) | v= SYMBOL LPAREN RPAREN -> ^( METH $v) | v= SYMBOL | v= SYMBOL LPAREN args= expr_list RPAREN -> ^( METH $v $args) | NUMBER | STRING | LPAREN expr RPAREN -> ^( expr ) );", 6, 6, input);
+                            new NoViableAltException("90:1: simple_expr : (v= DOLLARSYM | v= SYMBOL idx= array_idx -> ^( ARRAY $v $idx) | v= SYMBOL LPAREN RPAREN -> ^( METH $v) | v= SYMBOL | v= SYMBOL LPAREN args= expr_list RPAREN -> ^( METH $v $args) | NUMBER | STRING | LPAREN expr RPAREN -> ^( expr ) );", 5, 6, input);
 
                         throw nvae;
                     }
@@ -1721,10 +1895,12 @@
                     break;
                 case LSQUARE:
                     {
-                    alt6=2;
+                    alt5=2;
                     }
                     break;
                 case EOF:
+                case IF:
+                case DO:
                 case RPAREN:
                 case RSQUARE:
                 case SEPR:
@@ -1746,13 +1922,13 @@
                 case TERN_IF:
                 case COLON:
                     {
-                    alt6=4;
+                    alt5=4;
                     }
                     break;
                 default:
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("80:1: simple_expr : (v= DOLLARSYM | v= SYMBOL idx= array_idx -> ^( ARRAY $v $idx) | v= SYMBOL LPAREN RPAREN -> ^( METH $v) | v= SYMBOL | v= SYMBOL LPAREN args= expr_list RPAREN -> ^( METH $v $args) | NUMBER | STRING | LPAREN expr RPAREN -> ^( expr ) );", 6, 2, input);
+                        new NoViableAltException("90:1: simple_expr : (v= DOLLARSYM | v= SYMBOL idx= array_idx -> ^( ARRAY $v $idx) | v= SYMBOL LPAREN RPAREN -> ^( METH $v) | v= SYMBOL | v= SYMBOL LPAREN args= expr_list RPAREN -> ^( METH $v $args) | NUMBER | STRING | LPAREN expr RPAREN -> ^( expr ) );", 5, 2, input);
 
                     throw nvae;
                 }
@@ -1761,35 +1937,35 @@
                 break;
             case NUMBER:
                 {
-                alt6=6;
+                alt5=6;
                 }
                 break;
             case STRING:
                 {
-                alt6=7;
+                alt5=7;
                 }
                 break;
             case LPAREN:
                 {
-                alt6=8;
+                alt5=8;
                 }
                 break;
             default:
                 if (backtracking>0) {failed=true; return retval;}
                 NoViableAltException nvae =
-                    new NoViableAltException("80:1: simple_expr : (v= DOLLARSYM | v= SYMBOL idx= array_idx -> ^( ARRAY $v $idx) | v= SYMBOL LPAREN RPAREN -> ^( METH $v) | v= SYMBOL | v= SYMBOL LPAREN args= expr_list RPAREN -> ^( METH $v $args) | NUMBER | STRING | LPAREN expr RPAREN -> ^( expr ) );", 6, 0, input);
+                    new NoViableAltException("90:1: simple_expr : (v= DOLLARSYM | v= SYMBOL idx= array_idx -> ^( ARRAY $v $idx) | v= SYMBOL LPAREN RPAREN -> ^( METH $v) | v= SYMBOL | v= SYMBOL LPAREN args= expr_list RPAREN -> ^( METH $v $args) | NUMBER | STRING | LPAREN expr RPAREN -> ^( expr ) );", 5, 0, input);
 
                 throw nvae;
             }
 
-            switch (alt6) {
+            switch (alt5) {
                 case 1 :
-                    // dd/grammar/ECAGrammar.g:80:15: v= DOLLARSYM
+                    // dd/grammar/ECAGrammar.g:90:15: v= DOLLARSYM
                     {
                     root_0 = (Object)adaptor.nil();
 
                     v=(Token)input.LT(1);
-                    match(input,DOLLARSYM,FOLLOW_DOLLARSYM_in_simple_expr407); if (failed) return retval;
+                    match(input,DOLLARSYM,FOLLOW_DOLLARSYM_in_simple_expr463); if (failed) return retval;
                     if ( backtracking==0 ) {
                     v_tree = (Object)adaptor.create(v);
                     adaptor.addChild(root_0, v_tree);
@@ -1798,13 +1974,13 @@
                     }
                     break;
                 case 2 :
-                    // dd/grammar/ECAGrammar.g:81:4: v= SYMBOL idx= array_idx
+                    // dd/grammar/ECAGrammar.g:91:4: v= SYMBOL idx= array_idx
                     {
                     v=(Token)input.LT(1);
-                    match(input,SYMBOL,FOLLOW_SYMBOL_in_simple_expr414); if (failed) return retval;
+                    match(input,SYMBOL,FOLLOW_SYMBOL_in_simple_expr470); if (failed) return retval;
                     if ( backtracking==0 ) stream_SYMBOL.add(v);
 
-                    pushFollow(FOLLOW_array_idx_in_simple_expr418);
+                    pushFollow(FOLLOW_array_idx_in_simple_expr474);
                     idx=array_idx();
                     _fsp--;
                     if (failed) return retval;
@@ -1823,9 +1999,9 @@
                     RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
 
                     root_0 = (Object)adaptor.nil();
-                    // 81:29: -> ^( ARRAY $v $idx)
+                    // 91:29: -> ^( ARRAY $v $idx)
                     {
-                        // dd/grammar/ECAGrammar.g:81:32: ^( ARRAY $v $idx)
+                        // dd/grammar/ECAGrammar.g:91:32: ^( ARRAY $v $idx)
                         {
                         Object root_1 = (Object)adaptor.nil();
                         root_1 = (Object)adaptor.becomeRoot(adaptor.create(ARRAY, "ARRAY"), root_1);
@@ -1843,19 +2019,19 @@
                     }
                     break;
                 case 3 :
-                    // dd/grammar/ECAGrammar.g:82:4: v= SYMBOL LPAREN RPAREN
+                    // dd/grammar/ECAGrammar.g:92:4: v= SYMBOL LPAREN RPAREN
                     {
                     v=(Token)input.LT(1);
-                    match(input,SYMBOL,FOLLOW_SYMBOL_in_simple_expr439); if (failed) return retval;
+                    match(input,SYMBOL,FOLLOW_SYMBOL_in_simple_expr495); if (failed) return retval;
                     if ( backtracking==0 ) stream_SYMBOL.add(v);
 
-                    LPAREN34=(Token)input.LT(1);
-                    match(input,LPAREN,FOLLOW_LPAREN_in_simple_expr441); if (failed) return retval;
-                    if ( backtracking==0 ) stream_LPAREN.add(LPAREN34);
+                    LPAREN37=(Token)input.LT(1);
+                    match(input,LPAREN,FOLLOW_LPAREN_in_simple_expr497); if (failed) return retval;
+                    if ( backtracking==0 ) stream_LPAREN.add(LPAREN37);
 
-                    RPAREN35=(Token)input.LT(1);
-                    match(input,RPAREN,FOLLOW_RPAREN_in_simple_expr443); if (failed) return retval;
-                    if ( backtracking==0 ) stream_RPAREN.add(RPAREN35);
+                    RPAREN38=(Token)input.LT(1);
+                    match(input,RPAREN,FOLLOW_RPAREN_in_simple_expr499); if (failed) return retval;
+                    if ( backtracking==0 ) stream_RPAREN.add(RPAREN38);
 
 
                     // AST REWRITE
@@ -1870,9 +2046,9 @@
                     RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
 
                     root_0 = (Object)adaptor.nil();
-                    // 82:29: -> ^( METH $v)
+                    // 92:29: -> ^( METH $v)
                     {
-                        // dd/grammar/ECAGrammar.g:82:32: ^( METH $v)
+                        // dd/grammar/ECAGrammar.g:92:32: ^( METH $v)
                         {
                         Object root_1 = (Object)adaptor.nil();
                         root_1 = (Object)adaptor.becomeRoot(adaptor.create(METH, "METH"), root_1);
@@ -1889,12 +2065,12 @@
                     }
                     break;
                 case 4 :
-                    // dd/grammar/ECAGrammar.g:83:4: v= SYMBOL
+                    // dd/grammar/ECAGrammar.g:93:4: v= SYMBOL
                     {
                     root_0 = (Object)adaptor.nil();
 
                     v=(Token)input.LT(1);
-                    match(input,SYMBOL,FOLLOW_SYMBOL_in_simple_expr461); if (failed) return retval;
+                    match(input,SYMBOL,FOLLOW_SYMBOL_in_simple_expr517); if (failed) return retval;
                     if ( backtracking==0 ) {
                     v_tree = (Object)adaptor.create(v);
                     adaptor.addChild(root_0, v_tree);
@@ -1903,28 +2079,28 @@
                     }
                     break;
                 case 5 :
-                    // dd/grammar/ECAGrammar.g:84:4: v= SYMBOL LPAREN args= expr_list RPAREN
+                    // dd/grammar/ECAGrammar.g:94:4: v= SYMBOL LPAREN args= expr_list RPAREN
                     {
                     v=(Token)input.LT(1);
-                    match(input,SYMBOL,FOLLOW_SYMBOL_in_simple_expr468); if (failed) return retval;
+                    match(input,SYMBOL,FOLLOW_SYMBOL_in_simple_expr524); if (failed) return retval;
                     if ( backtracking==0 ) stream_SYMBOL.add(v);
 
-                    LPAREN36=(Token)input.LT(1);
-                    match(input,LPAREN,FOLLOW_LPAREN_in_simple_expr470); if (failed) return retval;
-                    if ( backtracking==0 ) stream_LPAREN.add(LPAREN36);
+                    LPAREN39=(Token)input.LT(1);
+                    match(input,LPAREN,FOLLOW_LPAREN_in_simple_expr526); if (failed) return retval;
+                    if ( backtracking==0 ) stream_LPAREN.add(LPAREN39);
 
-                    pushFollow(FOLLOW_expr_list_in_simple_expr474);
+                    pushFollow(FOLLOW_expr_list_in_simple_expr530);
                     args=expr_list();
                     _fsp--;
                     if (failed) return retval;
                     if ( backtracking==0 ) stream_expr_list.add(args.getTree());
-                    RPAREN37=(Token)input.LT(1);
-                    match(input,RPAREN,FOLLOW_RPAREN_in_simple_expr476); if (failed) return retval;
-                    if ( backtracking==0 ) stream_RPAREN.add(RPAREN37);
+                    RPAREN40=(Token)input.LT(1);
+                    match(input,RPAREN,FOLLOW_RPAREN_in_simple_expr532); if (failed) return retval;
+                    if ( backtracking==0 ) stream_RPAREN.add(RPAREN40);
 
 
                     // AST REWRITE
-                    // elements: v, args
+                    // elements: args, v
                     // token labels: v
                     // rule labels: args, retval
                     // token list labels: 
@@ -1936,9 +2112,9 @@
                     RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
 
                     root_0 = (Object)adaptor.nil();
-                    // 84:43: -> ^( METH $v $args)
+                    // 94:43: -> ^( METH $v $args)
                     {
-                        // dd/grammar/ECAGrammar.g:84:46: ^( METH $v $args)
+                        // dd/grammar/ECAGrammar.g:94:46: ^( METH $v $args)
                         {
                         Object root_1 = (Object)adaptor.nil();
                         root_1 = (Object)adaptor.becomeRoot(adaptor.create(METH, "METH"), root_1);
@@ -1956,48 +2132,48 @@
                     }
                     break;
                 case 6 :
-                    // dd/grammar/ECAGrammar.g:85:4: NUMBER
+                    // dd/grammar/ECAGrammar.g:95:4: NUMBER
                     {
                     root_0 = (Object)adaptor.nil();
 
-                    NUMBER38=(Token)input.LT(1);
-                    match(input,NUMBER,FOLLOW_NUMBER_in_simple_expr495); if (failed) return retval;
+                    NUMBER41=(Token)input.LT(1);
+                    match(input,NUMBER,FOLLOW_NUMBER_in_simple_expr551); if (failed) return retval;
                     if ( backtracking==0 ) {
-                    NUMBER38_tree = (Object)adaptor.create(NUMBER38);
-                    adaptor.addChild(root_0, NUMBER38_tree);
+                    NUMBER41_tree = (Object)adaptor.create(NUMBER41);
+                    adaptor.addChild(root_0, NUMBER41_tree);
                     }
 
                     }
                     break;
                 case 7 :
-                    // dd/grammar/ECAGrammar.g:86:4: STRING
+                    // dd/grammar/ECAGrammar.g:96:4: STRING
                     {
                     root_0 = (Object)adaptor.nil();
 
-                    STRING39=(Token)input.LT(1);
-                    match(input,STRING,FOLLOW_STRING_in_simple_expr500); if (failed) return retval;
+                    STRING42=(Token)input.LT(1);
+                    match(input,STRING,FOLLOW_STRING_in_simple_expr556); if (failed) return retval;
                     if ( backtracking==0 ) {
-                    STRING39_tree = (Object)adaptor.create(STRING39);
-                    adaptor.addChild(root_0, STRING39_tree);
+                    STRING42_tree = (Object)adaptor.create(STRING42);
+                    adaptor.addChild(root_0, STRING42_tree);
                     }
 
                     }
                     break;
                 case 8 :
-                    // dd/grammar/ECAGrammar.g:87:4: LPAREN expr RPAREN
+                    // dd/grammar/ECAGrammar.g:97:4: LPAREN expr RPAREN
                     {
-                    LPAREN40=(Token)input.LT(1);
-                    match(input,LPAREN,FOLLOW_LPAREN_in_simple_expr505); if (failed) return retval;
-                    if ( backtracking==0 ) stream_LPAREN.add(LPAREN40);
+                    LPAREN43=(Token)input.LT(1);
+                    match(input,LPAREN,FOLLOW_LPAREN_in_simple_expr561); if (failed) return retval;
+                    if ( backtracking==0 ) stream_LPAREN.add(LPAREN43);
 
-                    pushFollow(FOLLOW_expr_in_simple_expr507);
-                    expr41=expr();
+                    pushFollow(FOLLOW_expr_in_simple_expr563);
+                    expr44=expr();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) stream_expr.add(expr41.getTree());
-                    RPAREN42=(Token)input.LT(1);
-                    match(input,RPAREN,FOLLOW_RPAREN_in_simple_expr509); if (failed) return retval;
-                    if ( backtracking==0 ) stream_RPAREN.add(RPAREN42);
+                    if ( backtracking==0 ) stream_expr.add(expr44.getTree());
+                    RPAREN45=(Token)input.LT(1);
+                    match(input,RPAREN,FOLLOW_RPAREN_in_simple_expr565); if (failed) return retval;
+                    if ( backtracking==0 ) stream_RPAREN.add(RPAREN45);
 
 
                     // AST REWRITE
@@ -2011,9 +2187,9 @@
                     RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
 
                     root_0 = (Object)adaptor.nil();
-                    // 87:25: -> ^( expr )
+                    // 97:25: -> ^( expr )
                     {
-                        // dd/grammar/ECAGrammar.g:87:28: ^( expr )
+                        // dd/grammar/ECAGrammar.g:97:28: ^( expr )
                         {
                         Object root_1 = (Object)adaptor.nil();
                         root_1 = (Object)adaptor.becomeRoot(stream_expr.nextNode(), root_1);
@@ -2052,43 +2228,43 @@
     };
 
     // $ANTLR start expr_list
-    // dd/grammar/ECAGrammar.g:90:1: expr_list : ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr );
+    // dd/grammar/ECAGrammar.g:100:1: expr_list : ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr );
     public final expr_list_return expr_list() throws RecognitionException {
         expr_list_return retval = new expr_list_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        Token SEPR44=null;
-        expr_return expr43 = null;
+        Token SEPR47=null;
+        expr_return expr46 = null;
 
-        expr_list_return expr_list45 = null;
+        expr_list_return expr_list48 = null;
 
-        expr_return expr46 = null;
+        expr_return expr49 = null;
 
 
-        Object SEPR44_tree=null;
+        Object SEPR47_tree=null;
         RewriteRuleTokenStream stream_SEPR=new RewriteRuleTokenStream(adaptor,"token SEPR");
         RewriteRuleSubtreeStream stream_expr_list=new RewriteRuleSubtreeStream(adaptor,"rule expr_list");
         RewriteRuleSubtreeStream stream_expr=new RewriteRuleSubtreeStream(adaptor,"rule expr");
         try {
-            // dd/grammar/ECAGrammar.g:91:2: ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr )
-            int alt7=2;
+            // dd/grammar/ECAGrammar.g:101:2: ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr )
+            int alt6=2;
             switch ( input.LA(1) ) {
             case DOLLARSYM:
                 {
-                int LA7_1 = input.LA(2);
+                int LA6_1 = input.LA(2);
 
-                if ( (synpred16()) ) {
-                    alt7=1;
+                if ( (synpred14()) ) {
+                    alt6=1;
                 }
                 else if ( (true) ) {
-                    alt7=2;
+                    alt6=2;
                 }
                 else {
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("90:1: expr_list : ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr );", 7, 1, input);
+                        new NoViableAltException("100:1: expr_list : ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr );", 6, 1, input);
 
                     throw nvae;
                 }
@@ -2096,18 +2272,18 @@
                 break;
             case SYMBOL:
                 {
-                int LA7_2 = input.LA(2);
+                int LA6_2 = input.LA(2);
 
-                if ( (synpred16()) ) {
-                    alt7=1;
+                if ( (synpred14()) ) {
+                    alt6=1;
                 }
                 else if ( (true) ) {
-                    alt7=2;
+                    alt6=2;
                 }
                 else {
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("90:1: expr_list : ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr );", 7, 2, input);
+                        new NoViableAltException("100:1: expr_list : ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr );", 6, 2, input);
 
                     throw nvae;
                 }
@@ -2115,18 +2291,18 @@
                 break;
             case NUMBER:
                 {
-                int LA7_3 = input.LA(2);
+                int LA6_3 = input.LA(2);
 
-                if ( (synpred16()) ) {
-                    alt7=1;
+                if ( (synpred14()) ) {
+                    alt6=1;
                 }
                 else if ( (true) ) {
-                    alt7=2;
+                    alt6=2;
                 }
                 else {
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("90:1: expr_list : ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr );", 7, 3, input);
+                        new NoViableAltException("100:1: expr_list : ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr );", 6, 3, input);
 
                     throw nvae;
                 }
@@ -2134,18 +2310,18 @@
                 break;
             case STRING:
                 {
-                int LA7_4 = input.LA(2);
+                int LA6_4 = input.LA(2);
 
-                if ( (synpred16()) ) {
-                    alt7=1;
+                if ( (synpred14()) ) {
+                    alt6=1;
                 }
                 else if ( (true) ) {
-                    alt7=2;
+                    alt6=2;
                 }
                 else {
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("90:1: expr_list : ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr );", 7, 4, input);
+                        new NoViableAltException("100:1: expr_list : ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr );", 6, 4, input);
 
                     throw nvae;
                 }
@@ -2153,18 +2329,18 @@
                 break;
             case LPAREN:
                 {
-                int LA7_5 = input.LA(2);
+                int LA6_5 = input.LA(2);
 
-                if ( (synpred16()) ) {
-                    alt7=1;
+                if ( (synpred14()) ) {
+                    alt6=1;
                 }
                 else if ( (true) ) {
-                    alt7=2;
+                    alt6=2;
                 }
                 else {
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("90:1: expr_list : ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr );", 7, 5, input);
+                        new NoViableAltException("100:1: expr_list : ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr );", 6, 5, input);
 
                     throw nvae;
                 }
@@ -2173,18 +2349,18 @@
             case NOT:
             case TWIDDLE:
                 {
-                int LA7_6 = input.LA(2);
+                int LA6_6 = input.LA(2);
 
-                if ( (synpred16()) ) {
-                    alt7=1;
+                if ( (synpred14()) ) {
+                    alt6=1;
                 }
                 else if ( (true) ) {
-                    alt7=2;
+                    alt6=2;
                 }
                 else {
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("90:1: expr_list : ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr );", 7, 6, input);
+                        new NoViableAltException("100:1: expr_list : ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr );", 6, 6, input);
 
                     throw nvae;
                 }
@@ -2193,32 +2369,32 @@
             default:
                 if (backtracking>0) {failed=true; return retval;}
                 NoViableAltException nvae =
-                    new NoViableAltException("90:1: expr_list : ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr );", 7, 0, input);
+                    new NoViableAltException("100:1: expr_list : ( expr SEPR expr_list -> ^( SEPR expr expr_list ) | expr );", 6, 0, input);
 
                 throw nvae;
             }
 
-            switch (alt7) {
+            switch (alt6) {
                 case 1 :
-                    // dd/grammar/ECAGrammar.g:91:4: expr SEPR expr_list
+                    // dd/grammar/ECAGrammar.g:101:4: expr SEPR expr_list
                     {
-                    pushFollow(FOLLOW_expr_in_expr_list528);
-                    expr43=expr();
+                    pushFollow(FOLLOW_expr_in_expr_list584);
+                    expr46=expr();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) stream_expr.add(expr43.getTree());
-                    SEPR44=(Token)input.LT(1);
-                    match(input,SEPR,FOLLOW_SEPR_in_expr_list530); if (failed) return retval;
-                    if ( backtracking==0 ) stream_SEPR.add(SEPR44);
+                    if ( backtracking==0 ) stream_expr.add(expr46.getTree());
+                    SEPR47=(Token)input.LT(1);
+                    match(input,SEPR,FOLLOW_SEPR_in_expr_list586); if (failed) return retval;
+                    if ( backtracking==0 ) stream_SEPR.add(SEPR47);
 
-                    pushFollow(FOLLOW_expr_list_in_expr_list532);
-                    expr_list45=expr_list();
+                    pushFollow(FOLLOW_expr_list_in_expr_list588);
+                    expr_list48=expr_list();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) stream_expr_list.add(expr_list45.getTree());
+                    if ( backtracking==0 ) stream_expr_list.add(expr_list48.getTree());
 
                     // AST REWRITE
-                    // elements: expr, expr_list, SEPR
+                    // elements: expr, SEPR, expr_list
                     // token labels: 
                     // rule labels: retval
                     // token list labels: 
@@ -2228,9 +2404,9 @@
                     RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
 
                     root_0 = (Object)adaptor.nil();
-                    // 91:26: -> ^( SEPR expr expr_list )
+                    // 101:26: -> ^( SEPR expr expr_list )
                     {
-                        // dd/grammar/ECAGrammar.g:91:29: ^( SEPR expr expr_list )
+                        // dd/grammar/ECAGrammar.g:101:29: ^( SEPR expr expr_list )
                         {
                         Object root_1 = (Object)adaptor.nil();
                         root_1 = (Object)adaptor.becomeRoot(stream_SEPR.next(), root_1);
@@ -2248,15 +2424,15 @@
                     }
                     break;
                 case 2 :
-                    // dd/grammar/ECAGrammar.g:92:4: expr
+                    // dd/grammar/ECAGrammar.g:102:4: expr
                     {
                     root_0 = (Object)adaptor.nil();
 
-                    pushFollow(FOLLOW_expr_in_expr_list549);
-                    expr46=expr();
+                    pushFollow(FOLLOW_expr_in_expr_list605);
+                    expr49=expr();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) adaptor.addChild(root_0, expr46.getTree());
+                    if ( backtracking==0 ) adaptor.addChild(root_0, expr49.getTree());
 
                     }
                     break;
@@ -2285,40 +2461,40 @@
     };
 
     // $ANTLR start array_idx_list
-    // dd/grammar/ECAGrammar.g:95:1: array_idx_list : ( array_idx array_idx_list -> ^( SEPR array_idx array_idx_list ) | array_idx );
+    // dd/grammar/ECAGrammar.g:105:1: array_idx_list : ( array_idx array_idx_list -> ^( SEPR array_idx array_idx_list ) | array_idx );
     public final array_idx_list_return array_idx_list() throws RecognitionException {
         array_idx_list_return retval = new array_idx_list_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        array_idx_return array_idx47 = null;
+        array_idx_return array_idx50 = null;
 
-        array_idx_list_return array_idx_list48 = null;
+        array_idx_list_return array_idx_list51 = null;
 
-        array_idx_return array_idx49 = null;
+        array_idx_return array_idx52 = null;
 
 
         RewriteRuleSubtreeStream stream_array_idx=new RewriteRuleSubtreeStream(adaptor,"rule array_idx");
         RewriteRuleSubtreeStream stream_array_idx_list=new RewriteRuleSubtreeStream(adaptor,"rule array_idx_list");
         try {
-            // dd/grammar/ECAGrammar.g:96:2: ( array_idx array_idx_list -> ^( SEPR array_idx array_idx_list ) | array_idx )
-            int alt8=2;
-            int LA8_0 = input.LA(1);
+            // dd/grammar/ECAGrammar.g:106:2: ( array_idx array_idx_list -> ^( SEPR array_idx array_idx_list ) | array_idx )
+            int alt7=2;
+            int LA7_0 = input.LA(1);
 
-            if ( (LA8_0==LSQUARE) ) {
-                int LA8_1 = input.LA(2);
+            if ( (LA7_0==LSQUARE) ) {
+                int LA7_1 = input.LA(2);
 
-                if ( (synpred17()) ) {
-                    alt8=1;
+                if ( (synpred15()) ) {
+                    alt7=1;
                 }
                 else if ( (true) ) {
-                    alt8=2;
+                    alt7=2;
                 }
                 else {
                     if (backtracking>0) {failed=true; return retval;}
                     NoViableAltException nvae =
-                        new NoViableAltException("95:1: array_idx_list : ( array_idx array_idx_list -> ^( SEPR array_idx array_idx_list ) | array_idx );", 8, 1, input);
+                        new NoViableAltException("105:1: array_idx_list : ( array_idx array_idx_list -> ^( SEPR array_idx array_idx_list ) | array_idx );", 7, 1, input);
 
                     throw nvae;
                 }
@@ -2326,24 +2502,24 @@
             else {
                 if (backtracking>0) {failed=true; return retval;}
                 NoViableAltException nvae =
-                    new NoViableAltException("95:1: array_idx_list : ( array_idx array_idx_list -> ^( SEPR array_idx array_idx_list ) | array_idx );", 8, 0, input);
+                    new NoViableAltException("105:1: array_idx_list : ( array_idx array_idx_list -> ^( SEPR array_idx array_idx_list ) | array_idx );", 7, 0, input);
 
                 throw nvae;
             }
-            switch (alt8) {
+            switch (alt7) {
                 case 1 :
-                    // dd/grammar/ECAGrammar.g:96:4: array_idx array_idx_list
+                    // dd/grammar/ECAGrammar.g:106:4: array_idx array_idx_list
                     {
-                    pushFollow(FOLLOW_array_idx_in_array_idx_list560);
-                    array_idx47=array_idx();
+                    pushFollow(FOLLOW_array_idx_in_array_idx_list616);
+                    array_idx50=array_idx();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) stream_array_idx.add(array_idx47.getTree());
-                    pushFollow(FOLLOW_array_idx_list_in_array_idx_list562);
-                    array_idx_list48=array_idx_list();
+                    if ( backtracking==0 ) stream_array_idx.add(array_idx50.getTree());
+                    pushFollow(FOLLOW_array_idx_list_in_array_idx_list618);
+                    array_idx_list51=array_idx_list();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) stream_array_idx_list.add(array_idx_list48.getTree());
+                    if ( backtracking==0 ) stream_array_idx_list.add(array_idx_list51.getTree());
 
                     // AST REWRITE
                     // elements: array_idx, array_idx_list
@@ -2356,9 +2532,9 @@
                     RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
 
                     root_0 = (Object)adaptor.nil();
-                    // 96:31: -> ^( SEPR array_idx array_idx_list )
+                    // 106:31: -> ^( SEPR array_idx array_idx_list )
                     {
-                        // dd/grammar/ECAGrammar.g:96:34: ^( SEPR array_idx array_idx_list )
+                        // dd/grammar/ECAGrammar.g:106:34: ^( SEPR array_idx array_idx_list )
                         {
                         Object root_1 = (Object)adaptor.nil();
                         root_1 = (Object)adaptor.becomeRoot(adaptor.create(SEPR, "SEPR"), root_1);
@@ -2376,15 +2552,15 @@
                     }
                     break;
                 case 2 :
-                    // dd/grammar/ECAGrammar.g:97:4: array_idx
+                    // dd/grammar/ECAGrammar.g:107:4: array_idx
                     {
                     root_0 = (Object)adaptor.nil();
 
-                    pushFollow(FOLLOW_array_idx_in_array_idx_list579);
-                    array_idx49=array_idx();
+                    pushFollow(FOLLOW_array_idx_in_array_idx_list635);
+                    array_idx52=array_idx();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) adaptor.addChild(root_0, array_idx49.getTree());
+                    if ( backtracking==0 ) adaptor.addChild(root_0, array_idx52.getTree());
 
                     }
                     break;
@@ -2413,39 +2589,39 @@
     };
 
     // $ANTLR start array_idx
-    // dd/grammar/ECAGrammar.g:100:1: array_idx : LSQUARE expr RSQUARE -> ^( expr ) ;
+    // dd/grammar/ECAGrammar.g:110:1: array_idx : LSQUARE expr RSQUARE -> ^( expr ) ;
     public final array_idx_return array_idx() throws RecognitionException {
         array_idx_return retval = new array_idx_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        Token LSQUARE50=null;
-        Token RSQUARE52=null;
-        expr_return expr51 = null;
+        Token LSQUARE53=null;
+        Token RSQUARE55=null;
+        expr_return expr54 = null;
 
 
-        Object LSQUARE50_tree=null;
-        Object RSQUARE52_tree=null;
+        Object LSQUARE53_tree=null;
+        Object RSQUARE55_tree=null;
         RewriteRuleTokenStream stream_LSQUARE=new RewriteRuleTokenStream(adaptor,"token LSQUARE");
         RewriteRuleTokenStream stream_RSQUARE=new RewriteRuleTokenStream(adaptor,"token RSQUARE");
         RewriteRuleSubtreeStream stream_expr=new RewriteRuleSubtreeStream(adaptor,"rule expr");
         try {
-            // dd/grammar/ECAGrammar.g:101:2: ( LSQUARE expr RSQUARE -> ^( expr ) )
-            // dd/grammar/ECAGrammar.g:101:4: LSQUARE expr RSQUARE
+            // dd/grammar/ECAGrammar.g:111:2: ( LSQUARE expr RSQUARE -> ^( expr ) )
+            // dd/grammar/ECAGrammar.g:111:4: LSQUARE expr RSQUARE
             {
-            LSQUARE50=(Token)input.LT(1);
-            match(input,LSQUARE,FOLLOW_LSQUARE_in_array_idx590); if (failed) return retval;
-            if ( backtracking==0 ) stream_LSQUARE.add(LSQUARE50);
+            LSQUARE53=(Token)input.LT(1);
+            match(input,LSQUARE,FOLLOW_LSQUARE_in_array_idx646); if (failed) return retval;
+            if ( backtracking==0 ) stream_LSQUARE.add(LSQUARE53);
 
-            pushFollow(FOLLOW_expr_in_array_idx592);
-            expr51=expr();
+            pushFollow(FOLLOW_expr_in_array_idx648);
+            expr54=expr();
             _fsp--;
             if (failed) return retval;
-            if ( backtracking==0 ) stream_expr.add(expr51.getTree());
-            RSQUARE52=(Token)input.LT(1);
-            match(input,RSQUARE,FOLLOW_RSQUARE_in_array_idx594); if (failed) return retval;
-            if ( backtracking==0 ) stream_RSQUARE.add(RSQUARE52);
+            if ( backtracking==0 ) stream_expr.add(expr54.getTree());
+            RSQUARE55=(Token)input.LT(1);
+            match(input,RSQUARE,FOLLOW_RSQUARE_in_array_idx650); if (failed) return retval;
+            if ( backtracking==0 ) stream_RSQUARE.add(RSQUARE55);
 
 
             // AST REWRITE
@@ -2459,9 +2635,9 @@
             RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
 
             root_0 = (Object)adaptor.nil();
-            // 101:27: -> ^( expr )
+            // 111:27: -> ^( expr )
             {
-                // dd/grammar/ECAGrammar.g:101:30: ^( expr )
+                // dd/grammar/ECAGrammar.g:111:30: ^( expr )
                 {
                 Object root_1 = (Object)adaptor.nil();
                 root_1 = (Object)adaptor.becomeRoot(stream_expr.nextNode(), root_1);
@@ -2498,32 +2674,32 @@
     };
 
     // $ANTLR start infix_oper
-    // dd/grammar/ECAGrammar.g:104:1: infix_oper : ( infix_bit_oper | infix_arith_oper | infix_bool_oper | infix_cmp_oper );
+    // dd/grammar/ECAGrammar.g:114:1: infix_oper : ( infix_bit_oper | infix_arith_oper | infix_bool_oper | infix_cmp_oper );
     public final infix_oper_return infix_oper() throws RecognitionException {
         infix_oper_return retval = new infix_oper_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        infix_bit_oper_return infix_bit_oper53 = null;
+        infix_bit_oper_return infix_bit_oper56 = null;
 
-        infix_arith_oper_return infix_arith_oper54 = null;
+        infix_arith_oper_return infix_arith_oper57 = null;
 
-        infix_bool_oper_return infix_bool_oper55 = null;
+        infix_bool_oper_return infix_bool_oper58 = null;
 
-        infix_cmp_oper_return infix_cmp_oper56 = null;
+        infix_cmp_oper_return infix_cmp_oper59 = null;
 
 
 
         try {
-            // dd/grammar/ECAGrammar.g:104:12: ( infix_bit_oper | infix_arith_oper | infix_bool_oper | infix_cmp_oper )
-            int alt9=4;
+            // dd/grammar/ECAGrammar.g:114:12: ( infix_bit_oper | infix_arith_oper | infix_bool_oper | infix_cmp_oper )
+            int alt8=4;
             switch ( input.LA(1) ) {
             case BOR:
             case BAND:
             case BXOR:
                 {
-                alt9=1;
+                alt8=1;
                 }
                 break;
             case MUL:
@@ -2531,13 +2707,13 @@
             case PLUS:
             case MINUS:
                 {
-                alt9=2;
+                alt8=2;
                 }
                 break;
             case OR:
             case AND:
                 {
-                alt9=3;
+                alt8=3;
                 }
                 break;
             case EQ:
@@ -2547,67 +2723,67 @@
             case GEQ:
             case LEQ:
                 {
-                alt9=4;
+                alt8=4;
                 }
                 break;
             default:
                 if (backtracking>0) {failed=true; return retval;}
                 NoViableAltException nvae =
-                    new NoViableAltException("104:1: infix_oper : ( infix_bit_oper | infix_arith_oper | infix_bool_oper | infix_cmp_oper );", 9, 0, input);
+                    new NoViableAltException("114:1: infix_oper : ( infix_bit_oper | infix_arith_oper | infix_bool_oper | infix_cmp_oper );", 8, 0, input);
 
                 throw nvae;
             }
 
-            switch (alt9) {
+            switch (alt8) {
                 case 1 :
-                    // dd/grammar/ECAGrammar.g:104:14: infix_bit_oper
+                    // dd/grammar/ECAGrammar.g:114:14: infix_bit_oper
                     {
                     root_0 = (Object)adaptor.nil();
 
-                    pushFollow(FOLLOW_infix_bit_oper_in_infix_oper612);
-                    infix_bit_oper53=infix_bit_oper();
+                    pushFollow(FOLLOW_infix_bit_oper_in_infix_oper668);
+                    infix_bit_oper56=infix_bit_oper();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) adaptor.addChild(root_0, infix_bit_oper53.getTree());
+                    if ( backtracking==0 ) adaptor.addChild(root_0, infix_bit_oper56.getTree());
 
                     }
                     break;
                 case 2 :
-                    // dd/grammar/ECAGrammar.g:105:4: infix_arith_oper
+                    // dd/grammar/ECAGrammar.g:115:4: infix_arith_oper
                     {
                     root_0 = (Object)adaptor.nil();
 
-                    pushFollow(FOLLOW_infix_arith_oper_in_infix_oper617);
-                    infix_arith_oper54=infix_arith_oper();
+                    pushFollow(FOLLOW_infix_arith_oper_in_infix_oper673);
+                    infix_arith_oper57=infix_arith_oper();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) adaptor.addChild(root_0, infix_arith_oper54.getTree());
+                    if ( backtracking==0 ) adaptor.addChild(root_0, infix_arith_oper57.getTree());
 
                     }
                     break;
                 case 3 :
-                    // dd/grammar/ECAGrammar.g:106:4: infix_bool_oper
+                    // dd/grammar/ECAGrammar.g:116:4: infix_bool_oper
                     {
                     root_0 = (Object)adaptor.nil();
 
-                    pushFollow(FOLLOW_infix_bool_oper_in_infix_oper622);
-                    infix_bool_oper55=infix_bool_oper();
+                    pushFollow(FOLLOW_infix_bool_oper_in_infix_oper678);
+                    infix_bool_oper58=infix_bool_oper();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) adaptor.addChild(root_0, infix_bool_oper55.getTree());
+                    if ( backtracking==0 ) adaptor.addChild(root_0, infix_bool_oper58.getTree());
 
                     }
                     break;
                 case 4 :
-                    // dd/grammar/ECAGrammar.g:107:4: infix_cmp_oper
+                    // dd/grammar/ECAGrammar.g:117:4: infix_cmp_oper
                     {
                     root_0 = (Object)adaptor.nil();
 
-                    pushFollow(FOLLOW_infix_cmp_oper_in_infix_oper627);
-                    infix_cmp_oper56=infix_cmp_oper();
+                    pushFollow(FOLLOW_infix_cmp_oper_in_infix_oper683);
+                    infix_cmp_oper59=infix_cmp_oper();
                     _fsp--;
                     if (failed) return retval;
-                    if ( backtracking==0 ) adaptor.addChild(root_0, infix_cmp_oper56.getTree());
+                    if ( backtracking==0 ) adaptor.addChild(root_0, infix_cmp_oper59.getTree());
 
                     }
                     break;
@@ -2636,27 +2812,27 @@
     };
 
     // $ANTLR start infix_bit_oper
-    // dd/grammar/ECAGrammar.g:110:1: infix_bit_oper : ( BAND | BOR | BXOR );
+    // dd/grammar/ECAGrammar.g:120:1: infix_bit_oper : ( BAND | BOR | BXOR );
     public final infix_bit_oper_return infix_bit_oper() throws RecognitionException {
         infix_bit_oper_return retval = new infix_bit_oper_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        Token set57=null;
+        Token set60=null;
 
-        Object set57_tree=null;
+        Object set60_tree=null;
 
         try {
-            // dd/grammar/ECAGrammar.g:111:2: ( BAND | BOR | BXOR )
+            // dd/grammar/ECAGrammar.g:121:2: ( BAND | BOR | BXOR )
             // dd/grammar/ECAGrammar.g:
             {
             root_0 = (Object)adaptor.nil();
 
-            set57=(Token)input.LT(1);
+            set60=(Token)input.LT(1);
             if ( (input.LA(1)>=BOR && input.LA(1)<=BXOR) ) {
                 input.consume();
-                if ( backtracking==0 ) adaptor.addChild(root_0, adaptor.create(set57));
+                if ( backtracking==0 ) adaptor.addChild(root_0, adaptor.create(set60));
                 errorRecovery=false;failed=false;
             }
             else {
@@ -2692,27 +2868,27 @@
     };
 
     // $ANTLR start infix_arith_oper
-    // dd/grammar/ECAGrammar.g:116:1: infix_arith_oper : ( MUL | DIV | PLUS | MINUS );
+    // dd/grammar/ECAGrammar.g:126:1: infix_arith_oper : ( MUL | DIV | PLUS | MINUS );
     public final infix_arith_oper_return infix_arith_oper() throws RecognitionException {
         infix_arith_oper_return retval = new infix_arith_oper_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        Token set58=null;
+        Token set61=null;
 
-        Object set58_tree=null;
+        Object set61_tree=null;
 
         try {
-            // dd/grammar/ECAGrammar.g:117:2: ( MUL | DIV | PLUS | MINUS )
+            // dd/grammar/ECAGrammar.g:127:2: ( MUL | DIV | PLUS | MINUS )
             // dd/grammar/ECAGrammar.g:
             {
             root_0 = (Object)adaptor.nil();
 
-            set58=(Token)input.LT(1);
+            set61=(Token)input.LT(1);
             if ( (input.LA(1)>=MUL && input.LA(1)<=MINUS) ) {
                 input.consume();
-                if ( backtracking==0 ) adaptor.addChild(root_0, adaptor.create(set58));
+                if ( backtracking==0 ) adaptor.addChild(root_0, adaptor.create(set61));
                 errorRecovery=false;failed=false;
             }
             else {
@@ -2748,27 +2924,27 @@
     };
 
     // $ANTLR start infix_bool_oper
-    // dd/grammar/ECAGrammar.g:123:1: infix_bool_oper : ( AND | OR );
+    // dd/grammar/ECAGrammar.g:133:1: infix_bool_oper : ( AND | OR );
     public final infix_bool_oper_return infix_bool_oper() throws RecognitionException {
         infix_bool_oper_return retval = new infix_bool_oper_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        Token set59=null;
+        Token set62=null;
 
-        Object set59_tree=null;
+        Object set62_tree=null;
 
         try {
-            // dd/grammar/ECAGrammar.g:124:2: ( AND | OR )
+            // dd/grammar/ECAGrammar.g:134:2: ( AND | OR )
             // dd/grammar/ECAGrammar.g:
             {
             root_0 = (Object)adaptor.nil();
 
-            set59=(Token)input.LT(1);
+            set62=(Token)input.LT(1);
             if ( (input.LA(1)>=OR && input.LA(1)<=AND) ) {
                 input.consume();
-                if ( backtracking==0 ) adaptor.addChild(root_0, adaptor.create(set59));
+                if ( backtracking==0 ) adaptor.addChild(root_0, adaptor.create(set62));
                 errorRecovery=false;failed=false;
             }
             else {
@@ -2804,27 +2980,27 @@
     };
 
     // $ANTLR start infix_cmp_oper
-    // dd/grammar/ECAGrammar.g:128:1: infix_cmp_oper : ( EQ | NEQ | GT | LT | GEQ | LEQ );
+    // dd/grammar/ECAGrammar.g:138:1: infix_cmp_oper : ( EQ | NEQ | GT | LT | GEQ | LEQ );
     public final infix_cmp_oper_return infix_cmp_oper() throws RecognitionException {
         infix_cmp_oper_return retval = new infix_cmp_oper_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        Token set60=null;
+        Token set63=null;
 
-        Object set60_tree=null;
+        Object set63_tree=null;
 
         try {
-            // dd/grammar/ECAGrammar.g:129:2: ( EQ | NEQ | GT | LT | GEQ | LEQ )
+            // dd/grammar/ECAGrammar.g:139:2: ( EQ | NEQ | GT | LT | GEQ | LEQ )
             // dd/grammar/ECAGrammar.g:
             {
             root_0 = (Object)adaptor.nil();
 
-            set60=(Token)input.LT(1);
+            set63=(Token)input.LT(1);
             if ( (input.LA(1)>=EQ && input.LA(1)<=LEQ) ) {
                 input.consume();
-                if ( backtracking==0 ) adaptor.addChild(root_0, adaptor.create(set60));
+                if ( backtracking==0 ) adaptor.addChild(root_0, adaptor.create(set63));
                 errorRecovery=false;failed=false;
             }
             else {
@@ -2860,27 +3036,27 @@
     };
 
     // $ANTLR start unary_oper
-    // dd/grammar/ECAGrammar.g:137:1: unary_oper : ( NOT | TWIDDLE );
+    // dd/grammar/ECAGrammar.g:147:1: unary_oper : ( NOT | TWIDDLE );
     public final unary_oper_return unary_oper() throws RecognitionException {
         unary_oper_return retval = new unary_oper_return();
         retval.start = input.LT(1);
 
         Object root_0 = null;
 
-        Token set61=null;
+        Token set64=null;
 
-        Object set61_tree=null;
+        Object set64_tree=null;
 
         try {
-            // dd/grammar/ECAGrammar.g:137:12: ( NOT | TWIDDLE )
+            // dd/grammar/ECAGrammar.g:147:12: ( NOT | TWIDDLE )
             // dd/grammar/ECAGrammar.g:
             {
             root_0 = (Object)adaptor.nil();
 
-            set61=(Token)input.LT(1);
+            set64=(Token)input.LT(1);
             if ( input.LA(1)==NOT||input.LA(1)==TWIDDLE ) {
                 input.consume();
-                if ( backtracking==0 ) adaptor.addChild(root_0, adaptor.create(set61));
+                if ( backtracking==0 ) adaptor.addChild(root_0, adaptor.create(set64));
                 errorRecovery=false;failed=false;
             }
             else {
@@ -2910,122 +3086,122 @@
     }
     // $ANTLR end unary_oper
 
-    // $ANTLR start synpred3
-    public final void synpred3_fragment() throws RecognitionException {   
-        // dd/grammar/ECAGrammar.g:40:12: ( binding SEPR bindings )
-        // dd/grammar/ECAGrammar.g:40:12: binding SEPR bindings
+    // $ANTLR start synpred1
+    public final void synpred1_fragment() throws RecognitionException {   
+        // dd/grammar/ECAGrammar.g:50:12: ( binding SEPR bindings )
+        // dd/grammar/ECAGrammar.g:50:12: binding SEPR bindings
         {
-        pushFollow(FOLLOW_binding_in_synpred3162);
+        pushFollow(FOLLOW_binding_in_synpred1218);
         binding();
         _fsp--;
         if (failed) return ;
-        match(input,SEPR,FOLLOW_SEPR_in_synpred3164); if (failed) return ;
-        pushFollow(FOLLOW_bindings_in_synpred3166);
+        match(input,SEPR,FOLLOW_SEPR_in_synpred1220); if (failed) return ;
+        pushFollow(FOLLOW_bindings_in_synpred1222);
         bindings();
         _fsp--;
         if (failed) return ;
 
         }
     }
-    // $ANTLR end synpred3
+    // $ANTLR end synpred1
 
-    // $ANTLR start synpred5
-    public final void synpred5_fragment() throws RecognitionException {   
-        // dd/grammar/ECAGrammar.g:67:4: ( action_expr SEPR action_expr_list )
-        // dd/grammar/ECAGrammar.g:67:4: action_expr SEPR action_expr_list
+    // $ANTLR start synpred3
+    public final void synpred3_fragment() throws RecognitionException {   
+        // dd/grammar/ECAGrammar.g:77:4: ( action_expr SEPR action_expr_list )
+        // dd/grammar/ECAGrammar.g:77:4: action_expr SEPR action_expr_list
         {
-        pushFollow(FOLLOW_action_expr_in_synpred5281);
+        pushFollow(FOLLOW_action_expr_in_synpred3337);
         action_expr();
         _fsp--;
         if (failed) return ;
-        match(input,SEPR,FOLLOW_SEPR_in_synpred5283); if (failed) return ;
-        pushFollow(FOLLOW_action_expr_list_in_synpred5285);
+        match(input,SEPR,FOLLOW_SEPR_in_synpred3339); if (failed) return ;
+        pushFollow(FOLLOW_action_expr_list_in_synpred3341);
         action_expr_list();
         _fsp--;
         if (failed) return ;
 
         }
     }
-    // $ANTLR end synpred5
+    // $ANTLR end synpred3
 
-    // $ANTLR start synpred6
-    public final void synpred6_fragment() throws RecognitionException {   
-        // dd/grammar/ECAGrammar.g:74:8: ( simple_expr infix_oper expr )
-        // dd/grammar/ECAGrammar.g:74:8: simple_expr infix_oper expr
+    // $ANTLR start synpred4
+    public final void synpred4_fragment() throws RecognitionException {   
+        // dd/grammar/ECAGrammar.g:84:8: ( simple_expr infix_oper expr )
+        // dd/grammar/ECAGrammar.g:84:8: simple_expr infix_oper expr
         {
-        pushFollow(FOLLOW_simple_expr_in_synpred6320);
+        pushFollow(FOLLOW_simple_expr_in_synpred4376);
         simple_expr();
         _fsp--;
         if (failed) return ;
-        pushFollow(FOLLOW_infix_oper_in_synpred6322);
+        pushFollow(FOLLOW_infix_oper_in_synpred4378);
         infix_oper();
         _fsp--;
         if (failed) return ;
-        pushFollow(FOLLOW_expr_in_synpred6324);
+        pushFollow(FOLLOW_expr_in_synpred4380);
         expr();
         _fsp--;
         if (failed) return ;
 
         }
     }
-    // $ANTLR end synpred6
+    // $ANTLR end synpred4
 
-    // $ANTLR start synpred7
-    public final void synpred7_fragment() throws RecognitionException {   
-        // dd/grammar/ECAGrammar.g:75:4: ( simple_expr )
-        // dd/grammar/ECAGrammar.g:75:4: simple_expr
+    // $ANTLR start synpred5
+    public final void synpred5_fragment() throws RecognitionException {   
+        // dd/grammar/ECAGrammar.g:85:4: ( simple_expr )
+        // dd/grammar/ECAGrammar.g:85:4: simple_expr
         {
-        pushFollow(FOLLOW_simple_expr_in_synpred7342);
+        pushFollow(FOLLOW_simple_expr_in_synpred5398);
         simple_expr();
         _fsp--;
         if (failed) return ;
 
         }
     }
-    // $ANTLR end synpred7
+    // $ANTLR end synpred5
 
-    // $ANTLR start synpred16
-    public final void synpred16_fragment() throws RecognitionException {   
-        // dd/grammar/ECAGrammar.g:91:4: ( expr SEPR expr_list )
-        // dd/grammar/ECAGrammar.g:91:4: expr SEPR expr_list
+    // $ANTLR start synpred14
+    public final void synpred14_fragment() throws RecognitionException {   
+        // dd/grammar/ECAGrammar.g:101:4: ( expr SEPR expr_list )
+        // dd/grammar/ECAGrammar.g:101:4: expr SEPR expr_list
         {
-        pushFollow(FOLLOW_expr_in_synpred16528);
+        pushFollow(FOLLOW_expr_in_synpred14584);
         expr();
         _fsp--;
         if (failed) return ;
-        match(input,SEPR,FOLLOW_SEPR_in_synpred16530); if (failed) return ;
-        pushFollow(FOLLOW_expr_list_in_synpred16532);
+        match(input,SEPR,FOLLOW_SEPR_in_synpred14586); if (failed) return ;
+        pushFollow(FOLLOW_expr_list_in_synpred14588);
         expr_list();
         _fsp--;
         if (failed) return ;
 
         }
     }
-    // $ANTLR end synpred16
+    // $ANTLR end synpred14
 
-    // $ANTLR start synpred17
-    public final void synpred17_fragment() throws RecognitionException {   
-        // dd/grammar/ECAGrammar.g:96:4: ( array_idx array_idx_list )
-        // dd/grammar/ECAGrammar.g:96:4: array_idx array_idx_list
+    // $ANTLR start synpred15
+    public final void synpred15_fragment() throws RecognitionException {   
+        // dd/grammar/ECAGrammar.g:106:4: ( array_idx array_idx_list )
+        // dd/grammar/ECAGrammar.g:106:4: array_idx array_idx_list
         {
-        pushFollow(FOLLOW_array_idx_in_synpred17560);
+        pushFollow(FOLLOW_array_idx_in_synpred15616);
         array_idx();
         _fsp--;
         if (failed) return ;
-        pushFollow(FOLLOW_array_idx_list_in_synpred17562);
+        pushFollow(FOLLOW_array_idx_list_in_synpred15618);
         array_idx_list();
         _fsp--;
         if (failed) return ;
 
         }
     }
-    // $ANTLR end synpred17
+    // $ANTLR end synpred15
 
-    public final boolean synpred16() {
+    public final boolean synpred4() {
         backtracking++;
         int start = input.mark();
         try {
-            synpred16_fragment(); // can never throw exception
+            synpred4_fragment(); // can never throw exception
         } catch (RecognitionException re) {
             System.err.println("impossible: "+re);
         }
@@ -3035,11 +3211,11 @@
         failed=false;
         return success;
     }
-    public final boolean synpred7() {
+    public final boolean synpred14() {
         backtracking++;
         int start = input.mark();
         try {
-            synpred7_fragment(); // can never throw exception
+            synpred14_fragment(); // can never throw exception
         } catch (RecognitionException re) {
             System.err.println("impossible: "+re);
         }
@@ -3063,11 +3239,11 @@
         failed=false;
         return success;
     }
-    public final boolean synpred5() {
+    public final boolean synpred1() {
         backtracking++;
         int start = input.mark();
         try {
-            synpred5_fragment(); // can never throw exception
+            synpred1_fragment(); // can never throw exception
         } catch (RecognitionException re) {
             System.err.println("impossible: "+re);
         }
@@ -3077,11 +3253,11 @@
         failed=false;
         return success;
     }
-    public final boolean synpred17() {
+    public final boolean synpred5() {
         backtracking++;
         int start = input.mark();
         try {
-            synpred17_fragment(); // can never throw exception
+            synpred5_fragment(); // can never throw exception
         } catch (RecognitionException re) {
             System.err.println("impossible: "+re);
         }
@@ -3091,11 +3267,11 @@
         failed=false;
         return success;
     }
-    public final boolean synpred6() {
+    public final boolean synpred15() {
         backtracking++;
         int start = input.mark();
         try {
-            synpred6_fragment(); // can never throw exception
+            synpred15_fragment(); // can never throw exception
         } catch (RecognitionException re) {
             System.err.println("impossible: "+re);
         }
@@ -3111,91 +3287,97 @@
 
     public static final BitSet FOLLOW_eca_in_eca_rule86 = new BitSet(new long[]{0x0000000000000000L});
     public static final BitSet FOLLOW_EOF_in_eca_rule88 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_WHEN_in_eca97 = new BitSet(new long[]{0x0100000000000000L});
-    public static final BitSet FOLLOW_event_in_eca99 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_IF_in_eca112 = new BitSet(new long[]{0x0510002008011000L});
-    public static final BitSet FOLLOW_condition_in_eca114 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_DO_in_eca128 = new BitSet(new long[]{0x0510002008011000L});
-    public static final BitSet FOLLOW_action_in_eca130 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_bindings_in_event150 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_binding_in_bindings162 = new BitSet(new long[]{0x0000000000400000L});
-    public static final BitSet FOLLOW_SEPR_in_bindings164 = new BitSet(new long[]{0x0100000000000000L});
-    public static final BitSet FOLLOW_bindings_in_bindings166 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_binding_in_bindings181 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_bind_sym_in_binding191 = new BitSet(new long[]{0x0000000001000000L});
-    public static final BitSet FOLLOW_ASSIGN_in_binding193 = new BitSet(new long[]{0x0510002008011000L});
-    public static final BitSet FOLLOW_expr_in_binding195 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_SYMBOL_in_bind_sym218 = new BitSet(new long[]{0x0000100000000000L});
-    public static final BitSet FOLLOW_COLON_in_bind_sym220 = new BitSet(new long[]{0x0100000000000000L});
-    public static final BitSet FOLLOW_SYMBOL_in_bind_sym224 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_SYMBOL_in_bind_sym242 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_expr_in_condition256 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_action_expr_list_in_action270 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_action_expr_in_action_expr_list281 = new BitSet(new long[]{0x0000000000400000L});
-    public static final BitSet FOLLOW_SEPR_in_action_expr_list283 = new BitSet(new long[]{0x0510002008011000L});
-    public static final BitSet FOLLOW_action_expr_list_in_action_expr_list285 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_action_expr_in_action_expr_list300 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_expr_in_action_expr310 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_simple_expr_in_expr320 = new BitSet(new long[]{0x000003DFF6000000L});
-    public static final BitSet FOLLOW_infix_oper_in_expr322 = new BitSet(new long[]{0x0510002008011000L});
-    public static final BitSet FOLLOW_expr_in_expr324 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_simple_expr_in_expr342 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_unary_oper_in_expr347 = new BitSet(new long[]{0x0510002008011000L});
-    public static final BitSet FOLLOW_expr_in_expr349 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_simple_expr_in_expr368 = new BitSet(new long[]{0x0000080000000000L});
-    public static final BitSet FOLLOW_TERN_IF_in_expr370 = new BitSet(new long[]{0x0510002008011000L});
-    public static final BitSet FOLLOW_expr_in_expr374 = new BitSet(new long[]{0x0000100000000000L});
-    public static final BitSet FOLLOW_COLON_in_expr376 = new BitSet(new long[]{0x0510002008011000L});
+    public static final BitSet FOLLOW_event_in_eca_event105 = new BitSet(new long[]{0x0000000000000000L});
+    public static final BitSet FOLLOW_EOF_in_eca_event107 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_condition_in_eca_condition123 = new BitSet(new long[]{0x0000000000000000L});
+    public static final BitSet FOLLOW_EOF_in_eca_condition125 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_action_in_eca_action141 = new BitSet(new long[]{0x0000000000000000L});
+    public static final BitSet FOLLOW_EOF_in_eca_action143 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_BIND_in_eca159 = new BitSet(new long[]{0x0100000000000000L});
+    public static final BitSet FOLLOW_event_in_eca163 = new BitSet(new long[]{0x0000000000004000L});
+    public static final BitSet FOLLOW_IF_in_eca167 = new BitSet(new long[]{0x0510002008011000L});
+    public static final BitSet FOLLOW_condition_in_eca171 = new BitSet(new long[]{0x0000000000008000L});
+    public static final BitSet FOLLOW_DO_in_eca175 = new BitSet(new long[]{0x0510002008011000L});
+    public static final BitSet FOLLOW_action_in_eca179 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_bindings_in_event206 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_binding_in_bindings218 = new BitSet(new long[]{0x0000000000400000L});
+    public static final BitSet FOLLOW_SEPR_in_bindings220 = new BitSet(new long[]{0x0100000000000000L});
+    public static final BitSet FOLLOW_bindings_in_bindings222 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_binding_in_bindings237 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_bind_sym_in_binding247 = new BitSet(new long[]{0x0000000001000000L});
+    public static final BitSet FOLLOW_ASSIGN_in_binding249 = new BitSet(new long[]{0x0510002008011000L});
+    public static final BitSet FOLLOW_expr_in_binding251 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_SYMBOL_in_bind_sym274 = new BitSet(new long[]{0x0000100000000000L});
+    public static final BitSet FOLLOW_COLON_in_bind_sym276 = new BitSet(new long[]{0x0100000000000000L});
+    public static final BitSet FOLLOW_SYMBOL_in_bind_sym280 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_SYMBOL_in_bind_sym298 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_expr_in_condition312 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_action_expr_list_in_action326 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_action_expr_in_action_expr_list337 = new BitSet(new long[]{0x0000000000400000L});
+    public static final BitSet FOLLOW_SEPR_in_action_expr_list339 = new BitSet(new long[]{0x0510002008011000L});
+    public static final BitSet FOLLOW_action_expr_list_in_action_expr_list341 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_action_expr_in_action_expr_list356 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_expr_in_action_expr366 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_simple_expr_in_expr376 = new BitSet(new long[]{0x000003DFF6000000L});
+    public static final BitSet FOLLOW_infix_oper_in_expr378 = new BitSet(new long[]{0x0510002008011000L});
     public static final BitSet FOLLOW_expr_in_expr380 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_DOLLARSYM_in_simple_expr407 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_SYMBOL_in_simple_expr414 = new BitSet(new long[]{0x0000000000040000L});
-    public static final BitSet FOLLOW_array_idx_in_simple_expr418 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_SYMBOL_in_simple_expr439 = new BitSet(new long[]{0x0000000000010000L});
-    public static final BitSet FOLLOW_LPAREN_in_simple_expr441 = new BitSet(new long[]{0x0000000000020000L});
-    public static final BitSet FOLLOW_RPAREN_in_simple_expr443 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_SYMBOL_in_simple_expr461 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_SYMBOL_in_simple_expr468 = new BitSet(new long[]{0x0000000000010000L});
-    public static final BitSet FOLLOW_LPAREN_in_simple_expr470 = new BitSet(new long[]{0x0510002008011000L});
-    public static final BitSet FOLLOW_expr_list_in_simple_expr474 = new BitSet(new long[]{0x0000000000020000L});
-    public static final BitSet FOLLOW_RPAREN_in_simple_expr476 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_NUMBER_in_simple_expr495 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_STRING_in_simple_expr500 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_LPAREN_in_simple_expr505 = new BitSet(new long[]{0x0510002008011000L});
-    public static final BitSet FOLLOW_expr_in_simple_expr507 = new BitSet(new long[]{0x0000000000020000L});
-    public static final BitSet FOLLOW_RPAREN_in_simple_expr509 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_expr_in_expr_list528 = new BitSet(new long[]{0x0000000000400000L});
-    public static final BitSet FOLLOW_SEPR_in_expr_list530 = new BitSet(new long[]{0x0510002008011000L});
-    public static final BitSet FOLLOW_expr_list_in_expr_list532 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_expr_in_expr_list549 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_array_idx_in_array_idx_list560 = new BitSet(new long[]{0x0000000000040000L});
-    public static final BitSet FOLLOW_array_idx_list_in_array_idx_list562 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_array_idx_in_array_idx_list579 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_LSQUARE_in_array_idx590 = new BitSet(new long[]{0x0510002008011000L});
-    public static final BitSet FOLLOW_expr_in_array_idx592 = new BitSet(new long[]{0x0000000000080000L});
-    public static final BitSet FOLLOW_RSQUARE_in_array_idx594 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_infix_bit_oper_in_infix_oper612 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_infix_arith_oper_in_infix_oper617 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_infix_bool_oper_in_infix_oper622 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_infix_cmp_oper_in_infix_oper627 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_simple_expr_in_expr398 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_unary_oper_in_expr403 = new BitSet(new long[]{0x0510002008011000L});
+    public static final BitSet FOLLOW_expr_in_expr405 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_simple_expr_in_expr424 = new BitSet(new long[]{0x0000080000000000L});
+    public static final BitSet FOLLOW_TERN_IF_in_expr426 = new BitSet(new long[]{0x0510002008011000L});
+    public static final BitSet FOLLOW_expr_in_expr430 = new BitSet(new long[]{0x0000100000000000L});
+    public static final BitSet FOLLOW_COLON_in_expr432 = new BitSet(new long[]{0x0510002008011000L});
+    public static final BitSet FOLLOW_expr_in_expr436 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_DOLLARSYM_in_simple_expr463 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_SYMBOL_in_simple_expr470 = new BitSet(new long[]{0x0000000000040000L});
+    public static final BitSet FOLLOW_array_idx_in_simple_expr474 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_SYMBOL_in_simple_expr495 = new BitSet(new long[]{0x0000000000010000L});
+    public static final BitSet FOLLOW_LPAREN_in_simple_expr497 = new BitSet(new long[]{0x0000000000020000L});
+    public static final BitSet FOLLOW_RPAREN_in_simple_expr499 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_SYMBOL_in_simple_expr517 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_SYMBOL_in_simple_expr524 = new BitSet(new long[]{0x0000000000010000L});
+    public static final BitSet FOLLOW_LPAREN_in_simple_expr526 = new BitSet(new long[]{0x0510002008011000L});
+    public static final BitSet FOLLOW_expr_list_in_simple_expr530 = new BitSet(new long[]{0x0000000000020000L});
+    public static final BitSet FOLLOW_RPAREN_in_simple_expr532 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_NUMBER_in_simple_expr551 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_STRING_in_simple_expr556 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_LPAREN_in_simple_expr561 = new BitSet(new long[]{0x0510002008011000L});
+    public static final BitSet FOLLOW_expr_in_simple_expr563 = new BitSet(new long[]{0x0000000000020000L});
+    public static final BitSet FOLLOW_RPAREN_in_simple_expr565 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_expr_in_expr_list584 = new BitSet(new long[]{0x0000000000400000L});
+    public static final BitSet FOLLOW_SEPR_in_expr_list586 = new BitSet(new long[]{0x0510002008011000L});
+    public static final BitSet FOLLOW_expr_list_in_expr_list588 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_expr_in_expr_list605 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_array_idx_in_array_idx_list616 = new BitSet(new long[]{0x0000000000040000L});
+    public static final BitSet FOLLOW_array_idx_list_in_array_idx_list618 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_array_idx_in_array_idx_list635 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_LSQUARE_in_array_idx646 = new BitSet(new long[]{0x0510002008011000L});
+    public static final BitSet FOLLOW_expr_in_array_idx648 = new BitSet(new long[]{0x0000000000080000L});
+    public static final BitSet FOLLOW_RSQUARE_in_array_idx650 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_infix_bit_oper_in_infix_oper668 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_infix_arith_oper_in_infix_oper673 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_infix_bool_oper_in_infix_oper678 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_infix_cmp_oper_in_infix_oper683 = new BitSet(new long[]{0x0000000000000002L});
     public static final BitSet FOLLOW_set_in_infix_bit_oper0 = new BitSet(new long[]{0x0000000000000002L});
     public static final BitSet FOLLOW_set_in_infix_arith_oper0 = new BitSet(new long[]{0x0000000000000002L});
     public static final BitSet FOLLOW_set_in_infix_bool_oper0 = new BitSet(new long[]{0x0000000000000002L});
     public static final BitSet FOLLOW_set_in_infix_cmp_oper0 = new BitSet(new long[]{0x0000000000000002L});
     public static final BitSet FOLLOW_set_in_unary_oper0 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_binding_in_synpred3162 = new BitSet(new long[]{0x0000000000400000L});
-    public static final BitSet FOLLOW_SEPR_in_synpred3164 = new BitSet(new long[]{0x0100000000000000L});
-    public static final BitSet FOLLOW_bindings_in_synpred3166 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_action_expr_in_synpred5281 = new BitSet(new long[]{0x0000000000400000L});
-    public static final BitSet FOLLOW_SEPR_in_synpred5283 = new BitSet(new long[]{0x0510002008011000L});
-    public static final BitSet FOLLOW_action_expr_list_in_synpred5285 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_simple_expr_in_synpred6320 = new BitSet(new long[]{0x000003DFF6000000L});
-    public static final BitSet FOLLOW_infix_oper_in_synpred6322 = new BitSet(new long[]{0x0510002008011000L});
-    public static final BitSet FOLLOW_expr_in_synpred6324 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_simple_expr_in_synpred7342 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_expr_in_synpred16528 = new BitSet(new long[]{0x0000000000400000L});
-    public static final BitSet FOLLOW_SEPR_in_synpred16530 = new BitSet(new long[]{0x0510002008011000L});
-    public static final BitSet FOLLOW_expr_list_in_synpred16532 = new BitSet(new long[]{0x0000000000000002L});
-    public static final BitSet FOLLOW_array_idx_in_synpred17560 = new BitSet(new long[]{0x0000000000040000L});
-    public static final BitSet FOLLOW_array_idx_list_in_synpred17562 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_binding_in_synpred1218 = new BitSet(new long[]{0x0000000000400000L});
+    public static final BitSet FOLLOW_SEPR_in_synpred1220 = new BitSet(new long[]{0x0100000000000000L});
+    public static final BitSet FOLLOW_bindings_in_synpred1222 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_action_expr_in_synpred3337 = new BitSet(new long[]{0x0000000000400000L});
+    public static final BitSet FOLLOW_SEPR_in_synpred3339 = new BitSet(new long[]{0x0510002008011000L});
+    public static final BitSet FOLLOW_action_expr_list_in_synpred3341 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_simple_expr_in_synpred4376 = new BitSet(new long[]{0x000003DFF6000000L});
+    public static final BitSet FOLLOW_infix_oper_in_synpred4378 = new BitSet(new long[]{0x0510002008011000L});
+    public static final BitSet FOLLOW_expr_in_synpred4380 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_simple_expr_in_synpred5398 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_expr_in_synpred14584 = new BitSet(new long[]{0x0000000000400000L});
+    public static final BitSet FOLLOW_SEPR_in_synpred14586 = new BitSet(new long[]{0x0510002008011000L});
+    public static final BitSet FOLLOW_expr_list_in_synpred14588 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_array_idx_in_synpred15616 = new BitSet(new long[]{0x0000000000040000L});
+    public static final BitSet FOLLOW_array_idx_list_in_synpred15618 = new BitSet(new long[]{0x0000000000000002L});
 
 }
\ No newline at end of file

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/grammar/ECATokenLexer.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/grammar/ECATokenLexer.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/grammar/ECATokenLexer.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,4 +1,4 @@
-// $ANTLR 3.0.1 dd/grammar/ECAToken.g 2008-07-18 14:15:59
+// $ANTLR 3.0.1 dd/grammar/ECAToken.g 2008-09-12 18:02:58
 
 package org.jboss.jbossts.orchestration.rule.grammar;
 
@@ -41,7 +41,6 @@
     public static final int LSQUARE=18;
     public static final int DO=15;
     public static final int POINT=9;
-    public static final int WHEN=13;
     public static final int BARESYM=53;
     public static final int SEPR=22;
     public static final int WS=59;
@@ -64,6 +63,7 @@
     public static final int DIV=39;
     public static final int DOTSYM=55;
     public static final int BXOR=36;
+    public static final int BIND=13;
     public static final int NOT=27;
     public static final int UNDERSCORE=46;
     public static final int DOLLARSYM=58;
@@ -363,14 +363,14 @@
     }
     // $ANTLR end NUMBER
 
-    // $ANTLR start WHEN
-    public final void mWHEN() throws RecognitionException {
+    // $ANTLR start BIND
+    public final void mBIND() throws RecognitionException {
         try {
-            int _type = WHEN;
-            // dd/grammar/ECAToken.g:47:6: ( 'WHEN' )
-            // dd/grammar/ECAToken.g:47:8: 'WHEN'
+            int _type = BIND;
+            // dd/grammar/ECAToken.g:47:6: ( 'BIND' )
+            // dd/grammar/ECAToken.g:47:8: 'BIND'
             {
-            match("WHEN"); 
+            match("BIND"); 
 
 
             }
@@ -380,7 +380,7 @@
         finally {
         }
     }
-    // $ANTLR end WHEN
+    // $ANTLR end BIND
 
     // $ANTLR start IF
     public final void mIF() throws RecognitionException {
@@ -2078,7 +2078,7 @@
     // $ANTLR end WS
 
     public void mTokens() throws RecognitionException {
-        // dd/grammar/ECAToken.g:1:8: ( NUMBER | WHEN | IF | DO | LPAREN | RPAREN | LSQUARE | RSQUARE | LBRACE | RBRACE | SEPR | DOT | ASSIGN | OR | AND | NOT | EQ | NEQ | GT | LT | GEQ | LEQ | BOR | BAND | BXOR | TWIDDLE | MUL | DIV | PLUS | MINUS | MOD | TERN_IF | COLON | QUOTE | DQUOTE | STRING | SYMBOL | DOLLARSYM | WS )
+        // dd/grammar/ECAToken.g:1:8: ( NUMBER | BIND | IF | DO | LPAREN | RPAREN | LSQUARE | RSQUARE | LBRACE | RBRACE | SEPR | DOT | ASSIGN | OR | AND | NOT | EQ | NEQ | GT | LT | GEQ | LEQ | BOR | BAND | BXOR | TWIDDLE | MUL | DIV | PLUS | MINUS | MOD | TERN_IF | COLON | QUOTE | DQUOTE | STRING | SYMBOL | DOLLARSYM | WS )
         int alt28=39;
         switch ( input.LA(1) ) {
         case '+':
@@ -2106,17 +2106,17 @@
             alt28=1;
             }
             break;
-        case 'W':
+        case 'B':
             {
             int LA28_3 = input.LA(2);
 
-            if ( (LA28_3=='H') ) {
+            if ( (LA28_3=='I') ) {
                 int LA28_53 = input.LA(3);
 
-                if ( (LA28_53=='E') ) {
+                if ( (LA28_53=='N') ) {
                     int LA28_100 = input.LA(4);
 
-                    if ( (LA28_100=='N') ) {
+                    if ( (LA28_100=='D') ) {
                         int LA28_123 = input.LA(5);
 
                         if ( (LA28_123=='.'||(LA28_123>='0' && LA28_123<='9')||(LA28_123>='A' && LA28_123<='Z')||LA28_123=='_'||(LA28_123>='a' && LA28_123<='z')) ) {
@@ -3007,7 +3007,6 @@
                 alt28=35;}
             }
             break;
-        case 'B':
         case 'C':
         case 'F':
         case 'H':
@@ -3018,6 +3017,7 @@
         case 'S':
         case 'U':
         case 'V':
+        case 'W':
         case 'X':
         case 'Y':
         case 'Z':
@@ -3057,7 +3057,7 @@
             break;
         default:
             NoViableAltException nvae =
-                new NoViableAltException("1:1: Tokens : ( NUMBER | WHEN | IF | DO | LPAREN | RPAREN | LSQUARE | RSQUARE | LBRACE | RBRACE | SEPR | DOT | ASSIGN | OR | AND | NOT | EQ | NEQ | GT | LT | GEQ | LEQ | BOR | BAND | BXOR | TWIDDLE | MUL | DIV | PLUS | MINUS | MOD | TERN_IF | COLON | QUOTE | DQUOTE | STRING | SYMBOL | DOLLARSYM | WS );", 28, 0, input);
+                new NoViableAltException("1:1: Tokens : ( NUMBER | BIND | IF | DO | LPAREN | RPAREN | LSQUARE | RSQUARE | LBRACE | RBRACE | SEPR | DOT | ASSIGN | OR | AND | NOT | EQ | NEQ | GT | LT | GEQ | LEQ | BOR | BAND | BXOR | TWIDDLE | MUL | DIV | PLUS | MINUS | MOD | TERN_IF | COLON | QUOTE | DQUOTE | STRING | SYMBOL | DOLLARSYM | WS );", 28, 0, input);
 
             throw nvae;
         }
@@ -3071,9 +3071,9 @@
                 }
                 break;
             case 2 :
-                // dd/grammar/ECAToken.g:1:17: WHEN
+                // dd/grammar/ECAToken.g:1:17: BIND
                 {
-                mWHEN(); 
+                mBIND(); 
 
                 }
                 break;

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/type/Type.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/type/Type.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/type/Type.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -1,46 +1,106 @@
 package org.jboss.jbossts.orchestration.rule.type;
 
+import org.jboss.jbossts.orchestration.rule.exception.TypeException;
+
 import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
 
 /**
  * models the type of a rule binding or expression
  */
 public class Type {
-    // TODO we need eventually to be able to create array types
     /**
      * create a type with a given name and optionally an associated class
      *
-     * @param clazzName the name of the type which may or may not be fully qualified
+     * @param typeName the name of the type which may or may not be fully qualified
      * @param clazz the class associated with this name if it is know otherwise null
      */
-    public Type(String clazzName, Class clazz)
+    public Type(String typeName, Class clazz)
     {
-        this(clazzName, clazz, F_OBJECT);
+        this(typeName, clazz, F_OBJECT, null);
     }
 
     /**
      * create a type with a given name and no associated class
      *
-     * @param clazzName the name of the type which may or may not be fully qualified
+     * @param typeName the name of the type which may or may not be fully qualified
      */
-    public Type(String clazzName)
+    public Type(String typeName)
     {
-        this(clazzName, null);
+        this(typeName, null);
     }
 
     /**
+     * create an array type from this base type
+     * @return an array type
+     */
+    public Type arrayType()
+    {
+        return arrayType(null);
+    }
+    
+    /**
+     * create an array type from this base type
+     * @param clazz the class for the array type derived from the class of base type or
+     * null if the base type is undefined
+     * @return an array type
+     */
+    public Type arrayType(Class clazz)
+    {
+        if (this.arrayType ==  null) {
+            arrayType = new Type(typeName + "[]", clazz, F_ARRAY, this);
+        }
+        return arrayType;
+    }
+
+    /**
+     * retrieve the base type for an array type or null if this is not an array type
+     * @return an array type
+     */                                    
+    public Type getBaseType()
+    {
+        if (isArray()) {
+            return baseType;
+        } else {
+            return null;
+        }
+    }
+
+    /**
      * get the possibly unqualified name with which this type was created
-     * @return
+     * @return the type name
      */
     public String getName()
     {
-        return clazzName;
+        return typeName;
     }
 
     /**
-     * get the class associated with this type or a special undefined class if the type is not defined
-     * @return
+     * get the internal name for this type used by the class loader. this is only valid for
+     * defined types, defined array types or primitive types
+     * @return the type name
      */
+    public String getInternalName()
+    {
+        if (isArray()) {
+            return "[" + baseType.getInternalName();
+        } else if (isPrimitive()) {
+            return internalNames.get(typeName);
+        } else {
+            String name = aliasFor.getTargetClass().getCanonicalName();
+            if (name.indexOf(';') == 0) {
+                name = "L" + name + ";";
+            }
+            return name;
+        }
+    }
+
+    /**
+     * get the class associated with this type if it has one or a special undefined class if
+     * the type is not defined or null if there is no associated class
+     * @return the associated class
+     */
     public Class getTargetClass()
     {
         return clazz;
@@ -49,7 +109,7 @@
     /**
      * get the package component of the name associated with this type or the empty String
      * if it has no package or is was defiend with an unqualified name or is a builtin type
-     * @return
+     * @return the package component or an empty string
      */
 
     public String getPackageName()
@@ -79,6 +139,34 @@
         return target;
     }
 
+    public void resolve(ClassLoader loader)
+    {
+        if (this.isDefined() || this == Type.UNDEFINED || this == Type.N) {
+            return;
+        }
+        if (aliasFor != this) {
+            aliasFor.resolve(loader);
+            clazz = aliasFor.clazz;
+            if (clazz != null) {
+                flags &= ~F_UNKNOWN;
+            }
+        } else {
+            try {
+                clazz = loader.loadClass(getName());
+                flags &= ~F_UNKNOWN;
+            } catch (ClassNotFoundException e) {
+                // ok give up here -- we should get a type error later
+            }
+        }
+        // ensure any classes which might have been created via this one are also resolved
+        if (baseType != null) {
+            baseType.resolve(loader);
+        }
+        if (arrayType != null) {
+            arrayType.resolve(loader);
+        }
+    }
+
     /**
      * attempt to establish an alias from an package unqualified named object type to a package
      * qualified named object type whose unqualified name equals this type's name
@@ -105,14 +193,41 @@
             // we assume the caller has already checked that the names match so . . .
             // update the alias
             aliasFor = target;
+            // propagate type-class binding up or down alias link if available
+            if (clazz != null) {
+                if (target.clazz != null) {
+                    // oops class mismatch!
+                    return false;
+                } else {
+                    target.clazz = clazz;
+                    target.flags &= ~F_UNKNOWN;
+                }
+            } else if (target.clazz != null) {
+                clazz = target.clazz;
+                flags &= ~F_UNKNOWN;
+            }
+            if (arrayType != null) {
+                // ensure array types are also aliased
+                if (target.arrayType == null) {
+                    target.arrayType(target.clazz);
+                }
+                arrayType.aliasTo(target.arrayType);
+            } else if (target.arrayType != null) {
+                // point the array type at the target array type
+                arrayType = arrayType(this.clazz);
+                arrayType.aliasTo(target.arrayType);
+            }
             return true;
         }
     }
 
     /**
      * check whether this type can be assigned with values of the supplied type including
-     * the case where numeric conversion from known or unknown numeric types but excluding any other
-     * cases where this type is undefined
+     * the case where numeric conversion from known or unknown numeric types but excluding
+     * any other cases where this type is undefined
+     *
+     * n.b. the caller must dereference the recipient and argument types before calling
+     * this method
      * @param type the type poviding RHS values
      * @return true if it is known that the assignment is valid, false if it is not known to be valid or
      * is known not to be valid
@@ -120,158 +235,227 @@
 
     public boolean isAssignableFrom(Type type)
     {
-        return isAssignableFrom(type.clazz);
-    }
-
-    /**
-     * check whether this type can be assigned with values of the supplied class including
-     * the case where numeric conversion from known or unknown numeric types but excluding any other
-     * cases where this type is undefined
-     * @param clazz the class poviding RHS values
-     * @return true if it is known that the assignment is valid, false if it is not known to be valid or
-     * is known not to be valid
-     */
-    public boolean isAssignableFrom(Class clazz)
-    {
-        if (isUndefined()) {
-            // we can only say yes if this is void or if it is an undefined numeric type
-            if (isVoid()) {
-                return true;
-            } else if (isNumeric()) {
-                // we can coerce any numeric type to another numeric type
-                Type type = primitiveType(clazz);
-                if (type != null && type.isNumeric()) {
-                    return true;
-                }
-            }
-            // we don't know if we can coerce here so return false to play safe
+        if (this.aliasFor != this) {
+            return Type.dereference(this).isAssignableFrom(type);
+        }
+        type = Type.dereference(type);
+        
+        // check for unknown cases first
+        if (isNumeric()) {
+            // can always coerce numerics even if it involves boxing
+            return type.isNumeric();
+        } else if (isUndefined() || type.isUndefined()) {
+            // cannot answer this question yet
             return false;
+        } else if (this == type) {
+            return true;
+        } else if (isString()) {
+            // can always convert anything to a string via boxing and Object.toString();
+            return true;
+        } else if (isVoid()) {
+            // can always assign to void
+            return true;
+        } else if (isPrimitive()) {
+           if (!type.isPrimitive()) {
+               // see if we can arrive at the correct type by boxing or unboxing
+               Type boxedType = boxedTypes.get(type);
+               return (boxedType == this);
+           } else {
+               return false;
+           }
+        } else if (type.isPrimitive()) {
+            // see if we can arrive at the correct type by boxing or unboxing
+            Type boxedType = boxedTypes.get(type);
+            return (boxedType == this);
         } else if (isObject()) {
-            // we can always convert something to a string unless it is void
-            if (this == STRING) {
-                return (clazz != void.class);
-            }
+            // we only get here if we have a known type i.e. both clazz values ar enon-null
             // see if the supplied type is assignable from this type's class
-            if (this.clazz.isAssignableFrom(clazz)) {
-                return true;
-            }
+            return (this.clazz.isAssignableFrom(type.clazz));
+        } else {
             return false;
-        } else {
-
-            // ok this is a defined primitive type check that clazz is primitive
-
-            Type type = primitiveType(clazz);
-
-            if (type == null) {
-                return false;
-            }
-            // ok we can proceed if the two types have overlapping flags
-            return ((this.flags & type.flags) != 0);
         }
     }
 
+    /**
+     * test if this type is an unknown type. a type may be unknown either because it is one
+     * of the pseudo types used as type variables or because it represents an object type
+     * mentioned in a rule but not yet bound to a specific class
+     * @return true if the type is unknown otherwise false
+     */
     public boolean isUndefined()
     {
         return ((flags & F_UNKNOWN) != 0);
     }
 
+    /**
+     * check if this type is a known type. this is just teh oppositeof isUndefined
+     * @return false if the type is unknown otherwise true
+     */
     public boolean isDefined()
     {
         return !isUndefined();
     }
 
+    /**
+     * return true if this is a type mentioned in a rule but not yet bound to a specific class
+     * @return true if the type is not yet bound to a specific class
+     */
+    public boolean isUnbound()
+    {
+        // this only happens where we have an object type marked with the UNKNOWN marker
+
+        return (flags & (F_OBJECT | F_UNKNOWN)) == (F_OBJECT | F_UNKNOWN);
+    }
+
+    /**
+     * return true if this is a primitive value type
+     * @return true if this is a primitive value type
+     */
     public boolean isPrimitive()
     {
-        return ((flags & F_PRIMITIVE) != 0);
+        return (flags & F_PRIMITIVE) != 0;
     }
 
+    /**
+     * return true if this is a value type, which includes the boxed versions of primitive types
+     * @return true if this is a value type
+     */
+    public boolean isValue()
+    {
+        return (flags & F_VALUE) != 0;
+    }
+
+    /**
+     * return true if this is the void type
+     * @return true if this is void type
+     */
     public boolean isVoid()
     {
-        return this == VOID;
+        return (flags & F_VOID) != 0;
     }
 
+    /**
+     * return true if this is the string type
+     * @return true if this is string type
+     */
+    public boolean isString()
+    {
+        return (flags & F_STRING) != 0;
+    }
+
+    /**
+     * return true if this is a numeric type, including the unknown primitive numeric type
+     * @return true if this is a numeric type
+     */
     public boolean isNumeric()
     {
         return (flags & F_NUMERIC) != 0;
     }
 
+    /**
+     * return true if this is an integral type of whatever size, including the unknown
+     * primitive numeric type
+     * @return true if this is an integral type
+     */
     public boolean isIntegral()
     {
         return (flags & F_INTEGRAL) == F_INTEGRAL;
     }
 
+    /**
+     * return true if this is a floating type of whatever size, including the unknown
+     * primitive numeric type
+     * @return true if this is a floating type
+     */
     public boolean isFloating()
     {
         return (flags & F_FLOATING) == F_FLOATING;
     }
 
+    /**
+     * return true if this is a boolean type
+     * @return true if this is a boolean type
+     */
     public boolean isBoolean()
     {
         return (flags & F_BOOLEAN) != 0;
     }
 
+    /**
+     * return true if this is an object type, including unbound types mentioned in rules
+     * @return true if this is an object type
+     */
     public boolean isObject()
     {
         return (flags & F_OBJECT) != 0;
     }
 
+    /**
+     * return true if this is an array type
+     * @return true if this is an array type
+     */
     public boolean isArray()
     {
         return (flags & F_ARRAY) != 0;
     }
 
+    /**
+     * return the builtin type associated with a given class
+     * @return the corresponding builtin type
+     */
     public Type builtinType(Class clazz)
     {
         return builtinTypes.get(clazz.getName());
     }
 
+    /**
+     * return the primitive type whose boxed equivalent is associated with a given class
+     * @return the corresponding primitive type
+     */
     public Type primitiveType(Class clazz)
     {
         Type type = builtinType(clazz);
 
-        if (type != null && type.isDefined() && type.isPrimitive())
-        {
-            return type;
-        }
-
-        return null;
+        return boxedTypes.get(type);
     }
 
-    public Type numericType(Class clazz) {
-        Type type = builtinType(clazz);
-
-        if (type != null && type.isNumeric())
-        {
-            return type;
-        }
-
-        return null;
-    }
-
-    private String clazzName;
+    private String typeName;
     private Class clazz;
     private String packageName;
     private int flags;
     private Type aliasFor;
+    private Type baseType;
+    private Type arrayType;
 
-    private Type(String clazzName, Class clazz, int flags)
+    protected Type(String typeName, Class clazz, int flags)
     {
-        this.clazzName = clazzName;
+        this(typeName, clazz, flags, null);
+    }
 
-        if (clazz != null) {
-            this.clazz = clazz;
-        } else {
-            this.clazz = Undefined.class;
-            flags |= F_UNKNOWN; 
+    protected Type(String typeName, Class clazz, int flags, Type baseType)
+    {
+        this.typeName = typeName;
+
+        if (clazz == null && (flags & F_PRIMITIVE) == 0) {
+            flags |= F_UNKNOWN;
         }
 
+        this.clazz = clazz;
+
         this.flags = flags;
+        if ((flags & F_ARRAY) != 0) {
+            this.baseType = baseType;
+            baseType.arrayType = this;
+        } else {
+            this.baseType = null;
+        }
+        this.arrayType = null;
 
         // types dereference to themselves until they are aliased
 
         aliasFor = this;
 
-        packageName = packagePart(clazzName);
+        packageName = packagePart(typeName);
     }
 
     /**
@@ -286,38 +470,38 @@
         if (!type1.isNumeric() || !type2.isNumeric()) {
             // should not happen!
             System.err.println("Type.promote : unexpected non-numeric type argument");
-            return Type.NUMBER;
+            return N;
         } else if (type1.isUndefined() || type2.isUndefined()) {
                 // don't know for sure which is which so return undefined numeric
-                return Type.NUMBER;
+                return N;
         } else if (type1.isFloating() || type2.isFloating()) {
-            if (type1 == DOUBLE || type2 == DOUBLE) {
-                return DOUBLE;
+            if (type1 == DOUBLE || type2 == DOUBLE || type1 == D || type2 == D) {
+                return D;
             } else {
-                return FLOAT;
+                return F;
             }
         } else {
             // integral types -- ok lets invent^H^H^H declare some rules here :-)
             // either arg long forces a long result
-            // either arg integer forces an integer result
+            // either arg integer forces an int result
             // a matched pair of short, char or byte arguments retains the same type in the result
-            // otherwise the result is an integer and args wil be coerced to integer
-            if (type1 == LONG || type2 == LONG) {
-                return LONG;
-            } else if (type1 == INTEGER || type2 == INTEGER) {
-                return INTEGER;
-            } else if (type1 == SHORT && type2 == SHORT) {
+            // otherwise the result is an int and args will be coerced to int
+            if (type1 == LONG || type2 == LONG || type1 == J || type2 == J) {
+                return J;
+            } else if (type1 == INTEGER || type2 == INTEGER || type1 == I || type2 == I) {
+                return I;
+            } else if ((type1 == SHORT || type1 == S) && (type2 == SHORT || type2 == S)) {
                 return SHORT;
-            } else if (type1 == CHARACTER && type2 == CHARACTER) {
-                return CHARACTER;
-            } else if (type1 == BYTE && type2 == BYTE) {
-                return BYTE;
+            } else if ((type1 == CHARACTER || type1 == C) && (type2 == CHARACTER || type2 == C)) {
+                return C;
+            } else if ((type1 == BYTE || type1 == B) && (type2 == BYTE || type2 == B)) {
+                return B;
             } else {
                 return INTEGER;
             }
         }
     }
-
+    /* TODO we don't seem to need this?
     private static String classPart(String className)
     {
         int dotIdx = className.lastIndexOf('.');
@@ -328,7 +512,7 @@
             return className.substring(dotIdx);
         }
     }
-
+    */
     private static String packagePart(String className)
     {
         int dotIdx = className.lastIndexOf('.');
@@ -340,38 +524,224 @@
         }
     }
 
+    public static List<String> parseDescriptor(String descriptor, boolean includeReturnType)
+    {
+        List<String> argTypes = new ArrayList<String>();
+        int length = descriptor.length();
+        int idx = descriptor.indexOf("(");
+        int arrayDepth = 0;
+        if (idx < 0) {
+            return null;
+        }
+        idx = idx + 1;
+        while (idx < length) {
+            char c = descriptor.charAt(idx);
+            switch(descriptor.charAt(idx))
+            {
+                case 'Z':
+                {
+                    String baseType = "boolean";
+                    argTypes.add(fixArrayType(baseType, arrayDepth));
+                    arrayDepth = 0;
+                    idx++;
+                }
+                break;
+                case 'B':
+                {
+                    String baseType = "byte";
+                    argTypes.add(fixArrayType(baseType, arrayDepth));
+                    arrayDepth = 0;
+                    idx++;
+                }
+                break;
+                case 'S':
+                {
+                    String baseType = "short";
+                    argTypes.add(fixArrayType(baseType, arrayDepth));
+                    arrayDepth = 0;
+                    idx++;
+                }
+                break;
+                case 'C':
+                {
+                    String baseType = "char";
+                    argTypes.add(fixArrayType(baseType, arrayDepth));
+                    arrayDepth = 0;
+                    idx++;
+                }
+                break;
+                case 'I':
+                {
+                    String baseType = "int";
+                    argTypes.add(fixArrayType(baseType, arrayDepth));
+                    arrayDepth = 0;
+                    idx++;
+                }
+                break;
+                case 'J':
+                {
+                    String baseType = "long";
+                    argTypes.add(fixArrayType(baseType, arrayDepth));
+                    arrayDepth = 0;
+                    idx++;
+                }
+                break;
+                case 'F':
+                {
+                    String baseType = "float";
+                    argTypes.add(fixArrayType(baseType, arrayDepth));
+                    arrayDepth = 0;
+                    idx++;
+                }
+                break;
+                case 'D':
+                {
+                    String baseType = "double";
+                    argTypes.add(fixArrayType(baseType, arrayDepth));
+                    arrayDepth = 0;
+                    idx++;
+                }
+                break;
+                case 'V':
+                {
+                    if (arrayDepth != 0) {
+                        // hmm void arrays are definitely not kosher
+                        return null;
+                    } else if (!includeReturnType) {
+                        // hmm should not have got here
+                        return null;
+                    }
+                    argTypes.add("void");
+                    idx++;
+                }
+                break;
+                case 'L':
+                {
+                    int endIdx = descriptor.indexOf(';', idx);
+                    if (endIdx < 0) {
+                        return null;
+                    }
+                    String baseType = descriptor.substring(idx+1, endIdx).replaceAll("/", ".");
+                    argTypes.add(fixArrayType(baseType, arrayDepth));
+                    arrayDepth = 0;
+                    idx = endIdx + 1;
+                }
+                break;
+                case '[':
+                {
+                    arrayDepth++;
+                    idx++;
+                }
+                break;
+                case ')':
+                {
+                    if (arrayDepth != 0) {
+                        return null;
+                    } else if (!includeReturnType) {
+                        // stop here
+                        return argTypes;
+                    } else {
+                        // skip any trailing spaces before the return type
+                        idx++;
+                        while (descriptor.charAt(idx) == ' ')
+                        {
+                            idx++;
+                        }
+                    }
+                }
+                break;
+                default:
+                    return null;
+            }
+        }
+
+        return (arrayDepth == 0 ? argTypes : null);
+    }
+
+    public static String fixArrayType(String baseType, int dimension)
+    {
+        String result = baseType;
+
+        for (int i = 0; i < dimension; i++) {
+            result  += "[]";
+        }
+        
+        return result;
+    }
+
     // private class used to type unknown types
     private static class Undefined {
-    };
+    }
+    // tags divide types into exclusive categories
+    // unknown types may be associated with a tag group such as numeric, or object
+    // markers are used to identify type properties such as unknown, primitive
 
-    final public static int F_UNKNOWN       = 0x1000;
+    // value type tags
     final public static int F_BOOLEAN       = 0x0001;
     final public static int F_INTEGRAL      = 0x0002;
     final public static int F_FLOATING      = 0x0004;
-    final public static int F_NUMERIC       = F_UNKNOWN | F_INTEGRAL | F_FLOATING;
-    final public static int F_PRIMITIVE     = F_UNKNOWN | F_BOOLEAN | F_NUMERIC;
+    // object type tag
     final public static int F_OBJECT        = 0x0008;
-    final public static int F_ARRAY         = 0x0010;
-    final public static int F_ANY           = F_UNKNOWN | F_PRIMITIVE | F_OBJECT | F_ARRAY;
+    // void type tag
+    final public static int F_VOID          = 0x0010;
+    // array type tag
+    final public static int F_ARRAY         = 0x0020;
 
+    // value type tag groups
+    final public static int F_NUMERIC       = F_INTEGRAL | F_FLOATING;
+    final public static int F_VALUE         = F_BOOLEAN | F_NUMERIC;
+
+    // unknown type marker
+    final public static int F_UNKNOWN       = 0x1000;
+    // primitive type marker
+    final public static int F_PRIMITIVE     = 0x2000;
+    // string type marker
+    final public static int F_STRING        = 0x4000;
+
     // we need to cope with array types
-    final public static Type BOOLEAN = new Type("Boolean", Boolean.class, F_BOOLEAN);
-    final public static Type BYTE = new Type("Byte", Byte.class, F_INTEGRAL);
-    final public static Type SHORT = new Type("Short", Short.class, F_INTEGRAL);
-    final public static Type CHARACTER = new Type("Character", Character.class, F_INTEGRAL);
-    final public static Type INTEGER = new Type("Integer", Integer.class, F_INTEGRAL);
-    final public static Type LONG = new Type("Long", Long.class, F_INTEGRAL);
-    final public static Type FLOAT = new Type("Float", Float.class, F_FLOATING);
-    final public static Type DOUBLE = new Type("Double", Double.class, F_FLOATING);
-    final public static Type STRING = new Type("String", String.class, F_OBJECT);
-    final public static Type VOID = new Type("void", void.class, F_ANY);
-    final public static Type NUMBER = new Type("Number", Number.class, F_NUMERIC);
-    final public static Type UNDEFINED = new Type("", Undefined.class, F_ANY);
 
+    final public static Type Z = new Type("boolean", null, F_BOOLEAN|F_PRIMITIVE);
+    final public static Type B = new Type("byte", null, F_INTEGRAL|F_PRIMITIVE);
+    final public static Type S = new Type("short", null, F_INTEGRAL|F_PRIMITIVE);
+    final public static Type C = new Type("char", null, F_INTEGRAL|F_PRIMITIVE);
+    final public static Type I = new Type("int", null, F_INTEGRAL|F_PRIMITIVE);
+    final public static Type J = new Type("long", null, F_INTEGRAL|F_PRIMITIVE);
+    final public static Type F = new Type("float", null, F_FLOATING|F_PRIMITIVE);
+    final public static Type D = new Type("double", null, F_FLOATING|F_PRIMITIVE);
+    // pseudo type representing an undefined numeric primitive type
+    final public static Type N = new Type("", null, F_UNKNOWN|F_NUMERIC|F_PRIMITIVE);
+
+    final public static Type BOOLEAN = new Type("java.lang.Boolean", Boolean.class, F_BOOLEAN);
+    final public static Type BYTE = new Type("java.lang.Byte", Byte.class, F_INTEGRAL);
+    final public static Type SHORT = new Type("java.lang.Short", Short.class, F_INTEGRAL);
+    final public static Type CHARACTER = new Type("java.lang.Character", Character.class, F_INTEGRAL);
+    final public static Type INTEGER = new Type("java.lang.Integer", Integer.class, F_INTEGRAL);
+    final public static Type LONG = new Type("java.lang.Long", Long.class, F_INTEGRAL);
+    final public static Type FLOAT = new Type("java.lang.Float", Float.class, F_FLOATING);
+    final public static Type DOUBLE = new Type("java.lang.Double", Double.class, F_FLOATING);
+    final public static Type STRING = new Type("java.lang.String", String.class, F_OBJECT|F_STRING);
+    final public static Type VOID = new Type("void", void.class, F_VOID);
+    final public static Type NUMBER = new Type("java.lang.Number", Number.class, F_NUMERIC);
+    // pseudo type representing an undefined primitive or object type
+    final public static Type UNDEFINED = new Type("", Undefined.class, F_UNKNOWN);
+
     final private static HashMap<String, Type> builtinTypes;
+    final private static HashMap<String, Type> primitiveTypes;
+    final private static HashMap<Type, Type> boxedTypes;
+    final private static HashMap<String, String> internalNames;
 
     static {
         builtinTypes = new HashMap<String, Type>();
+        // primitive type names
+        builtinTypes.put(Z.getName(), Z);
+        builtinTypes.put(B.getName(), B);
+        builtinTypes.put(S.getName(), S);
+        builtinTypes.put(C.getName(), C);
+        builtinTypes.put(I.getName(), I);
+        builtinTypes.put(J.getName(), J);
+        builtinTypes.put(F.getName(), F);
+        builtinTypes.put(D.getName(), D);
+        builtinTypes.put("$number$", N);
         // canonical names
         builtinTypes.put(BOOLEAN.getTargetClass().getName(), BOOLEAN);
         builtinTypes.put(BYTE.getTargetClass().getName(), BYTE);
@@ -385,18 +755,70 @@
         builtinTypes.put(NUMBER.getTargetClass().getName(), NUMBER);
         builtinTypes.put(UNDEFINED.getTargetClass().getName(), UNDEFINED);
         // nicknames
-        builtinTypes.put(BOOLEAN.getName(), BOOLEAN);
-        builtinTypes.put(BYTE.getName(), BYTE);
-        builtinTypes.put(SHORT.getName(), SHORT);
-        builtinTypes.put(CHARACTER.getName(), CHARACTER);
-        builtinTypes.put(INTEGER.getName(), INTEGER);
-        builtinTypes.put(LONG.getName(), LONG);
-        builtinTypes.put(FLOAT.getName(), FLOAT);
-        builtinTypes.put(STRING.getName(), STRING);
-        builtinTypes.put(VOID.getName(), VOID);
-        builtinTypes.put(NUMBER.getName(), NUMBER);
-        builtinTypes.put(UNDEFINED.getName(), UNDEFINED);
+        builtinTypes.put("Boolean", BOOLEAN);
+        builtinTypes.put("Byte", BYTE);
+        builtinTypes.put("Short", SHORT);
+        builtinTypes.put("Character", CHARACTER);
+        builtinTypes.put("Integer", INTEGER);
+        builtinTypes.put("Long", LONG);
+        builtinTypes.put("Float", FLOAT);
+        builtinTypes.put("String", STRING);
+        builtinTypes.put("Number", NUMBER);
+        builtinTypes.put("", UNDEFINED);
         // allow undefined to be spelled out
         builtinTypes.put("Undefined", UNDEFINED);
+
+        primitiveTypes = new HashMap<String, Type>();
+        primitiveTypes.put(Z.getName(), Z);
+        primitiveTypes.put(B.getName(), B);
+        primitiveTypes.put(S.getName(), S);
+        primitiveTypes.put(C.getName(), C);
+        primitiveTypes.put(I.getName(), I);
+        primitiveTypes.put(J.getName(), J);
+        primitiveTypes.put(F.getName(), F);
+        primitiveTypes.put(D.getName(), D);
+        primitiveTypes.put("$number$", N);
+
+        // allow for boxing
+        boxedTypes = new HashMap<Type, Type>();
+        boxedTypes.put(Z, BOOLEAN);
+        boxedTypes.put(B, BYTE);
+        boxedTypes.put(S, SHORT);
+        boxedTypes.put(C, CHARACTER);
+        boxedTypes.put(I, INTEGER);
+        boxedTypes.put(J, LONG);
+        boxedTypes.put(F, FLOAT);
+        boxedTypes.put(D, DOUBLE);
+        // also allow for unboxing
+        boxedTypes.put(BOOLEAN, Z);
+        boxedTypes.put(BYTE, B);
+        boxedTypes.put(SHORT, S);
+        boxedTypes.put(CHARACTER, C);
+        boxedTypes.put(INTEGER, I);
+        boxedTypes.put(LONG, J);
+        boxedTypes.put(FLOAT, F);
+        boxedTypes.put(DOUBLE, D);
+
+        internalNames = new HashMap<String, String>();
+        // add translations from primitive names to internal tag
+        internalNames.put("Z", "boolean");
+        internalNames.put("B", "byte");
+        internalNames.put("S", "short");
+        internalNames.put("C", "char");
+        internalNames.put("I", "int");
+        internalNames.put("J", "long");
+        internalNames.put("F", "float");
+        internalNames.put("D", "double");
+        internalNames.put("V", "void");
+        // also add reverse translations
+        internalNames.put("boolean", "Z");
+        internalNames.put("byte", "B");
+        internalNames.put("short", "S");
+        internalNames.put("char", "C");
+        internalNames.put("int", "I");
+        internalNames.put("long", "J");
+        internalNames.put("float", "F");
+        internalNames.put("double", "D");
+        internalNames.put("void", "V");
     }
 }

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/type/TypeGroup.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/type/TypeGroup.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/type/TypeGroup.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -7,38 +7,52 @@
  */
 public class TypeGroup {
     protected HashMap<String, Type> typeTable;
+    private ClassLoader loader;
+    private boolean resolve;
 
     /**
      * create a type group for a rule containing all the basic types
      */
 
-    public TypeGroup()
+    public TypeGroup(ClassLoader loader)
     {
         // ensure default types are all in table
 
         typeTable = new HashMap<String, Type>();
 
-        typeTable.put("boolean", Type.BOOLEAN);
+        typeTable.put("boolean", Type.Z);
+        typeTable.put("java.lang.Boolean", Type.BOOLEAN);
         typeTable.put("Boolean", Type.BOOLEAN);
 
-        typeTable.put("byte", Type.BYTE);
+        typeTable.put("byte", Type.B);
+        typeTable.put("java.lang.Byte", Type.BYTE);
         typeTable.put("Byte", Type.BYTE);
-        typeTable.put("short", Type.SHORT);
+        typeTable.put("short", Type.S);
+        typeTable.put("java.lang.Short", Type.SHORT);
         typeTable.put("Short", Type.SHORT);
-        typeTable.put("char", Type.CHARACTER);
+        typeTable.put("char", Type.C);
+        typeTable.put("java.lang.Char", Type.CHARACTER);
         typeTable.put("Char", Type.CHARACTER);
-        typeTable.put("int", Type.INTEGER);
+        typeTable.put("int", Type.I);
+        typeTable.put("java.lang.Integer", Type.INTEGER);
         typeTable.put("Integer", Type.INTEGER);
-        typeTable.put("long", Type.LONG);
+        typeTable.put("long", Type.J);
+        typeTable.put("java.lang.Long", Type.LONG);
         typeTable.put("Long", Type.LONG);
 
-        typeTable.put("float", Type.FLOAT);
+        typeTable.put("float", Type.F);
+        typeTable.put("java.lang.Float", Type.FLOAT);
         typeTable.put("Float", Type.FLOAT);
-        typeTable.put("double", Type.DOUBLE);
-        typeTable.put("DOUBLE", Type.DOUBLE);
+        typeTable.put("double", Type.D);
+        typeTable.put("java.lang.Double", Type.DOUBLE);
+        typeTable.put("Double", Type.DOUBLE);
 
+        typeTable.put("java.lang.String", Type.STRING);
         typeTable.put("String", Type.STRING);
         typeTable.put("void", Type.VOID);
+
+        this.loader = loader;
+        this.resolve = false;
     }
 
     /**
@@ -76,41 +90,153 @@
     public Type create(String name, Class clazz)
     {
         Type existing = typeTable.get(name);
-        if (existing == null) {
-            Type newType = new Type(name, clazz);
-            if (checkAlias(newType)) {
-                return Type.dereference(newType);
+
+        if (existing != null) {
+            // use the existing type assuming we can match or upgrade the class
+            if (clazz != null) {
+                if (clazz == Type.dereference(existing).getTargetClass()) {
+                    return existing;
+                } else {
+                    return null;
+                }
             } else {
-                return null;
+                return existing;
             }
         } else {
-            if (existing.isAssignableFrom(clazz)) {
-                return Type.dereference(existing);
+            if (clazz == null && resolve) {
+                // try to find a class for this type using the class loader
+                try {
+                    clazz = loader.loadClass(name);
+                } catch (ClassNotFoundException cfe) {
+                    // ignore this for now as we may resolve it later
+                }
+            }
+
+            Type newType = new Type(name, clazz);
+            if (checkAlias(newType)) {
+                typeTable.put(name, newType);
+                return newType;
             } else {
                 return null;
             }
         }
     }
 
+    /**
+     * try to associate each type in the typegroup with a class 
+     */
+
+    public void resolveTypes() {
+        for (Type type : typeTable.values()) {
+            if (type.isUndefined()) {
+                type.resolve(loader);
+            }
+        }
+        resolve = true;
+    }
+
+    /**
+     * if the supplied type has a package qualified name ensure that any existing
+     * entry with the unqualified name is aliased to it or else add an entry with an
+     * unqualified name as an alias for it. do nothing if the type name is unqualified
+     * @param type the type to be checked for an alias
+     * @return true if the alias type is now or was already in the table or false if no such
+     * type can be installed because there is an existing alias to some other type
+     */
     private boolean checkAlias(Type type)
     {
         String name = type.getName();
         int dotIdx = name.lastIndexOf('.');
 
         if (dotIdx >= 0) {
-            // we are inserrting a qualified type -- ensure it does not clash with any
+            // we are inserting a qualified type -- ensure it does not clash with any
             // unqualified name
 
-            name = name.substring(dotIdx);
+            name = name.substring(dotIdx + 1);
 
             Type alias = typeTable.get(name);
 
-            if (alias != null && !alias.aliasTo(type)) {
-                return false;
+            if (alias != null) {
+                if (!alias.aliasTo(type)) {
+                    return false;
+                } else {
+                    return true;
+                }
+            } else {
+                // bag the unqualified name as an alias for this type
+
+                alias = new Type(name);
+                alias.aliasTo(type);
+
+                typeTable.put(name, alias);
             }
         }
 
         return true;
     }
+
+    public Type createArray(Type baseType)
+    {
+        String arrayTypeName = baseType.getName() + "[]";
+        Type arrayType = typeTable.get(arrayTypeName);
+        if (arrayType == null) {
+            Class arrayClazz = null;
+            if (baseType.isPrimitive() || baseType.isDefined()) {
+                try {
+                    arrayClazz = loader.loadClass("[" + baseType.getInternalName());
+                } catch (ClassNotFoundException e) {
+                    // ignore
+                }
+            }
+            arrayType = baseType.arrayType(arrayClazz);
+        }
+
+        return arrayType;
+    }
+
+    public Type ensureType(Class clazz)
+    {
+        if (clazz.isArray()) {
+            Class baseClazz = clazz.getComponentType();
+            Type baseType = ensureType(baseClazz);
+            return createArray(baseType);
+        } else if (clazz.isPrimitive()) {
+            return typeTable.get(clazz.getName());
+        } else {
+            String name = clazz.getCanonicalName();
+            Type type = typeTable.get(name);
+            if (type == null) {
+                type = create(name, clazz);
+            }
+            return type;
+        }
+    }
+
+    public Type match(String[] path)
+    {
+        // check to see if the first element of path is a known type or an alias for a known type
+        Type type = typeTable.get(path[0]);
+        if (type != null) {
+            return Type.dereference(type);
+        }
+        // ok, see if we can find a type using some initial segment of the path
+        
+        String fullName = "";
+        String sepr = "";
+        int length = path.length;
+
+        for (int i = 0; i < length; i++) {
+            fullName += sepr + path[i];
+            sepr = "";
+            try {
+                Class clazz = loader.loadClass(fullName);
+                return ensureType(clazz);
+            } catch (ClassNotFoundException e) {
+                // ignore
+            }
+        }
+        
+        return null;
+    }
 }
 

Added: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/type/TypeHelper.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/type/TypeHelper.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/rule/type/TypeHelper.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -0,0 +1,315 @@
+package org.jboss.jbossts.orchestration.rule.type;
+
+import org.jboss.jbossts.orchestration.annotation.EventHandler;
+
+/**
+ * Helpoer class providing static methods for manipulating type and class names,
+ * field and method descriptor names etc
+ */
+public class TypeHelper {
+
+    public static boolean equalDescriptors(String desc1, String desc2)
+    {
+        int idx1 = 0, idx2 = 0;
+        int len1 = desc1.length(), len2 = desc2.length();
+        while (idx1 < len1) {
+            // check the other has not dropped off the end
+            if (idx2 == len2) {
+                if ((idx1 == (len1 - 1)) && (desc1.charAt(idx1) == '$')) {
+                    return true;
+                }
+                return false;
+            }
+            // check type is the same
+            char char1 = desc1.charAt(idx1);
+            char char2 = desc2.charAt(idx2);
+            // if we have a $ at the end of the descriptor then this means any return
+            // type so special case this
+            if ((char1 == '$' && idx1 == len1 - 1) || (char2 == '$' && idx2 == len2 - 1)) {
+                return true;
+            }
+            // otherwise the chars must match
+            if (char1 != char2) {
+                return false;
+            }
+            // however an L indicates a class name and we allow a classname without a package
+            // to match a class name with a package
+            if (char1 == 'L') {
+                // ok, ensure the names must match modulo a missing package
+                int end1 = idx1 + 1;
+                int end2 = idx2 + 1;
+                while (end1 < len1 && desc1.charAt(end1) != ';') {
+                    end1++;
+                }
+                while (end2 < len2 && desc2.charAt(end2) != ';') {
+                    end2++;
+                }
+                if (end1 == len1 || end2 == len2) {
+                    // bad format for desc!!
+                    return false;
+                }
+                String typeName1 = desc1.substring(idx1 + 1, end1);
+                String typeName2 = desc2.substring(idx2 + 1, end2);
+                if (!typeName1.equals(typeName2)) {
+                    int tailIdx1 = typeName1.lastIndexOf('/');
+                    int tailIdx2 = typeName2.lastIndexOf('/');
+                    if (tailIdx1 > 0) {
+                        if (tailIdx2 > 0) {
+                            // both specify packages so they must be different types
+                            return false;
+                        } else {
+                            // only type 1 specifies a package so type 2 should match the tail
+                            if (!typeName2.equals(typeName1.substring(tailIdx1 + 1))) {
+                                return false;
+                            }
+                        }
+                    } else {
+                        if (tailIdx2 > 0) {
+                            // only type 2 specifies a package so type 1 should match the tail
+                            if (!typeName1.equals(typeName2.substring(tailIdx2 + 1))) {
+                                return false;
+                            }
+                        } else {
+                            // neither specify packages so they must be different types
+                            return false;
+                        }
+                    }
+                }
+                // skp past ';'s
+                idx1 = end1;
+                idx2 = end2;
+            }
+            idx1++;
+            idx2++;
+        }
+
+        // check the other has not reached the end
+        if (idx2 != len2) {
+            return false;
+        }
+
+        return true;
+    }
+    /**
+     * convert a classname from canonical form to the form used to represent it externally i.e. replace
+     * all dots with slashes
+     *
+     * @param className
+     * @return
+     */
+    public static String externalizeClass(String className)
+    {
+        return className.replaceAll("\\.", "/");
+    }
+
+    /**
+     * convert a classname from external form to canonical form i.e. replace
+     * all slashes with dots
+     *
+     * @param className
+     * @return
+     */
+    public static String internalizeClass(String className)
+    {
+        String result = className;
+        int length = result.length();
+        if (result.charAt(length - 1) == ';') {
+            result = result.substring(1, length - 2);
+        }
+        result = result.replaceAll("/", "\\.");
+        return result;
+    }
+
+    /**
+     * convert a type name from canonical form to the form used to represent it externally i.e.
+     * replace primitive type names by the appropriate single letter types, class names
+     * by the externalized class name bracketed by 'L' and ';' and array names by the
+     * base type name preceded by '['.
+     *
+     * @param typeName
+     * @return
+     */
+    public static String externalizeType(String typeName)
+    {
+        String externalName = "";
+        String[] typeAndArrayIndices = typeName.split("\\[");
+        String baseType = typeAndArrayIndices[0].trim();
+        for (int i = 1; i< typeAndArrayIndices.length; i++) {
+            String arrayIdx = typeAndArrayIndices[i];
+            if (arrayIdx.indexOf("\\]") != 0) {
+                externalName += '[';
+            }
+        }
+        for (int i = 0; i < internalNames.length; i++) {
+            if (internalNames[i].equals(baseType)) {
+                externalName += externalNames[i];
+                return externalName;
+            }
+        }
+
+        externalName += "L" + externalizeClass(baseType) + ";";
+
+        return externalName;
+    }
+
+    /**
+     * list of well known typenames as written in Java code
+     */
+    final static private String[] internalNames = {
+            "", /* equivalent to void */
+            "void",
+            "byte",
+            "char",
+            "short",
+            "int",
+            "long",
+            "float",
+            "double",
+            "boolean",
+            "Byte",
+            "Character",
+            "Short",
+            "Integer",
+            "Long",
+            "Float",
+            "Double",
+            "String",
+            "java.lang.Byte",
+            "java.lang.Character",
+            "java.lang.Short",
+            "java.lang.Integer",
+            "java.lang.Long",
+            "java.lang.Float",
+            "java.lang.Double",
+            "java.lang.String"
+    };
+
+    /**
+     * list of typenames in external form corresponding to entries ni previous list
+     */
+    final static private String[] externalNames = {
+            "$",
+            "V",
+            "B",
+            "C",
+            "S",
+            "I",
+            "J",
+            "F",
+            "D",
+            "Z",
+            "Ljava/lang/Byte;",
+            "Ljava/lang/Character;",
+            "Ljava/lang/Short;",
+            "Ljava/lang/Integer;",
+            "Ljava/lang/Long;",
+            "Ljava/lang/Float;",
+            "Ljava/lang/Double;",
+            "Ljava/lang/String;",
+            "Ljava/lang/Byte;",
+            "Ljava/lang/Character;",
+            "Ljava/lang/Short;",
+            "Ljava/lang/Integer;",
+            "Ljava/lang/Long;",
+            "Ljava/lang/Float;",
+            "Ljava/lang/Double;",
+            "Ljava/lang/String;"
+    };
+
+    /**
+     * convert a method descriptor from canonical form to the form used to represent it externally
+     *
+     * @param desc the method descriptor which must be trimmed of any surrounding white space
+     * @return an externalised form for the descriptor
+     */
+    public static String externalizeDescriptor(String desc)
+    {
+        // the descriptor will start with '(' and the arguments list should end with ')' and,
+        // if it is not void be followed by a return type
+        int openIdx = desc.indexOf('(');
+        int closeIdx = desc.indexOf(')');
+        int length = desc.length();
+        if (openIdx != 0) {
+            return "";
+        }
+        if (closeIdx < 0) {
+            return "";
+        }
+        String retType = (closeIdx < length ? desc.substring(closeIdx + 1).trim() : "");
+        String[] args = desc.substring(1, closeIdx).trim().split(",");
+        String externalRetType = externalizeType(retType);
+        String externalArgs = "";
+        for (int i = 0; i < args.length ; i++) {
+            externalArgs += externalizeType(args[i]);
+        }
+
+        return "(" + externalArgs + ")" + externalRetType;
+    }
+
+    /**
+     * split off the method name preceding the signature and return it
+     * @param targetMethod - the unqualified method name, possibly including signature
+     * @return
+     */
+    public static String parseMethodName(String targetMethod) {
+        int sigIdx = targetMethod.indexOf("(");
+        if (sigIdx > 0) {
+            return targetMethod.substring(0, sigIdx).trim();
+        } else {
+            return targetMethod;
+        }
+    }
+
+    /**
+     * split off the signature following the method name and return it
+     * @param targetMethod - the unqualified method name, possibly including signature
+     * @return
+     */
+    public static String parseMethodDescriptor(String targetMethod) {
+        int descIdx = targetMethod.indexOf("(");
+        if (descIdx >= 0) {
+            String desc = targetMethod.substring(descIdx, targetMethod.length()).trim();
+            return externalizeDescriptor(desc);
+        } else {
+            return "";
+        }
+    }
+
+    /**
+     * split off the signature following the method name and return it
+     * @param targetName the unqualified method name, not including signature
+     * @param targetSignature the method signature including brackets types and return type
+     * @return
+     */
+    public static String generateFieldName(String targetName, String targetSignature) {
+        String result = targetName;
+        int startIdx = targetSignature.indexOf("(");
+        int endIdx = targetSignature.indexOf(")");
+        if (startIdx < 0) {
+            startIdx = 0;
+        }
+        if (endIdx < 0) {
+            endIdx = targetSignature.length() - 1;
+        }
+
+        String args = targetSignature.substring(startIdx, endIdx + 1);
+
+        result = result.replaceAll("<", "\\$_");
+        result = result.replaceAll(">", "_\\$");
+
+        // remove any brackets, semi-colons and '[' characters
+        args = args.replaceAll("\\(", "\\$_");
+        args = args.replaceAll("\\)", "_\\$");
+        args = args.replaceAll(";", "__");
+        args = args.replaceAll("\\[", "\\$\\$_");
+        args = args.replaceAll("/", "_\\$_");
+
+        return result + args;
+    }
+
+    private static Class generateHandlerClass(EventHandler handler, ClassLoader loader, String targetClassName, Class targetClass)
+    {
+        // TODO -- write this but use Object for now
+        return Object.class;
+    }
+}

Added: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/synchronization/CountDown.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/synchronization/CountDown.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/synchronization/CountDown.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -0,0 +1,24 @@
+package org.jboss.jbossts.orchestration.synchronization;
+
+/**
+ * class provided to support rule builtins getCounter, decrementCounter and addCounter
+ */
+public class CountDown
+{
+    public CountDown(int count)
+    {
+        this.count = (count < 1 ? 1 : count);
+    }
+
+    public synchronized boolean decrement()
+    {
+        if (count > 0) {
+            count--;
+            return false;
+        }
+
+        return true;
+    }
+
+    private int count;
+}

Added: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/synchronization/Waiter.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/synchronization/Waiter.java	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/orchestration/synchronization/Waiter.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -0,0 +1,89 @@
+package org.jboss.jbossts.orchestration.synchronization;
+
+import org.jboss.jbossts.orchestration.rule.exception.ExecuteException;
+
+/**
+ * class used to manage rule wait operations
+ */
+public class Waiter
+{
+    public Waiter(Object object)
+    {
+        this.waiterFor = object;
+        this.signalled = false;
+        this.killed = false;
+    }
+
+    public void waitFor(long millisecs)
+    {
+        synchronized(this) {
+            if (!signalled) {
+                try {
+                    this.wait(millisecs);
+                } catch (InterruptedException e) {
+                    // ignore
+                }
+            }
+        }
+        
+        // if a signalKill was used then we have to throw an exception otherwise we just return
+        
+        if (killed) {
+            throw new ExecuteException("Waiter.waitFor waiting thread killed for " + waiterFor);
+        }
+    }
+
+    public boolean signal()
+    {
+        boolean result;
+
+        synchronized (this) {
+            result = signalled;
+            if (!signalled) {
+                signalled = true;
+            }
+        }
+        if (!result) {
+            this.notifyAll();
+        }
+
+        return result;
+    }
+
+    public boolean signalKill()
+    {
+        boolean result;
+
+        synchronized (this) {
+            result = signalled;
+            if (!signalled) {
+                signalled = true;
+                killed = true;
+            }
+        }
+
+        if (!result) {
+            this.notifyAll();
+        }
+
+        return result;
+    }
+
+    /**
+     * the object with which this waiter is associated
+     */
+
+    private Object waiterFor;
+
+    /**
+     * true if this waiter has been signalled by a call to signalKill
+     */
+
+    private boolean signalled;
+
+    /**
+     * true if this waiter has been signalled by a call to signal or signalKill
+     */
+
+    private boolean killed;
+}

Modified: labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/test/HandlerClass.java
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/test/HandlerClass.java	2008-09-16 12:04:41 UTC (rev 22808)
+++ labs/jbosstm/workspace/adinn/orchestration/src/org/jboss/jbossts/test/HandlerClass.java	2008-09-16 12:42:51 UTC (rev 22809)
@@ -11,35 +11,50 @@
     @EventHandler(
             targetClass="com.arjuna.wst11.messaging.engines.CoordinatorEngine",
             targetMethod="commit",
-            targetLine = 77,
-            event = "engine:CoordinatorEngine = $1, recovered:boolean = engine.isRecovered(), identifier:String = engine.getInstanceidentifier()",
-            condition = "recovered AND decrementCounter(identifier)"//,
-            //action = ""
+            targetLine = 316,
+            event = "engine:CoordinatorEngine = $0,\n" +
+                    "recovered:boolean = engine.isRecovered(),\n" + 
+                    "identifier:String = engine.getId()",
+            condition = "(NOT recovered)\n" +
+                    "AND\n" +
+                    "debug(\"commit on non-recovered engine \" + identifier)",
+            action = "debug(\"!!!killing JVM!!!\"), killJVM()"
     ) public static void handleCommit1()
     {
-        // decrements the counter identified by a recovered engine's identifier each time recovery is attempted
-        // for that engine -- if the counter decrements to zero it will be deactivated
+        // kills the JVM when a commit is attempted on a non-recovered engine
     }
 
     @EventHandler(
             targetClass="com.arjuna.wst11.messaging.engines.CoordinatorEngine",
             targetMethod="commit",
-            targetLine = 77,
-            event = "engine:CoordinatorEngine = $1, recovered:boolean = engine.isRecovered()",
-            condition = "NOT recovered",
-            action = "killJVM()"
+            targetLine = 316,
+            event = "engine:CoordinatorEngine = $0,\n " +
+                    "recovered:boolean = engine.isRecovered(),\n" +
+                    "identifier:String = engine.getId()",
+            condition = "recovered\n" +
+                    "AND\n" +
+                    "debug(\"commit on recovered engine \" + identifier)\n" +
+                    "AND\n" +
+                    "debug(\"counting down\")\n" +
+                    "AND\n" +
+                    "countDown(identifier)",
+            action = "debug(\"countdown completed for \" + identifier)"
     ) public static void handleCommit2()
     {
-        // kills the JVM when a commit is attempted on a non-recovered engine
+        // decrements the counter identified by a recovered engine's identifier each time recovery is attempted
+        // for that engine -- if the counter decrements to zero it will be deactivated
     }
 
     @EventHandler(
             targetClass="com.arjuna.wst11.messaging.engines.CoordinatorEngine",
-            targetMethod="<init>(String, boolean, W3CEndpointReference)",
-            targetLine = -1,
-            event = "engine:CoordinatorEngine = $1, recovered:boolean = engine.isRecovered(), identifier:String = engine.getInstanceIdentifier()",
+            targetMethod="<init>(String, boolean, W3CEndpointReference, boolean, State)",
+            targetLine = 96,
+            event = "engine:CoordinatorEngine = $0,\n" +
+                    "recovered:boolean = engine.isRecovered(),\n" +
+                    "identifier:String = engine.getId()",
             condition = "recovered",
-            action = "addCounter(identifier, 2)"
+            action = "debug(\"adding countdown for \" + identifier),\n" +
+                    "addCountDown(identifier, 2)"
     ) public static void handleNewEngine()
     {
         // activates a counter identified by a recovered engine's identifier when the engine is recreated
@@ -47,12 +62,19 @@
     }
 
     @EventHandler(
-            targetClass="com.arjuna.wst11.messaging.engines.CoordinatorProcessorImpl",
+            targetClass="com.arjuna.wst11.messaging.engines.CoordinatorEngine",
             targetMethod="committed(Notification, AddressingProperties, ArjunaContext)",
             targetLine = -1,
-            event = "processor:CoordinatorProcessorImpl = $1, identifier:String = $3.getInstanceIdentifier(), engine:CoordinatorEngine = processor.getCoordinator(identifier), recovered:boolean = engine.isRecovered()",
-            condition = "recovered AND getCounter(identifier)",
-            action = "killThread()"
+            event = "engine:CoordinatorEngine = $0,\n" +
+                    "recovered:boolean = engine.isRecovered()," +
+                    "identifier:String = engine.getId()\n",
+            condition = "recovered\n" +
+                    "AND\n" +
+                    "debug(\"committed on recovered engine \" + identifier)\n" +
+                    "AND\n" +
+                    "getCountDown(identifier)",
+            action = "debug(\"!!!killing committed thread for \" + identifier + \"!!!\"),\n" +
+                    "killThread()"
     ) public static void handleCommitted()
     {
         // kills the current thread when a committed message is received for an engine whose identifier identifies

Added: labs/jbosstm/workspace/adinn/orchestration/testrule.txt
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/testrule.txt	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/testrule.txt	2008-09-16 12:42:51 UTC (rev 22809)
@@ -0,0 +1,8 @@
+BIND x : CoordinatorEngine = $0,
+     y : String = x.getId(),
+     z : boolean = x.isDurable()
+IF   z
+     AND
+     NOT getCountDown(y)
+DO   debug("Adding countdown for " + y),
+     addCountDown(x, 3)

Added: labs/jbosstm/workspace/adinn/orchestration/testrule2.txt
===================================================================
--- labs/jbosstm/workspace/adinn/orchestration/testrule2.txt	                        (rev 0)
+++ labs/jbosstm/workspace/adinn/orchestration/testrule2.txt	2008-09-16 12:42:51 UTC (rev 22809)
@@ -0,0 +1,13 @@
+BIND processor:CoordinatorProcessorImpl = $0,
+     context:ArjunaContext = $3,
+     identifier:String = context.getId(),
+     engine:CoordinatorEngine = processor.getCoordinator(identifier),
+     recovered:boolean = engine.isRecovered()
+IF   recovered
+     AND
+     debug("commit on recovered engine " + identifier)
+     AND
+     debug("counting down")
+     AND
+     countDown(identifier)
+DO   killThread()




More information about the jboss-svn-commits mailing list