Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/jext/dawn/DawnParser.java


1   /*
2    * 11:58:05 07/08/00
3    *
4    * DawnParser.java - Dawn is a RPN based scripting language
5    * Copyright (C) 2000 Romain Guy
6    * romain.guy@jext.org
7    * www.jext.org
8    *
9    * This program is free software; you can redistribute it and/or
10   * modify it under the terms of the GNU General Public License
11   * as published by the Free Software Foundation; either version 2
12   * of the License,  or any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   *
19   * You should have received a copy of the GNU General Public License
20   * along with this program; if not,  write to the Free Software
21   * Foundation,  Inc.,  59 Temple Place - Suite 330,  Boston,  MA  02111-1307,  USA.
22   */
23  
24  package org.jext.dawn;
25  
26  import java.io.*;
27  
28  import java.util.Stack;
29  import java.util.Vector;
30  import java.util.Hashtable;
31  import java.util.Enumeration;
32  
33  /**
34   * <code>DawnParser</code> is the Dawn scripting language interpreter.
35   * Dawn is a language based on RPN. Dawn is also a very modulary language.
36   * Basic usage of Dawn is:<br>
37   * <pre>
38   * DawnParser.init();
39   * // code is a String containing the script
40   * DawnParser parser = new DawnParser(new StringReader(code));
41   * try
42   * {
43   *   parser.exec();
44   * } catch (DawnRuntimeException dre) {
45   *   System.err.println(dre.getMessage());
46   * }
47   * </pre><p>
48   * Note the call to <code>init()</code>. You may not want to call this method,
49   * but if you don't, then Dawn will provide NO FUNCTION AT ALL. Even basic ones,
50   * like + - * drop sto rcl, won't work !! This is due to the fact Dawn can be
51   * entirely customized.<br>
52   * In fact, <code>init()</code> simply install basic packages (loop, test,
53   * util, stack, math, err, io, string, naming). But you can load only one, or
54   * many, of them and also install your own packages to replace default ones.<br>
55   * You may also load extra packages with the: <code>installPackage()</code> method.<p>
56   * Read the documentation for further informations.
57   * @author Romain Guy
58   * @version 1.1.1
59   */
60  
61  public class DawnParser
62  {
63    /** Gives Dawn interpreter version numbering */
64    public static final String DAWN_VERSION = "Dawn v1.1.1 final [$12:12:55 07/08/00]";
65  
66    /** Identifier for a stack element containing a numeric value */
67    public static final int DAWN_NUMERIC_TYPE = 0;
68    /** Identifier for a stack element containing a string */
69    public static final int DAWN_STRING_TYPE = 1;
70    /** Identifier for a stack element defining a literal (variable name) */
71    public static final int DAWN_LITERAL_TYPE = 2;
72    /** Identifier for a stack element defining an array */
73    public static final int DAWN_ARRAY_TYPE = 3;
74  
75    // global functions loaded from packages
76    private static Hashtable functions = new Hashtable(200);
77    // global variables
78    private static Hashtable variables = new Hashtable();
79    // installed packages
80    private static Vector installedPackages = new Vector();
81    private static Vector installedRuntimePackages = new Vector();
82    // init flag
83    private static boolean isInited = false;
84  
85    // it true, the parser stops
86    private boolean stopped = false;
87    // properties set
88    private Hashtable properties = new Hashtable();
89    // stream tokenizer: this is Dawn parser engine
90    private StreamTokenizer st;
91    // the stack where datas are put
92    private Stack stack;
93    // functions created on runtime
94    private Hashtable runtimeFunctions;
95    // variables created on runtime
96    private Hashtable runtimeVariables;
97    // line number in the script
98    public int lineno = 1;
99  
100   // standard streams
101   public PrintStream out = System.out;
102   public PrintStream err = System.err;
103   public InputStream in  = System.in ;
104 
105   /**
106    * Initializes Dawn default packages. This is strongly recommended
107    * to call this method before any use of the parser.
108    */
109 
110   public static void init()
111   {
112     System.out.println(DAWN_VERSION);
113 
114     installPackage("dawn.array");
115     installPackage("dawn.err");
116     installPackage("dawn.io");
117     installPackage("dawn.javaccess");
118     installPackage("dawn.loop");
119     installPackage("dawn.math");
120     installPackage("dawn.naming");
121     installPackage("dawn.stack");
122     installPackage("dawn.string");
123     installPackage("dawn.test");
124     installPackage("dawn.util");
125 
126     System.out.println();
127     isInited = true;
128   }
129 
130   /**
131    * Returns true if the parser has already been initialized.
132    * Dawn is considered initialized when a call to <code>init()</code>
133    * has been made.
134    */
135 
136   public static boolean isInitialized()
137   {
138     return isInited;
139   }
140 
141   /**
142    * Installs a package from Dawn archive.
143    * @param packageName The package to load
144    */
145 
146   public static void installPackage(String packageName)
147   {
148     installPackage(DawnParser.class, packageName, null);
149   }
150 
151   /**
152    * Installs a package specific to a given class. The class will give infos
153    * to both load the package file and the package classes.
154    * @param loader The <code>Class</code> which calls this, if the class is
155    *               not part of Dawn standard package
156    * @param packageName The package to load
157    */
158 
159   public static void installPackage(Class loader, String packageName)
160   {
161     installPackage(loader, packageName, null);
162   }
163 
164   /**
165    * Installs a package specific to a given class. The class will give infos
166    * to both load the package file and the package classes.
167    * @param loader The <code>Class</code> which calls this, if the class is
168    *               not part of Dawn standard package
169    * @param packageName The package to load
170    * @param parser If this parameter is not set to null, the package is loaded
171    *               as runtime package
172    */
173 
174   public static void installPackage(Class loader, String packageName, DawnParser parser)
175   {
176     if (packageName == null || loader == null)
177       return;
178 
179     // check first if the package is already installed
180     // (case of packages dependencies)
181     if (installedPackages.contains(packageName))
182     {
183       System.out.println("Dawn:<installPackage>:package " + packageName + " is already installed");
184       return;
185     }
186 
187     // get classes to be loaded
188     String[] classes = getClasses(loader, packageName);
189     if (classes == null)
190     {
191       System.out.println("Dawn:<installPackage:err>:couldn't install " + packageName);
192       return;
193     }
194 
195     Object obj = null;
196     Class _class = null;
197     String className = null;
198     Function _function = null;
199     CodeSnippet _codeFunction = null;
200     // ClassLoader classLoader = loader.getClassLoader();
201 
202     try
203     {
204       // load classes
205       for (int i = 0; i < classes.length; i++)
206       {
207         className = classes[i];
208         _class = Class.forName(className); //, true, classLoader);
209         if (_class == null)
210         {
211           // if class is null, we get rid of it
212           System.out.println("Dawn:<installPackage:err>:couldn't find class " + className +
213                              " in package " + packageName);
214           continue;
215         }
216 
217         // we create an instance of the class to check it
218         obj = _class.newInstance();
219         if (obj instanceof Function)
220         {
221           // if it is a function, then we add it to the list
222           _function = (Function) obj;
223           (parser == null ? functions : parser.getRuntimeFunctions()).put(_function.getName(),
224                                                                           _function);
225         } else if (obj instanceof CodeSnippet) {
226           // it is a coded function, we build it
227           _codeFunction = (CodeSnippet) obj;
228           if (parser == null)
229             createGlobalFunction(_codeFunction.getName(), _codeFunction.getCode());
230           else
231             parser.createRuntimeFunction(_codeFunction.getName(), _codeFunction.getCode());
232         }
233       }
234     } catch(Exception e) {
235       System.out.println("Dawn:<installPackage:err>:couldn't load class " + className +
236                          " from package " + packageName);
237       System.out.println("Dawn:<installPackage:err>:package " + packageName + " wasn't loaded");
238       return;
239     }
240 
241     System.out.println("Dawn:<installPackage>:\t" + packageName + (packageName.length() < 8 ? "\t\t" : "\t") +
242                        "successfully installed");
243     (parser == null ? installedPackages : installedRuntimePackages).addElement(packageName);
244   }
245 
246   // reads a package file and get classes-to-be-loaded names. it also checks package
247   // dependencies. if a dependency is found, requested package is loaded
248 
249   private static String[] getClasses(Class loader, String packageName)
250   {
251     Vector buf = new Vector();
252 
253     InputStream _in = loader.getResourceAsStream(packageName);
254     if (_in == null)
255       return null;
256     BufferedReader in = new BufferedReader(new InputStreamReader(_in));
257 
258     String line;
259     try
260     {
261       while ((line = in.readLine()) != null)
262       {
263         line = line.trim();
264         if (line.length() == 0)
265           continue;
266 
267         if (line.charAt(0) == '#')
268           continue;
269         else if (line.startsWith("needs"))
270         {
271           int index = line.indexOf(' ');
272           if (index == -1 || index + 1 == line.length())
273           {
274             System.out.println("Dawn:<installPackage:err>:package " + packageName +
275                                " contains a bad \'needs\' statement");
276             continue;
277           }
278 
279           installPackage(loader, line.substring(index + 1), null);
280         } else
281           buf.addElement(line);
282       }
283       in.close();
284     } catch (IOException ioe) {
285       return null;
286     }
287 
288     if (buf.size() > 0)
289     {
290       String[] classes = new String[buf.size()];
291       buf.copyInto(classes);
292       buf = null;
293       return classes;
294     } else
295       return null;
296   }
297 
298   /**
299    * Creates a new parser.
300    * @param in A <code>Reader</code> which will deliver the script to the parser
301    */
302 
303   public DawnParser(Reader in)
304   {
305     st = createTokenizer(in);
306 
307     stack = new Stack();
308     runtimeFunctions = new Hashtable();
309     runtimeVariables = new Hashtable();
310   }
311 
312   /**
313    * Sets the parser print stream. Default packages may
314    * pass informations through this stream (println function
315    * for instance).
316    * @param out The new <code>PrintStream</code>
317    */
318 
319   public void setOut(PrintStream out)
320   {
321     this.out = out;
322   }
323 
324   /**
325    * Sets the parser error print stream. Default packages may
326    * pass informations through this stream.
327    * @param err The new <code>PrintStream</code> used for errors
328    */
329 
330   public void setErr(PrintStream err)
331   {
332     this.err = err;
333   }
334 
335   /**
336    * Sets the parser input stream. Default packages may
337    * pass informations through this stream (inputLine...)
338    * @param out The new <code>OutputStream</code>
339    */
340 
341   public void setIn(InputStream in)
342   {
343     this.in = in;
344   }
345 
346   /**
347    * Sets the <code>StreamTokenizer</code> used to execute a script.
348    * It is HIGHLY recommended NOT TO CALL this without a very good
349    * reason.
350    * @param _st The new stream where to get the script from
351    */
352 
353   public void setStream(StreamTokenizer _st)
354   {
355     st = _st;
356   }
357 
358   /**
359    * Returns current <code>StreamTokenizer</code>. It is mostly used
360    * by functions to parse the script further. 'if' statement from
361    * test package is a good example (see also for and while from the
362    * loop package).
363    */
364 
365   public StreamTokenizer getStream()
366   {
367     return st;
368   }
369 
370   /**
371    * Creates a new StreamTokenizer, setting its properties according to
372    * the Dawn scripting language specifications. the stream is built
373    * from a Reader which is most of the time a StringReader.
374    * @param in The <code>Reader</code> which will deliver the script
375    */
376 
377   public StreamTokenizer createTokenizer(Reader in)
378   {
379     StreamTokenizer st = new StreamTokenizer(in);
380     st.resetSyntax();
381     st.eolIsSignificant(true);
382     st.whitespaceChars(0, ' ');
383     st.wordChars(' ' + 1, 255);
384     st.quoteChar('"');
385     st.quoteChar('\'');
386     st.commentChar('#');
387     st.parseNumbers();
388     st.eolIsSignificant(true);
389 
390     return st;
391   }
392 
393   /**
394    * Returns an Hashtable containing all the current global functions.
395    */
396 
397   public static Hashtable getFunctions()
398   {
399     return functions;
400   }
401 
402   /**
403    * Returns the set of runtimes functions. This is needed by installPackage()
404    * when the keyword 'needsGlobal' is used in a script.
405    */
406 
407   public Hashtable getRuntimeFunctions()
408   {
409     return runtimeFunctions;
410   }
411 
412   /**
413    * Returns the stack which containes all the current availables datas.
414    */
415 
416   public Stack getStack()
417   {
418     return stack;
419   }
420 
421   /**
422    * Checks if a given variable name is valid or not.
423    * @parma function The <code>Function</code> which called this method
424    * @param var The variable name to be tested
425    */
426 
427   public void checkVarName(Function function, String var) throws DawnRuntimeException
428   {
429     if (var.equals("needs") || var.equals("needsGlobal"))
430       throw new DawnRuntimeException(function, this, "you cannot use reserved keyword" +
431                                      "\'needs\' or \'needsGlobal\'");
432 
433     boolean word = false;
434     for (int i = 0; i < var.length(); i++)
435     {
436       if (Character.isDigit(var.charAt(i)) && !word)
437       {
438         throw new DawnRuntimeException(function, this, "bad variable/function name:" + var);
439       } else
440         word = true;
441     }
442   }
443 
444   /**
445    * Checks if stack contains enough datas to feed a function.
446    * @parma function The <code>Function</code> which called this method
447    * @param nb The amount of arguments needed
448    */
449 
450   public void checkArgsNumber(Function function, int nb) throws DawnRuntimeException
451   {
452     if (stack.size() < nb)
453       throw new DawnRuntimeException(function, this, "bad arguments number, " + nb +
454                                      " are required");
455   }
456 
457   /**
458    * Checks if the stack is empty.
459    * @parma function The <code>Function</code> which called this method
460    */
461 
462   public void checkEmpty(Function function) throws DawnRuntimeException
463   {
464     if (stack.isEmpty())
465       throw new DawnRuntimeException(function, this, "empty stack");
466   }
467 
468   /**
469    * Checks if a given level is bound in the limits of the stack.
470    * @parma function The <code>Function</code> which called this method
471    * @param level The level to be tested
472    */
473 
474   public void checkLevel(Function function, int level) throws DawnRuntimeException
475   {
476     if (level >= stack.size() || level < 0)
477       throw new DawnRuntimeException(function, this, "stack level out of bounds:" + level);
478   }
479 
480   /**
481    * Sets a property in the parser. Properties are used by external functions
482    * to store objects they may need later.
483    * @param name An <code>Object</code> describing the property. It stands for the key
484    * @param property The property value
485    */
486 
487   public void setProperty(Object name, Object property)
488   {
489     if (name == null || property == null)
490       return;
491     properties.put(name, property);
492   }
493 
494   /**
495    * Returns a property according a given key.
496    * @param name The property key
497    */
498 
499   public Object getProperty(Object name)
500   {
501     if (name == null)
502       return null;
503     return properties.get(name);
504   }
505 
506   /**
507    * Unsets (remove) a given property.
508    */
509 
510   public void unsetProperty(Object name)
511   {
512     properties.remove(name);
513   }
514 
515   /**
516    * Stops the parser.
517    */
518 
519   public void stop()
520   {
521     stopped = true;
522   }
523 
524   /**
525    * Executes loaded script.
526    */
527 
528   public void exec() throws DawnRuntimeException
529   {
530     if (st == null)
531       throw new DawnRuntimeException(this, "parser cannot execute a non-existent script");
532 
533     try
534     {
535       for( ; ; )
536       {
537         if (stopped)
538           return;
539 
540         switch(st.nextToken())
541         {
542           case StreamTokenizer.TT_EOL:
543             lineno++;
544             break;
545           case StreamTokenizer.TT_EOF:
546             // end of script
547             return;
548           case StreamTokenizer.TT_NUMBER:
549             stack.push(new Double(st.nval));
550             break;
551           case StreamTokenizer.TT_WORD:
552             if (st.sval.equals("needs") || st.sval.equals("needsGlobal"))
553             {
554               int keyWord = (st.sval.equals("needs") ? 0 : 1);
555               if (st.nextToken() == StreamTokenizer.TT_WORD)
556               {
557                 if (keyWord == 1)
558                   installPackage(st.sval);
559                 else
560                   installPackage(DawnParser.class, st.sval, this);
561                 break;
562               } else {
563                 st.pushBack();
564                 throw new DawnRuntimeException(this, "bad usage of \'needs\' or \'needsGlobal\'" +
565                                                      "reserved keyword");
566               }
567             }
568 
569             Function func = (Function) functions.get(st.sval);
570             if (func != null)
571               func.invoke(this);
572             else
573             {
574               func = (Function) runtimeFunctions.get(st.sval);
575               if (func != null)
576                 func.invoke(this);
577               else
578                 stack.push(st.sval);
579             }
580             break;
581           case '-':
582             Function fc;
583             if (st.nextToken() == StreamTokenizer.TT_WORD)
584             {
585               fc = (Function) functions.get('-' + st.sval);
586               if (fc == null)
587               {
588                 fc = (Function) runtimeFunctions.get('-' + st.sval);
589                 if (fc == null)
590                 {
591                   st.pushBack();
592                   fc = (Function) functions.get("-");
593                 }
594               }
595             } else {
596               st.pushBack();
597               fc = (Function) functions.get("-");
598             }
599 
600             if (fc != null)
601               fc.invoke(this);
602             break;
603           case '"': case '\'':
604             pushString(st.sval);
605             break;
606         }
607       }
608     } catch (IOException ioe) {
609       throw new DawnRuntimeException(this, "unexpected error occured during parsing");
610     }
611   }
612 
613   /**
614    * Returns the <code>Hashtable</code> which contains the local variables.
615    */
616 
617   public Hashtable getVariables()
618   {
619     return runtimeVariables;
620   }
621 
622   /**
623    * Returns the <code>Hashtable</code> which contains the global variables.
624    */
625 
626   public Hashtable getGlobalVariables()
627   {
628     return variables;
629   }
630 
631   /**
632    * Returns the value of a given variable. Note that global variables got
633    * priority on runtime ones.
634    * @param var The variable to be recalled   
635    */
636 
637   public Object getVariable(String var)
638   {
639     Object obj = variables.get(var);
640     if (obj == null)
641       obj = runtimeVariables.get(var);
642     return obj;
643   }
644 
645   /**
646    * Sets a runtime variable. Runtime variables are stored temporarily. After
647    * the execution of the script, they are flushed.
648    * @param var The variable name
649    * @param value An <code>Object</code> containg the variable value
650    */
651 
652   public void setVariable(String var, Object value)
653   {
654     if (value == null)
655       runtimeVariables.remove(var);
656     else if (!functions.contains(var) && !runtimeFunctions.contains(var))
657       runtimeVariables.put(var, value);
658   }
659 
660   /**
661    * Sets a global variable. Global variables are stored permanently, until the
662    * JVM is killed or until the method <code>clearGlobalVariables()</code> is called.
663    * @param var The variable name
664    * @param value An <code>Object</code> containg the variable value
665    */
666 
667   public static void setGlobalVariable(String var, Object value)
668   {
669     if (value == null)
670       variables.remove(var);
671     else if (!functions.contains(var))
672       variables.put(var, value);
673   }
674 
675   /**
676    * Clears all the global variables.
677    */
678 
679   public static void clearGlobalVariables()
680   {
681     variables.clear();
682   }
683 
684   /**
685    * Returns current line number in the script.
686    */
687 
688   public int lineno()
689   {
690     // return st.lineno();
691     return lineno;
692   }
693 
694   /**
695    * Returns a <code>String</code> containing a simple description of the current stack
696    * state. All the levels are shown, each labeled by its level number.
697    */
698 
699   public String dump()
700   {
701     Object o;
702     StringBuffer buf = new StringBuffer();
703 
704     for (int i = 0; i < stack.size(); i++)
705     {
706       buf.append(stack.size() - 1 - i).append(':');
707       o = stack.elementAt(i);
708       if (o instanceof Vector)
709         buf.append("array[").append(((Vector) o).size()).append(']');
710       else
711         buf.append(o);
712       buf.append('\n');
713     }
714     return buf.toString();
715   }
716 
717   /**
718    * Get topmost element of the stack and return is as a double value
719    * if it can. Otherwise, an exception is thrown. In any case, the
720    * element is removed from the stack.
721    */
722 
723   public double popNumber() throws DawnRuntimeException
724   {
725     checkEmpty(null);
726     Object obj = stack.pop();
727     if (!(obj instanceof Double))
728     {
729       throw new DawnRuntimeException(this, "bad argument type");
730     }
731     return ((Double) obj).doubleValue();
732   }
733 
734   /**
735    * Get topmost element of the stack and return is as a double value
736    * if it can. Otherwise, an exception is thrown.
737    */
738 
739   public double peekNumber() throws DawnRuntimeException
740   {
741     checkEmpty(null);
742     Object obj = stack.peek();
743     if (!(obj instanceof Double))
744     {
745       throw new DawnRuntimeException(this, "bad argument type");
746     }
747     return ((Double) obj).doubleValue();
748   }
749 
750   /**
751    * Pushes a number on top of the stack.
752    * @param number The number to be put on the stack
753    */
754 
755   public void pushNumber(double number)
756   {
757     stack.push(new Double(number));
758   }
759 
760   /**
761    * Get the topmost element of the stack and returns it as
762    * a <code>String</code>. If the string is enclosed by " quote
763    * characters, they are removed. The element is removed from the stack.
764    */
765 
766   public String popString() throws DawnRuntimeException
767   {
768     checkEmpty(null);
769     String str = stack.pop().toString();
770     if (str.length() != 0 && str.startsWith("\"") && str.endsWith("\""))
771       str = str.substring(1, str.length() - 1);
772     return str;
773   }
774 
775   /**
776    * Get the topmost element of the stack and returns it as
777    * a <code>String</code>. If the string is enclosed by " quote
778    * characters, they are removed.
779    */
780 
781   public String peekString() throws DawnRuntimeException
782   {
783     checkEmpty(null);
784     String str = stack.peek().toString();
785     if (str.length() != 0 && str.startsWith("\"") && str.endsWith("\""))
786       str = str.substring(1, str.length() - 1);
787     return str;
788   }
789 
790   /**
791    * Puts a <code>String</code> on top of the stack.
792    * @param str The string to be put on the stack
793    */
794 
795   public void pushString(String str)
796   {
797     if (str.length() == 2 && str.charAt(0) == '\"' && str.charAt(1) == '\"')
798       stack.push("\"\"");
799     else
800       stack.push('"' + str + '"');
801   }
802 
803   /**
804    * Gets topmost stack element and returns it as a <code>Vector</code>
805    * which is the Java object for Dawn arrays. The element is removed
806    * from the stack.
807    */
808 
809   public Vector popArray() throws DawnRuntimeException
810   {
811     checkEmpty(null);
812     Object obj = stack.pop();
813     if (!(obj instanceof Vector))
814     {
815       throw new DawnRuntimeException(this, "bad argument type");
816     }
817     return (Vector) obj;
818   }
819 
820   /**
821    * Gets topmost stack element and returns it as a <code>Vector</code>
822    * which is the Java object for Dawn arrays.
823    */
824 
825   public Vector peekArray() throws DawnRuntimeException
826   {
827     checkEmpty(null);
828     Object obj = stack.peek();
829     if (!(obj instanceof Vector))
830     {
831       throw new DawnRuntimeException(this, "bad argument type");
832     }
833     return (Vector) obj;
834   }
835 
836   /**
837    * Pushes an array on top of the stack.
838    * @param array The array to be put on the stack
839    */
840 
841   public void pushArray(Vector array)
842   {
843     stack.push(array);
844   }
845 
846   /**
847    * Returns topmost objet of the stack and remove it.
848    */
849 
850   public Object pop() throws DawnRuntimeException
851   {
852     checkEmpty(null);
853     return stack.pop();
854   }
855 
856   /**
857    * Returns topmost object of the stack.
858    */
859 
860   public Object peek() throws DawnRuntimeException
861   {
862     checkEmpty(null);
863     return stack.peek();
864   }
865 
866   /**
867    * Puts an object on the top of the stack.
868    * @param obj The object to be put on the top
869    */
870 
871   public void push(Object obj)
872   {
873     stack.push(obj);
874   }
875 
876   /**
877    * Tells wether topmost object is a numeric value or not.
878    */
879 
880   public boolean isTopNumeric()
881   {
882     return stack.peek() instanceof Double;
883   }
884 
885   /**
886    * Tells wether topmost object is a string or not.
887    */
888 
889   public boolean isTopString()
890   {
891     Object obj = stack.peek();
892     if (obj instanceof String)
893     {
894       String str = (String) obj;
895       if (str.startsWith("\"") && str.endsWith("\""))
896         return true;
897     }
898     return false;
899   }
900 
901   /**
902    * Tells wether topmost object is an array or not.
903    */
904 
905   public boolean isTopArray()
906   {
907     return stack.peek() instanceof Vector;
908   }
909 
910   /**
911    * Tells wether topmost object is a literal identifier or not.
912    */
913 
914   public boolean isTopLiteral()
915   {
916     return !isTopString() && !isTopNumeric() && !isTopArray();
917   }
918 
919   /**
920    * Returns topmost stack element type.
921    */
922 
923   public int getTopType()
924   {
925     if (isTopNumeric())
926       return DAWN_NUMERIC_TYPE;
927     else if (isTopString())
928       return DAWN_STRING_TYPE;
929     else if (isTopArray())
930       return DAWN_ARRAY_TYPE;
931     else
932       return DAWN_LITERAL_TYPE;
933   }
934 
935   /**
936    * Adds given function to the global functions list.
937    * @param function The <code>Function</code> to be added
938    */
939 
940   public static void addGlobalFunction(Function function)
941   {
942     if (function == null)
943       return;
944     String name = function.getName();
945     if (!name.equals("needs") && !name.equals("needsGlobal"))
946       functions.put(name, function);
947   }
948 
949   /**
950    * Adds given function to the runtime functions list.
951    * @param function The <code>Function</code> to be added
952    */
953 
954   public void addRuntimeFunction(Function function)
955   {
956     if (function == null)
957       return;
958     String name = function.getName();
959     if (!name.equals("needs") && !name.equals("needsGlobal"))
960       runtimeFunctions.put(name, function);
961   }
962 
963   /**
964    * Creates dynamically a function which can execute the Dawn script
965    * passed in parameter.
966    * @param code The Dawn code which will be executed by the returned
967    *             function on invoke() call
968    */
969 
970   public Function createOnFlyFunction(final String code)
971   {
972     return new Function()
973     {
974       public void invoke(DawnParser parser) throws DawnRuntimeException
975       {
976         StreamTokenizer _st = st;
977         Hashtable _variables = (Hashtable) runtimeVariables.clone();
978         st = createTokenizer(new StringReader(code));
979         exec();
980         st = _st;
981 
982         // copy changed variables
983         String _varName;
984         for (Enumeration e = runtimeVariables.keys(); e.hasMoreElements(); )
985         {
986           _varName = (String) e.nextElement();
987           if (_variables.get(_varName) != null)
988           {
989             _variables.put(_varName, runtimeVariables.get(_varName));
990           }
991         }
992         runtimeVariables = (Hashtable) _variables.clone();
993       }
994     };
995   }
996 
997   /**
998    * Creates dynamically a function which can execute the Dawn script
999    * passed in parameter. The function is added to the global functions
1000   * list and not returned.
1001   * @param name Function Dawn name
1002   * @param code The Dawn code which will be executed by function
1003   */
1004
1005  public static void createGlobalFunction(String name, final String code)
1006  {
1007    if (name == null || name.length() == 0 || name.equals("needs") ||
1008        name.equals("needsGlobal") || code == null)
1009      return;
1010
1011    functions.put(name, new Function(name)
1012    {
1013      public void invoke(DawnParser parser) throws DawnRuntimeException
1014      {
1015        StreamTokenizer _st = parser.getStream();
1016        parser.setStream(parser.createTokenizer(new StringReader(code)));
1017        parser.exec();
1018        parser.setStream(_st);
1019      }
1020    });
1021  }
1022
1023  /**
1024   * Creates dynamically a function which can execute the Dawn script
1025   * passed in parameter. The function is added to the runtime functions
1026   * list and not returned.
1027   * @param name Function Dawn name
1028   * @param code The Dawn code which will be executed by function
1029   */
1030
1031  public void createRuntimeFunction(String name, final String code)
1032  {
1033    if (name == null || name.length() == 0 || name.equals("needs") ||
1034        name.equals("needsGlobal") || code == null)
1035      return;
1036
1037    runtimeFunctions.put(name, new Function(name)
1038    {
1039      public void invoke(DawnParser parser) throws DawnRuntimeException
1040      {
1041        StreamTokenizer _st = st;
1042        st = createTokenizer(new StringReader(code));
1043        exec();
1044        st = _st;
1045      }
1046    });
1047  }
1048}
1049
1050// End of DawnParser.java