Quote.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. package kawa.lang;
  2. import java.util.*;
  3. import gnu.mapping.*;
  4. import gnu.expr.*;
  5. import gnu.lists.*;
  6. import gnu.kawa.reflect.Invoke;
  7. import gnu.bytecode.ClassType;
  8. import gnu.bytecode.Method;
  9. import gnu.kawa.lispexpr.LispLanguage;
  10. import gnu.kawa.functions.CompileNamedPart;
  11. import gnu.kawa.functions.MakeSplice;
  12. /**
  13. * The Syntax transformer that re-writes the "quote" "quasiquote" primitive.
  14. * In both cases recursively resolves SyntaxForm wrappers and resolves
  15. * namespaces of symbols. In the case of quasiquote also handles unquoting.
  16. * @author Per Bothner
  17. */
  18. public class Quote extends Syntax {
  19. public static final Quote plainQuote = new Quote("quote", false);
  20. public static final Quote quasiQuote = new Quote("quasiquote", true);
  21. public Quote (String name, boolean isQuasi) {
  22. super(name);
  23. this.isQuasi = isQuasi;
  24. }
  25. protected boolean matchesUnquote(Pair pair, SyntaxForm syntax,
  26. Translator tr) {
  27. return tr.matches(pair.getCar(), syntax, LispLanguage.unquote_str);
  28. }
  29. protected boolean matchesUnquoteSplicing(Pair pair, SyntaxForm syntax,
  30. Translator tr) {
  31. return tr.matches(pair.getCar(), syntax, LispLanguage.unquotesplicing_str);
  32. }
  33. protected boolean matchesQuasiQuote(Object form, SyntaxForm syntax,
  34. Translator tr) {
  35. return tr.matches(form, syntax, LispLanguage.quasiquote_str);
  36. }
  37. /** An initial value for 'depth' for plain (non-quasi) quote. */
  38. protected static final int QUOTE_DEPTH = -1;
  39. /** True for quasiquote; false for plain quote. */
  40. protected boolean isQuasi;
  41. protected Object expand(Object template, int depth, Translator tr) {
  42. /* #ifdef use:java.util.IdentityHashMap */
  43. IdentityHashMap seen = new IdentityHashMap();
  44. /* #else */
  45. // Object seen = null;
  46. /* #endif */
  47. return expand(template, depth, null, seen, tr);
  48. }
  49. /** Quote an object (without namespace-expansion).
  50. * Basically just recursively removes SyntaxForm wrappers. */
  51. public static Object quote(Object obj, Translator tr) {
  52. return plainQuote.expand(obj, QUOTE_DEPTH, tr);
  53. }
  54. /** Quote an object (without namespace-expansion).
  55. * Basically just recursively removes SyntaxForm wrappers. */
  56. public static Object quote(Object obj) {
  57. return plainQuote.expand(obj, QUOTE_DEPTH, (Translator) Compilation.getCurrent());
  58. }
  59. protected Expression coerceExpression(Object val, Translator tr) {
  60. return val instanceof Expression ? (Expression) val : leaf(val, tr);
  61. }
  62. protected Expression leaf(Object val, Translator tr) {
  63. return new QuoteExp(val);
  64. }
  65. protected boolean expandColonForms() {
  66. return true;
  67. }
  68. public static Symbol makeSymbol(Namespace ns, Object local) {
  69. String name;
  70. /* #ifdef use:java.lang.CharSequence */
  71. if (local instanceof CharSequence)
  72. name = ((CharSequence) local).toString();
  73. /* #else */
  74. // if (local instanceof gnu.lists.CharSeq)
  75. // name = ((gnu.lists.CharSeq) local).toString();
  76. /* #endif */
  77. else
  78. name = (String) local;
  79. return ns.getSymbol(name.intern());
  80. }
  81. Object expand_pair(Pair list, int depth, SyntaxForm syntax,
  82. Object seen, Translator tr) {
  83. Pair pair = list;
  84. Object cdr;
  85. Object rest;
  86. for (;;) {
  87. // This would be simpler as plain recursion, but we try to iterate
  88. // over the given list, partly for speed, but more importantly
  89. // to avoid stack overflow in the case of long lists.
  90. rest = pair;
  91. Pair p1, p2;
  92. boolean isUnquote;
  93. // We're currently examining pair, which is the n'th cdr of list.
  94. // All previous elements (cars) are returned identically by expand.
  95. // What makes things complicated is that to the extent that no changes
  96. // are needed, we want to return the input list as-is.
  97. if (expandColonForms()
  98. && tr != null
  99. && pair == list
  100. && tr.matches(pair.getCar(), syntax, LispLanguage.lookup_sym)
  101. && pair.getCdr() instanceof Pair
  102. && (p1 = (Pair) pair.getCdr()) instanceof Pair
  103. && (p2 = (Pair) p1.getCdr()) instanceof Pair
  104. && p2.getCdr() == LList.Empty) {
  105. Expression part1 = tr.rewrite_car(p1, false);
  106. Expression part2 = tr.rewrite_car_for_lookup(p2);
  107. Namespace ns = tr.namespaceResolvePrefix(part1);
  108. Symbol sym = tr.namespaceResolve(ns, part2);
  109. if (sym != null)
  110. cdr = sym;
  111. else if (ns != null && depth == 1)
  112. cdr = new ApplyExp(quoteType.getDeclaredMethod("makeSymbol", 2),
  113. new Expression[] { QuoteExp.getInstance(ns),
  114. part2 });
  115. else if (p1.getCar() instanceof SimpleSymbol
  116. && part2 instanceof QuoteExp) {
  117. String s1 = ((QuoteExp) part2).getValue().toString();
  118. String s2 = p1.getCar().toString();
  119. cdr = Symbol.makeWithUnknownNamespace(s1, s2);
  120. } else {
  121. String combinedName
  122. = CompileNamedPart.combineName(part1, part2);
  123. if (combinedName != null)
  124. cdr = tr.getGlobalEnvironment().getSymbol(combinedName);
  125. else
  126. cdr = pair;
  127. }
  128. break;
  129. } else if (depth < 0) {
  130. } else if (matchesQuasiQuote(pair.getCar(), syntax, tr))
  131. depth++;
  132. else if ((isUnquote = matchesUnquote(pair, syntax, tr))
  133. || matchesUnquoteSplicing(pair, syntax, tr)) {
  134. depth--;
  135. Pair pair_cdr;
  136. if (! (pair.getCdr() instanceof Pair)
  137. || (pair_cdr = (Pair) pair.getCdr()).getCdr() != LList.Empty
  138. // Can't splice in cdr position (i.e. following dot).
  139. || (depth == 0 && ! isUnquote))
  140. return tr.syntaxError ("invalid used of " + pair.getCar() +
  141. " in quasiquote template");
  142. if (depth == 0) {
  143. cdr = tr.rewrite_car(pair_cdr, syntax);
  144. break;
  145. }
  146. }
  147. if (depth == 1 && pair.getCar() instanceof Pair) {
  148. Object form = pair.getCar();
  149. SyntaxForm subsyntax = syntax;
  150. while (form instanceof SyntaxForm) {
  151. subsyntax = (SyntaxForm) form;
  152. form = subsyntax.getDatum();
  153. }
  154. int splicing = -1;
  155. if (form instanceof Pair) {
  156. Pair pform = (Pair) form;
  157. if (matchesUnquote(pform, subsyntax, tr))
  158. splicing = 0;
  159. else if (matchesUnquoteSplicing(pform, subsyntax, tr))
  160. splicing = 1;
  161. }
  162. if (splicing >= 0) {
  163. form = ((Pair) form).getCdr(); // skip "unquote[splicing]".
  164. Vector vec = new Vector();
  165. cdr = null;
  166. // R5RS allows only a single argument. But
  167. // see Bawden: Quasiquotation in Lisp (1999), Appendix B.
  168. for (;;) {
  169. if (form instanceof SyntaxForm) {
  170. subsyntax = (SyntaxForm) form;
  171. form = subsyntax.getDatum();
  172. }
  173. if (form == LList.Empty)
  174. break;
  175. if (form instanceof Pair) {
  176. vec.addElement(tr.rewrite_car((Pair) form, subsyntax));
  177. form = ((Pair) form).getCdr();
  178. }
  179. else
  180. return tr.syntaxError("improper list argument to unquote");
  181. }
  182. int nargs = vec.size() + 1;
  183. cdr = expand(pair.getCdr(), 1, syntax, seen, tr);
  184. if (nargs > 1) {
  185. Expression[] args = new Expression[nargs];
  186. vec.copyInto(args);
  187. args[nargs-1] = coerceExpression(cdr, tr);
  188. Method method = splicing == 0 ? consXMethod : appendMethod;
  189. cdr = new ApplyExp(method, args);
  190. }
  191. rest = pair;
  192. break;
  193. }
  194. }
  195. Object car = expand (pair.getCar(), depth, syntax, seen, tr);
  196. if (car == pair.getCar()) {
  197. rest = pair.getCdr();
  198. if (rest instanceof Pair) {
  199. IdentityHashMap map = (IdentityHashMap) seen;
  200. Object old = map.get(rest);
  201. if (old == null) {
  202. map.put(rest, WORKING);
  203. pair = (Pair) rest;
  204. continue;
  205. }
  206. }
  207. cdr = expand(rest, depth, syntax, seen, tr);
  208. if (cdr == rest)
  209. return list;
  210. break;
  211. }
  212. cdr = expand (pair.getCdr(), depth, syntax, seen, tr);
  213. if (car instanceof Expression || cdr instanceof Expression) {
  214. Expression[] args = new Expression[2];
  215. args[0] = coerceExpression(car, tr);
  216. args[1] = coerceExpression(cdr, tr);
  217. cdr = new ApplyExp(makePairMethod, args);
  218. }
  219. else
  220. cdr = Translator.makePair(pair, car, cdr);
  221. break;
  222. }
  223. // rest is the n'th cdr of list. cdr is the expansion of rest.
  224. // The first n cars of list are returned identically by expand.
  225. // These do need to be copied because cdr!=rest.
  226. if (list == rest)
  227. return cdr;
  228. Pair p = list;
  229. Pair[] pairs = new Pair[20];
  230. int npairs = 0;
  231. for (;;) {
  232. if (npairs >= pairs.length) {
  233. Pair[] tmp = new Pair[2 * npairs];
  234. System.arraycopy(pairs, 0, tmp, 0, npairs);
  235. pairs = tmp;
  236. }
  237. pairs[npairs++] = p;
  238. if (p.getCdr() == rest)
  239. break;
  240. p = (Pair) p.getCdr();
  241. }
  242. Object result = cdr instanceof Expression ? LList.Empty : cdr;
  243. while (--npairs >= 0) {
  244. p = pairs[npairs];
  245. result = Translator.makePair(p, p.getCar(), result);
  246. }
  247. if (cdr instanceof Expression) {
  248. Expression[] args = new Expression[2];
  249. args[1] = (Expression) cdr;
  250. if (npairs == 1) {
  251. // The n==1 case: Only a single pair before rest.
  252. args[0] = leaf(list.getCar(), tr);
  253. return new ApplyExp(makePairMethod, args);
  254. } else {
  255. args[0] = leaf(result, tr);
  256. return new ApplyExp(appendMethod, args);
  257. }
  258. }
  259. return result;
  260. }
  261. // Note in 'seen' map that datum is currently being expanded.
  262. private static final Object WORKING = new String("(working)");
  263. // Note in 'seen' map that datum is used multiple times, partly in cycle.
  264. private static final Object SHARED = new String("(shared)");
  265. /** Backquote-expand a template.
  266. * @param template the quasiquoted template to expand
  267. * @param depth - the (net) number of quasiquotes we are inside.
  268. * The value QUOTE_DEPTH is a special case when we're inside
  269. * a quote rather than a quasiquote.
  270. * @param tr the rewrite context
  271. * @return the expanded Expression (the result can be a non-expression,
  272. * in which case it is implicitly a QuoteExp).
  273. */
  274. Object expand(Object template, int depth,
  275. SyntaxForm syntax, Object seen, Translator tr) {
  276. /* #ifdef use:java.util.IdentityHashMap */
  277. IdentityHashMap map = (IdentityHashMap) seen;
  278. Object old = map.get(template);
  279. if (old == WORKING) {
  280. map.put(template, SHARED);
  281. return template;
  282. } else if (old == SHARED) {
  283. return template;
  284. } else if (old != null)
  285. return old;
  286. map.put(template, WORKING);
  287. /* #endif */
  288. Object result;
  289. if (template instanceof Pair)
  290. result = expand_pair ((Pair) template, depth, syntax, seen, tr);
  291. else if (template instanceof SyntaxForm) {
  292. syntax = (SyntaxForm) template;
  293. result = expand(syntax.getDatum(), depth, syntax, seen, tr);
  294. } else if (template instanceof FVector) {
  295. FVector vector = (FVector) template;
  296. int n = vector.size();
  297. Object[] buffer = new Object[n];
  298. // For each element, the state is one of these four:
  299. // 0: the expanded element is the same as the original
  300. // 1: the expanded element is a constant
  301. // 2: the expanded element is neither constant nor a splice
  302. // 3: the element is spliced in
  303. byte[] state = new byte[n];
  304. byte max_state = 0;
  305. for (int i = 0; i < n; i++) {
  306. Object element = vector.get(i);
  307. int element_depth = depth;
  308. Pair pair;
  309. if (element instanceof Pair && depth > QUOTE_DEPTH
  310. && matchesUnquoteSplicing((pair = (Pair)element), syntax, tr)
  311. && --element_depth == 0) {
  312. Pair pair_cdr;
  313. if (! (pair.getCdr() instanceof Pair)
  314. || (pair_cdr = (Pair) pair.getCdr()).getCdr() != LList.Empty)
  315. return tr.syntaxError ("invalid used of " + pair.getCar() +
  316. " in quasiquote template");
  317. buffer[i] = tr.rewrite_car(pair_cdr, syntax);
  318. state[i] = 3;
  319. } else {
  320. buffer [i] = expand (element, element_depth, syntax, seen, tr);
  321. if (buffer[i] == element)
  322. state[i] = 0;
  323. else if (buffer[i] instanceof Expression)
  324. state[i] = 2;
  325. else
  326. state[i] = 1;
  327. }
  328. if (state[i] > max_state)
  329. max_state = state[i];
  330. }
  331. if (max_state == 0)
  332. result = vector;
  333. else if (max_state == 1)
  334. result = FVector.<Object>makeConstant(buffer);
  335. else {
  336. Expression[] args = new Expression[n];
  337. int firstSpliceArg = -1;
  338. for (int i = 0; i < n; i++) {
  339. if (state[i] == 3)
  340. args[i] = new ApplyExp(MakeSplice.quoteInstance,
  341. (Expression) buffer[i]);
  342. else
  343. args[i] = coerceExpression (buffer[i], tr);
  344. }
  345. ApplyExp exp = makeInvokeMakeVector(args);
  346. exp.firstSpliceArg = firstSpliceArg;
  347. result = exp;
  348. }
  349. }
  350. else
  351. result = template;
  352. /* #ifdef use:java.util.IdentityHashMap */
  353. if (template != result && map.get(template) == SHARED)
  354. tr.error('e', "cycle in non-literal data");
  355. map.put(template, result);
  356. /* #endif */
  357. return result;
  358. }
  359. private static ApplyExp makeInvokeMakeVector(Expression[] args) {
  360. return new ApplyExp(makeVectorMethod, args);
  361. }
  362. public Expression rewrite(Object obj, Translator tr) {
  363. Pair pair;
  364. if (! (obj instanceof Pair)
  365. || (pair = (Pair) obj).getCdr() != LList.Empty)
  366. return tr.syntaxError ("wrong number of arguments to quote");
  367. return coerceExpression(expand(pair.getCar(), isQuasi ? 1 : QUOTE_DEPTH, tr), tr);
  368. }
  369. /** A wrapper around LList.consX to make it a "variable-arg method". */
  370. public static Object consX$V(Object[] args) {
  371. return LList.consX(args);
  372. }
  373. /** Same as regular append, but handle SyntaxForm wrappers. */
  374. public static Object append$V(Object[] args) {
  375. int count = args.length;
  376. if (count == 0)
  377. return LList.Empty;
  378. Object result = args[count - 1];
  379. for (int i = count - 1; --i >= 0; ) {
  380. Object list = args[i];
  381. Object copy = null;
  382. Pair last = null;
  383. SyntaxForm syntax = null;
  384. for (;;) {
  385. while (list instanceof SyntaxForm) {
  386. syntax = (SyntaxForm) list;
  387. list = syntax.getDatum();
  388. }
  389. if (list == LList.Empty)
  390. break;
  391. if (! (list instanceof Pair)) {
  392. if (list instanceof ErrorExp)
  393. return list;
  394. throw new Error("expected list in quasi-quote splicing");
  395. }
  396. Pair list_pair = (Pair) list;
  397. Object car = list_pair.getCar();
  398. if (syntax != null && ! (car instanceof SyntaxForm))
  399. car = SyntaxForms.makeForm(car, syntax.getScope());
  400. Pair new_pair = new Pair(car, null);
  401. if (last == null)
  402. copy = new_pair;
  403. else
  404. last.setCdr(new_pair);
  405. last = new_pair;
  406. list = list_pair.getCdr();
  407. }
  408. if (last != null) {
  409. last.setCdr(result);
  410. result = copy;
  411. }
  412. }
  413. return result;
  414. }
  415. static final ClassType quoteType = ClassType.make("kawa.lang.Quote");
  416. static final Method consXMethod = quoteType.getDeclaredMethod("consX$V", 1);
  417. static final Method appendMethod = quoteType.getDeclaredMethod("append$V", 1);
  418. static final Method makePairMethod = Compilation.typePair.getDeclaredMethod("make", 2);
  419. static final Method makeVectorMethod = ClassType.make("gnu.lists.FVector")
  420. .getDeclaredMethod("makeConstant", 1);
  421. }