PageRenderTime 68ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/ClojureCLR/Clojure/Clojure/Lib/LispReader.cs

https://github.com/dsebban/clojure-contrib
C# | 1120 lines | 980 code | 88 blank | 52 comment | 191 complexity | 0ff63cacc73357599ad931d87301636c MD5 | raw file
  1. /**
  2. * Copyright (c) David Miller. All rights reserved.
  3. * The use and distribution terms for this software are covered by the
  4. * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
  5. * which can be found in the file epl-v10.html at the root of this distribution.
  6. * By using this software in any fashion, you are agreeing to be bound by
  7. * the terms of this license.
  8. * You must not remove this notice, or any other, from this software.
  9. **/
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Linq;
  13. using System.Text;
  14. using System.Text.RegularExpressions;
  15. using java.math;
  16. using System.IO;
  17. using System.Collections;
  18. using clojure.runtime;
  19. namespace clojure.lang
  20. {
  21. /// <summary>
  22. /// Implements the Lisp reader, a marvel to behold.
  23. /// </summary>
  24. public class LispReader
  25. {
  26. #region Symbol definitions
  27. static readonly Symbol QUOTE = Symbol.create("quote");
  28. static readonly Symbol THE_VAR = Symbol.create("var");
  29. static readonly Symbol UNQUOTE = Symbol.create("clojure.core", "unquote");
  30. static readonly Symbol UNQUOTE_SPLICING = Symbol.create("clojure.core", "unquote-splicing");
  31. static readonly Symbol DEREF = Symbol.create("clojure.core", "deref");
  32. static readonly Symbol META = Symbol.create("clojure.core", "meta");
  33. static readonly Symbol APPLY = Symbol.create("clojure.core", "apply");
  34. static readonly Symbol CONCAT = Symbol.create("clojure.core", "concat");
  35. static readonly Symbol HASHMAP = Symbol.create("clojure.core", "hash-map");
  36. static readonly Symbol HASHSET = Symbol.create("clojure.core", "hash-set");
  37. static readonly Symbol VECTOR = Symbol.create("clojure.core", "vector");
  38. static readonly Symbol WITH_META = Symbol.create("clojure.core", "with-meta");
  39. static readonly Symbol LIST = Symbol.create("clojure.core", "list");
  40. static readonly Symbol SEQ = Symbol.create("clojure.core","seq");
  41. static readonly Symbol SLASH = Symbol.create("/");
  42. static readonly Symbol CLOJURE_SLASH = Symbol.create("clojure.core","/");
  43. #endregion
  44. #region Var environments
  45. //symbol->gensymbol
  46. /// <summary>
  47. /// Dynamically bound var to a map from <see cref="Symbol">Symbol</see>s to ...
  48. /// </summary>
  49. static Var GENSYM_ENV = Var.create(null);
  50. //sorted-map num->gensymbol
  51. static Var ARG_ENV = Var.create(null);
  52. #endregion
  53. #region Macro characters & #-dispatch
  54. static IFn[] _macros = new IFn[256];
  55. static IFn[] _dispatchMacros = new IFn[256];
  56. static LispReader()
  57. {
  58. _macros['"'] = new StringReader();
  59. _macros[';'] = new CommentReader();
  60. _macros['\''] = new WrappingReader(QUOTE);
  61. _macros['@'] = new WrappingReader(DEREF);//new DerefReader();
  62. _macros['^'] = new WrappingReader(META);
  63. _macros['`'] = new SyntaxQuoteReader();
  64. _macros['~'] = new UnquoteReader();
  65. _macros['('] = new ListReader();
  66. _macros[')'] = new UnmatchedDelimiterReader();
  67. _macros['['] = new VectorReader();
  68. _macros[']'] = new UnmatchedDelimiterReader();
  69. _macros['{'] = new MapReader();
  70. _macros['}'] = new UnmatchedDelimiterReader();
  71. //// macros['|'] = new ArgVectorReader();
  72. _macros['\\'] = new CharacterReader();
  73. _macros['%'] = new ArgReader();
  74. _macros['#'] = new DispatchReader();
  75. _dispatchMacros['^'] = new MetaReader();
  76. _dispatchMacros['\''] = new VarReader();
  77. _dispatchMacros['"'] = new RegexReader();
  78. _dispatchMacros['('] = new FnReader();
  79. _dispatchMacros['{'] = new SetReader();
  80. _dispatchMacros['='] = new EvalReader();
  81. _dispatchMacros['!'] = new CommentReader();
  82. _dispatchMacros['<'] = new UnreadableReader();
  83. _dispatchMacros['_'] = new DiscardReader();
  84. }
  85. static bool isMacro(int ch)
  86. {
  87. return ch < _macros.Length && _macros[ch] != null;
  88. }
  89. static IFn getMacro(int ch)
  90. {
  91. return ch < _macros.Length ? _macros[ch] : null;
  92. }
  93. static bool isTerminatingMacro(int ch)
  94. {
  95. return (ch != '#' && ch < _macros.Length && _macros[ch] != null);
  96. }
  97. #endregion
  98. #region main entry point -- read
  99. // There is really no reason for the main entry point to have an isRecursive flag, is there?
  100. public static object read(PushbackTextReader r,
  101. bool eofIsError,
  102. object eofValue,
  103. bool isRecursive)
  104. {
  105. try
  106. {
  107. for (; ; )
  108. {
  109. int ch = r.Read();
  110. while (isWhitespace(ch))
  111. ch = r.Read();
  112. if (ch == -1)
  113. {
  114. if (eofIsError)
  115. throw new EndOfStreamException("EOF while reading");
  116. return eofValue;
  117. }
  118. if (Char.IsDigit((char)ch))
  119. {
  120. object n = readNumber(r, (char)ch);
  121. return RT.suppressRead() ? null : n;
  122. }
  123. IFn macroFn = getMacro(ch);
  124. if (macroFn != null)
  125. {
  126. object ret = macroFn.invoke(r, (char)ch);
  127. if (RT.suppressRead())
  128. return null;
  129. // no op macros return the reader
  130. if (ret == r)
  131. continue;
  132. return ret;
  133. }
  134. if (ch == '+' || ch == '-')
  135. {
  136. int ch2 = r.Read();
  137. if (Char.IsDigit((char)ch2))
  138. {
  139. Unread(r, ch2);
  140. object n = readNumber(r, (char)ch);
  141. return RT.suppressRead() ? null : n;
  142. }
  143. Unread(r, ch2);
  144. }
  145. string token = readToken(r, (char)ch);
  146. return RT.suppressRead() ? null : interpretToken(token);
  147. }
  148. }
  149. catch (Exception e)
  150. {
  151. if (isRecursive || !(r is LineNumberingTextReader))
  152. throw e;
  153. LineNumberingTextReader rdr = r as LineNumberingTextReader;
  154. throw new ReaderException(rdr.LineNumber, e);
  155. }
  156. }
  157. private static object ReadAux(PushbackTextReader r)
  158. {
  159. return read(r, true, null, true);
  160. }
  161. #endregion
  162. #region Character hacking
  163. static void Unread(PushbackTextReader r, int ch)
  164. {
  165. if (ch != -1)
  166. r.Unread(ch);
  167. }
  168. static bool isWhitespace(int ch)
  169. {
  170. return Char.IsWhiteSpace((char)ch) || ch == ',';
  171. }
  172. // Roughly a match to Java Character.digit(char,int),
  173. // though I don't handle all unicode digits.
  174. static int CharValueInRadix(int c, int radix)
  175. {
  176. if (char.IsDigit((char)c))
  177. return c - '0' < radix ? c - '0' : -1;
  178. if ('A' <= c && c <= 'Z')
  179. return c - 'A' < radix - 10 ? c - 'A' + 10: -1;
  180. if ('a' <= c && c <= 'z')
  181. return c - 'a' < radix - 10 ? c - 'a' + 10 : -1;
  182. return -1;
  183. }
  184. static int readUnicodeChar(string token, int offset, int length, int radix)
  185. {
  186. if (token.Length != offset + length)
  187. throw new ArgumentException("Invalid unicode character: \\" + token);
  188. int uc = 0;
  189. for (int i = offset; i < offset + length; ++i)
  190. {
  191. int d = CharValueInRadix(token[i], radix);
  192. if (d == -1)
  193. throw new ArgumentException("Invalid digit: " + (char)d);
  194. uc = uc * radix + d;
  195. }
  196. return (char)uc;
  197. }
  198. static int readUnicodeChar(PushbackTextReader r, int initch, int radix, int length, bool exact)
  199. {
  200. int uc = CharValueInRadix(initch, radix);
  201. if (uc == -1)
  202. throw new ArgumentException("Invalid digit: " + initch);
  203. int i = 1;
  204. for (; i < length; ++i)
  205. {
  206. int ch = r.Read();
  207. if (ch == -1 || isWhitespace(ch) || isMacro(ch))
  208. {
  209. Unread(r, ch);
  210. break;
  211. }
  212. int d = CharValueInRadix(ch, radix);
  213. if (d == -1)
  214. throw new ArgumentException("Invalid digit: " + (char)ch);
  215. uc = uc * radix + d;
  216. }
  217. if (i != length && exact)
  218. throw new ArgumentException("Invalid character length: " + i + ", should be: " + length);
  219. return uc;
  220. }
  221. #endregion
  222. #region Other
  223. public static List<Object> readDelimitedList(char delim, PushbackTextReader r, bool isRecursive)
  224. {
  225. List<Object> a = new List<object>();
  226. for (; ; )
  227. {
  228. int ch = r.Read();
  229. while (isWhitespace(ch))
  230. ch = r.Read();
  231. if (ch == -1)
  232. throw new EndOfStreamException("EOF while reading");
  233. if (ch == delim)
  234. {
  235. break;
  236. }
  237. IFn macroFn = getMacro(ch);
  238. if (macroFn != null)
  239. {
  240. Object mret = macroFn.invoke(r, (char)ch);
  241. //no op macros return the reader
  242. if (mret != r)
  243. a.Add(mret);
  244. }
  245. else
  246. {
  247. Unread(r, ch);
  248. object o = read(r, true, null, isRecursive);
  249. if (o != r)
  250. a.Add(o);
  251. }
  252. }
  253. return a;
  254. }
  255. static Symbol garg(int n)
  256. {
  257. return Symbol.intern(null, (n == -1 ? "rest" : ("p" + n)) + "__" + RT.nextID());
  258. }
  259. #endregion
  260. #region Reading tokens
  261. static string readToken(PushbackTextReader r, char initch)
  262. {
  263. StringBuilder sb = new StringBuilder();
  264. sb.Append(initch);
  265. for(; ;)
  266. {
  267. int ch = r.Read();
  268. if(ch == -1 || isWhitespace(ch) || isTerminatingMacro(ch))
  269. {
  270. Unread(r, ch);
  271. return sb.ToString();
  272. }
  273. sb.Append((char) ch);
  274. }
  275. }
  276. public static object interpretToken(string s)
  277. {
  278. if (s.Equals("nil"))
  279. {
  280. return null;
  281. }
  282. else if (s.Equals("true"))
  283. {
  284. return RT.T;
  285. }
  286. else if (s.Equals("false"))
  287. {
  288. return RT.F;
  289. }
  290. else if (s.Equals("/"))
  291. {
  292. return SLASH;
  293. }
  294. else if (s.Equals("clojure.core//"))
  295. {
  296. return CLOJURE_SLASH;
  297. }
  298. object ret = null;
  299. ret = matchSymbol(s);
  300. if (ret != null)
  301. return ret;
  302. throw new Exception("Invalid token: " + s);
  303. }
  304. static Regex symbolPat = new Regex("^[:]?([\\D-[/]].*/)?([\\D-[/]][^/]*)$");
  305. static object matchSymbol(string s)
  306. {
  307. Match m = symbolPat.Match(s);
  308. if (m.Success)
  309. {
  310. int gc = m.Groups.Count;
  311. string ns = m.Groups[1].Value;
  312. string name = m.Groups[2].Value;
  313. if (ns != null && ns.EndsWith(":/")
  314. || name.EndsWith(":")
  315. || s.IndexOf("::", 1) != -1)
  316. return null;
  317. // Maybe resolveSymbol should move here or into Namespace: resolveSymbol is not used in the compiler.
  318. if (s.StartsWith("::"))
  319. {
  320. Symbol ks = Symbol.intern(s.Substring(2));
  321. Namespace kns;
  322. if (ks.Namespace != null)
  323. kns = Compiler.NamespaceFor(ks);
  324. else
  325. kns = Compiler.CurrentNamespace;
  326. //auto-resolving keyword
  327. return Keyword.intern(kns.Name.Name, ks.Name);
  328. }
  329. bool isKeyword = s[0] == ':';
  330. Symbol sym = Symbol.intern(s.Substring(isKeyword ? 1 : 0));
  331. if (isKeyword)
  332. return Keyword.intern(sym);
  333. return sym;
  334. }
  335. return null;
  336. }
  337. #endregion
  338. #region Reading numbers
  339. static Regex intRE = new Regex("^([-+]?)(?:(0)|([1-9][0-9]*)|0[xX]([0-9A-Fa-f]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9A-Za-z]+)|0[0-9]+)\\.?$");
  340. static Regex ratioRE = new Regex("^([-+]?[0-9]+)/([0-9]+)$");
  341. static Regex floatRE = new Regex("^[-+]?[0-9]+(\\.[0-9]+)?([eE][-+]?[0-9]+)?[M]?$");
  342. static object readNumber(PushbackTextReader r, char initch)
  343. {
  344. StringBuilder sb = new StringBuilder();
  345. sb.Append(initch);
  346. for (; ; )
  347. {
  348. int ch = r.Read();
  349. if (ch == -1 || isWhitespace(ch) || isMacro(ch))
  350. {
  351. Unread(r, ch);
  352. break;
  353. }
  354. sb.Append((char)ch);
  355. }
  356. string s = sb.ToString();
  357. object n = matchNumber(s);
  358. if (n == null)
  359. throw new FormatException("Invalid number: " + s);
  360. return n;
  361. }
  362. public static object matchNumber(string s)
  363. {
  364. Match m = intRE.Match(s);
  365. if ( m.Success )
  366. {
  367. if ( m.Groups[2].Success )
  368. // matched 0 only
  369. return 0;
  370. bool isNeg = m.Groups[1].Value == "-";
  371. string n = null;
  372. int radix = 10;
  373. if (m.Groups[3].Success)
  374. {
  375. n = m.Groups[3].Value;
  376. radix = 10;
  377. }
  378. else if (m.Groups[4].Success)
  379. {
  380. n = m.Groups[4].Value;
  381. radix = 16;
  382. }
  383. else if (m.Groups[5].Success)
  384. {
  385. n = m.Groups[5].Value;
  386. radix = 8;
  387. }
  388. else if (m.Groups[7].Success)
  389. {
  390. n = m.Groups[7].Value;
  391. radix = Int32.Parse(m.Groups[6].Value);
  392. }
  393. if (n == null)
  394. return null;
  395. BigInteger bn = new BigInteger(n, radix);
  396. return Numbers.reduce(isNeg ? bn.negate() : bn);
  397. }
  398. m = floatRE.Match(s);
  399. if (m.Success)
  400. {
  401. return ( s[s.Length-1] == 'M' )
  402. ? new BigDecimal( s.Substring(0,s.Length-1)) // TODO: Fix MS inadequacy
  403. : (object)Double.Parse(s);
  404. }
  405. m = ratioRE.Match(s);
  406. if (m.Success)
  407. {
  408. // There is a bug in the BigInteger c-tor that causes it barf on a leading +.
  409. string numerString = m.Groups[1].Value;
  410. string denomString = m.Groups[2].Value;
  411. if (numerString[0] == '+')
  412. numerString = numerString.Substring(1);
  413. return Numbers.BIDivide(new BigInteger(numerString), new BigInteger(denomString));
  414. }
  415. return null;
  416. }
  417. #endregion
  418. // Reader classes made public according to Java rev 1121
  419. public abstract class ReaderBase : AFn
  420. {
  421. public override object invoke(object arg1, object arg2)
  422. {
  423. return Read((PushbackTextReader)arg1, (Char)arg2);
  424. }
  425. protected abstract object Read(PushbackTextReader r, char c);
  426. }
  427. public sealed class CharacterReader : ReaderBase
  428. {
  429. protected override object Read(PushbackTextReader r, char backslash)
  430. {
  431. int ch = r.Read();
  432. if (ch == -1)
  433. throw new EndOfStreamException("EOF while reading character");
  434. String token = readToken(r, (char)ch);
  435. if (token.Length == 1)
  436. return token[0];
  437. else if (token.Equals("newline"))
  438. return '\n';
  439. else if (token.Equals("space"))
  440. return ' ';
  441. else if (token.Equals("tab"))
  442. return '\t';
  443. else if (token.Equals("backspace"))
  444. return '\b';
  445. else if (token.Equals("formfeed"))
  446. return '\f';
  447. else if (token.Equals("return"))
  448. return '\r';
  449. else if (token.StartsWith("u"))
  450. {
  451. char c = (char)readUnicodeChar(token, 1, 4, 16);
  452. if (c >= '\uD800' && c <= '\uDFFF') // surrogate code unit?
  453. throw new Exception("Invalid character constant: \\u" + ((int)c).ToString("X"));
  454. return c;
  455. }
  456. else if (token.StartsWith("o"))
  457. {
  458. int len = token.Length - 1;
  459. if (len > 3)
  460. throw new Exception("Invalid octal escape sequence length: " + len);
  461. int uc = readUnicodeChar(token, 1, len, 8);
  462. if (uc > 255) //octal377
  463. throw new Exception("Octal escape sequence must be in range [0, 377].");
  464. return (char)uc;
  465. }
  466. throw new Exception("Unsupported character: \\" + token);
  467. }
  468. }
  469. public sealed class StringReader : ReaderBase
  470. {
  471. protected override object Read(PushbackTextReader r, char doublequote)
  472. {
  473. StringBuilder sb = new StringBuilder();
  474. for (int ch = r.Read(); ch != '"'; ch = r.Read())
  475. {
  476. if (ch == -1)
  477. throw new EndOfStreamException("EOF while reading string");
  478. if (ch == '\\') //escape
  479. {
  480. ch = r.Read();
  481. if (ch == -1)
  482. throw new EndOfStreamException("EOF while reading string");
  483. switch (ch)
  484. {
  485. case 't':
  486. ch = '\t';
  487. break;
  488. case 'r':
  489. ch = '\r';
  490. break;
  491. case 'n':
  492. ch = '\n';
  493. break;
  494. case '\\':
  495. break;
  496. case '"':
  497. break;
  498. case 'b':
  499. ch = '\b';
  500. break;
  501. case 'f':
  502. ch = '\f';
  503. break;
  504. case 'u':
  505. ch = r.Read();
  506. if (CharValueInRadix(ch, 16) == -1)
  507. throw new Exception("Invalid unicode escape: \\u" + (char)ch);
  508. ch = readUnicodeChar((PushbackTextReader)r, ch, 16, 4, true);
  509. break;
  510. default:
  511. {
  512. if (CharValueInRadix(ch, 8) != -1)
  513. {
  514. ch = readUnicodeChar((PushbackTextReader)r, ch, 8, 3, false);
  515. if (ch > 255) //octal377
  516. throw new Exception("Octal escape sequence must be in range [0, 377].");
  517. }
  518. else
  519. throw new Exception("Unsupported escape character: \\" + (char)ch);
  520. }
  521. break;
  522. }
  523. }
  524. sb.Append((char)ch);
  525. }
  526. return sb.ToString();
  527. }
  528. }
  529. public sealed class CommentReader : ReaderBase
  530. {
  531. protected override object Read(PushbackTextReader r, char semicolon)
  532. {
  533. int ch;
  534. do
  535. {
  536. ch = r.Read();
  537. } while (ch != -1 && ch != '\n' && ch != '\r');
  538. return r;
  539. }
  540. }
  541. public sealed class ListReader : ReaderBase
  542. {
  543. protected override object Read(PushbackTextReader r, char leftparen)
  544. {
  545. int line = -1;
  546. if (r is LineNumberingTextReader)
  547. line = ((LineNumberingTextReader)r).LineNumber;
  548. IList<Object> list = readDelimitedList(')', r, true);
  549. if (list.Count == 0)
  550. return PersistentList.EMPTY;
  551. IObj s = (IObj)PersistentList.create((IList)list);
  552. // IObj s = (IObj) RT.seq(list);
  553. if (line != -1)
  554. return s.withMeta(RT.map(RT.LINE_KEY, line));
  555. else
  556. return s;
  557. }
  558. }
  559. public sealed class VectorReader : ReaderBase
  560. {
  561. protected override object Read(PushbackTextReader r, char leftparen)
  562. {
  563. return LazilyPersistentVector.create(readDelimitedList(']', r, true));
  564. }
  565. }
  566. public sealed class MapReader : ReaderBase
  567. {
  568. protected override object Read(PushbackTextReader r, char leftbrace)
  569. {
  570. //return PersistentHashMap.create(readDelimitedList('}', r, true));
  571. return RT.map(readDelimitedList('}', r, true).ToArray());
  572. }
  573. }
  574. public sealed class UnmatchedDelimiterReader : ReaderBase
  575. {
  576. protected override object Read(PushbackTextReader reader, char rightdelim)
  577. {
  578. throw new Exception("Unmatched delimiter: " + rightdelim);
  579. }
  580. }
  581. public sealed class DiscardReader : ReaderBase
  582. {
  583. protected override object Read(PushbackTextReader r, char underscore)
  584. {
  585. ReadAux(r);
  586. return r;
  587. }
  588. }
  589. public sealed class WrappingReader : ReaderBase
  590. {
  591. readonly Symbol _sym;
  592. public WrappingReader(Symbol sym)
  593. {
  594. _sym = sym;
  595. }
  596. protected override object Read(PushbackTextReader r, char quote)
  597. {
  598. //object o = read(r, true, null, true);
  599. object o = ReadAux(r);
  600. return RT.list(_sym, o);
  601. }
  602. }
  603. public sealed class SyntaxQuoteReader : ReaderBase
  604. {
  605. protected override object Read(PushbackTextReader r, char backquote)
  606. {
  607. try
  608. {
  609. Var.pushThreadBindings(RT.map(GENSYM_ENV, PersistentHashMap.EMPTY));
  610. //object form = read(r, true, null, true);
  611. object form = ReadAux(r);
  612. return syntaxQuote(form);
  613. }
  614. finally
  615. {
  616. Var.popThreadBindings();
  617. }
  618. }
  619. static object syntaxQuote(object form)
  620. {
  621. object ret;
  622. if (Compiler.isSpecial(form))
  623. ret = RT.list(Compiler.QUOTE, form);
  624. else if (form is Symbol)
  625. {
  626. Symbol sym = (Symbol)form;
  627. if (sym.Namespace == null && sym.Name.EndsWith("#"))
  628. {
  629. IPersistentMap gmap = (IPersistentMap)GENSYM_ENV.deref();
  630. if (gmap == null)
  631. throw new InvalidDataException("Gensym literal not in syntax-quote");
  632. Symbol gs = (Symbol)gmap.valAt(sym);
  633. if (gs == null)
  634. GENSYM_ENV.set(gmap.assoc(sym, gs = Symbol.intern(null,
  635. sym.Name.Substring(0, sym.Name.Length - 1)
  636. + "__" + RT.nextID() + "__auto__")));
  637. sym = gs;
  638. }
  639. else if (sym.Namespace == null && sym.Name.EndsWith("."))
  640. {
  641. Symbol csym = Symbol.intern(null, sym.Name.Substring(0, sym.Name.Length - 1));
  642. csym = Compiler.resolveSymbol(csym);
  643. sym = Symbol.intern(null, csym.Name + ".");
  644. }
  645. else if ( sym.Namespace == null && sym.Name.StartsWith("."))
  646. {
  647. // simply quote method names
  648. }
  649. else
  650. sym = Compiler.resolveSymbol(sym);
  651. ret = RT.list(Compiler.QUOTE, sym);
  652. }
  653. //else if (form is Unquote)
  654. // return ((Unquote)form).Obj;
  655. // Rev 1184
  656. else if (isUnquote(form))
  657. return RT.second(form);
  658. else if (isUnquoteSplicing(form))
  659. throw new ArgumentException("splice not in list");
  660. else if (form is IPersistentCollection)
  661. {
  662. if (form is IPersistentMap)
  663. {
  664. IPersistentVector keyvals = flattenMap(form);
  665. ret = RT.list(APPLY, HASHMAP, RT.list(SEQ,RT.cons(CONCAT, sqExpandList(keyvals.seq()))));
  666. }
  667. else if (form is IPersistentVector)
  668. {
  669. ret = RT.list(APPLY, VECTOR, RT.list(SEQ,RT.cons(CONCAT, sqExpandList(((IPersistentVector)form).seq()))));
  670. }
  671. else if (form is IPersistentSet)
  672. {
  673. ret = RT.list(APPLY, HASHSET, RT.list(SEQ,RT.cons(CONCAT, sqExpandList(((IPersistentSet)form).seq()))));
  674. }
  675. else if (form is ISeq || form is IPersistentList)
  676. {
  677. ISeq seq = RT.seq(form);
  678. if (seq == null)
  679. ret = RT.cons(LIST, null);
  680. else
  681. ret = RT.list(SEQ,RT.cons(CONCAT, sqExpandList(seq)));
  682. }
  683. else
  684. throw new InvalidOperationException("Unknown Collection type");
  685. }
  686. else if (form is Keyword
  687. || Util.IsNumeric(form)
  688. || form is Char
  689. || form is String)
  690. ret = form;
  691. else
  692. ret = RT.list(Compiler.QUOTE, form);
  693. if (form is IObj && RT.meta(form) != null)
  694. {
  695. //filter line numbers
  696. IPersistentMap newMeta = ((IObj)form).meta().without(RT.LINE_KEY);
  697. if (newMeta.count() > 0)
  698. return RT.list(WITH_META, ret, syntaxQuote(((IObj)form).meta()));
  699. }
  700. return ret;
  701. }
  702. private static ISeq sqExpandList(ISeq seq)
  703. {
  704. IPersistentVector ret = PersistentVector.EMPTY;
  705. for (; seq != null; seq = seq.next())
  706. {
  707. Object item = seq.first();
  708. //if (item is Unquote)
  709. // ret = ret.cons(RT.list(LIST, ((Unquote)item).Obj));
  710. // REV 1184
  711. if (isUnquote(item))
  712. ret = ret.cons(RT.list(LIST, RT.second(item)));
  713. else if (isUnquoteSplicing(item))
  714. ret = ret.cons(RT.second(item));
  715. else
  716. ret = ret.cons(RT.list(LIST, syntaxQuote(item)));
  717. }
  718. return ret.seq();
  719. }
  720. private static IPersistentVector flattenMap(object form)
  721. {
  722. IPersistentVector keyvals = PersistentVector.EMPTY;
  723. for (ISeq s = RT.seq(form); s != null; s = s.next())
  724. {
  725. IMapEntry e = (IMapEntry)s.first();
  726. keyvals = (IPersistentVector)keyvals.cons(e.key());
  727. keyvals = (IPersistentVector)keyvals.cons(e.val());
  728. }
  729. return keyvals;
  730. }
  731. }
  732. sealed class UnquoteReader : ReaderBase
  733. {
  734. protected override object Read(PushbackTextReader r, char comma)
  735. {
  736. int ch = r.Read();
  737. if (ch == -1)
  738. throw new EndOfStreamException("EOF while reading character");
  739. if (ch == '@')
  740. {
  741. //object o = read(r, true, null, true);
  742. object o = ReadAux(r);
  743. return RT.list(UNQUOTE_SPLICING, o);
  744. }
  745. else
  746. {
  747. Unread(r, ch);
  748. //object o = read(r, true, null, true);
  749. object o = ReadAux(r);
  750. //return new Unquote(o);
  751. // per Rev 1184
  752. return RT.list(UNQUOTE, o);
  753. }
  754. }
  755. }
  756. #region Unquote helpers
  757. // Per rev 1184
  758. static bool isUnquote(object form)
  759. {
  760. return form is ISeq && Util.equals(RT.first(form),UNQUOTE);
  761. }
  762. static bool isUnquoteSplicing(object form)
  763. {
  764. return form is ISeq && Util.equals(RT.first(form), UNQUOTE_SPLICING);
  765. }
  766. #endregion
  767. public sealed class DispatchReader : ReaderBase
  768. {
  769. protected override object Read(PushbackTextReader r, char hash)
  770. {
  771. int ch = r.Read();
  772. if (ch == -1)
  773. throw new EndOfStreamException("EOF while reading character");
  774. IFn fn = _dispatchMacros[ch];
  775. if (fn == null)
  776. throw new Exception(String.Format("No dispatch macro for: {0}", (char)ch));
  777. return fn.invoke(r, (char)ch);
  778. }
  779. }
  780. public sealed class MetaReader : ReaderBase
  781. {
  782. protected override object Read(PushbackTextReader r, char caret)
  783. {
  784. int line = -1;
  785. if (r is LineNumberingTextReader)
  786. line = ((LineNumberingTextReader)r).LineNumber;
  787. //object meta = read(r, true, null, true);
  788. object meta = ReadAux(r);
  789. if (meta is Symbol || meta is Keyword || meta is String)
  790. meta = RT.map(RT.TAG_KEY, meta);
  791. else if (!(meta is IPersistentMap))
  792. throw new ArgumentException("Metadata must be Symbol,Keyword,String or Map");
  793. //object o = read(r, true, null, true);
  794. object o = ReadAux(r);
  795. if (o is IMeta)
  796. {
  797. if (line != -1 && o is ISeq)
  798. meta = ((IPersistentMap)meta).assoc(RT.LINE_KEY, line);
  799. if (o is IReference)
  800. {
  801. ((IReference)o).resetMeta((IPersistentMap)meta);
  802. return o;
  803. }
  804. return ((IObj)o).withMeta((IPersistentMap)meta);
  805. }
  806. else
  807. throw new ArgumentException("Metadata can only be applied to IMetas");
  808. }
  809. }
  810. public sealed class VarReader : ReaderBase
  811. {
  812. protected override object Read(PushbackTextReader r, char quote)
  813. {
  814. //object o = read(r, true, null, true);
  815. object o = ReadAux(r);
  816. // if(o instanceof Symbol)
  817. // {
  818. // Object v = Compiler.maybeResolveIn(Compiler.currentNS(), (Symbol) o);
  819. // if(v instanceof Var)
  820. // return v;
  821. // }
  822. return RT.list(THE_VAR, o);
  823. }
  824. }
  825. public sealed class RegexReader : ReaderBase
  826. {
  827. static readonly StringReader stringrdr = new StringReader();
  828. protected override object Read(PushbackTextReader r, char doublequote)
  829. {
  830. StringBuilder sb = new StringBuilder();
  831. for (int ch = r.Read(); ch != '"'; ch = r.Read())
  832. {
  833. if (ch == -1)
  834. throw new EndOfStreamException("EOF while reading regex");
  835. sb.Append((char)ch);
  836. if (ch == '\\') //escape
  837. {
  838. ch = r.Read();
  839. if (ch == -1)
  840. throw new EndOfStreamException("EOF while reading regex");
  841. sb.Append((char)ch);
  842. }
  843. }
  844. return new Regex(sb.ToString());
  845. }
  846. }
  847. public sealed class FnReader : ReaderBase
  848. {
  849. //static ListReader _listReader = new ListReader();
  850. protected override object Read(PushbackTextReader r, char lparen)
  851. {
  852. if (ARG_ENV.deref() != null)
  853. throw new InvalidOperationException("Nested #()s are not allowed");
  854. try
  855. {
  856. Var.pushThreadBindings(RT.map(ARG_ENV, PersistentTreeMap.EMPTY));
  857. r.Unread('(');
  858. ////object form = ReadAux(r, true, null, true);
  859. object form = ReadAux(r);
  860. //object form = _listReader.invoke(r, '(');
  861. IPersistentVector args = PersistentVector.EMPTY;
  862. PersistentTreeMap argsyms = (PersistentTreeMap)ARG_ENV.deref();
  863. ISeq rargs = argsyms.rseq();
  864. if (rargs != null)
  865. {
  866. int higharg = (int)((IMapEntry)rargs.first()).key();
  867. if (higharg > 0)
  868. {
  869. for (int i = 1; i <= higharg; ++i)
  870. {
  871. object sym = argsyms.valAt(i);
  872. if (sym == null)
  873. sym = garg(i);
  874. args = args.cons(sym);
  875. }
  876. }
  877. object restsym = argsyms.valAt(-1);
  878. if (restsym != null)
  879. {
  880. args = args.cons(Compiler._AMP_);
  881. args = args.cons(restsym);
  882. }
  883. }
  884. return RT.list(Compiler.FN, args, form);
  885. }
  886. finally
  887. {
  888. Var.popThreadBindings();
  889. }
  890. }
  891. }
  892. sealed class ArgReader : ReaderBase
  893. {
  894. protected override object Read(PushbackTextReader r, char pct)
  895. {
  896. int ch = r.Read();
  897. Unread(r, ch);
  898. //% alone is first arg
  899. if (ch == -1 || isWhitespace(ch) || isTerminatingMacro(ch))
  900. {
  901. return registerArg(1);
  902. }
  903. //object n = ReadAux(r, true, null, true);
  904. object n = ReadAux(r);
  905. if (n.Equals(Compiler._AMP_))
  906. return registerArg(-1);
  907. if (!Util.IsNumeric(n))
  908. throw new ArgumentException("arg literal must be %, %& or %integer");
  909. return registerArg(Util.ConvertToInt(n));
  910. }
  911. static Symbol registerArg(int n)
  912. {
  913. PersistentTreeMap argsyms = (PersistentTreeMap)ARG_ENV.deref();
  914. if (argsyms == null)
  915. {
  916. throw new InvalidOperationException("arg literal not in #()");
  917. }
  918. Symbol ret = (Symbol)argsyms.valAt(n);
  919. if (ret == null)
  920. {
  921. ret = garg(n);
  922. ARG_ENV.set(argsyms.assoc(n, ret));
  923. }
  924. return ret;
  925. }
  926. }
  927. public sealed class SetReader : ReaderBase
  928. {
  929. protected override object Read(PushbackTextReader r, char leftbracket)
  930. {
  931. return PersistentHashSet.create1(readDelimitedList('}', r, true));
  932. }
  933. }
  934. //TODO: Need to figure out who to deal with typenames in the context of multiple loaded assemblies.
  935. public sealed class EvalReader : ReaderBase
  936. {
  937. protected override object Read(PushbackTextReader r, char eq)
  938. {
  939. if (!RT.booleanCast(RT.READEVAL.deref()))
  940. {
  941. throw new Exception("EvalReader not allowed when *read-eval* is false.");
  942. }
  943. Object o = read(r, true, null, true);
  944. if (o is Symbol)
  945. {
  946. return RT.classForName(o.ToString());
  947. }
  948. else if (o is IPersistentList)
  949. {
  950. Symbol fs = (Symbol)RT.first(o);
  951. if (fs.Equals(THE_VAR))
  952. {
  953. Symbol vs = (Symbol)RT.second(o);
  954. return RT.var(vs.Namespace, vs.Name); //Compiler.resolve((Symbol) RT.second(o),true);
  955. }
  956. if (fs.Name.EndsWith("."))
  957. {
  958. Object[] args = RT.toArray(RT.next(o));
  959. return Reflector.InvokeConstructor(RT.classForName(fs.Name.Substring(0, fs.Name.Length - 1)), args);
  960. }
  961. if (Compiler.NamesStaticMember(fs))
  962. {
  963. Object[] args = RT.toArray(RT.next(o));
  964. return Reflector.InvokeStaticMethod(fs.Namespace, fs.Name, args);
  965. }
  966. Object v = Compiler.maybeResolveIn(Compiler.CurrentNamespace, fs);
  967. if (v is Var)
  968. {
  969. return ((IFn)v).applyTo(RT.next(o));
  970. }
  971. throw new Exception("Can't resolve " + fs);
  972. }
  973. else
  974. throw new ArgumentException("Unsupported #= form");
  975. }
  976. }
  977. public sealed class UnreadableReader : ReaderBase
  978. {
  979. protected override object Read(PushbackTextReader reader, char leftangle)
  980. {
  981. throw new Exception("Unreadable form");
  982. }
  983. }
  984. public sealed class ReaderException : Exception
  985. {
  986. readonly int _line;
  987. public int Line
  988. {
  989. get { return _line; }
  990. }
  991. public ReaderException(int line, Exception e)
  992. : base(null, e)
  993. {
  994. _line = line;
  995. }
  996. }
  997. }
  998. }