PageRenderTime 44ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/System.Xronos/Reader.cs

https://bitbucket.org/stefanrusek/xronos
C# | 322 lines | 246 code | 53 blank | 23 comment | 70 complexity | 4442afc7c06c98717b0f1088644923ec MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) 2008 Stefan Rusek and Benjamin Pollack
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy
  6. * of this software and associated documentation files (the "Software"), to deal
  7. * in the Software without restriction, including without limitation the rights
  8. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. * copies of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in
  13. * all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. * THE SOFTWARE.
  22. *
  23. * ***************************************************************************/
  24. using System.Xronos.Language;
  25. using System;
  26. using System.Collections.Generic;
  27. using System.Linq;
  28. using System.Text;
  29. using System.IO;
  30. using System.Text.RegularExpressions;
  31. using System.Collections;
  32. using System.Xronos.Builtins;
  33. using System.Xronos.Scripting;
  34. namespace System.Xronos
  35. {
  36. public class Reader
  37. {
  38. public static readonly Symbol Var = Symbol.Create("var");
  39. public static readonly Symbol Slash = Symbol.Create("/");
  40. Regex reSymbol = new Regex(@"[:]?([\D-[/]].*/)?([\D-[/]][^/]*)");
  41. ISequence<char> source;
  42. private Reader() { }
  43. internal bool IsWhitespace(char c)
  44. {
  45. return char.IsWhiteSpace(c) || c == ',';
  46. }
  47. public static object Read(XronosContext context, string filename, string source)
  48. {
  49. return Read(context, filename, new System.IO.StringReader(source), 1);
  50. }
  51. public static object Read(XronosContext context, string filename, TextReader reader, int line)
  52. {
  53. Statistics.ReaderTime.Start();
  54. try
  55. {
  56. var source = TextReaderSequence.FromTextReader(filename, reader, line);
  57. var list = new ArrayList();
  58. while (source != null)
  59. {
  60. object obj;
  61. source = Read(source, out obj);
  62. if (obj != null)
  63. list.Add(obj);
  64. }
  65. if (list.Count == 0)
  66. list.Add(null);
  67. if (list.Count == 1 && list[0] != null)
  68. return list[0];
  69. return RT.List(list.ToArray()).cons(SpecialForms.Do.withMeta(PersistentHashMap.Create(new object[] { "auto", true})));
  70. }
  71. finally
  72. {
  73. Statistics.ReaderTime.Stop();
  74. }
  75. }
  76. private static ISequence<char> Read(ISequence<char> source, out object form)
  77. {
  78. var reader = new Reader { source = source };
  79. form = reader.Read();
  80. return reader.source;
  81. }
  82. private object Read()
  83. {
  84. try
  85. {
  86. return InternalRead(true, null);
  87. }
  88. catch (Exception ex)
  89. {
  90. var lni = source as ILineNumberInfo;
  91. if (lni == null)
  92. throw;
  93. throw new ReaderException(lni, ex);
  94. }
  95. }
  96. public int Line
  97. {
  98. get
  99. {
  100. var lni = source as ILineNumberInfo;
  101. if (lni != null)
  102. return lni.Line;
  103. return -1;
  104. }
  105. }
  106. #region Tokenizer
  107. internal object InternalRead(bool expectEOF, object eofValue)
  108. {
  109. object r;
  110. do
  111. {
  112. var current = source;
  113. r = AddMeta(current, InternalReadNoLoop(expectEOF, eofValue));
  114. } while (r == this);
  115. return r;
  116. }
  117. private object AddMeta(ISequence<char> current, object p)
  118. {
  119. var lni = current as ILineNumberInfo;
  120. var lniend = source as ILineNumberInfo;
  121. var obj = p as XronosObject;
  122. if (obj != null && lni != null && lniend != null)
  123. return obj.withMeta((IPersistentMap)(obj.meta() ?? PersistentHashMap.Empty)
  124. .assoc(RT.Line, lni.Line)
  125. .assoc(RT.Char, lni.Char)
  126. .assoc(RT.LineEnd, lniend.Line)
  127. .assoc(RT.CharEnd, lniend.Char)
  128. .assoc(RT.Filename, lni.Filename));
  129. return p;
  130. }
  131. private object InternalReadNoLoop(bool expectEOF, object eofValue)
  132. {
  133. EatSpaces(Action.NoNullCheck);
  134. if (source == null)
  135. {
  136. if (!expectEOF)
  137. throw new EndOfStreamException();
  138. return eofValue;
  139. }
  140. char c = source.first();
  141. if (char.IsDigit(c))
  142. {
  143. return ReadNumber();
  144. }
  145. var fnMacro = SpecialMacros.ResolveMacro(c);
  146. if (fnMacro != null)
  147. {
  148. return FunctionHelper.Invoke<object>(fnMacro, this, c);
  149. }
  150. if ((c == '+' || c == '-') && source.rest() != null && char.IsDigit(source.rest().first()))
  151. {
  152. return ReadNumber();
  153. }
  154. var token = ReadToken();
  155. object result;
  156. if (InterpretToken(token, out result))
  157. return result;
  158. return BadToken(token);
  159. }
  160. private object BadToken(string token)
  161. {
  162. throw new ReaderException("Invalid Token: " + token);
  163. }
  164. private bool InterpretToken(string token, out object value)
  165. {
  166. value = null;
  167. switch (token)
  168. {
  169. case "nil":
  170. return true;
  171. case "true":
  172. value = RT.True;
  173. return true;
  174. case "false":
  175. value = RT.False;
  176. return true;
  177. case "/":
  178. value = Slash;
  179. return true;
  180. }
  181. value = MatchSymbol(token);
  182. return value != null;
  183. }
  184. private object MatchSymbol(string token)
  185. {
  186. Match m = reSymbol.Match(token);
  187. if (!m.Success)
  188. return null;
  189. var ns = m.Groups[1].Value;
  190. var name = m.Groups[2].Value;
  191. if (ns != null && ns.EndsWith(":/") || name.EndsWith(":") || token.IndexOf("::", 1) != -1)
  192. return null;
  193. if (token.StartsWith("::"))
  194. throw new NotImplementedException("AUTO KEYWORDS");
  195. if (token[0] == ':')
  196. return Keyword.Intern(Symbol.Intern(token.Substring(1)));
  197. return Symbol.Intern(token);
  198. }
  199. internal string ReadToken()
  200. {
  201. return ReadString((char c) => !IsWhitespace(c) && !SpecialMacros.IsTerminatingMacro(c));
  202. }
  203. private object ReadNumber()
  204. {
  205. string s = ReadString((char c) => !IsWhitespace(c) && !SpecialMacros.IsMacro(c));
  206. return RT.NormalizeNumber(s);
  207. }
  208. internal string ReadString(Func<char, bool> predicate)
  209. {
  210. var s = source;
  211. var sb = new StringBuilder();
  212. while (s != null && predicate(s.first()))
  213. {
  214. Append(sb, ref s);
  215. }
  216. source = s;
  217. return sb.ToString();
  218. }
  219. private void Append(StringBuilder sb, ref ISequence<char> s)
  220. {
  221. sb.Append(s.first());
  222. s = s.rest();
  223. }
  224. internal Reader Advance()
  225. {
  226. if (source == null)
  227. throw new EndOfStreamException();
  228. source = source.rest();
  229. return this;
  230. }
  231. internal char Peek()
  232. {
  233. if (source == null)
  234. throw new EndOfStreamException();
  235. return source.first();
  236. }
  237. enum Action
  238. {
  239. NoNullCheck,
  240. DoNullCheck,
  241. }
  242. private void EatSpaces()
  243. {
  244. EatSpaces(Action.DoNullCheck);
  245. }
  246. private void EatSpaces(Action check)
  247. {
  248. while (source != null && IsWhitespace(source.first()))
  249. source = source.rest();
  250. if (check == Action.DoNullCheck && source == null)
  251. throw new EndOfStreamException();
  252. }
  253. internal IList ReadList(char terminator)
  254. {
  255. var result = new ArrayList();
  256. EatSpaces();
  257. while (source.first() != terminator)
  258. {
  259. var current = source;
  260. var item = AddMeta(current, InternalReadNoLoop(false, null));
  261. if (item != this)
  262. result.Add(item);
  263. EatSpaces();
  264. }
  265. return result;
  266. }
  267. #endregion
  268. }
  269. }