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