0001: /*
0002: * @(#)Pnuts.java 1.11 05/05/26
0003: *
0004: * Copyright (c) 1997-2005 Sun Microsystems, Inc. All Rights Reserved.
0005: *
0006: * See the file "LICENSE.txt" for information on usage and redistribution
0007: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
0008: */
0009: package pnuts.lang;
0010:
0011: import java.io.BufferedReader;
0012: import java.io.FileNotFoundException;
0013: import java.io.IOException;
0014: import java.io.InputStream;
0015: import java.io.InputStreamReader;
0016: import java.io.PrintWriter;
0017: import java.io.Reader;
0018: import java.io.Serializable;
0019: import java.io.StringReader;
0020: import java.io.Writer;
0021: import java.net.URL;
0022: import java.util.Properties;
0023:
0024: import org.pnuts.util.Stack;
0025: import org.pnuts.lang.DefaultParseEnv;
0026: import org.pnuts.lang.PnutsClassLoader;
0027:
0028: /**
0029: * This class provides a set of static methods to parse/execute scripts.
0030: * <p>
0031: * This object also represents a parsed script. </p>
0032: * <p>
0033: * This class is serializable. When a Pnuts object is serialized, the syntax
0034: * tree is written to the object stream, along with the attributes such as
0035: * line, column, and script source.</p>
0036: * <p>
0037: * When the object is deserialized, the parsed script is restored using
0038: * the information read from the object stream. If the script had been
0039: * compiled, the script should be recompiled.</p>
0040: * <p>
0041: * Serialized objects of this class will not be compatible with
0042: * future releases. The current serialization support is
0043: * appropriate for short term storage or RMI between applications running
0044: * the same version of Swing. </p>
0045: */
0046: public class Pnuts implements Executable, Serializable {
0047:
0048: // final static boolean DEBUG = true;
0049:
0050: static final long serialVersionUID = 3808410089278038986L;
0051:
0052: /**
0053: * The version number
0054: */
0055: public final static String pnuts_version = "1.2.1";
0056:
0057: /**
0058: * "prompt" string for the command shell
0059: */
0060: public static String prompt = "> ";
0061:
0062: static String compiledClassPrefix;
0063:
0064: static Properties defaultSettings;
0065:
0066: private static Stack freeParsers = new Stack();
0067:
0068: private static int java2 = 0;
0069:
0070: static String getCompiledClassPrefix() {
0071: if (compiledClassPrefix == null) {
0072: try {
0073: compiledClassPrefix = Runtime
0074: .getProperty("pnuts.compiled.script.prefix");
0075: if (compiledClassPrefix == null) {
0076: compiledClassPrefix = "";
0077: } else if (!"".equals(compiledClassPrefix)
0078: && !compiledClassPrefix.endsWith(".")) {
0079: compiledClassPrefix = compiledClassPrefix + ".";
0080: }
0081: } catch (Throwable t) {
0082: }
0083: }
0084: return compiledClassPrefix;
0085: }
0086:
0087: static PnutsParser getParser(Reader reader) {
0088: synchronized (freeParsers) {
0089: if (freeParsers.size() > 0) {
0090: PnutsParser parser = (PnutsParser) freeParsers.pop();
0091: parser.ReInit(reader);
0092: return parser;
0093: } else {
0094: return new PnutsParser(reader);
0095: }
0096: }
0097: }
0098:
0099: static void recycleParser(PnutsParser parser) {
0100: synchronized (freeParsers) {
0101: if (freeParsers.size() < 3) {
0102: freeParsers.push(parser);
0103: }
0104: }
0105: }
0106:
0107: /**
0108: * Checks if the runtime environment supports J2SE.
0109: *
0110: * @return true if the runtime environment supports J2SE
0111: */
0112: final public static boolean isJava2() {
0113: if (java2 == 0) {
0114: try {
0115: if (Runtime.getProperty("pnuts.jdk11.compatible") != null) {
0116: java2 = -1;
0117: } else {
0118: Class.forName("java.lang.Package");
0119: java2 = 1;
0120: }
0121: } catch (Throwable t) {
0122: java2 = -1;
0123: }
0124: }
0125: if (java2 == 1) {
0126: return true;
0127: } else {
0128: return false;
0129: }
0130: }
0131:
0132: /**
0133: * Sets properties that affect the behavior of Pnuts interpreter/compiler.
0134: *
0135: * This method should be called before the classes that read the default
0136: * settings, such as pnuts.lang.Configuration and pnuts.lang.PnutsImpl.
0137: * Once those classes are loaded, this method call has no effect.
0138: *
0139: * <pre>
0140: * Pnuts.setDefaults(properties);
0141: * Context c = new Context(); // this line should not precede setDefaults() call.
0142: *
0143: * </pre>
0144: *
0145: * @param properties
0146: * the properties that override the system properties.
0147: */
0148: public static void setDefaults(Properties properties) {
0149: defaultSettings = properties;
0150: }
0151:
0152: /**
0153: * Gets the properties previously set by setDefaults() method.
0154: *
0155: * @return the default setting that affects the behavior of Pnuts
0156: * interpreter/compiler.
0157: */
0158: public static Properties getDefaults() {
0159: return defaultSettings;
0160: }
0161:
0162: /**
0163: * Loads the class by the following order.
0164: * <OL>
0165: * <LI>A class loader associated with Pnuts context.
0166: * <LI>The class loader associated with the current Thread (J2SE).
0167: * <LI>The class loader by which Pnuts classes are loaded.
0168: * </OL>
0169: *
0170: * @param name
0171: * the class name to be loaded
0172: * @param context
0173: * the context in which the class is loaded
0174: * @return the loaded class. Note that it is not initialized.
0175: */
0176: public final static Class loadClass(String name, Context context)
0177: throws ClassNotFoundException {
0178: ClassLoader classLoader = context.getClassLoader();
0179: if (classLoader != null) {
0180: try {
0181: return classLoader.loadClass(name);
0182: } catch (ClassNotFoundException e1) {
0183: } catch (LinkageError e2) {
0184: }
0185: }
0186: if (isJava2()) {
0187: ClassLoader ccl = Thread.currentThread()
0188: .getContextClassLoader();
0189: if (ccl != null && ccl != classLoader) {
0190: try {
0191: return ccl.loadClass(name);
0192: } catch (ClassNotFoundException e1) {
0193: } catch (LinkageError e2) {
0194: }
0195: }
0196: }
0197: return Class.forName(name);
0198: }
0199:
0200: /**
0201: * Get the resource URL.
0202: *
0203: * @param s
0204: * the resource name
0205: * @param context
0206: * the context in which the resource is read
0207: */
0208: public final static URL getResource(String s, Context context) {
0209: ClassLoader classLoader = context.getClassLoader();
0210: if (classLoader != null) {
0211: URL url = classLoader.getResource(s);
0212: if (url != null) {
0213: return url;
0214: }
0215: }
0216: if (isJava2()) {
0217: ClassLoader ccl = Thread.currentThread()
0218: .getContextClassLoader();
0219: if (ccl != null) {
0220: URL url = ccl.getResource(s);
0221: if (url != null) {
0222: return url;
0223: }
0224: }
0225: return Pnuts.class.getResource(s);
0226: } else {
0227: return ClassLoader.getSystemResource(s);
0228: }
0229: }
0230:
0231: /**
0232: * Sets a "prompt" string for the command shell
0233: */
0234: public static void setPrompt(String str) {
0235: prompt = str;
0236: }
0237:
0238: /**
0239: * Sets the verbose mode
0240: *
0241: * @deprecated replaced by Context.setVerbose()
0242: */
0243: public static void setVerbose(boolean b) {
0244: Context.defaultVerboseMode = b;
0245: }
0246:
0247: /**
0248: * Check the current verbose mode
0249: *
0250: * @return the current verbose mode
0251: *
0252: * @deprecated replaced by Context.isVerbose()
0253: */
0254: public static boolean isVerbose() {
0255: return Context.defaultVerboseMode;
0256: }
0257:
0258: /**
0259: * Returns the string representation of an object. When the object is a
0260: * number, a character, a boolean, or a string, it can be reconstructed by
0261: * eval() function.
0262: *
0263: * @param obj
0264: * the object.
0265: * @return the string representation of the object
0266: */
0267: public static String format(Object obj) {
0268: return Runtime.format(obj, 64);
0269: }
0270:
0271: /**
0272: * Get the value of a global variable
0273: *
0274: * @param str
0275: * the name of the global variable
0276: * @return the value of the global variable <em>str</em>
0277: *
0278: * @deprecated replaced by Context.getCurrentPackage().get(str.intern())
0279: */
0280: public static Object get(String str) {
0281: return get(str, "");
0282: }
0283:
0284: /**
0285: * Gets a global variable.
0286: *
0287: * @param str
0288: * the name of the variable
0289: * @param pkg
0290: * the package where the variable is defined
0291: * @return the value of a variable "str" in the package "pkg"
0292: *
0293: * @deprecated replaced by Package.getPackage(pkg, null).get(str)
0294: */
0295: public static Object get(String str, String pkg) {
0296: Package p = Package.find(pkg);
0297: if (p != null) {
0298: return p.get(str.intern());
0299: } else {
0300: return null;
0301: }
0302: }
0303:
0304: /**
0305: * set a value "val" to a global variable "str"
0306: *
0307: * @deprecated replaced by context.getCurrentPackage().set(str.intern(),
0308: * val)
0309: */
0310: public static void set(String str, Object val) {
0311: set(str, val, "");
0312: }
0313:
0314: /**
0315: * Set a value "val" to a variable "str" in package "pkg"
0316: *
0317: * @param str
0318: * @param val
0319: * @param pkg
0320: *
0321: * @deprecated replaced by Package.getPackage(pkg, null).set(str, val)
0322: */
0323: public static void set(String str, Object val, String pkg) {
0324: if (str.length() > 0
0325: && Character.isJavaIdentifierStart(str.charAt(0))) {
0326: Package p = Package.find(pkg);
0327: if (p != null) {
0328: p.set(str.intern(), val);
0329: } else {
0330: throw new IllegalArgumentException(Runtime.getMessage(
0331: "pnuts.lang.pnuts", "package.notFound",
0332: new Object[] { pkg }));
0333: }
0334: } else {
0335: throw new IllegalArgumentException(Runtime.getMessage(
0336: "pnuts.lang.pnuts", "illegal.symbolForId",
0337: Runtime.NO_PARAM));
0338: }
0339: }
0340:
0341: /**
0342: * Evaluates "str" in "context"
0343: *
0344: * @param expr
0345: * the expression to be evaluated
0346: * @param context
0347: * the context in which the expression is evaluated
0348: * @return the result of the evaluation
0349: */
0350: public static Object eval(String expr, Context context) {
0351: return context.pnutsImpl.eval(expr, context);
0352: }
0353:
0354: /**
0355: * Loads a local script "file" in "context"
0356: *
0357: * @param file
0358: * the script file to be loaded.
0359: * @param context
0360: * the context in which the file is loaded.
0361: */
0362: public static Object loadFile(String file, Context context)
0363: throws FileNotFoundException {
0364: Thread th = Thread.currentThread();
0365: ClassLoader ccl = th.getContextClassLoader();
0366: try {
0367: return context.pnutsImpl.loadFile(file, context);
0368: } finally {
0369: th.setContextClassLoader(ccl);
0370: }
0371: }
0372:
0373: /**
0374: * Loads a script "file" in "context"
0375: *
0376: * @param name
0377: * the name of the script to be loaded
0378: * @param context
0379: * the context in which the script is loaded.
0380: */
0381: public static Object load(String name, Context context)
0382: throws FileNotFoundException {
0383: Thread th = Thread.currentThread();
0384: ClassLoader ccl = th.getContextClassLoader();
0385: String file = name;
0386: if (!name.endsWith(".pnut")) {
0387: file = name + ".pnut";
0388:
0389: final Executable rt = Runtime.getCompiledScript(name,
0390: context);
0391:
0392: if (rt != null) {
0393: if (context.verbose) {
0394: System.out.println("[loading "
0395: + Pnuts.format(rt.getClass()) + "]");
0396: }
0397: int depth = enter(context);
0398: context.pushFile(rt);
0399: boolean completed = false;
0400: Context old = Runtime.getThreadContext();
0401: try {
0402: context.provide(name);
0403: Runtime.setThreadContext(context);
0404: Object ret = rt.run(context);
0405: completed = true;
0406: return ret;
0407: } catch (Jump jump) {
0408: completed = true;
0409: return jump.getValue();
0410: } finally {
0411: if (!completed) {
0412: context.revoke(name);
0413: }
0414: th.setContextClassLoader(ccl);
0415: context.popFile();
0416: context.depth = depth;
0417: Runtime.setThreadContext(old);
0418: }
0419: }
0420: }
0421: try {
0422: return context.pnutsImpl.load(file, context);
0423: } finally {
0424: th.setContextClassLoader(ccl);
0425: }
0426: }
0427:
0428: /**
0429: * Loads a script specifed as a URL.
0430: *
0431: * @param url
0432: * the URL
0433: * @param context
0434: * the context in which the script is loaded.
0435: */
0436: public static Object load(URL url, Context context) {
0437: Thread th = Thread.currentThread();
0438: ClassLoader ccl = th.getContextClassLoader();
0439: try {
0440: return context.pnutsImpl.load(url, context);
0441: } finally {
0442: th.setContextClassLoader(ccl);
0443: }
0444: }
0445:
0446: /**
0447: * Loads a script from InputStream "in" in "context"
0448: *
0449: * @param in
0450: * the input stream from which the script can be read.
0451: * @param context
0452: * the context in which the script is loaded.
0453: */
0454: public static Object load(InputStream in, Context context) {
0455: return load(Runtime.getScriptReader(in, context), context);
0456: }
0457:
0458: /**
0459: * Load a script from an InputStream in the specified Context.
0460: *
0461: * @param in
0462: * an InputStream from which the interpreter reads an input
0463: * @param interactive
0464: * <ul>
0465: * <li>When "interactive" is true, the greeting message, the
0466: * prompt, and the results of evaluations are displayed. When an
0467: * exception is thrown and not caught by any exception handler,
0468: * it is caught at the top level of the interpreter, display an
0469: * error message, and resume the interactive session. If the
0470: * exception is caught by a handler that is registered at the top
0471: * level, the result of the handler becomes the return value of
0472: * the last expression.
0473: *
0474: * <li>When "interactive" is false, exceptions are caught at the top level
0475: * of the interpreter and exits this function. If the exception thrown is
0476: * caught by a handler that is registered at the top level, the result of
0477: * the handler becomes the return value of this method.
0478: * </ul>
0479: * @param context
0480: * a Context in which the interpretation is taken place.
0481: * @return the result of the last expression
0482: */
0483: public static Object load(InputStream in, boolean interactive,
0484: Context context) {
0485: return load(Runtime.getScriptReader(in, context), interactive,
0486: context);
0487: }
0488:
0489: /**
0490: * This method loads a script
0491: *
0492: * @param reader
0493: * the Reader from which the script is loaded
0494: * @param context
0495: * the context in which the script is loaded
0496: * @return the result of the last expression
0497: */
0498: public static Object load(Reader reader, Context context) {
0499: PnutsParser parser = getParser(reader);
0500: synchronized (context.namespaceRefreshed) {
0501: context.namespaceRefreshed[0] = false;
0502: }
0503: Thread th = Thread.currentThread();
0504: ClassLoader ccl = th.getContextClassLoader();
0505:
0506: int depth = enter(context);
0507: Object value = null;
0508: try {
0509: while (true) {
0510: try {
0511: SimpleNode start = parser.Start(DefaultParseEnv
0512: .getInstance());
0513: switch (start.toplevel) {
0514: case -1:
0515: start.toplevel = 0;
0516: return value;
0517: case 0:
0518: reset(parser);
0519: break;
0520: case 1:
0521: value = context.pnutsImpl
0522: .accept(start, context);
0523: if (start.toplevel == -1) {
0524: return start.value;
0525: }
0526: reset(parser);
0527: context.onExit(value);
0528: break;
0529: }
0530: } catch (ParseException p) {
0531: Runtime.checkException(context, p);
0532: } catch (Escape esc) {
0533: reset(parser);
0534: throw esc;
0535: } catch (Throwable t) {
0536: if (t instanceof PnutsException) {
0537: throw (PnutsException) t;
0538: } else if (t instanceof ThreadDeath) {
0539: throw (ThreadDeath) t;
0540: } else {
0541: throw new PnutsException(t, context);
0542: }
0543: }
0544: }
0545: } catch (Escape esc) {
0546: context.onExit(value);
0547: flush(context);
0548: return esc.getValue();
0549: } catch (PnutsException pe) {
0550: context.onError(pe);
0551: throw pe;
0552: } catch (Throwable t) {
0553: PnutsException e = new PnutsException(t, context);
0554: context.onError(t);
0555: throw e;
0556: } finally {
0557: th.setContextClassLoader(ccl);
0558: Executable hook = context.exitHook;
0559: if (hook != null) {
0560: hook.run(context);
0561: }
0562: context.depth = depth;
0563: recycleParser(parser);
0564: }
0565: }
0566:
0567: /**
0568: * all public entry points must go through this to ensure the correct
0569: * behavior of evalDepth method.
0570: *
0571: * @param context
0572: * the context in which the execution is taken place.
0573: * @return the last value of context.depth.
0574: */
0575: static int enter(Context context) {
0576: int depth = context.depth;
0577: if (depth < 0x7fffffff) {
0578: context.depth++;
0579: }
0580: return depth;
0581: }
0582:
0583: /**
0584: * Get the depth of evaluation.
0585: *
0586: * This value increases when load(), loadFile(), or eval() is called.
0587: *
0588: * @param context
0589: * the context of the evaluation.
0590: */
0591: public static int evalDepth(Context context) {
0592: return context.depth;
0593: }
0594:
0595: /**
0596: * This method loads a script
0597: *
0598: * @param reader
0599: * the Reader from which the script is loaded
0600: * @param interactive
0601: * specifies if the execution is in interactive mode.
0602: * @param context
0603: * the context in which the script is loaded
0604: * @return the result of the last expression
0605: */
0606: public static Object load(Reader reader, boolean interactive,
0607: Context context) {
0608: if (interactive) {
0609: return session(reader, context);
0610: } else {
0611: return load(reader, context);
0612: }
0613: }
0614:
0615: /**
0616: * Parses a script from InputStream and return a Pnuts object
0617: *
0618: * @return the Pnuts object including a parsed syntax tree
0619: * @param in
0620: * the InputStream
0621: * @deprecated replaced by parse(Reader)
0622: */
0623: public static Pnuts parse(InputStream in) throws ParseException,
0624: IOException {
0625: return parse(new InputStreamReader(in));
0626: }
0627:
0628: /**
0629: * parse a script from Reader and return a Pnuts object
0630: *
0631: * @return the Pnuts object including a parsed syntax tree
0632: * @param reader
0633: * the Reader
0634: * @since Pnuts 1.0beta3
0635: */
0636: public static Pnuts parse(Reader reader) throws ParseException,
0637: IOException {
0638: return parse(reader, DefaultParseEnv.getInstance());
0639: }
0640:
0641: /**
0642: * parse a script from Reader and return a Pnuts object
0643: */
0644: public static Pnuts parse(Reader reader, ParseEnvironment env)
0645: throws ParseException, IOException {
0646: PnutsParser parser = getParser(reader);
0647: Pnuts p = new Pnuts();
0648: try {
0649: p.startNodes = parser.StartSet(env);
0650: } catch (ParseException e) {
0651: env.handleParseException(e);
0652: } finally {
0653: recycleParser(parser);
0654: }
0655: return p;
0656: }
0657:
0658: /**
0659: * parse a script from Reader and return a Pnuts object
0660: *
0661: * @return the Pnuts object including a parsed syntax tree
0662: * @param reader
0663: * the Reader
0664: * @param scriptSource
0665: * the script source
0666: */
0667: public static Pnuts parse(Reader reader, Object scriptSource,
0668: Context context) throws IOException {
0669: return parse(reader, scriptSource, context, DefaultParseEnv
0670: .getInstance(scriptSource));
0671: }
0672:
0673: /**
0674: * parse a script from Reader and return a Pnuts object
0675: *
0676: * @return the Pnuts object including a parsed syntax tree
0677: * @param reader
0678: * the Reader
0679: * @param scriptSource
0680: * the script source
0681: * @param env
0682: */
0683: public static Pnuts parse(Reader reader, Object scriptSource,
0684: Context context, ParseEnvironment env) throws IOException {
0685: Function frame = context.frame;
0686: context.frame = null;
0687:
0688: PnutsParser parser = getParser(reader);
0689: try {
0690: Pnuts p = new Pnuts();
0691: p.startNodes = parser.StartSet(env);
0692: p.setScriptSource(scriptSource);
0693: return p;
0694: } catch (ParseException e) {
0695: throw new PnutsException(e, context);
0696: } finally {
0697: context.frame = frame;
0698: recycleParser(parser);
0699: }
0700: }
0701:
0702: /**
0703: * Parses a script and return a Pnuts object
0704: *
0705: * @return the Pnuts object including a parsed syntax tree
0706: * @param expr
0707: * the script
0708: */
0709: public static Pnuts parse(String expr) throws ParseException {
0710: try {
0711: Pnuts p = parse(new StringReader(expr));
0712: p.scriptSource = expr;
0713: return p;
0714: } catch (IOException e) {
0715: throw new InternalError();
0716: }
0717: }
0718:
0719: /**
0720: * Loads a script "file" only if the script has not been read. It is
0721: * guaranteed that the script runs at most once in this context.
0722: *
0723: * @param file
0724: * the script file, which must be an intern'ed String.
0725: * @param context
0726: * the context in which the script is loaded
0727: */
0728: public static void require(String file, Context context)
0729: throws FileNotFoundException {
0730: require(file, context, false);
0731: }
0732:
0733: public static void require(String file, Context context,
0734: boolean checkForUpdate) throws FileNotFoundException {
0735: if (file.endsWith(".pnut")) {
0736: file = file.substring(0, file.length() - 5);
0737: }
0738: file = file.intern();
0739: context.require(file, checkForUpdate);
0740: }
0741:
0742: static Object session(Reader r, Context context) {
0743: BufferedReader br = new BufferedReader(r);
0744: StringBuffer sbuf = new StringBuffer();
0745: ParseEnvironment parseEnv = DefaultParseEnv.getInstance();
0746: Object value = null;
0747: while (true) {
0748: PnutsParser parser = null;
0749: try {
0750: PrintWriter term = null;
0751: flush(context);
0752:
0753: term = context.getTerminalWriter();
0754: if (term != null) {
0755: term.print(prompt);
0756: term.flush();
0757: }
0758: String line = br.readLine();
0759: if (line == null) {
0760: return value;
0761: }
0762: sbuf.append(line);
0763: StringReader sr = new StringReader(sbuf.toString());
0764: parser = getParser(sr);
0765: try {
0766: SimpleNode start = parser.Start(parseEnv);
0767: sbuf.setLength(0);
0768: try {
0769: value = context.pnutsImpl
0770: .accept(start, context);
0771: } catch (Jump jump) {
0772: value = jump.getValue();
0773: } finally {
0774: context.onExit(value);
0775: }
0776:
0777: try {
0778: term.println(context.getConfiguration()
0779: .formatObject(value));
0780: term.flush();
0781: } catch (ThreadDeath td) {
0782: throw td;
0783: } catch (Throwable t) {
0784: Runtime.checkException(context, t);
0785: }
0786:
0787: } catch (ParseException pe) {
0788: Token t = pe.currentToken;
0789: Token t2 = t.next;
0790: while (t != null) {
0791: t2 = t;
0792: t = t.next;
0793: }
0794: if (t2.kind != PnutsParserConstants.EOF) {
0795: throw pe;
0796: }
0797: sbuf.append('\n');
0798: } finally {
0799: recycleParser(parser);
0800: }
0801: } catch (ParseException p) {
0802: sbuf.setLength(0);
0803: if (context.depth > 1) {
0804: Runtime.checkException(context, p);
0805: } else {
0806: int line = context.beginLine;
0807: try {
0808: context.beginLine = p.currentToken.next.beginLine;
0809: Runtime.checkException(context, p);
0810: } catch (Jump j) {
0811: value = j.getValue();
0812: PrintWriter term = context.getTerminalWriter();
0813: if (term != null) {
0814: term.println(value);
0815: term.flush();
0816: }
0817: } catch (PnutsException t) {
0818: Runtime.printError(t, context);
0819: } finally {
0820: context.beginLine = line;
0821: }
0822: reset(parser);
0823: recover(context);
0824: value = null;
0825: }
0826: } catch (Escape esc) {
0827: reset(parser);
0828: context.onExit(value);
0829: return esc.getValue();
0830: } catch (Throwable t) {
0831: if (context.depth > 1) {
0832: if (t instanceof PnutsException) {
0833: throw (PnutsException) t;
0834: } else if (t instanceof ThreadDeath) {
0835: throw (ThreadDeath) t;
0836: } else {
0837: throw new PnutsException(t, context);
0838: }
0839: }
0840: if (t instanceof ThreadDeath) {
0841: return null;
0842: }
0843: Runtime.printError(t, context);
0844: if (parser == null) {
0845: break;
0846: }
0847: reset(parser);
0848: recover(context);
0849: value = null;
0850: }
0851: }
0852: return null;
0853: }
0854:
0855: private static void reset(PnutsParser parser) {
0856: parser.jjtree.reset();
0857: }
0858:
0859: private static void recover(Context context) {
0860: context.resetStackFrame();
0861: context.loadingResource = null;
0862: }
0863:
0864: private static void flush(Context context) {
0865: PrintWriter out = context.getWriter();
0866: if (out != null) {
0867: out.flush();
0868: }
0869: }
0870:
0871: /**
0872: * Create a classloader that can compile scripted classes
0873: * with the current thread's context classloader as its parent classloader
0874: *
0875: * @param context the context in which scripts are compiled
0876: * @return the classloader
0877: */
0878: public static ClassLoader createClassLoader(Context context) {
0879: return createClassLoader(context, Thread.currentThread()
0880: .getContextClassLoader());
0881: }
0882:
0883: /**
0884: * Create a classloader that can compile scripted classes
0885: *
0886: * @param context the context in which scripts are compiled
0887: * @param parent the parent classloader
0888: * @return the classloader
0889: */
0890: public static ClassLoader createClassLoader(Context context,
0891: ClassLoader parent) {
0892: return new PnutsClassLoader(parent, context);
0893: }
0894:
0895: /**
0896: * Parsed scripts
0897: *
0898: * @serial
0899: */
0900: protected SimpleNode startNodes = null;
0901:
0902: /**
0903: * The script source, from where the script came. It is usually a URL
0904: * object, but not limitted to. If this variable is not null, error message
0905: * would include the positional information such as the line number and the
0906: * file name.
0907: */
0908: protected Object scriptSource;
0909:
0910: protected Pnuts() {
0911: }
0912:
0913: /**
0914: * Executes a Pnuts object with the specified Context
0915: *
0916: * @param context
0917: * the Context
0918: * @return the result
0919: */
0920: public Object run(Context context) {
0921: Thread th = Thread.currentThread();
0922: ClassLoader ccl = th.getContextClassLoader();
0923: int depth = enter(context);
0924: context.pushFile(scriptSource);
0925: Function frame = context.frame;
0926: context.frame = null;
0927: try {
0928: return accept(context);
0929: } finally {
0930: th.setContextClassLoader(ccl);
0931: context.depth = depth;
0932: context.frame = frame;
0933: context.popFile();
0934: }
0935: }
0936:
0937: /**
0938: * Associates a script source with this parsed (compiled) expression.
0939: *
0940: * @param src
0941: * the script source to be associated with.
0942: */
0943: public void setScriptSource(Object src) {
0944: this .scriptSource = src;
0945: }
0946:
0947: /**
0948: * Gets the script source associated with this parsed (compiled) expression
0949: *
0950: * @return the script source to be associated with.
0951: */
0952: public Object getScriptSource() {
0953: return this .scriptSource;
0954: }
0955:
0956: /**
0957: * traverse the parsed tree with the specified Visitor and Context
0958: *
0959: * @param context
0960: * the Context
0961: * @return the result
0962: * @since Pnuts 1.0beta3
0963: */
0964: public Object accept(Visitor visitor, Context context) {
0965: int depth = enter(context);
0966: Object value = null;
0967: Context old = Runtime.getThreadContext();
0968: Runtime.setThreadContext(context);
0969: try {
0970: value = startNodes.accept(visitor, context);
0971: context.onExit(value);
0972: return value;
0973: } catch (Escape esc) {
0974: context.onExit(value);
0975: Object val = esc.getValue();
0976: flush(context);
0977: return val;
0978: } catch (PnutsException pe) {
0979: context.onError(pe);
0980: throw pe;
0981: } catch (Throwable t) {
0982: PnutsException e = new PnutsException(t, context);
0983: context.onError(t);
0984: throw e;
0985: } finally {
0986: context.depth = depth;
0987: Runtime.setThreadContext(old);
0988: }
0989: }
0990:
0991: /**
0992: * Obtain the script code from a parsed object
0993: *
0994: * @return the script code
0995: */
0996: public String unparse() {
0997: return Runtime.unparse(startNodes, null);
0998: }
0999:
1000: /**
1001: * Obtain the script code from a parsed object and write it to the specified
1002: * Writer.
1003: *
1004: * @param writer
1005: * the Writer to which the script code is written
1006: */
1007: public void unparse(Writer writer) throws IOException {
1008: writer.write(unparse());
1009: }
1010:
1011: /**
1012: * Executes the parsed script
1013: *
1014: * @param context
1015: * the context in which the script is executed
1016: * @return the result
1017: */
1018: protected Object accept(Context context) {
1019: Object value = null;
1020: Thread th = Thread.currentThread();
1021: ClassLoader ccl = th.getContextClassLoader();
1022: try {
1023: value = context.pnutsImpl.accept(startNodes, context);
1024: context.onExit(value);
1025: return value;
1026: } catch (Escape esc) {
1027: context.onExit(value);
1028: Object val = esc.getValue();
1029: flush(context);
1030: context.onExit(val);
1031: return val;
1032: } catch (PnutsException pe) {
1033: context.onError(pe);
1034: throw pe;
1035: } catch (Throwable t) {
1036: PnutsException e = new PnutsException(t, context);
1037: context.onError(t);
1038: throw e;
1039: } finally {
1040: th.setContextClassLoader(ccl);
1041: }
1042: }
1043: }
|