From 67f56f0795ac0214f9828c42f8face229e204c1d Mon Sep 17 00:00:00 2001 From: David Miller Date: Sat, 21 Feb 2009 06:55:20 +0000 Subject: ClojureCLR: added ClojureCLR project to repo. --- .../Clojure.Tests/LibTests/LispReaderTests.cs | 1789 ++++++++++++++++++++ 1 file changed, 1789 insertions(+) create mode 100644 ClojureCLR/Clojure/Clojure.Tests/LibTests/LispReaderTests.cs (limited to 'ClojureCLR/Clojure/Clojure.Tests/LibTests/LispReaderTests.cs') diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/LispReaderTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/LispReaderTests.cs new file mode 100644 index 00000000..99d45e36 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/LispReaderTests.cs @@ -0,0 +1,1789 @@ +/** + * Copyright (c) David Miller. All rights reserved. + * The use and distribution terms for this software are covered by the + * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) + * which can be found in the file epl-v10.html at the root of this distribution. + * By using this software in any fashion, you are agreeing to be bound by + * the terms of this license. + * You must not remove this notice, or any other, from this software. + **/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + + +using NUnit.Framework; +using Rhino.Mocks; + +using clojure.lang; + +using RMExpect = Rhino.Mocks.Expect; +using java.math; + +using clojure.lang.Readers; + + +namespace Clojure.Tests.LibTests +{ + [TestFixture] + public class LispReaderTests : AssertionHelper + { + #region matchNumber tests + + [Test] + public void MatchNumberMatchesZero() + { + object o1 = LispReader.matchNumber("0"); + object o2 = LispReader.matchNumber("-0"); + object o3 = LispReader.matchNumber("+0"); + + Expect(o1, EqualTo(0)); + Expect(o2, EqualTo(0)); + Expect(o3, EqualTo(0)); + } + + [Test] + public void MatchNumberMatchesDecimal() + { + object o1 = LispReader.matchNumber("123"); + object o2 = LispReader.matchNumber("+123"); + object o3 = LispReader.matchNumber("-123"); + object o4 = LispReader.matchNumber("123456789123456789123456789"); + + Expect(o1, EqualTo(123)); + Expect(o2, EqualTo(123)); + Expect(o3, EqualTo(-123)); + Expect(o4, EqualTo(new BigInteger("123456789123456789123456789"))); + } + + [Test] + public void MatchNumberMatchesHexadecimal() + { + object o1 = LispReader.matchNumber("0X12A"); + object o2 = LispReader.matchNumber("0xFFF"); + object o3 = LispReader.matchNumber("0xFFFFFFFFFFFFFFFFFFFFFFFF"); + + Expect(o1, EqualTo(0x12A)); + Expect(o2, EqualTo(0xFFF)); + Expect(o3, EqualTo(new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFF", 16))); + } + + + [Test] + public void MatchNumberMatchesOctal() + { + object o1 = LispReader.matchNumber("0123"); + object o2 = LispReader.matchNumber("+0123"); + object o3 = LispReader.matchNumber("-0123"); + object o4 = LispReader.matchNumber("01234567012345670123456777"); + + Expect(o1, EqualTo(83)); + Expect(o2, EqualTo(83)); + Expect(o3, EqualTo(-83)); + Expect(o4, EqualTo(new BigInteger("1234567012345670123456777", 8))); + } + + [Test] + public void MatchNumberMatchesSpecifiedRadix() + { + object o1 = LispReader.matchNumber("2R1100"); + object o2 = LispReader.matchNumber("4R123"); + object o3 = LispReader.matchNumber("-4R123"); + object o4 = LispReader.matchNumber("30R1234AQ"); + + Expect(o1, EqualTo(12)); + Expect(o2, EqualTo(27)); + Expect(o3, EqualTo(-27)); + Expect(o4, EqualTo(new BigInteger("1234AQ", 30).longValue())); + } + + [Test] + public void MatchNumberMatchesFloats() + { + object o1 = LispReader.matchNumber("123.7"); + object o2 = LispReader.matchNumber("-123.7E4"); + object o3 = LispReader.matchNumber("+1.237e4"); + object o4 = LispReader.matchNumber("+1.237e-4"); + object o5 = LispReader.matchNumber("1.237e+4"); + + Expect(o1, EqualTo(123.7)); + Expect(o2, EqualTo(-1237000.0)); + Expect(o3, EqualTo(1.237e4)); + Expect(o4, EqualTo(1.237e-4)); + Expect(o5, EqualTo(1.237e4)); + } + + [Test] + public void MatchNumberMatchesDecimals() + { + object o1 = LispReader.matchNumber("123.7M"); + // MS implementation of BigDecimal parser does not allow these. + //object o2 = LispReader.matchNumber("-123.7E4M"); + //object o3 = LispReader.matchNumber("+1.237e4M"); + //object o4 = LispReader.matchNumber("+1.237e-4M"); + //object o5 = LispReader.matchNumber("1.237e+4M"); + + Expect(o1, EqualTo(new BigDecimal("123.7"))); + //Expect(o2, EqualTo(-1237000.0M)); + //Expect(o3, EqualTo(1.237e4M)); + //Expect(o4, EqualTo(1.237e-4M)); + //Expect(o5, EqualTo(1.237e4M)); + } + + [Test] + public void MatchNumberMatchesRatios() + { + object o1 = LispReader.matchNumber("12/1"); + object o2 = LispReader.matchNumber("12/4"); + object o3 = LispReader.matchNumber("12/5"); + object o4 = LispReader.matchNumber("12345678900000/123456789"); + + Expect(o1, EqualTo(12)); + Expect(o2, EqualTo(3)); + Expect(o3, EqualTo(new Ratio(new BigInteger("12"),new BigInteger("5")))); + Expect(o4, EqualTo(100000)); + } + + [Test] + public void MatchNumberReadsWholeString() + { + object o1 = LispReader.matchNumber(" 123"); + object o2 = LispReader.matchNumber("123 "); + object o3 = LispReader.matchNumber(" 12.3"); + object o4 = LispReader.matchNumber("12.3 "); + object o5 = LispReader.matchNumber(" 1/23"); + object o6 = LispReader.matchNumber("1/23 "); + + Expect(o1, Null); + Expect(o2, Null); + Expect(o3, Null); + Expect(o4, Null); + Expect(o5, Null); + Expect(o6, Null); + } + + [Test] + public void MatchNumberFailsToMatchWeirdThings() + { + object o1 = LispReader.matchNumber("123a"); + object o2 = LispReader.matchNumber("0x123Z"); + object o4 = LispReader.matchNumber("12.4/24.2"); + object o5 = LispReader.matchNumber("1.7M3"); + + Expect(o1, Null); + Expect(o2, Null); + Expect(o4, Null); + Expect(o5, Null); + } + + + [Test] + [ExpectedException(typeof(java.lang.NumberFormatException))] + public void MatchNumberFailsOnRadixSnafu() + { + object o3 = LispReader.matchNumber("10RAA"); + } + #endregion + + #region Helpers + + static TextReader CreatePushbackReaderFromString(string s) + { + return new StringReader(s); + } + + static object ReadFromString(string s) + { + return LispReader.read(CreatePushbackReaderFromString(s),true,null,false); + } + + static LineNumberingReader CreateLNPBRFromString(string s) + { + return new LineNumberingReader(new StringReader(s)); + } + + static object ReadFromStringNumbering(string s) + { + return LispReader.read(CreateLNPBRFromString(s),true,null,false); + } + + + #endregion + + #region Testing EOF + + [Test] + public void EofValueReturnedOnEof() + { + object o = LispReader.read(CreatePushbackReaderFromString(" "), false, 7, false); + Expect(o, EqualTo(7)); + } + + [Test] + [ExpectedException(typeof(System.IO.EndOfStreamException))] + public void EofValueFailsOnEof() + { + object o = LispReader.read(CreatePushbackReaderFromString(" "), true, 7, false); + } + + #endregion + + #region Testing a few numbers + + [Test] + public void ReadReadsIntegers() + { + object o1 = ReadFromString("123"); + object o2 = ReadFromString("-123"); + object o3 = ReadFromString("+123"); + object o4 = ReadFromString("123456789123456789123456789"); + + Expect(o1, EqualTo(123)); + Expect(o2, EqualTo(-123)); + Expect(o3, EqualTo(123)); + Expect(o4, EqualTo(new BigInteger("123456789123456789123456789"))); + } + + [Test] + public void ReadReadsFloats() + { + object o1 = ReadFromString("123.4"); + object o2 = ReadFromString("-123.4E4"); + object o3 = ReadFromString("+123.4E-2"); + + Expect(o1, EqualTo(123.4)); + Expect(o2, EqualTo(-123.4E4)); + Expect(o3, EqualTo(123.4E-2)); + } + + [Test] + public void ReadReadsRatios() + { + object o1 = ReadFromString("123/456"); + object o2 = ReadFromString("-123/456"); + object o3 = ReadFromString("+123/456"); + + Expect(o1, TypeOf(typeof(Ratio))); + Expect(o2, TypeOf(typeof(Ratio))); + Expect(o3, TypeOf(typeof(Ratio))); + } + + + + #endregion + + #region Special tokens + + [Test] + public void SlashAloneIsSlash() + { + object o = ReadFromString("/"); + Expect(o, TypeOf(typeof(Symbol))); + Expect(((Symbol)o).Name, EqualTo("/")); + Expect(((Symbol)o).Namespace, Null); + } + + [Test] + public void ClojureSlashIsSpecial() + { + object o = ReadFromString("clojure.core//"); + Expect(o, TypeOf(typeof(Symbol))); + Expect(((Symbol)o).Name, EqualTo("/")); + Expect(((Symbol)o).Namespace, EqualTo("clojure.core")); + } + + [Test] + public void TrueReturnsT() + { + object o = ReadFromString("true"); + Expect(o, TypeOf(typeof(bool))); + Expect(o,EqualTo(true)); + } + + [Test] + public void FalseReturnsF() + { + object o = ReadFromString("false"); + Expect(o, TypeOf(typeof(bool))); + Expect(o, EqualTo(false)); + } + + [Test] + public void NilIsNull() + { + object o = ReadFromString("nil"); + Expect(o, Null); + } + + #endregion + + #region Symbolic tests + + [Test] + public void ReadReadsSymbolWithNoNS() + { + object o1 = ReadFromString("abc"); + + Expect(o1, TypeOf(typeof(Symbol))); + Expect(((Symbol)o1).Name, EqualTo("abc")); + Expect(((Symbol)o1).Namespace, Null); + } + + [Test] + public void ReadReadsSymbolWithNS() + { + object o1 = ReadFromString("ab/cd"); + + Expect(o1, TypeOf(typeof(Symbol))); + Expect(((Symbol)o1).Name, EqualTo("cd")); + Expect(((Symbol)o1).Namespace, EqualTo("ab")); + } + + [Test] + public void TwoSlashesIsOkayApparently() + { + object o1 = ReadFromString("ab/cd/e"); + + Expect(o1, TypeOf(typeof(Symbol))); + Expect(((Symbol)o1).Name, EqualTo("e")); + Expect(((Symbol)o1).Namespace, EqualTo("ab/cd")); + } + + [Test] + [ExpectedException(typeof(Exception))] + public void NamespaceEndingWithColonSlashIsBad() + { + object o1 = ReadFromString("ab:/cd"); + } + + [Test] + [ExpectedException(typeof(Exception))] + public void NameEndingWithColonIsBad() + { + object o1 = ReadFromString("ab/cd:"); + } + + [Test] + [ExpectedException(typeof(Exception))] + public void NameEndingWithColonIsBad2() + { + object o1 = ReadFromString("cd:"); + } + + [Test] + public void NameMayContainMultipleColons() + { + object o1 = ReadFromString("a:b:c/d:e:f"); + Expect(o1, TypeOf(typeof(Symbol))); + } + + [Test] + [ExpectedException(typeof(Exception))] + public void NameContainingDoubleColonNotAtBeginningIsBad() + { + object o1 = ReadFromString("ab::cd"); + } + + [Test] + [ExpectedException(typeof(Exception))] + public void NamespaceContainingDoubleColonNotAtBeginningIsBad() + { + object o1 = ReadFromString("ab::cd/ef"); + } + + #endregion + + #region Keyword tests + + [Test] + public void LeadingColonIsKeyword() + { + object o1 = ReadFromString(":abc"); + Expect(o1, TypeOf(typeof(Keyword))); + Expect(((Keyword)o1).Namespace, Null); + Expect(((Keyword)o1).Name, EqualTo("abc")); + } + + [Test] + public void LeadingColonWithNSIsKeyword() + { + object o1 = ReadFromString(":ab/cd"); + Expect(o1, TypeOf(typeof(Keyword))); + Expect(((Keyword)o1).Namespace, EqualTo("ab")); + Expect(((Keyword)o1).Name, EqualTo("cd")); + } + + // TODO: Add more tests dealing with :: resolution. + + [Test] + public void LeadingDoubleColonMakesKeywordInCurrentNamespace() + { + object o1 = ReadFromString("::abc"); + Expect(o1, TypeOf(typeof(Keyword))); + Expect(((Keyword)o1).Namespace, EqualTo(((Namespace)RT.CURRENT_NS.deref()).Name.Name)); + Expect(((Keyword)o1).Name, EqualTo("abc")); + } + + // At one time, this test worked. Now, according to the documentation, it should not work. Did something change? Never mind. + //[Test] + //public void LeadingDoubleColonDoesNotSetNamespaceIfPeriodsInName() + //{ + // object o1 = ReadFromString("::ab.cd"); + // Expect(o1, TypeOf(typeof(Keyword))); + // Expect(((Keyword)o1).Namespace, Null); + // Expect(((Keyword)o1).Name, EqualTo("ab.cd")); + //} + + #endregion + + #region String tests + + [Test] + public void DoubleQuotesSurroundAString() + { + object o1 = ReadFromString("\"abc\""); + Expect(o1,TypeOf(typeof(string))); + Expect(o1, EqualTo("abc")); + } + + [Test] + [ExpectedException(typeof(System.IO.EndOfStreamException))] + public void NoEndingDoubleQuoteFails() + { + object o1 = ReadFromString("\"abc"); + } + + [Test] + public void EmptyStringWorks() + { + object o1 = ReadFromString("\"\""); + Expect(o1, TypeOf(typeof(string))); + Expect(o1, EqualTo(String.Empty)); + + } + + [Test] + public void EscapesWorkInStrings() + { + char[] chars = new char[] { + '"', 'a', + '\\', 't', 'b', + '\\', 'r', 'c', + '\\', 'n', 'd', + '\\', '\\', 'e', + '\\', '"', 'f', + '\\', 'b', 'g', + '\\', 'f', 'h', '"' + }; + + string s = new String(chars); + Expect(s.Length, EqualTo(24)); + + + object o1 = ReadFromString(s); + Expect(o1, EqualTo("a\tb\rc\nd\\e\"f\bg\fh")); + } + + [Test] + [ExpectedException(typeof(System.IO.EndOfStreamException))] + public void EOFinEscapeIsError() + { + char[] chars = new char[] { + '"', 'a', + '\\', 't', 'b', + '\\', 'r', 'c', + '\\', 'n', 'd', + '\\' + }; + string s = new String(chars); + + object o1 = ReadFromString(s); + } + + [Test] + public void UnicodeEscapeInsertsUnicodeCharacter() + { + char[] chars = new char[] { + '"', 'a', + '\\', 'u', '1', '2', 'C', '4', + 'b', '"' + }; + + string s = new String(chars); + + object o1 = ReadFromString(s); + Expect(o1, EqualTo("a\u12C4b")); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void UnicodeEscapeWithBadCharacterFails() + { + char[] chars = new char[] { + '"', 'a', + '\\', 'u', '1', '2', 'X', '4', + 'b', '"' + }; + string s = new String(chars); + + object o1 = ReadFromString(s); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void UnicodeEscapeWithEOFFails() + { + char[] chars = new char[] { + '"', 'a', + '\\', 'u', '1', '2', 'A', '"' + }; + string s = new String(chars); + + object o1 = ReadFromString(s); + } + + + [Test] + public void OctalEscapeInsertsCharacter() + { + char[] chars = new char[] { + '"', 'a', + '\\', '1', '2', '4', + 'b', '"' + }; + string s = new String(chars); + + object o1 = ReadFromString(s); + Expect(o1, EqualTo("a\x0054b")); // hex/octal conversion + } + + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void OctalEscapeWithBadDigitFails() + { + char[] chars = new char[] { + '"', 'a', + '\\', '1', '8', '4', + 'b', '"' + }; + string s = new String(chars); + + object o1 = ReadFromString(s); + } + + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void OctalEscapeWithEOFFails() + { + char[] chars = new char[] { + '"', 'a', + '\\', '1', '8', '"' + }; + string s = new String(chars); + + object o1 = ReadFromString(s); + } + + [ExpectedException(typeof(ArgumentException))] + public void OctalEscapeOutOfRangeFails() + { + char[] chars = new char[] { + '"', 'a', + '\\', '4', '7', '7', + 'b', '"' + }; + string s = new String(chars); + + object o1 = ReadFromString(s); + } + + + #endregion + + #region Character tests + + [Test] + public void BackslashYieldsNextCharacter() + { + object o1 = ReadFromString("\\a"); + Expect(o1, TypeOf(typeof(Char))); + Expect(o1, EqualTo('a')); + } + + [Test] + public void BackslashYieldsNextCharacterStoppingAtTerminator() + { + object o1 = ReadFromString("\\a b"); + Expect(o1, TypeOf(typeof(Char))); + Expect(o1, EqualTo('a')); + } + + [Test] + [ExpectedException(typeof(System.IO.EndOfStreamException))] + public void BackslashFollowedByEOFFails() + { + object o1 = ReadFromString("\\"); + } + + [Test] + public void BackslashRecognizesSpecialNames() + { + object o1 = ReadFromString("\\newline"); + object o2 = ReadFromString("\\space"); + object o3 = ReadFromString("\\tab"); + object o4 = ReadFromString("\\backspace"); + object o5 = ReadFromString("\\formfeed"); + object o6 = ReadFromString("\\return"); + + Expect(o1, EqualTo('\n')); + Expect(o2, EqualTo(' ')); + Expect(o3, EqualTo('\t')); + Expect(o4, EqualTo('\b')); + Expect(o5, EqualTo('\f')); + Expect(o6, EqualTo('\r')); + } + + [Test] + public void BackslashRecognizesUnicode() + { + object o1 = ReadFromString("\\u12C4"); + Expect(o1, EqualTo('\u12C4')); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void BackslashUnicodeWithEOFFails() + { + object o1 = ReadFromString("\\u12C 4"); + } + + [Test] + [ExpectedException(typeof(Exception))] + public void BackslashUnicodeInBadRangeFails() + { + object o1 = ReadFromString("\\uDAAA"); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void BackslashUnicodeWithBadDigitFails() + { + object o1 = ReadFromString("\\u12X4"); + } + + [Test] + public void BackslashRecognizesOctal() + { + object o1 = ReadFromString("\\o124"); + Expect(o1, EqualTo('\x54')); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void BackslashOctalWithEOFFails() + { + object o1 = ReadFromString("\\u12 4"); + } + + [Test] + [ExpectedException(typeof(Exception))] + public void BackslashOctalInBadRangeFails() + { + object o1 = ReadFromString("\\o444"); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void BackslashOctalWithBadDigitFails() + { + object o1 = ReadFromString("\\o128"); + } + + [Test] + [ExpectedException(typeof(Exception))] + public void BackslashOctalWithTooManyDigitsFails() + { + object o1 = ReadFromString("\\o0012 aa"); + } + + [Test] + [ExpectedException(typeof(Exception))] + public void BackslashWithOtherFails() + { + object o1 = ReadFromString("\\aa"); + } + + #endregion + + #region comment tests + + [Test] + public void SemicolonIgnoresToEndOfLine() + { + object o1 = ReadFromString(" ; ignore \n 123"); + Expect(o1, EqualTo(123)); + } + + [Test] + public void SharpBangIgnoresToEndOfLine() + { + object o1 = ReadFromString(" #! ignore \n 123"); + Expect(o1, EqualTo(123)); + } + + #endregion + + #region Discard tests + + [Test] + public void SharpUnderscoreIgnoresNextForm() + { + object o1 = ReadFromString("#_ (1 2 3) 4"); + Expect(o1, EqualTo(4)); + } + + [Test] + public void SharpUnderscoreIgnoresNextFormInList() + { + object o1 = ReadFromString("( abc #_ (1 2 3) 12)"); + Expect(o1, TypeOf(typeof(PersistentList))); + PersistentList pl = o1 as PersistentList; + Expect(pl.count(), EqualTo(2)); + Expect(pl.first(), TypeOf(typeof(Symbol))); + Expect(((Symbol)pl.first()).Name, EqualTo("abc")); + Expect(((Symbol)pl.first()).Namespace, Null); + Expect(pl.rest().first(), TypeOf(typeof(int))); + Expect(pl.rest().first(), EqualTo(12)); + Expect(pl.rest().rest(), Null); + } + + #endregion + + #region List tests + + [Test] + public void CanReadBasicList() + { + Object o1 = ReadFromString("(abc 12)"); + Expect(o1, TypeOf(typeof(PersistentList))); + PersistentList pl = o1 as PersistentList; + Expect(pl.count(), EqualTo(2)); + Expect(pl.first(), TypeOf(typeof(Symbol))); + Expect(((Symbol)pl.first()).Name, EqualTo("abc")); + Expect(((Symbol)pl.first()).Namespace, Null); + Expect(pl.rest().first(), TypeOf(typeof(int))); + Expect(pl.rest().first(), EqualTo(12)); + Expect(pl.rest().rest(), Null); + } + + [Test] + public void CanReadEmptyList() + { + Object o1 = ReadFromString("( )"); + Expect(o1, InstanceOfType(typeof(IPersistentList))); + IPersistentList pl = o1 as IPersistentList; + Expect(pl.count(), EqualTo(0)); + } + + [Test] + public void CanReadNestedList() + { + Object o1 = ReadFromString("(a (b c) d)"); + Expect(o1, InstanceOfType(typeof(IPersistentList))); + IPersistentList pl = o1 as IPersistentList; + ISeq seq = pl.seq(); + Expect(pl.count(), EqualTo(3)); + Expect(seq.rest().first(), InstanceOfType(typeof(IPersistentList))); + IPersistentList sub = seq.rest().first() as IPersistentList; + Expect(sub.count(), EqualTo(2)); + } + + [Test] + [ExpectedException(typeof(System.IO.EndOfStreamException))] + public void MissingListTerminatorFails() + { + Object o1 = ReadFromString("(a b 1 2"); + } + + [Test] + public void ListGetsLineNumber() + { + Object o1 = ReadFromStringNumbering("\n\n(a b \n1 2)"); + Expect(o1, InstanceOfType(typeof(IObj))); + IObj io = o1 as IObj; + Expect(io.meta().valAt(Keyword.intern(null,"line")), EqualTo(3)); + } + + #endregion + + #region VectorTests + + [Test] + public void CanReadBasicVector() + { + Object o1 = ReadFromString("[abc 12]"); + Expect(o1, TypeOf(typeof(LazilyPersistentVector))); + LazilyPersistentVector pl = o1 as LazilyPersistentVector; + Expect(pl.count(), EqualTo(2)); + Expect(pl.nth(0), TypeOf(typeof(Symbol))); + Expect(((Symbol)pl.nth(0)).Name, EqualTo("abc")); + Expect(((Symbol)pl.nth(0)).Namespace, Null); + Expect(pl.nth(1), TypeOf(typeof(int))); + Expect(pl.nth(1), EqualTo(12)); + } + + [Test] + public void CanReadEmptyVector() + { + Object o1 = ReadFromString("[ ]"); + Expect(o1, InstanceOfType(typeof(IPersistentVector))); + IPersistentVector v = o1 as IPersistentVector; + Expect(v.count(), EqualTo(0)); + } + + [Test] + public void VectorCanContainNestedList() + { + Object o1 = ReadFromString("[a (b c) d]"); + Expect(o1, InstanceOfType(typeof(IPersistentVector))); + IPersistentVector v = o1 as IPersistentVector; + Expect(v.count(), EqualTo(3)); + Expect(v.nth(1), InstanceOfType(typeof(IPersistentList))); + IPersistentList sub = v.nth(1) as IPersistentList; + Expect(sub.count(), EqualTo(2)); + } + + [Test] + public void VectorCanContainNestedVector() + { + Object o1 = ReadFromString("[a [b c] d]"); + Expect(o1, InstanceOfType(typeof(IPersistentVector))); + IPersistentVector v = o1 as IPersistentVector; + Expect(v.count(), EqualTo(3)); + Expect(v.nth(1), InstanceOfType(typeof(IPersistentVector))); + IPersistentVector sub = v.nth(1) as IPersistentVector; + Expect(sub.count(), EqualTo(2)); + } + + [Test] + [ExpectedException(typeof(System.IO.EndOfStreamException))] + public void MissingVectorTerminatorFails() + { + Object o1 = ReadFromString("[a b 1 2"); + } + + #endregion + + #region Map tests + + [Test] + public void CanReadBasicMap() + { + Object o1 = ReadFromString("{:abc 12 14 a}"); + Expect(o1, InstanceOfType(typeof(IPersistentMap))); + IPersistentMap m = o1 as IPersistentMap; + Expect(m.count(), EqualTo(2)); + Expect(m.valAt(Keyword.intern(null, "abc")), EqualTo(12)); + Expect(m.valAt(14), EqualTo(Symbol.intern("a"))); + } + + [Test] + public void CanReadEmptyMap() + { + Object o1 = ReadFromString("{ }"); + Expect(o1, InstanceOfType(typeof(IPersistentMap))); + IPersistentMap m = o1 as IPersistentMap; + Expect(m.count(), EqualTo(0)); + } + + + [Test] + [ExpectedException(typeof(System.IO.EndOfStreamException))] + public void MissingRightBraceFails() + { + Object o1 = ReadFromString("{a b 1 2"); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void MapWithOddNumberOfEntriesFails() + { + Object o1 = ReadFromString("{a b 1}"); + } + + + + #endregion + + #region Set tests + + [Test] + public void CanReadBasicSet() + { + Object o1 = ReadFromString("#{abc 12}"); + Expect(o1, InstanceOfType(typeof(IPersistentSet))); + IPersistentSet s = o1 as IPersistentSet; + Expect(s.count(), EqualTo(2)); + Expect(s.contains(Symbol.intern("abc"))); + Expect(s.contains(12)); + } + + [Test] + public void CanReadEmptySet() + { + Object o1 = ReadFromString("#{ }"); + Expect(o1, InstanceOfType(typeof(IPersistentSet))); + IPersistentSet s = o1 as IPersistentSet; + Expect(s.count(), EqualTo(0)); + } + + [Test] + [ExpectedException(typeof(System.IO.EndOfStreamException))] + public void MissingSetTerminatorFails() + { + Object o1 = ReadFromString("#{a b 1 2"); + } + + #endregion + + #region Unmatched delimiter tests + + [Test] + [ExpectedException(typeof(Exception))] + public void NakedRightParenIsBad() + { + Object o1 = ReadFromString("}"); + } + + [Test] + [ExpectedException(typeof(Exception))] + public void NakedRightBracketIsBad() + { + Object o1 = ReadFromString("]"); + } + + + [Test] + [ExpectedException(typeof(Exception))] + public void NakedRightBraceIsBad() + { + Object o1 = ReadFromString("}"); + } + + [Test] + [ExpectedException(typeof(Exception))] + public void MismatchedDelimiterIsBad() + { + Object o1 = ReadFromString("( a b c }"); + } + + #endregion + + #region Wrapping forms + + [Test] + public void QuoteWraps() + { + object o1 = ReadFromString("'a"); + Expect(o1,InstanceOfType(typeof(IPersistentList))); + IPersistentList p = o1 as IPersistentList; + ISeq s = p.seq(); + Expect(s.count(),EqualTo(2)); + Expect(s.first(),EqualTo(Symbol.intern("quote"))); + Expect(s.rest().first(),TypeOf(typeof(Symbol))); + } + + [Test] + public void QuoteWraps2() + { + object o1 = ReadFromString("'(a b c)"); + Expect(o1, InstanceOfType(typeof(IPersistentList))); + IPersistentList p = o1 as IPersistentList; + ISeq s = p.seq(); + Expect(s.count(), EqualTo(2)); + Expect(s.first(), EqualTo(Symbol.intern("quote"))); + Expect(s.rest().first(), InstanceOfType(typeof(IPersistentList))); + Expect(((IPersistentList)s.rest().first()).count(), EqualTo(3)); + } + + + [Test] + public void MetaWraps() + { + object o1 = ReadFromString("^a"); + Expect(o1, InstanceOfType(typeof(IPersistentList))); + IPersistentList p = o1 as IPersistentList; + ISeq s = p.seq(); + Expect(s.count(), EqualTo(2)); + Expect(s.first(), EqualTo(Symbol.intern("clojure.core","meta"))); + Expect(s.rest().first(), TypeOf(typeof(Symbol))); + } + + [Test] + public void MetaWraps2() + { + object o1 = ReadFromString("^(a b c)"); + Expect(o1, InstanceOfType(typeof(IPersistentList))); + IPersistentList p = o1 as IPersistentList; + ISeq s = p.seq(); + Expect(s.count(), EqualTo(2)); + Expect(s.first(), EqualTo(Symbol.intern("clojure.core", "meta"))); + Expect(s.rest().first(), InstanceOfType(typeof(IPersistentList))); + Expect(((IPersistentList)s.rest().first()).count(), EqualTo(3)); + } + + [Test] + public void DerefWraps() + { + object o1 = ReadFromString("@a"); + Expect(o1, InstanceOfType(typeof(IPersistentList))); + IPersistentList p = o1 as IPersistentList; + ISeq s = p.seq(); + Expect(s.count(), EqualTo(2)); + Expect(s.first(), EqualTo(Symbol.intern("clojure.core", "deref"))); + Expect(s.rest().first(), TypeOf(typeof(Symbol))); + } + + [Test] + public void DerefWraps2() + { + object o1 = ReadFromString("@(a b c)"); + Expect(o1, InstanceOfType(typeof(IPersistentList))); + IPersistentList p = o1 as IPersistentList; + ISeq s = p.seq(); + Expect(s.count(), EqualTo(2)); + Expect(s.first(), EqualTo(Symbol.intern("clojure.core", "deref"))); + Expect(s.rest().first(), InstanceOfType(typeof(IPersistentList))); + Expect(((IPersistentList)s.rest().first()).count(), EqualTo(3)); + } + + #endregion + + #region Syntax-quote tests + + [Test] + public void SQOnSelfEvaluatingReturnsQuotedThing() + { + object o1 = ReadFromString("`:abc"); + object o2 = ReadFromString("`222"); + object o3 = ReadFromString("`\\a)"); + object o4 = ReadFromString("`\"abc\""); + + Expect(o1, TypeOf(typeof(Keyword))); + Expect(o1, EqualTo(Keyword.intern(null, "abc"))); + Expect(o2, TypeOf(typeof(int))); + Expect(o2, EqualTo(222)); + Expect(o3, TypeOf(typeof(char))); + Expect(o3, EqualTo('a')); + Expect(o4, TypeOf(typeof(string))); + Expect(o4, EqualTo("abc")); + } + + [Test] + public void SQOnSpecialFormQuotes() + { + object o1 = ReadFromString("`def"); + Expect(o1,InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + Expect(s.count(),EqualTo(2) ); + Expect(s.first(),EqualTo(Symbol.intern("quote"))); + Expect(s.rest().first(),InstanceOfType(typeof(Symbol))); + Symbol sym = s.rest().first() as Symbol; + Expect(sym.Namespace, Null); + Expect(sym.Name, EqualTo("def")); + } + + [Test] + public void SQOnRegularSymbolResolves() + { + object o1 = ReadFromString("`abc"); + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + Expect(s.count(), EqualTo(2)); + Expect(s.first(), EqualTo(Symbol.intern("quote"))); + Expect(s.rest().first(), InstanceOfType(typeof(Symbol))); + Symbol sym = s.rest().first() as Symbol; + Expect(sym.Namespace, EqualTo(((Namespace)RT.CURRENT_NS.deref()).Name.Name)); + Expect(sym.Name, EqualTo("abc")); + } + + [Test] + public void SQOnGensymGenerates() + { + object o1 = ReadFromString("`abc#"); + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + Expect(s.count(), EqualTo(2)); + Expect(s.first(), EqualTo(Symbol.intern("quote"))); + Expect(s.rest().first(), InstanceOfType(typeof(Symbol))); + Symbol sym = s.rest().first() as Symbol; + Expect(sym.Namespace, Null); + Expect(sym.Name.StartsWith("abc_")); ; + } + + + [Test] + public void SQOnGensymSeesSameTwice() + { + object o1 = ReadFromString("`(abc# abc#)"); + // Return should be + // (clojure/concat (clojure/list (quote abc__N)) + // (clojure/list (quote abc__N)))) + string str = o1.ToString(); + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + Expect(s.count(), EqualTo(3)); + Expect(s.first(), EqualTo(Symbol.intern("clojure.core","concat"))); + + Expect(s.rest().first(), InstanceOfType(typeof(ISeq))); + ISeq s2 = s.rest().first() as ISeq; + Expect(s2.count(), EqualTo(2)); + + Expect(s2.rest().first(), InstanceOfType(typeof(ISeq))); + ISeq s2a = s2.rest().first() as ISeq; + Expect(s2a.rest().first(), InstanceOfType(typeof(Symbol))); + Symbol sym1 = s2a.rest().first() as Symbol; + + Expect(s.rest().rest().first(), InstanceOfType(typeof(ISeq))); + ISeq s3 = s.rest().rest().first() as ISeq; + Expect(s3.count(), EqualTo(2)); + + Expect(s3.rest().first(), InstanceOfType(typeof(ISeq))); + ISeq s3a = s3.rest().first() as ISeq; + Expect(s3a.rest().first(), InstanceOfType(typeof(Symbol))); + Symbol sym2 = s3a.rest().first() as Symbol; + + Expect(sym1.Namespace, Null); + Expect(sym1.Name.StartsWith("abc__")); + Expect(sym1, EqualTo(sym2)); + } + + [Test] + public void SQOnMapMakesMap() + { + Object o1 = ReadFromString("`{:a 1 :b 2}"); + // (clojure/apply + // clojure/hash-map + // (clojure/concat (clojure/list :a) + // (clojure/list 1) + // (clojure/list :b) + // (clojure/list 2))) + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + Expect(s.count(), EqualTo(3)); + Expect(s.first(), EqualTo(Symbol.intern("clojure.core/apply"))); + Expect(s.rest().first(), EqualTo(Symbol.intern("clojure.core/hash-map"))); + Expect(s.rest().rest().first(), InstanceOfType(typeof(ISeq))); + + ISeq s1 = s.rest().rest().first() as ISeq; + ISeq s2; + + Expect(s1.count(), EqualTo(5)); + Expect(s1.first(), EqualTo(Symbol.intern("clojure.core/concat"))); + + s1 = s1.rest(); + Expect(s1.first(),InstanceOfType(typeof(ISeq))); + + s2 = s1.first() as ISeq; + Expect(s2.first(),EqualTo(Symbol.intern("clojure.core/list"))); + Expect(s2.rest().first(),EqualTo(Keyword.intern(null,"a"))); + + + s1 = s1.rest(); + Expect(s1.first(), InstanceOfType(typeof(ISeq))); + + s2 = s1.first() as ISeq; + Expect(s2.first(), EqualTo(Symbol.intern("clojure.core/list"))); + Expect(s2.rest().first(), EqualTo(1)); + + s1 = s1.rest(); + Expect(s1.first(), InstanceOfType(typeof(ISeq))); + + s2 = s1.first() as ISeq; + Expect(s2.first(), EqualTo(Symbol.intern("clojure.core/list"))); + Expect(s2.rest().first(), EqualTo(Keyword.intern(null, "b"))); + + + s1 = s1.rest(); + Expect(s1.first(), InstanceOfType(typeof(ISeq))); + + s2 = s1.first() as ISeq; + Expect(s2.first(), EqualTo(Symbol.intern("clojure.core/list"))); + Expect(s2.rest().first(), EqualTo(2)); + } + + public void SQOnVectorMakesVector() + { + Object o1 = ReadFromString("`[:b 2]"); + // (clojure/apply + // clojure/vector + // (clojure/concat (clojure/list :b) + // (clojure/list 2))) + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + Expect(s.count(), EqualTo(3)); + Expect(s.first(), EqualTo(Symbol.intern("clojure.core/apply"))); + Expect(s.rest().first(), EqualTo(Symbol.intern("clojure.core/vector"))); + Expect(s.rest().rest().first(), InstanceOfType(typeof(ISeq))); + + ISeq s1 = s.rest().rest().first() as ISeq; + ISeq s2; + + Expect(s1.count(), EqualTo(3)); + Expect(s1.first(), EqualTo(Symbol.intern("clojure.core/concat"))); + + s1 = s1.rest(); + Expect(s1.first(), InstanceOfType(typeof(ISeq))); + + s2 = s1.first() as ISeq; + Expect(s2.first(), EqualTo(Symbol.intern("clojure.core/list"))); + Expect(s2.rest().first(), EqualTo(Keyword.intern(null, "b"))); + + + s1 = s1.rest(); + Expect(s1.first(), InstanceOfType(typeof(ISeq))); + + s2 = s1.first() as ISeq; + Expect(s2.first(), EqualTo(Symbol.intern("clojure.core/list"))); + Expect(s2.rest().first(), EqualTo(2)); + } + + [Test] + public void SQOnSetMakesSet() + { + Object o1 = ReadFromString("`#{:b 2}"); + // (clojure/apply + // clojure/hash-set + // (clojure/concat (clojure/list :b) + // (clojure/list 2))) + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + Expect(s.count(), EqualTo(3)); + Expect(s.first(), EqualTo(Symbol.intern("clojure.core/apply"))); + Expect(s.rest().first(), EqualTo(Symbol.intern("clojure.core/hash-set"))); + Expect(s.rest().rest().first(), InstanceOfType(typeof(ISeq))); + + ISeq s1 = s.rest().rest().first() as ISeq; + + Expect(s1.count(), EqualTo(3)); + Expect(s1.first(), EqualTo(Symbol.intern("clojure.core/concat"))); + + s1 = s1.rest(); + Expect(s1.first(), InstanceOfType(typeof(ISeq))); + ISeq s2 = s1.first() as ISeq; + s1 = s1.rest(); + Expect(s1.first(), InstanceOfType(typeof(ISeq))); + ISeq s3 = s1.first() as ISeq; + + Expect(s2.first(), EqualTo(Symbol.intern("clojure.core/list"))); + Expect(s2.first(), EqualTo(Symbol.intern("clojure.core/list"))); + + + object e1 = s2.rest().first(); + object e2 = s3.rest().first(); + + // Set elements can occur in any order + + Expect(e1, EqualTo(Keyword.intern(null, "b")) | EqualTo(2)); + Expect(e2, EqualTo(Keyword.intern(null, "b")) | EqualTo(2)); + Expect(e1, Not.EqualTo(e2)); + } + + [Test] + public void SQOnListMakesList() + { + Object o1 = ReadFromString("`(:b 2)"); + // (clojure/concat (clojure/list :b) + // (clojure/list 2))) + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s1 = o1 as ISeq; + ISeq s2; + + Expect(s1.count(), EqualTo(3)); + Expect(s1.first(), EqualTo(Symbol.intern("clojure.core/concat"))); + + s1 = s1.rest(); + Expect(s1.first(), InstanceOfType(typeof(ISeq))); + + s2 = s1.first() as ISeq; + Expect(s2.first(), EqualTo(Symbol.intern("clojure.core/list"))); + Expect(s2.rest().first(), EqualTo(Keyword.intern(null, "b"))); + + + s1 = s1.rest(); + Expect(s1.first(), InstanceOfType(typeof(ISeq))); + + s2 = s1.first() as ISeq; + Expect(s2.first(), EqualTo(Symbol.intern("clojure.core/list"))); + Expect(s2.rest().first(), EqualTo(2)); + } + + + [Test] + public void UnquoteStandaloneReturnsUnquoteObject() + { + object o1 = ReadFromString("~x"); + + //Expect(o1, InstanceOfType(typeof(LispReader.Unquote))); + //LispReader.Unquote u = o1 as LispReader.Unquote; + //Expect(u.Obj, EqualTo(Symbol.intern("x"))); + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + Expect(s.first(), EqualTo(Symbol.intern("clojure.core/unquote"))); + Expect(s.rest().first(), EqualTo(Symbol.intern("x"))); + Expect(s.count(), EqualTo(2)); + + } + + [Test] + public void UnquoteSpliceStandaloneReturnsUnquoteSpliceObject() + { + object o1 = ReadFromString("~@x"); + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + Expect(s.first(), EqualTo(Symbol.intern("clojure.core/unquote-splicing"))); + Expect(s.rest().first(), EqualTo(Symbol.intern("x"))); + Expect(s.count(), EqualTo(2)); + } + + [Test] + public void SQonUnquoteDequotes() + { + object o1 = ReadFromString("`(a ~b)"); + // (clojure/concat (clojure/list (quote NS/a)) + // (clojure/list b)) + + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s1 = o1 as ISeq; + ISeq s2; + + Expect(s1.count(), EqualTo(3)); + Expect(s1.first(), EqualTo(Symbol.intern("clojure.core/concat"))); + + s1 = s1.rest(); + Expect(s1.first(), InstanceOfType(typeof(ISeq))); + + s2 = s1.first() as ISeq; + Expect(s2.first(), EqualTo(Symbol.intern("clojure.core/list"))); + Expect(s2.rest().first(), InstanceOfType(typeof(ISeq))); + ISeq s3 = s2.rest().first() as ISeq; + + Expect(s3.count(), EqualTo(2)); + Expect(s3.first(), EqualTo(Symbol.intern("quote"))); + Expect(s3.rest().first(), EqualTo(Symbol.intern(((Namespace)RT.CURRENT_NS.deref()).Name.Name,"a"))); + + + s1 = s1.rest(); + Expect(s1.first(), InstanceOfType(typeof(ISeq))); + + s2 = s1.first() as ISeq; + Expect(s2.first(), EqualTo(Symbol.intern("clojure.core/list"))); + Expect(s2.rest().first(), EqualTo(Symbol.intern("b"))); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void SQonUnquoteSpliceNotInListFails() + { + object o1 = ReadFromString("`~@x"); + } + + [Test] + public void SqOnUnquoteSpliceSplices() + { + object o1 = ReadFromString("`(a ~@b)"); + // (clojure/concat (clojure/list (quote user/a)) b) + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s1 = o1 as ISeq; + ISeq s2; + + Expect(s1.count(), EqualTo(3)); + Expect(s1.first(), EqualTo(Symbol.intern("clojure.core/concat"))); + + s1 = s1.rest(); + Expect(s1.first(), InstanceOfType(typeof(ISeq))); + + s2 = s1.first() as ISeq; + Expect(s2.first(), EqualTo(Symbol.intern("clojure.core/list"))); + Expect(s2.rest().first(), InstanceOfType(typeof(ISeq))); + ISeq s3 = s2.rest().first() as ISeq; + + Expect(s3.count(), EqualTo(2)); + Expect(s3.first(), EqualTo(Symbol.intern("quote"))); + Expect(s3.rest().first(), EqualTo(Symbol.intern(((Namespace)RT.CURRENT_NS.deref()).Name.Name, "a"))); + + + s1 = s1.rest(); + Expect(s1.first(), EqualTo(Symbol.intern("b"))); + } + + // We should test to see that 'line' meta info is not preserved. + + [Test] + public void SQOnLparenRParenReturnsEmptyList() + { + object o1 = ReadFromString("`()"); + Expect(o1,EqualTo(PersistentList.EMPTY)); + } + + #endregion + + #region #-dispatch tests + + [Test] + [ExpectedException(typeof(Exception))] + public void SharpDispatchOnInvalidCharFails() + { + object o1 = ReadFromString("#a(1 2)"); + } + + #endregion + + #region Meta reader tests + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void MetaOnImproperMetadataFails() + { + object o1 = ReadFromString("#^1 (a b c"); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void MetaOnAppliedToNonIObjFails() + { + object o1 = ReadFromString("#^{:a 1} 7"); + } + + [Test] + public void MetaAppliesHashMetaDataToObject() + { + object o1 = ReadFromString("#^{a 1} (a b)"); + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + + Expect(s.count(), EqualTo(2)); + Expect(s.first(), EqualTo(Symbol.intern("a"))); + Expect(s.rest().first(), EqualTo(Symbol.intern("b"))); + + Expect(o1, InstanceOfType(typeof(IObj))); + IObj o = o1 as IObj; + Expect(o.meta(), InstanceOfType(typeof(IPersistentMap))); + IPersistentMap m = o.meta() as IPersistentMap; + + Expect(m.count(), EqualTo(1)); + Expect(m.valAt(Symbol.intern("a")), EqualTo(1)); + } + + + [Test] + public void MetaAppliesSymbolAsTagMetaDataToObject() + { + object o1 = ReadFromString("#^c (a b)"); + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + + Expect(s.count(), EqualTo(2)); + Expect(s.first(), EqualTo(Symbol.intern("a"))); + Expect(s.rest().first(), EqualTo(Symbol.intern("b"))); + + Expect(o1, InstanceOfType(typeof(IObj))); + IObj o = o1 as IObj; + Expect(o.meta(), InstanceOfType(typeof(IPersistentMap))); + IPersistentMap m = o.meta() as IPersistentMap; + + Expect(m.count(), EqualTo(1)); + Expect(m.valAt(Keyword.intern(null,"tag")), EqualTo(Symbol.intern("c"))); + } + + [Test] + public void MetaAppliesKeywordAsTagMetaDataToObject() + { + object o1 = ReadFromString("#^:c (a b)"); + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + + Expect(s.count(), EqualTo(2)); + Expect(s.first(), EqualTo(Symbol.intern("a"))); + Expect(s.rest().first(), EqualTo(Symbol.intern("b"))); + + Expect(o1, InstanceOfType(typeof(IObj))); + IObj o = o1 as IObj; + Expect(o.meta(), InstanceOfType(typeof(IPersistentMap))); + IPersistentMap m = o.meta() as IPersistentMap; + + Expect(m.count(), EqualTo(1)); + Expect(m.valAt(Keyword.intern(null, "tag")), EqualTo(Keyword.intern(null,"c"))); + } + + [Test] + public void MetaAppliesStringAsTagMetaDataToObject() + { + object o1 = ReadFromString("#^\"help\" (a b)"); + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + + Expect(s.count(), EqualTo(2)); + Expect(s.first(), EqualTo(Symbol.intern("a"))); + Expect(s.rest().first(), EqualTo(Symbol.intern("b"))); + + Expect(o1, InstanceOfType(typeof(IObj))); + IObj o = o1 as IObj; + Expect(o.meta(), InstanceOfType(typeof(IPersistentMap))); + IPersistentMap m = o.meta() as IPersistentMap; + + Expect(m.count(), EqualTo(1)); + Expect(m.valAt(Keyword.intern(null, "tag")), EqualTo("help")); + } + + [Test] + public void MetaAddsLineupNumberAsMetaDataIfAvailable() + { + object o1 = ReadFromStringNumbering("\n\n#^:c (a b)"); + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + + Expect(s.count(), EqualTo(2)); + Expect(s.first(), EqualTo(Symbol.intern("a"))); + Expect(s.rest().first(), EqualTo(Symbol.intern("b"))); + + Expect(o1, InstanceOfType(typeof(IObj))); + IObj o = o1 as IObj; + Expect(o.meta(), InstanceOfType(typeof(IPersistentMap))); + IPersistentMap m = o.meta() as IPersistentMap; + + Expect(m.count(), EqualTo(2)); + Expect(m.valAt(Keyword.intern(null, "tag")), EqualTo(Keyword.intern(null,"c"))); + Expect(m.valAt(Keyword.intern(null, "line")), EqualTo(3)); + } + + #endregion + + #region Var reader tests + + [Test] + public void VarWrapsVar() + { + Object o1 = ReadFromString("#'abc"); + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + Expect(s.count(), EqualTo(2)); + Expect(s.first(), EqualTo(Symbol.intern("var"))); + Expect(s.rest().first(), EqualTo(Symbol.intern("abc"))); + } + + #endregion + + #region Regex reader tests + + [Test] + public void SharpDoubleQuoteGeneratesRegex() + { + object o1 = ReadFromString("#\"abc\""); + + Expect(o1, InstanceOfType(typeof(System.Text.RegularExpressions.Regex))); + System.Text.RegularExpressions.Regex r = o1 as System.Text.RegularExpressions.Regex; + Expect(r.ToString(), EqualTo("abc")); + } + + [Test] + [ExpectedException(typeof(System.IO.EndOfStreamException))] + public void SharpDQHitsEOFFails() + { + object o1 = ReadFromString("#\"abc"); + } + + [Test] + public void SharpDQEscapesOnBackslash() + { + char[] chars = new char[] { + '#', '"', 'a', '\\', '"', 'b', 'c', '"' + }; + + // input = #"a\"bc" -- should go over the " + + string str = new String(chars); + + object o1 = ReadFromString(str); + + Expect(o1, InstanceOfType(typeof(System.Text.RegularExpressions.Regex))); + System.Text.RegularExpressions.Regex r = o1 as System.Text.RegularExpressions.Regex; + Expect(r.ToString(), EqualTo("a\\\"bc")); + } + + #endregion + + #region Fn reader & Arg reader tests + + [Test] + public void SharpFnWithNoArgsGeneratesNoArgFn() + { + object o1 = ReadFromString("#(+ 1 2)"); + // (fn* [] (+ 1 2)) + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + + Expect(s.first(), EqualTo(Symbol.intern("fn*"))); + s = s.rest(); + Expect(s.first(), InstanceOfType(typeof(IPersistentVector))); + IPersistentVector arglist = s.first() as IPersistentVector; + + Expect(arglist.count(), EqualTo(0)); + + s = s.rest(); + Expect(s.first(), InstanceOfType(typeof(ISeq))); + Expect(s.rest(), Null); + + ISeq form = s.first() as ISeq; + + Expect(form.count(), EqualTo(3)); + Expect(form.first(), EqualTo(Symbol.intern("+"))); + Expect(form.rest().first(), EqualTo(1)); + Expect(form.rest().rest().first(), EqualTo(2)); + } + + [Test] + public void SharpFnWithArgsGeneratesFnWithArgs() + { + object o1 = ReadFromString("#(+ %2 2)"); + // (fn* [p1__N p2__M] (+ p2__M 2)) + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + + Expect(s.first(), EqualTo(Symbol.intern("fn*"))); + s = s.rest(); + Expect(s.first(), InstanceOfType(typeof(IPersistentVector))); + IPersistentVector arglist = s.first() as IPersistentVector; + + Expect(arglist.count(), EqualTo(2)); + Expect(arglist.nth(0), InstanceOfType(typeof(Symbol))); + Expect(arglist.nth(1), InstanceOfType(typeof(Symbol))); + Symbol arg1 = arglist.nth(0) as Symbol; + Symbol arg2 = arglist.nth(1) as Symbol; + Expect(arg1.Name, StartsWith("p1__")); + Expect(arg2.Name, StartsWith("p2__")); + + s = s.rest(); + Expect(s.first(), InstanceOfType(typeof(ISeq))); + Expect(s.rest(), Null); + + ISeq form = s.first() as ISeq; + + Expect(form.count(), EqualTo(3)); + Expect(form.first(), EqualTo(Symbol.intern("+"))); + Expect(form.rest().first(), EqualTo(arg2)); + Expect(form.rest().rest().first(), EqualTo(2)); + } + + [Test] + public void SharpFnWithRestArgGeneratesFnWithRestArg() + { + object o1 = ReadFromString("#(+ %2 %&)"); + // (fn* [p1__N p2__M & rest__X] (+ p2__M rest__X)) + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + + Expect(s.first(), EqualTo(Symbol.intern("fn*"))); + s = s.rest(); + Expect(s.first(), InstanceOfType(typeof(IPersistentVector))); + IPersistentVector arglist = s.first() as IPersistentVector; + + Expect(arglist.count(), EqualTo(4)); + Expect(arglist.nth(0), InstanceOfType(typeof(Symbol))); + Expect(arglist.nth(1), InstanceOfType(typeof(Symbol))); + Expect(arglist.nth(2), InstanceOfType(typeof(Symbol))); + Expect(arglist.nth(3), InstanceOfType(typeof(Symbol))); + Symbol arg1 = arglist.nth(0) as Symbol; + Symbol arg2 = arglist.nth(1) as Symbol; + Symbol arg3 = arglist.nth(2) as Symbol; + Symbol arg4 = arglist.nth(3) as Symbol; + Expect(arg1.Name, StartsWith("p1__")); + Expect(arg2.Name, StartsWith("p2__")); + Expect(arg3.Name, EqualTo("&")); + Expect(arg4.Name, StartsWith("rest__")); + + s = s.rest(); + Expect(s.first(), InstanceOfType(typeof(ISeq))); + Expect(s.rest(), Null); + + ISeq form = s.first() as ISeq; + + Expect(form.count(), EqualTo(3)); + Expect(form.first(), EqualTo(Symbol.intern("+"))); + Expect(form.rest().first(), EqualTo(arg2)); + Expect(form.rest().rest().first(), EqualTo(arg4)); + } + + [Test] + public void SharpFnWithAnonArgGeneratesFnWithArgs() + { + object o1 = ReadFromString("#(+ % 2)"); + // (fn* [p1__N] (+ p1__N 2)) + + Expect(o1, InstanceOfType(typeof(ISeq))); + ISeq s = o1 as ISeq; + + Expect(s.first(), EqualTo(Symbol.intern("fn*"))); + s = s.rest(); + Expect(s.first(), InstanceOfType(typeof(IPersistentVector))); + IPersistentVector arglist = s.first() as IPersistentVector; + + Expect(arglist.count(), EqualTo(1)); + Expect(arglist.nth(0), InstanceOfType(typeof(Symbol))); + Symbol arg1 = arglist.nth(0) as Symbol; + Expect(arg1.Name, StartsWith("p1__")); + + s = s.rest(); + Expect(s.first(), InstanceOfType(typeof(ISeq))); + Expect(s.rest(), Null); + + ISeq form = s.first() as ISeq; + + Expect(form.count(), EqualTo(3)); + Expect(form.first(), EqualTo(Symbol.intern("+"))); + Expect(form.rest().first(), EqualTo(arg1)); + Expect(form.rest().rest().first(), EqualTo(2)); + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void ArgReaderOutsideSharpFnFails() + { + object o1 = ReadFromString("(+ %2 2)"); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void ArgReaderFollowedByNonNumericFails() + { + object o1 = ReadFromString("#(+ %a 2)"); + } + + + + #endregion + + } +} -- cgit v1.2.3-18-g5258