001: // Copyright (c) 2002, 2003, 2004, 2005 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.expr;
005:
006: import gnu.mapping.*;
007: import gnu.bytecode.*;
008: import gnu.mapping.Location;
009: import gnu.lists.*;
010: import gnu.text.Lexer;
011: import gnu.text.SourceMessages;
012: import gnu.kawa.reflect.*;
013: import java.io.*;
014: import java.lang.reflect.InvocationTargetException;
015: import gnu.kawa.lispexpr.ClassNamespace; // FIXME
016:
017: /**
018: * Contains various language-dependent methods.
019: * Also contains "global" state about the executation environment,
020: * such as the global Environment. There can be multiple Languages
021: * associated with different threads, representing mutiple top-levels.
022: * (However, this functionality is incomplete.)
023: */
024:
025: public abstract class Language {
026: protected static final ThreadLocation current = new ThreadLocation(
027: "language");
028:
029: public static Language getDefaultLanguage() {
030: return (Language) current.get(null);
031: }
032:
033: static {
034: Environment.setGlobal(BuiltinEnvironment.getInstance());
035: }
036:
037: public static void setDefaultLanguage(Language language) {
038: current.set(language);
039: }
040:
041: /**
042: * List of known languages and their Language classes.
043: * Each element is one or more language names, or filename extensions,
044: * followed by the name of the Language sub-class.
045: * The table is searched from the beginning.
046: */
047:
048: static String[][] languages = {
049: { "scheme", ".scm", ".sc", "kawa.standard.Scheme" },
050: { "krl", ".krl", "gnu.kawa.brl.BRL" },
051: { "brl", ".brl", "gnu.kawa.brl.BRL" },
052: { "emacs", "elisp", "emacs-lisp", ".el",
053: "gnu.jemacs.lang.ELisp" },
054: { "xquery", ".xquery", ".xq", ".xql",
055: "gnu.xquery.lang.XQuery" },
056: { "q2", ".q2", "gnu.q2.lang.Q2" },
057: { "xslt", "xsl", ".xsl", "gnu.kawa.xslt.XSLT" },
058: { "commonlisp", "common-lisp", "clisp", "lisp", ".lisp",
059: ".lsp", ".cl", "gnu.commonlisp.lang.CommonLisp" } };
060:
061: /** Get a list of all available languages */
062:
063: public static String[][] getLanguages() {
064: return languages;
065: }
066:
067: /** Add a language to the list.
068: *
069: * @param langMapping is a language definition, the first index
070: * is the language name, subsequent indexes are file types that
071: * might cause the language to be used and the final index is the
072: * name of the class that implements the language.
073: */
074: public static void registerLanguage(String[] langMapping) {
075: String[][] newLangs = new String[languages.length + 1][];
076: System.arraycopy(languages, 0, newLangs, 0, languages.length);
077: newLangs[newLangs.length - 1] = langMapping;
078: languages = newLangs;
079: }
080:
081: /** Detect the programming language of a file based on its first line.
082: * @return a suitable Language or null if we didn't recognize one.
083: */
084: public static Language detect(InputStream in) throws IOException {
085: if (!in.markSupported())
086: return null;
087: StringBuffer sbuf = new StringBuffer();
088: in.mark(200);
089: for (;;) {
090: if (sbuf.length() >= 200)
091: break;
092: int c = in.read();
093: if (c < 0 || c == '\n' || c == '\r')
094: break;
095: sbuf.append((char) c);
096: }
097: in.reset();
098: return detect(sbuf.toString());
099: }
100:
101: /** Detect the programming language of a file based on its first line.
102: * @return a suitable Language or null if we didn't recognize one.
103: */
104: public static Language detect(InPort port) throws IOException {
105: StringBuffer sbuf = new StringBuffer();
106: port.mark(300);
107: port.readLine(sbuf, 'P');
108: port.reset();
109: return detect(sbuf.toString());
110: }
111:
112: /** Detect the programming language of a file based on its first line.
113: * @param line the first input line
114: * @return a suitable Language or null if we didn't recognize one.
115: */
116: public static Language detect(String line) {
117: String str = line.trim();
118: // Does the line contain the string "kawa:LANGAUGE" for a valid LANGUAGE?
119: int k = str.indexOf("kawa:");
120: if (k >= 0) {
121: int i = k + 5;
122: int j = i;
123: while (j < str.length()
124: && Character.isJavaIdentifierPart(str.charAt(j)))
125: j++;
126: if (j > i) {
127: String w = str.substring(i, j);
128: Language lang = getInstance(w);
129: if (lang != null)
130: return lang;
131: }
132: }
133: // Check for various Emacs language/mode patterns.
134: if (str.indexOf("-*- scheme -*-") >= 0)
135: return getInstance("scheme");
136: if (str.indexOf("-*- xquery -*-") >= 0)
137: return getInstance("xquery");
138: if (str.indexOf("-*- emacs-lisp -*-") >= 0)
139: return getInstance("elisp");
140: if (str.indexOf("-*- common-lisp -*-") >= 0
141: || str.indexOf("-*- lisp -*-") >= 0)
142: return getInstance("common-lisp");
143: // Does it start with an XQuery comment or XQuery version statement?
144: if ((str.charAt(0) == '(' && str.charAt(1) == ':')
145: || (str.length() >= 7 && str.substring(0, 7).equals(
146: "xquery ")))
147: return getInstance("xquery");
148: if (str.charAt(0) == ';' && str.charAt(1) == ';')
149: return getInstance("scheme");
150: return null;
151: }
152:
153: public static Language getInstanceFromFilenameExtension(
154: String filename) {
155: int dot = filename.lastIndexOf('.');
156: if (dot > 0) {
157: Language lang = Language.getInstance(filename
158: .substring(dot));
159: if (lang != null)
160: return lang;
161: }
162: return null;
163: }
164:
165: /** Look for a language with the given name or extension.
166: * If name is null, look for the first language available. */
167: public static Language getInstance(String name) {
168: int langCount = languages.length;
169: for (int i = 0; i < langCount; i++) {
170: String[] names = languages[i];
171: int nameCount = names.length - 1;
172: for (int j = nameCount; --j >= 0;) {
173: if (name == null || names[j].equalsIgnoreCase(name)) {
174: Class langClass;
175: try {
176: langClass = Class.forName(names[nameCount]);
177: } catch (ClassNotFoundException ex) {
178: // In the future, we may support languages names that
179: // can be implemented by more than one Language,
180: // so don't give up yet.
181: break;
182: }
183: return getInstance(names[0], langClass);
184: }
185: }
186: }
187: return null;
188: }
189:
190: protected Language() {
191: gnu.lists.Convert.setInstance(KawaConvert.getInstance());
192: }
193:
194: public static Language getInstance(String langName, Class langClass) {
195: try {
196: java.lang.reflect.Method method;
197: Class[] args = {};
198: try {
199: String capitalizedName = (Character
200: .toTitleCase(langName.charAt(0)) + langName
201: .substring(1).toLowerCase());
202: String methodName = "get" + capitalizedName
203: + "Instance";
204: method = langClass.getDeclaredMethod(methodName, args);
205: } catch (Exception ex) {
206: method = langClass.getDeclaredMethod("getInstance",
207: args);
208: }
209: return (Language) method.invoke(null, Values.noArgs);
210: } catch (Exception ex) {
211: langName = langClass.getName();
212: Throwable th;
213: if (ex instanceof InvocationTargetException)
214: th = ((InvocationTargetException) ex)
215: .getTargetException();
216: else
217: th = ex;
218: // th.printStackTrace();
219: throw new WrappedException("getInstance for '" + langName
220: + "' failed", th);
221: }
222: }
223:
224: /** Test if a value is considered "true" in this language. */
225: public boolean isTrue(Object value) {
226: return value != Boolean.FALSE;
227: }
228:
229: public Object booleanObject(boolean b) {
230: return b ? Boolean.TRUE : Boolean.FALSE;
231: }
232:
233: /** The value to return for a "void" result. */
234: public Object noValue() {
235: return Values.empty;
236: }
237:
238: /** True if functions are in a separate anme space from variable.
239: * Is true for e.g. Common Lisp, Emacs Lisp; false for Scheme. */
240: public boolean hasSeparateFunctionNamespace() {
241: return false;
242: }
243:
244: /** The environment for language built-ins and predefined bindings. */
245: protected Environment environ;
246:
247: /** If non-null, the user environment.
248: * This allows "bunding" an Environment with a Language. This is partly to
249: * match existing documentation, and partly for convenience from Java code.
250: * Normally, userEnv is null, in which case the user environment is
251: * extracted from the current thread. */
252: protected Environment userEnv;
253:
254: /** Get current user environment. */
255: public final Environment getEnvironment() {
256: return userEnv != null ? userEnv : Environment.getCurrent();
257: }
258:
259: static int envCounter;
260:
261: public final Environment getNewEnvironment() {
262: return Environment.make("environment-" + (++envCounter),
263: environ);
264: }
265:
266: public Environment getLangEnvironment() {
267: return environ;
268: }
269:
270: public NamedLocation lookupBuiltin(Symbol name, Object property,
271: int hash) {
272: return environ == null ? null : environ.lookup(name, property,
273: hash);
274: }
275:
276: /** Enter a value into the current environment. */
277: public void define(String sym, Object p) {
278: Symbol s = getSymbol(sym);
279: environ.define(s, null, p);
280: }
281:
282: /** Declare in the current Environment a variable aliased to a static field.
283: */
284: protected void defAliasStFld(String name, String cname, String fname) {
285: StaticFieldLocation.define(environ, getSymbol(name), null,
286: cname, fname);
287: }
288:
289: /** Declare in the current Environment a procedure bound to a static field.
290: * @param name the procedure's source-level name.
291: * @param cname the name of the class containing the field.
292: * @param fname the name of the field, which should be a static
293: * final field whose type extends gnu.mapping.Procedure.
294: */
295: protected void defProcStFld(String name, String cname, String fname) {
296: Object property = (hasSeparateFunctionNamespace() ? EnvironmentKey.FUNCTION
297: : null);
298: Symbol sym = getSymbol(name);
299: StaticFieldLocation loc = StaticFieldLocation.define(environ,
300: sym, property, cname, fname);
301: loc.setProcedure();
302: }
303:
304: /** Declare in the current Environment a procedure bound to a static field.
305: * @param name the procedure's source-level name.
306: * @param cname the name of the class containing the field.
307: * The name of the field is the mangling of <code>name</code>.
308: */
309: protected void defProcStFld(String name, String cname) {
310: defProcStFld(name, cname, Compilation.mangleNameIfNeeded(name));
311: }
312:
313: /** Enter a named function into the current environment. */
314: public final void defineFunction(Named proc) {
315: Object name = proc.getSymbol();
316: Symbol sym = (name instanceof Symbol ? (Symbol) name
317: : getSymbol(name.toString()));
318: Object property = (hasSeparateFunctionNamespace() ? EnvironmentKey.FUNCTION
319: : null);
320: environ.define(sym, property, proc);
321: }
322:
323: /** Enter a function into the current environment.
324: * Same as define(name,proc) for Scheme, but not for (say) Common Lisp.
325: **/
326: public void defineFunction(String name, Object proc) {
327: Object property = (hasSeparateFunctionNamespace() ? EnvironmentKey.FUNCTION
328: : null);
329: environ.define(getSymbol(name), property, proc);
330: }
331:
332: /** Import all the public fields of an object. */
333: private void defineAll(Object object) {
334: Class clas = object.getClass();
335: java.lang.reflect.Field[] fields = clas.getFields();
336: for (int i = fields.length; --i >= 0;) {
337: java.lang.reflect.Field field = fields[i];
338: String name = field.getName();
339: if (name.startsWith(Declaration.PRIVATE_PREFIX)
340: || name.endsWith("$instance"))
341: continue;
342: if ((field.getModifiers() & java.lang.reflect.Modifier.FINAL) != 0) {
343: try {
344: defineFromFieldValue(field, field.get(object));
345: } catch (Throwable ex) {
346: throw new WrappedException("error accessing field "
347: + field, ex);
348: }
349: } else {
350: System.err.println("INTERNAL ERROR in defineAll for "
351: + name + " in " + clas);
352: }
353: }
354: }
355:
356: private void defineFromFieldValue(java.lang.reflect.Field fld,
357: Object value) throws Throwable {
358: if (value instanceof Location) {
359: Location loc = (Location) value;
360: Symbol sym = loc.getKeySymbol();
361: if (sym != null) {
362: environ.addLocation(sym, loc.getKeyProperty(), loc);
363: return;
364: }
365: } else {
366: Object vname;
367: if (value instanceof Named)
368: vname = ((Named) value).getSymbol();
369: else
370: vname = null;
371: if (vname == null)
372: vname = Compilation.demangleName(fld.getName(), true)
373: .intern();
374: Symbol symbol = vname instanceof Symbol ? (Symbol) vname
375: : environ.getSymbol(vname.toString());
376: Object prop = getEnvPropertyFor(fld, value);
377: environ.define(symbol, prop, value);
378: }
379: }
380:
381: public Object getEnvPropertyFor(java.lang.reflect.Field fld,
382: Object value) {
383: if (!hasSeparateFunctionNamespace())
384: return null;
385: if (Compilation.typeProcedure.getReflectClass()
386: .isAssignableFrom(fld.getType()))
387: return EnvironmentKey.FUNCTION;
388: return null;
389: }
390:
391: public Object getEnvPropertyFor(Declaration decl) {
392: if (hasSeparateFunctionNamespace() && decl.isProcedureDecl())
393: return EnvironmentKey.FUNCTION;
394: return null;
395: }
396:
397: public void loadClass(String name)
398: throws java.lang.ClassNotFoundException {
399: try {
400: Class clas = Class.forName(name);
401: Object inst = clas.newInstance();
402: defineAll(inst);
403: if (inst instanceof ModuleBody)
404: ((ModuleBody) inst).run();
405: } catch (java.lang.ClassNotFoundException ex) {
406: throw ex;
407: } catch (Exception ex) {
408: throw new WrappedException("cannot load " + name, ex);
409: }
410: }
411:
412: public Symbol getSymbol(String name) {
413: return environ.getSymbol(name);
414: }
415:
416: public Object lookup(String name) {
417: return environ.get(name);
418: }
419:
420: public AbstractFormat getFormat(boolean readable) {
421: return null;
422: }
423:
424: public Consumer getOutputConsumer(Writer out) {
425: OutPort oport = out instanceof OutPort ? (OutPort) out
426: : new OutPort(out);
427: oport.objectFormat = getFormat(false);
428: return oport;
429: }
430:
431: public String getName() {
432: String name = getClass().getName();
433: int dot = name.lastIndexOf('.');
434: if (dot >= 0)
435: name = name.substring(dot + 1);
436: return name;
437: }
438:
439: public abstract Lexer getLexer(InPort inp, SourceMessages messages);
440:
441: public Compilation getCompilation(Lexer lexer,
442: SourceMessages messages) {
443: return new Compilation(this , messages);
444: }
445:
446: /** Flag to tell parse that expression will be evaluated immediately.
447: * I.e. we're not creating class files for future execution. */
448: public static final int PARSE_IMMEDIATE = 1;
449: /** Flag to tell parse to only read a single line if possible.
450: * Multiple lines may be read if syntactically required. */
451: public static final int PARSE_ONE_LINE = 2;
452: /** Flag to tell parser to continue until we have the module name.
453: * The parser is allowed to continue further, but must stop before
454: * any module import. */
455: public static final int PARSE_PROLOG = 4;
456:
457: public static boolean requirePedantic;
458:
459: /** Parse one or more expressions.
460: * @param port the InPort to read the expressions from.
461: * @param messages where to send error messages and warnings
462: * @param options various flags, includding PARSE_IMMEDIATE
463: * and PARSE_ONE_LINE
464: * @return a new Compilation.
465: * May return null if PARSE_ONE_LINE on end-of-file.
466: */
467: public final Compilation parse(InPort port,
468: gnu.text.SourceMessages messages, int options)
469: throws java.io.IOException, gnu.text.SyntaxException {
470: return parse(getLexer(port, messages), options, null);
471: }
472:
473: public final Compilation parse(InPort port,
474: gnu.text.SourceMessages messages, ModuleInfo info)
475: throws java.io.IOException, gnu.text.SyntaxException {
476: return parse(getLexer(port, messages), Language.PARSE_PROLOG,
477: info);
478: }
479:
480: public final Compilation parse(Lexer lexer, int options,
481: ModuleInfo info) throws java.io.IOException,
482: gnu.text.SyntaxException {
483: SourceMessages messages = lexer.getMessages();
484: Compilation tr = getCompilation(lexer, messages);
485: if (requirePedantic)
486: tr.pedantic = true;
487: tr.immediate = (options & PARSE_IMMEDIATE) != 0;
488: if ((options & PARSE_PROLOG) != 0)
489: tr.setState(Compilation.PROLOG_PARSING);
490: tr.pushNewModule(lexer);
491: if (info != null)
492: info.setCompilation(tr);
493: if (!parse(tr, options))
494: return null;
495: if (tr.getState() == Compilation.PROLOG_PARSING)
496: tr.setState(Compilation.PROLOG_PARSED);
497: return tr;
498: }
499:
500: public abstract boolean parse(Compilation comp, int options)
501: throws java.io.IOException, gnu.text.SyntaxException;
502:
503: /** Perform any need post-processing after we've read all the modules
504: * to be compiled.
505: * Using a separate pass allows compiling mutually recursive modules. */
506: public void resolve(Compilation comp) {
507: }
508:
509: public Type getTypeFor(Class clas) {
510: return Type.make(clas);
511: }
512:
513: public final Type getLangTypeFor(Type type) {
514: if (!(type instanceof ObjectType)
515: || ((ObjectType) type).isExisting()) {
516: Class clas = type.getReflectClass();
517: if (clas != null)
518: return getTypeFor(clas);
519: }
520: return type;
521: }
522:
523: public String formatType(Type type) {
524: return type.toString();
525: }
526:
527: public static Type string2Type(String name) {
528: Type t;
529: if (name.endsWith("[]")) {
530: t = string2Type(name.substring(0, name.length() - 2));
531: if (t == null)
532: return null;
533: t = gnu.bytecode.ArrayType.make(t);
534: } else if (gnu.bytecode.Type.isValidJavaTypeName(name))
535: t = gnu.bytecode.Type.getType(name);
536: else
537: return null;
538: return t;
539: }
540:
541: public Type getTypeFor(String name) {
542: return string2Type(name);
543: }
544:
545: public final Type getTypeFor(Object spec, boolean lenient) {
546: if (spec instanceof Type)
547: return (Type) spec;
548: if (spec instanceof Class)
549: return getTypeFor((Class) spec);
550: if (lenient
551: && (spec instanceof FString
552: || spec instanceof String
553: || (spec instanceof Symbol && ((Symbol) spec)
554: .hasEmptyNamespace()) || spec instanceof CharSeq))
555: return getTypeFor(spec.toString());
556: if (spec instanceof Namespace) {
557: String uri = ((Namespace) spec).getName();
558: if (uri != null && uri.startsWith("class:"))
559: return getLangTypeFor(string2Type(uri.substring(6)));
560: }
561: return null;
562: }
563:
564: /** "Coerce" a language-specific "type specifier" object to a Type. */
565: public final Type asType(Object spec) {
566: Type type = getTypeFor(spec, true);
567: return type == null ? (Type) spec : type;
568: }
569:
570: public final Type getTypeFor(Expression exp) {
571: return getTypeFor(exp, true);
572: }
573:
574: public Type getTypeFor(Expression exp, boolean lenient) {
575: if (exp instanceof QuoteExp) {
576: return getTypeFor(((QuoteExp) exp).getValue(), lenient);
577: } else if (exp instanceof ReferenceExp) {
578: ReferenceExp rexp = (ReferenceExp) exp;
579: Declaration decl = Declaration.followAliases(rexp
580: .getBinding());
581: String name = rexp.getName();
582: if (decl != null) {
583: name = decl.getName();
584: exp = decl.getValue();
585: if (decl.isAlias() && exp instanceof QuoteExp) {
586: Object val = ((QuoteExp) exp).getValue();
587: if (val instanceof Location) {
588: Location loc = (Location) val;
589: if (loc.isBound())
590: return asType(loc.get());
591: if (!(loc instanceof Named))
592: return null;
593: name = ((Named) loc).getName();
594: }
595: } else if (!decl.getFlag(Declaration.IS_UNKNOWN))
596: return getTypeFor(exp, lenient);
597: }
598: Object val = getEnvironment().get(name);
599: if (val instanceof Type)
600: return (Type) val;
601: if (val instanceof ClassNamespace)
602: return ((ClassNamespace) val).getClassType();
603: int len = name.length();
604: if (len > 2 && name.charAt(0) == '<'
605: && name.charAt(len - 1) == '>')
606: return getTypeFor(name.substring(1, len - 1));
607: } else if (exp instanceof ClassExp || exp instanceof ModuleExp) {
608: return ((LambdaExp) exp).getType();
609: }
610: return null;
611: }
612:
613: public Declaration declFromField(ModuleExp mod, Object fvalue,
614: Field fld) {
615: String fname = fld.getName();
616: Type ftype = fld.getType();
617: boolean isAlias = ftype.isSubtype(Compilation.typeLocation);
618: Object fdname;
619: // FIXME if fvalue is FieldLocation, and field is final,
620: // get name from value of field.
621: boolean isImportedInstance;
622: boolean externalAccess = false;
623: if ((isImportedInstance = fname.endsWith("$instance")))
624: fdname = fname;
625: else if (fvalue instanceof Named) // && ! isAlias
626: fdname = ((Named) fvalue).getSymbol();
627: else {
628: // FIXME move this to demangleName
629: if (fname.startsWith(Declaration.PRIVATE_PREFIX)) {
630: externalAccess = true;
631: fname = fname.substring(Declaration.PRIVATE_PREFIX
632: .length());
633: }
634: fdname = Compilation.demangleName(fname, true).intern();
635: }
636: Type dtype = isAlias ? Type.pointer_type : getTypeFor(ftype
637: .getReflectClass());
638: Declaration fdecl = mod.addDeclaration(fdname, dtype);
639: boolean isStatic = (fld.getModifiers() & Access.STATIC) != 0;
640: boolean isFinal = (fld.getModifiers() & Access.FINAL) != 0;
641: if (isAlias)
642: fdecl.setIndirectBinding(true);
643: else if (isFinal && ftype.isSubtype(Compilation.typeProcedure))
644: fdecl.setProcedureDecl(true);
645: if (isStatic)
646: fdecl.setFlag(Declaration.STATIC_SPECIFIED);
647: fdecl.field = fld;
648: if (isFinal && !isAlias) // FIXME? ok for location?
649: fdecl.setFlag(Declaration.IS_CONSTANT);
650: if (isImportedInstance)
651: fdecl.setFlag(Declaration.MODULE_REFERENCE);
652: fdecl.setSimple(false);
653: if (externalAccess)
654: fdecl.setFlag(Declaration.EXTERNAL_ACCESS
655: | Declaration.PRIVATE);
656: return fdecl;
657: }
658:
659: public static final int VALUE_NAMESPACE = 1 << 0;
660: public static final int FUNCTION_NAMESPACE = 1 << 1;
661: public static final int NAMESPACE_PREFIX_NAMESPACE = 1 << 2;
662:
663: /** Return the namespace (e.g value or function) of a Declaration.
664: * Return a bitmask of all the namespaces "covered" by the Declaration.
665: * Note this isn't a namespace in the XML sense; if a Declaration has
666: * a specific namespace URI, then that is part of its symbol.
667: * This namespace bitmap is a separate dimension, for the use of
668: * languages that have separate namespaces for different kinds of
669: * declarations, such as variables and functions.
670: */
671: public int getNamespaceOf(Declaration decl) {
672: return VALUE_NAMESPACE;
673: }
674:
675: /** True if a Declaration is in the specified namespace.
676: * @param namespace normally a bitmask as returned by getNamespaceOf. */
677: public boolean hasNamespace(Declaration decl, int namespace) {
678: return (getNamespaceOf(decl) & namespace) != 0;
679: }
680:
681: public void emitPushBoolean(boolean value, CodeAttr code) {
682: code.emitGetStatic(value ? Compilation.trueConstant
683: : Compilation.falseConstant);
684: }
685:
686: /** Generate code to test if an object is considered true.
687: * Assume the object has been pushed on the JVM stack.
688: * Generate code to push true or false as appropriate. */
689: public void emitCoerceToBoolean(CodeAttr code) {
690: emitPushBoolean(false, code);
691: code.emitIfNEq();
692: code.emitPushInt(1);
693: code.emitElse();
694: code.emitPushInt(0);
695: code.emitFi();
696: }
697:
698: public Object coerceFromObject(Class clas, Object obj) {
699: return getTypeFor(clas).coerceFromObject(obj);
700: }
701:
702: public Object coerceToObject(Class clas, Object obj) {
703: return getTypeFor(clas).coerceToObject(obj);
704: }
705:
706: public Object coerceToObject(int val) {
707: return gnu.math.IntNum.make(val);
708: }
709:
710: public static synchronized void setDefaults(Language lang) {
711: Language.setDefaultLanguage(lang);
712: current.setGlobal(lang);
713: // Assuming this is the initial (main) thread, make it's Environment
714: // the default (global) one, so child threads can inherit from it.
715: // Thus command-line definitions etc get inherited.
716: if (Environment.getGlobal() == BuiltinEnvironment.getInstance())
717: Environment.setGlobal(Environment.getCurrent());
718: }
719:
720: public Procedure getPrompter() {
721: Object property = null;
722: if (hasSeparateFunctionNamespace())
723: property = EnvironmentKey.FUNCTION;
724: Procedure prompter = (Procedure) getEnvironment().get(
725: getSymbol("default-prompter"), property, null);
726: if (prompter != null)
727: return prompter;
728: else
729: return new SimplePrompter();
730: }
731:
732: /** Return the result of evaluating a string as a source expression. */
733: public final Object eval(String string) throws Throwable {
734: return eval(new CharArrayInPort(string));
735: }
736:
737: /** Evaluate expression(s) read from a Reader.
738: * This just calls eval(InPort).
739: */
740: public final Object eval(Reader in) throws Throwable {
741: return eval(in instanceof InPort ? (InPort) in : new InPort(in));
742: }
743:
744: /** Evaluate expression(s) read from an InPort. */
745: public final Object eval(InPort port) throws Throwable {
746: CallContext ctx = CallContext.getInstance();
747: int oldIndex = ctx.startFromContext();
748: try {
749: eval(port, ctx);
750: return ctx.getFromContext(oldIndex);
751: } catch (Throwable ex) {
752: ctx.cleanupFromContext(oldIndex);
753: throw ex;
754: }
755: }
756:
757: /** Evaluate a string and write the result value(s) on a Writer. */
758: public final void eval(String string, Writer out) throws Throwable {
759: eval(new CharArrayInPort(string), out);
760: }
761:
762: /** Evaluate a string and write the result value(s) to a PrintConsumer.
763: * This is to disambiguate calls using OutPort or XMLPrinter,
764: * which are both Writer and Consumer. */
765: public final void eval(String string, PrintConsumer out)
766: throws Throwable {
767: eval(string, getOutputConsumer(out));
768: }
769:
770: /** Evaluate a string and write the result value(s) to a Consumer. */
771: public final void eval(String string, Consumer out)
772: throws Throwable {
773: eval(new CharArrayInPort(string), out);
774: }
775:
776: /** Read expressions from a Reader and write the result to a Writer. */
777: public final void eval(Reader in, Writer out) throws Throwable {
778: eval(in, getOutputConsumer(out));
779: }
780:
781: /** Read expressions from a Reader and write the result to a Consumer. */
782: public void eval(Reader in, Consumer out) throws Throwable {
783: InPort port = in instanceof InPort ? (InPort) in : new InPort(
784: in);
785: CallContext ctx = CallContext.getInstance();
786: Consumer save = ctx.consumer;
787: try {
788: ctx.consumer = out;
789: eval(port, ctx);
790: } finally {
791: ctx.consumer = save;
792: }
793: }
794:
795: public void eval(InPort port, CallContext ctx) throws Throwable {
796: SourceMessages messages = new SourceMessages();
797: Language saveLang = getDefaultLanguage();
798: setDefaultLanguage(this );
799: try {
800: Compilation comp = parse(port, messages, PARSE_IMMEDIATE);
801: ModuleExp.evalModule(getEnvironment(), ctx, comp, null,
802: null);
803: } finally {
804: setDefaultLanguage(saveLang);
805: }
806: if (messages.seenErrors())
807: throw new RuntimeException("invalid syntax in eval form:\n"
808: + messages.toString(20));
809: }
810:
811: static protected int env_counter = 0;
812:
813: public void runAsApplication(String[] args) {
814: setDefaults(this );
815: kawa.repl.main(args);
816: }
817: }
818:
819: class SimplePrompter extends Procedure1 {
820: public String prefix = "[";
821: public String suffix = "] ";
822:
823: public Object apply1(Object arg) {
824: if (arg instanceof InPort) {
825: InPort port = (InPort) arg;
826: int line = port.getLineNumber() + 1;
827: if (line >= 0)
828: return prefix + line + suffix;
829: }
830: return suffix;
831: }
832:
833: // The compiler finds registerEnvironment by using reflection.
834: //
835: // public static void registerEnvironment()
836: // { Environment.setGlobal(new ...().getEnvironment()); }
837: }
|