KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > es > parser > Parser


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source 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, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  * Free SoftwareFoundation, Inc.
23  * 59 Temple Place, Suite 330
24  * Boston, MA 02111-1307 USA
25  *
26  * @author Scott Ferguson
27  */

28
29 package com.caucho.es.parser;
30
31 import com.caucho.es.ESBase;
32 import com.caucho.es.ESException;
33 import com.caucho.es.ESId;
34 import com.caucho.es.ESParseException;
35 import com.caucho.es.Script;
36 import com.caucho.java.JavaCompiler;
37 import com.caucho.java.LineMap;
38 import com.caucho.loader.SimpleLoader;
39 import com.caucho.log.Log;
40 import com.caucho.server.util.CauchoSystem;
41 import com.caucho.util.CharBuffer;
42 import com.caucho.util.IntArray;
43 import com.caucho.util.L10N;
44 import com.caucho.vfs.MergePath;
45 import com.caucho.vfs.Path;
46 import com.caucho.vfs.ReadStream;
47 import com.caucho.vfs.Vfs;
48 import com.caucho.vfs.WriteStream;
49
50 import java.io.IOException JavaDoc;
51 import java.util.ArrayList JavaDoc;
52 import java.util.logging.Logger JavaDoc;
53
54 /**
55  * Parser is a factory for generating compiled Script objects.
56  *
57  * <p>Most applications will use the <code>parse(String)</code> interface
58  * to parse JavaScript. That method will try to load a precompiled
59  * script from the work directory before trying to parse it.
60  *
61  * <p>Applications will often set the script path a directory for
62  * script and include the classpath in the path. Applications will
63  * often override the work dir for a more appropriate work directory.
64  *
65  * <code><pre>
66  * package com.caucho.vfs.*;
67  * package com.caucho.es.*;
68  *
69  * ...
70  *
71  * com.caucho.es.parser.Parser parser;
72  * parser = new com.caucho.es.parser.Parser();
73  *
74  * // configure the path to search for *.js files
75  * MergePath scriptPath = new MergePath();
76  * scriptPath.addMergePath(Vfs.lookup("/home/ferg/js"));
77  * ClassLoader loader = Thread.currentThread().contextClassLoader();
78  * scriptPath.addClassPath(loader);
79  * parser.setScriptPath(scriptPath);
80  *
81  * // configure the directory storing compiled scripts
82  * Path workPath = Vfs.lookup("/tmp/caucho/work");
83  * parser.setWorkDir(workPath);
84  *
85  * Script script = parser.parse("test.js");
86  * </pre></code>
87  */

88 public class Parser {
89   private static final Logger JavaDoc log = Log.open(Parser.class);
90   private static final L10N L = new L10N(Parser.class);
91   private static final Object JavaDoc LOCK = new Object JavaDoc();
92   
93   static ESId CLINIT = ESId.intern("__clinit__");
94   static ESId PROTOTYPE = ESId.intern("prototype");
95   static ESId FINALLY = ESId.intern("finally");
96   static ESId ANONYMOUS = ESId.intern("anonymous");
97   static ESId OBJECT = ESId.intern("Object");
98   static ESId REGEXP = ESId.intern("RegExp");
99   static ESId ARRAY = ESId.intern("Array");
100   static ESId LENGTH = ESId.intern("length");
101   static ESId PACKAGES = ESId.intern("Packages");
102   static ESId JAVA = ESId.intern("java");
103   static ESId COM = ESId.intern("com");
104   static ESId CAUCHO = ESId.intern("caucho");
105
106   static final int PREC_DOT = 1;
107   static final int PREC_POST = PREC_DOT;
108   static final int PREC_FUN = PREC_POST + 1;
109   static final int PREC_UMINUS = PREC_FUN + 1;
110   static final int PREC_TIMES = PREC_UMINUS + 1;
111   static final int PREC_PLUS = PREC_TIMES + 1;
112   static final int PREC_SHIFT = PREC_PLUS + 1;
113   static final int PREC_CMP = PREC_SHIFT + 1;
114   static final int PREC_EQ = PREC_CMP + 1;
115   static final int PREC_BITAND = PREC_EQ + 1;
116   static final int PREC_BITXOR = PREC_BITAND + 1;
117   static final int PREC_BITOR = PREC_BITXOR + 1;
118   static final int PREC_AND = PREC_BITOR + 1;
119   static final int PREC_OR = PREC_AND + 1;
120   static final int PREC_COND = PREC_OR + 1;
121   static final int PREC_ASSIGN = PREC_COND + 1;
122   static final int PREC_COMMA = PREC_ASSIGN + 1;
123   static final int PREC_MAX = PREC_COMMA + 1;
124
125   ClassLoader JavaDoc parentLoader;
126   ClassLoader JavaDoc loader;
127   
128   Path scriptPath;
129   boolean isEval;
130   // Name of the generated class
131
String JavaDoc className;
132   
133   Lexer lexer;
134   IntArray hashes = new IntArray();
135
136   ArrayList JavaDoc importList = new ArrayList JavaDoc();
137
138   ParseClass parseClass;
139   Function globalFunction;
140   Function staticFunction;
141   Function function; // the current prototype, i.e. class
142
Block block;
143
144   LineMap lineMap;
145   Path workPath;
146   //JavaCompiler compiler;
147
boolean isFast;
148
149   public Parser()
150   {
151     workPath = CauchoSystem.getWorkPath();
152     //compiler.setEncoding("utf8");
153
addImport("java.lang.*");
154     addImport("com.caucho.jslib.*");
155   }
156
157   /**
158    * Sets the parent class loader. If unspecified, defaults to the context
159    * classloader. Most applications will just use the default.
160    *
161    * @param parentLoader the classloader to be used for the script's parent.
162    */

163   public void setParentLoader(ClassLoader JavaDoc parentLoader)
164   {
165     this.parentLoader = parentLoader;
166   }
167
168   /**
169    * Internal method to set the actual class loader.
170    * Normally, this should only be called from com.caucho.es functions.
171    */

172   public void setClassLoader(ClassLoader JavaDoc loader)
173   {
174     this.loader = loader;
175   }
176
177   /**
178    * Returns the current class loader. If null, creates from the parent
179    * loader and the work-dir.
180    */

181   ClassLoader JavaDoc getClassLoader()
182   {
183     if (loader != null)
184       return loader;
185
186     if (parentLoader != null)
187       loader = SimpleLoader.create(parentLoader, workPath, null);
188     else
189       loader = SimpleLoader.create(null, workPath, null);
190
191     return loader;
192   }
193
194   /**
195    * Sets the path to search for imported javascript source files.
196    * Normally, ScriptPath will be a MergePath. If the MergePath
197    * adds the classpath, then JavaScript files can be put in the
198    * normal Java classpath.
199    *
200    * <p>If the ScriptPath is not specified, it will use the
201    * current directory and the classpath from the context class loader.
202    *
203    * <code><pre>
204    * MergePath scriptPath = new MergePath();
205    * scriptPath.addMergePath(Vfs.lookup("/home/ferg/js"));
206    *
207    * ClassLoader loader = Thread.currentThread().contextClassLoader();
208    * scriptPath.addClassPath(loader);
209    *
210    * parser.setScriptPath(scriptPath);
211    * </pre></code>
212    *
213    * @param scriptPath path to search for imported scripts.
214    */

215   public void setScriptPath(Path scriptPath)
216   {
217     this.scriptPath = scriptPath;
218   }
219
220   /**
221    * Returns the path to search for imported javascript. Normally, scriptPath
222    * will be a MergePath.
223    *
224    * @param scriptPath path to search for imported scripts.
225    */

226   public Path getScriptPath()
227   {
228     return scriptPath;
229   }
230
231   /**
232    * Adds a package/script to be automatically imported by the script.
233    * Each import is the equivalent of adding the following javascript:
234    *
235    * <code><pre>
236    * package <em>name</em>;
237    * </pre></code>
238    *
239    * @param name package or script name to be automatically imported.
240    */

241   public void addImport(String JavaDoc name)
242   {
243     if (! importList.contains(name))
244       importList.add(name);
245   }
246
247   /**
248    * Sets "fast" mode, i.e. JavaScript 2.0. Fast mode lets the compiler
249    * make assumptions about types and classes, e.g. that class methods
250    * won't change dynamically. This lets the compiler generate more code
251    * that directly translates to Java calls.
252    */

253   public void setFast(boolean isFast)
254   {
255     this.isFast = isFast;
256   }
257
258   /**
259    * Sets a line number map. For generated files like JSP or XTP,
260    * the error messages need an extra translation to get to the original
261    * line numbers.
262    */

263   public void setLineMap(LineMap lineMap)
264   {
265     this.lineMap = lineMap;
266   }
267
268   /**
269    * Sets the name of the generated java class. If unset, the parser will
270    * mangle the input name.
271    */

272   public void setClassName(String JavaDoc name)
273   {
274     this.className = name;
275   }
276
277   /**
278    * Sets the directory for generated *.java and *.class files.
279    * The parser will check this directory for any precompiled javascript
280    * classes. The default work-dir is /tmp/caucho on unix and
281    * c:\temp\caucho on windows.
282    *
283    * @param path the work directory.
284    */

285   public void setWorkDir(Path path)
286   {
287     workPath = path;
288   }
289
290   /**
291    * Returns the directory for generated *.java and *.class files.
292    */

293   public Path getWorkDir()
294   {
295     return workPath;
296   }
297
298   /**
299    * Main application parsing method. The parser will try to load
300    * the compiled script. If the compiled script exists and the
301    * source file has not changed, parse will return the old script.
302    * Otherwise, it will parse and compile the javascript.
303    *
304    * @param name the name of the javascript source.
305    *
306    * @return the parsed script
307    */

308   public Script parse(String JavaDoc name) throws ESException, IOException JavaDoc
309   {
310     Path path;
311
312     try {
313       String JavaDoc className;
314
315       if (this.className != null)
316         className = this.className;
317       else
318         className = "_js." + JavaCompiler.mangleName(name);
319       
320       if (scriptPath == null) {
321         MergePath mergePath = new MergePath();
322         mergePath.addMergePath(Vfs.lookup());
323         ClassLoader JavaDoc parentLoader = this.parentLoader;
324         if (parentLoader == null)
325           parentLoader = Thread.currentThread().getContextClassLoader();
326       
327         mergePath.addClassPath(parentLoader);
328         scriptPath = mergePath;
329       }
330       
331       Script script = loadScript(className);
332
333       if (! script.isModified()) {
334         script.setScriptPath(getScriptPath());
335         script.setClassDir(workPath);
336         return script;
337       }
338     } catch (Throwable JavaDoc e) {
339     }
340   
341     path = getScriptPath().lookup(name);
342
343     ReadStream is = path.openRead();
344     try {
345       return parse(is, name, 1);
346     } finally {
347       is.close();
348     }
349   }
350
351   /**
352    * Alternative parsing method when the application only has an open
353    * stream to the file. Since this method will always compile a new script,
354    * it can be significantly slower than the <code>parse(String)</code>
355    * method.
356    *
357    * @param is a read stream to the javascript source.
358    *
359    * @return the parsed script.
360    */

361   public Script parse(ReadStream is) throws ESException, IOException JavaDoc
362   {
363     return parse(is, null, 1);
364   }
365
366   /**
367    * An alternative parsing method given an open stream, a filename and
368    * a line number.
369    *
370    * @param is a stream to the javascript source.
371    * @param name filename to use for error messages.
372    * @param line initial line number.
373    *
374    * @return the compiled script
375    */

376   public Script parse(ReadStream is, String JavaDoc name, int line)
377     throws ESException, IOException JavaDoc
378   {
379     if (name == null)
380       name = is.getUserPath();
381
382     if (line <= 0)
383       line = 1;
384     
385     return parse(is, name, line, false);
386   }
387
388   /**
389    * Parses a script for the JavaScript "eval" expression. The
390    * semantics for "eval" are slightly different from standard
391    * scripts.
392    *
393    * @param is stream to the eval source.
394    * @param name filename to use for error messages
395    * @param line initial line number to use for error messages.
396    *
397    * @return the compiled script.
398    */

399   public Script parseEval(ReadStream is, String JavaDoc name, int line)
400     throws ESException, IOException JavaDoc
401   {
402     if (name == null)
403       name = "eval";
404
405     if (line <= 0)
406       line = 1;
407
408     return parse(is, name, line, true);
409   }
410
411   /**
412    * The main parser method.
413    *
414    * @param is stream to read the script.
415    * @param name the filename to use for error messages.
416    * @param line the line number to use for error messages.
417    * @param isEval if true, parse for an eval expression.
418    *
419    * @return the compiled javascript
420    */

421   private Script parse(ReadStream is, String JavaDoc name,
422                        int line, boolean isEval)
423     throws ESException, IOException JavaDoc
424   {
425     if (name == null)
426       name = "anonymous";
427
428     if (line <= 0)
429       line = 1;
430     
431     lexer = new Lexer(is, name, line);
432     if (lineMap != null)
433       lexer.setLineMap(lineMap);
434     
435     if (className == null)
436       className = "_js." + JavaCompiler.mangleName(name);
437       
438     if (scriptPath == null) {
439       MergePath mergePath = new MergePath();
440       if (is.getPath() != null)
441         mergePath.addMergePath(is.getPath().getParent());
442       else
443         mergePath.addMergePath(Vfs.lookup());
444       ClassLoader JavaDoc parentLoader = this.parentLoader;
445       if (parentLoader == null)
446         parentLoader = Thread.currentThread().getContextClassLoader();
447       
448       mergePath.addClassPath(parentLoader);
449       scriptPath = mergePath;
450     }
451     
452     block = null;
453
454     JavaCompiler compiler = JavaCompiler.create(getClassLoader());
455     compiler.setClassDir(workPath);
456
457     parseClass = new ParseClass(name, className);
458     parseClass.setParser(this);
459     if (is.getPath() != null && is.getPath().getLastModified() > 0)
460       parseClass.setSourcePath(is.getPath());
461
462     globalFunction = parseClass.newFunction(null, ESId.intern("global"), false);
463     globalFunction.setFast(isFast);
464     staticFunction = parseClass.newFunction(null, ESId.intern("__es_static"), false);
465     parseClass.setGlobal(globalFunction);
466
467     if (isEval) {
468       block = Block.create(this, globalFunction);
469       block.finish();
470       function = parseClass.newFunction(globalFunction, ESId.intern("eval"), false);
471       function.setEval();
472     }
473     else
474       function = globalFunction;
475     
476     block = Block.create(this, function);
477     parseBlock(true);
478     block.finish();
479
480     if (lexer.peek() != Lexer.EOF)
481       throw expect(L.l("end of file"));
482
483     block = Block.create(this, staticFunction);
484     block.finish();
485
486     synchronized (LOCK) {
487       Path path = workPath.lookup(className.replace('.', '/') + ".java");
488       path.getParent().mkdirs();
489     
490       WriteStream os = path.openWrite();
491       os.setEncoding("JAVA");
492       parseClass.writeCode(os);
493       os.close();
494
495       Script script;
496       try {
497         compiler.compile(className.replace('.', '/') + ".java", null);
498         script = loadScript(className);
499       } catch (Exception JavaDoc e) {
500         throw new ESParseException(e);
501       }
502
503       script.setScriptPath(getScriptPath());
504       script.setClassDir(workPath);
505       
506       return script;
507     }
508   }
509
510   /**
511    * Tries to load an already compiled script.
512    *
513    * @param className mangled classname of the script.
514    */

515   private Script loadScript(String JavaDoc className)
516     throws Exception JavaDoc
517   {
518     ClassLoader JavaDoc loader = getClassLoader();
519     Class JavaDoc cl = CauchoSystem.loadClass(className, false, loader);
520       
521     Script script = (Script) cl.newInstance();
522     script.setScriptPath(getScriptPath());
523     script.setClassDir(workPath);
524
525     return script;
526   }
527
528   /**
529    * Parse a function
530    */

531   private Function parseFunction() throws ESException
532   {
533     function.setNeedsScope();
534
535     ESId id = null;
536     if (lexer.peek() == Lexer.IDENTIFIER) {
537       lexer.next();
538       id = lexer.getId();
539     }
540
541     if (lexer.next() != '(')
542       throw expect("`('");
543
544     if (id != null) {
545       function.addVariable(block, id, null);
546       block.newVar(id).getVar().setType(Expr.TYPE_ES);
547     }
548
549     Block oldBlock = block;
550     Function oldFun = function;
551     function = parseClass.newFunction(oldFun, id, false);
552     
553     oldFun.addFunction(function);
554     
555     block = Block.create(this, function);
556
557     boolean isFirst = true;
558     while (lexer.peek() != ')') {
559       if (! isFirst && lexer.next() != ',')
560     throw expect("`,'");
561       isFirst = false;
562
563       if (lexer.next() != Lexer.IDENTIFIER)
564     throw expect(L.l("formal argument"));
565
566       ESId argId = lexer.getId();
567       
568       Expr type = null;
569       if (lexer.peek() == ':') {
570         lexer.next();
571         type = parseType();
572       }
573
574       function.addFormal(block, argId, type);
575     }
576     lexer.next();
577
578     if (lexer.peek() == ':') {
579       lexer.next();
580       Expr type = parseType();
581
582       function.setReturnType(type);
583     }
584
585     if (lexer.next() != '{')
586     throw expect("`{'");
587
588     parseBlock(false);
589
590     if (lexer.next() != '}') {
591     throw expect("`}'");
592     }
593
594     block.finish();
595
596     Function newFun = function;
597
598     function = oldFun;
599     block = oldBlock;
600
601     return newFun;
602   }
603
604   /**
605    * Parses a list of statements, returning a block representing the
606    * statements.
607    *
608    * @param parent the containing block
609    * @param isTop true if this is a top level block
610    */

611   private void parseBlock(boolean isTop)
612     throws ESException
613   {
614     loop:
615     while (true) {
616       switch (lexer.peek()) {
617       case Lexer.UNARY_OP:
618       case Lexer.BANDU_OP:
619       case Lexer.NEW:
620       case Lexer.DELETE:
621       case Lexer.VOID:
622       case Lexer.TYPEOF:
623       case Lexer.POSTFIX:
624       case Lexer.IDENTIFIER:
625       case Lexer.THIS:
626       case Lexer.EVAL:
627       case Lexer.LITERAL:
628       case Lexer.REGEXP:
629       case '(':
630       case '[':
631       case Lexer.HASH_REF:
632       case Lexer.HASH_DEF:
633       case Lexer.FUNCTION:
634         parseStatement();
635         break;
636
637       case Lexer.IF:
638       case Lexer.FOR:
639       case Lexer.WHILE:
640       case Lexer.DO:
641       case Lexer.VAR:
642       case Lexer.BREAK:
643       case Lexer.WITH:
644       case Lexer.SYNCHRONIZED:
645       case Lexer.CONTINUE:
646       case Lexer.RETURN:
647       case Lexer.SWITCH:
648       case Lexer.TRY:
649       case Lexer.THROW:
650       case ';':
651         parseStatement();
652         break;
653         
654       case '{':
655         block = block.startBlock();
656         parseStatement();
657         block = block.finishBlock();
658         break;
659
660       case Lexer.CATCH:
661         block.doTry();
662         parseCatch();
663         break;
664
665       case Lexer.FINALLY:
666         block.doTry();
667         parseFinally();
668         break;
669
670       case Lexer.CLASS:
671         parseClass();
672         break;
673
674       case Lexer.IMPORT:
675         parseImport();
676         break;
677
678       case Lexer.STATIC:
679         if (true) throw new ESException("nostatus");
680         //parseStatic();
681
break;
682
683       default:
684         break loop;
685       }
686     }
687   }
688
689   /**
690    * Parses a statement.
691    */

692   private void parseStatement() throws ESException
693   {
694     int lexeme = lexer.peek();
695     hashes.clear();
696     int line = lexer.getLine();
697     Expr expr = null;
698
699     block.setLine(line);
700
701     if (block.isDead) {
702       switch (lexeme) {
703       case ';':
704       case Lexer.VAR:
705       case Lexer.FUNCTION:
706       case Lexer.CATCH:
707       case Lexer.FINALLY:
708         break;
709
710       default:
711         throw error(L.l("can't reach statement"));
712       }
713     }
714     
715     switch (lexeme) {
716     case Lexer.IDENTIFIER:
717       parseIdentifierStatement();
718       break;
719
720     case Lexer.UNARY_OP:
721     case Lexer.BANDU_OP:
722     case Lexer.NEW:
723     case Lexer.THIS:
724     case Lexer.EVAL:
725     case Lexer.LITERAL:
726     case Lexer.REGEXP:
727     case Lexer.POSTFIX:
728     case Lexer.DELETE:
729     case Lexer.VOID:
730     case Lexer.TYPEOF:
731     case '(':
732     case '[':
733     case Lexer.HASH_REF:
734     case Lexer.HASH_DEF:
735       block.addExpr(parseExpression(PREC_MAX, true));
736       parseStatementEnd();
737       break;
738
739     case Lexer.FUNCTION:
740       lexer.next();
741       Function newFun = parseFunction();
742       break;
743
744     case Lexer.VAR:
745       parseVar(false);
746       parseStatementEnd();
747       break;
748
749     case Lexer.BREAK:
750       lexer.next();
751       if (lexer.peek() == Lexer.IDENTIFIER && ! lexer.seenLineFeed()) {
752         block.doBreak(lexer.getId());
753     lexer.next();
754       } else
755     block.doBreak();
756       parseStatementEnd();
757       break;
758
759     case Lexer.CONTINUE:
760       lexer.next();
761       if (lexer.peek() == Lexer.IDENTIFIER && ! lexer.seenLineFeed()) {
762         block.doContinue(lexer.getId());
763     lexer.next();
764       } else
765         block.doContinue();
766       
767       parseStatementEnd();
768       break;
769
770     case Lexer.RETURN:
771       lexer.next();
772
773       if (lexer.peek() == ';' || lexer.peek() == '}' ||
774       lexer.seenLineFeed()) {
775         block.doReturn();
776       } else {
777     block.doReturn(parseExpression(PREC_MAX, true));
778       }
779       
780       parseStatementEnd();
781       break;
782
783     case Lexer.IF:
784       parseIf();
785       break;
786
787     case Lexer.SWITCH:
788       parseSwitch();
789       break;
790
791     case Lexer.WHILE:
792       parseWhile(null);
793       break;
794
795     case Lexer.DO:
796       parseDo(null);
797       break;
798
799     case Lexer.FOR:
800       parseFor(null);
801       break;
802
803     case Lexer.WITH:
804       parseWith();
805       break;
806
807     case Lexer.SYNCHRONIZED:
808       parseSynchronized();
809       break;
810
811     case Lexer.TRY:
812       parseTry();
813       break;
814
815     case Lexer.THROW:
816       lexer.next();
817       block.doThrow(parseExpression(PREC_MAX));
818       break;
819
820     case ';':
821       lexer.next();
822       break;
823
824     case '{':
825       lexer.next();
826       parseBlock(false);
827       if (lexer.next() != '}')
828     throw expect("`}'");
829       break;
830
831     default:
832       throw expect(L.l("statement"));
833     }
834   }
835
836   private void parseStatementEnd() throws ESException
837   {
838     if (lexer.peek() == ';')
839       lexer.next();
840     else if (lexer.peek() == '}' ||
841          lexer.peek() == Lexer.EOF || lexer.seenLineFeed()) {
842     } else {
843       throw expect("`;'");
844     }
845   }
846
847   private void parseIdentifierStatement()
848     throws ESException
849   {
850     ESId id = lexer.getId();
851     int line = lexer.getLine();
852
853     lexer.next();
854     if (lexer.peek() != ':') {
855       Expr var = getVar(id);
856       Expr expr = parseExprRec(parseTermTail(var, false, true),
857                                PREC_MAX, false, true);
858       block.addExpr(expr);
859       parseStatementEnd();
860       return;
861     }
862
863     lexer.next();
864
865     /*
866     if (findLoop(null, id) != null)
867       throw error("duplicate label `" + id + "'");
868     */

869
870     switch (lexer.peek()) {
871     case Lexer.WHILE:
872       parseWhile(id);
873       break;
874
875     case Lexer.DO:
876       parseDo(id);
877       break;
878
879     case Lexer.FOR:
880       parseFor(id);
881       break;
882
883     default:
884       block = block.startBlock(id);
885       parseStatement();
886       block = block.finishBlock();
887       break;
888     }
889   }
890
891   private Expr parseType() throws ESException
892   {
893     if (lexer.next() != Lexer.IDENTIFIER)
894       throw expect(L.l("identifier"));
895
896     Expr type = block.newType(lexer.getId());
897
898     while (lexer.peek() == '.') {
899       lexer.next();
900       if (lexer.next() != Lexer.IDENTIFIER)
901         throw expect(L.l("identifier"));
902
903       type = type.fieldReference(lexer.getId());
904     }
905
906     return type;
907   }
908
909   /**
910    * if ::= IF '(' expr ')' stmt
911    */

912   private void parseIf() throws ESException
913   {
914     boolean isFirst = true;
915     boolean isDead = true;
916
917     block = block.create();
918
919     while (lexer.peek() == Lexer.IF) {
920       lexer.next();
921
922       if (lexer.next() != '(')
923     throw expect("`('");
924
925       block.startIf(parseBooleanExpression(PREC_MAX), ! isFirst);
926       isFirst = false;
927
928       if (lexer.next() != ')')
929     throw expect("`)'");
930
931       parseStatement();
932
933       block.endBlock();
934       if (! block.isDead)
935         isDead = false;
936       block.isDead = false;
937
938       if (lexer.peek() != Lexer.ELSE) {
939         block = block.pop();
940         return;
941       }
942
943       lexer.next();
944     }
945
946     block.startElse();
947     parseStatement();
948     block.endBlock();
949     if (! block.isDead)
950       isDead = false;
951     block = block.pop();
952     block.isDead = isDead;
953   }
954
955   /**
956    * switch ::= SWITCH '(' expr ')' '{' ((CASE expr:|DEFAULT)+ stmt*)* '}'
957    */

958   private void parseSwitch() throws ESException
959   {
960     lexer.next();
961     if (lexer.next() != '(')
962       throw expect("`)'");
963
964     Expr test = parseExpression(PREC_MAX);
965
966     if (lexer.next() != ')')
967       throw expect("`)'");
968
969     if (lexer.next() != '{')
970       throw expect("`{'");
971
972     ArrayList JavaDoc exprs = new ArrayList JavaDoc();
973
974     block = block.startSwitch(test);
975       
976     int ch;
977     while ((ch = lexer.peek()) != -1 && ch != '}') {
978       switch (ch) {
979       case Lexer.CASE:
980         lexer.next();
981         block.doCase(exprs.size());
982         exprs.add(parseExpression(PREC_MAX));
983
984         if (lexer.next() != ':')
985           throw expect("`:'");
986         break;
987
988       case Lexer.DEFAULT:
989         lexer.next();
990         if (lexer.next() != ':')
991           throw expect("`:'");
992
993         block.doDefault();
994         break;
995
996       default:
997         parseStatement();
998       }
999     }
1000
1001    if (lexer.next() != '}')
1002      throw expect("`}'");
1003
1004    block = block.fillSwitch(exprs);
1005  }
1006
1007  /**
1008   * while ::= WHILE '(' expr ')' stmt
1009   */

1010  private void parseWhile(ESId id) throws ESException
1011  {
1012    lexer.next();
1013    if (lexer.next() != '(')
1014      throw expect("`('");
1015
1016    Expr expr = parseBooleanExpression(PREC_MAX);
1017    if (expr instanceof LiteralExpr &&
1018        ! ((LiteralExpr) expr).getLiteral().toBoolean())
1019      throw error(L.l("while (false) is never executed."));
1020    block = block.startWhile(id, expr);
1021
1022    if (lexer.next() != ')')
1023      throw expect("`)'");
1024
1025    parseStatement();
1026    block = block.endLoop();
1027  }
1028
1029  /**
1030   * for ::= FOR '(' expr ')' stmt
1031   */

1032  private void parseFor(ESId id) throws ESException
1033  {
1034    lexer.next();
1035    if (lexer.next() != '(')
1036      throw expect("`('");
1037    
1038    boolean hasValue = false;
1039    Expr lhs = null;
1040    if (lexer.peek() == Lexer.VAR) {
1041      lhs = parseVar(true);
1042    }
1043    else if (lexer.peek() != ';') {
1044      lhs = parseExpression(PREC_MAX);
1045    } else if (lexer.peek() == Lexer.IN)
1046      throw expect(L.l("expression"));
1047
1048    if (lexer.peek() == Lexer.IN) {
1049      parseForIn(id, lhs);
1050      return;
1051    }
1052    
1053    if (lhs != null)
1054      lhs.exprStatement(block.function);
1055
1056    if (lexer.next() != ';')
1057      throw expect("`;'");
1058
1059    Expr test = null;
1060    if (lexer.peek() != ';')
1061      test = parseExpression(PREC_MAX);
1062
1063    if (lexer.next() != ';')
1064      throw expect("`;'");
1065
1066    Expr incr = null;
1067    if (lexer.peek() != ')') {
1068      incr = parseExpression(PREC_MAX);
1069      incr.killValue();
1070    }
1071    
1072    if (lexer.next() != ')')
1073      throw expect("`)'");
1074
1075    if (test instanceof LiteralExpr &&
1076        ! ((LiteralExpr) test).getLiteral().toBoolean())
1077      throw error(L.l("for (;false;) is never executed."));
1078    block = block.startFor(id, test, incr);
1079    parseStatement();
1080    block = block.endLoop();
1081  }
1082
1083  private void parseForIn(ESId id, Expr lhs)
1084    throws ESException
1085  {
1086    lexer.next();
1087
1088    String JavaDoc var = block.newIterator(id, parseExpression(PREC_MAX));
1089
1090    if (lexer.next() != ')')
1091      throw expect("`)'");
1092
1093    block = block.startWhile(id, block.hasNext(var));
1094    
1095    block.addExpr(lhs.next(var, lhs));
1096    
1097    parseStatement();
1098    block = block.endLoop();
1099  }
1100
1101  /**
1102   * do ::= DO stmt WHILE '(' expr ')'
1103   */

1104  private void parseDo(ESId id) throws ESException
1105  {
1106    lexer.next();
1107
1108    block = block.startDo(id);
1109    parseStatement();
1110
1111    if (lexer.next() != Lexer.WHILE)
1112      throw expect("`while'");
1113
1114    if (lexer.next() != '(')
1115      throw expect("`('");
1116
1117    block = block.endDo(parseBooleanExpression(PREC_MAX));
1118    
1119    if (lexer.next() != ')')
1120      throw expect("`)'");
1121    
1122    parseStatementEnd();
1123  }
1124
1125  /**
1126   * with ::= WITH '(' expr ')' stmt
1127   */

1128  private void parseWith() throws ESException
1129  {
1130    lexer.next();
1131    if (lexer.next() != '(')
1132      throw expect("`('");
1133
1134    block = block.startWith(parseExpression(PREC_MAX));
1135    
1136    if (lexer.next() != ')')
1137      throw expect("`)'");
1138    
1139    parseStatement();
1140
1141    block = block.endWith();
1142  }
1143
1144  /**
1145   * var ::= VAR id (= expr)? (, id (= expr)?)*
1146   */

1147  private Expr parseVar(boolean keepValue) throws ESException
1148  {
1149    boolean isFirst = true;
1150    Expr retVar = null;
1151    do {
1152      lexer.next();
1153    
1154      if (lexer.next() != Lexer.IDENTIFIER)
1155    throw expect(L.l("identifier"));
1156
1157      ESId name = lexer.getId();
1158
1159      Expr type = null;
1160
1161      if (lexer.peek() == ':') {
1162        lexer.next();
1163        
1164        type = parseType();
1165      }
1166      
1167      block.defVar(name, type);
1168      
1169      if (lexer.peek() == '=') {
1170    lexer.next();
1171
1172        Expr var = block.newVar(name, type);
1173        Expr value = parseExpression(Parser.PREC_ASSIGN + 1, true);
1174        block.evalExpr();
1175        var.assign(value).exprStatement(block.function);
1176      } else if (keepValue)
1177        retVar = block.newVar(name, type);
1178
1179      isFirst = false;
1180    } while (lexer.peek() == ',');
1181
1182    return retVar;
1183  }
1184
1185  /**
1186   * synchronized ::= SYNCHRONIZED '(' expr ')' stmt
1187   */

1188  private void parseSynchronized() throws ESException
1189  {
1190    lexer.next();
1191    if (lexer.next() != '(')
1192      throw expect("`('");
1193    
1194    block = block.startSynchronized(parseExpression(PREC_MAX));
1195
1196    if (lexer.next() != ')')
1197      throw expect("`)'");
1198    
1199    parseStatement();
1200
1201    block = block.endSynchronized();
1202  }
1203  
1204  /**
1205   * try ::= TRY stmt (CATCH | FINALLY)
1206   */

1207  private void parseTry() throws ESException
1208  {
1209    lexer.next();
1210
1211    block = block.startTry();
1212    parseStatement();
1213    block = block.endTry();
1214 
1215    if (lexer.peek() == Lexer.CATCH) {
1216      parseCatch();
1217    }
1218    else if (lexer.peek() == Lexer.FINALLY)
1219      parseFinally();
1220    else
1221      throw error(L.l("expected `catch' or `finally' at {0}", getToken()));
1222  }
1223
1224  /**
1225   * catch ::= CATCH '(' (expr lhs?)? ')' stmt
1226   */

1227  private void parseCatch() throws ESException
1228  {
1229    block.function.disallowJavaLocal();
1230    // XXX: don't forget catch w/o try
1231

1232    boolean oldDead = block.isDead;
1233    boolean hasCatchall = false;
1234
1235    while (lexer.peek() == Lexer.CATCH) {
1236      block.isDead = false;
1237    
1238      if (hasCatchall)
1239    throw error(L.l("catch () must be last catch clause"));
1240
1241      lexer.next();
1242      if (lexer.next() != '(')
1243    throw expect("`('");
1244
1245      String JavaDoc exceptionClass = "";
1246      while (lexer.peek() == Lexer.IDENTIFIER) {
1247    lexer.next();
1248    exceptionClass = exceptionClass + lexer.getText();
1249
1250    if (lexer.peek() != '.')
1251      break;
1252
1253    lexer.next();
1254    exceptionClass = exceptionClass + ".";
1255    if (lexer.peek() != Lexer.IDENTIFIER)
1256      throw expect(L.l("identifier"));
1257      }
1258
1259      ESId name = null;
1260      if (lexer.peek() == Lexer.IDENTIFIER) {
1261    name = lexer.getId();
1262    lexer.next();
1263      }
1264
1265      if (lexer.next() != ')') {
1266    if (exceptionClass.equals(""))
1267      throw expect(L.l("identifier"));
1268    else
1269      throw expect("`)'");
1270      }
1271
1272      if (name == null) {
1273        name = ESId.intern(exceptionClass);
1274        exceptionClass = "java.lang.Exception";
1275      }
1276
1277      Expr var = null;
1278      if (name != null)
1279        var = block.newVar(name);
1280      
1281      block = block.startCatch(exceptionClass, var);
1282      parseStatement();
1283      if (! block.isDead)
1284        oldDead = false;
1285      block = block.endCatch();
1286    }
1287
1288    block.isDead = oldDead;
1289    // Don't forget to throw
1290
if (lexer.peek() == Lexer.FINALLY)
1291      parseFinally();
1292  }
1293
1294  /**
1295   * finally ::= FINALLY stmt
1296   */

1297  private void parseFinally() throws ESException
1298  {
1299    boolean oldDead = block.isDead;
1300    block.isDead = false;
1301    lexer.next();
1302
1303    block = block.startFinally();
1304    
1305    parseStatement();
1306
1307    block = block.endFinally();
1308    block.isDead = oldDead;
1309  }
1310
1311  static ESId BOGUS = ESId.intern("return ");
1312
1313  /**
1314   * Parse a class
1315   */

1316  private void parseClass() throws ESException
1317  {
1318    if (function.getParent() != null)
1319      throw error(L.l("`class' only allowed at top level"));
1320    
1321    lexer.next();
1322
1323    if (lexer.next() != Lexer.IDENTIFIER)
1324      throw expect("class name");
1325
1326    ESId id = lexer.getId();
1327
1328    ESId proto = parseExtends();
1329
1330    if (lexer.next() != '{')
1331      throw expect("`{'");
1332
1333    ParseClass oldClass = parseClass;
1334    Function oldGlobal = globalFunction;
1335    Function oldStatic = staticFunction;
1336    Function oldFunction = function;
1337    Block oldBlock = block;
1338
1339    parseClass = oldClass.newClass(id);
1340    parseClass.setProto(proto);
1341    
1342    globalFunction = parseClass.newFunction(null, ESId.intern("global"), true);
1343    staticFunction = parseClass.newFunction(null, ESId.intern("__es_static"), true);
1344    parseClass.setGlobal(globalFunction);
1345
1346    function = globalFunction;
1347    block = Block.create(this, function);
1348      
1349    parseBlock(true);
1350    block.finish();
1351    
1352    block = Block.create(this, staticFunction);
1353    block.finish();
1354
1355    if (parseClass.getFunction(id) == null) {
1356      function = parseClass.newFunction(null, id, false);
1357      block = Block.create(this, function);
1358      block.finish();
1359    }
1360
1361    block = oldBlock;
1362    function = oldFunction;
1363    globalFunction = oldGlobal;
1364    staticFunction = oldStatic;
1365    parseClass = oldClass;
1366    
1367    if (lexer.next() != '}')
1368      throw expect("`}'");
1369  }
1370
1371  private ESId parseExtends()
1372    throws ESException
1373  {
1374    if (lexer.peek() != Lexer.EXTENDS)
1375      return null;
1376
1377    lexer.next();
1378    if (lexer.next() != Lexer.IDENTIFIER)
1379      throw expect(L.l("parent class name"));
1380
1381    return lexer.getId();
1382  }
1383
1384  private void parseImport()
1385    throws ESException
1386  {
1387    CharBuffer path = new CharBuffer();
1388
1389    lexer.next();
1390
1391    while (true) {
1392      if (lexer.peek() == Lexer.BIN_OP && lexer.getOp() == '*') {
1393        lexer.next();
1394        path.append('*');
1395        importList.add(path.close());
1396        return;
1397      }
1398
1399      if (lexer.peek() != Lexer.IDENTIFIER)
1400    throw expect(L.l("identifier"));
1401
1402      path.append(lexer.getText());
1403
1404      lexer.next();
1405
1406      if (lexer.peek() != '.')
1407    break;
1408
1409      lexer.next();
1410      path.append('.');
1411    }
1412
1413    String JavaDoc className = path.close();
1414    String JavaDoc pathName = className.replace('.', '/') + ".js";
1415
1416    Path importPath = getScriptPath().lookup(pathName);
1417
1418    if (importPath.exists()) {
1419      function.cl.addImport(pathName);
1420      return;
1421    }
1422
1423    try {
1424      CauchoSystem.loadClass(className, false, getClassLoader());
1425
1426      importList.add(className);
1427    } catch (ClassNotFoundException JavaDoc e) {
1428      throw error(L.l("can't open import `{0}'", pathName));
1429    }
1430  }
1431  
1432/*
1433  private void parseStatic() throws ESException
1434  {
1435    if (function.rest != null || staticCode == null)
1436      throw error("illegal static statement");
1437
1438    lexer.next();
1439
1440    int oldVar = stmtVar;
1441    ParseFun oldFun = function;
1442    try {
1443      stmtVar = -1;
1444      function = staticFunction;
1445      parseStatement(staticCode);
1446    } finally {
1447      function = oldFun;
1448      stmtVar = oldVar;
1449    }
1450    } */

1451
1452  private Expr parseExpression(int prevPrec, boolean isTop)
1453    throws ESException
1454  {
1455    Expr result = parseExprRec(parseTerm(isTop), prevPrec, false, isTop);
1456    result.getType();
1457    return result;
1458  }
1459
1460  private Expr parseBooleanExpression(int prevPrec)
1461       throws ESException
1462  {
1463    Expr result = parseExprRec(parseTerm(false), prevPrec, true, false);
1464    result.getType();
1465    return result;
1466  }
1467
1468  private Expr parseExpression(int prevPrec)
1469       throws ESException
1470  {
1471    Expr result = parseExprRec(parseTerm(false), prevPrec, false, false);
1472    result.getType();
1473    return result;
1474  }
1475
1476  private Expr parseExpression()
1477       throws ESException
1478  {
1479    Expr result = parseExprRec(parseTerm(false), PREC_MAX, false, false);
1480    result.getType();
1481    return result;
1482  }
1483
1484  /*
1485   * parseExpression ()
1486   *
1487   * Grammar:
1488   * expr ::= obj (op obj)*
1489   */

1490  private Expr parseExprRec(Expr lexpr, int prevPrec,
1491                            boolean isBool, boolean isTop)
1492       throws ESException
1493  {
1494    Expr rexpr = null;
1495    int op = 0;
1496    int lex = 0;
1497    int prec = 0;
1498
1499    while (true) {
1500      boolean doLookahead = false;
1501      boolean doPostfix = false;
1502      boolean isRightAssoc = false;
1503      int nextPrec = 0;
1504      int nextOp = 0;
1505      int nextLex = 0;
1506
1507      switch (lexer.peek()) {
1508      case '=':
1509    if (op != 0 && lex != ',')
1510      throw error(L.l("illegal left hand side of assignment"));
1511    if (isBool)
1512      throw error(L.l("assignment used as boolean needs parentheses"));
1513
1514      case Lexer.BIN_OP:
1515        // careful with and/or for unassigned local variables
1516
if (lexer.getOp() == Lexer.AND || lexer.getOp() == Lexer.OR)
1517          function.setVars();
1518        // fall through
1519
case ',':
1520      case Lexer.BANDU_OP:
1521      case '?':
1522    nextLex = lexer.peek();
1523    nextOp = lexer.getOp();
1524    nextPrec = lexer.getPrecedence();
1525    isRightAssoc = lexer.isRightAssoc();
1526    doLookahead = true;
1527    break;
1528
1529      default:
1530    return op != 0 ? lexpr.binaryOp(lex, op, rexpr) : lexpr;
1531      }
1532
1533      if (nextPrec >= prevPrec) {
1534    return op != 0 ? lexpr.binaryOp(lex, op, rexpr) : lexpr;
1535      }
1536      else if (prec == 0) {
1537      }
1538      else if (nextPrec < prec) {
1539    rexpr = parseExprRec(rexpr, prec, isBool, isTop);
1540    continue;
1541      }
1542      else {
1543    lexpr = op != 0 ? lexpr.binaryOp(lex, op, rexpr) : lexpr;
1544      }
1545
1546      prec = nextPrec;
1547      lex = nextLex;
1548      op = nextOp;
1549      
1550      if (doLookahead)
1551    lexer.next();
1552
1553      if (isRightAssoc) {
1554    /* XXX: is this the right thing to do? + 1 */
1555    rexpr = parseExpression(prec + 1, isTop);
1556      }
1557      else if (op == '?') {
1558        function.setVars();
1559    Expr mexpr = parseExpression(Parser.PREC_ASSIGN + 1);
1560    if (lexer.peek() != ':')
1561      throw expect("`:'");
1562    lexer.next();
1563    rexpr = parseExpression(Parser.PREC_ASSIGN + 1, isTop);
1564
1565    lexpr = lexpr.conditional(mexpr, rexpr);
1566    op = 0;
1567      }
1568      else
1569    rexpr = parseTerm(isTop);
1570    }
1571  }
1572
1573  /*
1574   * parseTerm
1575   *
1576   * term ::= BANDU_OP term
1577   * ::= UNARY_OP term
1578   * ::= IDENTIFIER termTail
1579   * ::= LITERAL termTail
1580   * ::= objLiteral termTail
1581   * ::= '(' expr ')' termTail
1582   */

1583  private Expr parseTerm(boolean isTop) throws ESException
1584  {
1585    int op;
1586
1587    switch (lexer.peek()) {
1588    case Lexer.BANDU_OP:
1589    case Lexer.UNARY_OP:
1590      lexer.next();
1591      op = lexer.getOp();
1592      return parseTerm(isTop).unaryOp(op);
1593
1594    case Lexer.VOID:
1595      lexer.next();
1596      return parseTerm(isTop).doVoid();
1597
1598    case Lexer.TYPEOF:
1599      lexer.next();
1600      return parseTerm(isTop).typeof();
1601
1602    case Lexer.DELETE:
1603      lexer.next();
1604      return parseTerm(isTop).delete();
1605
1606    case Lexer.POSTFIX:
1607      lexer.next();
1608      op = lexer.getOp();
1609      return parseTerm(isTop).prefix(op);
1610
1611    case Lexer.LITERAL:
1612    case Lexer.REGEXP:
1613    case Lexer.IDENTIFIER:
1614    case Lexer.THIS:
1615    case Lexer.EVAL:
1616    case Lexer.NEW:
1617    case '(':
1618    case '{':
1619    case '[':
1620    case Lexer.HASH_REF:
1621    case Lexer.HASH_DEF:
1622    case Lexer.FUNCTION:
1623      return parseLhs(false, isTop);
1624
1625    default:
1626      throw expect(L.l("expression"));
1627    }
1628  }
1629
1630  /**
1631   * Parses the left-hand side of an expression
1632   *
1633   * @param hasNew true if this follows a new
1634   * @return the new expression
1635   */

1636  private Expr parseLhs(boolean hasNew, boolean isTop)
1637    throws ESException
1638  {
1639    String JavaDoc name;
1640    int op;
1641    Expr expr = null;
1642
1643    switch (lexer.next()) {
1644    case Lexer.NEW:
1645      return parseTermTail(parseLhs(true, isTop), hasNew, isTop);
1646
1647    case Lexer.LITERAL:
1648      return parseTermTail(block.newLiteral(lexer.getLiteral()),
1649                           hasNew, isTop);
1650
1651    case Lexer.REGEXP:
1652      /*
1653      return parseTermTail(block.newRegexp(lexer.getLiteral(),
1654                                          lexer.getFlags()),
1655               hasNew, isTop);
1656      */

1657      throw new UnsupportedOperationException JavaDoc();
1658    
1659    case Lexer.IDENTIFIER:
1660      return parseTermTail(getVar(lexer.getId()),
1661                           hasNew, isTop);
1662
1663    case Lexer.THIS:
1664      return parseTermTail(block.newThis(), hasNew, isTop);
1665
1666    case Lexer.EVAL:
1667      if (lexer.peek() != '(')
1668    throw expect("`('");
1669      function.setArguments();
1670      return parseTermTail(block.newVar(ESId.intern("eval")), hasNew, false);
1671
1672    case '(':
1673      expr = parseExpression(PREC_MAX);
1674      if (lexer.next() != ')')
1675    throw expect("`)'");
1676      return parseTermTail(expr, hasNew, isTop);
1677
1678    case '{':
1679      expr = parseObjectLiteral(-1);
1680      if (lexer.next() != '}')
1681    throw expect("`}'");
1682      return parseTermTail(expr, hasNew, isTop);
1683
1684    case '[':
1685      expr = parseArrayLiteral(-1);
1686      if (lexer.next() != ']')
1687    throw expect("`]'");
1688      return parseTermTail(expr, hasNew, isTop);
1689
1690    case Lexer.FUNCTION:
1691      Function newFun = parseFunction();
1692        
1693      //function.addFunction(newFun);
1694
function.addVariable(block, newFun.id, null);
1695      block.newVar(newFun.id).getVar().setType(Expr.TYPE_ES);
1696      expr = block.newVar(newFun.id);
1697      return parseTermTail(expr, hasNew, isTop);
1698
1699    case Lexer.HASH_DEF:
1700      switch (lexer.peek()) {
1701      case '{':
1702    lexer.next();
1703    expr = parseObjectLiteral(lexer.intValue);
1704    if (lexer.next() != '}')
1705      throw expect("`}'");
1706    return parseTermTail(expr, hasNew, isTop);
1707
1708      case '[':
1709    lexer.next();
1710    expr = parseArrayLiteral(lexer.intValue);
1711    if (lexer.next() != ']')
1712      throw expect("`]'");
1713    return parseTermTail(expr, hasNew, isTop);
1714
1715      default:
1716        /* XXX:
1717    expr = parseLhs(hasNew, isTop);
1718    int var = code.newVar();
1719    code.store(var);
1720    hashes.add(lexer.intValue, var);
1721    code.load(var);
1722        */

1723    return expr;
1724      }
1725      /*
1726    case Lexer.HASH_REF:
1727      if (hashes.size() <= lexer.intValue ||
1728      hashes.get(lexer.intValue) <= 0)
1729    throw error("bad sharp reference at " + getToken());
1730
1731      return parseTermTail(code.load(hashes.get(lexer.intValue)),
1732               hasNew, isTop);
1733      */

1734    default:
1735      throw expect(L.l("term"));
1736    }
1737  }
1738
1739  /*
1740   * parseTermTail
1741   *
1742   * termTail ::=
1743   * ::= '.' Id termTail
1744   * ::= '(' exprList ')' termTail
1745   * ::= '[' expr ']' termTail
1746   * ::= '++' termTail
1747   */

1748  private Expr parseTermTail(Expr term, boolean hasNew, boolean isTop)
1749    throws ESException
1750  {
1751    int op;
1752
1753    while (true) {
1754      switch (lexer.peek()) {
1755      case '.':
1756    lexer.next();
1757    if (lexer.next() != Lexer.IDENTIFIER)
1758      throw expect(L.l("property name"));
1759
1760    term = term.fieldReference(lexer.getId());
1761    break;
1762
1763      case '(':
1764    if (isTop && lexer.seenLineFeed())
1765      return term;
1766
1767    lexer.next();
1768    
1769    int n = 0;
1770        CallExpr call;
1771        if (hasNew)
1772          call = term.startNew();
1773        else
1774          call = term.startCall();
1775        
1776    while (lexer.peek() != ')') {
1777      if (n != 0 && lexer.peek() != ',')
1778        throw expect("`,'");
1779      else if (n != 0)
1780        lexer.next();
1781
1782      call.addCallParam(parseExpression(PREC_COMMA));
1783      n++;
1784    }
1785    lexer.next();
1786
1787        if (hasNew)
1788          return call;
1789        else
1790          term = call;
1791        break;
1792
1793      case '[':
1794    if (isTop && lexer.seenLineFeed())
1795      return term;
1796
1797    lexer.next();
1798    term = term.fieldReference(parseExpression(PREC_MAX));
1799
1800    if (lexer.next() != ']')
1801      throw expect("`]'");
1802    break;
1803
1804      case Lexer.POSTFIX:
1805    if (hasNew)
1806      return term.startNew();
1807
1808    if (lexer.seenLineFeed())
1809      return term;
1810
1811    term = term.postfix(lexer.getOp());
1812
1813    lexer.next();
1814    break;
1815
1816      case '@':
1817    lexer.next();
1818
1819    term = term.cast(parseType());
1820    break;
1821
1822      default:
1823    if (hasNew)
1824      return term.startNew();
1825        else
1826          return term;
1827      }
1828    }
1829  }
1830
1831  private Expr parseObjectLiteral(int hash) throws ESException
1832  {
1833    Expr expr = block.newVar(ESId.intern("Object"));
1834    CallExpr call = expr.startCall();
1835
1836    /*
1837    if (hash >= 0) {
1838      if (hashes.size() <= hash)
1839    hashes.setLength(hash + 1);
1840      hashes.add(hash, var);
1841    }
1842    */

1843
1844    if (lexer.peek() == ',') {
1845      lexer.next();
1846
1847      return call;
1848    }
1849
1850    while (lexer.peek() == Lexer.LITERAL ||
1851       lexer.peek() == Lexer.IDENTIFIER) {
1852      ESId id;
1853
1854      if (lexer.next() == Lexer.LITERAL)
1855        id = ESId.intern(lexer.literal.toString());
1856      else
1857    id = lexer.getId();
1858
1859      if (lexer.next() != ':')
1860    throw expect("`:'");
1861
1862      call.addCallParam(block.newLiteral(id));
1863      call.addCallParam(parseExpression(PREC_COMMA));
1864
1865      if (lexer.peek() != ',')
1866    break;
1867      lexer.next();
1868    }
1869
1870    return call;
1871  }
1872
1873  private Expr parseArrayLiteral(int hash)
1874    throws ESException
1875  {
1876    Expr expr = block.newVar(ESId.intern("Array"));
1877    
1878    CallExpr call = expr.startCall();
1879
1880    boolean isFirst = true;
1881    while (lexer.peek() != ']') {
1882      if (lexer.peek() == ',') {
1883    lexer.next();
1884        call.addCallParam(block.newLiteral(ESBase.esUndefined));
1885        isFirst = false;
1886    continue;
1887      }
1888
1889      Expr value = parseExpression(PREC_COMMA);
1890      
1891      if (isFirst && lexer.peek() == ']')
1892        return block.newArray(value);
1893
1894      if (lexer.peek() != ',') {
1895        call.addCallParam(value);
1896    break;
1897      }
1898      
1899      lexer.next();
1900      
1901      if (isFirst && lexer.peek() == ']')
1902        return block.newArray(value);
1903      
1904      isFirst = false;
1905      call.addCallParam(value);
1906    }
1907
1908    return call;
1909  }
1910
1911  /**
1912   * Gets a variable instance.
1913   */

1914  private Expr getVar(ESId name)
1915    throws ESException
1916  {
1917    if (name == PACKAGES)
1918      return new PackageExpr(block);
1919    else if (name == JAVA)
1920      return new PackageExpr(block).fieldReference(JAVA);
1921    else if (name == CAUCHO)
1922      return new PackageExpr(block).fieldReference(COM).fieldReference(CAUCHO);
1923    else if (block.hasVar(name))
1924      return block.newVar(name);
1925    else {
1926      for (int i = 0; i < importList.size(); i++) {
1927        String JavaDoc className = (String JavaDoc) importList.get(i);
1928
1929        if (className.endsWith(".*"))
1930          className = className.substring(0, className.length() - 1) + name;
1931
1932        try {
1933          Class JavaDoc cl = CauchoSystem.loadClass(className, false,
1934                                            getClassLoader());
1935
1936          return new JavaClassExpr(block, cl);
1937        } catch (Throwable JavaDoc e) {
1938        }
1939      }
1940      
1941      return block.newVar(name);
1942    }
1943  }
1944
1945  /**
1946   * Returns the current filename being parsed.
1947   */

1948  public String JavaDoc getFilename()
1949  {
1950    return lexer.getFilename();
1951  }
1952
1953  /**
1954   * Creates an error message with the given text.
1955   */

1956  ESException error(String JavaDoc text)
1957  {
1958    return lexer.error(text);
1959  }
1960
1961  /**
1962   * Returns the current token for an error message.
1963   */

1964  private String JavaDoc getToken()
1965  {
1966    if (lexer.isEof())
1967      return "end of file";
1968    else
1969      return "`" + lexer.getToken() + "'";
1970  }
1971
1972  /**
1973   * Returns a parse exception when expecting a result.
1974   */

1975  private ESException expect(String JavaDoc expect)
1976  {
1977    try {
1978      return lexer.error(L.l("expected {0} at {1}", expect, getToken()));
1979    } catch (Exception JavaDoc e) {
1980      e.printStackTrace();
1981      return null;
1982    }
1983  }
1984}
1985
Popular Tags