123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- // Copyright (c) 2001, 2004, 2005, 2012 Per M.A. Bothner
- // This is free software; for terms and warranty disclaimer see ./COPYING.
- package gnu.kawa.lispexpr;
- import gnu.expr.*;
- import gnu.mapping.*;
- import gnu.lists.*;
- import gnu.bytecode.*;
- import gnu.mapping.EnvironmentKey;
- import gnu.kawa.io.InPort;
- import gnu.kawa.io.TtyInPort;
- import gnu.kawa.reflect.StaticFieldLocation;
- import gnu.text.Lexer;
- import gnu.text.SourceMessages;
- import java.util.HashMap;
- import kawa.lang.Translator; // FIXME
- import kawa.lang.Syntax; // FIXME
- /** Language sub-class for Lisp-like languages (including Scheme). */
- public abstract class LispLanguage extends Language
- {
- static public final String quote_str = "quote";
- static public final String unquote_str = "unquote";
- static public final String unquotesplicing_str = "unquote-splicing";
- static public final String quasiquote_str = "quasiquote";
- public static final Symbol quasiquote_sym =
- Namespace.EmptyNamespace.getSymbol(quasiquote_str);
- public static final SimpleSymbol dots3_sym = Symbol.valueOf("...");
- static public final String splice_str = "$splice$";
- static public final Symbol splice_sym = Namespace.EmptyNamespace.getSymbol(splice_str);
- static public final String splice_colon_str = "$splice-colon$";
- static public final Symbol splice_colon_sym = Namespace.EmptyNamespace.getSymbol(splice_colon_str);
- /** Used for Kawa infix ':' operator. */
- static public final Symbol lookup_sym = Namespace.EmptyNamespace.getSymbol("$lookup$");
- // FUTURE: Used for: [ e1 e2 ... ]
- // for future sequence/list constructors.
- static public final Symbol bracket_list_sym = Namespace.EmptyNamespace.getSymbol("$bracket-list$");
- // FUTURE: Used for: name[ e1 e2 ... ]
- // Needed for array types - e.g. Object[]
- // and (possible future) parameterized types - e.g. java.util.List[integer]
- static public final Symbol bracket_apply_sym = Namespace.EmptyNamespace.getSymbol("$bracket-apply$");
- public static StaticFieldLocation getNamedPartLocation =
- new StaticFieldLocation("gnu.kawa.functions.GetNamedPart", "getNamedPart");
- static { getNamedPartLocation.setProcedure(); }
-
- /**
- * The unit namespace contains the bindings for symbols such as `cm',
- * `s', etc.
- */
- public static final Namespace unitNamespace =
- Namespace.valueOf("http://kawa.gnu.org/unit", "unit");
- public static final Namespace constructNamespace =
- Namespace.valueOf("http://kawa.gnu.org/construct", "$construct$");
- public static final Namespace entityNamespace =
- Namespace.valueOf("http://kawa.gnu.org/entity", "$entity$");
- /** The default <code>ReadTable</code> for this language. */
- protected ReadTable defaultReadTable;
- /** Create a fresh <code>ReadTable</code> appropriate for this language. */
- public abstract ReadTable createReadTable ();
- public LispReader getLexer(InPort inp, SourceMessages messages)
- {
- return new LispReader(inp, messages);
- }
- public String getCompilationClass () { return "kawa.lang.Translator"; }
- public boolean parse (Compilation comp, int options)
- throws java.io.IOException, gnu.text.SyntaxException
- {
- kawa.lang.Translator tr = (kawa.lang.Translator) comp;
- Lexer lexer = tr.lexer;
- ModuleExp mexp = tr.getModule();
- LispReader reader = (LispReader) lexer;
- Compilation saveComp = Compilation.setSaveCurrent(tr);
- InPort in = reader == null ? null : reader.getPort();
- if (in instanceof TtyInPort)
- ((TtyInPort) in).resetAndKeep();
- try
- {
- if (tr.pendingForm != null)
- {
- tr.scanForm(tr.pendingForm, mexp);
- tr.pendingForm = null;
- }
- for (;;)
- {
- if (reader == null)
- break;
- Object sexp = reader.readCommand();
- // A literal unquoted #!eof
- if (Translator.listLength(sexp) == 2
- && Translator.safeCar(sexp) == kawa.standard.begin.begin
- && Translator.safeCar(Translator.safeCdr(sexp)) == Sequence.eofValue
- && (options & (PARSE_ONE_LINE|PARSE_INTERACTIVE_MODULE)) != 0) {
- return false;
- }
- if (sexp == Sequence.eofValue)
- {
- if ((options & PARSE_ONE_LINE) != 0)
- return false; // FIXME
- break;
- }
- int ch;
- do { ch = lexer.read(); }
- while (ch == ' ' || ch == '\t'|| ch == '\r');
- if (ch == ')')
- lexer.fatal("An unexpected close paren was read.");
- if (ch != '\n')
- lexer.unread(ch);
- tr.scanForm(sexp, mexp);
- if ((options & PARSE_ONE_LINE) != 0)
- {
- // In a REPL we want to read all the forms until EOL.
- // One reason is in case an expression reads from stdin,
- // in which case we want to separate that.
- // Another reason to be consistent when a UI gives
- // a multi-line block.
- if (ch < 0 || ch == '\n' || ! lexer.isInteractive())
- break;
- }
- else if ((options & PARSE_PROLOG) != 0
- && tr.getState() >= Compilation.PROLOG_PARSED)
- {
- return true;
- }
- }
- // Must be done before any other module imports this module.
- tr.finishModule(mexp);
- tr.setState(Compilation.BODY_PARSED);
- }
- finally
- {
- if (in instanceof TtyInPort)
- ((TtyInPort) in).setKeepAll(false);
- Compilation.restoreCurrent(saveComp);
- }
- return true;
- }
- /** Resolve names and other post-parsing processing. */
- public void resolve (Compilation comp)
- {
- Translator tr = (Translator) comp;
- ModuleExp mexp = tr.getModule();
- tr.resolveModule(mexp);
- if (tr.subModuleMap != null && tr.mainClass != null) {
- String mainName = tr.mainClass.getName();
- ModuleInfo subinfo = tr.subModuleMap.get(mainName);
- if (subinfo != null
- && ! (mexp.body == QuoteExp.voidExp && mexp.firstDecl() == null)) {
- ModuleExp submodule = subinfo.getModuleExpRaw();
- tr.error('e', "module has both statements and a submodule with the same name: "+tr.mainClass.getName(),
- submodule != null ? submodule : mexp);
- }
- }
- }
- public Declaration declFromField (ModuleExp mod, Object fvalue, Field fld)
- {
- Declaration fdecl = super.declFromField(mod, fvalue, fld);
- boolean isFinal = (fld.getModifiers() & Access.FINAL) != 0;
- if (isFinal && fvalue instanceof Syntax) // FIXME - should check type? not value?
- fdecl.setSyntax();
- return fdecl;
- }
- /** Declare in the current Environment a Syntax bound to a static field.
- * @param name the procedure's source-level name.
- * @param cname the name of the class containing the field.
- * @param fname the name of the field, which should be a static
- * final field whose type extends kawa.lang.Syntax.
- */
- protected void defSntxStFld(String name, String cname, String fname)
- {
- Object property
- = hasSeparateFunctionNamespace() ? EnvironmentKey.FUNCTION : null;
- StaticFieldLocation loc =
- StaticFieldLocation.define(environ, environ.getSymbol(name), property,
- cname, fname);
- loc.setSyntax();
- }
- protected void defSntxStFld(String name, String cname)
- {
- defSntxStFld(name, cname, Mangling.mangleField(name));
- }
- /**
- * Are keywords self-evaluating?
- * True in CommonLisp. Used to be true for Scheme also, but now
- * in Scheme literal keywords should only be used for keyword arguments;
- * if you want a Keyword value if should be quoted.
- * @return true if we should treat keywords as self-evaluating.
- */
- public boolean keywordsAreSelfEvaluating() { return true; }
- public boolean selfEvaluatingSymbol (Object obj)
- {
- // FUTURE: return keywordsAreSelfEvaluating() && obj instanceof Keyword;
- return obj instanceof Keyword;
- }
- /** Convert the Language's idea of a symbol to a gnu.mapping.Symbol. */
- public static Symbol langSymbolToSymbol (Object sym)
- {
- return ((LispLanguage) Language.getDefaultLanguage()).fromLangSymbol(sym);
- }
- protected Symbol fromLangSymbol (Object sym)
- {
- if (sym instanceof String)
- return getSymbol((String) sym);
- return (Symbol) sym;
- }
- /** The types common to Lisp-like languages. */
- private HashMap<String,Type> types;
- /** The string representations of Lisp-like types. */
- private HashMap<Type,String> typeToStringMap;
- protected synchronized HashMap<String, Type> getTypeMap () {
- if (types == null) {
- types = new HashMap<String, Type>(64); // Plently of space.
- types.put("void", LangPrimType.voidType);
- types.put("int", LangPrimType.intType);
- types.put("char", LangPrimType.charType);
- types.put("character", LangPrimType.characterType);
- types.put("character-or-eof", LangPrimType.characterOrEofType);
- types.put("byte", LangPrimType.byteType);
- types.put("short", LangPrimType.shortType);
- types.put("long", LangPrimType.longType);
- types.put("float", LangPrimType.floatType);
- types.put("double", LangPrimType.doubleType);
- types.put("ubyte", LangPrimType.unsignedByteType);
- types.put("ushort", LangPrimType.unsignedShortType);
- types.put("uint", LangPrimType.unsignedIntType);
- types.put("ulong", LangPrimType.unsignedLongType);
- types.put("never-returns", Type.neverReturnsType);
- types.put("dynamic", LangObjType.dynamicType);
- types.put("Object", Type.objectType);
- types.put("String", Type.toStringType);
- types.put("arglist", LangObjType.argListType);
- types.put("argvector", LangObjType.argVectorType);
- types.put("object", Type.objectType);
- types.put("number", LangObjType.numericType);
- types.put("quantity", ClassType.make("gnu.math.Quantity"));
- types.put("complex", ClassType.make("gnu.math.Complex"));
- types.put("real", LangObjType.realType);
- types.put("rational", LangObjType.rationalType);
- types.put("integer", LangObjType.integerType);
- types.put("symbol", ClassType.make("gnu.mapping.Symbol"));
- types.put("simple-symbol", ClassType.make("gnu.mapping.SimpleSymbol"));
- types.put("namespace", ClassType.make("gnu.mapping.Namespace"));
- types.put("keyword", ClassType.make("gnu.expr.Keyword"));
- types.put("pair", ClassType.make("gnu.lists.Pair"));
- types.put("pair-with-position",
- ClassType.make("gnu.lists.PairWithPosition"));
- // FIXME should be UNION(java.lang.String, gnu.lists.IString)
- types.put("constant-string", ClassType.make("java.lang.CharSequence"));
- types.put("abstract-string", ClassType.make("gnu.lists.CharSeq"));
- types.put("vector", LangObjType.vectorType);
- types.put("gvector", LangObjType.gvectorType);
- types.put("string", LangObjType.stringType);
- types.put("empty-list", ClassType.make("gnu.lists.EmptyList"));
- types.put("sequence", LangObjType.sequenceType);
- types.put("list", LangObjType.listType);
- types.put("function", ClassType.make("gnu.mapping.Procedure"));
- types.put("procedure", LangObjType.procedureType);
- types.put("input-port", ClassType.make("gnu.kawa.io.InPort"));
- types.put("output-port", ClassType.make("gnu.kawa.io.OutPort"));
- types.put("string-output-port",
- ClassType.make("gnu.kawa.io.CharArrayOutPort"));
- types.put("string-input-port",
- ClassType.make("gnu.kawa.io.CharArrayInPort"));
- types.put("record", ClassType.make("kawa.lang.Record"));
- types.put("type", LangObjType.typeType);
- types.put("class-type", LangObjType.typeClassType);
- types.put("class", LangObjType.typeClass);
- types.put("promise", LangObjType.promiseType);
- types.put("document", ClassType.make("gnu.kawa.xml.KDocument"));
- types.put("readtable",
- ClassType.make("gnu.kawa.lispexpr.ReadTable"));
- types.put("string-cursor", LangPrimType.stringCursorType);
- }
- return types;
- }
- /**
- * Try to get a type of the form lang:type.
- *
- * E.g. elisp:buffer.
- *
- * @param name The package-style type name as a string.
- * @return null if no such type could be found, or the corresponding
- * {@code Type}.
- */
- public Type getPackageStyleType(String name) {
- int colon = name.indexOf(':');
- if (colon > 0) {
- String lang = name.substring(0, colon);
- Language interp = Language.getInstance(lang);
- if (interp == null)
- throw new RuntimeException("unknown type '" + name
- + "' - unknown language '" + lang + '\'');
- Type type = interp.getNamedType(name.substring(colon + 1));
- if (type != null)
- types.put(name, type);
- return type;
- }
- return null;
- }
- public static Type decodeArrayType(String name) {
- int nlen = name.length();
- if (nlen == 5)
- return GenArrayType.generalInstance;
- try {
- int rank = Integer.parseInt(name.substring(5));
- if (rank >= 0)
- return new GenArrayType(rank, Type.objectType);
- } catch (Throwable ex) {
- }
- return null;
- }
- @Override
- // FIXME: getNamedType is over-specialised....
- public Type getNamedType (String name) {
- // Initialise the type map if necessary.
- Type type = getTypeMap().get(name);
- if (type == null && name.startsWith("array"))
- return decodeArrayType(name);
- return (type != null) ? type : getPackageStyleType(name);
- }
- public Type getTypeFor (Object spec, boolean lenient) {
- if (spec == String.class)
- return LangObjType.jstringType;
- else
- return super.getTypeFor(spec, lenient);
- }
- public Type getTypeFor(Class clas) {
- String name = clas.getName();
- if (clas.isPrimitive())
- return getNamedType(name);
- if (clas.isArray())
- return ArrayType.make(getTypeFor(clas.getComponentType()));
- /* #ifdef JAVA7 */
- ; // FIXME - FUTURE: Use a switch with string keys.
- /* #endif */
- if ("java.lang.String".equals(name)) // ???
- return LangObjType.jstringType;
- Type t = LangObjType.getInstanceFromClass(name);
- if (t != null)
- return t;
- return super.getTypeFor(clas);
- }
- @Override
- public String getPrimaryPrompt() { return "#|kawa:%N|# "; }
- @Override
- public String getSecondaryPrompt() { return "#|%P.%N|# "; }
- }
|