View Javadoc

1   /*
2    * Copyright (c) 2001, Zoltan Farkas All Rights Reserved.
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 2.1 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this program; if not, write to the Free Software
16   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17   */
18  package net.sf.zel.vm;
19  
20  import java.io.BufferedReader;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.InputStreamReader;
24  import java.io.PrintStream;
25  import java.io.Serializable;
26  import java.util.Arrays;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.concurrent.Callable;
30  import java.util.concurrent.ExecutionException;
31  import java.util.concurrent.Future;
32  import java.util.concurrent.ThreadPoolExecutor;
33  import java.util.concurrent.TimeUnit;
34  import java.util.concurrent.TimeoutException;
35  import javax.annotation.CheckReturnValue;
36  import javax.annotation.Nonnull;
37  import javax.annotation.Nonnegative;
38  import javax.annotation.Nullable;
39  import javax.annotation.concurrent.Immutable;
40  import net.sf.zel.instr.Instruction;
41  import net.sf.zel.math.ExtendedMathContext;
42  
43  /**
44   * <p>Title: Program</p>
45   * @author zoly
46   * @version 1.0
47   *
48   * This is a Turing machine a Program will always be preety much an array of operations.
49   */
50  @Immutable
51  public final class Program implements Serializable
52  {
53  
54      public enum Type
55      {
56  
57          DETERMINISTIC, NONDETERMINISTIC
58      };
59      static final long serialVersionUID = 748365748433474932L;
60      private final Type type;
61      private final int id; // program ID, unique ID identifying the program
62      private final Object[] instructions;
63      private final ExtendedMathContext mathContext;
64  
65      /**
66       * initializes program with an array of objects.
67       * @param objs
68       */
69      public Program(@Nonnull final Object[] objs, @Nonnegative final int start,
70              @Nonnegative final int end, final Type progType,
71              @Nonnull final ExtendedMathContext mathContext)
72      {
73          int length = end - start;
74          instructions = new Object[length];
75          System.arraycopy(objs, start, instructions, 0, length);
76          this.type = progType;
77          id = ProgramBuilder.generateID();
78          this.mathContext = mathContext;
79      }
80  
81      @Override
82      @CheckReturnValue
83      public boolean equals(@Nullable final Object obj)
84      {
85          if (obj == null)
86          {
87              return false;
88          }
89          if (getClass() != obj.getClass())
90          {
91              return false;
92          }
93          final Program other = (Program) obj;
94          if (this.id != other.id)
95          {
96              return false;
97          }
98          return true;
99      }
100 
101     @Override
102     @CheckReturnValue
103     public int hashCode()
104     {
105         int hash = 7;
106         hash = 79 * hash + this.id;
107         return hash;
108     }
109 
110     /**
111      * @return the instructions
112      */
113     @CheckReturnValue
114     public Object get(final int i)
115     {
116         return instructions[i];
117     }
118 
119     @CheckReturnValue
120     Object[] toArray()
121     {
122         return instructions.clone();
123     }
124 
125     @CheckReturnValue
126     public int size()
127     {
128         return instructions.length;
129     }
130 
131     public static Program compile(@Nonnull final String zExpr,
132             @Nonnull final ExtendedMathContext mc,
133             @Nonnull String... varNames)
134             throws ParseException
135     {
136         ExtendedMathContext.CURRENT_EXTENDED_MATH_CONTEXT.set(mc);
137         return compile(zExpr, varNames);
138     }
139 
140     /**
141      * create a compiled Z Byte Code
142      * @param zExpr String
143      * @throws com.zoltran.z.vm.ParseException
144      * @return Program
145      */
146     public static Program compile(@Nonnull final String zExpr, @Nonnull String... varNames) throws ParseException
147     {
148 
149         CompileContext cc = new CompileContext();
150         try
151         {
152             ZCompiler.compile(zExpr, cc, varNames);
153         } // transform TokenMgrError to ParseException, Errors are bad and
154         // I like to reserve them for non-recoverable faults
155         catch (TokenMgrError err)
156         {
157             ParseException te = new ParseException(err.getMessage());
158             te.setStackTrace(err.getStackTrace());
159             throw te;
160         }
161         return cc.getProgramBuilder().toProgram();
162 
163     }
164 
165     public Object execute() throws ZExecutionException
166     {
167         return execute(newMem(), null, null, null);
168     }
169 
170     public Object execute(Object... args) throws ZExecutionException
171     {
172         return execute(newMem(), null, null, null, args);
173     }
174 
175     /**
176      * Execute the program with the provided memory
177      * @param memory Map
178      * @return Object
179      * @throws com.zoltran.z.vm.ZExecutionException
180      */
181     public Object execute(@Nonnull java.util.Map memory, Object... args) throws ZExecutionException
182     {
183         return execute(memory, null, null, null, args);
184     }
185 
186     /**
187      * Execute the program with the provided memory
188      * and input / output streams
189      * @param memory Map
190      * @param in
191      * @param out
192      * @param err
193      * @return Object
194      * @throws com.zoltran.z.vm.ExecutionException
195      * @throws java.lang.InterruptedException
196      */
197     public Object executeInterruptibly(@Nonnull final java.util.Map memory, @Nonnull final InputStream in,
198             @Nonnull final PrintStream out, @Nonnull final PrintStream err, ThreadPoolExecutor execService, Object... args)
199             throws ZExecutionException, InterruptedException
200     {
201         ExtendedMathContext.CURRENT_EXTENDED_MATH_CONTEXT.set(this.mathContext);
202         ExecutionContext ectx = new ExecutionContext(this, memory, in, out, err, execService);
203         ectx.stack.pushAll(args);
204         while (!ectx.terminated)
205         {
206             try
207             {
208                 ((Instruction) ectx.code.instructions[ectx.ip]).execute(ectx);
209             } catch (Exception e)
210             {
211                 // Wrap Exception
212                 throw new ZExecutionException("Exception detected while running program", e, ectx);
213             }
214 
215             if (Thread.interrupted())
216             {
217                 throw new InterruptedException("Program execution Interrupted");
218             }
219         }
220         return ectx.stack.pop();
221     }
222 
223     /**
224      * Execute the program with the provided memory
225      * and input / output streams
226      * when a exec service is specified this function will return a Future
227      * Also it is recomended that a thread safe memory is used in case your
228      * functions will modify the memory (not recomended)
229      * @param memory Map
230      * @param in
231      * @param out
232      * @param err
233      * @param execService
234      * @return Object
235      * @throws com.zoltran.z.vm.ExecutionException
236      */
237     public Object execute(@Nonnull final java.util.Map memory, @Nonnull final InputStream in,
238             @Nonnull final PrintStream out, @Nonnull final PrintStream err, ThreadPoolExecutor execService, Object... args)
239             throws ZExecutionException
240     {
241         ExtendedMathContext.CURRENT_EXTENDED_MATH_CONTEXT.set(this.mathContext);
242         final ExecutionContext ectx = new ExecutionContext(this, memory, in, out, err, execService);
243         ectx.stack.pushAll(args);
244         return execute(ectx);
245     }
246 
247     public static Object execute(@Nonnull final ExecutionContext ectx)
248             throws ZExecutionException
249     {
250         final Callable<Object> execution = new Callable<Object>()
251         {
252 
253             @Override
254             public Object call() throws ExecutionException
255             {
256                 while (!ectx.terminated)
257                 {
258                     try
259                     {
260                         Object code = ectx.code.instructions[ectx.ip];
261                         if (code instanceof Instruction)
262                         {
263                             ((Instruction) code).execute(ectx);
264                         } else
265                         {
266                             ectx.stack.push(ectx.code.instructions[ectx.ip++]);
267                         }
268                     } catch (Exception e)
269                     {
270                         // Wrap Exception
271                         throw new ZExecutionException("Exception detected while running program", e, ectx);
272                     }
273                 }
274                 if (!ectx.stack.isEmpty())
275                 {
276                     return ectx.stack.pop();
277                 } else
278                 {
279                     return null;
280                 }
281             }
282         };
283         if (ectx.execService != null)
284         {
285             if (ectx.execService.getActiveCount() < ectx.execService.getPoolSize())
286             {
287                 return ectx.execService.submit(execution);
288             } else
289             {
290                 return new Future()
291                 {
292 
293                     @Override
294                     public boolean cancel(boolean mayInterruptIfRunning)
295                     {
296                         throw new UnsupportedOperationException("Not supported yet.");
297                     }
298 
299                     @Override
300                     public boolean isCancelled()
301                     {
302                         return false;
303                     }
304 
305                     @Override
306                     public boolean isDone()
307                     {
308                         return true;
309                     }
310 
311                     @Override
312                     public Object get() throws InterruptedException, ExecutionException
313                     {
314                         try
315                         {
316                             return execution.call();
317                         } catch (Exception ex)
318                         {
319                             throw new ExecutionException(ex);
320                         }
321                     }
322 
323                     @Override
324                     public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
325                     {
326                         try
327                         {
328                             return execution.call();
329                         } catch (Exception ex)
330                         {
331                             throw new ExecutionException(ex);
332                         }
333                     }
334                 };
335             }
336         } else
337         {
338             try
339             {
340                 return execution.call();
341             } catch (Exception ex)
342             {
343                 throw new ZExecutionException(ex);
344             }
345         }
346 
347     }
348 
349     public Object execute(@Nonnull final java.util.Map memory, @Nonnull final InputStream in,
350             @Nonnull final PrintStream out, @Nonnull final PrintStream err, Object... args)
351             throws ZExecutionException
352     {
353         return execute(memory, in, out, err, null, args);
354     }
355 
356     /**
357      * returns the set of dependencies for provided expression
358      *
359      * @param expr
360      * @return
361      * @throws Exception
362      */
363     public java.util.Set<Variable> getDependencySet(@Nonnull String expr) throws Exception
364     {
365         ProcDepContext pc = new ProcDepContext();
366         ZCompiler.compile(expr, pc, null);
367         return pc.getDependencySet();
368     }
369 
370     @CheckReturnValue
371     @Nonnull
372     public static java.util.Map newMem()
373     {
374         //return  (java.util.Map) exampleMem.clone(); // (cannot be used because of java's poor implementation of clone)
375         //return new gnu.trove.THashMap(); // (in comparisons vs the java implementations the VM performs 5% slower)
376         return new Memory();
377     }
378 
379     /**
380      * link 2 mem units in a hierarchical way
381      * TODO: syntax unsafe, have to change that later
382      * @param mem Map
383      * @param name String
384      * @param mem2 Map
385      * @throws Exception
386      */
387     @SuppressWarnings("unchecked")
388     public static void linkMem(@Nonnull java.util.Map mem, @Nonnull String name, java.util.Map mem2)
389             throws ParseException, ZExecutionException
390     {
391         final List tokens = ZCompiler.getReference(name);
392         java.util.Map ctx = mem;
393         int i = 0;
394         for (; i < tokens.size() - 1; i++)
395         {
396             final Object token = tokens.get(i);
397             Map nctx = null;
398             Object tmpObj = ctx.get(token);
399             if (!(tmpObj instanceof Map))
400             {
401                 StringBuilder parsedVar = new StringBuilder(tokens.get(0).toString());
402                 for (int u = 1; u < i + 1; u++)
403                 {
404                     parsedVar.append("." + tokens.get(u).toString());
405                 }
406                 throw new ZExecutionException("Cannot change type of existing variable "
407                         + parsedVar + " = " + ctx.get(token) + " to structure type ");
408             }
409             nctx = (Map) tmpObj;
410             if (nctx == null)
411             {
412                 nctx = newMem();
413                 ctx.put(token, nctx);
414             }
415             ctx = nctx;
416         }
417         ctx.put(tokens.get(i), mem2);
418     }
419 
420     /**
421      * get a value of a variable from memory, this function is syntax safe
422      *
423      * @param mem Map
424      * @param name String
425      * @throws Exception
426      * @return Object
427      */
428     public static Object getValue(@Nonnull java.util.Map mem, @Nonnull String name)
429             throws ParseException, ZExecutionException
430     {
431         return Program.compile(name + ";").execute(mem);
432     }
433 
434     /**
435      * Load a value into memory
436      * Have to go through the VM so that the assignement is acurate
437      * @param mem
438      * @param name String
439      * @param value Object
440      * @throws java.lang.Exception 
441      */
442     public static void addValue(@Nonnull java.util.Map mem, @Nonnull String name,
443             Object value, @Nonnull ExtendedMathContext mc) throws ParseException, ZExecutionException
444     {
445         Program.compile(name + "=" + value + ";", mc).execute(mem);
446     }
447 
448     /**
449      * build indentation string
450      * @param indent
451      * @return 
452      */
453     @CheckReturnValue
454     public static String strIndent(@Nonnegative int indent)
455     {
456         StringBuilder result = new StringBuilder();
457         for (int i = 0; i < indent; i++)
458         {
459             result.append(' ');
460         }
461         return result.toString();
462     }
463 
464     /**
465      * Output Core, in hierarchical tab indented mode
466      * @param name
467      * @param mem
468      * @param indent
469      * @param maxIndent
470      * @return 
471      */
472     @SuppressWarnings("unchecked")
473     @CheckReturnValue
474     public static String dumpCore(String name, Object mem, int indent, int maxIndent)
475     {
476         if (mem == null)
477         {
478             return "";
479         }
480         if (maxIndent > 0 && indent > maxIndent)
481         {
482             return "";
483         }
484         StringBuilder result = new StringBuilder();
485         if (mem instanceof java.util.Map)
486         {
487             result.append(strIndent(indent)).append(name).append('\n');
488             for (Map.Entry<Object, Object> elem : ((Map<Object, Object>) mem).entrySet())
489             {
490                 result.append(dumpCore(elem.getKey().toString(), elem.getValue(), indent + 1, maxIndent));
491             }
492         } else
493         {
494             result.append(strIndent(indent)).append(name).append('=').append(mem.toString()).append('\n');
495         }
496         return result.toString();
497     }
498 
499     /***
500      * This allows to run ZEL in an interactive mode
501      * @param args
502      */
503     public static void main(final String[] args) throws IOException
504     {
505         System.out.println("ZEL Shell");
506         boolean terminated = false;
507         Memory mem = new Memory();
508         InputStreamReader inp = new InputStreamReader(System.in);
509         BufferedReader br = new BufferedReader(inp);
510         while (!terminated)
511         {
512             System.out.print("zel>");
513             String line = br.readLine();
514             if (line.toUpperCase().startsWith("QUIT"))
515             {
516                 terminated = true;
517             } else
518             {
519                 try
520                 {
521                     Program.compile(line).execute(mem, System.in, System.out, System.err);
522                 } catch (ParseException ex)
523                 {
524                     System.out.println("Syntax Error: " + ex.getMessage());
525                 } catch (ExecutionException ex)
526                 {
527                     System.out.println("Execution Error: " + ex.getMessage());
528                 }
529             }
530         }
531     }
532 
533     @Override
534     public String toString()
535     {
536         return "Program: " + Arrays.toString(instructions);
537     }
538 
539     /**
540      * @return the type
541      */
542     public Program.Type getType()
543     {
544         return type;
545     }
546 }