/System.Xronos/Builtins/SpecialMacros.cs
C# | 369 lines | 329 code | 17 blank | 23 comment | 13 complexity | f640e4f236978eb6a599cbc04bfbbced 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;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Xronos.Language;
- using System.Text.RegularExpressions;
-
- namespace System.Xronos.Builtins
- {
- public sealed partial class SpecialMacros
- {
- public static readonly Symbol Quote = Symbol.Create("quote");
- public static readonly Symbol Deref = Symbol.Create("xronos", "deref");
- public static readonly Symbol Meta = Symbol.Create("xronos", "meta");
-
- static ReaderBase[] macros = new ReaderBase[128];
-
- static SpecialMacros()
- {
- macros['"'] = new StringReader();
- macros[';'] = new CommentReader();
- macros['\''] = new WrappingReader(Quote);
- macros['@'] = new WrappingReader(Deref);
- macros['^'] = new WrappingReader(Meta);
- macros['`'] = new SyntaxQuoteReader();
- macros['~'] = new UnquoteReader();
- macros['('] = new ListReader();
- macros[')'] = new UnmatchedDelimiterReader();
- macros['['] = new VectorReader();
- macros[']'] = new UnmatchedDelimiterReader();
- macros['{'] = new MapReader();
- macros['}'] = new UnmatchedDelimiterReader();
- macros['\\'] = new CharacterReader();
- macros['%'] = new ArgReader();
- macros['#'] = new DispatchReader();
- }
-
- internal static object ResolveMacro(char c)
- {
- if (c >= macros.Length)
- return null;
- return macros[c];
- }
-
- internal static bool IsMacro(char c)
- {
- return ResolveMacro(c) != null;
- }
-
- internal static bool IsTerminatingMacro(char c)
- {
- return c != '#' && IsMacro(c);
- }
-
- #region Readers
-
- internal abstract class ReaderBase : XronosObject
- {
- protected ReaderBase() : base(null) { }
-
- public virtual object Invoke(object reader, object c) { return Read((Reader)reader, (char)c); }
- protected abstract object Read(Reader reader, char c);
-
- public override XronosObject withMeta(IPersistentMap meta)
- {
- throw new NotImplementedException();
- }
- }
-
- class UnmatchedDelimiterReader : ReaderBase
- {
- protected override object Read(Reader reader, char c)
- {
- throw new ReaderException("Unmatched delimiter: " + c);
- }
- }
-
- class StringReader : ReaderBase
- {
- protected override object Read(Reader reader, char quote)
- {
- reader.Advance();
-
- int slashes = 0;
- char prev = '\0';
-
- Func<char, bool> predicate = c =>
- {
- slashes = prev == '\\' ? slashes + 1 : 0;
- prev = c;
-
- return c != quote || (slashes & 1) != 0;
- };
-
- string str = reader.ReadString(predicate);
- reader.Advance();
-
- return UnescapeString(str);
- }
-
- private object UnescapeString(string str)
- {
- return CharacterReader.reCharEscapes.Replace(str,
- (Match m) => new string(CharacterReader.ParseCharacter(m.Groups[1].Value), 1));
- }
- }
-
- class CommentReader : ReaderBase
- {
- protected override object Read(Reader reader, char semiColon)
- {
- reader.ReadString((char c) => c != '\n' && c != '\r');
- return reader;
- }
- }
-
- class WrappingReader : ReaderBase
- {
- readonly Symbol sym;
- public WrappingReader(Symbol sym)
- {
- this.sym = sym;
- }
-
- protected override object Read(Reader reader, char c)
- {
- reader.Advance();
- return RT.List(sym, reader.InternalRead(false, null));
- }
- }
-
- class ListReader : ReaderBase
- {
- protected override object Read(Reader reader, char c)
- {
- reader.Advance();
- var list = reader.ReadList(')');
- reader.Advance();
-
- return PersistentList.Create(list);
- }
- }
-
- class VectorReader : ReaderBase
- {
- protected override object Read(Reader reader, char c)
- {
- reader.Advance();
- var list = reader.ReadList(']');
- reader.Advance();
-
- return PersistentVector.Create(list);
- }
- }
-
- class MapReader : ReaderBase
- {
- protected override object Read(Reader reader, char c)
- {
- reader.Advance();
- var result = PersistentHashMap.Create(reader.ReadList('}'));
- reader.Advance();
-
- return result;
- }
- }
-
- class CharacterReader : ReaderBase
- {
- public static Regex reCharEscapes = new Regex(@"\\(newline|space|tab|backspace|formfeed|return|(u[a-zA-Z0-9]{4})|(o[0-7]{1,3})|.)");
-
- protected override object Read(Reader reader, char c)
- {
- reader.Advance();
- return ParseCharacter(reader.ReadToken());
- }
-
- public static char ParseCharacter(string token)
- {
- if (token.Length == 1)
- return token[0];
-
- switch (token)
- {
- case "newline":
- return '\n';
- case "space":
- return ' ';
- case "tab":
- return '\t';
- case "backspace":
- return '\b';
- case "formfeed":
- return '\f';
- case "return":
- return '\r';
-
- default:
-
- switch (token[0])
- {
- case 'u':
- #if FixClojureBugs
- if (token.Length > 5)
- throw new ReaderException("Invalid hex escape sequence length: " + token.Length);
- #endif
- return ParseNumericCharacter(token, 1, 4, 16);
-
- case 'o':
- return ParseOctalEscape(token);
- }
-
- throw new ReaderException("Unsupported character: \\" + token);
- }
- }
-
- private static char ParseOctalEscape(string token)
- {
- int len = token.Length - 1;
- if (len > 3)
- throw new ReaderException("Invalid octal escape sequence length: " + len);
- char oc = ParseNumericCharacter(token, 1, len, 8);
- if (oc > 0xFF)
- throw new ReaderException("Octal escape sequence must be in range [0, 377].");
- return oc;
- }
-
- private static char ParseNumericCharacter(string token, int offset, int chars, int radix)
- {
- int c = 0, end = offset + chars;
- if (token.Length < end)
- throw new ArgumentException("Invalid unicode character: \\" + token, "token");
-
- for (int i = offset; i < end; i++)
- c = c * radix + Digit(token[i], radix);
-
- return unchecked((char)c);
- }
-
- private static int Digit(char c, int radix)
- {
- int result = int.MaxValue;
- if (c >= '0' && c <= '9')
- result = c - '0';
- else if (c >= 'a' && c <= 'z')
- result = c - 'a';
- else if (c >= 'A' && c <= 'Z')
- result = c - 'A';
-
- if (result > radix)
- throw new ArgumentException("Invalid digit: " + c, "c");
-
- return result;
- }
- }
-
- class ArgReader : ReaderBase
- {
- protected override object Read(Reader reader, char c)
- {
- throw new NotImplementedException();
- }
- }
-
- class DispatchReader : ReaderBase
- {
- ReaderBase[] macros = new ReaderBase[128];
-
- public DispatchReader()
- {
- macros['^'] = new MetaReader();
- macros['\''] = new WrappingReader(Reader.Var);
- macros['"'] = new RegexReader();
- macros['('] = new FnReader();
- macros['{'] = new SetReader();
- macros['!'] = new CommentReader();
- }
-
- protected override object Read(Reader reader, char quote)
- {
- reader.Advance();
- char c = reader.Peek();
- if (c < macros.Length)
- {
- ReaderBase fn = macros[c];
- if (fn != null)
- return fn.Invoke(reader, c);
- }
- throw new ReaderException("No dispatch macro for: " + c);
- }
- }
-
- class RegexReader : ReaderBase
- {
- readonly StringReader inner = new StringReader();
-
- protected override object Read(Reader reader, char c)
- {
- var pattern = (string)inner.Invoke(reader, c);
- return new Regex(pattern);
- }
- }
-
- class MetaReader : ReaderBase
- {
- protected override object Read(Reader reader, char c)
- {
- reader.Advance();
- var meta = reader.InternalRead(false, null);
-
- int line = reader.Line;
-
- if (meta is string || meta is Symbol || meta is Keyword)
- meta = RT.Map(RT.Tag, meta);
- else if (!(meta is IPersistentMap))
- throw new ReaderException("Metadata must be Symbol,Keyword,String or Map");
-
- var obj = reader.InternalRead(false, null) as XronosObject;
- if (obj == null)
- throw new ReaderException("Metadata can only be applied to XronosObjects");
-
- return obj.withMeta(((IPersistentMap)meta).assocEx(RT.Line, line));
- }
- }
-
- class FnReader : ReaderBase
- {
- protected override object Read(Reader reader, char c)
- {
- var list = reader.ReadList(')');
-
- return RT.ListStar(list).cons(PersistentVector.Empty).cons(Symbol.Create("fn"));
- }
- }
-
- class SetReader : ReaderBase
- {
- protected override object Read(Reader reader, char c)
- {
- throw new NotImplementedException();
- }
- }
-
- #endregion
- }
- }