/System.Xronos/Reader.cs
C# | 322 lines | 246 code | 53 blank | 23 comment | 70 complexity | 4442afc7c06c98717b0f1088644923ec MD5 | raw file
- /* ****************************************************************************
- *
- * Copyright (c) 2008 Stefan Rusek and Benjamin Pollack
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * ***************************************************************************/
- using System.Xronos.Language;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.IO;
- using System.Text.RegularExpressions;
- using System.Collections;
- using System.Xronos.Builtins;
- using System.Xronos.Scripting;
-
- namespace System.Xronos
- {
- public class Reader
- {
- public static readonly Symbol Var = Symbol.Create("var");
- public static readonly Symbol Slash = Symbol.Create("/");
-
- Regex reSymbol = new Regex(@"[:]?([\D-[/]].*/)?([\D-[/]][^/]*)");
-
- ISequence<char> source;
-
- private Reader() { }
-
- internal bool IsWhitespace(char c)
- {
- return char.IsWhiteSpace(c) || c == ',';
- }
-
- public static object Read(XronosContext context, string filename, string source)
- {
- return Read(context, filename, new System.IO.StringReader(source), 1);
- }
-
- public static object Read(XronosContext context, string filename, TextReader reader, int line)
- {
- Statistics.ReaderTime.Start();
- try
- {
- var source = TextReaderSequence.FromTextReader(filename, reader, line);
- var list = new ArrayList();
- while (source != null)
- {
- object obj;
- source = Read(source, out obj);
- if (obj != null)
- list.Add(obj);
- }
-
- if (list.Count == 0)
- list.Add(null);
- if (list.Count == 1 && list[0] != null)
- return list[0];
-
- return RT.List(list.ToArray()).cons(SpecialForms.Do.withMeta(PersistentHashMap.Create(new object[] { "auto", true})));
- }
- finally
- {
- Statistics.ReaderTime.Stop();
- }
- }
-
- private static ISequence<char> Read(ISequence<char> source, out object form)
- {
- var reader = new Reader { source = source };
- form = reader.Read();
- return reader.source;
- }
-
- private object Read()
- {
- try
- {
- return InternalRead(true, null);
- }
- catch (Exception ex)
- {
- var lni = source as ILineNumberInfo;
- if (lni == null)
- throw;
-
- throw new ReaderException(lni, ex);
- }
- }
-
- public int Line
- {
- get
- {
- var lni = source as ILineNumberInfo;
- if (lni != null)
- return lni.Line;
- return -1;
- }
- }
-
- #region Tokenizer
- internal object InternalRead(bool expectEOF, object eofValue)
- {
- object r;
- do
- {
- var current = source;
- r = AddMeta(current, InternalReadNoLoop(expectEOF, eofValue));
- } while (r == this);
- return r;
- }
-
- private object AddMeta(ISequence<char> current, object p)
- {
- var lni = current as ILineNumberInfo;
- var lniend = source as ILineNumberInfo;
- var obj = p as XronosObject;
- if (obj != null && lni != null && lniend != null)
- return obj.withMeta((IPersistentMap)(obj.meta() ?? PersistentHashMap.Empty)
- .assoc(RT.Line, lni.Line)
- .assoc(RT.Char, lni.Char)
- .assoc(RT.LineEnd, lniend.Line)
- .assoc(RT.CharEnd, lniend.Char)
- .assoc(RT.Filename, lni.Filename));
- return p;
- }
-
- private object InternalReadNoLoop(bool expectEOF, object eofValue)
- {
- EatSpaces(Action.NoNullCheck);
-
- if (source == null)
- {
- if (!expectEOF)
- throw new EndOfStreamException();
- return eofValue;
- }
-
- char c = source.first();
-
- if (char.IsDigit(c))
- {
- return ReadNumber();
- }
-
- var fnMacro = SpecialMacros.ResolveMacro(c);
- if (fnMacro != null)
- {
- return FunctionHelper.Invoke<object>(fnMacro, this, c);
- }
-
- if ((c == '+' || c == '-') && source.rest() != null && char.IsDigit(source.rest().first()))
- {
- return ReadNumber();
- }
-
- var token = ReadToken();
- object result;
- if (InterpretToken(token, out result))
- return result;
-
- return BadToken(token);
- }
-
- private object BadToken(string token)
- {
- throw new ReaderException("Invalid Token: " + token);
- }
-
- private bool InterpretToken(string token, out object value)
- {
- value = null;
- switch (token)
- {
- case "nil":
- return true;
-
- case "true":
- value = RT.True;
- return true;
-
- case "false":
- value = RT.False;
- return true;
-
- case "/":
- value = Slash;
- return true;
- }
-
- value = MatchSymbol(token);
- return value != null;
- }
-
- private object MatchSymbol(string token)
- {
- Match m = reSymbol.Match(token);
- if (!m.Success)
- return null;
-
- var ns = m.Groups[1].Value;
- var name = m.Groups[2].Value;
-
- if (ns != null && ns.EndsWith(":/") || name.EndsWith(":") || token.IndexOf("::", 1) != -1)
- return null;
-
- if (token.StartsWith("::"))
- throw new NotImplementedException("AUTO KEYWORDS");
-
- if (token[0] == ':')
- return Keyword.Intern(Symbol.Intern(token.Substring(1)));
- return Symbol.Intern(token);
- }
-
- internal string ReadToken()
- {
- return ReadString((char c) => !IsWhitespace(c) && !SpecialMacros.IsTerminatingMacro(c));
- }
-
- private object ReadNumber()
- {
- string s = ReadString((char c) => !IsWhitespace(c) && !SpecialMacros.IsMacro(c));
-
- return RT.NormalizeNumber(s);
- }
-
- internal string ReadString(Func<char, bool> predicate)
- {
- var s = source;
- var sb = new StringBuilder();
-
- while (s != null && predicate(s.first()))
- {
- Append(sb, ref s);
- }
-
- source = s;
- return sb.ToString();
- }
-
- private void Append(StringBuilder sb, ref ISequence<char> s)
- {
- sb.Append(s.first());
- s = s.rest();
- }
-
- internal Reader Advance()
- {
- if (source == null)
- throw new EndOfStreamException();
- source = source.rest();
- return this;
- }
-
- internal char Peek()
- {
- if (source == null)
- throw new EndOfStreamException();
- return source.first();
- }
-
- enum Action
- {
- NoNullCheck,
- DoNullCheck,
- }
-
- private void EatSpaces()
- {
- EatSpaces(Action.DoNullCheck);
- }
-
- private void EatSpaces(Action check)
- {
- while (source != null && IsWhitespace(source.first()))
- source = source.rest();
-
- if (check == Action.DoNullCheck && source == null)
- throw new EndOfStreamException();
- }
-
- internal IList ReadList(char terminator)
- {
- var result = new ArrayList();
-
- EatSpaces();
-
- while (source.first() != terminator)
- {
- var current = source;
- var item = AddMeta(current, InternalReadNoLoop(false, null));
- if (item != this)
- result.Add(item);
-
- EatSpaces();
- }
-
- return result;
- }
-
- #endregion
- }
- }