001 /**
002 * @author paulk
003 */
004
005 package toolbus.atom;
006
007 import java.util.ArrayList;
008 import java.util.Iterator;
009 import java.util.List;
010 import java.util.Stack;
011 import toolbus.AtomList;
012 import toolbus.Functions;
013 import toolbus.State;
014 import toolbus.StateElement;
015 import toolbus.TBTermFactory;
016 import toolbus.ToolBus;
017 import toolbus.environment.Environment;
018 import toolbus.exceptions.ToolBusException;
019 import toolbus.parsercup.PositionInformation;
020 import toolbus.process.ProcessExpression;
021 import toolbus.process.ProcessInstance;
022 import aterm.AFun;
023 import aterm.ATerm;
024 import aterm.ATermAppl;
025 import aterm.ATermInt;
026 import aterm.ATermList;
027
028 /**
029 * The class Atom forms the basic building block of Tscripts. Instances of Atom
030 * are both the most primitive elements of process expressions and the elements of
031 * the states to which process exprssions are being compiled.
032 *
033 */
034
035 abstract public class Atom extends ProcessExpression implements StateElement{
036 private final static String SECS = "sec";
037 private final static String MSECS = "msec";
038
039 private final static Ref[] NOATOMARGS = new Ref[0];
040
041 private ProcessInstance processInstance; // process instance to which the atom belongs
042 private Environment env; // the environment of this atom
043 private List<Test> tests; // optional tests that guard this atom
044 private Ref[] atomArgs = NOATOMARGS; // arguments of the atom
045 private int delay = 0; // time delay before atom can be executed
046 private int timeout = 0; // timeout after which atom can no longer be executed
047 private boolean timeExpr = false; // the actual time expression for delay/timeout
048 private long activateTime; // when this atom was activated
049 private long enabledTime; // When this atom becomes ready for execution
050 private long timeoutTime; // When this atom becomes no longer eligable for execution
051 protected String externalNameAsReceivedByTool;
052
053 public Atom(TBTermFactory tbfactory, PositionInformation posInfo){
054 super(tbfactory, posInfo);
055 addToFirst(this);
056 externalNameAsReceivedByTool = shortName();
057 }
058
059 public void destroy(){/* Intended to (optionally) be overwritten in subclasses to enable them to execute cleanup actions if needed. */}
060
061 public void setAtomArgs(Ref[] refs){
062 atomArgs = refs;
063 }
064
065 public ATerm getAtomArgValue(int i){
066 return atomArgs[i].value;
067 }
068
069 public void copyAtomAttributes(Atom a){
070 delay = a.delay;
071 timeout = a.timeout;
072 timeExpr = a.timeExpr;
073 }
074
075 protected void setEnv(Environment env){
076 this.env = env;
077 }
078
079 public Environment getEnv(){
080 return env;
081 }
082
083 public void setTest(ATerm test, Environment e) throws ToolBusException{
084 if(test != null){
085 //System.err.println(this + "." + "setTest: env " + env.hashCode() + " => " + e.hashCode());
086 if(tests == null) tests = new ArrayList<Test>(4);
087 Test t = new Test(test, e);
088 //System.out.println("setTest: " + t);
089 this.tests.add(t);
090 }
091 }
092
093 public List<ATerm> getTests(){
094 if(tests != null){
095 List<ATerm> testExpressions = new ArrayList<ATerm>(tests.size());
096 Iterator<Test> testsIterator = tests.iterator();
097 while(testsIterator.hasNext()){
098 testExpressions.add(testsIterator.next().testExpr);
099 }
100 return testExpressions;
101 }
102 return new ArrayList<ATerm>(0);
103 }
104
105 public void setDelay(ATerm delay){
106 if(delay instanceof ATermAppl){
107 ATermAppl timeExpression = (ATermAppl) delay;
108 AFun timeFun = timeExpression.getAFun();
109 if(timeFun.getArity() == 1){
110 String order = timeFun.getName();
111 ATerm argument = timeExpression.getArgument(0);
112 if(argument instanceof ATermInt){
113 ATermInt time = (ATermInt) argument;
114 if(order == SECS){
115 this.delay = time.getInt() * 1000;
116 timeExpr = true;
117 return;
118 }else if(order == MSECS){
119 this.delay = time.getInt();
120 timeExpr = true;
121 return;
122 }
123 }
124 }
125 }
126 // If the function didn't return before this point the argument wasn't formatted properly.
127 throw new RuntimeException("Not a time expression: "+delay);
128 }
129
130 public void setAbsoluteDelay(ATermList delay){
131 // TODO Implement as soon as we know how to do it properly.
132 throw new UnsupportedOperationException("Absolute delays are currently unsupported.");
133 }
134
135 public int getDelay(){
136 return delay;
137 }
138
139 public void setTimeout(ATerm timeout){
140 if(timeout instanceof ATermAppl){
141 ATermAppl timeExpression = (ATermAppl) timeout;
142 AFun timeFun = timeExpression.getAFun();
143 if(timeFun.getArity() == 1){
144 String order = timeFun.getName();
145 ATerm argument = timeExpression.getArgument(0);
146 if(argument instanceof ATermInt){
147 ATermInt time = (ATermInt) argument;
148 if(order == SECS){
149 this.timeout = time.getInt() * 1000;
150 timeExpr = true;
151 return;
152 }else if(order == MSECS){
153 this.timeout = time.getInt();
154 timeExpr = true;
155 return;
156 }
157 }
158 }
159 }
160 // If the function didn't return before this point the argument wasn't formatted properly.
161 throw new RuntimeException("Not a time expression: "+delay);
162 }
163
164 public void setAbsoluteTimeout(ATermList timeout){
165 // TODO Implement as soon as we know how to do it properly.
166 throw new UnsupportedOperationException("Absolute timeouts are currently unsupported.");
167 }
168
169 public int getTimeout(){
170 return timeout;
171 }
172
173 public ToolBus getToolBus(){
174 return processInstance.getToolBus();
175 }
176
177 public AtomList getAtoms(){
178 return new AtomList(this);
179 }
180
181 private String shortName(){
182 return getClass().getSimpleName();
183 }
184
185 public String toString(){
186 /*String pidStr = (processInstance != null) ? "[" + processInstance.getProcessName() + "/" + processInstance.getProcessId() + "]" : "";
187 String args = "(";
188 String sep = "";
189
190 for(int i = 0; i < atomArgs.length; i++){
191 args = args + sep + atomArgs[i];
192 //args = args + sep + tbfactory.substitute(atomArgs[i].value, env);
193 sep = ", ";
194 }
195 args = args + ")";
196 String strtest = (tests == null) ? "" : " if " + tests;*/
197 if(atomArgs.length > 0){
198 StringBuilder sb = new StringBuilder();
199 sb.append(shortName());
200 sb.append('(');
201 sb.append(atomArgs[0].value);
202 int nrOfAtomArgs = atomArgs.length;
203 for(int i = 1; i < nrOfAtomArgs; i++){
204 sb.append(',');
205 sb.append(atomArgs[i].value);
206 }
207 sb.append(')');
208 return sb.toString();
209 }
210 return shortName()/* + pidStr + args + strtest*/;
211 }
212
213 public ATermAppl toATerm(){
214 int nargs = atomArgs.length;
215 // System.err.println("toATerm: " + externalNameAsReceivedByTool);
216
217 AFun afun = tbfactory.makeAFun(externalNameAsReceivedByTool, nargs, false);
218 ATerm pat[] = new ATerm[nargs];
219
220 for(int i = 0; i < nargs; i++){
221 pat[i] = tbfactory.makePattern(atomArgs[i].value);
222 }
223 return tbfactory.makeAppl(afun, pat);
224 }
225
226 public void computeFirst(){/* Overwritten in subclass */}
227
228 public void replaceFormals(Environment e) throws ToolBusException{
229 this.env = e;
230 //System.err.println("Atom.replaceFormals: " + env);
231 for(int i = 0; i < atomArgs.length; i++){
232 //System.err.println("atomArg[" + i + "] = " + atomArgs[i] + " ; env = " + env);
233 ATerm arg = tbfactory.replaceFormals(atomArgs[i].value, env);
234 //System.err.println("atomArg[" + i + "] = " + atomArgs[i].value + " => " + arg + "; env = " + env);
235 atomArgs[i].value = arg;
236 }
237 }
238
239 public void compile(ProcessInstance pi, Stack<String> calls, State follow) throws ToolBusException{
240 this.processInstance = pi;
241 // System.err.println("Atom.compile, prev env = " + env);
242 // this.env = env.copy();
243 setFollow(follow);
244 // System.err.println("Compiling " + this + ";\n env = " + this.env);
245 // replaceFormals(env); //TODO redundant?
246 }
247
248 // Implementation of the StateElement interface
249
250 public boolean isEnabled() throws ToolBusException{
251 // System.err.println("Atom.isEnabled: " + this.getProcess().getProcessId() + ": " + this);
252 if(timeExpr){
253 // System.err.println("Has a TimeExpr; delay = " + delay + "; timeout = " + timeout);
254 long currentTime = getToolBus().getRunTime();
255 // System.err.println("startTime = " + startTime + "; currentTime = " + currentTime);
256 if(currentTime < enabledTime){
257 getToolBus().setNextTime(enabledTime);
258 // System.err.println("currentTime < startTime + delay");
259 /*incr(notEnabled);*/
260 return false;
261 }
262 if(timeout != 0 && currentTime > timeoutTime){
263 // System.err.println("currentTime > startTime + timeout");
264 /*incr(notEnabled);*/
265 return false;
266 }
267 }
268 if(tests != null){
269 //System.err.println("Atom.isEnabled: " + this.getProcess().getProcessId() + ": " + this);
270 Iterator<Test> testIterator = tests.iterator();
271 while(testIterator.hasNext()){
272 Test t = testIterator.next();
273 //System.err.print("evaluate: " + t);
274 boolean res = tbfactory.isTrue(Functions.eval(t.testExpr, processInstance, t.testEnv));
275 //System.err.println(" ==> " + res);
276 if(!res){
277 /*incr(notEnabled);*/
278 return false;
279 }
280 }
281 }
282 /*incr(enabled);*/
283 return true;
284 }
285
286 public boolean contains(StateElement b){
287 return this == b;
288 }
289
290 public ProcessInstance getProcess(){
291 return processInstance;
292 }
293
294 public State gotoNextStateAndActivate(){
295 // System.err.println(this + "getNextState ==> " + getFollow());
296 State s = getFollow();
297 s.activate();
298 return s;
299 }
300
301 public State gotoNextStateAndActivate(StateElement b){
302 if(this.equals(b)){
303 State s = getFollow();
304 s.activate();
305 return s;
306 }
307 System.err.println("Atom.getNextState2: wrong arg: " + b); // TODO exception
308 return null;
309 }
310
311 public void activate(){
312 activateTime = getToolBus().getRunTime();
313 enabledTime = activateTime + delay;
314 timeoutTime = activateTime + timeout;
315
316 // System.err.println("used_vars = " + collected);
317 // System.err.println("Atom.activate: " + this);
318 }
319
320 /**
321 * This class holds static reference to an empty process instance array, which can be used as
322 * return value in the debugExecute method. Using this instead of a newly created array every
323 * time will reduce garbage creation.
324 *
325 * @author Arnold Lankamp
326 */
327 private static class EmptyProcessInstanceArrayHolder{
328 public final static ProcessInstance[] ZEROPARTNERS = new ProcessInstance[0];
329 }
330
331 public ProcessInstance[] debugExecute() throws ToolBusException{
332 if(execute()) return EmptyProcessInstanceArrayHolder.ZEROPARTNERS;
333
334 return null;
335 }
336
337 /**
338 * This class represents a test associated with an atom.
339 * It carries its own execution environment.
340 */
341 private static class Test{
342 public final ATerm testExpr;
343 public final Environment testEnv;
344
345 public Test(ATerm test, Environment env){
346 testExpr = test;
347 testEnv = env;
348 }
349 public String toString(){
350 return "Test(" + testExpr + ", " + testEnv + ")";
351 }
352 }
353 }