LispLanguage.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. // Copyright (c) 2001, 2004, 2005, 2012 Per M.A. Bothner
  2. // This is free software; for terms and warranty disclaimer see ./COPYING.
  3. package gnu.kawa.lispexpr;
  4. import gnu.expr.*;
  5. import gnu.mapping.*;
  6. import gnu.lists.*;
  7. import gnu.bytecode.*;
  8. import gnu.mapping.EnvironmentKey;
  9. import gnu.kawa.io.InPort;
  10. import gnu.kawa.io.TtyInPort;
  11. import gnu.kawa.reflect.StaticFieldLocation;
  12. import gnu.text.Lexer;
  13. import gnu.text.SourceMessages;
  14. import java.util.HashMap;
  15. import kawa.lang.Translator; // FIXME
  16. import kawa.lang.Syntax; // FIXME
  17. /** Language sub-class for Lisp-like languages (including Scheme). */
  18. public abstract class LispLanguage extends Language
  19. {
  20. static public final String quote_str = "quote";
  21. static public final String unquote_str = "unquote";
  22. static public final String unquotesplicing_str = "unquote-splicing";
  23. static public final String quasiquote_str = "quasiquote";
  24. public static final Symbol quasiquote_sym =
  25. Namespace.EmptyNamespace.getSymbol(quasiquote_str);
  26. public static final SimpleSymbol dots3_sym = Symbol.valueOf("...");
  27. static public final String splice_str = "$splice$";
  28. static public final Symbol splice_sym = Namespace.EmptyNamespace.getSymbol(splice_str);
  29. static public final String splice_colon_str = "$splice-colon$";
  30. static public final Symbol splice_colon_sym = Namespace.EmptyNamespace.getSymbol(splice_colon_str);
  31. /** Used for Kawa infix ':' operator. */
  32. static public final Symbol lookup_sym = Namespace.EmptyNamespace.getSymbol("$lookup$");
  33. // FUTURE: Used for: [ e1 e2 ... ]
  34. // for future sequence/list constructors.
  35. static public final Symbol bracket_list_sym = Namespace.EmptyNamespace.getSymbol("$bracket-list$");
  36. // FUTURE: Used for: name[ e1 e2 ... ]
  37. // Needed for array types - e.g. Object[]
  38. // and (possible future) parameterized types - e.g. java.util.List[integer]
  39. static public final Symbol bracket_apply_sym = Namespace.EmptyNamespace.getSymbol("$bracket-apply$");
  40. public static StaticFieldLocation getNamedPartLocation =
  41. new StaticFieldLocation("gnu.kawa.functions.GetNamedPart", "getNamedPart");
  42. static { getNamedPartLocation.setProcedure(); }
  43. /**
  44. * The unit namespace contains the bindings for symbols such as `cm',
  45. * `s', etc.
  46. */
  47. public static final Namespace unitNamespace =
  48. Namespace.valueOf("http://kawa.gnu.org/unit", "unit");
  49. public static final Namespace constructNamespace =
  50. Namespace.valueOf("http://kawa.gnu.org/construct", "$construct$");
  51. public static final Namespace entityNamespace =
  52. Namespace.valueOf("http://kawa.gnu.org/entity", "$entity$");
  53. /** The default <code>ReadTable</code> for this language. */
  54. protected ReadTable defaultReadTable;
  55. /** Create a fresh <code>ReadTable</code> appropriate for this language. */
  56. public abstract ReadTable createReadTable ();
  57. public LispReader getLexer(InPort inp, SourceMessages messages)
  58. {
  59. return new LispReader(inp, messages);
  60. }
  61. public String getCompilationClass () { return "kawa.lang.Translator"; }
  62. public boolean parse (Compilation comp, int options)
  63. throws java.io.IOException, gnu.text.SyntaxException
  64. {
  65. kawa.lang.Translator tr = (kawa.lang.Translator) comp;
  66. Lexer lexer = tr.lexer;
  67. ModuleExp mexp = tr.getModule();
  68. LispReader reader = (LispReader) lexer;
  69. Compilation saveComp = Compilation.setSaveCurrent(tr);
  70. InPort in = reader == null ? null : reader.getPort();
  71. if (in instanceof TtyInPort)
  72. ((TtyInPort) in).resetAndKeep();
  73. try
  74. {
  75. if (tr.pendingForm != null)
  76. {
  77. tr.scanForm(tr.pendingForm, mexp);
  78. tr.pendingForm = null;
  79. }
  80. for (;;)
  81. {
  82. if (reader == null)
  83. break;
  84. Object sexp = reader.readCommand();
  85. // A literal unquoted #!eof
  86. if (Translator.listLength(sexp) == 2
  87. && Translator.safeCar(sexp) == kawa.standard.begin.begin
  88. && Translator.safeCar(Translator.safeCdr(sexp)) == Sequence.eofValue
  89. && (options & (PARSE_ONE_LINE|PARSE_INTERACTIVE_MODULE)) != 0) {
  90. return false;
  91. }
  92. if (sexp == Sequence.eofValue)
  93. {
  94. if ((options & PARSE_ONE_LINE) != 0)
  95. return false; // FIXME
  96. break;
  97. }
  98. int ch;
  99. do { ch = lexer.read(); }
  100. while (ch == ' ' || ch == '\t'|| ch == '\r');
  101. if (ch == ')')
  102. lexer.fatal("An unexpected close paren was read.");
  103. if (ch != '\n')
  104. lexer.unread(ch);
  105. tr.scanForm(sexp, mexp);
  106. if ((options & PARSE_ONE_LINE) != 0)
  107. {
  108. // In a REPL we want to read all the forms until EOL.
  109. // One reason is in case an expression reads from stdin,
  110. // in which case we want to separate that.
  111. // Another reason to be consistent when a UI gives
  112. // a multi-line block.
  113. if (ch < 0 || ch == '\n' || ! lexer.isInteractive())
  114. break;
  115. }
  116. else if ((options & PARSE_PROLOG) != 0
  117. && tr.getState() >= Compilation.PROLOG_PARSED)
  118. {
  119. return true;
  120. }
  121. }
  122. // Must be done before any other module imports this module.
  123. tr.finishModule(mexp);
  124. tr.setState(Compilation.BODY_PARSED);
  125. }
  126. finally
  127. {
  128. if (in instanceof TtyInPort)
  129. ((TtyInPort) in).setKeepAll(false);
  130. Compilation.restoreCurrent(saveComp);
  131. }
  132. return true;
  133. }
  134. /** Resolve names and other post-parsing processing. */
  135. public void resolve (Compilation comp)
  136. {
  137. Translator tr = (Translator) comp;
  138. ModuleExp mexp = tr.getModule();
  139. tr.resolveModule(mexp);
  140. if (tr.subModuleMap != null && tr.mainClass != null) {
  141. String mainName = tr.mainClass.getName();
  142. ModuleInfo subinfo = tr.subModuleMap.get(mainName);
  143. if (subinfo != null
  144. && ! (mexp.body == QuoteExp.voidExp && mexp.firstDecl() == null)) {
  145. ModuleExp submodule = subinfo.getModuleExpRaw();
  146. tr.error('e', "module has both statements and a submodule with the same name: "+tr.mainClass.getName(),
  147. submodule != null ? submodule : mexp);
  148. }
  149. }
  150. }
  151. public Declaration declFromField (ModuleExp mod, Object fvalue, Field fld)
  152. {
  153. Declaration fdecl = super.declFromField(mod, fvalue, fld);
  154. boolean isFinal = (fld.getModifiers() & Access.FINAL) != 0;
  155. if (isFinal && fvalue instanceof Syntax) // FIXME - should check type? not value?
  156. fdecl.setSyntax();
  157. return fdecl;
  158. }
  159. /** Declare in the current Environment a Syntax bound to a static field.
  160. * @param name the procedure's source-level name.
  161. * @param cname the name of the class containing the field.
  162. * @param fname the name of the field, which should be a static
  163. * final field whose type extends kawa.lang.Syntax.
  164. */
  165. protected void defSntxStFld(String name, String cname, String fname)
  166. {
  167. Object property
  168. = hasSeparateFunctionNamespace() ? EnvironmentKey.FUNCTION : null;
  169. StaticFieldLocation loc =
  170. StaticFieldLocation.define(environ, environ.getSymbol(name), property,
  171. cname, fname);
  172. loc.setSyntax();
  173. }
  174. protected void defSntxStFld(String name, String cname)
  175. {
  176. defSntxStFld(name, cname, Mangling.mangleField(name));
  177. }
  178. /**
  179. * Are keywords self-evaluating?
  180. * True in CommonLisp. Used to be true for Scheme also, but now
  181. * in Scheme literal keywords should only be used for keyword arguments;
  182. * if you want a Keyword value if should be quoted.
  183. * @return true if we should treat keywords as self-evaluating.
  184. */
  185. public boolean keywordsAreSelfEvaluating() { return true; }
  186. public boolean selfEvaluatingSymbol (Object obj)
  187. {
  188. // FUTURE: return keywordsAreSelfEvaluating() && obj instanceof Keyword;
  189. return obj instanceof Keyword;
  190. }
  191. /** Convert the Language's idea of a symbol to a gnu.mapping.Symbol. */
  192. public static Symbol langSymbolToSymbol (Object sym)
  193. {
  194. return ((LispLanguage) Language.getDefaultLanguage()).fromLangSymbol(sym);
  195. }
  196. protected Symbol fromLangSymbol (Object sym)
  197. {
  198. if (sym instanceof String)
  199. return getSymbol((String) sym);
  200. return (Symbol) sym;
  201. }
  202. /** The types common to Lisp-like languages. */
  203. private HashMap<String,Type> types;
  204. /** The string representations of Lisp-like types. */
  205. private HashMap<Type,String> typeToStringMap;
  206. protected synchronized HashMap<String, Type> getTypeMap () {
  207. if (types == null) {
  208. types = new HashMap<String, Type>(64); // Plently of space.
  209. types.put("void", LangPrimType.voidType);
  210. types.put("int", LangPrimType.intType);
  211. types.put("char", LangPrimType.charType);
  212. types.put("character", LangPrimType.characterType);
  213. types.put("character-or-eof", LangPrimType.characterOrEofType);
  214. types.put("byte", LangPrimType.byteType);
  215. types.put("short", LangPrimType.shortType);
  216. types.put("long", LangPrimType.longType);
  217. types.put("float", LangPrimType.floatType);
  218. types.put("double", LangPrimType.doubleType);
  219. types.put("ubyte", LangPrimType.unsignedByteType);
  220. types.put("ushort", LangPrimType.unsignedShortType);
  221. types.put("uint", LangPrimType.unsignedIntType);
  222. types.put("ulong", LangPrimType.unsignedLongType);
  223. types.put("never-returns", Type.neverReturnsType);
  224. types.put("dynamic", LangObjType.dynamicType);
  225. types.put("Object", Type.objectType);
  226. types.put("String", Type.toStringType);
  227. types.put("arglist", LangObjType.argListType);
  228. types.put("argvector", LangObjType.argVectorType);
  229. types.put("object", Type.objectType);
  230. types.put("number", LangObjType.numericType);
  231. types.put("quantity", ClassType.make("gnu.math.Quantity"));
  232. types.put("complex", ClassType.make("gnu.math.Complex"));
  233. types.put("real", LangObjType.realType);
  234. types.put("rational", LangObjType.rationalType);
  235. types.put("integer", LangObjType.integerType);
  236. types.put("symbol", ClassType.make("gnu.mapping.Symbol"));
  237. types.put("simple-symbol", ClassType.make("gnu.mapping.SimpleSymbol"));
  238. types.put("namespace", ClassType.make("gnu.mapping.Namespace"));
  239. types.put("keyword", ClassType.make("gnu.expr.Keyword"));
  240. types.put("pair", ClassType.make("gnu.lists.Pair"));
  241. types.put("pair-with-position",
  242. ClassType.make("gnu.lists.PairWithPosition"));
  243. // FIXME should be UNION(java.lang.String, gnu.lists.IString)
  244. types.put("constant-string", ClassType.make("java.lang.CharSequence"));
  245. types.put("abstract-string", ClassType.make("gnu.lists.CharSeq"));
  246. types.put("vector", LangObjType.vectorType);
  247. types.put("gvector", LangObjType.gvectorType);
  248. types.put("string", LangObjType.stringType);
  249. types.put("empty-list", ClassType.make("gnu.lists.EmptyList"));
  250. types.put("sequence", LangObjType.sequenceType);
  251. types.put("list", LangObjType.listType);
  252. types.put("function", ClassType.make("gnu.mapping.Procedure"));
  253. types.put("procedure", LangObjType.procedureType);
  254. types.put("input-port", ClassType.make("gnu.kawa.io.InPort"));
  255. types.put("output-port", ClassType.make("gnu.kawa.io.OutPort"));
  256. types.put("string-output-port",
  257. ClassType.make("gnu.kawa.io.CharArrayOutPort"));
  258. types.put("string-input-port",
  259. ClassType.make("gnu.kawa.io.CharArrayInPort"));
  260. types.put("record", ClassType.make("kawa.lang.Record"));
  261. types.put("type", LangObjType.typeType);
  262. types.put("class-type", LangObjType.typeClassType);
  263. types.put("class", LangObjType.typeClass);
  264. types.put("promise", LangObjType.promiseType);
  265. types.put("document", ClassType.make("gnu.kawa.xml.KDocument"));
  266. types.put("readtable",
  267. ClassType.make("gnu.kawa.lispexpr.ReadTable"));
  268. types.put("string-cursor", LangPrimType.stringCursorType);
  269. }
  270. return types;
  271. }
  272. /**
  273. * Try to get a type of the form lang:type.
  274. *
  275. * E.g. elisp:buffer.
  276. *
  277. * @param name The package-style type name as a string.
  278. * @return null if no such type could be found, or the corresponding
  279. * {@code Type}.
  280. */
  281. public Type getPackageStyleType(String name) {
  282. int colon = name.indexOf(':');
  283. if (colon > 0) {
  284. String lang = name.substring(0, colon);
  285. Language interp = Language.getInstance(lang);
  286. if (interp == null)
  287. throw new RuntimeException("unknown type '" + name
  288. + "' - unknown language '" + lang + '\'');
  289. Type type = interp.getNamedType(name.substring(colon + 1));
  290. if (type != null)
  291. types.put(name, type);
  292. return type;
  293. }
  294. return null;
  295. }
  296. public static Type decodeArrayType(String name) {
  297. int nlen = name.length();
  298. if (nlen == 5)
  299. return GenArrayType.generalInstance;
  300. try {
  301. int rank = Integer.parseInt(name.substring(5));
  302. if (rank >= 0)
  303. return new GenArrayType(rank, Type.objectType);
  304. } catch (Throwable ex) {
  305. }
  306. return null;
  307. }
  308. @Override
  309. // FIXME: getNamedType is over-specialised....
  310. public Type getNamedType (String name) {
  311. // Initialise the type map if necessary.
  312. Type type = getTypeMap().get(name);
  313. if (type == null && name.startsWith("array"))
  314. return decodeArrayType(name);
  315. return (type != null) ? type : getPackageStyleType(name);
  316. }
  317. public Type getTypeFor (Object spec, boolean lenient) {
  318. if (spec == String.class)
  319. return LangObjType.jstringType;
  320. else
  321. return super.getTypeFor(spec, lenient);
  322. }
  323. public Type getTypeFor(Class clas) {
  324. String name = clas.getName();
  325. if (clas.isPrimitive())
  326. return getNamedType(name);
  327. if (clas.isArray())
  328. return ArrayType.make(getTypeFor(clas.getComponentType()));
  329. /* #ifdef JAVA7 */
  330. ; // FIXME - FUTURE: Use a switch with string keys.
  331. /* #endif */
  332. if ("java.lang.String".equals(name)) // ???
  333. return LangObjType.jstringType;
  334. Type t = LangObjType.getInstanceFromClass(name);
  335. if (t != null)
  336. return t;
  337. return super.getTypeFor(clas);
  338. }
  339. @Override
  340. public String getPrimaryPrompt() { return "#|kawa:%N|# "; }
  341. @Override
  342. public String getSecondaryPrompt() { return "#|%P.%N|# "; }
  343. }