0001: /*
0002: * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: * Free SoftwareFoundation, Inc.
0023: * 59 Temple Place, Suite 330
0024: * Boston, MA 02111-1307 USA
0025: *
0026: * @author Scott Ferguson
0027: */
0028:
0029: package com.caucho.es.parser;
0030:
0031: import com.caucho.es.ESBase;
0032: import com.caucho.es.ESException;
0033: import com.caucho.es.ESId;
0034: import com.caucho.es.ESParseException;
0035: import com.caucho.es.Script;
0036: import com.caucho.java.JavaCompiler;
0037: import com.caucho.java.LineMap;
0038: import com.caucho.loader.SimpleLoader;
0039: import com.caucho.log.Log;
0040: import com.caucho.server.util.CauchoSystem;
0041: import com.caucho.util.CharBuffer;
0042: import com.caucho.util.IntArray;
0043: import com.caucho.util.L10N;
0044: import com.caucho.vfs.MergePath;
0045: import com.caucho.vfs.Path;
0046: import com.caucho.vfs.ReadStream;
0047: import com.caucho.vfs.Vfs;
0048: import com.caucho.vfs.WriteStream;
0049:
0050: import java.io.IOException;
0051: import java.util.ArrayList;
0052: import java.util.logging.Logger;
0053:
0054: /**
0055: * Parser is a factory for generating compiled Script objects.
0056: *
0057: * <p>Most applications will use the <code>parse(String)</code> interface
0058: * to parse JavaScript. That method will try to load a precompiled
0059: * script from the work directory before trying to parse it.
0060: *
0061: * <p>Applications will often set the script path a directory for
0062: * script and include the classpath in the path. Applications will
0063: * often override the work dir for a more appropriate work directory.
0064: *
0065: * <code><pre>
0066: * package com.caucho.vfs.*;
0067: * package com.caucho.es.*;
0068: *
0069: * ...
0070: *
0071: * com.caucho.es.parser.Parser parser;
0072: * parser = new com.caucho.es.parser.Parser();
0073: *
0074: * // configure the path to search for *.js files
0075: * MergePath scriptPath = new MergePath();
0076: * scriptPath.addMergePath(Vfs.lookup("/home/ferg/js"));
0077: * ClassLoader loader = Thread.currentThread().contextClassLoader();
0078: * scriptPath.addClassPath(loader);
0079: * parser.setScriptPath(scriptPath);
0080: *
0081: * // configure the directory storing compiled scripts
0082: * Path workPath = Vfs.lookup("/tmp/caucho/work");
0083: * parser.setWorkDir(workPath);
0084: *
0085: * Script script = parser.parse("test.js");
0086: * </pre></code>
0087: */
0088: public class Parser {
0089: private static final Logger log = Log.open(Parser.class);
0090: private static final L10N L = new L10N(Parser.class);
0091: private static final Object LOCK = new Object();
0092:
0093: static ESId CLINIT = ESId.intern("__clinit__");
0094: static ESId PROTOTYPE = ESId.intern("prototype");
0095: static ESId FINALLY = ESId.intern("finally");
0096: static ESId ANONYMOUS = ESId.intern("anonymous");
0097: static ESId OBJECT = ESId.intern("Object");
0098: static ESId REGEXP = ESId.intern("RegExp");
0099: static ESId ARRAY = ESId.intern("Array");
0100: static ESId LENGTH = ESId.intern("length");
0101: static ESId PACKAGES = ESId.intern("Packages");
0102: static ESId JAVA = ESId.intern("java");
0103: static ESId COM = ESId.intern("com");
0104: static ESId CAUCHO = ESId.intern("caucho");
0105:
0106: static final int PREC_DOT = 1;
0107: static final int PREC_POST = PREC_DOT;
0108: static final int PREC_FUN = PREC_POST + 1;
0109: static final int PREC_UMINUS = PREC_FUN + 1;
0110: static final int PREC_TIMES = PREC_UMINUS + 1;
0111: static final int PREC_PLUS = PREC_TIMES + 1;
0112: static final int PREC_SHIFT = PREC_PLUS + 1;
0113: static final int PREC_CMP = PREC_SHIFT + 1;
0114: static final int PREC_EQ = PREC_CMP + 1;
0115: static final int PREC_BITAND = PREC_EQ + 1;
0116: static final int PREC_BITXOR = PREC_BITAND + 1;
0117: static final int PREC_BITOR = PREC_BITXOR + 1;
0118: static final int PREC_AND = PREC_BITOR + 1;
0119: static final int PREC_OR = PREC_AND + 1;
0120: static final int PREC_COND = PREC_OR + 1;
0121: static final int PREC_ASSIGN = PREC_COND + 1;
0122: static final int PREC_COMMA = PREC_ASSIGN + 1;
0123: static final int PREC_MAX = PREC_COMMA + 1;
0124:
0125: ClassLoader parentLoader;
0126: ClassLoader loader;
0127:
0128: Path scriptPath;
0129: boolean isEval;
0130: // Name of the generated class
0131: String className;
0132:
0133: Lexer lexer;
0134: IntArray hashes = new IntArray();
0135:
0136: ArrayList importList = new ArrayList();
0137:
0138: ParseClass parseClass;
0139: Function globalFunction;
0140: Function staticFunction;
0141: Function function; // the current prototype, i.e. class
0142: Block block;
0143:
0144: LineMap lineMap;
0145: Path workPath;
0146: //JavaCompiler compiler;
0147: boolean isFast;
0148:
0149: public Parser() {
0150: workPath = CauchoSystem.getWorkPath();
0151: //compiler.setEncoding("utf8");
0152: addImport("java.lang.*");
0153: addImport("com.caucho.jslib.*");
0154: }
0155:
0156: /**
0157: * Sets the parent class loader. If unspecified, defaults to the context
0158: * classloader. Most applications will just use the default.
0159: *
0160: * @param parentLoader the classloader to be used for the script's parent.
0161: */
0162: public void setParentLoader(ClassLoader parentLoader) {
0163: this .parentLoader = parentLoader;
0164: }
0165:
0166: /**
0167: * Internal method to set the actual class loader.
0168: * Normally, this should only be called from com.caucho.es functions.
0169: */
0170: public void setClassLoader(ClassLoader loader) {
0171: this .loader = loader;
0172: }
0173:
0174: /**
0175: * Returns the current class loader. If null, creates from the parent
0176: * loader and the work-dir.
0177: */
0178: ClassLoader getClassLoader() {
0179: if (loader != null)
0180: return loader;
0181:
0182: if (parentLoader != null)
0183: loader = SimpleLoader.create(parentLoader, workPath, null);
0184: else
0185: loader = SimpleLoader.create(null, workPath, null);
0186:
0187: return loader;
0188: }
0189:
0190: /**
0191: * Sets the path to search for imported javascript source files.
0192: * Normally, ScriptPath will be a MergePath. If the MergePath
0193: * adds the classpath, then JavaScript files can be put in the
0194: * normal Java classpath.
0195: *
0196: * <p>If the ScriptPath is not specified, it will use the
0197: * current directory and the classpath from the context class loader.
0198: *
0199: * <code><pre>
0200: * MergePath scriptPath = new MergePath();
0201: * scriptPath.addMergePath(Vfs.lookup("/home/ferg/js"));
0202: *
0203: * ClassLoader loader = Thread.currentThread().contextClassLoader();
0204: * scriptPath.addClassPath(loader);
0205: *
0206: * parser.setScriptPath(scriptPath);
0207: * </pre></code>
0208: *
0209: * @param scriptPath path to search for imported scripts.
0210: */
0211: public void setScriptPath(Path scriptPath) {
0212: this .scriptPath = scriptPath;
0213: }
0214:
0215: /**
0216: * Returns the path to search for imported javascript. Normally, scriptPath
0217: * will be a MergePath.
0218: *
0219: * @param scriptPath path to search for imported scripts.
0220: */
0221: public Path getScriptPath() {
0222: return scriptPath;
0223: }
0224:
0225: /**
0226: * Adds a package/script to be automatically imported by the script.
0227: * Each import is the equivalent of adding the following javascript:
0228: *
0229: * <code><pre>
0230: * package <em>name</em>;
0231: * </pre></code>
0232: *
0233: * @param name package or script name to be automatically imported.
0234: */
0235: public void addImport(String name) {
0236: if (!importList.contains(name))
0237: importList.add(name);
0238: }
0239:
0240: /**
0241: * Sets "fast" mode, i.e. JavaScript 2.0. Fast mode lets the compiler
0242: * make assumptions about types and classes, e.g. that class methods
0243: * won't change dynamically. This lets the compiler generate more code
0244: * that directly translates to Java calls.
0245: */
0246: public void setFast(boolean isFast) {
0247: this .isFast = isFast;
0248: }
0249:
0250: /**
0251: * Sets a line number map. For generated files like JSP or XTP,
0252: * the error messages need an extra translation to get to the original
0253: * line numbers.
0254: */
0255: public void setLineMap(LineMap lineMap) {
0256: this .lineMap = lineMap;
0257: }
0258:
0259: /**
0260: * Sets the name of the generated java class. If unset, the parser will
0261: * mangle the input name.
0262: */
0263: public void setClassName(String name) {
0264: this .className = name;
0265: }
0266:
0267: /**
0268: * Sets the directory for generated *.java and *.class files.
0269: * The parser will check this directory for any precompiled javascript
0270: * classes. The default work-dir is /tmp/caucho on unix and
0271: * c:\temp\caucho on windows.
0272: *
0273: * @param path the work directory.
0274: */
0275: public void setWorkDir(Path path) {
0276: workPath = path;
0277: }
0278:
0279: /**
0280: * Returns the directory for generated *.java and *.class files.
0281: */
0282: public Path getWorkDir() {
0283: return workPath;
0284: }
0285:
0286: /**
0287: * Main application parsing method. The parser will try to load
0288: * the compiled script. If the compiled script exists and the
0289: * source file has not changed, parse will return the old script.
0290: * Otherwise, it will parse and compile the javascript.
0291: *
0292: * @param name the name of the javascript source.
0293: *
0294: * @return the parsed script
0295: */
0296: public Script parse(String name) throws ESException, IOException {
0297: Path path;
0298:
0299: try {
0300: String className;
0301:
0302: if (this .className != null)
0303: className = this .className;
0304: else
0305: className = "_js." + JavaCompiler.mangleName(name);
0306:
0307: if (scriptPath == null) {
0308: MergePath mergePath = new MergePath();
0309: mergePath.addMergePath(Vfs.lookup());
0310: ClassLoader parentLoader = this .parentLoader;
0311: if (parentLoader == null)
0312: parentLoader = Thread.currentThread()
0313: .getContextClassLoader();
0314:
0315: mergePath.addClassPath(parentLoader);
0316: scriptPath = mergePath;
0317: }
0318:
0319: Script script = loadScript(className);
0320:
0321: if (!script.isModified()) {
0322: script.setScriptPath(getScriptPath());
0323: script.setClassDir(workPath);
0324: return script;
0325: }
0326: } catch (Throwable e) {
0327: }
0328:
0329: path = getScriptPath().lookup(name);
0330:
0331: ReadStream is = path.openRead();
0332: try {
0333: return parse(is, name, 1);
0334: } finally {
0335: is.close();
0336: }
0337: }
0338:
0339: /**
0340: * Alternative parsing method when the application only has an open
0341: * stream to the file. Since this method will always compile a new script,
0342: * it can be significantly slower than the <code>parse(String)</code>
0343: * method.
0344: *
0345: * @param is a read stream to the javascript source.
0346: *
0347: * @return the parsed script.
0348: */
0349: public Script parse(ReadStream is) throws ESException, IOException {
0350: return parse(is, null, 1);
0351: }
0352:
0353: /**
0354: * An alternative parsing method given an open stream, a filename and
0355: * a line number.
0356: *
0357: * @param is a stream to the javascript source.
0358: * @param name filename to use for error messages.
0359: * @param line initial line number.
0360: *
0361: * @return the compiled script
0362: */
0363: public Script parse(ReadStream is, String name, int line)
0364: throws ESException, IOException {
0365: if (name == null)
0366: name = is.getUserPath();
0367:
0368: if (line <= 0)
0369: line = 1;
0370:
0371: return parse(is, name, line, false);
0372: }
0373:
0374: /**
0375: * Parses a script for the JavaScript "eval" expression. The
0376: * semantics for "eval" are slightly different from standard
0377: * scripts.
0378: *
0379: * @param is stream to the eval source.
0380: * @param name filename to use for error messages
0381: * @param line initial line number to use for error messages.
0382: *
0383: * @return the compiled script.
0384: */
0385: public Script parseEval(ReadStream is, String name, int line)
0386: throws ESException, IOException {
0387: if (name == null)
0388: name = "eval";
0389:
0390: if (line <= 0)
0391: line = 1;
0392:
0393: return parse(is, name, line, true);
0394: }
0395:
0396: /**
0397: * The main parser method.
0398: *
0399: * @param is stream to read the script.
0400: * @param name the filename to use for error messages.
0401: * @param line the line number to use for error messages.
0402: * @param isEval if true, parse for an eval expression.
0403: *
0404: * @return the compiled javascript
0405: */
0406: private Script parse(ReadStream is, String name, int line,
0407: boolean isEval) throws ESException, IOException {
0408: if (name == null)
0409: name = "anonymous";
0410:
0411: if (line <= 0)
0412: line = 1;
0413:
0414: lexer = new Lexer(is, name, line);
0415: if (lineMap != null)
0416: lexer.setLineMap(lineMap);
0417:
0418: if (className == null)
0419: className = "_js." + JavaCompiler.mangleName(name);
0420:
0421: if (scriptPath == null) {
0422: MergePath mergePath = new MergePath();
0423: if (is.getPath() != null)
0424: mergePath.addMergePath(is.getPath().getParent());
0425: else
0426: mergePath.addMergePath(Vfs.lookup());
0427: ClassLoader parentLoader = this .parentLoader;
0428: if (parentLoader == null)
0429: parentLoader = Thread.currentThread()
0430: .getContextClassLoader();
0431:
0432: mergePath.addClassPath(parentLoader);
0433: scriptPath = mergePath;
0434: }
0435:
0436: block = null;
0437:
0438: JavaCompiler compiler = JavaCompiler.create(getClassLoader());
0439: compiler.setClassDir(workPath);
0440:
0441: parseClass = new ParseClass(name, className);
0442: parseClass.setParser(this );
0443: if (is.getPath() != null && is.getPath().getLastModified() > 0)
0444: parseClass.setSourcePath(is.getPath());
0445:
0446: globalFunction = parseClass.newFunction(null, ESId
0447: .intern("global"), false);
0448: globalFunction.setFast(isFast);
0449: staticFunction = parseClass.newFunction(null, ESId
0450: .intern("__es_static"), false);
0451: parseClass.setGlobal(globalFunction);
0452:
0453: if (isEval) {
0454: block = Block.create(this , globalFunction);
0455: block.finish();
0456: function = parseClass.newFunction(globalFunction, ESId
0457: .intern("eval"), false);
0458: function.setEval();
0459: } else
0460: function = globalFunction;
0461:
0462: block = Block.create(this , function);
0463: parseBlock(true);
0464: block.finish();
0465:
0466: if (lexer.peek() != Lexer.EOF)
0467: throw expect(L.l("end of file"));
0468:
0469: block = Block.create(this , staticFunction);
0470: block.finish();
0471:
0472: synchronized (LOCK) {
0473: Path path = workPath.lookup(className.replace('.', '/')
0474: + ".java");
0475: path.getParent().mkdirs();
0476:
0477: WriteStream os = path.openWrite();
0478: os.setEncoding("JAVA");
0479: parseClass.writeCode(os);
0480: os.close();
0481:
0482: Script script;
0483: try {
0484: compiler.compile(className.replace('.', '/') + ".java",
0485: null);
0486: script = loadScript(className);
0487: } catch (Exception e) {
0488: throw new ESParseException(e);
0489: }
0490:
0491: script.setScriptPath(getScriptPath());
0492: script.setClassDir(workPath);
0493:
0494: return script;
0495: }
0496: }
0497:
0498: /**
0499: * Tries to load an already compiled script.
0500: *
0501: * @param className mangled classname of the script.
0502: */
0503: private Script loadScript(String className) throws Exception {
0504: ClassLoader loader = getClassLoader();
0505: Class cl = CauchoSystem.loadClass(className, false, loader);
0506:
0507: Script script = (Script) cl.newInstance();
0508: script.setScriptPath(getScriptPath());
0509: script.setClassDir(workPath);
0510:
0511: return script;
0512: }
0513:
0514: /**
0515: * Parse a function
0516: */
0517: private Function parseFunction() throws ESException {
0518: function.setNeedsScope();
0519:
0520: ESId id = null;
0521: if (lexer.peek() == Lexer.IDENTIFIER) {
0522: lexer.next();
0523: id = lexer.getId();
0524: }
0525:
0526: if (lexer.next() != '(')
0527: throw expect("`('");
0528:
0529: if (id != null) {
0530: function.addVariable(block, id, null);
0531: block.newVar(id).getVar().setType(Expr.TYPE_ES);
0532: }
0533:
0534: Block oldBlock = block;
0535: Function oldFun = function;
0536: function = parseClass.newFunction(oldFun, id, false);
0537:
0538: oldFun.addFunction(function);
0539:
0540: block = Block.create(this , function);
0541:
0542: boolean isFirst = true;
0543: while (lexer.peek() != ')') {
0544: if (!isFirst && lexer.next() != ',')
0545: throw expect("`,'");
0546: isFirst = false;
0547:
0548: if (lexer.next() != Lexer.IDENTIFIER)
0549: throw expect(L.l("formal argument"));
0550:
0551: ESId argId = lexer.getId();
0552:
0553: Expr type = null;
0554: if (lexer.peek() == ':') {
0555: lexer.next();
0556: type = parseType();
0557: }
0558:
0559: function.addFormal(block, argId, type);
0560: }
0561: lexer.next();
0562:
0563: if (lexer.peek() == ':') {
0564: lexer.next();
0565: Expr type = parseType();
0566:
0567: function.setReturnType(type);
0568: }
0569:
0570: if (lexer.next() != '{')
0571: throw expect("`{'");
0572:
0573: parseBlock(false);
0574:
0575: if (lexer.next() != '}') {
0576: throw expect("`}'");
0577: }
0578:
0579: block.finish();
0580:
0581: Function newFun = function;
0582:
0583: function = oldFun;
0584: block = oldBlock;
0585:
0586: return newFun;
0587: }
0588:
0589: /**
0590: * Parses a list of statements, returning a block representing the
0591: * statements.
0592: *
0593: * @param parent the containing block
0594: * @param isTop true if this is a top level block
0595: */
0596: private void parseBlock(boolean isTop) throws ESException {
0597: loop: while (true) {
0598: switch (lexer.peek()) {
0599: case Lexer.UNARY_OP:
0600: case Lexer.BANDU_OP:
0601: case Lexer.NEW:
0602: case Lexer.DELETE:
0603: case Lexer.VOID:
0604: case Lexer.TYPEOF:
0605: case Lexer.POSTFIX:
0606: case Lexer.IDENTIFIER:
0607: case Lexer.THIS:
0608: case Lexer.EVAL:
0609: case Lexer.LITERAL:
0610: case Lexer.REGEXP:
0611: case '(':
0612: case '[':
0613: case Lexer.HASH_REF:
0614: case Lexer.HASH_DEF:
0615: case Lexer.FUNCTION:
0616: parseStatement();
0617: break;
0618:
0619: case Lexer.IF:
0620: case Lexer.FOR:
0621: case Lexer.WHILE:
0622: case Lexer.DO:
0623: case Lexer.VAR:
0624: case Lexer.BREAK:
0625: case Lexer.WITH:
0626: case Lexer.SYNCHRONIZED:
0627: case Lexer.CONTINUE:
0628: case Lexer.RETURN:
0629: case Lexer.SWITCH:
0630: case Lexer.TRY:
0631: case Lexer.THROW:
0632: case ';':
0633: parseStatement();
0634: break;
0635:
0636: case '{':
0637: block = block.startBlock();
0638: parseStatement();
0639: block = block.finishBlock();
0640: break;
0641:
0642: case Lexer.CATCH:
0643: block.doTry();
0644: parseCatch();
0645: break;
0646:
0647: case Lexer.FINALLY:
0648: block.doTry();
0649: parseFinally();
0650: break;
0651:
0652: case Lexer.CLASS:
0653: parseClass();
0654: break;
0655:
0656: case Lexer.IMPORT:
0657: parseImport();
0658: break;
0659:
0660: case Lexer.STATIC:
0661: if (true)
0662: throw new ESException("nostatus");
0663: //parseStatic();
0664: break;
0665:
0666: default:
0667: break loop;
0668: }
0669: }
0670: }
0671:
0672: /**
0673: * Parses a statement.
0674: */
0675: private void parseStatement() throws ESException {
0676: int lexeme = lexer.peek();
0677: hashes.clear();
0678: int line = lexer.getLine();
0679: Expr expr = null;
0680:
0681: block.setLine(line);
0682:
0683: if (block.isDead) {
0684: switch (lexeme) {
0685: case ';':
0686: case Lexer.VAR:
0687: case Lexer.FUNCTION:
0688: case Lexer.CATCH:
0689: case Lexer.FINALLY:
0690: break;
0691:
0692: default:
0693: throw error(L.l("can't reach statement"));
0694: }
0695: }
0696:
0697: switch (lexeme) {
0698: case Lexer.IDENTIFIER:
0699: parseIdentifierStatement();
0700: break;
0701:
0702: case Lexer.UNARY_OP:
0703: case Lexer.BANDU_OP:
0704: case Lexer.NEW:
0705: case Lexer.THIS:
0706: case Lexer.EVAL:
0707: case Lexer.LITERAL:
0708: case Lexer.REGEXP:
0709: case Lexer.POSTFIX:
0710: case Lexer.DELETE:
0711: case Lexer.VOID:
0712: case Lexer.TYPEOF:
0713: case '(':
0714: case '[':
0715: case Lexer.HASH_REF:
0716: case Lexer.HASH_DEF:
0717: block.addExpr(parseExpression(PREC_MAX, true));
0718: parseStatementEnd();
0719: break;
0720:
0721: case Lexer.FUNCTION:
0722: lexer.next();
0723: Function newFun = parseFunction();
0724: break;
0725:
0726: case Lexer.VAR:
0727: parseVar(false);
0728: parseStatementEnd();
0729: break;
0730:
0731: case Lexer.BREAK:
0732: lexer.next();
0733: if (lexer.peek() == Lexer.IDENTIFIER
0734: && !lexer.seenLineFeed()) {
0735: block.doBreak(lexer.getId());
0736: lexer.next();
0737: } else
0738: block.doBreak();
0739: parseStatementEnd();
0740: break;
0741:
0742: case Lexer.CONTINUE:
0743: lexer.next();
0744: if (lexer.peek() == Lexer.IDENTIFIER
0745: && !lexer.seenLineFeed()) {
0746: block.doContinue(lexer.getId());
0747: lexer.next();
0748: } else
0749: block.doContinue();
0750:
0751: parseStatementEnd();
0752: break;
0753:
0754: case Lexer.RETURN:
0755: lexer.next();
0756:
0757: if (lexer.peek() == ';' || lexer.peek() == '}'
0758: || lexer.seenLineFeed()) {
0759: block.doReturn();
0760: } else {
0761: block.doReturn(parseExpression(PREC_MAX, true));
0762: }
0763:
0764: parseStatementEnd();
0765: break;
0766:
0767: case Lexer.IF:
0768: parseIf();
0769: break;
0770:
0771: case Lexer.SWITCH:
0772: parseSwitch();
0773: break;
0774:
0775: case Lexer.WHILE:
0776: parseWhile(null);
0777: break;
0778:
0779: case Lexer.DO:
0780: parseDo(null);
0781: break;
0782:
0783: case Lexer.FOR:
0784: parseFor(null);
0785: break;
0786:
0787: case Lexer.WITH:
0788: parseWith();
0789: break;
0790:
0791: case Lexer.SYNCHRONIZED:
0792: parseSynchronized();
0793: break;
0794:
0795: case Lexer.TRY:
0796: parseTry();
0797: break;
0798:
0799: case Lexer.THROW:
0800: lexer.next();
0801: block.doThrow(parseExpression(PREC_MAX));
0802: break;
0803:
0804: case ';':
0805: lexer.next();
0806: break;
0807:
0808: case '{':
0809: lexer.next();
0810: parseBlock(false);
0811: if (lexer.next() != '}')
0812: throw expect("`}'");
0813: break;
0814:
0815: default:
0816: throw expect(L.l("statement"));
0817: }
0818: }
0819:
0820: private void parseStatementEnd() throws ESException {
0821: if (lexer.peek() == ';')
0822: lexer.next();
0823: else if (lexer.peek() == '}' || lexer.peek() == Lexer.EOF
0824: || lexer.seenLineFeed()) {
0825: } else {
0826: throw expect("`;'");
0827: }
0828: }
0829:
0830: private void parseIdentifierStatement() throws ESException {
0831: ESId id = lexer.getId();
0832: int line = lexer.getLine();
0833:
0834: lexer.next();
0835: if (lexer.peek() != ':') {
0836: Expr var = getVar(id);
0837: Expr expr = parseExprRec(parseTermTail(var, false, true),
0838: PREC_MAX, false, true);
0839: block.addExpr(expr);
0840: parseStatementEnd();
0841: return;
0842: }
0843:
0844: lexer.next();
0845:
0846: /*
0847: if (findLoop(null, id) != null)
0848: throw error("duplicate label `" + id + "'");
0849: */
0850:
0851: switch (lexer.peek()) {
0852: case Lexer.WHILE:
0853: parseWhile(id);
0854: break;
0855:
0856: case Lexer.DO:
0857: parseDo(id);
0858: break;
0859:
0860: case Lexer.FOR:
0861: parseFor(id);
0862: break;
0863:
0864: default:
0865: block = block.startBlock(id);
0866: parseStatement();
0867: block = block.finishBlock();
0868: break;
0869: }
0870: }
0871:
0872: private Expr parseType() throws ESException {
0873: if (lexer.next() != Lexer.IDENTIFIER)
0874: throw expect(L.l("identifier"));
0875:
0876: Expr type = block.newType(lexer.getId());
0877:
0878: while (lexer.peek() == '.') {
0879: lexer.next();
0880: if (lexer.next() != Lexer.IDENTIFIER)
0881: throw expect(L.l("identifier"));
0882:
0883: type = type.fieldReference(lexer.getId());
0884: }
0885:
0886: return type;
0887: }
0888:
0889: /**
0890: * if ::= IF '(' expr ')' stmt
0891: */
0892: private void parseIf() throws ESException {
0893: boolean isFirst = true;
0894: boolean isDead = true;
0895:
0896: block = block.create();
0897:
0898: while (lexer.peek() == Lexer.IF) {
0899: lexer.next();
0900:
0901: if (lexer.next() != '(')
0902: throw expect("`('");
0903:
0904: block.startIf(parseBooleanExpression(PREC_MAX), !isFirst);
0905: isFirst = false;
0906:
0907: if (lexer.next() != ')')
0908: throw expect("`)'");
0909:
0910: parseStatement();
0911:
0912: block.endBlock();
0913: if (!block.isDead)
0914: isDead = false;
0915: block.isDead = false;
0916:
0917: if (lexer.peek() != Lexer.ELSE) {
0918: block = block.pop();
0919: return;
0920: }
0921:
0922: lexer.next();
0923: }
0924:
0925: block.startElse();
0926: parseStatement();
0927: block.endBlock();
0928: if (!block.isDead)
0929: isDead = false;
0930: block = block.pop();
0931: block.isDead = isDead;
0932: }
0933:
0934: /**
0935: * switch ::= SWITCH '(' expr ')' '{' ((CASE expr:|DEFAULT)+ stmt*)* '}'
0936: */
0937: private void parseSwitch() throws ESException {
0938: lexer.next();
0939: if (lexer.next() != '(')
0940: throw expect("`)'");
0941:
0942: Expr test = parseExpression(PREC_MAX);
0943:
0944: if (lexer.next() != ')')
0945: throw expect("`)'");
0946:
0947: if (lexer.next() != '{')
0948: throw expect("`{'");
0949:
0950: ArrayList exprs = new ArrayList();
0951:
0952: block = block.startSwitch(test);
0953:
0954: int ch;
0955: while ((ch = lexer.peek()) != -1 && ch != '}') {
0956: switch (ch) {
0957: case Lexer.CASE:
0958: lexer.next();
0959: block.doCase(exprs.size());
0960: exprs.add(parseExpression(PREC_MAX));
0961:
0962: if (lexer.next() != ':')
0963: throw expect("`:'");
0964: break;
0965:
0966: case Lexer.DEFAULT:
0967: lexer.next();
0968: if (lexer.next() != ':')
0969: throw expect("`:'");
0970:
0971: block.doDefault();
0972: break;
0973:
0974: default:
0975: parseStatement();
0976: }
0977: }
0978:
0979: if (lexer.next() != '}')
0980: throw expect("`}'");
0981:
0982: block = block.fillSwitch(exprs);
0983: }
0984:
0985: /**
0986: * while ::= WHILE '(' expr ')' stmt
0987: */
0988: private void parseWhile(ESId id) throws ESException {
0989: lexer.next();
0990: if (lexer.next() != '(')
0991: throw expect("`('");
0992:
0993: Expr expr = parseBooleanExpression(PREC_MAX);
0994: if (expr instanceof LiteralExpr
0995: && !((LiteralExpr) expr).getLiteral().toBoolean())
0996: throw error(L.l("while (false) is never executed."));
0997: block = block.startWhile(id, expr);
0998:
0999: if (lexer.next() != ')')
1000: throw expect("`)'");
1001:
1002: parseStatement();
1003: block = block.endLoop();
1004: }
1005:
1006: /**
1007: * for ::= FOR '(' expr ')' stmt
1008: */
1009: private void parseFor(ESId id) throws ESException {
1010: lexer.next();
1011: if (lexer.next() != '(')
1012: throw expect("`('");
1013:
1014: boolean hasValue = false;
1015: Expr lhs = null;
1016: if (lexer.peek() == Lexer.VAR) {
1017: lhs = parseVar(true);
1018: } else if (lexer.peek() != ';') {
1019: lhs = parseExpression(PREC_MAX);
1020: } else if (lexer.peek() == Lexer.IN)
1021: throw expect(L.l("expression"));
1022:
1023: if (lexer.peek() == Lexer.IN) {
1024: parseForIn(id, lhs);
1025: return;
1026: }
1027:
1028: if (lhs != null)
1029: lhs.exprStatement(block.function);
1030:
1031: if (lexer.next() != ';')
1032: throw expect("`;'");
1033:
1034: Expr test = null;
1035: if (lexer.peek() != ';')
1036: test = parseExpression(PREC_MAX);
1037:
1038: if (lexer.next() != ';')
1039: throw expect("`;'");
1040:
1041: Expr incr = null;
1042: if (lexer.peek() != ')') {
1043: incr = parseExpression(PREC_MAX);
1044: incr.killValue();
1045: }
1046:
1047: if (lexer.next() != ')')
1048: throw expect("`)'");
1049:
1050: if (test instanceof LiteralExpr
1051: && !((LiteralExpr) test).getLiteral().toBoolean())
1052: throw error(L.l("for (;false;) is never executed."));
1053: block = block.startFor(id, test, incr);
1054: parseStatement();
1055: block = block.endLoop();
1056: }
1057:
1058: private void parseForIn(ESId id, Expr lhs) throws ESException {
1059: lexer.next();
1060:
1061: String var = block.newIterator(id, parseExpression(PREC_MAX));
1062:
1063: if (lexer.next() != ')')
1064: throw expect("`)'");
1065:
1066: block = block.startWhile(id, block.hasNext(var));
1067:
1068: block.addExpr(lhs.next(var, lhs));
1069:
1070: parseStatement();
1071: block = block.endLoop();
1072: }
1073:
1074: /**
1075: * do ::= DO stmt WHILE '(' expr ')'
1076: */
1077: private void parseDo(ESId id) throws ESException {
1078: lexer.next();
1079:
1080: block = block.startDo(id);
1081: parseStatement();
1082:
1083: if (lexer.next() != Lexer.WHILE)
1084: throw expect("`while'");
1085:
1086: if (lexer.next() != '(')
1087: throw expect("`('");
1088:
1089: block = block.endDo(parseBooleanExpression(PREC_MAX));
1090:
1091: if (lexer.next() != ')')
1092: throw expect("`)'");
1093:
1094: parseStatementEnd();
1095: }
1096:
1097: /**
1098: * with ::= WITH '(' expr ')' stmt
1099: */
1100: private void parseWith() throws ESException {
1101: lexer.next();
1102: if (lexer.next() != '(')
1103: throw expect("`('");
1104:
1105: block = block.startWith(parseExpression(PREC_MAX));
1106:
1107: if (lexer.next() != ')')
1108: throw expect("`)'");
1109:
1110: parseStatement();
1111:
1112: block = block.endWith();
1113: }
1114:
1115: /**
1116: * var ::= VAR id (= expr)? (, id (= expr)?)*
1117: */
1118: private Expr parseVar(boolean keepValue) throws ESException {
1119: boolean isFirst = true;
1120: Expr retVar = null;
1121: do {
1122: lexer.next();
1123:
1124: if (lexer.next() != Lexer.IDENTIFIER)
1125: throw expect(L.l("identifier"));
1126:
1127: ESId name = lexer.getId();
1128:
1129: Expr type = null;
1130:
1131: if (lexer.peek() == ':') {
1132: lexer.next();
1133:
1134: type = parseType();
1135: }
1136:
1137: block.defVar(name, type);
1138:
1139: if (lexer.peek() == '=') {
1140: lexer.next();
1141:
1142: Expr var = block.newVar(name, type);
1143: Expr value = parseExpression(Parser.PREC_ASSIGN + 1,
1144: true);
1145: block.evalExpr();
1146: var.assign(value).exprStatement(block.function);
1147: } else if (keepValue)
1148: retVar = block.newVar(name, type);
1149:
1150: isFirst = false;
1151: } while (lexer.peek() == ',');
1152:
1153: return retVar;
1154: }
1155:
1156: /**
1157: * synchronized ::= SYNCHRONIZED '(' expr ')' stmt
1158: */
1159: private void parseSynchronized() throws ESException {
1160: lexer.next();
1161: if (lexer.next() != '(')
1162: throw expect("`('");
1163:
1164: block = block.startSynchronized(parseExpression(PREC_MAX));
1165:
1166: if (lexer.next() != ')')
1167: throw expect("`)'");
1168:
1169: parseStatement();
1170:
1171: block = block.endSynchronized();
1172: }
1173:
1174: /**
1175: * try ::= TRY stmt (CATCH | FINALLY)
1176: */
1177: private void parseTry() throws ESException {
1178: lexer.next();
1179:
1180: block = block.startTry();
1181: parseStatement();
1182: block = block.endTry();
1183:
1184: if (lexer.peek() == Lexer.CATCH) {
1185: parseCatch();
1186: } else if (lexer.peek() == Lexer.FINALLY)
1187: parseFinally();
1188: else
1189: throw error(L.l("expected `catch' or `finally' at {0}",
1190: getToken()));
1191: }
1192:
1193: /**
1194: * catch ::= CATCH '(' (expr lhs?)? ')' stmt
1195: */
1196: private void parseCatch() throws ESException {
1197: block.function.disallowJavaLocal();
1198: // XXX: don't forget catch w/o try
1199:
1200: boolean oldDead = block.isDead;
1201: boolean hasCatchall = false;
1202:
1203: while (lexer.peek() == Lexer.CATCH) {
1204: block.isDead = false;
1205:
1206: if (hasCatchall)
1207: throw error(L.l("catch () must be last catch clause"));
1208:
1209: lexer.next();
1210: if (lexer.next() != '(')
1211: throw expect("`('");
1212:
1213: String exceptionClass = "";
1214: while (lexer.peek() == Lexer.IDENTIFIER) {
1215: lexer.next();
1216: exceptionClass = exceptionClass + lexer.getText();
1217:
1218: if (lexer.peek() != '.')
1219: break;
1220:
1221: lexer.next();
1222: exceptionClass = exceptionClass + ".";
1223: if (lexer.peek() != Lexer.IDENTIFIER)
1224: throw expect(L.l("identifier"));
1225: }
1226:
1227: ESId name = null;
1228: if (lexer.peek() == Lexer.IDENTIFIER) {
1229: name = lexer.getId();
1230: lexer.next();
1231: }
1232:
1233: if (lexer.next() != ')') {
1234: if (exceptionClass.equals(""))
1235: throw expect(L.l("identifier"));
1236: else
1237: throw expect("`)'");
1238: }
1239:
1240: if (name == null) {
1241: name = ESId.intern(exceptionClass);
1242: exceptionClass = "java.lang.Exception";
1243: }
1244:
1245: Expr var = null;
1246: if (name != null)
1247: var = block.newVar(name);
1248:
1249: block = block.startCatch(exceptionClass, var);
1250: parseStatement();
1251: if (!block.isDead)
1252: oldDead = false;
1253: block = block.endCatch();
1254: }
1255:
1256: block.isDead = oldDead;
1257: // Don't forget to throw
1258: if (lexer.peek() == Lexer.FINALLY)
1259: parseFinally();
1260: }
1261:
1262: /**
1263: * finally ::= FINALLY stmt
1264: */
1265: private void parseFinally() throws ESException {
1266: boolean oldDead = block.isDead;
1267: block.isDead = false;
1268: lexer.next();
1269:
1270: block = block.startFinally();
1271:
1272: parseStatement();
1273:
1274: block = block.endFinally();
1275: block.isDead = oldDead;
1276: }
1277:
1278: static ESId BOGUS = ESId.intern("return ");
1279:
1280: /**
1281: * Parse a class
1282: */
1283: private void parseClass() throws ESException {
1284: if (function.getParent() != null)
1285: throw error(L.l("`class' only allowed at top level"));
1286:
1287: lexer.next();
1288:
1289: if (lexer.next() != Lexer.IDENTIFIER)
1290: throw expect("class name");
1291:
1292: ESId id = lexer.getId();
1293:
1294: ESId proto = parseExtends();
1295:
1296: if (lexer.next() != '{')
1297: throw expect("`{'");
1298:
1299: ParseClass oldClass = parseClass;
1300: Function oldGlobal = globalFunction;
1301: Function oldStatic = staticFunction;
1302: Function oldFunction = function;
1303: Block oldBlock = block;
1304:
1305: parseClass = oldClass.newClass(id);
1306: parseClass.setProto(proto);
1307:
1308: globalFunction = parseClass.newFunction(null, ESId
1309: .intern("global"), true);
1310: staticFunction = parseClass.newFunction(null, ESId
1311: .intern("__es_static"), true);
1312: parseClass.setGlobal(globalFunction);
1313:
1314: function = globalFunction;
1315: block = Block.create(this , function);
1316:
1317: parseBlock(true);
1318: block.finish();
1319:
1320: block = Block.create(this , staticFunction);
1321: block.finish();
1322:
1323: if (parseClass.getFunction(id) == null) {
1324: function = parseClass.newFunction(null, id, false);
1325: block = Block.create(this , function);
1326: block.finish();
1327: }
1328:
1329: block = oldBlock;
1330: function = oldFunction;
1331: globalFunction = oldGlobal;
1332: staticFunction = oldStatic;
1333: parseClass = oldClass;
1334:
1335: if (lexer.next() != '}')
1336: throw expect("`}'");
1337: }
1338:
1339: private ESId parseExtends() throws ESException {
1340: if (lexer.peek() != Lexer.EXTENDS)
1341: return null;
1342:
1343: lexer.next();
1344: if (lexer.next() != Lexer.IDENTIFIER)
1345: throw expect(L.l("parent class name"));
1346:
1347: return lexer.getId();
1348: }
1349:
1350: private void parseImport() throws ESException {
1351: CharBuffer path = new CharBuffer();
1352:
1353: lexer.next();
1354:
1355: while (true) {
1356: if (lexer.peek() == Lexer.BIN_OP && lexer.getOp() == '*') {
1357: lexer.next();
1358: path.append('*');
1359: importList.add(path.close());
1360: return;
1361: }
1362:
1363: if (lexer.peek() != Lexer.IDENTIFIER)
1364: throw expect(L.l("identifier"));
1365:
1366: path.append(lexer.getText());
1367:
1368: lexer.next();
1369:
1370: if (lexer.peek() != '.')
1371: break;
1372:
1373: lexer.next();
1374: path.append('.');
1375: }
1376:
1377: String className = path.close();
1378: String pathName = className.replace('.', '/') + ".js";
1379:
1380: Path importPath = getScriptPath().lookup(pathName);
1381:
1382: if (importPath.exists()) {
1383: function.cl.addImport(pathName);
1384: return;
1385: }
1386:
1387: try {
1388: CauchoSystem.loadClass(className, false, getClassLoader());
1389:
1390: importList.add(className);
1391: } catch (ClassNotFoundException e) {
1392: throw error(L.l("can't open import `{0}'", pathName));
1393: }
1394: }
1395:
1396: /*
1397: private void parseStatic() throws ESException
1398: {
1399: if (function.rest != null || staticCode == null)
1400: throw error("illegal static statement");
1401:
1402: lexer.next();
1403:
1404: int oldVar = stmtVar;
1405: ParseFun oldFun = function;
1406: try {
1407: stmtVar = -1;
1408: function = staticFunction;
1409: parseStatement(staticCode);
1410: } finally {
1411: function = oldFun;
1412: stmtVar = oldVar;
1413: }
1414: } */
1415:
1416: private Expr parseExpression(int prevPrec, boolean isTop)
1417: throws ESException {
1418: Expr result = parseExprRec(parseTerm(isTop), prevPrec, false,
1419: isTop);
1420: result.getType();
1421: return result;
1422: }
1423:
1424: private Expr parseBooleanExpression(int prevPrec)
1425: throws ESException {
1426: Expr result = parseExprRec(parseTerm(false), prevPrec, true,
1427: false);
1428: result.getType();
1429: return result;
1430: }
1431:
1432: private Expr parseExpression(int prevPrec) throws ESException {
1433: Expr result = parseExprRec(parseTerm(false), prevPrec, false,
1434: false);
1435: result.getType();
1436: return result;
1437: }
1438:
1439: private Expr parseExpression() throws ESException {
1440: Expr result = parseExprRec(parseTerm(false), PREC_MAX, false,
1441: false);
1442: result.getType();
1443: return result;
1444: }
1445:
1446: /*
1447: * parseExpression ()
1448: *
1449: * Grammar:
1450: * expr ::= obj (op obj)*
1451: */
1452: private Expr parseExprRec(Expr lexpr, int prevPrec, boolean isBool,
1453: boolean isTop) throws ESException {
1454: Expr rexpr = null;
1455: int op = 0;
1456: int lex = 0;
1457: int prec = 0;
1458:
1459: while (true) {
1460: boolean doLookahead = false;
1461: boolean doPostfix = false;
1462: boolean isRightAssoc = false;
1463: int nextPrec = 0;
1464: int nextOp = 0;
1465: int nextLex = 0;
1466:
1467: switch (lexer.peek()) {
1468: case '=':
1469: if (op != 0 && lex != ',')
1470: throw error(L
1471: .l("illegal left hand side of assignment"));
1472: if (isBool)
1473: throw error(L
1474: .l("assignment used as boolean needs parentheses"));
1475:
1476: case Lexer.BIN_OP:
1477: // careful with and/or for unassigned local variables
1478: if (lexer.getOp() == Lexer.AND
1479: || lexer.getOp() == Lexer.OR)
1480: function.setVars();
1481: // fall through
1482: case ',':
1483: case Lexer.BANDU_OP:
1484: case '?':
1485: nextLex = lexer.peek();
1486: nextOp = lexer.getOp();
1487: nextPrec = lexer.getPrecedence();
1488: isRightAssoc = lexer.isRightAssoc();
1489: doLookahead = true;
1490: break;
1491:
1492: default:
1493: return op != 0 ? lexpr.binaryOp(lex, op, rexpr) : lexpr;
1494: }
1495:
1496: if (nextPrec >= prevPrec) {
1497: return op != 0 ? lexpr.binaryOp(lex, op, rexpr) : lexpr;
1498: } else if (prec == 0) {
1499: } else if (nextPrec < prec) {
1500: rexpr = parseExprRec(rexpr, prec, isBool, isTop);
1501: continue;
1502: } else {
1503: lexpr = op != 0 ? lexpr.binaryOp(lex, op, rexpr)
1504: : lexpr;
1505: }
1506:
1507: prec = nextPrec;
1508: lex = nextLex;
1509: op = nextOp;
1510:
1511: if (doLookahead)
1512: lexer.next();
1513:
1514: if (isRightAssoc) {
1515: /* XXX: is this the right thing to do? + 1 */
1516: rexpr = parseExpression(prec + 1, isTop);
1517: } else if (op == '?') {
1518: function.setVars();
1519: Expr mexpr = parseExpression(Parser.PREC_ASSIGN + 1);
1520: if (lexer.peek() != ':')
1521: throw expect("`:'");
1522: lexer.next();
1523: rexpr = parseExpression(Parser.PREC_ASSIGN + 1, isTop);
1524:
1525: lexpr = lexpr.conditional(mexpr, rexpr);
1526: op = 0;
1527: } else
1528: rexpr = parseTerm(isTop);
1529: }
1530: }
1531:
1532: /*
1533: * parseTerm
1534: *
1535: * term ::= BANDU_OP term
1536: * ::= UNARY_OP term
1537: * ::= IDENTIFIER termTail
1538: * ::= LITERAL termTail
1539: * ::= objLiteral termTail
1540: * ::= '(' expr ')' termTail
1541: */
1542: private Expr parseTerm(boolean isTop) throws ESException {
1543: int op;
1544:
1545: switch (lexer.peek()) {
1546: case Lexer.BANDU_OP:
1547: case Lexer.UNARY_OP:
1548: lexer.next();
1549: op = lexer.getOp();
1550: return parseTerm(isTop).unaryOp(op);
1551:
1552: case Lexer.VOID:
1553: lexer.next();
1554: return parseTerm(isTop).doVoid();
1555:
1556: case Lexer.TYPEOF:
1557: lexer.next();
1558: return parseTerm(isTop).typeof();
1559:
1560: case Lexer.DELETE:
1561: lexer.next();
1562: return parseTerm(isTop).delete();
1563:
1564: case Lexer.POSTFIX:
1565: lexer.next();
1566: op = lexer.getOp();
1567: return parseTerm(isTop).prefix(op);
1568:
1569: case Lexer.LITERAL:
1570: case Lexer.REGEXP:
1571: case Lexer.IDENTIFIER:
1572: case Lexer.THIS:
1573: case Lexer.EVAL:
1574: case Lexer.NEW:
1575: case '(':
1576: case '{':
1577: case '[':
1578: case Lexer.HASH_REF:
1579: case Lexer.HASH_DEF:
1580: case Lexer.FUNCTION:
1581: return parseLhs(false, isTop);
1582:
1583: default:
1584: throw expect(L.l("expression"));
1585: }
1586: }
1587:
1588: /**
1589: * Parses the left-hand side of an expression
1590: *
1591: * @param hasNew true if this follows a new
1592: * @return the new expression
1593: */
1594: private Expr parseLhs(boolean hasNew, boolean isTop)
1595: throws ESException {
1596: String name;
1597: int op;
1598: Expr expr = null;
1599:
1600: switch (lexer.next()) {
1601: case Lexer.NEW:
1602: return parseTermTail(parseLhs(true, isTop), hasNew, isTop);
1603:
1604: case Lexer.LITERAL:
1605: return parseTermTail(block.newLiteral(lexer.getLiteral()),
1606: hasNew, isTop);
1607:
1608: case Lexer.REGEXP:
1609: /*
1610: return parseTermTail(block.newRegexp(lexer.getLiteral(),
1611: lexer.getFlags()),
1612: hasNew, isTop);
1613: */
1614: throw new UnsupportedOperationException();
1615:
1616: case Lexer.IDENTIFIER:
1617: return parseTermTail(getVar(lexer.getId()), hasNew, isTop);
1618:
1619: case Lexer.THIS:
1620: return parseTermTail(block.newThis(), hasNew, isTop);
1621:
1622: case Lexer.EVAL:
1623: if (lexer.peek() != '(')
1624: throw expect("`('");
1625: function.setArguments();
1626: return parseTermTail(block.newVar(ESId.intern("eval")),
1627: hasNew, false);
1628:
1629: case '(':
1630: expr = parseExpression(PREC_MAX);
1631: if (lexer.next() != ')')
1632: throw expect("`)'");
1633: return parseTermTail(expr, hasNew, isTop);
1634:
1635: case '{':
1636: expr = parseObjectLiteral(-1);
1637: if (lexer.next() != '}')
1638: throw expect("`}'");
1639: return parseTermTail(expr, hasNew, isTop);
1640:
1641: case '[':
1642: expr = parseArrayLiteral(-1);
1643: if (lexer.next() != ']')
1644: throw expect("`]'");
1645: return parseTermTail(expr, hasNew, isTop);
1646:
1647: case Lexer.FUNCTION:
1648: Function newFun = parseFunction();
1649:
1650: //function.addFunction(newFun);
1651: function.addVariable(block, newFun.id, null);
1652: block.newVar(newFun.id).getVar().setType(Expr.TYPE_ES);
1653: expr = block.newVar(newFun.id);
1654: return parseTermTail(expr, hasNew, isTop);
1655:
1656: case Lexer.HASH_DEF:
1657: switch (lexer.peek()) {
1658: case '{':
1659: lexer.next();
1660: expr = parseObjectLiteral(lexer.intValue);
1661: if (lexer.next() != '}')
1662: throw expect("`}'");
1663: return parseTermTail(expr, hasNew, isTop);
1664:
1665: case '[':
1666: lexer.next();
1667: expr = parseArrayLiteral(lexer.intValue);
1668: if (lexer.next() != ']')
1669: throw expect("`]'");
1670: return parseTermTail(expr, hasNew, isTop);
1671:
1672: default:
1673: /* XXX:
1674: expr = parseLhs(hasNew, isTop);
1675: int var = code.newVar();
1676: code.store(var);
1677: hashes.add(lexer.intValue, var);
1678: code.load(var);
1679: */
1680: return expr;
1681: }
1682: /*
1683: case Lexer.HASH_REF:
1684: if (hashes.size() <= lexer.intValue ||
1685: hashes.get(lexer.intValue) <= 0)
1686: throw error("bad sharp reference at " + getToken());
1687:
1688: return parseTermTail(code.load(hashes.get(lexer.intValue)),
1689: hasNew, isTop);
1690: */
1691: default:
1692: throw expect(L.l("term"));
1693: }
1694: }
1695:
1696: /*
1697: * parseTermTail
1698: *
1699: * termTail ::=
1700: * ::= '.' Id termTail
1701: * ::= '(' exprList ')' termTail
1702: * ::= '[' expr ']' termTail
1703: * ::= '++' termTail
1704: */
1705: private Expr parseTermTail(Expr term, boolean hasNew, boolean isTop)
1706: throws ESException {
1707: int op;
1708:
1709: while (true) {
1710: switch (lexer.peek()) {
1711: case '.':
1712: lexer.next();
1713: if (lexer.next() != Lexer.IDENTIFIER)
1714: throw expect(L.l("property name"));
1715:
1716: term = term.fieldReference(lexer.getId());
1717: break;
1718:
1719: case '(':
1720: if (isTop && lexer.seenLineFeed())
1721: return term;
1722:
1723: lexer.next();
1724:
1725: int n = 0;
1726: CallExpr call;
1727: if (hasNew)
1728: call = term.startNew();
1729: else
1730: call = term.startCall();
1731:
1732: while (lexer.peek() != ')') {
1733: if (n != 0 && lexer.peek() != ',')
1734: throw expect("`,'");
1735: else if (n != 0)
1736: lexer.next();
1737:
1738: call.addCallParam(parseExpression(PREC_COMMA));
1739: n++;
1740: }
1741: lexer.next();
1742:
1743: if (hasNew)
1744: return call;
1745: else
1746: term = call;
1747: break;
1748:
1749: case '[':
1750: if (isTop && lexer.seenLineFeed())
1751: return term;
1752:
1753: lexer.next();
1754: term = term.fieldReference(parseExpression(PREC_MAX));
1755:
1756: if (lexer.next() != ']')
1757: throw expect("`]'");
1758: break;
1759:
1760: case Lexer.POSTFIX:
1761: if (hasNew)
1762: return term.startNew();
1763:
1764: if (lexer.seenLineFeed())
1765: return term;
1766:
1767: term = term.postfix(lexer.getOp());
1768:
1769: lexer.next();
1770: break;
1771:
1772: case '@':
1773: lexer.next();
1774:
1775: term = term.cast(parseType());
1776: break;
1777:
1778: default:
1779: if (hasNew)
1780: return term.startNew();
1781: else
1782: return term;
1783: }
1784: }
1785: }
1786:
1787: private Expr parseObjectLiteral(int hash) throws ESException {
1788: Expr expr = block.newVar(ESId.intern("Object"));
1789: CallExpr call = expr.startCall();
1790:
1791: /*
1792: if (hash >= 0) {
1793: if (hashes.size() <= hash)
1794: hashes.setLength(hash + 1);
1795: hashes.add(hash, var);
1796: }
1797: */
1798:
1799: if (lexer.peek() == ',') {
1800: lexer.next();
1801:
1802: return call;
1803: }
1804:
1805: while (lexer.peek() == Lexer.LITERAL
1806: || lexer.peek() == Lexer.IDENTIFIER) {
1807: ESId id;
1808:
1809: if (lexer.next() == Lexer.LITERAL)
1810: id = ESId.intern(lexer.literal.toString());
1811: else
1812: id = lexer.getId();
1813:
1814: if (lexer.next() != ':')
1815: throw expect("`:'");
1816:
1817: call.addCallParam(block.newLiteral(id));
1818: call.addCallParam(parseExpression(PREC_COMMA));
1819:
1820: if (lexer.peek() != ',')
1821: break;
1822: lexer.next();
1823: }
1824:
1825: return call;
1826: }
1827:
1828: private Expr parseArrayLiteral(int hash) throws ESException {
1829: Expr expr = block.newVar(ESId.intern("Array"));
1830:
1831: CallExpr call = expr.startCall();
1832:
1833: boolean isFirst = true;
1834: while (lexer.peek() != ']') {
1835: if (lexer.peek() == ',') {
1836: lexer.next();
1837: call.addCallParam(block.newLiteral(ESBase.esUndefined));
1838: isFirst = false;
1839: continue;
1840: }
1841:
1842: Expr value = parseExpression(PREC_COMMA);
1843:
1844: if (isFirst && lexer.peek() == ']')
1845: return block.newArray(value);
1846:
1847: if (lexer.peek() != ',') {
1848: call.addCallParam(value);
1849: break;
1850: }
1851:
1852: lexer.next();
1853:
1854: if (isFirst && lexer.peek() == ']')
1855: return block.newArray(value);
1856:
1857: isFirst = false;
1858: call.addCallParam(value);
1859: }
1860:
1861: return call;
1862: }
1863:
1864: /**
1865: * Gets a variable instance.
1866: */
1867: private Expr getVar(ESId name) throws ESException {
1868: if (name == PACKAGES)
1869: return new PackageExpr(block);
1870: else if (name == JAVA)
1871: return new PackageExpr(block).fieldReference(JAVA);
1872: else if (name == CAUCHO)
1873: return new PackageExpr(block).fieldReference(COM)
1874: .fieldReference(CAUCHO);
1875: else if (block.hasVar(name))
1876: return block.newVar(name);
1877: else {
1878: for (int i = 0; i < importList.size(); i++) {
1879: String className = (String) importList.get(i);
1880:
1881: if (className.endsWith(".*"))
1882: className = className.substring(0, className
1883: .length() - 1)
1884: + name;
1885:
1886: try {
1887: Class cl = CauchoSystem.loadClass(className, false,
1888: getClassLoader());
1889:
1890: return new JavaClassExpr(block, cl);
1891: } catch (Throwable e) {
1892: }
1893: }
1894:
1895: return block.newVar(name);
1896: }
1897: }
1898:
1899: /**
1900: * Returns the current filename being parsed.
1901: */
1902: public String getFilename() {
1903: return lexer.getFilename();
1904: }
1905:
1906: /**
1907: * Creates an error message with the given text.
1908: */
1909: ESException error(String text) {
1910: return lexer.error(text);
1911: }
1912:
1913: /**
1914: * Returns the current token for an error message.
1915: */
1916: private String getToken() {
1917: if (lexer.isEof())
1918: return "end of file";
1919: else
1920: return "`" + lexer.getToken() + "'";
1921: }
1922:
1923: /**
1924: * Returns a parse exception when expecting a result.
1925: */
1926: private ESException expect(String expect) {
1927: try {
1928: return lexer.error(L.l("expected {0} at {1}", expect,
1929: getToken()));
1930: } catch (Exception e) {
1931: e.printStackTrace();
1932: return null;
1933: }
1934: }
1935: }
|