diff options
author | David Miller <dmiller2718@gmail.com> | 2009-02-21 06:55:20 +0000 |
---|---|---|
committer | David Miller <dmiller2718@gmail.com> | 2009-02-21 06:55:20 +0000 |
commit | 67f56f0795ac0214f9828c42f8face229e204c1d (patch) | |
tree | f01b57d581c3a48d87fb4d72b023bc8d609a24cd /ClojureCLR/Clojure | |
parent | 64d79207eed0fe057a19fe4d3d0f6b714a63c69a (diff) |
ClojureCLR: added ClojureCLR project to repo.
Diffstat (limited to 'ClojureCLR/Clojure')
149 files changed, 40902 insertions, 0 deletions
diff --git a/ClojureCLR/Clojure/Clojure.Console/Clojure.Console.csproj b/ClojureCLR/Clojure/Clojure.Console/Clojure.Console.csproj new file mode 100644 index 00000000..eeaf5a07 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Console/Clojure.Console.csproj @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{4AFFC540-543E-4F56-9F49-14210D6C143A}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Clojure.Console</RootNamespace>
+ <AssemblyName>Clojure.Console</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <TargetFrameworkSubset>
+ </TargetFrameworkSubset>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="ClojureConsole.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\DLR_Main\Runtime\src\Microsoft.Scripting.Core\Microsoft.Scripting.Core.csproj">
+ <Project>{2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}</Project>
+ <Name>Microsoft.Scripting.Core</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\DLR_Main\Runtime\src\Microsoft.Scripting.Core\Microsoft.Scripting.ExtensionAttribute.csproj">
+ <Project>{8B0F1074-750E-4D64-BF23-A1E0F54261E5}</Project>
+ <Name>Microsoft.Scripting.ExtensionAttribute</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\DLR_Main\Runtime\src\Microsoft.Scripting\Microsoft.Scripting.csproj">
+ <Project>{EB66B766-6354-4208-A3D4-AACBDCB5C3B3}</Project>
+ <Name>Microsoft.Scripting</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Clojure\Clojure.csproj">
+ <Project>{B8089F66-DFBD-4906-BEE0-B317689C2524}</Project>
+ <Name>Clojure</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="app.config" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
\ No newline at end of file diff --git a/ClojureCLR/Clojure/Clojure.Console/ClojureConsole.cs b/ClojureCLR/Clojure/Clojure.Console/ClojureConsole.cs new file mode 100644 index 00000000..f0f88869 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Console/ClojureConsole.cs @@ -0,0 +1,217 @@ +/**
+ * 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 Microsoft.Scripting.Hosting.Shell;
+using clojure.lang;
+using System.IO;
+using Microsoft.Linq.Expressions;
+using Microsoft.Scripting.Generation;
+using Microsoft.Scripting;
+using Microsoft.Scripting.Runtime;
+using Microsoft.Scripting.Hosting;
+using Microsoft.Scripting.Hosting.Providers;
+using System.Diagnostics;
+using System.Resources;
+using System.Threading;
+
+
+using clojure.runtime;
+using clojure.compiler;
+
+
+namespace clojure.console
+{
+ class ClojureConsole : ConsoleHost, Compiler.EEHooks
+ {
+ #region Data
+
+ private bool _isInitialized = false;
+
+ #endregion
+
+ #region Convenience accessors
+
+ private ClojureContext GetLanguageContext()
+ {
+ return (ClojureContext)HostingHelpers.GetLanguageContext(Engine);
+ }
+
+
+ private static SourceUnit GetSourceUnit(ScriptSource scriptSource)
+ {
+ return HostingHelpers.GetSourceUnit(scriptSource);
+ }
+
+ #endregion
+
+ #region Basic overrides
+
+ protected override Type Provider
+ {
+ get
+ {
+ return typeof(ClojureContext);
+ }
+ }
+
+ protected override CommandLine CreateCommandLine()
+ {
+ return new ClojureCommandLine();
+ }
+
+ #endregion
+
+ #region Main routine
+
+ [STAThread]
+ static int Main(string[] args)
+ {
+ ClojureConsole cc = new ClojureConsole();
+
+ int ret = cc.Run(args);
+
+ Console.ReadLine();
+ return ret;
+ }
+
+ #endregion
+
+ #region Execution override
+
+ protected override void ExecuteInternal()
+ {
+ Debug.Assert(Engine != null);
+
+ Var.pushThreadBindings(
+ RT.map(RT.CURRENT_NS, RT.CURRENT_NS.deref()));
+ try
+ {
+ Snippets.SetSaveAssemblies(true, ".");
+ MaybeInitialize();
+ Snippets.SaveAndVerifyAssemblies();
+ base.ExecuteInternal();
+ }
+ catch (Exception e)
+ {
+ UnhandledException(Engine, e);
+ }
+
+ finally
+ {
+ Snippets.SaveAndVerifyAssemblies();
+ Var.popThreadBindings();
+ }
+ }
+
+
+ #endregion
+
+ #region Initialization
+
+ private void MaybeInitialize()
+ {
+ if (_isInitialized)
+ return;
+
+ _isInitialized = true;
+
+ Compiler.SetHooks(this);
+
+ Stopwatch sw = new Stopwatch();
+ sw.Start();
+
+ LoadFromStream(new StringReader(clojure.properties.Resources.core));
+ LoadFromStream(new StringReader(clojure.properties.Resources.core_print));
+ LoadFromStream(new StringReader(clojure.properties.Resources.test));
+
+ sw.Stop();
+ Console.WriteLine("Loading took {0} milliseconds.", sw.ElapsedMilliseconds);
+
+
+ }
+
+ #endregion
+
+ #region EEHooks Members
+
+ public object Eval(object form)
+ {
+ ScriptSource scriptSource = Engine.CreateScriptSourceFromString("<internal>");
+
+ Expression expr = Generator.Eval(GetLanguageContext(), form);
+ LambdaExpression ast = Expression.Lambda(expr);
+ ast = new GlobalLookupRewriter().RewriteLambda(ast);
+ ScriptCode code = new ScriptCode(ast, GetSourceUnit(scriptSource));
+ return code.Run();
+ }
+
+ public object Macroexpand1(object form)
+ {
+ return Generator.Macroexpand1(GetLanguageContext(), form);
+ }
+
+ public object LoadFromStream(TextReader rdr)
+ {
+ ScriptSource scriptSource = Engine.CreateScriptSourceFromString("<already opened TextReader>");
+ //PushbackReader pbr = new PushbackReader(rdr);
+
+ return LoadFromPushbackReader(scriptSource, rdr, false);
+ }
+
+ public object LoadFile(string filename)
+ {
+ ScriptSource scriptSource = Engine.CreateScriptSourceFromFile(filename);
+
+ return LoadFromPushbackReader(scriptSource, scriptSource.GetReader(), false);
+ }
+
+ private static object LoadFromPushbackReader(ScriptSource scriptSource, TextReader pbr, bool addPrint)
+ {
+ object ret = null;
+ object eofVal = new object();
+ object form;
+ while ((form = LispReader.read(pbr, false, eofVal, false)) != eofVal)
+ {
+ LambdaExpression ast = Generator.Generate(form, addPrint);
+ ast = new GlobalLookupRewriter().RewriteLambda(ast);
+
+ ScriptCode code = new ScriptCode(ast, GetSourceUnit(scriptSource));
+ ret = code.Run();
+ }
+
+ return ret;
+ }
+
+ public Delegate GenerateTypedDelegate(Type delegateType, Symbol optName, IPersistentVector argList, ISeq body)
+ {
+ ScriptSource scriptSource = Engine.CreateScriptSourceFromString("<internal>");
+
+ LambdaExpression ast = Generator.GenerateTypedDelegateExpression(GetLanguageContext(), delegateType, optName, argList, body);
+ return ast.Compile();
+
+ //ast = new GlobalLookupRewriter().RewriteLambda(ast); -- doesn't work unless no args
+ //ScriptCode code = new ScriptCode(ast, GetSourceUnit(scriptSource));
+ //return code;
+ }
+
+ #endregion
+
+ protected override ScriptRuntimeSetup CreateRuntimeSetup()
+ {
+ ScriptRuntimeSetup setup = base.CreateRuntimeSetup();
+ setup.DebugMode = true;
+ return setup;
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Console/Properties/AssemblyInfo.cs b/ClojureCLR/Clojure/Clojure.Console/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..6f21300e --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Console/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Clojure.Console")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Clojure.Console")]
+[assembly: AssemblyCopyright("Copyright © 2009")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("64eccb33-4ad4-49b4-944c-ff486ccb5ea0")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/ClojureCLR/Clojure/Clojure.Console/app.config b/ClojureCLR/Clojure/Clojure.Console/app.config new file mode 100644 index 00000000..b7db2817 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Console/app.config @@ -0,0 +1,3 @@ +<?xml version="1.0"?>
+<configuration>
+<startup><supportedRuntime version="v2.0.50727"/></startup></configuration>
diff --git a/ClojureCLR/Clojure/Clojure.Tests/Clojure.Tests.csproj b/ClojureCLR/Clojure/Clojure.Tests/Clojure.Tests.csproj new file mode 100644 index 00000000..9bf437a0 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/Clojure.Tests.csproj @@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{656E1517-B0CA-47B4-B068-3DF43DC41F8A}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Clojure.Tests</RootNamespace>
+ <AssemblyName>Clojure.Tests</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <TargetFrameworkSubset>
+ </TargetFrameworkSubset>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Castle.Core, Version=1.0.3.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\..\..\dev\RhinoMocks\Castle.Core.dll</HintPath>
+ </Reference>
+ <Reference Include="Castle.DynamicProxy2, Version=2.0.3.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\..\..\dev\RhinoMocks\Castle.DynamicProxy2.dll</HintPath>
+ </Reference>
+ <Reference Include="nunit.framework, Version=2.4.8.0, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL" />
+ <Reference Include="Rhino.Mocks, Version=3.5.0.1337, Culture=neutral, PublicKeyToken=0b3305902db7183f, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\..\..\dev\RhinoMocks\Rhino.Mocks.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ <Reference Include="vjslib" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="LibTests\AFnImplTests.cs" />
+ <Compile Include="LibTests\AgentTests.cs" />
+ <Compile Include="LibTests\APersistentVectorTests.cs" />
+ <Compile Include="LibTests\AReferenceTests.cs" />
+ <Compile Include="LibTests\ARefTests.cs" />
+ <Compile Include="LibTests\ArraySeqTests.cs" />
+ <Compile Include="LibTests\ArrayStreamTests.cs" />
+ <Compile Include="LibTests\AtomTests.cs" />
+ <Compile Include="LibTests\CachedSeqTests.cs" />
+ <Compile Include="LibTests\ConsTests.cs" />
+ <Compile Include="LibTests\DelayTests.cs" />
+ <Compile Include="LibTests\FnSeqTests.cs" />
+ <Compile Include="LibTests\IObjTests.cs" />
+ <Compile Include="LibTests\ISeqTestHelper.cs" />
+ <Compile Include="LibTests\IteratorStreamTests.cs" />
+ <Compile Include="LibTests\KeywordTests.cs" />
+ <Compile Include="LibTests\LazilyPersistentVectorTests.cs" />
+ <Compile Include="LibTests\LazyConsTests.cs" />
+ <Compile Include="LibTests\LispReaderTests.cs" />
+ <Compile Include="LibTests\LockingTransactionTests.cs" />
+ <Compile Include="LibTests\MapEntryTests.cs" />
+ <Compile Include="LibTests\NamespaceTests.cs" />
+ <Compile Include="LibTests\NumbersTests.cs" />
+ <Compile Include="LibTests\ObjTests.cs" />
+ <Compile Include="LibTests\PersistentArrayMapTests.cs" />
+ <Compile Include="LibTests\PersistentHashMapTests.cs" />
+ <Compile Include="LibTests\PersistentHashSetTests.cs" />
+ <Compile Include="LibTests\PersistentListTests.cs" />
+ <Compile Include="LibTests\PersistentQueueTests.cs" />
+ <Compile Include="LibTests\PersistentStructMapTests.cs" />
+ <Compile Include="LibTests\PersistentTreeMapTests.cs" />
+ <Compile Include="LibTests\PersistentTreeSetTests.cs" />
+ <Compile Include="LibTests\PersistentVectorTests.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="LibTests\RangeTests.cs" />
+ <Compile Include="LibTests\RefTests.cs" />
+ <Compile Include="LibTests\RestFnImplTests.cs" />
+ <Compile Include="LibTests\RestFnTests.cs" />
+ <Compile Include="LibTests\StreamSeqTests.cs" />
+ <Compile Include="LibTests\StringSeqTests.cs" />
+ <Compile Include="LibTests\SymbolTests.cs" />
+ <Compile Include="LibTests\TestTest.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Clojure\Clojure.csproj">
+ <Project>{B8089F66-DFBD-4906-BEE0-B317689C2524}</Project>
+ <Name>Clojure</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
\ No newline at end of file diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/AFnImplTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/AFnImplTests.cs new file mode 100644 index 00000000..3c79b69b --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/AFnImplTests.cs @@ -0,0 +1,22 @@ +/**
+ * 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;
+
+namespace Clojure.Tests.LibTests
+{
+ // TODO: Add AFnImpl tests
+ class AFnImplTests
+ {
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/APersistentVectorTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/APersistentVectorTests.cs new file mode 100644 index 00000000..88213a02 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/APersistentVectorTests.cs @@ -0,0 +1,707 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+
+
+namespace Clojure.Tests.LibTests
+{
+
+ // TODO: Add tests for APersistentVector.SubVector
+ // TODO: Add tests for APersistentVector.stream
+
+ [TestFixture]
+ public class APersistentVectorTests : AssertionHelper
+ {
+
+ // Usually, we test the abstract classes via the simplest concrete class that derives from it.
+ // For APersistentVector, all the concrete classes are fairly complicated.
+ // Hence we create a test concrete implementation class.
+ // This class has no guarantees of persistence/immutability, thread-safety,
+ // or much of anything else, certainly not efficiency.
+ // We determined the methods to override by trying to compile the class with no methods.
+ // Thus, we have implemented only the absolute minimum.
+ // We will write tests for these methods, too.
+ // This class just has an underlying List<int> to hold values.
+ class CPV : APersistentVector
+ {
+ object[] _values;
+
+ public CPV(object[] values)
+ : base(null)
+ {
+ _values = values;
+ }
+
+
+ public CPV(IPersistentMap meta, object[] values)
+ : base(meta)
+ {
+ _values = values;
+ }
+
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return meta == _meta
+ ? this
+ : new CPV(meta, _values);
+ }
+
+ //public override object applyTo(ISeq arglist)
+ //{
+ // throw new NotImplementedException();
+ //}
+
+ public override IPersistentStack pop()
+ {
+ if (_values.Length == 0)
+ throw new InvalidOperationException("Can't pop a null stack.");
+
+ object[] newArr = new object[_values.Length - 1];
+ Array.Copy(_values, newArr, _values.Length - 1);
+
+ return new CPV(_meta, newArr);
+ }
+
+ public override IPersistentVector cons(object o)
+ {
+ object[] newArr = new object[_values.Length + 1];
+ _values.CopyTo(newArr, 0);
+ newArr[_values.Length] = o;
+ return new CPV(_meta, newArr);
+ }
+
+ public override IPersistentVector assocN(int i, object val)
+ {
+ if ( 0 <= i && i < _values.Length )
+ {
+ object[] newArr = new object[_values.Length];
+ _values.CopyTo(newArr, 0);
+ newArr[i] = val;
+ return new CPV(_meta, newArr);
+ }
+ if ( i == _values.Length )
+ return cons(val);
+ throw new IndexOutOfRangeException();
+ }
+
+ public override object nth(int i)
+ {
+ return _values[i];
+ }
+
+ public override int length()
+ {
+ return _values.Length;
+ }
+
+ private static CPV EMPTY = new CPV(new object[0]);
+ public override IPersistentCollection empty()
+ {
+ return EMPTY;
+ }
+
+ public override int count()
+ {
+ return _values.Length;
+ }
+ }
+
+ #region C-tor tests
+
+ [Test]
+ public void NoMetaCtorHasNoMeta()
+ {
+ CPV v = new CPV(new object[] { 1, 2, 3 });
+
+ Expect(v.meta(),Null);
+ }
+
+ [Test]
+ public void MetaCtorHasMeta()
+ {
+ MockRepository mocks = new MockRepository();
+ IPersistentMap meta = mocks.StrictMock<IPersistentMap>();
+ mocks.ReplayAll();
+
+ CPV v = new CPV(meta,new object[] { 1, 2, 3 });
+
+ Expect(v.meta(), SameAs(meta));
+ mocks.VerifyAll();
+ }
+
+ #endregion
+
+ #region Object tests
+
+ [Test]
+ public void ToStringMentionsTheCount()
+ {
+ CPV v = new CPV(new object[] { 1, 2, 3 });
+ string str = v.ToString();
+
+ Expect(str.Contains("3"));
+ }
+
+ [Test]
+ public void HashCodeRepeats()
+ {
+ CPV v = new CPV(new object[] { 1, 2, 3 });
+
+ Expect(v.GetHashCode(), EqualTo(v.GetHashCode()));
+ }
+
+ [Test]
+ public void HashCodeDependsOnItems()
+ {
+ CPV v1 = new CPV(new object[] { 1, 2, 3 });
+ CPV v2 = new CPV(new object[] { 1, 2, 4 });
+
+ Expect(v1.GetHashCode(), Not.EqualTo(v2.GetHashCode()));
+ }
+
+ [Test]
+ public void EqualsOnNonPersistentVectorIsFalse()
+ {
+ CPV v1 = new CPV(new object[] { 1, 2, 3 });
+
+ Expect(v1.equiv(7), False);
+ }
+
+ [Test]
+ public void EqualsOnPersistentVectorWithDifferentItemsIsFalse()
+ {
+ CPV v1 = new CPV(new object[] { 1, 2, 3 });
+ CPV v2 = new CPV(new object[] { 1, 2, 4 });
+ CPV v3 = new CPV(new object[] { 1, 2 });
+ CPV v4 = new CPV(new object[] { 1, 2, 3, 4 });
+
+ Expect(v1.equiv(v2), False);
+ Expect(v1.equiv(v3), False);
+ Expect(v1.equiv(v4), False);
+ }
+
+ [Test]
+ public void EqualsOnPersistentVectorWithSameItemsIsTrue()
+ {
+ CPV v1 = new CPV(new object[] { 1, 2, 3 });
+ CPV v2 = new CPV(new object[] { 1, 2, 3 });
+ CPV v3 = new CPV(new object[] { 1 });
+ CPV v4 = new CPV(new object[] { 1 });
+ CPV v5 = new CPV(new object[] { });
+ CPV v6 = new CPV(new object[] { });
+
+ Expect(v1.equiv(v2));
+ Expect(v3.equiv(v4));
+ Expect(v5.equiv(v6));
+ }
+
+ [Test]
+ public void EqualsOnSimilarISeqWorks()
+ {
+ CPV v1 = new CPV(new object[] { 'a', 'b', 'c' });
+ StringSeq s1 = StringSeq.create("abc");
+
+ Expect(v1.equiv(s1));
+ }
+
+ [Test]
+ public void EqualsOnDissimilarISeqFails()
+ {
+ CPV v1 = new CPV(new object[] { 'a', 'b', 'c' });
+ StringSeq s1 = StringSeq.create("ab");
+ StringSeq s2 = StringSeq.create("abd");
+ StringSeq s3 = StringSeq.create("abcd");
+
+ Expect(v1.equiv(s1), False);
+ Expect(v1.equiv(s2), False);
+ Expect(v1.equiv(s3), False);
+ }
+
+
+ #endregion
+
+ #region IFn tests
+
+ [Test]
+ public void InvokeCallsNth()
+ {
+ CPV v = new CPV(new object[] { 5, 6, 7 });
+
+ Expect(v.invoke(0),EqualTo(5));
+ Expect(v.invoke(1),EqualTo(6));
+ Expect(v.invoke(2),EqualTo(7));
+ Expect(v.invoke("1"), EqualTo(6));
+ Expect(v.invoke(1.0), EqualTo(6));
+ Expect(v.invoke(1.2), EqualTo(6));
+ Expect(v.invoke(1.8), EqualTo(6)); // Rounds or not-- should it?
+ Expect(v.invoke(1.4M), EqualTo(6));
+ }
+
+
+ #endregion
+
+ #region IPersistentCollection tests
+
+ [Test]
+ public void SeqOnCount0YieldsNull()
+ {
+ CPV v = new CPV(new object[0]);
+
+ Expect(v.seq(), Null);
+ }
+
+ [Test]
+ public void SeqOnPositiveCountYieldsNotNull()
+ {
+ CPV v = new CPV(new object[]{ 1,2,3});
+
+ Expect(v.seq(), Not.Null);
+ }
+
+ [Test]
+ public void SeqOnPositiveCountYieldsValidSequence()
+ {
+ CPV v = new CPV(new object[] { 1, 2, 3 });
+ ISeq s = v.seq();
+
+ Expect(s.first(), EqualTo(1));
+ Expect(s.rest().first(), EqualTo(2));
+ Expect(s.rest().rest().first(), EqualTo(3));
+ Expect(s.rest().rest().rest(), Null);
+ }
+
+ [Test]
+ public void Explicit_IPersistentCollection_cons_works()
+ {
+ CPV v = new CPV(new object[] { 1, 2 });
+ IPersistentCollection c = v as IPersistentCollection;
+
+ Expect(c, Not.Null);
+
+ IPersistentCollection c2 = c.cons(3);
+ Expect(c2.count(), EqualTo(3));
+
+ ISeq s2 = c2.seq();
+
+ Expect(s2.first(), EqualTo(1));
+ Expect(s2.rest().first(), EqualTo(2));
+ Expect(s2.rest().rest().first(), EqualTo(3));
+ Expect(s2.rest().rest().rest(), Null);
+ }
+
+ #endregion
+
+ #region Reversible tests
+
+ [Test]
+ public void RseqOnCount0YieldsNull()
+ {
+ CPV v = new CPV(new object[0]);
+
+ Expect(v.rseq(), Null);
+ }
+
+ [Test]
+ public void RSeqOnPositiveCountYieldsNotNull()
+ {
+ CPV v = new CPV(new object[] { 1, 2, 3 });
+
+ Expect(v.rseq(), Not.Null);
+ }
+
+ [Test]
+ public void RseqOnPositiveCountYieldsValidSequence()
+ {
+ CPV v = new CPV(new object[] { 1, 2, 3 });
+ ISeq s = v.rseq();
+
+ Expect(s.first(), EqualTo(3));
+ Expect(s.rest().first(), EqualTo(2));
+ Expect(s.rest().rest().first(), EqualTo(1));
+ Expect(s.rest().rest().rest(), Null);
+ }
+
+
+ #endregion
+
+ #region Associative tests
+
+ [Test]
+ public void ContainsKeyOnNonNumericIsFalse()
+ {
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+
+ Expect(v.containsKey("a"), False);
+ }
+
+ [Test]
+ public void ContainsKeyOnIndexInRangeIsTrue()
+ {
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+
+ Expect(v.containsKey(1.2));
+ }
+
+
+ [Test]
+ public void ContainsKeyOnIndexOutOfRangeIsFalse()
+ {
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+
+ Expect(v.containsKey(5),False);
+ }
+
+ [Test]
+ public void EntryAtOnNonNumericReturnsNull()
+ {
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+
+ IMapEntry me = v.entryAt("a");
+
+ Expect(me, Null);
+ }
+
+ [Test]
+ public void EntryAtOnIndexInRangeReturnsEntry()
+ {
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+
+ IMapEntry me = v.entryAt(1);
+
+ Expect(me.key(), EqualTo(1));
+ Expect(me.val(),EqualTo(5));
+ }
+
+
+ [Test]
+ public void EntryAtOnIndexOutOfRangeReturnsNull()
+ {
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+
+ IMapEntry me = v.entryAt(5);
+
+ Expect(me, Null);
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void AssocWithNonNumericKeyThrowsException()
+ {
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+ Associative a = v.assoc("a", 7);
+ }
+
+ [Test]
+ public void AssocWithNumericKeyInRangeChangesValue()
+ {
+ //This just checks that APersistentVector.assoc calls CPV.assocN
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+ Associative a = v.assoc(1, 10);
+
+ Expect(a.valAt(0), EqualTo(4));
+ Expect(a.valAt(1), EqualTo(10));
+ Expect(a.valAt(2), EqualTo(6));
+ Expect(a.count(), EqualTo(3));
+ }
+
+ [Test]
+ public void AssocWithNumericKeyOnePastEndAddValue()
+ {
+ //This just checks that APersistentVector.assoc calls CPV.assocN
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+ Associative a = v.assoc(3, 10);
+
+ Expect(a.valAt(0), EqualTo(4));
+ Expect(a.valAt(1), EqualTo(5));
+ Expect(a.valAt(2), EqualTo(6));
+ Expect(a.valAt(3), EqualTo(10));
+ Expect(a.count(), EqualTo(4));
+ }
+
+ [Test]
+ [ExpectedException(typeof(IndexOutOfRangeException))]
+ public void AssocWithNumericKeyOutOfRangeHighThrowsException()
+ {
+ //This just checks that APersistentVector.assoc calls CPV.assocN
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+ Associative a = v.assoc(4, 10);
+ }
+
+ [Test]
+ [ExpectedException(typeof(IndexOutOfRangeException))]
+ public void AssocWithNumericKeyOutOfRangeLowThrowsException()
+ {
+ //This just checks that APersistentVector.assoc calls CPV.assocN
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+ Associative a = v.assoc(-1, 10);
+ }
+
+ [Test]
+ public void ValAtOnNonNumericReturnsDefault()
+ {
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+
+ object val1 = v.valAt("a");
+ object val2 = v.valAt("a", "abc");
+
+ Expect(val1, Null);
+ Expect(val2, EqualTo("abc"));
+ }
+
+ [Test]
+ public void ValAtOnIndexInRangeReturnsEntry()
+ {
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+
+ object val1 = v.valAt(1);
+ object val2 = v.valAt(1, "abc");
+
+ Expect(val1, EqualTo(5));
+ Expect(val2, EqualTo(5));
+ }
+
+
+ [Test]
+ public void ValAtOnIndexOutOfRangeReturnsDefault()
+ {
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+
+ IMapEntry me = v.entryAt(5);
+
+ object val1 = v.valAt(4);
+ object val2 = v.valAt(4, "abc");
+
+ Expect(val1, Null);
+ Expect(val2, EqualTo("abc"));
+ }
+
+
+
+ #endregion
+
+ #region IPersistentStack tests
+
+ [Test]
+ public void PeekOnCount0ReturnsNull()
+ {
+ CPV v = new CPV(new object[] {});
+
+ Expect(v.peek(), Null);
+ }
+
+ [Test]
+ public void PeekOnPositiveCountReturnsLastItem()
+ {
+ CPV v = new CPV(new object[] { 1, 2, 3 });
+
+ Expect(v.peek(), EqualTo(3));
+ }
+
+ #endregion
+
+ #region APersistentVector.Seq tests
+
+ // We'll do all the tests indirectly.
+
+ [Test]
+ public void SeqFirstAndRestWork()
+ {
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+ ISeq s = v.seq();
+
+ Expect(s.first(), EqualTo(4));
+ Expect(s.rest().first(), EqualTo(5));
+ Expect(s.rest().rest().first(), EqualTo(6));
+ Expect(s.rest().rest().rest(), Null);
+ }
+
+ [Test]
+ public void SeqIndexedWorks()
+ {
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+ ISeq s0 = v.seq();
+ IndexedSeq i0 = s0 as IndexedSeq;
+
+ ISeq s1 = s0.rest();
+ IndexedSeq i1 = s1 as IndexedSeq;
+
+ Expect(i0.index(), EqualTo(0));
+ Expect(i1.index(), EqualTo(1));
+ }
+
+ [Test]
+ public void SeqCountWorks()
+ {
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+ ISeq s = v.seq();
+
+ Expect(s.count(), EqualTo(3));
+ Expect(s.rest().count(), EqualTo(2));
+ Expect(s.rest().rest().count(), EqualTo(1));
+ }
+
+ [Test]
+ public void SeqWithMetaHasMeta()
+ {
+ MockRepository mocks = new MockRepository();
+ IPersistentMap meta = mocks.StrictMock<IPersistentMap>();
+ mocks.ReplayAll();
+
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+ IObj s = (IObj)v.seq();
+ IObj obj = s.withMeta(meta);
+
+ Expect(obj.meta(), SameAs(meta));
+ mocks.VerifyAll();
+ }
+
+ [Test]
+ public void SeqReduceWithNoStartIterates()
+ {
+ MockRepository mocks = new MockRepository();
+ IFn fn = mocks.StrictMock<IFn>();
+ RMExpect.Call(fn.invoke(1, 2)).Return(5);
+ RMExpect.Call(fn.invoke(5, 3)).Return(7);
+ mocks.ReplayAll();
+
+ CPV v = new CPV(new object[] { 1, 2, 3 });
+ IReduce r = (IReduce)v.seq();
+ object ret = r.reduce(fn);
+
+ Expect(ret, EqualTo(7));
+ mocks.VerifyAll();
+ }
+
+ [Test]
+ public void SeqReduceWithStartIterates()
+ {
+ MockRepository mocks = new MockRepository();
+ IFn fn = mocks.StrictMock<IFn>();
+ RMExpect.Call(fn.invoke(20, 1)).Return(10);
+ RMExpect.Call(fn.invoke(10, 2)).Return(5);
+ RMExpect.Call(fn.invoke(5, 3)).Return(7);
+ mocks.ReplayAll();
+
+ CPV v = new CPV(new object[] { 1, 2, 3 });
+ IReduce r = (IReduce)v.seq();
+ object ret = r.reduce(fn, 20);
+
+ Expect(ret, EqualTo(7));
+ mocks.VerifyAll();
+ }
+
+ #endregion
+
+ #region APersistentVector.RSeq tests
+
+ // We'll do all the tests indirectly.
+
+ [Test]
+ public void RSeqFirstAndRestWork()
+ {
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+ ISeq s = v.rseq();
+
+ Expect(s.first(), EqualTo(6));
+ Expect(s.rest().first(), EqualTo(5));
+ Expect(s.rest().rest().first(), EqualTo(4));
+ Expect(s.rest().rest().rest(), Null);
+ }
+
+ [Test]
+ public void RSeqIndexedWorks()
+ {
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+ ISeq s0 = v.rseq();
+ IndexedSeq i0 = s0 as IndexedSeq;
+
+ ISeq s1 = s0.rest();
+ IndexedSeq i1 = s1 as IndexedSeq;
+
+ Expect(i0.index(), EqualTo(0));
+ Expect(i1.index(), EqualTo(1));
+ }
+
+ [Test]
+ public void RSeqCountWorks()
+ {
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+ ISeq s = v.rseq();
+
+ Expect(s.count(), EqualTo(3));
+ Expect(s.rest().count(), EqualTo(2));
+ Expect(s.rest().rest().count(), EqualTo(1));
+ }
+
+ [Test]
+ public void RSeqWithMetaHasMeta()
+ {
+ MockRepository mocks = new MockRepository();
+ IPersistentMap meta = mocks.StrictMock<IPersistentMap>();
+ mocks.ReplayAll();
+
+ CPV v = new CPV(new object[] { 4, 5, 6 });
+ IObj s = (IObj)v.rseq();
+ IObj obj = s.withMeta(meta);
+
+ Expect(obj.meta(), SameAs(meta));
+ mocks.VerifyAll();
+ }
+
+ [Test]
+ public void RSeqReduceWithNoStartIterates()
+ {
+ MockRepository mocks = new MockRepository();
+ IFn fn = mocks.StrictMock<IFn>();
+ RMExpect.Call(fn.invoke(3, 2)).Return(5);
+ RMExpect.Call(fn.invoke(5, 1)).Return(7);
+ mocks.ReplayAll();
+
+ CPV v = new CPV(new object[] { 1, 2, 3 });
+ IReduce r = (IReduce)v.rseq();
+ object ret = r.reduce(fn);
+
+ Expect(ret, EqualTo(7));
+ mocks.VerifyAll();
+ }
+
+ [Test]
+ public void RSeqReduceWithStartIterates()
+ {
+ MockRepository mocks = new MockRepository();
+ IFn fn = mocks.StrictMock<IFn>();
+ RMExpect.Call(fn.invoke(20, 3)).Return(10);
+ RMExpect.Call(fn.invoke(10, 2)).Return(5);
+ RMExpect.Call(fn.invoke(5, 1)).Return(7);
+ mocks.ReplayAll();
+
+ CPV v = new CPV(new object[] { 1, 2, 3 });
+ IReduce r = (IReduce)v.rseq();
+ object ret = r.reduce(fn, 20);
+
+ Expect(ret, EqualTo(7));
+ mocks.VerifyAll();
+ }
+
+ #endregion
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/ARefTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/ARefTests.cs new file mode 100644 index 00000000..c2cbe01e --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/ARefTests.cs @@ -0,0 +1,22 @@ +/**
+ * 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;
+
+namespace Clojure.Tests.LibTests
+{
+ // TODO: Add tests for Aref
+ class ARefTests
+ {
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/AReferenceTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/AReferenceTests.cs new file mode 100644 index 00000000..8f621ae2 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/AReferenceTests.cs @@ -0,0 +1,98 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+
+namespace Clojure.Tests.LibTests
+{
+
+ [TestFixture]
+ public class AReferenceTests : AssertionHelper
+ {
+ // AReference is abstract. We need a class to instantiate.
+
+ class ConcreteAReference : AReference
+ {
+ public ConcreteAReference() : base() { }
+ public ConcreteAReference(IPersistentMap meta) : base(meta) { }
+ }
+
+
+ #region C-tor tests
+
+ [Test]
+ public void Default_ctor_creates_with_null_metadata()
+ {
+ ConcreteAReference c = new ConcreteAReference();
+ Expect(c.meta(), Null);
+ }
+
+ [Test]
+ public void Map_ctor_creates_with_given_metadata()
+ {
+ MockRepository mocks = new MockRepository();
+ IPersistentMap meta = mocks.StrictMock<IPersistentMap>();
+ mocks.ReplayAll();
+
+ ConcreteAReference c = new ConcreteAReference(meta);
+ Expect(c.meta(), EqualTo(meta));
+
+ mocks.VerifyAll();
+ }
+
+ #endregion
+
+
+ #region IReference tests
+
+ [Test]
+ public void AlterMeta_changes_meta()
+ {
+ MockRepository mocks = new MockRepository();
+ IPersistentMap meta = mocks.StrictMock<IPersistentMap>();
+ IFn fn = mocks.StrictMock<IFn>();
+ RMExpect.Call(fn.applyTo(null)).IgnoreArguments().Return(meta);
+ mocks.ReplayAll();
+
+ ConcreteAReference c = new ConcreteAReference();
+ c.alterMeta(fn, null);
+
+ Expect(c.meta(), EqualTo(meta));
+ mocks.VerifyAll();
+ }
+
+ [Test]
+ public void ResetMeta_sets_meta()
+ {
+ MockRepository mocks = new MockRepository();
+ IPersistentMap meta = mocks.StrictMock<IPersistentMap>();
+ mocks.ReplayAll();
+
+ ConcreteAReference c = new ConcreteAReference();
+ c.resetMeta(meta);
+
+ Expect(c.meta(), EqualTo(meta));
+ mocks.VerifyAll();
+ }
+
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/AgentTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/AgentTests.cs new file mode 100644 index 00000000..76bd2185 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/AgentTests.cs @@ -0,0 +1,22 @@ +/**
+ * 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;
+
+namespace Clojure.Tests.LibTests
+{
+ // TODO: Add tests for Agent
+ class AgentTests
+ {
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/ArraySeqTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/ArraySeqTests.cs new file mode 100644 index 00000000..cd033bb9 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/ArraySeqTests.cs @@ -0,0 +1,214 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class ArraySeqTests : AssertionHelper
+ {
+
+ #region C-tor tests
+
+ [Test]
+ public void Create_on_nothing_returns_null()
+ {
+ ArraySeq a = ArraySeq.create();
+
+ Expect(a, Null);
+ }
+
+ [Test]
+ public void Create_on_array_creates()
+ {
+ object[] array = new object[] { 1, 2, 3 };
+ ArraySeq a = ArraySeq.create(array);
+
+ Expect(a, Not.Null);
+ }
+
+ [Test]
+ public void Create_on_null_returns_null()
+ {
+ ArraySeq a = ArraySeq.create(null);
+
+ Expect(a, Null);
+ }
+
+ [Test]
+ public void Create_on_array_has_no_meta()
+ {
+ object[] array = new object[] { 1, 2, 3 };
+ ArraySeq a = ArraySeq.create(array);
+
+ Expect(a.meta(), Null);
+ }
+
+ [Test]
+ public void Create_on_array_and_index_creates()
+ {
+ object[] array = new object[] { 1, 2, 3 };
+ ArraySeq a = ArraySeq.create(array,0);
+
+ Expect(a, Not.Null);
+ }
+
+ [Test]
+ public void Create_on_array_and_index_with_high_index_returns_null()
+ {
+ object[] array = new object[] { 1, 2, 3 };
+ ArraySeq a = ArraySeq.create(array, 10);
+
+ Expect(a, Null);
+ }
+
+ [Test]
+ public void Create_on_array_and_index_has_no_meta()
+ {
+ object[] array = new object[] { 1, 2, 3 };
+ ArraySeq a = ArraySeq.create(array,0);
+
+ Expect(a.meta(), Null);
+ }
+
+ #endregion
+
+ #region IPersistentCollection tests
+
+ [Test]
+ public void ArraySeq_has_correct_count_1()
+ {
+ object[] array = new object[] { 1, 2, 3 };
+ ArraySeq a = ArraySeq.create(array);
+
+ Expect(a.count(), EqualTo(3));
+ }
+
+ [Test]
+ public void ArraySeq_has_correct_count_2()
+ {
+ object[] array = new object[] { 1, 2, 3 };
+ ArraySeq a = ArraySeq.create(array,1);
+
+ Expect(a.count(), EqualTo(2));
+ }
+
+ #endregion
+
+
+ #region IReduce tests
+
+ [Test]
+ public void ReduceWithNoStartIterates()
+ {
+ MockRepository mocks = new MockRepository();
+ IFn fn = mocks.StrictMock<IFn>();
+ RMExpect.Call(fn.invoke(2, 3)).Return(5);
+ RMExpect.Call(fn.invoke(5, 4)).Return(7);
+ mocks.ReplayAll();
+
+ object[] array = new object[] { 2, 3, 4 };
+ ArraySeq a = ArraySeq.create(array);
+ object ret = a.reduce(fn);
+
+ Expect(ret, EqualTo(7));
+
+ mocks.VerifyAll();
+ }
+
+ [Test]
+ public void ReduceWithStartIterates()
+ {
+ MockRepository mocks = new MockRepository();
+ IFn fn = mocks.StrictMock<IFn>();
+ RMExpect.Call(fn.invoke(20, 2)).Return(10);
+ RMExpect.Call(fn.invoke(10, 3)).Return(5);
+ RMExpect.Call(fn.invoke(5, 4)).Return(7);
+ mocks.ReplayAll();
+
+ object[] array = new object[] { 2, 3, 4 };
+ ArraySeq a = ArraySeq.create(array);
+ object ret = a.reduce(fn, 20);
+
+ Expect(ret, EqualTo(7));
+
+ mocks.VerifyAll();
+ }
+ #endregion
+
+ }
+
+ [TestFixture]
+ public class ArraySeq_ISeq_Tests : ISeqTestHelper
+ {
+ object[] _array0;
+ object[] _array1;
+ ArraySeq _a0;
+ ArraySeq _a1;
+
+ [SetUp]
+ public void Setup()
+ {
+ _array0 = new object[] { 1, 2, 3 };
+ _array1 = new object[] { 2, 3 };
+ _a0 = ArraySeq.create(_array0);
+ _a1 = ArraySeq.create(_array0, 1);
+ }
+
+ [Test]
+ public void ArraySeq_ISeq_std_ctor_has_correct_elements()
+ {
+ VerifyISeqContents(_a0, _array0);
+ }
+
+ [Test]
+ public void ArraySeq_ISeq_index_ctor_has_correct_elements()
+ {
+ VerifyISeqContents(_a1, _array1 );
+ }
+
+ [Test]
+ public void ArraySeq_ISeq_std_ctor_conses()
+ {
+ VerifyISeqCons(_a0, 4, _array0);
+ }
+
+ [Test]
+ public void ArraySeq_ISeq_index_ctor_conses()
+ {
+ VerifyISeqCons(_a1, 4, _array1);
+ }
+
+
+ }
+
+ [TestFixture]
+ public class ArraySeq_IObj_Tests : IObjTests
+ {
+ [SetUp]
+ public void Setup()
+ {
+ object[] array = new object[] { 1, 2, 3 };
+ _objWithNullMeta = _obj = ArraySeq.create(array, 0);
+ _expectedType = typeof(ArraySeq);
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/ArrayStreamTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/ArrayStreamTests.cs new file mode 100644 index 00000000..45377efb --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/ArrayStreamTests.cs @@ -0,0 +1,22 @@ +/**
+ * 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;
+
+namespace Clojure.Tests.LibTests
+{
+ // TODO: Add ArrayStream tests
+ class ArrayStreamTests
+ {
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/AtomTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/AtomTests.cs new file mode 100644 index 00000000..d4ea90b5 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/AtomTests.cs @@ -0,0 +1,22 @@ +/**
+ * 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;
+
+namespace Clojure.Tests.LibTests
+{
+ // TODO: Add tests for Atom
+ class AtomTests
+ {
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/CachedSeqTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/CachedSeqTests.cs new file mode 100644 index 00000000..cd4b967d --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/CachedSeqTests.cs @@ -0,0 +1,22 @@ +/**
+ * 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;
+
+namespace Clojure.Tests.LibTests
+{
+ // TODO: write tests for CachedSeq
+ class CachedSeqTests
+ {
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/ConsTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/ConsTests.cs new file mode 100644 index 00000000..b01e1786 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/ConsTests.cs @@ -0,0 +1,428 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+using System.Collections;
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class ConsTests : AssertionHelper
+ {
+ #region C-tor tests
+
+ [Test]
+ public void NoMetaCtorHasNoMeta()
+ {
+ Cons c = new Cons("abc",null);
+ Expect(c.meta(),Null);
+ }
+
+ [Test]
+ public void MetaCtorHasMeta()
+ {
+ MockRepository mocks = new MockRepository();
+ IPersistentMap meta = mocks.StrictMock<IPersistentMap>();
+ mocks.ReplayAll();
+
+ Cons c = new Cons(meta, "abc", null);
+
+ Expect(c.meta(), SameAs(meta));
+ mocks.VerifyAll();
+ }
+
+ #endregion
+
+ #region IPersistentCollection tests
+
+ [Test]
+ public void CountOfOneItem()
+ {
+ Cons c = new Cons("abc", null);
+
+ Expect(c.count(), EqualTo(1));
+ }
+
+ [Test]
+ public void CountOfTwoItems()
+ {
+ Cons c1 = new Cons("abc", null);
+ Cons c2 = new Cons("def", c1);
+
+ Expect(c2.count(), EqualTo(2));
+ }
+
+ [Test]
+ public void SeqReturnsSelf()
+ {
+ Cons c1 = new Cons("abc", null);
+
+ Expect(c1.seq(), SameAs(c1));
+ }
+
+ [Test]
+ public void EmptyIsNull()
+ {
+ // Test of ASeq
+ Cons c = new Cons("abc", null);
+ Expect(c.empty(), Null);
+ }
+
+ [Test]
+ public void IPC_Cons_works()
+ {
+ Cons c1 = new Cons("abc", null);
+ IPersistentCollection ipc1 = c1 as IPersistentCollection;
+
+ IPersistentCollection ipc2 = ipc1.cons("def");
+ ISeq s = ipc2.seq();
+
+ Expect(s.first(), EqualTo("def"));
+ Expect(s.rest(), SameAs(c1));
+ }
+
+ #endregion
+
+ #region ASeq tests
+
+ // Some aspects of ASeq have been tested above.
+ // Here are the remaining bits
+
+ private Cons CreateComplicatedCons()
+ {
+ Cons c1 = new Cons(1, null);
+ Cons c2 = new Cons(2, c1);
+ Cons c3 = new Cons("abc", null);
+ Cons c4 = new Cons(c3, c2);
+ Cons c5 = new Cons("def", c4);
+
+ return c5;
+ }
+
+ [Test]
+ public void EqualsDoesValueComparison()
+ {
+ Cons a = CreateComplicatedCons();
+ Cons b = CreateComplicatedCons();
+
+ Expect(a.Equals(b));
+ Expect(a, EqualTo(b));
+ }
+
+ [Test]
+ public void GetHashCodeComputesOnValue()
+ {
+ Cons a = CreateComplicatedCons();
+ Cons b = CreateComplicatedCons();
+
+ Expect(a.GetHashCode(), EqualTo(b.GetHashCode()));
+ }
+
+
+ #endregion
+
+ #region ASeq.ICollection tests
+
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void ASeqICollCopyToFailsOnNullArray()
+ {
+ ICollection ic = new Cons(1, null);
+ ic.CopyTo(null, 0);
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void ASeqICollCopyToFailsOnInsufficientSpace()
+ {
+ ICollection ic = new Cons(1, new Cons(2, new Cons(3,null)));
+ Array arr = new object[2];
+ ic.CopyTo(arr, 0);
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void ASeqICollCopyToFailsOnInsufficientSpace2()
+ {
+ ICollection ic = new Cons(1, new Cons(2, new Cons(3, null)));
+ Array arr = new object[10];
+ ic.CopyTo(arr, 8);
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void ASeqICollCopyToFailsOnMultidimArray()
+ {
+ ICollection ic = new Cons(1, new Cons(2, new Cons(3, null)));
+ Array arr = Array.CreateInstance(typeof(int), 4, 4);
+ ic.CopyTo(arr, 0);
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void ASeqICollCopyToFailsOnNegativeIndex()
+ {
+ ICollection ic = new Cons(1, new Cons(2, new Cons(3, null)));
+ Array arr = new object[10];
+ ic.CopyTo(arr, -1);
+ }
+
+ [Test]
+ public void ASeqICollCopyToCopiesToIndex0()
+ {
+ ICollection ic = new Cons(1, new Cons(2, new Cons(3, null)));
+ int[] arr = new int[4];
+ ic.CopyTo(arr, 0);
+
+ Expect(arr[0], EqualTo(1));
+ Expect(arr[1], EqualTo(2));
+ Expect(arr[2], EqualTo(3));
+ Expect(arr[3], EqualTo(0));
+ }
+
+ [Test]
+ public void ASeqICollCopyToCopiesToIndexPositive()
+ {
+ ICollection ic = new Cons(1, new Cons(2, new Cons(3, null)));
+ int[] arr = new int[4];
+ ic.CopyTo(arr, 1);
+
+ Expect(arr[0], EqualTo(0));
+ Expect(arr[1], EqualTo(1));
+ Expect(arr[2], EqualTo(2));
+ Expect(arr[3], EqualTo(3));
+ }
+
+ [Test]
+ public void ASeqICollCountWorks()
+ {
+ ICollection ic = new Cons(1, new Cons(2, new Cons(3, null)));
+
+ Expect(ic.Count, EqualTo(3));
+ }
+
+ [Test]
+ public void ASeqICollIsSynchronized()
+ {
+ ICollection ic = new Cons(1, new Cons(2, new Cons(3, null)));
+ Expect(ic.IsSynchronized);
+ }
+
+ [Test]
+ [ExpectedException(typeof(NotImplementedException))]
+ public void ASeqICollHasDoesntImplementSyncRoot()
+ {
+ ICollection ic = new Cons(1, new Cons(2, new Cons(3, null)));
+ Object o = ic.SyncRoot;
+ }
+
+ [Test]
+ public void ASeqIEnumWorks()
+ {
+ ICollection ic = new Cons(1, new Cons(2, new Cons(3, null)));
+ IEnumerator e = ic.GetEnumerator();
+
+ Expect(e.MoveNext());
+ Expect(e.Current, EqualTo(1));
+ Expect(e.MoveNext());
+ Expect(e.Current, EqualTo(2));
+ Expect(e.MoveNext());
+ Expect(e.Current, EqualTo(3));
+ Expect(e.MoveNext(),False);
+ }
+
+
+ #endregion
+
+ #region SeqIterator tests
+
+ [Test]
+ public void SeqIteratorOnEmptySeqGoesNowhere()
+ {
+ SeqEnumerator s = new SeqEnumerator(null);
+
+ Expect(s.MoveNext(), False);
+ }
+
+ [Test]
+ [ExpectedException(typeof(InvalidOperationException))]
+ public void SeqIteratorOnEmptyHasNoCurrent()
+ {
+ SeqEnumerator s = new SeqEnumerator(null);
+ object o = s.Current;
+ }
+
+ [Test]
+ public void SeqIteratorIterates()
+ {
+ Cons c = new Cons(1, new Cons(2, null));
+ SeqEnumerator s = new SeqEnumerator(c);
+
+ Expect(s.MoveNext());
+ Expect(s.Current, EqualTo(1));
+ Expect(s.MoveNext());
+ Expect(s.Current, EqualTo(2));
+ Expect(s.Current, EqualTo(2));
+ Expect(s.MoveNext(), False);
+ }
+
+ [Test]
+ [ExpectedException(typeof(InvalidOperationException))]
+ public void SeqIteratorMovedToEmptyHasNoCurrent()
+ {
+ Cons c = new Cons(1, new Cons(2, null));
+ SeqEnumerator s = new SeqEnumerator(c);
+
+ s.MoveNext();
+ s.MoveNext();
+ s.MoveNext();
+ object o = s.Current;
+ }
+
+
+ [ExpectedException(typeof(InvalidOperationException))]
+ public void SeqIteratorResetAtBeginningWorks()
+ {
+ Cons c = new Cons(1, new Cons(2, null));
+ SeqEnumerator s = new SeqEnumerator(c);
+
+ s.Reset();
+
+ Expect(s.MoveNext());
+ Expect(s.Current, EqualTo(1));
+ Expect(s.MoveNext());
+ Expect(s.Current, EqualTo(2));
+ Expect(s.Current, EqualTo(2));
+ Expect(s.MoveNext(), False);
+ }
+
+ [Test]
+ public void SeqIteratorResetAtFirstWorks()
+ {
+ Cons c = new Cons(1, new Cons(2, null));
+ SeqEnumerator s = new SeqEnumerator(c);
+
+ s.MoveNext();
+ s.Reset();
+
+ Expect(s.MoveNext());
+ Expect(s.Current, EqualTo(1));
+ Expect(s.MoveNext());
+ Expect(s.Current, EqualTo(2));
+ Expect(s.Current, EqualTo(2));
+ Expect(s.MoveNext(), False);
+ }
+
+ [Test]
+ public void SeqIteratorResetInMiddleWorks()
+ {
+ Cons c = new Cons(1, new Cons(2, null));
+ SeqEnumerator s = new SeqEnumerator(c);
+
+ s.MoveNext();
+ s.MoveNext();
+ s.Reset();
+
+ Expect(s.MoveNext());
+ Expect(s.Current, EqualTo(1));
+ Expect(s.MoveNext());
+ Expect(s.Current, EqualTo(2));
+ Expect(s.Current, EqualTo(2));
+ Expect(s.MoveNext(), False);
+ }
+
+ [Test]
+ public void SeqIteratorResetAtEndWorks()
+ {
+ Cons c = new Cons(1, new Cons(2, null));
+ SeqEnumerator s = new SeqEnumerator(c);
+
+ s.MoveNext();
+ s.MoveNext();
+ s.MoveNext();
+ s.Reset();
+
+ Expect(s.MoveNext());
+ Expect(s.Current, EqualTo(1));
+ Expect(s.MoveNext());
+ Expect(s.Current, EqualTo(2));
+ Expect(s.Current, EqualTo(2));
+ Expect(s.MoveNext(), False);
+ }
+
+ #endregion
+ }
+
+ [TestFixture]
+ public class Cons_ISeq_Tests : ISeqTestHelper
+ {
+
+ [Test]
+ public void Cons_ISeq_has_correct_values()
+ {
+ Cons c1 = new Cons("def", null);
+ Cons c2 = new Cons("abc", c1);
+
+ VerifyISeqContents(c2, new object[] { "abc", "def" });
+ }
+
+ [Test]
+ public void Cons_ISeq_with_meta_has_correct_values()
+ {
+ MockRepository mocks = new MockRepository();
+ IPersistentMap meta = mocks.StrictMock<IPersistentMap>();
+ mocks.ReplayAll();
+
+ Cons c1 = new Cons("def", null);
+ Cons c2 = new Cons(meta,"abc", c1);
+
+ VerifyISeqContents(c2, new object[] { "abc", "def" });
+ mocks.VerifyAll();
+ }
+
+ [Test]
+ public void Cons_ISeq_conses()
+ {
+
+ Cons c1 = new Cons("def", null);
+ Cons c2 = new Cons("abc", c1);
+
+ VerifyISeqCons(c2, "ghi", new object[] { "abc", "def" });
+ }
+
+
+
+ }
+
+
+ [TestFixture]
+ public class Cons_IMeta_Tests : IObjTests
+ {
+ [SetUp]
+ public void Setup()
+ {
+ IPersistentMap meta = PersistentHashMap.create("a", 1, "b", 2);
+
+ Cons c1 = new Cons("abc", null);
+ Cons c2 = new Cons(meta,"def", c1);
+ _obj = c2;
+ _objWithNullMeta = c1;
+ _expectedType = typeof(Cons);
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/DelayTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/DelayTests.cs new file mode 100644 index 00000000..07d51a1c --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/DelayTests.cs @@ -0,0 +1,22 @@ +/**
+ * 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;
+
+namespace Clojure.Tests.LibTests
+{
+ class DelayTests
+ {
+ //TODO: Write Delay tests
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/FnSeqTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/FnSeqTests.cs new file mode 100644 index 00000000..93d64773 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/FnSeqTests.cs @@ -0,0 +1,120 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class FnSeqTests : AssertionHelper
+ {
+ #region C-tor tests
+
+ // Couldn't think of anything except to make sure it doesn't throw an exception.
+ [Test]
+ public void CtorWorks()
+ {
+ FnSeq fs = new FnSeq("abc",null);
+
+ Expect(fs, Not.Null);
+ }
+
+ #endregion
+ }
+
+ [TestFixture]
+ public class FnSeq_ISeq_Tests : ISeqTestHelper
+ {
+ object[] _restValues;
+ object[] _values;
+ MockRepository _mocks;
+ FnSeq _fs;
+
+ [SetUp]
+ public void Setup()
+ {
+ _restValues = new object[] { 2, 3, 4 };
+ _values = new object[] { "abc", 2, 3, 4 };
+ _mocks = new MockRepository();
+ IFn _fn = _mocks.StrictMock<IFn>();
+ RMExpect.Call(_fn.invoke()).Return(PersistentList.create(_restValues));
+
+ _fs = new FnSeq("abc", _fn);
+
+ _mocks.ReplayAll();
+ }
+
+ [TearDown]
+ public void Teardown()
+ {
+ _mocks.VerifyAll();
+ }
+
+
+ [Test]
+ public void FnSeq_ISeq_has_correct_values()
+ {
+ VerifyISeqContents(_fs, _values);
+ }
+
+ [Test]
+ public void FnSeq_ISeq_conses()
+ {
+ VerifyISeqCons(_fs, 12, _values);
+ }
+
+ [Test]
+ public void RestCachesResult()
+ {
+ _fs.rest();
+ _fs.rest();
+ }
+
+
+
+ }
+
+ [TestFixture]
+ public class FnSeq_IObj_Tests : IObjTests
+ {
+ MockRepository _mocks;
+
+ [SetUp]
+ public void Setup()
+ {
+ _mocks = new MockRepository();
+ IFn fn = _mocks.StrictMock<IFn>();
+ RMExpect.Call(fn.invoke()).Return(null);
+ _mocks.ReplayAll();
+
+ FnSeq fs = new FnSeq("abc", fn);
+
+ _obj = _objWithNullMeta = fs;
+ _expectedType = typeof(FnSeq);
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ _mocks.ReplayAll();
+ }
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/IObjTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/IObjTests.cs new file mode 100644 index 00000000..34f1ba72 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/IObjTests.cs @@ -0,0 +1,91 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+using clojure.lang;
+
+namespace Clojure.Tests.LibTests
+{
+ /// <summary>
+ /// Base class for testing the IMeta interface functionality.
+ /// </summary>
+ public abstract class IObjTests : AssertionHelper
+ {
+ /// <summary>
+ /// Object to test for null meta. Set null if no test. Initialize in Setup.
+ /// </summary>
+ protected IObj _objWithNullMeta;
+
+
+ /// <summary>
+ /// The object to test. Initialize in Setup.
+ /// </summary>
+ protected IObj _obj;
+
+ /// <summary>
+ /// Expected type of return from withMeta. Set null if no test. Initialize in Setup.
+ /// </summary>
+ protected Type _expectedType;
+
+
+ MockRepository _mocks = null;
+ IPersistentMap _meta = null;
+
+ void InitMocks()
+ {
+ _mocks = new MockRepository();
+ _meta = _mocks.StrictMock<IPersistentMap>();
+ _mocks.ReplayAll();
+ }
+
+
+ [Test]
+ public void withMeta_has_correct_meta()
+ {
+ InitMocks();
+ IObj obj2 = _obj.withMeta(_meta);
+ Expect(obj2.meta(), SameAs(_meta));
+ _mocks.VerifyAll();
+ }
+
+ [Test]
+ public void withMeta_returns_correct_type()
+ {
+ if (_expectedType == null)
+ return;
+
+ InitMocks();
+ IObj obj2 = _obj.withMeta(_meta);
+ Expect(obj2, TypeOf(_expectedType));
+ _mocks.VerifyAll();
+ }
+
+ [Test]
+ public void withMeta_returns_self_if_no_change()
+ {
+ IObj obj2 = _obj.withMeta(_obj.meta());
+ Expect(obj2, SameAs(_obj));
+ }
+
+ [Test]
+ public void Verify_Null_Meta()
+ {
+ if (_objWithNullMeta == null)
+ return;
+ Expect(_objWithNullMeta.meta(), Null);
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/ISeqTestHelper.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/ISeqTestHelper.cs new file mode 100644 index 00000000..ba04a65e --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/ISeqTestHelper.cs @@ -0,0 +1,54 @@ +/**
+ * 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 NUnit.Framework;
+using clojure.lang;
+
+namespace Clojure.Tests.LibTests
+{
+ public class ISeqTestHelper : AssertionHelper
+ {
+ public void VerifyISeqContents(ISeq s, IList<object> values)
+ {
+ int i=0;
+
+ for (; s != null; s = s.rest(), i++)
+ Expect(s.first(), EqualTo(values[i]));
+
+ Expect(i, EqualTo(values.Count));
+ }
+
+ public void VerifyISeqCons(ISeq s, object newVal, IList<object> values)
+ {
+ ISeq newSeq = s.cons(newVal);
+
+ Expect(newSeq.first(), EqualTo(newVal));
+ VerifyISeqContents(newSeq.rest(), values);
+ }
+
+ public void VerifyISeqRestTypes(ISeq s, Type type)
+ {
+ for ( ; s.rest() != null; s = s.rest())
+ Expect(s.rest(), InstanceOfType(type));
+ }
+
+ public void VerifyISeqRestMaintainsMeta(ISeq s)
+ {
+ IPersistentMap meta = ((IMeta)s).meta();
+
+ for (; s.rest() != null; s = s.rest())
+ Expect(((IMeta)s.rest()).meta(), EqualTo(meta));
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/IteratorStreamTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/IteratorStreamTests.cs new file mode 100644 index 00000000..abc8291d --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/IteratorStreamTests.cs @@ -0,0 +1,22 @@ +/**
+ * 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;
+
+namespace Clojure.Tests.LibTests
+{
+ //TODO: add tests for IteratorStream
+ class IteratorStreamTests
+ {
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/KeywordTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/KeywordTests.cs new file mode 100644 index 00000000..6960f14c --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/KeywordTests.cs @@ -0,0 +1,239 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+using System.Collections;
+
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class KeywordTests : AssertionHelper
+ {
+
+ #region c-tor tests
+
+ [Test]
+ public void InternCreatesKeywordBasedOnSymbol()
+ {
+ Symbol sym = Symbol.intern("def","abc");
+ Keyword k1 = Keyword.intern(sym);
+ Expect(k1.Name,EqualTo(sym.Name));
+ Expect(k1.Namespace,EqualTo(sym.Namespace));
+ }
+
+ [Test]
+ public void InternReturnsSameKeywordOnEqualSym()
+ {
+ Symbol sym1 = Symbol.intern("def", "abc");
+ Symbol sym2 = Symbol.intern("def", "abc");
+ Keyword k1 = Keyword.intern(sym1);
+ Keyword k2 = Keyword.intern(sym2);
+
+ Expect(Object.ReferenceEquals(k1, k2));
+ }
+
+ [Test]
+ public void Intern2CreatesKeywordBasedOnSymbol()
+ {
+ Keyword k1 = Keyword.intern("def","abc");
+ Expect(k1.Name, EqualTo("abc"));
+ Expect(k1.Namespace, EqualTo("def"));
+ }
+
+ [Test]
+ public void Intern2ReturnsSameKeywordOnEqualSym()
+ {
+ Keyword k1 = Keyword.intern("def", "abc");
+ Keyword k2 = Keyword.intern("def", "abc");
+
+ Expect(Object.ReferenceEquals(k1, k2));
+ }
+
+ #endregion
+
+ #region object override tests
+
+ [Test]
+ public void ToStringReturnsStringNameWithColonPrepended()
+ {
+ Symbol sym1 = Symbol.intern("abc");
+ Symbol sym2 = Symbol.intern("abc/def");
+ Keyword k1 = Keyword.intern(sym1);
+ Keyword k2 = Keyword.intern(sym2);
+
+ Expect(k1.ToString(), EqualTo(":abc"));
+ Expect(k2.ToString(), EqualTo(":abc/def"));
+ }
+
+ [Test]
+ public void EqualOnIdentityIsTrue()
+ {
+ Symbol sym1 = Symbol.intern("abc");
+ Keyword k1 = Keyword.intern(sym1);
+
+ Expect(k1.Equals(k1));
+ }
+
+ [Test]
+ public void EqualsOnNonKeywordIsFalse()
+ {
+ Symbol sym1 = Symbol.intern("abc");
+ Keyword k1 = Keyword.intern(sym1);
+
+ Expect(k1.Equals(sym1), False);
+ }
+
+ //[Test]
+ //public void EqualsDependsOnSym()
+ //{
+ // Symbol sym1 = Symbol.intern("abc");
+ // Symbol sym2 = Symbol.intern("abc");
+ // Keyword k1 = Keyword.intern(sym1);
+ // Keyword k2 = Keyword.intern(sym2);
+ // // I don't know how we ever create two keywords that will force
+ // // the code to go into the sym.equals part of the code.
+ // // At least, not through the factory methods.
+ //}
+
+ [Test]
+ public void HashCodeDependsOnValue()
+ {
+ Symbol sym1 = Symbol.intern("abc");
+ Symbol sym2 = Symbol.intern("abc/def");
+ Keyword k1 = Keyword.intern(sym1);
+ Keyword k2 = Keyword.intern(sym2);
+
+ Expect(k1.GetHashCode(), Not.EqualTo(k2.GetHashCode()));
+ }
+
+
+ #endregion
+
+ #region Named tests
+
+ public void NameAndNamespaceComeFromTheSymbol()
+ {
+ Symbol sym1 = Symbol.intern("def", "abc");
+ Keyword k1 = Keyword.intern(sym1);
+ Symbol sym2 = Symbol.intern("abc");
+ Keyword k2 = Keyword.intern(sym2);
+ Expect(k1.Name, EqualTo("abc"));
+ Expect(k1.Namespace, EqualTo("def"));
+ Expect(k2.Name, EqualTo("abc"));
+ Expect(k2.Namespace, Null);
+ }
+
+ #endregion
+
+ #region IFn Tests
+
+ [Test]
+ public void Invoke2IndexesIntoItsFirstArg()
+ {
+ Keyword k1 = Keyword.intern(Symbol.intern("abc"));
+ Keyword k2 = Keyword.intern(Symbol.intern("ab"));
+
+ IDictionary dict = new Hashtable();
+ dict[k1] = 7;
+ dict["abc"] = 8;
+
+ Expect(k1.invoke(dict), EqualTo(7));
+ Expect(k2.invoke(dict), Null);
+ }
+
+ [Test]
+ public void Invoke3IndexesIntoItsFirstArg()
+ {
+ Keyword k1 = Keyword.intern(Symbol.intern("abc"));
+ Keyword k2 = Keyword.intern(Symbol.intern("ab"));
+
+ IDictionary dict = new Hashtable();
+ dict[k1] = 7;
+ dict["abc"] = 8;
+
+ Expect(k1.invoke(dict, 20), EqualTo(7));
+ Expect(k2.invoke(dict, 20), EqualTo(20));
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void InvokeOnNoArgsFails()
+ {
+ Keyword k1 = Keyword.intern(Symbol.intern("abc"));
+ object o = k1.invoke();
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void InvokeOnTooManyArgsFails()
+ {
+ Keyword k1 = Keyword.intern(Symbol.intern("abc"));
+ IDictionary dict = new Hashtable();
+ dict[k1] = 7;
+ dict["abc"] = 8;
+
+ object o = k1.invoke(dict, 20, null);
+ }
+
+ #endregion
+
+ #region IComparable tests
+
+ [Test]
+ [ExpectedException(typeof(InvalidCastException))]
+ public void CompareToNonKeywordFails()
+ {
+ Symbol s1 = Symbol.intern("abc");
+ Keyword k1 = Keyword.intern(s1);
+ int c = k1.CompareTo(s1);
+ }
+
+ [Test]
+ public void CompareToEqualKeywordIsZero()
+ {
+ Keyword k1 = Keyword.intern(Symbol.intern("abc"));
+ Keyword k2 = Keyword.intern(Symbol.intern("abc"));
+
+ Expect(k1.CompareTo(k2), EqualTo(0));
+ }
+
+ [Test]
+ public void CompareDependsOnSymbolCompare()
+ {
+ Symbol sym1 = Symbol.intern("abc");
+ Symbol sym2 = Symbol.intern("a", "abc");
+ Symbol sym3 = Symbol.intern("b", "abc");
+ Keyword k1 = Keyword.intern(sym1);
+ Keyword k2 = Keyword.intern(sym2);
+ Keyword k3 = Keyword.intern(sym3);
+
+ Expect(k1.CompareTo(k2), LessThan(0));
+ Expect(k2.CompareTo(k1), GreaterThan(0));
+ Expect(k1.CompareTo(k3), LessThan(0));
+ Expect(k3.CompareTo(k1), GreaterThan(0));
+ }
+
+
+ #endregion
+ }
+
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/LazilyPersistentVectorTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/LazilyPersistentVectorTests.cs new file mode 100644 index 00000000..e5549288 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/LazilyPersistentVectorTests.cs @@ -0,0 +1,220 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class LazilyPersistentVectorTests : AssertionHelper
+ {
+ #region C-tor tests
+
+ [Test]
+ public void CreateOwningOnNoParamsReturnsEmptyVector()
+ {
+ IPersistentVector v = LazilyPersistentVector.createOwning();
+ Expect(v.count(),EqualTo(0));
+ }
+
+ [Test]
+ public void CreatingOwningOnParamsReturnsVector()
+ {
+ IPersistentVector v = LazilyPersistentVector.createOwning(1, 2, 3);
+ Expect(v.count(), EqualTo(3));
+ Expect(v.nth(0), EqualTo(1));
+ Expect(v.nth(1), EqualTo(2));
+ Expect(v.nth(2), EqualTo(3));
+ }
+
+ [Test]
+ public void CreateOnEmptySeqReturnsEmptyVector()
+ {
+ IPersistentVector v = LazilyPersistentVector.create(new object[] {});
+ Expect(v.count(), EqualTo(0));
+ }
+
+ [Test]
+ public void CreateOnNonEmptyCollectionReturnsVector()
+ {
+ IPersistentVector v = LazilyPersistentVector.createOwning(new object[] {1, 2, 3});
+ Expect(v.count(), EqualTo(3));
+ Expect(v.nth(0), EqualTo(1));
+ Expect(v.nth(1), EqualTo(2));
+ Expect(v.nth(2), EqualTo(3));
+ }
+
+
+ #endregion
+
+ #region IPersistentVector tests
+
+ [Test]
+ public void NthInRangeWorks()
+ {
+ IPersistentVector v = LazilyPersistentVector.createOwning(1, 2, 3);
+
+ Expect(v.count(), EqualTo(3));
+ Expect(v.nth(0), EqualTo(1));
+ Expect(v.nth(1), EqualTo(2));
+ Expect(v.nth(2), EqualTo(3));
+ }
+
+ [Test]
+ [ExpectedException(typeof(IndexOutOfRangeException))]
+ public void NthOutOfRangeLowFails()
+ {
+ IPersistentVector v = LazilyPersistentVector.createOwning(1, 2, 3);
+
+ Expect(v.nth(-4), EqualTo(1));
+ }
+
+ [Test]
+ [ExpectedException(typeof(IndexOutOfRangeException))]
+ public void NthOutOfRangeHighFails()
+ {
+ IPersistentVector v = LazilyPersistentVector.createOwning(1, 2, 3);
+
+ Expect(v.nth(4), EqualTo(1));
+ }
+
+ [Test]
+ public void AssocnInRangeModifies()
+ {
+ IPersistentVector v = LazilyPersistentVector.createOwning(1, 2, 3);
+ IPersistentVector v2 = v.assocN(1, 4);
+
+ Expect(v.count(), EqualTo(3));
+ Expect(v.nth(0), EqualTo(1));
+ Expect(v.nth(1), EqualTo(2));
+ Expect(v.nth(2), EqualTo(3));
+
+ Expect(v2.count(), EqualTo(3));
+ Expect(v2.nth(0), EqualTo(1));
+ Expect(v2.nth(1), EqualTo(4));
+ Expect(v2.nth(2), EqualTo(3));
+ }
+
+ [Test]
+ public void AssocnAtEndModifies()
+ {
+ IPersistentVector v = LazilyPersistentVector.createOwning(1, 2, 3);
+ IPersistentVector v2 = v.assocN(3, 4);
+
+ Expect(v.count(), EqualTo(3));
+ Expect(v.nth(0), EqualTo(1));
+ Expect(v.nth(1), EqualTo(2));
+ Expect(v.nth(2), EqualTo(3));
+
+ Expect(v2.count(), EqualTo(4));
+ Expect(v2.nth(0), EqualTo(1));
+ Expect(v2.nth(1), EqualTo(2));
+ Expect(v2.nth(2), EqualTo(3));
+ Expect(v2.nth(3), EqualTo(4));
+ }
+
+ [Test]
+ [ExpectedException(typeof(IndexOutOfRangeException))]
+ public void AssocNOutOfRangeLowFails()
+ {
+ IPersistentVector v = LazilyPersistentVector.createOwning(1, 2, 3);
+ IPersistentVector v2 = v.assocN(-4, 4);
+ }
+
+ [Test]
+ [ExpectedException(typeof(IndexOutOfRangeException))]
+ public void AssocNOutOfRangeHighFails()
+ {
+ IPersistentVector v = LazilyPersistentVector.createOwning(1, 2, 3);
+ IPersistentVector v2 = v.assocN(4, 4);
+ }
+
+
+ [Test]
+ public void ConsAddsAtEnd()
+ {
+ IPersistentVector v = LazilyPersistentVector.createOwning(1, 2, 3);
+ IPersistentVector v2 = v.cons(4);
+
+ Expect(v.count(), EqualTo(3));
+ Expect(v.nth(0), EqualTo(1));
+ Expect(v.nth(1), EqualTo(2));
+ Expect(v.nth(2), EqualTo(3));
+
+ Expect(v2.count(), EqualTo(4));
+ Expect(v2.nth(0), EqualTo(1));
+ Expect(v2.nth(1), EqualTo(2));
+ Expect(v2.nth(2), EqualTo(3));
+ Expect(v2.nth(3), EqualTo(4));
+ }
+
+ [Test]
+ public void LengthWorks()
+ {
+ IPersistentVector v = LazilyPersistentVector.createOwning(1, 2, 3);
+
+ Expect(v.length(), EqualTo(3));
+ }
+
+ #endregion
+
+ #region IPersistentStack tests
+
+ [Test]
+ public void PopOnNonEmptyWorks()
+ {
+ IPersistentVector v = LazilyPersistentVector.createOwning(1, 2, 3);
+ IPersistentVector v2 = (IPersistentVector)((IPersistentStack)v).pop();
+
+ Expect(v.count(), EqualTo(3));
+ Expect(v.nth(0), EqualTo(1));
+ Expect(v.nth(1), EqualTo(2));
+ Expect(v.nth(2), EqualTo(3));
+
+ Expect(v2.count(), EqualTo(2));
+ Expect(v2.nth(0), EqualTo(1));
+ Expect(v2.nth(1), EqualTo(2));
+ }
+
+ [Test]
+ [ExpectedException(typeof(InvalidOperationException))]
+ public void PopOnEmptyFails()
+ {
+ IPersistentVector v = LazilyPersistentVector.createOwning();
+ IPersistentStack v2 = ((IPersistentStack)v).pop();
+ }
+
+ #endregion
+ }
+
+ [TestFixture]
+ public class LazilyPersistentVector_IObj_Tests : IObjTests
+ {
+ [SetUp]
+ public void Setup()
+ {
+ IPersistentVector v = LazilyPersistentVector.createOwning(1, 2, 3);
+
+ _obj = _objWithNullMeta = (IObj)v;
+ _expectedType = typeof(LazilyPersistentVector);
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/LazyConsTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/LazyConsTests.cs new file mode 100644 index 00000000..0a230c0e --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/LazyConsTests.cs @@ -0,0 +1,123 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class LazyConsTests : AssertionHelper
+ {
+
+ #region C-tor tests
+
+ // Couldn't think of anything except to make sure it doesn't throw an exception.
+ [Test]
+ public void CtorWorks()
+ {
+ LazyCons lc = new LazyCons(null);
+
+ Expect(lc, Not.Null);
+ }
+
+ #endregion
+ }
+
+ [TestFixture]
+ public class LazyCons_ISeq_Tests : ISeqTestHelper
+ {
+ MockRepository _mocks ;
+ IFn _fn;
+ LazyCons _lc;
+ object[] _values;
+
+ [SetUp]
+ public void Setup()
+ {
+ _mocks = new MockRepository();
+ _fn = _mocks.StrictMock<IFn>();
+ RMExpect.Call(_fn.invoke()).Return(10);
+ RMExpect.Call(_fn.invoke(null)).Return(new Cons(20,null));
+ _lc = new LazyCons(_fn);
+ _values = new object[] { 10, 20 };
+ _mocks.ReplayAll();
+ }
+
+ [TearDown]
+ public void Teardown()
+ {
+ _mocks.VerifyAll();
+ }
+
+ [Test]
+ public void ISeq_has_proper_values()
+ {
+ VerifyISeqContents(_lc, _values);
+ }
+
+ [Test]
+ public void First_caches()
+ {
+ _lc.first();
+ _lc.first();
+
+ // Need to meet expectation that _rest is called.
+ _lc.rest();
+ }
+
+ [Test]
+ public void Rest_calcs_first()
+ {
+ _lc.rest();
+ }
+
+ [Test]
+ public void Rest_caches()
+ {
+ _lc.rest();
+ _lc.rest();
+ }
+
+
+
+ }
+
+ [TestFixture]
+ public class LazyCons_IObj_Tests : IObjTests
+ {
+ [SetUp]
+ public void Setup()
+ {
+ MockRepository mocks = new MockRepository();
+ IPersistentMap meta = mocks.StrictMock<IPersistentMap>();
+ IFn fn = mocks.StrictMock<IFn>();
+ RMExpect.Call(fn.invoke()).Return(10);
+ RMExpect.Call(fn.invoke(null)).Return(null);
+ mocks.ReplayAll();
+
+ _objWithNullMeta = new LazyCons(fn);
+ _obj = _objWithNullMeta.withMeta(meta);
+ _expectedType = typeof(LazyCons);
+
+ mocks.VerifyAll();
+ }
+ }
+}
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
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/LockingTransactionTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/LockingTransactionTests.cs new file mode 100644 index 00000000..23682559 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/LockingTransactionTests.cs @@ -0,0 +1,194 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+using System.Threading;
+
+namespace Clojure.Tests.LibTests
+{
+ // TODO: Add tests for LockingTransaction
+ [TestFixture]
+ public class LockingTransactionTests : AssertionHelper
+ {
+
+
+ //// TODO: Make this work.
+
+ //// This test is taken from the Java code.
+ //[Test]
+ //public void BigTest()
+ //{
+ // // We have to start the main work unit on its own thread because
+ // // the main during testing is STA and so does not support waiting on multiple handles.
+
+ // BigTester<Incrementer> tester = new BigTester<Incrementer>(5, 10, 2000);
+ // EventWaitHandle h = new EventWaitHandle(false, EventResetMode.ManualReset);
+ // Thread t = new Thread(tester.Work);
+ // t.Start(h);
+ // h.WaitOne();
+ // Console.WriteLine("Done");
+ //}
+
+ //class BigTester<T> where T : LockingTransactionTests.RefTester
+ //{
+ // int _nthreads;
+ // int _niters;
+ // int _nitems;
+
+ // public BigTester(int nthreads, int nitems, int niters)
+ // {
+ // _nthreads = nthreads;
+ // _nitems = nitems;
+ // _niters = niters;
+ // }
+
+ // public void Work(object o)
+ // {
+ // List<Ref> refs = new List<Ref>();
+
+ // for (int i = 0; i < _nitems; i++)
+ // refs.Add(new Ref(0));
+
+ // List<Thread> threads = new List<Thread>(_nthreads);
+ // List<EventWaitHandle> handles = new List<EventWaitHandle>(_nthreads);
+ // List<Incrementer> tasks = new List<Incrementer>(_nthreads);
+
+ // for (int i = 0; i < _nthreads; i++)
+ // {
+ // List<Ref> copy = refs.GetRange(0, refs.Count);
+ // Shuffle(copy);
+ // tasks.Add(new Incrementer(i, _niters, copy));
+ // threads.Add(new Thread(tasks[i].Work));
+ // handles.Add(new EventWaitHandle(false, EventResetMode.ManualReset));
+ // }
+
+
+ // for (int i = 0; i < _nthreads; i++)
+ // {
+ // threads[i].Name = "Thr " + i;
+ // threads[i].Start(handles[i]);
+ // }
+
+ // EventWaitHandle.WaitAll(handles.ToArray());
+
+ // foreach (Incrementer task in tasks)
+ // Console.WriteLine("Task {0}: {1} millisecs", task.Id, task.Nanos / 10000.0);
+
+ // foreach (Ref r in refs)
+ // Console.WriteLine("Ref is {0}", r.get());
+
+ // EventWaitHandle ewh = (EventWaitHandle)o;
+ // ewh.Set();
+
+ // }
+
+
+ // void Shuffle(List<Ref> refs)
+ // {
+ // }
+ //}
+
+ //public abstract class RefTester
+ //{
+ // readonly int _id;
+ // public int Id
+ // {
+ // get { return _id; }
+ // }
+
+ // protected readonly int _niters;
+ // protected readonly List<Ref> _items;
+
+ // long _nanos = 0;
+ // public long Nanos
+ // {
+ // get { return _nanos; }
+ // }
+
+
+ // public RefTester(int id, int niters, List<Ref> items)
+ // {
+ // _id = id;
+ // _niters = niters;
+ // _items = items;
+ // }
+
+ // public void Work(object o)
+ // {
+ // for (int i = 0; i < _niters; i++)
+ // {
+ // long startTime = DateTime.Now.Ticks;
+ // LockingTransaction.runInTransaction(this.TxUnit);
+ // long finishTime = DateTime.Now.Ticks;
+ // _nanos += finishTime - startTime;
+ // }
+
+ // EventWaitHandle h = (EventWaitHandle)o;
+ // h.Set();
+ // }
+
+ // public abstract object TxUnit(object[] args);
+ //}
+
+ //class Incrementer : RefTester
+ //{
+ // public Incrementer(int id, int niters, List<Ref> items)
+ // : base(id, niters, items)
+ // {
+ // }
+
+ // public override object TxUnit(object[] args)
+ // {
+ // foreach (Ref r in _items)
+ // {
+ // int val = (int)r.get();
+ // r.set(val + 1);
+ // }
+ // return null;
+ // }
+ //}
+
+
+ //class Commuter : RefTester
+ //{
+ // public Commuter(int id, int niters, List<Ref> items)
+ // : base(id, niters, items)
+ // {
+ // }
+
+ // public override object TxUnit(object[] args)
+ // {
+ // foreach (Ref r in _items)
+ // {
+ // r.commute(Commuter.Incr,null);
+ // }
+ // return null;
+ // }
+
+ // static object Incr(object[] args)
+ // {
+ // int val = (int)args[0];
+ // return val + 1;
+ // }
+ //}
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/MapEntryTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/MapEntryTests.cs new file mode 100644 index 00000000..2a5253fb --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/MapEntryTests.cs @@ -0,0 +1,369 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class MapEntryTests : AssertionHelper
+ {
+ #region C-tor tests
+
+ [Test]
+ public void CtorCreatesEntryWithProperKeyVal()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+
+ Expect(me.key(), EqualTo(1));
+ Expect(me.val(), EqualTo("abc"));
+ }
+
+ #endregion
+
+ #region Object override tests
+
+ [Test]
+ public void HashCodeSameAsPersistentVector()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+ PersistentVector v = PersistentVector.create(1, "abc");
+
+ Expect(me.GetHashCode(), EqualTo(v.GetHashCode()));
+ }
+
+ [Test]
+ public void HashCodeFalseOnDifferentValues()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+ PersistentVector v = PersistentVector.create(1, "abcd");
+
+ Expect(me.GetHashCode(), Not.EqualTo(v.GetHashCode()));
+ }
+
+ [Test]
+ public void EqualsWorksOnPersistentVector()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+ PersistentVector v = PersistentVector.create(1, "abc");
+
+ Expect(me.Equals(v));
+ }
+
+ [Test]
+ public void EqualsWorksFalseOnDifferentValues()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+ PersistentVector v = PersistentVector.create(1, "abcd");
+
+ Expect(me.Equals(v),False);
+ }
+
+
+ #endregion
+
+ #region IMapEntry tests
+
+ #endregion
+
+ #region IPersistentVector tests
+
+ [Test]
+ public void LengthIs2()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+
+ Expect(me.length(), EqualTo(2));
+ }
+
+ [Test]
+ public void NthInRangeWorks()
+ {
+
+ MapEntry me = new MapEntry(1, "abc");
+
+ Expect(me.nth(0), EqualTo(1));
+ Expect(me.nth(1), EqualTo("abc"));
+ }
+
+ [Test]
+ [ExpectedException(typeof(IndexOutOfRangeException))]
+ public void NthOutOfRangeLowFails()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+ object obj = me.nth(-4);
+ }
+
+ [Test]
+ [ExpectedException(typeof(IndexOutOfRangeException))]
+ public void NthOutOfRangeHighFails()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+ object obj = me.nth(4);
+ }
+
+ [Test]
+ public void AssocNInRangeModifies()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+ IPersistentVector v1 = me.assocN(0, 2);
+ IPersistentVector v2 = me.assocN(1, "def");
+ IPersistentVector v3 = me.assocN(2, "ghi");
+
+ Expect(me.count(), EqualTo(2));
+ Expect(me.key(), EqualTo(1));
+ Expect(me.val(), EqualTo("abc"));
+
+ Expect(v1.count(), EqualTo(2));
+ Expect(v1.nth(0), EqualTo(2));
+ Expect(v1.nth(1), EqualTo("abc"));
+
+ Expect(v2.count(), EqualTo(2));
+ Expect(v2.nth(0), EqualTo(1));
+ Expect(v2.nth(1), EqualTo("def"));
+
+ Expect(v3.count(), EqualTo(3));
+ Expect(v3.nth(0), EqualTo(1));
+ Expect(v3.nth(1), EqualTo("abc"));
+ Expect(v3.nth(2), EqualTo("ghi"));
+ }
+
+ [Test]
+ [ExpectedException(typeof(IndexOutOfRangeException))]
+ public void AssocNOutOfRangeLowThrows()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+ IPersistentVector v1 = me.assocN(-4, 2);
+ }
+
+ [Test]
+ [ExpectedException(typeof(IndexOutOfRangeException))]
+ public void AssocNOutOfRangeHighThrows()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+ IPersistentVector v1 = me.assocN(4, 2);
+ }
+
+ [Test]
+ public void ConsWorks()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+ IPersistentVector v1 = me.cons(2);
+
+ Expect(me.count(), EqualTo(2));
+ Expect(me.key(), EqualTo(1));
+ Expect(me.val(), EqualTo("abc"));
+
+
+ Expect(v1.count(), EqualTo(3));
+ Expect(v1.nth(0), EqualTo(1));
+ Expect(v1.nth(1), EqualTo("abc"));
+ Expect(v1.nth(2), EqualTo(2));
+ }
+
+ #endregion
+
+ #region Associative tests
+
+ [Test]
+ public void ContainsKeyOnExistingKeyWorks()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+
+ Expect(me.containsKey(0));
+ Expect(me.containsKey(1));
+ }
+
+ [Test]
+ public void ContainsKeyOutOfRangeIsFalse()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+
+ Expect(me.containsKey(-4),False);
+ Expect(me.containsKey(4), False);
+ }
+
+
+ [Test]
+ public void EntryAtOnExistingKeyWorks()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+ IMapEntry me1 = me.entryAt(0);
+ IMapEntry me2 = me.entryAt(1);
+
+ Expect(me1.key(), EqualTo(0));
+ Expect(me1.val(), EqualTo(1));
+ Expect(me2.key(), EqualTo(1));
+ Expect(me2.val(), EqualTo("abc"));
+ }
+
+ [Test]
+ public void EntryAtOutOfRangeLowReturnsNull()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+ IMapEntry me1 = me.entryAt(-4);
+
+ Expect(me1,Null);
+ }
+
+ [Test]
+ public void EntryAtOutOfRangeHighReturnsNull()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+ IMapEntry me1 = me.entryAt(4);
+
+ Expect(me1, Null);
+ }
+
+ [Test]
+ public void ValAtOnExistingKeyReturnsValue()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+
+ Expect(me.valAt(0), EqualTo(1));
+ Expect(me.valAt(1), EqualTo("abc"));
+ }
+
+ [Test]
+ public void ValAtOnMissingKeyReturnsNull()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+
+ Expect(me.valAt(-4), Null);
+ Expect(me.valAt(4), Null);
+ }
+
+ [Test]
+ public void ValAtWithDefaultOnExistingKeyReturnsValue()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+
+ Expect(me.valAt(0,7), EqualTo(1));
+ Expect(me.valAt(1,7), EqualTo("abc"));
+ }
+
+ [Test]
+ public void ValAtWithDefaultOnMissingKeyReturnsDefault()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+
+ Expect(me.valAt(-4,7), EqualTo(7));
+ Expect(me.valAt(4, 7), EqualTo(7));
+ }
+
+ #endregion
+
+ #region Reversible tests
+
+ [Test]
+ public void RseqReturnReverseSeq()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+
+ ISeq s = me.rseq();
+
+ Expect(s.count(), EqualTo(2));
+ Expect(s.first(), EqualTo("abc"));
+ Expect(s.rest().first(), EqualTo(1));
+ Expect(s.rest().rest(), Null);
+ }
+
+ #endregion
+
+ #region IPersistentCollection tests
+
+ [Test]
+ public void CountIs2()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+ Expect(me.count(), EqualTo(2));
+ }
+
+ [Test]
+ public void SeqReturnsASeq()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+ ISeq s = me.seq();
+
+ Expect(s.count(), EqualTo(2));
+ Expect(s.first(), EqualTo(1));
+ Expect(s.rest().first(), EqualTo("abc"));
+ Expect(s.rest().rest(), Null);
+ }
+
+ [Test]
+ public void EmptyReutrnsNull()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+
+ Expect(me.empty(), Null);
+ }
+
+
+ [Test]
+ public void ExplictIPersistentCollectionConsWorks()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+ IPersistentCollection c = (IPersistentCollection)me;
+ ISeq s = c.cons(2).seq();
+
+ Expect(me.count(), EqualTo(2));
+ Expect(me.key(), EqualTo(1));
+ Expect(me.val(), EqualTo("abc"));
+
+ Expect(s.count(), EqualTo(3));
+ Expect(s.first(), EqualTo(1));
+ Expect(s.rest().first(), EqualTo("abc"));
+ Expect(s.rest().rest().first(), EqualTo(2));
+ Expect(s.rest().rest().rest(), Null);
+ }
+
+ #endregion
+
+ #region IPersistentStack tests
+
+ [Test]
+ public void PeekReturnsVal()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+
+ Expect(me.peek(), EqualTo("abc"));
+ Expect(me.count(), EqualTo(2));
+ Expect(me.key(), EqualTo(1));
+ Expect(me.val(), EqualTo("abc"));
+ }
+
+ [Test]
+ public void PopLosesTheValue()
+ {
+ MapEntry me = new MapEntry(1, "abc");
+ IPersistentVector v = (IPersistentVector)me.pop();
+
+ Expect(v.length(), EqualTo(1));
+ Expect(v.nth(0), EqualTo(1));
+ }
+
+
+
+
+ #endregion
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/NamespaceTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/NamespaceTests.cs new file mode 100644 index 00000000..4e6fcbab --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/NamespaceTests.cs @@ -0,0 +1,257 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+
+
+namespace Clojure.Tests.LibTests
+{
+
+ //TODO: NEed to fix NS tests to clear the created namespaces after each test.
+ /*
+ [TestFixture]
+ public class NamespaceTests : AssertionHelper
+ {
+ #region C-tor tests
+
+ [Test]
+ public void FindOrCreateCreatesANewNamespace()
+ {
+ Symbol sym = Symbol.intern("abc");
+ Namespace ns = Namespace.findOrCreate(sym);
+
+ Expect(ns, Not.Null);
+ Expect(ns.Name, EqualTo(sym));
+ }
+
+ [Test]
+ public void FindOrCreateFindsExistingNamespace()
+ {
+ Symbol sym1 = Symbol.intern("abc");
+ Namespace ns1 = Namespace.findOrCreate(sym1);
+ Symbol sym2 = Symbol.intern("abc");
+ Namespace ns2 = Namespace.findOrCreate(sym2);
+
+ Expect(Object.ReferenceEquals(ns1, ns2));
+ }
+
+ [Test]
+ public void FindGivesNullOnMissingNS()
+ {
+ Symbol sym = Symbol.intern("abc");
+ Namespace ns = Namespace.find(sym);
+
+ Expect(ns, Null);
+ }
+
+ [Test]
+ public void FindFindsExistingNS()
+ {
+ Symbol sym1 = Symbol.intern("abc");
+ Namespace ns1 = Namespace.findOrCreate(sym1);
+ Symbol sym2 = Symbol.intern("abc");
+ Namespace ns2 = Namespace.find(sym2);
+
+ Expect(Object.ReferenceEquals(ns1, ns2));
+ }
+
+ [Test]
+ public void RemoveRemovesExisingNamespace()
+ {
+ Symbol sym1 = Symbol.intern("abc");
+ Namespace ns1 = Namespace.findOrCreate(sym1);
+ Symbol sym2 = Symbol.intern("abc");
+ Namespace ns2 = Namespace.remove(sym2);
+ Namespace ns3 = Namespace.find(sym1);
+
+ Expect(object.ReferenceEquals(ns1, ns2));
+ Expect(ns3, Null);
+ }
+
+
+ [Test]
+ public void RemoveReturnsNullOnNonExisingNamespace()
+ {
+ Symbol sym2 = Symbol.intern("abc");
+ Namespace ns2 = Namespace.remove(sym2);
+
+ Expect(ns2, Null);
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void RemoveFailsRemovingClojureCoreNS()
+ {
+ Symbol sym = Symbol.intern("clojure.core");
+ Namespace ns = Namespace.remove(sym);
+ }
+
+
+ // how to test the thread-safety of findOrCreatea?
+
+ #endregion
+
+ #region object override tests
+
+ [Test]
+ public void ToStringWorks()
+ {
+ // do we care all that much?
+ Namespace ns = Namespace.findOrCreate(Symbol.intern("abc"));
+ Expect(ns.ToString(),EqualTo("#<Namespace: abc>"));
+ }
+
+ #endregion
+
+ #region Interning symbols tests
+
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void InterningSymbolWithNamespaceFails()
+ {
+ Symbol sym = Symbol.intern("abc", "def");
+ Namespace ns = Namespace.findOrCreate(Symbol.intern("ghi"));
+ Var v = ns.intern(sym);
+ }
+
+ [Test]
+ public void InterningSymbolCreatesVar()
+ {
+ Symbol sym = Symbol.intern("def");
+ Namespace ns = Namespace.findOrCreate(Symbol.intern("abc"));
+
+ Var v = ns.intern(sym);
+
+ Expect(v, Not.Null);
+ Expect(v.Namespace, EqualTo(ns));
+
+ }
+
+ [Test]
+ public void InterningSymbolEntersVarInMap()
+ {
+ Symbol sym = Symbol.intern("def");
+ Namespace ns = Namespace.findOrCreate(Symbol.intern("abc"));
+
+ Var v = ns.intern(sym);
+
+ Expect(ns.findInternedVar(sym), SameAs(v));
+ }
+
+
+
+ [Test]
+ public void InterningSymbolAgainFindsVar()
+ {
+ Symbol sym = Symbol.intern("def");
+ Namespace ns = Namespace.findOrCreate(Symbol.intern("abc"));
+
+ Var v1 = ns.intern(sym);
+
+ Var v2 = ns.intern(sym);
+
+ Expect(Object.ReferenceEquals(v1, v2));
+ }
+
+ [Test]
+ [ExpectedException(typeof(InvalidOperationException))]
+ public void ReferSymbolToVarInOtherAndThenInterningThrows()
+ {
+ // I don't know why
+
+ Namespace ns1 = Namespace.findOrCreate(Symbol.intern("abc"));
+
+ Symbol sym1 = Symbol.intern("def");
+ Var v1 = ns1.intern(sym1);
+
+ Namespace ns2 = Namespace.findOrCreate(Symbol.intern("d"));
+ Symbol sym2 = Symbol.intern("g");
+
+ ns2.refer(sym2, v1);
+ ns2.intern(sym2);
+ }
+
+
+
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void ReferOfSymbolWithNamespaceFails()
+ {
+ Symbol sym = Symbol.intern("abc", "def");
+ Namespace ns = Namespace.findOrCreate(Symbol.intern("ghi"));
+ ns.refer(sym, Var.create());
+ }
+
+ [Test]
+ public void ReferEntersVar()
+ {
+ Namespace ns = Namespace.findOrCreate(Symbol.intern("abc"));
+
+ Symbol sym = Symbol.intern("def");
+ Var v = Var.create();
+ ns.refer(sym, v);
+
+ Expect(ns.getMapping(sym), SameAs(v));
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void ImportTypeOnSymbolWithNamespaceFails()
+ {
+ Symbol sym = Symbol.intern("abc", "def");
+ Namespace ns = Namespace.findOrCreate(Symbol.intern("ghi"));
+ ns.importType(sym, typeof(Int32));
+ }
+
+ [Test]
+ public void ImportTypeEntersType()
+ {
+ Namespace ns = Namespace.findOrCreate(Symbol.intern("abc"));
+
+ Symbol sym = Symbol.intern("def");
+ ns.importType(sym, typeof(Int32));
+
+ Expect(ns.getMapping(sym), SameAs(typeof(Int32)));
+ }
+
+
+ [Test]
+ public void FindInternedVarFailsIfNonVarValueInMap()
+ {
+ Namespace ns = Namespace.findOrCreate(Symbol.intern("abc"));
+
+ Symbol sym = Symbol.intern("def");
+ ns.importType(sym, typeof(Int32));
+
+ Var v = ns.findInternedVar(sym);
+
+ Expect(v, Null);
+ }
+
+
+ // Don't know how to test the race condition in the loops for
+ // intern(Symbol), reference(Symbol), unmap(Symbol)
+
+ #endregion
+ }
+ */
+
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/NumbersTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/NumbersTests.cs new file mode 100644 index 00000000..e5c869fc --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/NumbersTests.cs @@ -0,0 +1,137 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+using java.math;
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class NumbersTests : AssertionHelper
+ {
+ #region Helpers
+
+ private void ExpectInt32(object x)
+ {
+ Expect(x, TypeOf(typeof(Int32)));
+ }
+
+ private void ExpectSameObject(object x, object y)
+ {
+ Expect(x, SameAs(y));
+ }
+
+ private void ExpectEqualObject(object x, object y)
+ {
+ Expect(x, EqualTo(y));
+ }
+
+ #endregion
+
+ #region reduce tests
+
+ [Test]
+ public void ReduceOnBigIntReducesSmallerValues()
+ {
+ BigInteger b1 = new BigInteger("123");
+ BigInteger b2 = new BigInteger("0");
+ BigInteger b3 = new BigInteger(Int32.MaxValue.ToString());
+ BigInteger b4 = new BigInteger(Int32.MinValue.ToString());
+
+ ExpectInt32(Numbers.reduce(b1));
+ ExpectInt32(Numbers.reduce(b2));
+ ExpectInt32(Numbers.reduce(b3));
+ ExpectInt32(Numbers.reduce(b4));
+ }
+
+ [Test]
+ public void ReduceOnBigIntReturnsLargerValues()
+ {
+ BigInteger b1 = new BigInteger("100000000000000000000", 16);
+ BigInteger b2 = b1.negate();
+ BigInteger b3 = new BigInteger("123456789012345678901234567890");
+ BigInteger b4 = b3.negate();
+
+ ExpectSameObject(b1, Numbers.reduce(b1));
+ ExpectSameObject(b2, Numbers.reduce(b2));
+ ExpectSameObject(b3, Numbers.reduce(b3));
+ ExpectSameObject(b4, Numbers.reduce(b4));
+ }
+
+ [Test]
+ public void ReduceOnLongReducesSmallerValues()
+ {
+ long b1 = 123;
+ long b2 = 0;
+ long b3 = Int32.MaxValue;
+ long b4 = Int32.MinValue;
+
+ ExpectInt32(Numbers.reduce(b1));
+ ExpectInt32(Numbers.reduce(b2));
+ ExpectInt32(Numbers.reduce(b3));
+ ExpectInt32(Numbers.reduce(b4));
+ }
+
+
+ [Test]
+ public void ReduceOnLongReturnsLargerValues()
+ {
+ long b1 = ((long)Int32.MaxValue) + 1;
+ long b2 = ((long)Int32.MinValue) - 1;
+ long b3 = 123456789000;
+ long b4 = -b3;
+
+ ExpectEqualObject(b1, Numbers.reduce(b1));
+ ExpectEqualObject(b2, Numbers.reduce(b2));
+ ExpectEqualObject(b3, Numbers.reduce(b3));
+ ExpectEqualObject(b4, Numbers.reduce(b4));
+ }
+
+ #endregion
+
+ #region divide tests
+
+ [Test]
+ [ExpectedException(typeof(ArithmeticException))]
+ public void DivideByZeroFails()
+ {
+ object o = Numbers.BIDivide(Numbers.BigIntegerOne, Numbers.BigIntegerZero);
+ }
+
+ [Test]
+ public void DivideReducesToIntOnDenomOne()
+ {
+ object o = Numbers.BIDivide(new BigInteger("75"), new BigInteger("25"));
+ Expect(o, EqualTo(3));
+ }
+
+ [Test]
+ public void DivideReturnsReducedRatio()
+ {
+ object o = Numbers.BIDivide(new BigInteger("42"), new BigInteger("30"));
+
+ Expect(o, TypeOf(typeof(Ratio)));
+
+ Ratio r = o as Ratio;
+ Expect(r.Numerator, EqualTo(new BigInteger("7")));
+ Expect(r.Denominator, EqualTo(new BigInteger("5")));
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/ObjTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/ObjTests.cs new file mode 100644 index 00000000..bf7423ba --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/ObjTests.cs @@ -0,0 +1,68 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class ObjTests : IObjTests
+ {
+
+ class MockObj : Obj
+ {
+ public MockObj()
+ {
+ }
+
+ public MockObj(IPersistentMap meta)
+ : base(meta)
+ {
+ }
+
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return meta == _meta
+ ? this
+ : new MockObj(meta);
+ }
+ }
+
+ MockRepository _mocks;
+
+ [SetUp]
+ public void Setup()
+ {
+ _mocks = new MockRepository();
+ IPersistentMap meta = _mocks.StrictMock<IPersistentMap>();
+ _mocks.ReplayAll();
+
+ _objWithNullMeta = new MockObj();
+ _obj = new MockObj(meta);
+ _expectedType = typeof(MockObj);
+ }
+
+ [TearDown]
+ public void Teardown()
+ {
+ _mocks.VerifyAll();
+ }
+ }
+}
+
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentArrayMapTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentArrayMapTests.cs new file mode 100644 index 00000000..87692146 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentArrayMapTests.cs @@ -0,0 +1,917 @@ +/**
+ * 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.Collections;
+
+using NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class PersistentArrayMapTests : AssertionHelper
+ {
+ #region C-tor tests
+
+ [Test]
+ public void CreateOnEmptyDictionaryReturnsEmptyMap()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ Expect(m.count(), EqualTo(0));
+ }
+
+ [Test]
+ public void CreateOnDictionaryReturnsMap()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.valAt(1), EqualTo("a"));
+ Expect(m.valAt(2), EqualTo("b"));
+ Expect(m.containsKey(3), False);
+ }
+
+ // other c-tors are not public.
+
+
+ #endregion
+
+ #region Associative tests
+
+ [Test]
+ public void ContainsKeyOnMissingKeyIsFalse()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ Expect(m.containsKey(3), False);
+ }
+
+
+ [Test]
+ public void ContainsKeyOnExistingKeyIsTrue()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ Expect(m.containsKey(1));
+ Expect(m.containsKey(2));
+ }
+
+ [Test]
+ public void ContainsKeyNotConfusedByValue()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ Expect(m.containsKey("a"), False);
+ }
+
+ [Test]
+ public void EntryAtReturnsNullOnMissingKey()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ Expect(m.entryAt(3), Null);
+ }
+
+ [Test]
+ public void EntryAtReturnsEntryforKey()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+ IMapEntry me = m.entryAt(1);
+
+ Expect(me.key(), EqualTo(1));
+ Expect(me.val(), EqualTo("a"));
+ }
+
+ [Test]
+ public void ValAt1ReturnsNullOnMissingKey()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ Expect(m.valAt(3), Null);
+ }
+
+ [Test]
+ public void ValAt1ReturnsValueforKey()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ Expect(m.valAt(1), EqualTo("a"));
+ }
+
+
+ [Test]
+ public void ValAt2ReturnsDefaultOnMissingKey()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ Expect(m.valAt(3,99), EqualTo(99));
+ }
+
+ [Test]
+ public void ValAt2ReturnsValueforKey()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ Expect(m.valAt(1,99), EqualTo("a"));
+ }
+
+ #endregion
+
+ #region IPersistentCollection tests
+
+ [Test]
+ public void CountOnEmptyReturns0()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ Expect(m.count(), EqualTo(0));
+ }
+
+ [Test]
+ public void CountOnNonEmptyReturnsCount()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ Expect(m.count(), EqualTo(2));
+ }
+
+ [Test]
+ public void EmptyReturnsEmpty()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+ IPersistentMap m = PersistentArrayMap.create(d);
+ IPersistentCollection c = m.empty();
+
+ Expect(c.count(), EqualTo(0));
+ Expect(c.seq(), Null);
+ }
+
+
+ [Test]
+ public void SeqOnEmptyReturnNull()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ IPersistentMap m = PersistentArrayMap.create(d);
+ ISeq s = m.seq();
+
+ Expect(s, Null);
+ }
+
+ [Test]
+ public void SeqOnNonEmptyIterates()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+ IPersistentMap m = PersistentArrayMap.create(d);
+ ISeq s = m.seq();
+ IMapEntry me1 = (IMapEntry)s.first();
+ IMapEntry me2 = (IMapEntry)s.rest().first();
+ ISeq end = s.rest().rest();
+
+ Expect(s.count(), EqualTo(2));
+ Expect(me1.key(), EqualTo(1) | EqualTo(2));
+ Expect(me1.val(), EqualTo(((int)me1.key()==1 ? "a" : "b")));
+ Expect(me2.key(), EqualTo(1) | EqualTo(2));
+ Expect(me2.val(), EqualTo(((int)me2.key() == 1 ? "a" : "b")));
+ Expect(end, Null);
+ }
+
+ #endregion
+
+ #region IPersistentMap tests
+
+ [Test]
+ public void AssocModifiesOnExistingKey()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m1 = PersistentArrayMap.create(d);
+ IPersistentMap m2 = m1.assoc(2, "c");
+
+ Expect(m1.count(), EqualTo(2));
+ Expect(m1.valAt(2), EqualTo("b"));
+ Expect(m2.count(), EqualTo(2));
+ Expect(m2.valAt(2), EqualTo("c"));
+ }
+
+
+ [Test]
+ public void AssocAddsOnNewKey()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m1 = PersistentArrayMap.create(d);
+ IPersistentMap m2 = m1.assoc(3, "c");
+
+ Expect(m1.count(), EqualTo(2));
+ Expect(m1.containsKey(3), False);
+ Expect(m2.count(), EqualTo(3));
+ Expect(m2.valAt(3), EqualTo("c"));
+ }
+
+
+ [Test]
+ [ExpectedException(typeof(Exception))]
+ public void AssocExFailsOnExistingKey()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m1 = PersistentArrayMap.create(d);
+ IPersistentMap m2 = m1.assocEx(2, "c");
+ }
+
+
+
+ [Test]
+ public void AssocExModifiesOnNewKey()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m1 = PersistentArrayMap.create(d);
+ IPersistentMap m2 = m1.assocEx(3, "c");
+
+ Expect(m1.count(), EqualTo(2));
+ Expect(m1.containsKey(3), False);
+ Expect(m2.count(), EqualTo(3));
+ Expect(m2.valAt(3), EqualTo("c"));
+ }
+
+ [Test]
+ public void WithoutOnExistingKeyRemovesKey()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[3] = "a";
+ d[5] = "b";
+ d[7] = "c";
+
+ IPersistentMap m1 = PersistentArrayMap.create(d);
+ IPersistentMap m2 = m1.without(5);
+
+ Expect(m1.count(), EqualTo(3));
+ Expect(m1.valAt(5), EqualTo("b"));
+ Expect(m2.count(), EqualTo(2));
+ Expect(m2.containsKey(5), False);
+ }
+
+ [Test]
+ public void WithoutOnMissingKeyIsIdentity()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[3] = "a";
+ d[5] = "b";
+ d[7] = "c";
+
+ IPersistentMap m1 = PersistentArrayMap.create(d);
+ IPersistentMap m2 = m1.without(4);
+
+ Expect(m2, SameAs(m1));
+ }
+
+ #endregion
+
+ #region APersistentMap tests
+
+ [Test]
+ public void EqualsOnSimilarDictionaryReturnsTrue()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ Expect(m.equiv(d));
+ }
+
+ [Test]
+ public void EqualsOnDictionaryWIthDifferntValueReturnsFalse()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ d[2] = "c";
+
+ Expect(m.Equals(d),False);
+ }
+
+
+ [Test]
+ public void EqualsOnDictionaryWithExtraValueReturnsFalse()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ d[3] = "c";
+
+ Expect(m.Equals(d), False);
+ }
+
+ [Test]
+ public void HashCodeBasedOnValue()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m1 = PersistentArrayMap.create(d);
+
+ d[3] = "c";
+ IPersistentMap m2 = PersistentArrayMap.create(d);
+
+ Expect(m1.GetHashCode(), Not.EqualTo(m2.GetHashCode()));
+ }
+
+ [Test]
+ public void AssociativeDotAssocWorks()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+ Associative a = (Associative)m;
+
+ Associative a1 = a.assoc(3, "c");
+ Associative a2 = a.assoc(2, "c");
+
+ Expect(a.count(), EqualTo(2));
+ Expect(a.valAt(1), EqualTo("a"));
+ Expect(a.valAt(2), EqualTo("b"));
+ Expect(a.containsKey(3), False);
+
+ Expect(a1.count(), EqualTo(3));
+ Expect(a1.valAt(1), EqualTo("a"));
+ Expect(a1.valAt(2), EqualTo("b"));
+ Expect(a1.valAt(3), EqualTo("c"));
+
+ Expect(a2.count(), EqualTo(2));
+ Expect(a2.valAt(1), EqualTo("a"));
+ Expect(a2.valAt(2), EqualTo("c"));
+ Expect(a2.containsKey(3), False);
+ }
+
+ [Test]
+ public void ConsOnIMapEntryAddsNew()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+ IPersistentMap c = m.cons(new MapEntry(3, "c"));
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.valAt(1), EqualTo("a"));
+ Expect(m.valAt(2), EqualTo("b"));
+
+ Expect(c.count(), EqualTo(3));
+ Expect(c.valAt(1), EqualTo("a"));
+ Expect(c.valAt(2), EqualTo("b"));
+ Expect(c.valAt(3), EqualTo("c"));
+ }
+
+ [Test]
+ public void ConsOnIMapEntryReplacesExisting()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+ IPersistentMap c = m.cons(new MapEntry(2, "c"));
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.valAt(1), EqualTo("a"));
+ Expect(m.valAt(2), EqualTo("b"));
+
+ Expect(c.count(), EqualTo(2));
+ Expect(c.valAt(1), EqualTo("a"));
+ Expect(c.valAt(2), EqualTo("c"));
+ }
+
+
+ [Test]
+ public void ConsOnDictionaryEntryAddsNew()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+ IPersistentMap c = m.cons(new DictionaryEntry(3, "c"));
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.valAt(1), EqualTo("a"));
+ Expect(m.valAt(2), EqualTo("b"));
+
+ Expect(c.count(), EqualTo(3));
+ Expect(c.valAt(1), EqualTo("a"));
+ Expect(c.valAt(2), EqualTo("b"));
+ Expect(c.valAt(3), EqualTo("c"));
+ }
+
+ [Test]
+ public void ConsOnDictionaryEntryReplacesExisting()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+ IPersistentMap c = m.cons(new DictionaryEntry(2, "c"));
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.valAt(1), EqualTo("a"));
+ Expect(m.valAt(2), EqualTo("b"));
+
+ Expect(c.count(), EqualTo(2));
+ Expect(c.valAt(1), EqualTo("a"));
+ Expect(c.valAt(2), EqualTo("c"));
+ }
+
+ [Test]
+ public void ConsOnKeyValuePairAddsNew()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+ IPersistentMap c = m.cons(new KeyValuePair<int,string>(3, "c"));
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.valAt(1), EqualTo("a"));
+ Expect(m.valAt(2), EqualTo("b"));
+
+ Expect(c.count(), EqualTo(3));
+ Expect(c.valAt(1), EqualTo("a"));
+ Expect(c.valAt(2), EqualTo("b"));
+ Expect(c.valAt(3), EqualTo("c"));
+ }
+
+ [Test]
+ public void ConsOnKeyValuePairReplacesExisting()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+ IPersistentMap c = m.cons(new KeyValuePair<int,string>(2, "c"));
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.valAt(1), EqualTo("a"));
+ Expect(m.valAt(2), EqualTo("b"));
+
+ Expect(c.count(), EqualTo(2));
+ Expect(c.valAt(1), EqualTo("a"));
+ Expect(c.valAt(2), EqualTo("c"));
+ }
+
+ [Test]
+ public void ConsOnIPVAddsNew()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ IPersistentVector v = PersistentVector.create(3, "c");
+ IPersistentMap c = m.cons(v);
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.valAt(1), EqualTo("a"));
+ Expect(m.valAt(2), EqualTo("b"));
+
+ Expect(c.count(), EqualTo(3));
+ Expect(c.valAt(1), EqualTo("a"));
+ Expect(c.valAt(2), EqualTo("b"));
+ Expect(c.valAt(3), EqualTo("c"));
+ }
+
+ [Test]
+ public void ConsOnIPVReplacesExisting()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ IPersistentVector v = PersistentVector.create(2, "c");
+ IPersistentMap c = m.cons(v);
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.valAt(1), EqualTo("a"));
+ Expect(m.valAt(2), EqualTo("b"));
+
+ Expect(c.count(), EqualTo(2));
+ Expect(c.valAt(1), EqualTo("a"));
+ Expect(c.valAt(2), EqualTo("c"));
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void ConsOnNon2IPVFails()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentArrayMap.create(d);
+
+ IPersistentVector v = PersistentVector.create(2, "c", 3, "d");
+ IPersistentMap c = m.cons(v);
+
+ }
+
+ [Test]
+ public void ConsOnIPersistentMapAddsOrReplacesMany()
+ {
+ Dictionary<int, string> d1 = new Dictionary<int, string>();
+ d1[1] = "a";
+ d1[2] = "b";
+
+ IPersistentMap m1 = PersistentArrayMap.create(d1);
+
+
+ Dictionary<int, string> d2 = new Dictionary<int, string>();
+ d2[2] = "c";
+ d2[3] = "d";
+
+ IPersistentMap m2 = PersistentArrayMap.create(d2);
+ IPersistentMap m3 = m1.cons(m2);
+
+
+ Expect(m1.count(), EqualTo(2));
+ Expect(m1.valAt(1), EqualTo("a"));
+ Expect(m1.valAt(2), EqualTo("b"));
+
+ Expect(m2.count(), EqualTo(2));
+ Expect(m2.valAt(2), EqualTo("c"));
+ Expect(m2.valAt(3), EqualTo("d"));
+
+ Expect(m3.count(), EqualTo(3));
+ Expect(m3.valAt(1), EqualTo("a"));
+ Expect(m3.valAt(2), EqualTo("c"));
+ Expect(m3.valAt(3), EqualTo("d"));
+ }
+
+ [Test]
+ public void InvokeOn1ArgDoesValAt1()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IFn f = (IFn)PersistentArrayMap.create(d);
+
+ Expect(f.invoke(1), EqualTo("a"));
+ Expect(f.invoke(7),Null);
+
+ }
+ [Test]
+ public void InvokeOn2ArgsDoesValAt2()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IFn f = (IFn)PersistentArrayMap.create(d);
+
+ Expect(f.invoke(1,99), EqualTo("a"));
+ Expect(f.invoke(7,99), EqualTo(99));
+ }
+
+ [Test]
+ [ExpectedException(typeof(NotImplementedException))]
+ public void IDictionary_Add_fails()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IDictionary id = (IDictionary)PersistentArrayMap.create(d);
+ id.Add(1, "c");
+ }
+
+ [Test]
+ [ExpectedException(typeof(NotImplementedException))]
+ public void IDictionary_Clear_fails()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IDictionary id = (IDictionary)PersistentArrayMap.create(d);
+ id.Clear();
+ }
+
+ [Test]
+ [ExpectedException(typeof(NotImplementedException))]
+ public void IDictionary_Remove_fails()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IDictionary id = (IDictionary)PersistentArrayMap.create(d);
+ id.Remove(1);
+ }
+
+ [Test]
+ public void IDictionary_Contains_finds_existing_key()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IDictionary id = (IDictionary)PersistentArrayMap.create(d);
+ Expect(id.Contains(1));
+ }
+
+
+
+ [Test]
+ public void IDictionary_Contains_does_not_find_existing_key()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IDictionary id = (IDictionary)PersistentArrayMap.create(d);
+
+ Expect(id.Contains(3),False);
+ }
+
+ [Test]
+ public void IDictionary_IsFixedSize_is_true()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IDictionary id = (IDictionary)PersistentArrayMap.create(d);
+
+ Expect(id.IsFixedSize);
+ }
+
+ [Test]
+ public void IDictionary_IsReadOnly_is_true()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IDictionary id = (IDictionary)PersistentArrayMap.create(d);
+
+ Expect(id.IsReadOnly);
+ }
+
+ [Test]
+ public void IDictionary_index_acts_like_valAt()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IDictionary id = (IDictionary)PersistentArrayMap.create(d);
+ Expect(id[2], EqualTo("b"));
+ Expect(id[3], Null);
+ }
+
+ [Test]
+ public void IDictionary_Keys_creates_key_collection()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IDictionary id = (IDictionary)PersistentArrayMap.create(d);
+ ICollection keys = id.Keys;
+
+ Expect(keys.Count, EqualTo(2));
+ int[] akeys = new int[2];
+ keys.CopyTo(akeys, 0);
+ Array.Sort(akeys);
+ Expect(akeys[0], EqualTo(1));
+ Expect(akeys[1], EqualTo(2));
+ }
+
+ [Test]
+ public void IDictionary_Values_creates_value_collection()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IDictionary id = (IDictionary)PersistentArrayMap.create(d);
+ ICollection vals = id.Values;
+
+ Expect(vals.Count, EqualTo(2));
+ string[] avals = new string[2];
+ vals.CopyTo(avals, 0);
+ Array.Sort(avals);
+ Expect(avals[0], EqualTo("a"));
+ Expect(avals[1], EqualTo("b"));
+ }
+
+
+
+
+ [Test]
+ public void IDictionary_GetEnumerator_returns_an_enumerator()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IDictionary id = (IDictionary)PersistentArrayMap.create(d);
+ IDictionaryEnumerator e = id.GetEnumerator();
+
+ Expect(e.MoveNext());
+ DictionaryEntry de1 = (DictionaryEntry)e.Current;
+ Expect(e.MoveNext());
+ DictionaryEntry de2 = (DictionaryEntry)e.Current;
+ Expect(e.MoveNext(), False);
+
+ Expect(de1.Key, EqualTo(1) | EqualTo(2));
+ Expect(de2.Key, EqualTo(1) | EqualTo(2));
+ Expect(de1.Value, EqualTo(((int)de1.Key) == 1 ? "a" : "b"));
+ Expect(de2.Value, EqualTo(((int)de2.Key) == 1 ? "a" : "b"));
+ }
+
+ [Test]
+ public void ICollection_CopyTo_Copies()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ ICollection c = (ICollection)PersistentArrayMap.create(d);
+ IMapEntry[] a = new IMapEntry[c.Count];
+ c.CopyTo(a, 0);
+
+ int key0 = (int)a[0].key();
+ int key1 = (int)a[1].key();
+ string val0 = (string)a[0].val();
+ string val1 = (string)a[1].val();
+
+ Expect(key0, EqualTo(1) | EqualTo(2));
+ Expect(key1, EqualTo(key0 == 1 ? 2 : 1));
+ Expect(val0, EqualTo(key0 == 1 ? "a" : "b"));
+ Expect(val1, EqualTo(key1 == 1 ? "a" : "b"));
+ }
+
+
+ [Test]
+ public void ICollection_Count_returns_count_of_items()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ ICollection c = (ICollection)PersistentArrayMap.create(d);
+
+ Expect(c.Count, EqualTo(2));
+ }
+
+ [Test]
+ public void ICollection_IsSynchronized_is_true()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ ICollection c = (ICollection)PersistentArrayMap.create(d);
+
+ Expect(c.IsSynchronized);
+ }
+
+
+ [Test]
+ [ExpectedException(typeof(NotImplementedException))]
+ public void ICollection_SyncRoot_fails()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ ICollection c = (ICollection)PersistentArrayMap.create(d);
+
+ object s = c.SyncRoot;
+ }
+
+
+ #endregion
+ }
+
+ [TestFixture]
+ public class PersistentArrayMap_IObj_Tests : IObjTests
+ {
+ MockRepository _mocks;
+
+ [SetUp]
+ public void Setup()
+ {
+ _mocks = new MockRepository();
+ IPersistentMap meta = _mocks.StrictMock<IPersistentMap>();
+ _mocks.ReplayAll();
+
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "abc";
+
+ _objWithNullMeta = (IObj)PersistentArrayMap.create(d);
+ _obj = _objWithNullMeta.withMeta(meta);
+ _expectedType = typeof(PersistentArrayMap);
+ }
+
+ [TearDown]
+ public void Teardown()
+ {
+ _mocks.VerifyAll();
+ }
+
+ }
+
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentHashMapTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentHashMapTests.cs new file mode 100644 index 00000000..200494b5 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentHashMapTests.cs @@ -0,0 +1,242 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+using System.Collections;
+
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class PersistentHashMapTests : AssertionHelper
+ {
+
+ #region C-tor tests
+
+ [Test]
+ public void CreateOnEmptyDictionaryReturnsEmptyMap()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ IPersistentMap m = PersistentHashMap.create(d);
+
+ Expect(m.count(), EqualTo(0));
+ }
+
+ [Test]
+ public void CreateOnDictionaryReturnsMap()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentHashMap.create(d);
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.valAt(1), EqualTo("a"));
+ Expect(m.valAt(2), EqualTo("b"));
+ Expect(m.containsKey(3), False);
+ }
+
+ [Test]
+ public void CreateOnEmptyListReturnsEmptyMap()
+ {
+ ArrayList a = new ArrayList();
+ IPersistentMap m = PersistentHashMap.create(a);
+
+ Expect(m.count(), EqualTo(0));
+ }
+
+ [Test]
+ public void CreateOnListReturnsMap()
+ {
+ object[] items = new object[] { 1, "a", 2, "b" };
+ ArrayList a = new ArrayList(items);
+
+ IPersistentMap m = PersistentHashMap.create(a);
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.valAt(1), EqualTo("a"));
+ Expect(m.valAt(2), EqualTo("b"));
+ Expect(m.containsKey(3), False);
+ }
+
+ [Test]
+ public void CreateOnEmptyISeqReturnsEmptyMap()
+ {
+ object[] items = new object[] {};
+ ArrayList a = new ArrayList(items);
+ ISeq s = PersistentList.create(a).seq();
+ IPersistentMap m = PersistentHashMap.create(s);
+
+ Expect(m.count(), EqualTo(0));
+ }
+
+ [Test]
+ public void CreateOnISeqReturnsMap()
+ {
+ object[] items = new object[] { 1, "a", 2, "b" };
+ ArrayList a = new ArrayList(items);
+ ISeq s = PersistentList.create(a).seq();
+ IPersistentMap m = PersistentHashMap.create(s);
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.valAt(1), EqualTo("a"));
+ Expect(m.valAt(2), EqualTo("b"));
+ Expect(m.containsKey(3), False);
+ }
+
+ [Test]
+ public void CreateOnNoArgsReturnsEmptyMap()
+ {
+ PersistentHashMap m = PersistentHashMap.create();
+
+ Expect(m.count(), EqualTo(0));
+ Expect(m.meta(), Null);
+ }
+
+ [Test]
+ public void CreateOnNoArgsReturnsMap()
+ {
+ PersistentHashMap m = PersistentHashMap.create(1, "a", 2, "b");
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.valAt(1), EqualTo("a"));
+ Expect(m.valAt(2), EqualTo("b"));
+ Expect(m.containsKey(3), False);
+ Expect(m.meta(), Null);
+ }
+
+
+ [Test]
+ public void CreateOnMetaNoArgsReturnsEmptyMap()
+ {
+ MockRepository mocks = new MockRepository();
+ IPersistentMap meta = mocks.StrictMock<IPersistentMap>();
+ mocks.ReplayAll();
+
+ PersistentHashMap m = PersistentHashMap.create(meta);
+
+ Expect(m.count(), EqualTo(0));
+ Expect(m.meta(), SameAs(meta));
+ mocks.VerifyAll();
+ }
+
+ [Test]
+ public void CreateOnMetaNoArgsReturnsMap()
+ {
+ MockRepository mocks = new MockRepository();
+ IPersistentMap meta = mocks.StrictMock<IPersistentMap>();
+ mocks.ReplayAll();
+
+ PersistentHashMap m = PersistentHashMap.create(meta,1, "a", 2, "b");
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.valAt(1), EqualTo("a"));
+ Expect(m.valAt(2), EqualTo("b"));
+ Expect(m.containsKey(3), False);
+ Expect(m.meta(), SameAs(meta));
+ mocks.VerifyAll();
+ }
+
+ #endregion
+
+ #region Associative tests
+
+ #endregion
+
+ #region IPersistentMap tests
+
+ #endregion
+
+ #region IPersistentCollection tests
+
+ #endregion
+
+ #region Big tests
+
+ [Test]
+ public void DoSomeBigTests()
+ {
+ DoBigTest(100);
+ DoBigTest(1000);
+ DoBigTest(10000);
+ DoBigTest(100000);
+ }
+
+ public void DoBigTest(int numEntries)
+ {
+ System.Console.WriteLine("Testing {0} items.", numEntries);
+
+ Random rnd = new Random();
+ Dictionary<int, int> dict = new Dictionary<int, int>(numEntries);
+ for (int i = 0; i < numEntries; i++)
+ {
+ int r = rnd.Next();
+ dict[r] = r;
+ }
+ PersistentHashMap m = (PersistentHashMap) PersistentHashMap.create(dict);
+
+ Expect(m.count(),EqualTo(dict.Count));
+
+ foreach ( int key in dict.Keys )
+ {
+ Expect(m.containsKey(key));
+ Expect(m.valAt(key),EqualTo(key));
+ }
+
+ for ( ISeq s = m.seq(); s != null; s = s.rest() )
+ Expect(dict.ContainsKey((int)((IMapEntry)s.first()).key()));
+
+ }
+
+ #endregion
+
+ }
+
+ [TestFixture]
+ public class PersistentHashMap_IObj_Tests : IObjTests
+ {
+ MockRepository _mocks;
+
+ [SetUp]
+ public void Setup()
+ {
+ _mocks = new MockRepository();
+ IPersistentMap meta = _mocks.StrictMock<IPersistentMap>();
+ _mocks.ReplayAll();
+
+ PersistentHashMap m = PersistentHashMap.create(1, "a", 2, "b");
+
+
+ _objWithNullMeta = (IObj)m;
+ _obj = _objWithNullMeta.withMeta(meta);
+ _expectedType = typeof(PersistentHashMap);
+ }
+
+ [TearDown]
+ public void Teardown()
+ {
+ _mocks.VerifyAll();
+ }
+
+ }
+
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentHashSetTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentHashSetTests.cs new file mode 100644 index 00000000..9e71e14d --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentHashSetTests.cs @@ -0,0 +1,188 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+using System.Collections;
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class PersistentHashSetTests : AssertionHelper
+ {
+
+ #region C-tor tests
+
+ [Test]
+ public void CreateOnEmptyListReturnsEmptySet()
+ {
+ ArrayList a = new ArrayList();
+ IPersistentSet m = PersistentHashSet.create1(a);
+
+ Expect(m.count(), EqualTo(0));
+ }
+
+ [Test]
+ public void CreateOnListReturnsSet()
+ {
+ object[] items = new object[] { 1, "a" };
+ ArrayList a = new ArrayList(items);
+
+ IPersistentSet m = PersistentHashSet.create1(a);
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.contains(1));
+ Expect(m.contains("a"));
+ Expect(m.contains(3), False);
+ }
+
+ [Test]
+ public void CreateOnEmptyISeqReturnsEmptySet()
+ {
+ object[] items = new object[] { };
+ ArrayList a = new ArrayList(items);
+ ISeq s = PersistentList.create(a).seq();
+ IPersistentSet m = PersistentHashSet.create(s);
+
+ Expect(m.count(), EqualTo(0));
+ }
+
+ [Test]
+ public void CreateOnISeqReturnsSet()
+ {
+ object[] items = new object[] { 1, "a" };
+ ArrayList a = new ArrayList(items);
+ ISeq s = PersistentList.create(a).seq();
+ IPersistentSet m = PersistentHashSet.create(s);
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.contains(1));
+ Expect(m.contains("a"));
+ Expect(m.contains(3), False);
+ }
+
+ [Test]
+ public void CreateOnNoArgsReturnsEmptySet()
+ {
+ PersistentHashSet m = PersistentHashSet.create();
+
+ Expect(m.count(), EqualTo(0));
+ Expect(m.meta(), Null);
+ }
+
+ [Test]
+ public void CreateOnNoArgsReturnsSet()
+ {
+ PersistentHashSet m = PersistentHashSet.create(1, "a");
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.contains(1));
+ Expect(m.contains("a"));
+ Expect(m.contains(3), False);
+ Expect(m.meta(), Null);
+ }
+
+
+
+ #endregion
+
+ #region Associative tests
+
+ #endregion
+
+ #region IPersistentMap tests
+
+ #endregion
+
+ #region IPersistentCollection tests
+
+ #endregion
+
+ #region Big tests
+
+ [Test]
+ public void DoSomeBigTests()
+ {
+ DoBigTest(100);
+ DoBigTest(1000);
+ DoBigTest(10000);
+ DoBigTest(100000);
+ }
+
+ public void DoBigTest(int numEntries)
+ {
+ System.Console.WriteLine("Testing {0} items.", numEntries);
+
+ Random rnd = new Random();
+ Dictionary<object, object> dict = new Dictionary<object, object>(numEntries);
+ for (int i = 0; i < numEntries; i++)
+ {
+ int r = rnd.Next();
+ dict[r] = r;
+ }
+
+ object[] items = dict.Keys.ToArray();
+
+ PersistentHashSet m = (PersistentHashSet)PersistentHashSet.create(items);
+
+ Expect(m.count(), EqualTo(dict.Count));
+
+ foreach (int key in dict.Keys)
+ {
+ Expect(m.contains(key));
+ }
+
+ for (ISeq s = m.seq(); s != null; s = s.rest())
+ Expect(dict.ContainsKey((int)s.first()));
+
+ }
+
+ #endregion
+ }
+
+ [TestFixture]
+ public class PersistentHashSet_IObj_Tests : IObjTests
+ {
+ MockRepository _mocks;
+
+ [SetUp]
+ public void Setup()
+ {
+ _mocks = new MockRepository();
+ IPersistentMap meta = _mocks.StrictMock<IPersistentMap>();
+ _mocks.ReplayAll();
+
+ PersistentHashSet m = PersistentHashSet.create(1, "a", 2, "b");
+
+
+ _objWithNullMeta = (IObj)m;
+ _obj = _objWithNullMeta.withMeta(meta);
+ _expectedType = typeof(PersistentHashSet);
+ }
+
+ [TearDown]
+ public void Teardown()
+ {
+ _mocks.VerifyAll();
+ }
+
+ }
+
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentListTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentListTests.cs new file mode 100644 index 00000000..dd4539d4 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentListTests.cs @@ -0,0 +1,245 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class PersistentListTests : AssertionHelper
+ {
+ #region C-tor tests
+
+ [Test]
+ public void OneArgCtorConstructsListOfOneElement()
+ {
+ PersistentList p = new PersistentList("abc");
+
+ Expect(p.first(), EqualTo("abc"));
+ Expect(p.rest(), Null);
+ Expect(p.count(), EqualTo(1));
+ }
+
+ [Test]
+ public void ListCtorConstructsListOfSeveralElements()
+ {
+ object[] items = new object[] { 1, "abc", 2, "def" };
+ IPersistentList p = PersistentList.create(items);
+
+ Expect(p.count(), EqualTo(4));
+
+ ISeq s = p.seq();
+ Expect(s.first(), EqualTo(1));
+ Expect(s.rest().first(), EqualTo("abc"));
+ Expect(s.rest().rest().first(), EqualTo(2));
+ Expect(s.rest().rest().rest().first(), EqualTo("def"));
+ Expect(s.rest().rest().rest().rest(), Null);
+ }
+
+
+ #endregion
+
+ #region IPersistentStack tests
+
+ [Test]
+ public void PeekYieldsFirstElementAndListUnchanged()
+ {
+ PersistentList p = (PersistentList)PersistentList.create(new object[] { "abc", 1, "def" });
+
+ Expect(p.peek(), EqualTo("abc"));
+ Expect(p.count(), EqualTo(3));
+ }
+
+ [Test]
+ public void PopLosesfirstElement()
+ {
+ PersistentList p = (PersistentList)PersistentList.create(new object[]{"abc", 1, "def"});
+ PersistentList p2 = (PersistentList)p.pop();
+ Expect(p2.count(), EqualTo(2));
+ Expect(p2.peek(), EqualTo(1));
+ }
+
+ [Test]
+ public void PopOnSingletonListYieldsEmptyList()
+ {
+ PersistentList p = new PersistentList("abc");
+ IPersistentStack s = p.pop();
+ Expect(s.count(), EqualTo(0));
+ }
+
+ [Test]
+ [ExpectedException(typeof(InvalidOperationException))]
+ public void DoublePopOnSingletonListYieldsException()
+ {
+ PersistentList p = new PersistentList("abc");
+ IPersistentStack s = p.pop().pop();
+ }
+
+ #endregion
+
+ #region IPersistentCollection tests
+
+ [Test]
+ public void EmptyHasNoElements()
+ {
+ PersistentList p = new PersistentList("abc");
+ IPersistentCollection c = p.empty();
+
+ Expect(c.count(), EqualTo(0));
+ }
+
+ [Test]
+ public void EmptyPreservesMeta()
+ {
+ MockRepository mocks = new MockRepository();
+ IPersistentMap meta = mocks.StrictMock<IPersistentMap>();
+ mocks.ReplayAll();
+
+ IPersistentCollection p = (IPersistentCollection)new PersistentList("abc").withMeta(meta);
+ IObj obj = (IObj) p.empty();
+
+ Expect(obj.meta(), SameAs(meta));
+ mocks.VerifyAll();
+ }
+
+ #endregion
+
+ #region IReduce tests
+
+ [Test]
+ public void ReduceWithNoStartIterates()
+ {
+ MockRepository mocks = new MockRepository();
+ IFn fn = mocks.StrictMock<IFn>();
+ RMExpect.Call(fn.invoke(1, 2)).Return(5);
+ RMExpect.Call(fn.invoke(5, 3)).Return(7);
+ mocks.ReplayAll();
+
+ PersistentList p = (PersistentList)PersistentList.create(new object[] { 1, 2, 3 });
+ object ret = p.reduce(fn);
+
+ Expect(ret, EqualTo(7));
+
+ mocks.VerifyAll();
+ }
+
+ [Test]
+ public void ReduceWithStartIterates()
+ {
+ MockRepository mocks = new MockRepository();
+ IFn fn = mocks.StrictMock<IFn>();
+ RMExpect.Call(fn.invoke(20, 1)).Return(10);
+ RMExpect.Call(fn.invoke(10, 2)).Return(5);
+ RMExpect.Call(fn.invoke(5, 3)).Return(7);
+ mocks.ReplayAll();
+
+ PersistentList p = (PersistentList)PersistentList.create(new object[] { 1, 2, 3 });
+ object ret = p.reduce(fn, 20);
+
+ Expect(ret, EqualTo(7));
+
+ mocks.VerifyAll();
+ }
+
+
+ #endregion
+
+ }
+
+ [TestFixture]
+ public class PersistentList_ISeq_Tests : ISeqTestHelper
+ {
+ PersistentList _pl;
+ PersistentList _plWithMeta;
+ object[] _values;
+
+
+ [SetUp]
+ public void Setup()
+ {
+ PersistentList p1 = new PersistentList("abc");
+ PersistentList p2 = (PersistentList)p1.cons("def");
+ _pl = (PersistentList)p2.cons(7);
+ _values = new object[] { 7, "def", "abc" };
+ _plWithMeta = (PersistentList)_pl.withMeta(PersistentHashMap.create("a", 1));
+ }
+
+ [Test]
+ public void ISeq_has_correct_valuess()
+ {
+ VerifyISeqContents(_pl, _values);
+ }
+
+ [Test]
+ public void ISeq_with_meta_has_correct_valuess()
+ {
+ VerifyISeqContents(_plWithMeta, _values);
+ }
+
+ [Test]
+ public void Rest_has_correct_type()
+ {
+ VerifyISeqRestTypes(_pl, typeof(PersistentList));
+ }
+
+ [Test]
+ public void Cons_works()
+ {
+ VerifyISeqCons(_pl, "pqr", _values);
+ }
+
+ [Test]
+ public void ConsPreservesMeta()
+ {
+ PersistentList p2 = (PersistentList)_plWithMeta.cons("def");
+ Expect(p2.meta(), SameAs(_plWithMeta.meta()));
+ }
+ }
+
+
+ [TestFixture]
+ public class PersistentList_IObj_Tests : IObjTests
+ {
+ MockRepository _mocks;
+
+ [SetUp]
+ public void Setup()
+ {
+ _mocks = new MockRepository();
+ IPersistentMap meta = _mocks.StrictMock<IPersistentMap>();
+ _mocks.ReplayAll();
+
+ PersistentList p1 = (PersistentList)PersistentList.create(new object[] { "abc", "def" });
+
+
+ _objWithNullMeta = (IObj)p1;
+ _obj = _objWithNullMeta.withMeta(meta);
+ _expectedType = typeof(PersistentList);
+ }
+
+ [TearDown]
+ public void Teardown()
+ {
+ _mocks.VerifyAll();
+ }
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentQueueTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentQueueTests.cs new file mode 100644 index 00000000..3281ac44 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentQueueTests.cs @@ -0,0 +1,22 @@ +/**
+ * 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;
+
+namespace Clojure.Tests.LibTests
+{
+ // TODO: Add tests for PersistentQueue
+ class PersistentQueueTests
+ {
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentStructMapTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentStructMapTests.cs new file mode 100644 index 00000000..c2db638a --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentStructMapTests.cs @@ -0,0 +1,22 @@ +/**
+ * 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;
+
+namespace Clojure.Tests.LibTests
+{
+ // TODO: Add tests for PersistentStructMap
+ class PersistentStructMapTests
+ {
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentTreeMapTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentTreeMapTests.cs new file mode 100644 index 00000000..aebfd478 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentTreeMapTests.cs @@ -0,0 +1,233 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+using System.Collections;
+
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class PersistentTreeMapTests : AssertionHelper
+ {
+ #region C-tor tests
+
+ [Test]
+ public void CreateOnEmptyDictionaryReturnsEmptyMap()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ IPersistentMap m = PersistentTreeMap.create(d);
+
+ Expect(m.count(), EqualTo(0));
+ }
+
+ [Test]
+ public void CreateOnDictionaryReturnsMap()
+ {
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ IPersistentMap m = PersistentTreeMap.create(d);
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.valAt(1), EqualTo("a"));
+ Expect(m.valAt(2), EqualTo("b"));
+ Expect(m.containsKey(3), False);
+ }
+
+ //[Test]
+ //public void CreateOnEmptyListReturnsEmptyMap()
+ //{
+ // ArrayList a = new ArrayList();
+ // IPersistentMap m = PersistentTreeMap.create(a);
+
+ // Expect(m.count(), EqualTo(0));
+ //}
+
+ //[Test]
+ //public void CreateOnListReturnsMap()
+ //{
+ // object[] items = new object[] { 1, "a", 2, "b" };
+ // ArrayList a = new ArrayList(items);
+
+ // IPersistentMap m = PersistentTreeMap.create(a);
+
+ // Expect(m.count(), EqualTo(2));
+ // Expect(m.valAt(1), EqualTo("a"));
+ // Expect(m.valAt(2), EqualTo("b"));
+ // Expect(m.containsKey(3), False);
+ //}
+
+ [Test]
+ public void CreateOnEmptyISeqReturnsEmptyMap()
+ {
+ object[] items = new object[] { };
+ ArrayList a = new ArrayList(items);
+ ISeq s = PersistentList.create(a).seq();
+ IPersistentMap m = PersistentTreeMap.create(s);
+
+ Expect(m.count(), EqualTo(0));
+ }
+
+ [Test]
+ public void CreateOnISeqReturnsMap()
+ {
+ object[] items = new object[] { 1, "a", 2, "b" };
+ ArrayList a = new ArrayList(items);
+ ISeq s = PersistentList.create(a).seq();
+ IPersistentMap m = PersistentTreeMap.create(s);
+
+ Expect(m.count(), EqualTo(2));
+ Expect(m.valAt(1), EqualTo("a"));
+ Expect(m.valAt(2), EqualTo("b"));
+ Expect(m.containsKey(3), False);
+ }
+
+ [Test]
+ public void DefaultCtorReturnsEmptyMap()
+ {
+ PersistentTreeMap m = new PersistentTreeMap();
+
+ Expect(m.count(), EqualTo(0));
+ Expect(m.meta(), Null);
+ }
+
+
+ //[Test]
+ //public void CreateOnMetaNoArgsReturnsEmptyMap()
+ //{
+ // MockRepository mocks = new MockRepository();
+ // IPersistentMap meta = mocks.StrictMock<IPersistentMap>();
+ // mocks.ReplayAll();
+
+ // PersistentTreeMap m = PersistentTreeMap.create(meta);
+
+ // Expect(m.count(), EqualTo(0));
+ // Expect(m.meta(), SameAs(meta));
+ // mocks.VerifyAll();
+ //}
+
+ //[Test]
+ //public void CreateOnMetaNoArgsReturnsMap()
+ //{
+ // MockRepository mocks = new MockRepository();
+ // IPersistentMap meta = mocks.StrictMock<IPersistentMap>();
+ // mocks.ReplayAll();
+
+ // PersistentTreeMap m = PersistentTreeMap.create(meta, 1, "a", 2, "b");
+
+ // Expect(m.count(), EqualTo(2));
+ // Expect(m.valAt(1), EqualTo("a"));
+ // Expect(m.valAt(2), EqualTo("b"));
+ // Expect(m.containsKey(3), False);
+ // Expect(m.meta(), SameAs(meta));
+ // mocks.VerifyAll();
+ //}
+
+ #endregion
+
+ #region Associative tests
+
+ #endregion
+
+ #region IPersistentMap tests
+
+ #endregion
+
+ #region IPersistentCollection tests
+
+ #endregion
+
+ #region Big tests
+
+ [Test]
+ public void DoSomeBigTests()
+ {
+ DoBigTest(100);
+ DoBigTest(1000);
+ DoBigTest(10000);
+ DoBigTest(100000);
+ }
+
+ public void DoBigTest(int numEntries)
+ {
+ System.Console.WriteLine("Testing {0} items.", numEntries);
+
+ Random rnd = new Random();
+ Dictionary<int, int> dict = new Dictionary<int, int>(numEntries);
+ for (int i = 0; i < numEntries; i++)
+ {
+ int r = rnd.Next();
+ dict[r] = r;
+ }
+ PersistentTreeMap m = (PersistentTreeMap)PersistentTreeMap.create(dict);
+
+ Expect(m.count(), EqualTo(dict.Count));
+
+ foreach (int key in dict.Keys)
+ {
+ Expect(m.containsKey(key));
+ Expect(m.valAt(key), EqualTo(key));
+ }
+
+ for (ISeq s = m.seq(); s != null; s = s.rest())
+ Expect(dict.ContainsKey((int)((IMapEntry)s.first()).key()));
+
+ }
+
+ #endregion
+ }
+
+
+ [TestFixture]
+ public class PersistentTreeMap_IObj_Tests : IObjTests
+ {
+ MockRepository _mocks;
+
+ [SetUp]
+ public void Setup()
+ {
+ _mocks = new MockRepository();
+ IPersistentMap meta = _mocks.StrictMock<IPersistentMap>();
+ _mocks.ReplayAll();
+
+ Dictionary<int, string> d = new Dictionary<int, string>();
+ d[1] = "a";
+ d[2] = "b";
+
+ PersistentTreeMap m = (PersistentTreeMap)PersistentTreeMap.create(d);
+
+
+ _objWithNullMeta = (IObj)m;
+ _obj = _objWithNullMeta.withMeta(meta);
+ _expectedType = typeof(PersistentTreeMap);
+ }
+
+ [TearDown]
+ public void Teardown()
+ {
+ _mocks.VerifyAll();
+ }
+
+ }
+
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentTreeSetTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentTreeSetTests.cs new file mode 100644 index 00000000..77b9a4d4 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentTreeSetTests.cs @@ -0,0 +1,58 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+
+namespace Clojure.Tests.LibTests
+{
+ // TODO: Add tests for PersistentTreeSet
+ class PersistentTreeSetTests
+ {
+ }
+
+ [TestFixture]
+ public class PersistentTreeSet_IObj_Tests : IObjTests
+ {
+ MockRepository _mocks;
+
+ [SetUp]
+ public void Setup()
+ {
+ _mocks = new MockRepository();
+ IPersistentMap meta = _mocks.StrictMock<IPersistentMap>();
+ _mocks.ReplayAll();
+
+ PersistentTreeSet m = PersistentTreeSet.create("a", "b");
+
+
+ _objWithNullMeta = (IObj)m;
+ _obj = _objWithNullMeta.withMeta(meta);
+ _expectedType = typeof(PersistentTreeSet);
+ }
+
+ [TearDown]
+ public void Teardown()
+ {
+ _mocks.VerifyAll();
+ }
+
+ }
+
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentVectorTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentVectorTests.cs new file mode 100644 index 00000000..bde46e9f --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/PersistentVectorTests.cs @@ -0,0 +1,330 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class PersistentVectorTests : AssertionHelper
+ {
+
+ #region C-tor tests
+
+ [Test]
+ public void CreateOnISeqReturnsCorrectCount()
+ {
+ Range r = new Range(2,5);
+ PersistentVector v = PersistentVector.create(r);
+
+ Expect(v.count(),EqualTo(r.count()));
+ }
+
+ [Test]
+ public void CreateOnISeqHasItems()
+ {
+ Range r = new Range(2, 5);
+ PersistentVector v = PersistentVector.create(r);
+
+ Expect(v.nth(0), EqualTo(2));
+ Expect(v.nth(1), EqualTo(3));
+ Expect(v.nth(2), EqualTo(4));
+ }
+
+ [Test]
+ public void CreateOnISeqWithManyItemsWorks()
+ {
+ // Want to bust out of the first tail, so need to insert more than 32 elements.
+ Range r = new Range(2, 1000);
+ PersistentVector v = PersistentVector.create(r);
+
+ Expect(v.count(), EqualTo(r.count()));
+ for (int i = 0; i < v.count(); ++i)
+ Expect(v.nth(i), EqualTo(i + 2));
+ }
+
+ [Test]
+ public void CreateOnISeqWithManyManyItemsWorks()
+ {
+ // Want to bust out of the first tail, so need to insert more than 32 elements.
+ // Let's get out of the second level, too.
+
+ Range r = new Range(2, 100000);
+ PersistentVector v = PersistentVector.create(r);
+
+ Expect(v.count(), EqualTo(r.count()));
+ for (int i = 0; i < v.count(); ++i)
+ Expect(v.nth(i), EqualTo(i + 2));
+ }
+
+ [Test]
+ public void CreateOnMultipleItemsWorks()
+ {
+ PersistentVector v = PersistentVector.create(2,3,4);
+
+ Expect(v.count(),EqualTo(3));
+ Expect(v.nth(0), EqualTo(2));
+ Expect(v.nth(1), EqualTo(3));
+ Expect(v.nth(2), EqualTo(4));
+ }
+
+ #endregion
+
+ #region IPersistentVector tests
+
+
+ // nth - tested in c-tor tests
+
+
+ [Test]
+ public void CountYieldsLength()
+ {
+ PersistentVector v = PersistentVector.create(1, 2, 3);
+
+ Expect(v.length(), EqualTo(3));
+ }
+
+ [Test]
+ [ExpectedException(typeof(IndexOutOfRangeException))]
+ public void NthOutOfRangeLowFails()
+ {
+ PersistentVector v = PersistentVector.create(1, 2, 3);
+ object obj = v.nth(-4);
+ }
+
+ [Test]
+ [ExpectedException(typeof(IndexOutOfRangeException))]
+ public void NthOutOfRangeHighFails()
+ {
+ PersistentVector v = PersistentVector.create(1, 2, 3);
+ object obj = v.nth(4);
+ }
+
+ [Test]
+ public void AssocNReplacesInRangeForSmall()
+ {
+ Range r = new Range(2, 5);
+ PersistentVector v1 = PersistentVector.create(r);
+ IPersistentVector v2 = v1.assocN(1,10);
+
+ Expect(v1.nth(0), EqualTo(2));
+ Expect(v1.nth(1), EqualTo(3));
+ Expect(v1.nth(2), EqualTo(4));
+ Expect(v1.count(), EqualTo(3));
+ Expect(v2.nth(0), EqualTo(2));
+ Expect(v2.nth(1), EqualTo(10));
+ Expect(v2.nth(2), EqualTo(4));
+ Expect(v2.count(), EqualTo(3));
+ }
+
+ [Test]
+ public void AssocNAddsAtEndForSmall()
+ {
+ Range r = new Range(2, 5);
+ PersistentVector v1 = PersistentVector.create(r);
+ IPersistentVector v2 = v1.assocN(3, 10);
+
+ Expect(v1.nth(0), EqualTo(2));
+ Expect(v1.nth(1), EqualTo(3));
+ Expect(v1.nth(2), EqualTo(4));
+ Expect(v1.count(), EqualTo(3));
+ Expect(v2.nth(0), EqualTo(2));
+ Expect(v2.nth(1), EqualTo(3));
+ Expect(v2.nth(2), EqualTo(4));
+ Expect(v2.nth(3), EqualTo(10));
+ Expect(v2.count(), EqualTo(4));
+ }
+
+ [Test]
+ [ExpectedException(typeof(IndexOutOfRangeException))]
+ public void AssocNOutOfRangeLowThrowsException()
+ {
+ Range r = new Range(2, 5);
+ PersistentVector v1 = PersistentVector.create(r);
+ IPersistentVector v2 = v1.assocN(-4, 10);
+ }
+
+ [Test]
+ [ExpectedException(typeof(IndexOutOfRangeException))]
+ public void AssocNOutOfRangeHighThrowsException()
+ {
+ Range r = new Range(2, 5);
+ PersistentVector v1 = PersistentVector.create(r);
+ IPersistentVector v2 = v1.assocN(4, 10);
+ }
+
+ [Test]
+ public void AssocNAddsAtEndForEmpty()
+ {
+ PersistentVector v1 = PersistentVector.create();
+ IPersistentVector v2 = v1.assocN(0, "abc");
+
+ Expect(v1.count(), EqualTo(0));
+ Expect(v2.count(), EqualTo(1));
+ Expect(v2.nth(0), EqualTo("abc"));
+ }
+
+ [Test]
+ public void AssocNChangesForBig()
+ {
+ Range r = new Range(2, 100000);
+ PersistentVector v1 = PersistentVector.create(r);
+ IPersistentVector v2 = v1;
+
+ for (int i = 0; i < 110000; i++)
+ v2 = v2.assocN(i, i + 20);
+
+ for ( int i=0; i<v1.count(); ++i )
+ Expect(v1.nth(i),EqualTo(i+2));
+
+ for (int i = 0; i < v2.count(); ++i)
+ Expect(v2.nth(i), EqualTo(i + 20));
+ }
+
+ [Test]
+ public void ConsWorks()
+ {
+ PersistentVector v1 = PersistentVector.create(2,3,4);
+ IPersistentVector v2 = v1;
+
+ for (int i = 3; i < 100000; i++)
+ v2 = v2.cons(i+2);
+
+ Expect(v1.count(), EqualTo(3));
+ Expect(v2.count(), EqualTo(100000));
+
+ for (int i = 0; i < v2.count(); ++i)
+ Expect(v2.nth(i), EqualTo(i + 2));
+ }
+
+ #endregion
+
+ #region IPersistentCollection tests
+
+ [Test]
+ public void EmptyReturnsEmptyCollection()
+ {
+ PersistentVector v = PersistentVector.create(1, 2, 3);
+ IPersistentCollection e = v.empty();
+
+ Expect(e.count(), EqualTo(0));
+ }
+
+ [Test]
+ public void EmptyCopiesMeta()
+ {
+ MockRepository mocks = new MockRepository();
+ IPersistentMap meta = mocks.StrictMock<IPersistentMap>();
+ mocks.ReplayAll();
+
+ PersistentVector v1 = PersistentVector.create(1, 2, 3);
+ IPersistentCollection e1 = v1.empty();
+
+ PersistentVector v2 = (PersistentVector) v1.withMeta(meta);
+ IPersistentCollection e2 = v2.empty();
+
+ Expect(((IObj)e1).meta(), Null);
+ Expect(((IObj)e2).meta(), SameAs(meta));
+
+ mocks.VerifyAll();
+ }
+
+
+ #endregion
+
+ #region IPersistentStack tests
+
+ [Test]
+ [ExpectedException(typeof(InvalidOperationException))]
+ public void PopOnEmptyThrowsException()
+ {
+ PersistentVector v = PersistentVector.create();
+ IPersistentStack s = v.pop();
+ }
+
+ [Test]
+ public void PopOnSizeOneReturnsEmpty()
+ {
+ PersistentVector v = PersistentVector.create(1);
+ IPersistentStack s = v.pop();
+
+ Expect(s.count(), EqualTo(0));
+ }
+
+ [Test]
+ public void PopOnSmallReturnsOneLess()
+ {
+ Range r = new Range(2, 20);
+ PersistentVector v = PersistentVector.create(r);
+ IPersistentStack s = v.pop();
+
+ Expect(v.count(),EqualTo(r.count()));
+ Expect(s.count(),EqualTo(v.count()-1));
+ }
+
+ [Test]
+ public void PopOnBigWorks()
+ {
+ Range r = new Range(0, 100000);
+ PersistentVector v = PersistentVector.create(r);
+ IPersistentStack s = v;
+ for (int i = 16; i < 100000; i++)
+ s = s.pop();
+
+ Expect(v.count(), EqualTo(r.count()));
+ Expect(s.count(), EqualTo(16));
+ }
+
+
+ #endregion
+
+ #region IFn tests
+
+ #endregion
+ }
+
+ [TestFixture]
+ public class PersistentVector_IObj_Tests : IObjTests
+ {
+ MockRepository _mocks;
+
+ [SetUp]
+ public void Setup()
+ {
+ _mocks = new MockRepository();
+ IPersistentMap meta = _mocks.StrictMock<IPersistentMap>();
+ _mocks.ReplayAll();
+
+ PersistentVector v = PersistentVector.create(2, 3, 4);
+
+ _objWithNullMeta = (IObj)v;
+ _obj = _objWithNullMeta.withMeta(meta);
+ _expectedType = typeof(PersistentVector);
+ }
+
+ [TearDown]
+ public void Teardown()
+ {
+ _mocks.VerifyAll();
+ }
+
+ }
+
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/RangeTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/RangeTests.cs new file mode 100644 index 00000000..7948a681 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/RangeTests.cs @@ -0,0 +1,182 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class RangeTests : AssertionHelper
+ {
+ #region C-tor tests
+
+ [Test]
+ public void Basic_ctor_has_no_meta()
+ {
+ Range r = new Range(2, 5);
+ Expect(r.meta(), Null);
+ }
+
+ [Test]
+ public void Meta_ctor_has_meta()
+ {
+ MockRepository mocks = new MockRepository();
+ IPersistentMap meta = mocks.StrictMock<IPersistentMap>();
+ mocks.ReplayAll();
+
+ Range r = new Range(meta, 2, 5);
+
+ Expect(r.meta(), EqualTo(meta));
+
+ mocks.VerifyAll();
+ }
+
+ #endregion
+
+ #region IPersistentCollection tests
+
+ [Test]
+ public void Range_has_correct_count()
+ {
+ Range r = new Range(2, 20);
+
+ Expect(r.count(), EqualTo(18));
+ }
+
+
+ #endregion
+
+ #region IReduce tests
+
+ [Test]
+ public void ReduceWithNoStartIterates()
+ {
+ MockRepository mocks = new MockRepository();
+ IFn fn = mocks.StrictMock<IFn>();
+ RMExpect.Call(fn.invoke(2, 3)).Return(5);
+ RMExpect.Call(fn.invoke(5, 4)).Return(7);
+ mocks.ReplayAll();
+
+ Range r = new Range(2, 5);
+ object ret = r.reduce(fn);
+
+ Expect(ret, EqualTo(7));
+
+ mocks.VerifyAll();
+ }
+
+ [Test]
+ public void ReduceWithStartIterates()
+ {
+ MockRepository mocks = new MockRepository();
+ IFn fn = mocks.StrictMock<IFn>();
+ RMExpect.Call(fn.invoke(20, 2)).Return(10);
+ RMExpect.Call(fn.invoke(10, 3)).Return(5);
+ RMExpect.Call(fn.invoke(5, 4)).Return(7);
+ mocks.ReplayAll();
+
+ Range r = new Range(2, 5);
+ object ret = r.reduce(fn, 20);
+
+ Expect(ret, EqualTo(7));
+
+ mocks.VerifyAll();
+ }
+
+ #endregion
+
+ // TODO: test stream capability of Range
+ }
+
+ [TestFixture]
+ public class Range_ISeq_Tests : ISeqTestHelper
+ {
+ Range _r;
+ Range _rWithMeta;
+ object[] _values;
+
+ [SetUp]
+ public void Setup()
+ {
+ IPersistentMap meta = PersistentHashMap.create("a", 1, "b", 2);
+
+ _r = new Range(2, 5);
+ _rWithMeta = new Range(meta, 2, 5);
+ _values = new object[] { 2, 3, 4 };
+ }
+
+ [Test]
+ public void Range_has_correct_values()
+ {
+ VerifyISeqContents(_r, _values);
+ }
+
+ [Test]
+ public void Range_with_meta_has_correct_values()
+ {
+ VerifyISeqContents(_rWithMeta, _values);
+ }
+
+ [Test]
+ public void Rest_preserves_meta()
+ {
+ VerifyISeqRestMaintainsMeta(_rWithMeta);
+ }
+
+ [Test]
+ public void Rest_preserves_type()
+ {
+ VerifyISeqRestTypes(_r, typeof(Range));
+ }
+
+ [Test]
+ public void Cons_works()
+ {
+ VerifyISeqCons(_r, 12, _values);
+ }
+ }
+
+ [TestFixture]
+ public class Range_IObj_Tests : IObjTests
+ {
+ MockRepository _mocks;
+
+ [SetUp]
+ public void Setup()
+ {
+ _mocks = new MockRepository();
+ IPersistentMap meta = _mocks.StrictMock<IPersistentMap>();
+ _mocks.ReplayAll();
+
+ Range r = new Range(2, 5);
+
+ _objWithNullMeta = (IObj)r;
+ _obj = _objWithNullMeta.withMeta(meta);
+ _expectedType = typeof(Range);
+ }
+
+ [TearDown]
+ public void Teardown()
+ {
+ _mocks.VerifyAll();
+ }
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/RefTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/RefTests.cs new file mode 100644 index 00000000..efc67335 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/RefTests.cs @@ -0,0 +1,23 @@ +/**
+ * 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;
+
+namespace Clojure.Tests.LibTests
+{
+ // TODO: Add tests for Ref
+
+ class RefTests
+ {
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/RestFnImplTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/RestFnImplTests.cs new file mode 100644 index 00000000..3d9870e4 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/RestFnImplTests.cs @@ -0,0 +1,22 @@ +/**
+ * 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;
+
+namespace Clojure.Tests.LibTests
+{
+ //TODO: Add RestFnImpl tests
+ class RestFnImplTests
+ {
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/RestFnTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/RestFnTests.cs new file mode 100644 index 00000000..90ef2168 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/RestFnTests.cs @@ -0,0 +1,22 @@ +/**
+ * 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;
+
+namespace Clojure.Tests.LibTests
+{
+ // TODO: Add RestFn tests
+ class RestFnTests
+ {
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/StreamSeqTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/StreamSeqTests.cs new file mode 100644 index 00000000..7f4c4f61 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/StreamSeqTests.cs @@ -0,0 +1,22 @@ +/**
+ * 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;
+
+namespace Clojure.Tests.LibTests
+{
+ // TODO: add tests for StreamSeq
+ class StreamSeqTests
+ {
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/StringSeqTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/StringSeqTests.cs new file mode 100644 index 00000000..b17280b2 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/StringSeqTests.cs @@ -0,0 +1,163 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class StringSeqTests : AssertionHelper
+ {
+
+ #region C-tor tests
+
+ [Test]
+ public void Create_on_empty_string_yields_null()
+ {
+ StringSeq s = StringSeq.create(String.Empty);
+
+ Expect(s, Null);
+ }
+
+ [Test]
+ public void Create_on_nonempty_string_yields_a_StringSeq()
+ {
+ StringSeq s = StringSeq.create("abcde");
+
+ Expect(s, Not.Null);
+ }
+
+ #endregion
+
+ #region IPersistentCollection tests
+
+ [Test]
+ public void Count_is_string_length()
+ {
+ StringSeq s = StringSeq.create("abcde");
+
+ Expect(s.count(),EqualTo(5));
+ }
+
+ #endregion
+
+ #region IndexedSeq tests
+
+ [Test]
+ public void Initial_index_is_zero()
+ {
+ StringSeq s = StringSeq.create("abc");
+
+ Expect(s.index(), EqualTo(0));
+ }
+
+ [Test]
+ public void Index_of_rest_is_one()
+ {
+ StringSeq s = StringSeq.create("abc");
+ IndexedSeq i = (IndexedSeq)s.rest();
+
+ Expect(i.index(), EqualTo(1));
+ }
+
+ #endregion
+
+ }
+
+ [TestFixture]
+ public class StringSeq_ISeq_Tests : ISeqTestHelper
+ {
+ StringSeq _s;
+ StringSeq _sWithMeta;
+ object[] _values;
+
+ [SetUp]
+ public void Setup()
+ {
+ IPersistentMap meta = PersistentHashMap.create("a", 1, "b", 2);
+
+ _s = StringSeq.create("abcde");
+ _sWithMeta = (StringSeq)((IObj)StringSeq.create("abcde")).withMeta(meta);
+ _values = new object[] { 'a', 'b', 'c', 'd', 'e' };
+ }
+
+ [Test]
+ public void StringSeq_has_correct_ISeq_values()
+ {
+ VerifyISeqContents(_s, _values);
+ }
+
+ [Test]
+ public void StringSeq_with_meta_has_correct_ISeq_values()
+ {
+ VerifyISeqContents(_sWithMeta, _values);
+ }
+
+ [Test]
+ public void StringSeq_ISeq_rest_preserves_meta()
+ {
+ VerifyISeqRestMaintainsMeta(_sWithMeta);
+ }
+
+ [Test]
+ public void StringSeq_ISeq_rest_preserves_type()
+ {
+ VerifyISeqRestTypes(_s,typeof(StringSeq));
+ }
+
+ [Test]
+ public void StringSeq_ISeq_cons_works()
+ {
+ VerifyISeqCons(_s, 12, _values);
+ }
+
+ }
+
+ [TestFixture]
+ public class StringSeq_IObj_Tests : IObjTests
+ {
+ MockRepository _mocks;
+
+ [SetUp]
+ public void Setup()
+ {
+ _mocks = new MockRepository();
+ IPersistentMap meta = _mocks.StrictMock<IPersistentMap>();
+ _mocks.ReplayAll();
+
+ StringSeq s = StringSeq.create("abcde");
+
+
+ _objWithNullMeta = (IObj)s;
+ _obj = _objWithNullMeta.withMeta(meta);
+ _expectedType = typeof(StringSeq);
+ }
+
+ [TearDown]
+ public void Teardown()
+ {
+ _mocks.VerifyAll();
+ }
+
+ }
+
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/SymbolTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/SymbolTests.cs new file mode 100644 index 00000000..3cc32821 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/SymbolTests.cs @@ -0,0 +1,326 @@ +/**
+ * 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 NUnit.Framework;
+using Rhino.Mocks;
+
+using clojure.lang;
+
+using RMExpect = Rhino.Mocks.Expect;
+using System.Collections;
+
+
+
+namespace Clojure.Tests.LibTests
+{
+ [TestFixture]
+ public class SymbolTests : AssertionHelper
+ {
+
+ #region c-tor tests
+
+ [Test]
+ public void Intern2CreatesSymbolWithNoNS()
+ {
+ Symbol sym = Symbol.intern(null, "abc");
+
+ Expect(sym.Name, EqualTo("abc"));
+ Expect(sym.Namespace, Null);
+ Expect(sym.meta(), Null);
+ }
+
+ [Test]
+ public void Intern2CreatesSymbolWithNS()
+ {
+ Symbol sym = Symbol.intern("def", "abc");
+
+ Expect(sym.Name, EqualTo("abc"));
+ Expect(sym.Namespace, EqualTo("def"));
+ Expect(sym.meta(), Null);
+ }
+
+ [Test]
+ public void Intern2InternsStrings()
+ {
+ String symname = new StringBuilder().Append("ab").Append("c").ToString();
+ String nsname = new StringBuilder().Append("ab").Append("c").ToString();
+
+ Symbol sym1 = Symbol.intern(nsname,symname);
+ Symbol sym2 = Symbol.intern(nsname,symname);
+
+ Expect(String.IsInterned(sym1.Name),Not.Null);
+ Expect(String.IsInterned(sym1.Namespace), Not.Null);
+ Expect(Object.ReferenceEquals(sym1.Name, sym2.Name));
+ Expect(Object.ReferenceEquals(sym1.Namespace, sym2.Namespace));
+ Expect(object.ReferenceEquals(sym1.Name, symname), False);
+ }
+
+
+
+ [Test]
+ public void Intern1CreatesSymbolWithNoNS()
+ {
+ Symbol sym = Symbol.intern("abc");
+
+ Expect(sym.Name, EqualTo("abc"));
+ Expect(sym.Namespace, Null);
+ Expect(sym.meta(), Null);
+ }
+
+ [Test]
+ public void Intern1CreatesSymbolWithNS()
+ {
+ Symbol sym = Symbol.intern("def/abc");
+
+ Expect(sym.Name, EqualTo("abc"));
+ Expect(sym.Namespace, EqualTo("def"));
+ Expect(sym.meta(), Null);
+ }
+
+
+ [Test]
+ public void Intern1CreatesSymbolWithNSFromLastSlash()
+ {
+ Symbol sym = Symbol.intern("ghi/def/abc");
+
+ Expect(sym.Name, EqualTo("abc"));
+ Expect(sym.Namespace, EqualTo("ghi/def"));
+ Expect(sym.meta(), Null);
+ }
+
+ [Test]
+ public void Intern1InternsStrings()
+ {
+ String name = new StringBuilder().Append("def/").Append("abc").ToString();
+
+ Symbol sym1 = Symbol.intern(name);
+ Symbol sym2 = Symbol.intern(name);
+
+ Expect(String.IsInterned(sym1.Name), Not.Null);
+ Expect(String.IsInterned(sym1.Namespace), Not.Null);
+ Expect(Object.ReferenceEquals(sym1.Name, sym2.Name));
+ Expect(Object.ReferenceEquals(sym1.Namespace, sym2.Namespace));
+ }
+
+ #endregion
+
+ #region Object overrides
+
+ [Test]
+ public void SymToStringWithNoNSIsJustName()
+ {
+ Symbol sym = Symbol.intern("abc");
+ Expect(sym.ToString(), EqualTo("abc"));
+ }
+
+ [Test]
+ public void SymToStringWithNsConcatenatesNames()
+ {
+ Symbol sym = Symbol.intern("def", "abc");
+ Expect(sym.ToString(), EqualTo("def/abc"));
+ }
+
+ [Test]
+ public void EqualsOnIdentityIsTrue()
+ {
+ Symbol sym = Symbol.intern("abc");
+ Expect(sym.Equals(sym));
+ }
+
+ [Test]
+ public void EqualsOnNonSymbolIsFalse()
+ {
+ Symbol sym = Symbol.intern("abc");
+ Expect(sym.Equals("abc"), False);
+ }
+
+ [Test]
+ public void EqualsOnDissimilarSymbolIsFalse()
+ {
+ Symbol sym1 = Symbol.intern("abc");
+ Symbol sym2 = Symbol.intern("ab");
+ Symbol sym3 = Symbol.intern("def", "abc");
+ Symbol sym4 = Symbol.intern("de","abc");
+
+ Expect(sym1.Equals(sym2), False);
+ Expect(sym1.Equals(sym3), False);
+ Expect(sym3.Equals(sym4), False);
+ }
+
+ [Test]
+ public void EqualsOnSimilarSymbolIsTrue()
+ {
+
+ Symbol sym1 = Symbol.intern("abc");
+ Symbol sym2 = Symbol.intern("abc");
+ Symbol sym3 = Symbol.intern("def", "abc");
+ Symbol sym4 = Symbol.intern("def", "abc");
+
+ Expect(sym1.Equals(sym2));
+ Expect(sym3.Equals(sym4));
+ }
+
+ [Test]
+ public void HashCodeDependsOnNames()
+ {
+ Symbol sym1 = Symbol.intern("abc");
+ Symbol sym2 = Symbol.intern("abc");
+ Symbol sym3 = Symbol.intern("def", "abc");
+ Symbol sym4 = Symbol.intern("def", "abc");
+ Symbol sym5 = Symbol.intern("ab");
+ Symbol sym6 = Symbol.intern("de", "abc");
+
+ Expect(sym1.GetHashCode(), EqualTo(sym2.GetHashCode()));
+ Expect(sym3.GetHashCode(), EqualTo(sym4.GetHashCode()));
+ Expect(sym1.GetHashCode(), Not.EqualTo(sym3.GetHashCode()));
+ Expect(sym1.GetHashCode(), Not.EqualTo(sym5.GetHashCode()));
+ Expect(sym3.GetHashCode(), Not.EqualTo(sym6.GetHashCode()));
+ }
+
+ #endregion
+
+ #region Named tests
+
+ // We've been testing these all along.
+
+ #endregion
+
+ #region IFn tests
+
+ [Test]
+ public void Invoke2IndexesIntoItsFirstArg()
+ {
+ Symbol sym1 = Symbol.intern("abc");
+ Symbol sym2 = Symbol.intern("abc");
+ Symbol sym3 = Symbol.intern("ab");
+
+ IDictionary dict = new Hashtable();
+ dict[sym1] = 7;
+ dict["abc"] = 8;
+
+ Expect(sym1.invoke(dict), EqualTo(7));
+ Expect(sym2.invoke(dict), EqualTo(7));
+ Expect(sym3.invoke(dict), Null);
+ }
+
+ [Test]
+ public void Invoke3IndexesIntoItsFirstArg()
+ {
+ Symbol sym1 = Symbol.intern("abc");
+ Symbol sym2 = Symbol.intern("abc");
+ Symbol sym3 = Symbol.intern("ab");
+
+ IDictionary dict = new Hashtable();
+ dict[sym1] = 7;
+ dict["abc"] = 8;
+
+ Expect(sym1.invoke(dict,20), EqualTo(7));
+ Expect(sym2.invoke(dict,20), EqualTo(7));
+ Expect(sym3.invoke(dict,20), EqualTo(20));
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void InvokeOnNoArgsFails()
+ {
+ Symbol sym1 = Symbol.intern("abc");
+ object o = sym1.invoke();
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentException))]
+ public void InvokeOnTooManyArgsFails()
+ {
+ Symbol sym1 = Symbol.intern("abc");
+ IDictionary dict = new Hashtable();
+ dict[sym1] = 7;
+ dict["abc"] = 8;
+
+ object o = sym1.invoke(dict,20,null);
+ }
+
+ #endregion
+
+ #region IComparable tests
+
+ [Test]
+ [ExpectedException(typeof(InvalidOperationException))]
+ public void CompareToNonSymbolFails()
+ {
+ Symbol sym1 = Symbol.intern("abc");
+ int c = sym1.CompareTo("abc");
+ }
+
+ [Test]
+ public void CompareToEqualSymbolIsZero()
+ {
+ Symbol sym1 = Symbol.intern("abc");
+ Symbol sym2 = Symbol.intern("abc");
+
+ Expect(sym1.CompareTo(sym2), EqualTo(0));
+ }
+
+ [Test]
+ public void NullNSIsLessThanNonNullNS()
+ {
+ Symbol sym1 = Symbol.intern("abc");
+ Symbol sym2 = Symbol.intern("a", "abc");
+
+ Expect(sym1.CompareTo(sym2), LessThan(0));
+ Expect(sym2.CompareTo(sym1), GreaterThan(0));
+ }
+
+ [Test]
+ public void DissimilarNSCompareOnNS()
+ {
+ Symbol sym1 = Symbol.intern("a", "abc");
+ Symbol sym2 = Symbol.intern("b", "abc");
+
+ Expect(sym1.CompareTo(sym2), LessThan(0));
+ Expect(sym2.CompareTo(sym1), GreaterThan(0));
+ }
+
+ #endregion
+
+ }
+
+ [TestFixture]
+ public class Symbol_IObj_Tests : IObjTests
+ {
+ MockRepository _mocks;
+
+ [SetUp]
+ public void Setup()
+ {
+ _mocks = new MockRepository();
+ IPersistentMap meta = _mocks.StrictMock<IPersistentMap>();
+ _mocks.ReplayAll();
+
+ Symbol sym1 = Symbol.intern("def", "abc");
+
+ _objWithNullMeta = (IObj)sym1;
+ _obj = _objWithNullMeta.withMeta(meta);
+ _expectedType = typeof(Symbol);
+ }
+
+ [TearDown]
+ public void Teardown()
+ {
+ _mocks.VerifyAll();
+ }
+
+ }
+
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/TestTest.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/TestTest.cs new file mode 100644 index 00000000..3013a348 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/TestTest.cs @@ -0,0 +1,62 @@ +/**
+ * 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 NUnit.Framework;
+
+
+namespace Clojure.Tests.LibTests
+{
+
+
+ //public class BaseTest : AssertionHelper
+ //{
+ // [Test]
+ // public void Test1()
+ // {
+ // Console.WriteLine("T1");
+ // }
+
+ // [Test]
+ // public void Test2()
+ // {
+ // Console.WriteLine("T2");
+ // }
+
+ //}
+
+ //[TestFixture]
+ //public class TestTest : BaseTest
+ //{
+ // [Test]
+ // public void Test3()
+ // {
+ // Console.WriteLine("T3");
+ // }
+ //}
+
+
+
+ //[TestFixture]
+ //public class TestTest2 : BaseTest
+ //{
+ // [Test]
+ // public void Test4()
+ // {
+ // Console.WriteLine("T4");
+ // }
+ //}
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/Properties/AssemblyInfo.cs b/ClojureCLR/Clojure/Clojure.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..648f8405 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Clojure.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Clojure.Tests")]
+[assembly: AssemblyCopyright("Copyright © 2009")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("6741a194-da64-4dd1-8fcf-6ed1248acd29")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/ClojureCLR/Clojure/Clojure/Bootstrap/ants.clj b/ClojureCLR/Clojure/Clojure/Bootstrap/ants.clj new file mode 100644 index 00000000..7cd1c5b7 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Bootstrap/ants.clj @@ -0,0 +1,407 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ant sim ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 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. + +;dimensions of square world +(def dim 80) ;; 80 +;number of ants = nants-sqrt^2 +(def nants-sqrt 7) ;; 7 +;number of places with food +(def food-places 35) ;; 3 +;range of amount of food at a place +(def food-range 100) +;scale factor for pheromone drawing +(def pher-scale 20.0) +;scale factor for food drawing +(def food-scale 30.0) +;evaporation rate +(def evap-rate 0.99) + +(def animation-sleep-ms 100) ; 100 +(def ant-sleep-ms 40) ;40 +(def evap-sleep-ms 1000) + +(def running true) + +(defstruct cell :food :pher) ;may also have :ant and :home + +;world is a 2d vector of refs to cells +(def world + (apply vector + (map (fn [_] + (apply vector (map (fn [_] (ref (struct cell 0 0))) + (range dim)))) + (range dim)))) + +(defn place [[x y]] + (-> world (nth x) (nth y))) + +(defstruct ant :dir) ;may also have :food + +(defn create-ant + "create an ant at the location, returning an ant agent on the location" + [loc dir] + (sync nil + (let [p (place loc) + a (struct ant dir)] + (alter p assoc :ant a) + (agent loc)))) + +(def home-off (/ dim 4)) +(def home-range (range home-off (+ nants-sqrt home-off))) + +(defn setup + "places initial food and ants, returns seq of ant agents" + [] + (sync nil + (dotimes [i food-places] + (let [p (place [(rand-int dim) (rand-int dim)])] + (alter p assoc :food (rand-int food-range)))) + (doall + (for [x home-range y home-range] + (do + (alter (place [x y]) + assoc :home true) + (create-ant [x y] (rand-int 8))))))) + +(defn bound + "returns n wrapped into range 0-b" + [b n] + (let [n (rem n b)] + (if (neg? n) + (+ n b) + n))) + +(defn wrand + "given a vector of slice sizes, returns the index of a slice given a + random spin of a roulette wheel with compartments proportional to + slices." + [slices] + (let [total (reduce + slices) + r (rand total)] + (loop [i 0 sum 0] + (if (< r (+ (slices i) sum)) + i + (recur (inc i) (+ (slices i) sum)))))) + +;dirs are 0-7, starting at north and going clockwise +;these are the deltas in order to move one step in given dir +(def dir-delta {0 [0 -1] + 1 [1 -1] + 2 [1 0] + 3 [1 1] + 4 [0 1] + 5 [-1 1] + 6 [-1 0] + 7 [-1 -1]}) + +(defn delta-loc + "returns the location one step in the given dir. Note the world is a torus" + [[x y] dir] + (let [[dx dy] (dir-delta (bound 8 dir))] + [(bound dim (+ x dx)) (bound dim (+ y dy))])) + +;(defmacro dosync [& body] +; `(sync nil ~@body)) + +;ant agent functions +;an ant agent tracks the location of an ant, and controls the behavior of +;the ant at that location + +(defn turn + "turns the ant at the location by the given amount" + [loc amt] + (dosync + (let [p (place loc) + ant (:ant @p)] + (alter p assoc :ant (assoc ant :dir (bound 8 (+ (:dir ant) amt)))))) + loc) + +(defn move + "moves the ant in the direction it is heading. Must be called in a + transaction that has verified the way is clear" + [loc] + (let [oldp (place loc) + ant (:ant @oldp) + newloc (delta-loc loc (:dir ant)) + p (place newloc)] + ;move the ant + (alter p assoc :ant ant) + (alter oldp dissoc :ant) + ;leave pheromone trail + (when-not (:home @oldp) + (alter oldp assoc :pher (inc (:pher @oldp)))) + newloc)) + +(defn take-food [loc] + "Takes one food from current location. Must be called in a + transaction that has verified there is food available" + (let [p (place loc) + ant (:ant @p)] + (alter p assoc + :food (dec (:food @p)) + :ant (assoc ant :food true)) + loc)) + +(defn drop-food [loc] + "Drops food at current location. Must be called in a + transaction that has verified the ant has food" + (let [p (place loc) + ant (:ant @p)] + (alter p assoc + :food (inc (:food @p)) + :ant (dissoc ant :food)) + loc)) + +(defn rank-by + "returns a map of xs to their 1-based rank when sorted by keyfn" + [keyfn xs] + (let [sorted (sort-by (comp float keyfn) xs)] + (reduce (fn [ret i] (assoc ret (nth sorted i) (inc i))) + {} (range (count sorted))))) + +(defn behave + "the main function for the ant agent" + [loc] + (let [p (place loc) + ant (:ant @p) + ahead (place (delta-loc loc (:dir ant))) + ahead-left (place (delta-loc loc (dec (:dir ant)))) + ahead-right (place (delta-loc loc (inc (:dir ant)))) + places [ahead ahead-left ahead-right]] + (. System.Threading.Thread (Sleep ant-sleep-ms)) ;;; Thread => System.Threading.Thread, sleep => .Sleep + (dosync + (when running + (send-off *agent* #'behave)) + (if (:food ant) + ;going home + (cond + (:home @p) + (-> loc drop-food (turn 4)) + (and (:home @ahead) (not (:ant @ahead))) + (move loc) + :else + (let [ranks (merge-with + + (rank-by (comp #(if (:home %) 1 0) deref) places) + (rank-by (comp :pher deref) places))] + (([move #(turn % -1) #(turn % 1)] + (wrand [(if (:ant @ahead) 0 (ranks ahead)) + (ranks ahead-left) (ranks ahead-right)])) + loc))) + ;foraging + (cond + (and (pos? (:food @p)) (not (:home @p))) + (-> loc take-food (turn 4)) + (and (pos? (:food @ahead)) (not (:home @ahead)) (not (:ant @ahead))) + (move loc) + :else + (let [ranks (merge-with + + (rank-by (comp :food deref) places) + (rank-by (comp :pher deref) places))] + (([move #(turn % -1) #(turn % 1)] + (wrand [(if (:ant @ahead) 0 (ranks ahead)) + (ranks ahead-left) (ranks ahead-right)])) + loc))))))) + +(defn evaporate + "causes all the pheromones to evaporate a bit" + [] + (dorun + (for [x (range dim) y (range dim)] + (dosync + (let [p (place [x y])] + (alter p assoc :pher (* evap-rate (:pher @p)))))))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; UI ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(import + '(System.Drawing + Graphics Brush SolidBrush Color Pen Rectangle Size Point + BufferedGraphicsContext BufferedGraphicsManager BufferedGraphics) + '(System.Windows.Forms + Panel Form DockStyle Application + PaintEventHandler PaintEventArgs) + ) + + +;pixels per world cell +(def scale 5) + + +(defn fill-cell [#^Graphics g x y #^Color c] + (let [b (new SolidBrush c) ] + (.FillRectangle g b (* x scale) (* y scale) scale scale) + (.Dispose b))) + +(def white-brush (new SolidBrush (.White Color))) + +(def black-pen (new Pen (.Black Color))) +(def grey-pen (new Pen (.FromArgb Color 100 100 100))) +(def red-pen (new Pen (.Red Color))) +(def blue-pen (new Pen (.Blue Color))) + +(defn render-ant [ant #^Graphics g x y] + (let [[hx hy tx ty] ({0 [2 0 2 4] + 1 [4 0 0 4] + 2 [4 2 0 2] + 3 [4 4 0 0] + 4 [2 4 2 0] + 5 [0 4 4 0] + 6 [0 2 4 2] + 7 [0 0 4 4]} + (:dir ant)) + + pen (if (:foot ant) red-pen black-pen)] + (.DrawLine g pen + (+ hx (* x scale)) + (+ hy (* y scale)) + (+ tx (* x scale)) + (+ ty (* y scale))))) + + +(defn render-place [ #^Graphics g p x y] + (when (pos? (:pher p)) + (fill-cell g x y + (.FromArgb Color (int (min 255 (* 255 (/ (:pher p) pher-scale)))) (.Green Color)))) + + (when (pos? (:food p)) + (fill-cell g x y + (.FromArgb Color (int (min 255 (* 255 (/ (:food p) food-scale)))) (.Red Color)))) + + (when (:ant p) + (render-ant (:ant p) g x y))) + + +(def current-wins (ref nil)) +(defstruct wins :form :panel :buffered-graphics) + +(defn create-buffered-graphics [panel] + (let [context (.Current BufferedGraphicsManager) + side (* scale dim) + size (new Size (inc side) (inc side)) + rect (new Rectangle 0 0 side side) + pgrafx (. panel CreateGraphics) ] + (. context set_MaximumBuffer size) + (.Allocate context pgrafx rect))) + + +(defn render [g] + (let [v (dosync (apply vector (for [x (range dim) y (range dim)] + @(place [x y])))) + side (* scale dim) + grafx (:buffered-graphics @current-wins) + bg (.Graphics grafx)] + (.FillRectangle bg white-brush 0 0 side side) + + (dorun + (for [x (range dim) y (range dim)] + (render-place bg (v (+ (* x dim) y)) x y))) + + (.DrawRectangle bg blue-pen + (* scale home-off) (* scale home-off) + (* scale nants-sqrt) (* scale nants-sqrt)) + + (.Render grafx))) + + + +(defn create-wins [] + (let [ panel (new Panel) + form (new Form) + bgrfx (create-buffered-graphics panel) ] + (doto panel + (.set_Dock (. DockStyle Fill)) + (.set_Location (new Point 0 0)) + (.set_Name "panel1") + (.set_Size (new Size (* dim scale) (* dim scale))) + (.add_Paint (. clojure.lang.Compiler + (GenerateTypedDelegate PaintEventHandler + 'MyPainter + '[sender args] + '((render (. args Graphics))))))) + (doto form + (.set_ClientSize (new Size (* dim scale) (* dim scale)))) + (.. form (Controls) (Add panel)) + (struct-map wins :form form :panel panel :buffered-graphics bgrfx))) + + + + +(def win-app (agent nil)) + +(def winforms-app-inited (ref false)) + +(defn init-winforms-app [] + (dosync + (ensure winforms-app-inited) + (when-not @winforms-app-inited + (.EnableVisualStyles Application) + (.SetCompatibleTextRenderingDefault Application false) + (ref-set winforms-app-inited true)))) + + + (defn start-gui [x] + (init-winforms-app) + (dosync + (ref-set current-wins (create-wins))) + (.Run Application (:form @current-wins)) + ) + + +(def animator (agent nil)) + +; We can't call refresh directly because it is running on a different thread. +; we have to call Invoke to make things happen. +; This requires having a delegate. Sigh. +; I used ThreadStart as the delegate type because it has void() signature. + +(def refresh-panel-delegate + (. clojure.lang.Compiler GenerateTypedDelegate System.Threading.ThreadStart 'RefreshPanel [] + '((.Refresh (:panel @current-wins))))) + + +(defn animation [x] + (when running + (send-off *agent* #'animation)) + (.Invoke (:panel @current-wins) refresh-panel-delegate) + (.Sleep System.Threading.Thread animation-sleep-ms) + nil) + + + +(def evaporator (agent nil)) + +(defn evaporation [x] + (when running + (send-off *agent* #'evaporation)) + (evaporate) + (. System.Threading.Thread (Sleep evap-sleep-ms)) + nil) + + + +(defn test [] + (def ants (setup)) + (send-off win-app start-gui) + (dorun (map #(send-off % behave) ants)) + (send-off evaporator evaporation) +) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; use ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(comment +;demo +;(load-file "/Users/rich/dev/clojure/ants.clj") +(def ants (setup)) +(send-off win-app start-gui) +(send-off animator animation) +(dorun (map #(send-off % behave) ants)) +(send-off evaporator evaporation) + +) + +
\ No newline at end of file diff --git a/ClojureCLR/Clojure/Clojure/Bootstrap/core-print.clj b/ClojureCLR/Clojure/Clojure/Bootstrap/core-print.clj new file mode 100644 index 00000000..91e71f67 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Bootstrap/core-print.clj @@ -0,0 +1,310 @@ +; Copyright (c) Rich Hickey. 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.
+
+(in-ns 'clojure.core)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; printing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(import '(System.IO.System.IO.TextWriter)) ;;; was (import '(java.io Writer)) (I have replaced #^Writer with #^System.IO.TextWriter throughout
+;; Other global replaces: .write => .Write, .append => .Write, #^Class => #^Type, #^Character => #^Char
+(def
+ #^{:doc "*print-length* controls how many items of each collection the
+ printer will print. If it is bound to logical false, there is no
+ limit. Otherwise, it must be bound to an integer indicating the maximum
+ number of items of each collection to print. If a collection contains
+ more items, the printer will print items up to the limit followed by
+ '...' to represent the remaining items. The root binding is nil
+ indicating no limit."}
+ *print-length* nil)
+
+(def
+ #^{:doc "*print-level* controls how many levels deep the printer will
+ print nested objects. If it is bound to logical false, there is no
+ limit. Otherwise, it must be bound to an integer indicating the maximum
+ level to print. Each argument to print is at level 0; if an argument is a
+ collection, its items are at level 1; and so on. If an object is a
+ collection and is at a level greater than or equal to the value bound to
+ *print-level*, the printer prints '#' to represent it. The root binding
+ is nil indicating no limit."}
+*print-level* nil)
+
+(defn- print-sequential [#^String begin, print-one, #^String sep, #^String end, sequence, #^System.IO.TextWriter w]
+ (binding [*print-level* (and (not *print-dup*) *print-level* (dec *print-level*))]
+ (if (and *print-level* (neg? *print-level*))
+ (.Write w "#")
+ (do
+ (.Write w begin)
+ (when-let [xs (seq sequence)]
+ (if (and (not *print-dup*) *print-length*)
+ (loop [[x & xs] xs
+ print-length *print-length*]
+ (if (zero? print-length)
+ (.Write w "...")
+ (do
+ (print-one x w)
+ (when xs
+ (.Write w sep)
+ (recur xs (dec print-length))))))
+ (loop [[x & xs] xs]
+ (print-one x w)
+ (when xs
+ (.Write w sep)
+ (recur xs)))))
+ (.Write w end)))))
+
+(defn- print-meta [o, #^System.IO.TextWriter w]
+ (when-let [m (meta o)]
+ (when (and (pos? (count m))
+ (or *print-dup*
+ (and *print-meta* *print-readably*)))
+ (.Write w "#^")
+ (if (and (= (count m) 1) (:tag m))
+ (pr-on (:tag m) w)
+ (pr-on m w))
+ (.Write w " "))))
+
+(defmethod print-method nil [o, #^System.IO.TextWriter w]
+ (.Write w "nil"))
+
+(defmethod print-dup nil [o w] (print-method o w))
+
+(defn print-ctor [o print-args #^System.IO.TextWriter w]
+ (.Write w "#=(")
+ (.Write w (.FullName #^Type (class o))) ;;; .getName => .FullName
+ (.Write w ". ")
+ (print-args o w)
+ (.Write w ")"))
+
+(defmethod print-method :default [o, #^System.IO.TextWriter w]
+ (.Write w "#<")
+ (.Write w (.Name (class o))) ;;; .getSimpleName => .Name
+ (.Write w " ")
+ (.Write w (str o))
+ (.Write w ">"))
+
+(defmethod print-method clojure.lang.Keyword [o, #^System.IO.TextWriter w]
+ (.Write w (str o)))
+
+(defmethod print-dup clojure.lang.Keyword [o w] (print-method o w))
+;;; MAJOR PROBLEM: no Number type in CLR. We will just ask every ValueType to print itself. Need to deal with BigDecimal and BigInteger later.
+(defmethod print-method ValueType [o, #^System.IO.TextWriter w] ;; Number => ValueType
+ (.Write w (str o)))
+
+(defmethod print-dup ValueType [o, #^System.IO.TextWriter w] ;;; Number => ValueType
+ (print-ctor o
+ (fn [o w]
+ (print-dup (str o) w))
+ w))
+
+(defmethod print-dup clojure.lang.AFn [o, #^System.IO.TextWriter w]
+ (print-ctor o (fn [o w]) w))
+
+(prefer-method print-dup clojure.lang.IPersistentCollection clojure.lang.AFn)
+(prefer-method print-dup java.util.Map clojure.lang.AFn)
+(prefer-method print-dup java.util.Collection clojure.lang.AFn)
+
+(defmethod print-method Boolean [o, #^System.IO.TextWriter w]
+ (.Write w (str o)))
+
+(defmethod print-dup Boolean [o w] (print-method o w))
+
+(defn print-simple [o, #^System.IO.TextWriter w]
+ (print-meta o w)
+ (.Write w (str o)))
+
+(defmethod print-method clojure.lang.Symbol [o, #^System.IO.TextWriter w]
+ (print-simple o w))
+
+(defmethod print-dup clojure.lang.Symbol [o w] (print-method o w))
+
+(defmethod print-method clojure.lang.Var [o, #^System.IO.TextWriter w]
+ (print-simple o w))
+
+(defmethod print-dup clojure.lang.Var [#^clojure.lang.Var o, #^System.IO.TextWriter w]
+ (.Write w (str "#=(var " (.name (.ns o)) "/" (.sym o) ")"))) ;;; .name => .Name, .sym => .Symbol
+
+(defmethod print-method clojure.lang.ISeq [o, #^System.IO.TextWriter w]
+ (print-meta o w)
+ (print-sequential "(" pr-on " " ")" o w))
+
+(defmethod print-dup clojure.lang.ISeq [o w] (print-method o w))
+(defmethod print-dup clojure.lang.IPersistentList [o w] (print-method o w))
+(prefer-method print-method clojure.lang.IPersistentList clojure.lang.ISeq)
+(prefer-method print-dup clojure.lang.IPersistentList clojure.lang.ISeq)
+
+(defmethod print-method clojure.lang.IPersistentList [o, #^System.IO.TextWriter w]
+ (print-meta o w)
+ (print-sequential "(" print-method " " ")" o w))
+
+
+(defmethod print-dup System.Collections.ICollection [o, #^System.IO.TextWriter w] ;; java.util.Collection => System.Collections.ICollection
+ (print-ctor o #(print-sequential "[" print-method " " "]" %1 %2) w))
+
+(defmethod print-dup clojure.lang.IPersistentCollection [o, #^System.IO.TextWriter w]
+ (print-meta o w)
+ (.Write w "#=(")
+ (.Write w (.FullName #^Type (class o))) ;; .getName => .FullName
+ (.Write w "/create ")
+ (print-sequential "[" print-dup " " "]" o w)
+ (.Write w ")"))
+
+(prefer-method print-dup clojure.lang.IPersistentCollection System.Collections.ICollection) ;; java.util.Collection => System.Collections.ICollection
+
+(def #^{:tag String
+ :doc "Returns escape string for char or nil if none"}
+ char-escape-string
+ {\newline "\\n"
+ \tab "\\t"
+ \return "\\r"
+ \" "\\\""
+ \\ "\\\\"
+ \formfeed "\\f"
+ \backspace "\\b"})
+
+(defmethod print-method String [#^String s, #^System.IO.TextWriter w]
+ (if (or *print-dup* *print-readably*)
+ (do (.Write w \")
+ (dotimes [n (count s)]
+ (let [c (.get_Chars s n) ;; .charAt => .get_Chars
+ e (char-escape-string c)]
+ (if e (.Write w e) (.Write w c))))
+ (.Write w \"))
+ (.write w s))
+ nil)
+
+(defmethod print-dup String [s w] (print-method s w))
+
+(defmethod print-method clojure.lang.IPersistentVector [v, #^System.IO.TextWriter w]
+ (print-meta v w)
+ (print-sequential "[" pr-on " " "]" v w))
+
+(defn- print-map [m print-one w]
+ (print-sequential
+ "{"
+ (fn [e #^System.IO.TextWriter w]
+ (do (print-one (key e) w) (.Write w \space) (print-one (val e) w)))
+ ", "
+ "}"
+ (seq m) w))
+
+(defmethod print-method clojure.lang.IPersistentMap [m, #^System.IO.TextWriter w]
+ (print-meta m w)
+ (print-map m pr-on w))
+
+(defmethod print-dup java.util.Map [m, #^System.IO.TextWriter w]
+ (print-ctor m #(print-map (seq %1) print-method %2) w))
+
+(defmethod print-dup clojure.lang.IPersistentMap [m, #^System.IO.TextWriter w]
+ (print-meta m w)
+ (.Write w "#=(")
+ (.Write w (.FullName (class m))) ;; .getName => .FullName
+ (.Write w "/create ")
+ (print-map m print-dup w)
+ (.Write w ")"))
+
+(prefer-method print-dup clojure.lang.IPersistentMap System.Collections.IDictionary) ;; java.util.Map -> System.Collections.IDictionary
+
+(defmethod print-method clojure.lang.IPersistentSet [s, #^System.IO.TextWriter w]
+ (print-meta s w)
+ (print-sequential "#{" pr-on " " "}" (seq s) w))
+
+(def #^{:tag String
+ :doc "Returns name string for char or nil if none"}
+ char-name-string
+ {\newline "newline"
+ \tab "tab"
+ \space "space"
+ \backspace "backspace"
+ \formfeed "formfeed"
+ \return "return"})
+
+(defmethod print-method Char [#^Char c, #^System.IO.TextWriter w]
+ (if (or *print-dup* *print-readably*)
+ (do (.Write w \\)
+ (let [n (char-name-string c)]
+ (if n (.Write w n) (.Write w c))))
+ (.Write w c))
+ nil)
+
+(defmethod print-dup Char [c w] (print-method c w)) ;;; java.lang.Character
+(defmethod print-dup Int32 [o w] (print-method o w)) ;;; java.lang.Integer
+(defmethod print-dup Double [o w] (print-method o w)) ;;; java.lang.Double
+(defmethod print-dup clojure.lang.Ratio [o w] (print-method o w))
+(defmethod print-dup java.math.BigDecimal [o w] (print-method o w))
+(defmethod print-dup clojure.lang.PersistentHashMap [o w] (print-method o w))
+(defmethod print-dup clojure.lang.PersistentHashSet [o w] (print-method o w))
+(defmethod print-dup clojure.lang.PersistentVector [o w] (print-method o w))
+(defmethod print-dup clojure.lang.LazilyPersistentVector [o w] (print-method o w))
+
+(def primitives-classnames ;; not clear what the equiv should be
+ {Single "Single" ;;{Float/TYPE "Float/TYPE"
+ Int32 "Int32" ;; Integer/TYPE "Integer/TYPE"
+ Int64 "Int64" ;; Long/TYPE "Long/TYPE"
+ Boolean "Boolean" ;; Boolean/TYPE "Boolean/TYPE"
+ Char "Char" ;; Character/TYPE "Character/TYPE"
+ Double "Double" ;; Double/TYPE "Double/TYPE"
+ Byte "Byte" ;; Byte/TYPE "Byte/TYPE"
+ Int16 "Int16"}) ;; Short/TYPE "Short/TYPE"})
+
+(defmethod print-method Type [#^Type c, #^System.IO.TextWriter w]
+ (.Write w (.FullName c))) ;;; .getName => .FullName
+
+(defmethod print-dup Type [#^Type c, #^System.IO.TextWriter w]
+ (cond
+ (.IsPrimitive c) (do ;; .isPrimitive
+ (.Write w "#=(identity ")
+ (.Write w #^String (primitives-classnames c))
+ (.Write w ")"))
+ (.IsArray c) (do ;; .isArray , java.lang.Class/forName =>
+ (.Write w "#=(clojure.lang.RT/classForName \"")
+ (.Write w (.FullName c)) ;; .getName => .FullName
+ (.Write w "\")"))
+ :else (do
+ (.Write w "#=")
+ (.Write w (.FullName c))))) ;;; .getName => .FullName
+
+(defmethod print-method java.math.BigDecimal [b, #^System.IO.TextWriter w]
+ (.Write w (str b))
+ (.Write w "M"))
+
+(defmethod print-method System.Text.RegularExpressions.Regex [p #^System.IO.TextWriter w] ;;; java.util.regex.Pattern =>
+ (.write w "#\"")
+ (loop [[#^Char c & r :as s] (seq (.ToString #^System.Text.RegularExpressions.Regex p)) ;;; .pattern => .ToString
+ qmode false]
+ (when s
+ (cond
+ (= c \\) (let [[#^Char c2 & r2] r]
+ (.Write w \\)
+ (.Write w c2)
+ (if qmode
+ (recur r2 (not= c2 \E))
+ (recur r2 (= c2 \Q))))
+ (= c \") (do
+ (if qmode
+ (.Write w "\\E\\\"\\Q")
+ (.Write w "\\\""))
+ (recur r qmode))
+ :else (do
+ (.Write w c)
+ (recur r qmode)))))
+ (.Write w \"))
+
+(defmethod print-dup System.Text.RegularExpressions.Regex [p #^System.IO.TextWriter w] (print-method p w)) ;;; java.util.regex.Pattern =>
+
+(defmethod print-dup clojure.lang.Namespace [#^clojure.lang.Namespace n #^System.IO.TextWriter w]
+ (.Write w "#=(find-ns ")
+ (print-dup (.Name n) w) ;; .name
+ (.Write w ")"))
+
+(defmethod print-method clojure.lang.IDeref [o #^System.IO.TextWriter w]
+ (print-sequential (format "#<%s@%x: "
+ (.Name (class o)) ;;; .getSimpleName => .Name
+ (.GetHashCode o)) ;;; No easy equivelent in CLR: (System/identityHashCode o)))
+ pr-on, "", ">", (list @o), w))
+
+(def #^{:private true} print-initialized true)
\ No newline at end of file diff --git a/ClojureCLR/Clojure/Clojure/Bootstrap/core.clj b/ClojureCLR/Clojure/Clojure/Bootstrap/core.clj new file mode 100644 index 00000000..5a1c829b --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Bootstrap/core.clj @@ -0,0 +1,3969 @@ +; Copyright (c) Rich Hickey. 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.
+
+(in-ns 'clojure.core)
+
+(def unquote)
+(def unquote-splicing)
+
+(def
+ #^{:arglists '([& items])
+ :doc "Creates a new list containing the items."}
+ list (. clojure.lang.PersistentList creator))
+
+(def
+ #^{:arglists '([x seq])
+ :doc "Returns a new seq where x is the first element and seq is
+ the rest."}
+
+ cons (fn* cons [x seq] (. clojure.lang.RT (cons x seq))))
+
+ ;during bootstrap we don't have destructuring let, loop or fn, will redefine later
+(def
+ #^{:macro true}
+ let (fn* let [& decl] (cons 'let* decl)))
+
+(def
+ #^{:macro true}
+ loop (fn* loop [& decl] (cons 'loop* decl)))
+
+ (def
+ #^{:macro true}
+ fn (fn* fn [& decl] (cons 'fn* decl)))
+
+(def
+ #^{:arglists '([coll])
+ :doc "Returns the first item in the collection. Calls seq on its
+ argument. If coll is nil, returns nil."}
+ first (fn first [coll] (. clojure.lang.RT (first coll))))
+
+(def
+ #^{:arglists '([coll])
+ :doc "Returns a seq of the items after the first. Calls seq on its
+ argument. If there are no more items, returns nil."}
+ rest (fn rest [x] (. clojure.lang.RT (rest x))))
+
+(def
+ #^{:arglists '([coll x] [coll x & xs])
+ :doc "conj[oin]. Returns a new collection with the xs
+ 'added'. (conj nil item) returns (item). The 'addition' may
+ happen at different 'places' depending on the concrete type."}
+ conj (fn conj
+ ([coll x] (. clojure.lang.RT (conj coll x)))
+ ([coll x & xs]
+ (if xs
+ (recur (conj coll x) (first xs) (rest xs))
+ (conj coll x)))))
+
+(def
+ #^{:doc "Same as (first (rest x))"
+ :arglists '([x])}
+ second (fn second [x] (first (rest x))))
+
+(def
+ #^{:doc "Same as (first (first x))"
+ :arglists '([x])}
+ ffirst (fn ffirst [x] (first (first x))))
+
+(def
+ #^{:doc "Same as (rest (first x))"
+ :arglists '([x])}
+ rfirst (fn rfirst [x] (rest (first x))))
+
+(def
+ #^{:doc "Same as (first (rest x))"
+ :arglists '([x])}
+ frest (fn frest [x] (first (rest x))))
+
+(def
+ #^{:doc "Same as (rest (rest x))"
+ :arglists '([x])}
+ rrest (fn rrest [x] (rest (rest x))))
+
+(def
+ #^{:arglists '([coll])
+ :doc "Sequence. Returns a new ISeq on the collection. If the
+ collection is empty, returns nil. (seq nil) returns nil. seq also
+ works on Strings, native Java arrays (of reference types) and any
+ objects that implement Iterable."
+ :tag clojure.lang.ISeq}
+ seq (fn seq [coll] (. clojure.lang.RT (seq coll))))
+
+(def ;;; Had do change Class to Type and isInstance to IsInstanceOfType
+ #^{:arglists '([#^Class c x])
+ :doc "Evaluates x and tests if it is an instance of the class
+ c. Returns true or false"}
+ instance? (fn instance? [#^Type c x] (. c (IsInstanceOfType x))))
+
+(def
+ #^{:arglists '([x])
+ :doc "Return true if x implements ISeq"}
+ seq? (fn seq? [x] (instance? clojure.lang.ISeq x)))
+
+(def
+ #^{:arglists '([x])
+ :doc "Return true if x is a String"}
+ string? (fn string? [x] (instance? String x)))
+
+(def
+ #^{:arglists '([x])
+ :doc "Return true if x implements IPersistentMap"}
+ map? (fn map? [x] (instance? clojure.lang.IPersistentMap x)))
+
+(def
+ #^{:arglists '([x])
+ :doc "Return true if x implements IPersistentVector "}
+ vector? (fn vector? [x] (instance? clojure.lang.IPersistentVector x)))
+
+(def
+ #^{:private true}
+ sigs
+ (fn [fdecl]
+ (if (seq? (first fdecl))
+ (loop [ret [] fdecl fdecl]
+ (if fdecl
+ (recur (conj ret (first (first fdecl))) (rest fdecl))
+ (seq ret)))
+ (list (first fdecl)))))
+
+(def
+ #^{:arglists '([map key val] [map key val & kvs])
+ :doc "assoc[iate]. When applied to a map, returns a new map of the
+ same (hashed/sorted) type, that contains the mapping of key(s) to
+ val(s). When applied to a vector, returns a new vector that
+ contains val at index. Note - index must be <= (count vector)."}
+ assoc
+ (fn assoc
+ ([map key val] (. clojure.lang.RT (assoc map key val)))
+ ([map key val & kvs]
+ (let [ret (assoc map key val)]
+ (if kvs
+ (recur ret (first kvs) (second kvs) (rrest kvs))
+ ret)))))
+
+;;;;;;;;;;;;;;;;; metadata ;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(def
+ #^{:arglists '([obj])
+ :doc "Returns the metadata of obj, returns nil if there is no metadata."}
+ meta (fn meta [x]
+ (if (instance? clojure.lang.IMeta x)
+ (. #^clojure.lang.IMeta x (meta)))))
+
+(def
+ #^{:arglists '([#^clojure.lang.IObj obj m])
+ :doc "Returns an object of the same type and value as obj, with
+ map m as its metadata."}
+ with-meta (fn with-meta [#^clojure.lang.IObj x m]
+ (. x (withMeta m))))
+
+(def
+ #^{:arglists '([coll])
+ :doc "Return the last item in coll, in linear time"}
+ last (fn last [s]
+ (if (rest s)
+ (recur (rest s))
+ (first s))))
+
+(def
+ #^{:arglists '([coll])
+ :doc "Return a sequence of all but the last item in coll, in linear time"}
+ butlast (fn butlast [s]
+ (loop [ret [] s s]
+ (if (rest s)
+ (recur (conj ret (first s)) (rest s))
+ (seq ret)))))
+
+(def
+
+ #^{:doc "Same as (def name (fn [params* ] exprs*)) or (def
+ name (fn ([params* ] exprs*)+)) with any doc-string or attrs added
+ to the var metadata"
+ :arglists '([name doc-string? attr-map? [params*] body]
+ [name doc-string? attr-map? ([params*] body)+ attr-map?])}
+ defn (fn defn [name & fdecl]
+ (let [m (if (string? (first fdecl))
+ {:doc (first fdecl)}
+ {})
+ fdecl (if (string? (first fdecl))
+ (rest fdecl)
+ fdecl)
+ m (if (map? (first fdecl))
+ (conj m (first fdecl))
+ m)
+ fdecl (if (map? (first fdecl))
+ (rest fdecl)
+ fdecl)
+ fdecl (if (vector? (first fdecl))
+ (list fdecl)
+ fdecl)
+ m (if (map? (last fdecl))
+ (conj m (last fdecl))
+ m)
+ fdecl (if (map? (last fdecl))
+ (butlast fdecl)
+ fdecl)
+ m (conj {:arglists (list 'quote (sigs fdecl))} m)]
+ (list 'def (with-meta name (conj (if (meta name) (meta name) {}) m))
+ (cons `fn fdecl)))))
+
+(. (var defn) (setMacro))
+;;; The following didn't work, I've handled the few uses below as special cases.
+;;;(defn cast
+;;; "Throws a ClassCastException if x is not a c, else returns x."
+;;; [#^Type c x] ;;; changed Class to Type
+;;; (. clojure.lang.RT (Cast c x))) ;;; original (. c (cast x)))
+
+(defn to-array
+ "Returns an array of Objects containing the contents of coll, which
+ can be any Collection. Maps to java.util.Collection.toArray()."
+ {:tag "Object[]" } ;;;{:tag "[Ljava.lang.Object;"}
+ [coll] (. clojure.lang.RT (toArray coll)))
+
+(defn vector
+ "Creates a new vector containing the args."
+ ([] [])
+ ([& args]
+ (. clojure.lang.LazilyPersistentVector (create args))))
+
+(defn vec
+ "Creates a new vector containing the contents of coll."
+ ([coll]
+ (. clojure.lang.LazilyPersistentVector (createOwning (to-array coll)))))
+
+(defn hash-map
+ "keyval => key val
+ Returns a new hash map with supplied mappings."
+ ([] {})
+ ([& keyvals]
+ (. clojure.lang.PersistentHashMap (create keyvals))))
+
+(defn hash-set
+ "Returns a new hash set with supplied keys."
+ ([] #{})
+ ([& keys]
+ (. clojure.lang.PersistentHashSet (create keys))))
+
+(defn sorted-map
+ "keyval => key val
+ Returns a new sorted map with supplied mappings."
+ ([] (. clojure.lang.PersistentTreeMap EMPTY)) ;;; I HAD TO ADD THIS EXTRA CASE TO AVOID AMBIGUOUS CALL TO CREATE WITH NULL
+ ([& keyvals] (. clojure.lang.PersistentTreeMap (create keyvals))))
+
+(defn sorted-set
+ "Returns a new sorted set with supplied keys."
+ ([] (. clojure.lang.PersistentTreeSet EMPTY)) ;;; I HAD TO ADD THIS EXTRA CASE TO AVOID AMBIGUOUS CALL TO CREATE WITH NULL
+ ([& keys] (. clojure.lang.PersistentTreeSet (create keys))))
+
+(defn sorted-map-by
+ "keyval => key val
+ Returns a new sorted map with supplied mappings, using the supplied comparator."
+ ([comparator & keyvals]
+ (. clojure.lang.PersistentTreeMap (create comparator keyvals))))
+
+;;;;;;;;;;;;;;;;;;;;
+(def
+
+ #^{:doc "Like defn, but the resulting function name is declared as a
+ macro and will be used as a macro by the compiler when it is
+ called."
+ :arglists '([name doc-string? attr-map? [params*] body]
+ [name doc-string? attr-map? ([params*] body)+ attr-map?])}
+ defmacro (fn [name & args]
+ (list 'do
+ (cons `defn (cons name args))
+ (list '. (list 'var name) '(setMacro))
+ (list 'var name))))
+
+(. (var defmacro) (setMacro))
+
+(defmacro when
+ "Evaluates test. If logical true, evaluates body in an implicit do."
+ [test & body]
+ (list 'if test (cons 'do body)))
+
+(defmacro when-not
+ "Evaluates test. If logical false, evaluates body in an implicit do."
+ [test & body]
+ (list 'if test nil (cons 'do body)))
+
+(defn nil?
+ "Returns true if x is nil, false otherwise."
+ {:tag Boolean}
+ [x] (identical? x nil))
+
+(defn false?
+ "Returns true if x is the value false, false otherwise."
+ {:tag Boolean}
+ [x] (identical? x false))
+
+(defn true?
+ "Returns true if x is the value true, false otherwise."
+ {:tag Boolean}
+ [x] (identical? x true))
+
+(defn not
+ "Returns true if x is logical false, false otherwise."
+ {:tag Boolean}
+ [x] (if x false true))
+
+(defn str
+ "With no args, returns the empty string. With one arg x, returns
+ x.toString(). (str nil) returns the empty string. With more than
+ one arg, returns the concatenation of the str values of the args."
+ {:tag String}
+ ([] "")
+ ([#^Object x]
+ (if (nil? x) "" (. x (ToString)))) ;; java: toString
+ ([x & ys]
+ ((fn [#^StringBuilder sb more]
+ (if more
+ (recur (. sb (Append (str (first more)))) (rest more)) ;; java: append
+ (str sb)))
+ (new StringBuilder #^String (str x)) ys)))
+
+
+(defn symbol?
+ "Return true if x is a Symbol"
+ [x] (instance? clojure.lang.Symbol x))
+
+(defn keyword?
+ "Return true if x is a Keyword"
+ [x] (instance? clojure.lang.Keyword x))
+
+(defn symbol
+ "Returns a Symbol with the given namespace and name."
+ ([name] (if (symbol? name) name (. clojure.lang.Symbol (intern name))))
+ ([ns name] (. clojure.lang.Symbol (intern ns name))))
+
+(defn keyword
+ "Returns a Keyword with the given namespace and name. Do not use :
+ in the keyword strings, it will be added automatically."
+ ([name] (if (keyword? name) name (. clojure.lang.Keyword (intern nil name))))
+ ([ns name] (. clojure.lang.Keyword (intern ns name))))
+
+(defn gensym
+ "Returns a new symbol with a unique name. If a prefix string is
+ supplied, the name is prefix# where # is some unique number. If
+ prefix is not supplied, the prefix is 'G__'."
+ ([] (gensym "G__"))
+ ([prefix-string] (. clojure.lang.Symbol (intern (str prefix-string (str (. clojure.lang.RT (nextID))))))))
+
+(defmacro cond
+ "Takes a set of test/expr pairs. It evaluates each test one at a
+ time. If a test returns logical true, cond evaluates and returns
+ the value of the corresponding expr and doesn't evaluate any of the
+ other tests or exprs. (cond) returns nil."
+ [& clauses]
+ (when clauses
+ (list 'if (first clauses)
+ (if (rest clauses) + (second clauses) + (throw (ArgumentException. ;;;IllegalArgumentException. + "cond requires an even number of forms"))) + (cons 'clojure.core/cond (rest (rest clauses))))))
+
+(defn spread
+ {:private true}
+ [arglist]
+ (cond
+ (nil? arglist) nil
+ (nil? (rest arglist)) (seq (first arglist))
+ :else (cons (first arglist) (spread (rest arglist)))))
+
+(defn apply
+ "Applies fn f to the argument list formed by prepending args to argseq."
+ { :arglists '([f args* argseq])}
+ [#^clojure.lang.IFn f & args] + (. f (applyTo (spread args)))) +
+(defn vary-meta
+ "Returns an object of the same type and value as obj, with
+ (apply f (meta obj) args) as its metadata."
+ [obj f & args]
+ (with-meta obj (apply f (meta obj) args)))
+
+(defn list*
+ "Creates a new list containing the item prepended to more."
+ [item & more]
+ (spread (cons item more)))
+
+(defmacro delay
+ "Takes a body of expressions and yields a Delay object than will
+ invoke the body only the first time it is forced (with force), and
+ will cache the result and return it on all subsequent force calls"
+ [& body]
+ (list 'new 'clojure.lang.Delay (list* `fn [] body)))
+
+(defn delay?
+ "returns true if x is a Delay created with delay"
+ [x] (instance? clojure.lang.Delay x))
+
+(defn force
+ "If x is a Delay, returns the (possibly cached) value of its expression, else returns x"
+ [x] (. clojure.lang.Delay (force x)))
+
+(defn fnseq
+ "Returns a seq object whose first is first and whose rest is the
+ value produced by calling restfn with no arguments. restfn will be
+ called at most once per step in the sequence, e.g. calling rest
+ repeatedly on the head of the seq calls restfn once - the value it
+ yields is cached."
+ [ first restfn]
+ (new clojure.lang.FnSeq first restfn))
+
+(defmacro lazy-cons
+ "Expands to code which produces a seq object whose first is
+ first-expr and whose rest is rest-expr, neither of which is
+ evaluated until first/rest is called. Each expr will be evaluated at most
+ once per step in the sequence, e.g. calling first/rest repeatedly on the
+ same node of the seq evaluates first/rest-expr once - the values they yield are
+ cached."
+ [first-expr & rest-expr]
+ (list 'new 'clojure.lang.LazyCons (list `fn (list [] first-expr) (list* [(gensym)] rest-expr))))
+
+;(defmacro lazy-seq ;;; THIS IS COMMENTED OUT IN THE JAVA VERSION
+; "Expands to code which produces a seq object whose first is the
+; value of first-expr and whose rest is the value of rest-expr,
+; neither of which is evaluated until first/rest is called. Each expr
+; will be evaluated every step in the sequence, e.g. calling
+; first/rest repeatedly on the same node of the seq evaluates
+; first/rest-expr repeatedly - the values they yield are not cached."
+; [first-expr rest-expr]
+; (list 'new 'clojure.lang.LazySeq (list `fn (list [] first-expr) (list [(gensym)] rest-expr))))
+
+(defn cache-seq
+ "Given a seq s, returns a lazy seq that will touch each element of s
+ at most once, caching the results."
+ [s] (when s (clojure.lang.CachedSeq. s)))
+
+(defn concat
+ "Returns a lazy seq representing the concatenation of the elements in the supplied colls."
+ ([] nil)
+ ([x] (seq x))
+ ([x y]
+ (if (seq x)
+ (lazy-cons (first x) (concat (rest x) y))
+ (seq y)))
+ ([x y & zs]
+ (let [cat (fn cat [xys zs]
+ (if (seq xys)
+ (lazy-cons (first xys) (cat (rest xys) zs))
+ (when zs
+ (recur (first zs) (rest zs)))))]
+ (cat (concat x y) zs))))
+
+;;;;;;;;;;;;;;;;at this point all the support for syntax-quote exists;;;;;;;;;;;;;;;;;;;;;;
+(defmacro if-not + "Evaluates test. If logical false, evaluates and returns then expr, otherwise else expr, if supplied, else nil." + ([test then] `(if-not ~test ~then nil)) + ([test then else] + `(if (not ~test) ~then ~else)))
+
+(defn =
+ "Equality. Returns true if x equals y, false if not. Same as
+ Java x.equals(y) except it also works for nil, and compares
+ numbers and collections in a type-independent manner. Clojure's immutable data
+ structures define equals() (and thus =) as a value, not an identity,
+ comparison."
+ {:tag Boolean
+ :inline (fn [x y] `(. clojure.lang.Util equiv ~x ~y))
+ :inline-arities #{2}}
+ ([x] true)
+ ([x y] (clojure.lang.Util/equiv x y))
+ ([x y & more]
+ (if (= x y)
+ (if (rest more)
+ (recur y (first more) (rest more))
+ (= y (first more)))
+ false)))
+
+(defn not=
+ "Same as (not (= obj1 obj2))"
+ {:tag Boolean}
+ ([x] false)
+ ([x y] (not (= x y)))
+ ([x y & more]
+ (not (apply = x y more))))
+
+
+
+(defn compare
+ "Comparator. Returns 0 if x equals y, -1 if x is logically 'less
+ than' y, else 1. Same as Java x.compareTo(y) except it also works
+ for nil, and compares numbers and collections in a type-independent
+ manner. x must implement Comparable"
+ {:tag Int32 ; was Integer
+ :inline (fn [x y] `(. clojure.lang.Util compare ~x ~y))}
+ [x y] (. clojure.lang.Util (compare x y)))
+
+(defmacro and
+ "Evaluates exprs one at a time, from left to right. If a form
+ returns logical false (nil or false), and returns that value and
+ doesn't evaluate any of the other expressions, otherwise it returns
+ the value of the last expr. (and) returns true."
+ ([] true)
+ ([x] x)
+ ([x & rest]
+ `(let [and# ~x]
+ (if and# (and ~@rest) and#))))
+
+(defmacro or
+ "Evaluates exprs one at a time, from left to right. If a form
+ returns a logical true value, or returns that value and doesn't
+ evaluate any of the other expressions, otherwise it returns the
+ value of the last expression. (or) returns nil."
+ ([] nil)
+ ([x] x)
+ ([x & rest]
+ `(let [or# ~x]
+ (if or# or# (or ~@rest)))))
+
+;;;;;;;;;;;;;;;;;;; sequence fns ;;;;;;;;;;;;;;;;;;;;;;;
+(defn reduce
+ "f should be a function of 2 arguments. If val is not supplied,
+ returns the result of applying f to the first 2 items in coll, then
+ applying f to that result and the 3rd item, etc. If coll contains no
+ items, f must accept no arguments as well, and reduce returns the
+ result of calling f with no arguments. If coll has only 1 item, it
+ is returned and f is not called. If val is supplied, returns the
+ result of applying f to val and the first item in coll, then
+ applying f to that result and the 2nd item, etc. If coll contains no
+ items, returns val and f is not called."
+ ([f coll]
+ (let [s (seq coll)]
+ (if s
+ (if (instance? clojure.lang.IReduce s)
+ (. #^clojure.lang.IReduce s (reduce f))
+ (reduce f (first s) (rest s)))
+ (f))))
+ ([f val coll]
+ (let [s (seq coll)]
+ (if (instance? clojure.lang.IReduce s)
+ (. #^clojure.lang.IReduce s (reduce f val))
+ ((fn [f val s]
+ (if s
+ (recur f (f val (first s)) (rest s))
+ val))
+ f val s)))))
+
+(defn reverse
+ "Returns a seq of the items in coll in reverse order. Not lazy."
+ [coll]
+ (reduce conj nil coll))
+
+;;math stuff
+(defn +
+ "Returns the sum of nums. (+) returns 0."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (add ~x ~y)))
+ :inline-arities #{2}}
+ ([] 0)
+ ([x] (. clojure.lang.RT (NumberCast x))) ;; (cast Number x))
+ ([x y] (. clojure.lang.Numbers (add x y)))
+ ([x y & more]
+ (reduce + (+ x y) more)))
+
+(defn *
+ "Returns the product of nums. (*) returns 1."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (multiply ~x ~y)))
+ :inline-arities #{2}}
+ ([] 1)
+ ([x] (. clojure.lang.RT (NumberCast x))) ;; (cast Number x))
+ ([x y] (. clojure.lang.Numbers (multiply x y)))
+ ([x y & more]
+ (reduce * (* x y) more)))
+
+(defn /
+ "If no denominators are supplied, returns 1/numerator,
+ else returns numerator divided by all of the denominators."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (divide ~x ~y)))
+ :inline-arities #{2}}
+ ([x] (/ 1 x))
+ ([x y] (. clojure.lang.Numbers (divide x y)))
+ ([x y & more]
+ (reduce / (/ x y) more)))
+
+(defn -
+ "If no ys are supplied, returns the negation of x, else subtracts
+ the ys from x and returns the result."
+ {:inline (fn [& args] `(. clojure.lang.Numbers (minus ~@args)))
+ :inline-arities #{1 2}}
+ ([x] (. clojure.lang.Numbers (minus x)))
+ ([x y] (. clojure.lang.Numbers (minus x y)))
+ ([x y & more]
+ (reduce - (- x y) more)))
+
+(defn <
+ "Returns non-nil if nums are in monotonically increasing order,
+ otherwise false."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (lt ~x ~y)))
+ :inline-arities #{2}}
+ ([x] true)
+ ([x y] (. clojure.lang.Numbers (lt x y)))
+ ([x y & more]
+ (if (< x y)
+ (if (rest more)
+ (recur y (first more) (rest more))
+ (< y (first more)))
+ false)))
+
+(defn <=
+ "Returns non-nil if nums are in monotonically non-decreasing order,
+ otherwise false."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (lte ~x ~y)))
+ :inline-arities #{2}}
+ ([x] true)
+ ([x y] (. clojure.lang.Numbers (lte x y)))
+ ([x y & more]
+ (if (<= x y)
+ (if (rest more)
+ (recur y (first more) (rest more))
+ (<= y (first more)))
+ false)))
+
+(defn >
+ "Returns non-nil if nums are in monotonically decreasing order,
+ otherwise false."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (gt ~x ~y)))
+ :inline-arities #{2}}
+ ([x] true)
+ ([x y] (. clojure.lang.Numbers (gt x y)))
+ ([x y & more]
+ (if (> x y)
+ (if (rest more)
+ (recur y (first more) (rest more))
+ (> y (first more)))
+ false)))
+
+(defn >=
+ "Returns non-nil if nums are in monotonically non-increasing order,
+ otherwise false."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (gte ~x ~y)))
+ :inline-arities #{2}}
+ ([x] true)
+ ([x y] (. clojure.lang.Numbers (gte x y)))
+ ([x y & more]
+ (if (>= x y)
+ (if (rest more)
+ (recur y (first more) (rest more))
+ (>= y (first more)))
+ false)))
+
+(defn ==
+ "Returns non-nil if nums all have the same value, otherwise false"
+ {:inline (fn [x y] `(. clojure.lang.Numbers (equiv ~x ~y)))
+ :inline-arities #{2}}
+ ([x] true)
+ ([x y] (. clojure.lang.Numbers (equiv x y)))
+ ([x y & more]
+ (if (== x y)
+ (if (rest more)
+ (recur y (first more) (rest more))
+ (== y (first more)))
+ false)))
+
+(defn max
+ "Returns the greatest of the nums."
+ ([x] x)
+ ([x y] (if (> x y) x y))
+ ([x y & more]
+ (reduce max (max x y) more)))
+
+(defn min
+ "Returns the least of the nums."
+ ([x] x)
+ ([x y] (if (< x y) x y))
+ ([x y & more]
+ (reduce min (min x y) more)))
+
+(defn inc
+ "Returns a number one greater than num."
+ {:inline (fn [x] `(. clojure.lang.Numbers (inc ~x)))}
+ [x] (. clojure.lang.Numbers (inc x)))
+
+(defn dec
+ "Returns a number one less than num."
+ {:inline (fn [x] `(. clojure.lang.Numbers (dec ~x)))}
+ [x] (. clojure.lang.Numbers (dec x)))
+
+(defn unchecked-inc
+ "Returns a number one greater than x, an int or long.
+ Note - uses a primitive operator subject to overflow."
+ {:inline (fn [x] `(. clojure.lang.Numbers (unchecked_inc ~x)))}
+ [x] (. clojure.lang.Numbers (unchecked_inc x)))
+
+(defn unchecked-dec
+ "Returns a number one less than x, an int or long.
+ Note - uses a primitive operator subject to overflow."
+ {:inline (fn [x] `(. clojure.lang.Numbers (unchecked_dec ~x)))}
+ [x] (. clojure.lang.Numbers (unchecked_dec x)))
+
+(defn unchecked-negate
+ "Returns the negation of x, an int or long.
+ Note - uses a primitive operator subject to overflow."
+ {:inline (fn [x] `(. clojure.lang.Numbers (unchecked_negate ~x)))}
+ [x] (. clojure.lang.Numbers (unchecked_negate x)))
+
+(defn unchecked-add
+ "Returns the sum of x and y, both int or long.
+ Note - uses a primitive operator subject to overflow."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (unchecked_add ~x ~y)))}
+ [x y] (. clojure.lang.Numbers (unchecked_add x y)))
+
+(defn unchecked-subtract
+ "Returns the difference of x and y, both int or long.
+ Note - uses a primitive operator subject to overflow."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (unchecked_subtract ~x ~y)))}
+ [x y] (. clojure.lang.Numbers (unchecked_subtract x y)))
+
+(defn unchecked-multiply
+ "Returns the product of x and y, both int or long.
+ Note - uses a primitive operator subject to overflow."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (unchecked_multiply ~x ~y)))}
+ [x y] (. clojure.lang.Numbers (unchecked_multiply x y)))
+
+(defn unchecked-divide
+ "Returns the division of x by y, both int or long.
+ Note - uses a primitive operator subject to truncation."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (unchecked_divide ~x ~y)))}
+ [x y] (. clojure.lang.Numbers (unchecked_divide x y)))
+
+(defn unchecked-remainder + "Returns the remainder of division of x by y, both int or long. + Note - uses a primitive operator subject to truncation." + {:inline (fn [x y] `(. clojure.lang.Numbers (unchecked_remainder ~x ~y)))} + [x y] (. clojure.lang.Numbers (unchecked_remainder x y))) +
+(defn pos?
+ "Returns true if num is greater than zero, else false"
+ {:tag Boolean
+ :inline (fn [x] `(. clojure.lang.Numbers (isPos ~x)))}
+ [x] (. clojure.lang.Numbers (isPos x)))
+
+(defn neg?
+ "Returns true if num is less than zero, else false"
+ {:tag Boolean
+ :inline (fn [x] `(. clojure.lang.Numbers (isNeg ~x)))}
+ [x] (. clojure.lang.Numbers (isNeg x)))
+
+(defn zero?
+ "Returns true if num is zero, else false"
+ {:tag Boolean
+ :inline (fn [x] `(. clojure.lang.Numbers (isZero ~x)))}
+ [x] (. clojure.lang.Numbers (isZero x)))
+
+(defn quot
+ "quot[ient] of dividing numerator by denominator."
+ [num div]
+ (. clojure.lang.Numbers (quotient num div)))
+
+(defn rem
+ "remainder of dividing numerator by denominator."
+ [num div]
+ (. clojure.lang.Numbers (remainder num div)))
+
+(defn rationalize
+ "returns the rational value of num"
+ [num]
+ (. clojure.lang.Numbers (rationalize num)))
+
+;;Bit ops
+
+(defn bit-not
+ "Bitwise complement"
+ {:inline (fn [x] `(. clojure.lang.Numbers (not ~x)))}
+ [x] (. clojure.lang.Numbers not x))
+
+
+(defn bit-and
+ "Bitwise and"
+ {:inline (fn [x y] `(. clojure.lang.Numbers (and ~x ~y)))}
+ [x y] (. clojure.lang.Numbers and x y))
+
+(defn bit-or
+ "Bitwise or"
+ {:inline (fn [x y] `(. clojure.lang.Numbers (or ~x ~y)))}
+ [x y] (. clojure.lang.Numbers or x y))
+
+(defn bit-xor
+ "Bitwise exclusive or"
+ {:inline (fn [x y] `(. clojure.lang.Numbers (xor ~x ~y)))}
+ [x y] (. clojure.lang.Numbers xor x y))
+
+(defn bit-and-not
+ "Bitwise and with complement"
+ [x y] (. clojure.lang.Numbers andNot x y))
+
+
+(defn bit-clear
+ "Clear bit at index n"
+ [x n] (. clojure.lang.Numbers clearBit x n))
+
+(defn bit-set
+ "Set bit at index n"
+ [x n] (. clojure.lang.Numbers setBit x n))
+
+(defn bit-flip
+ "Flip bit at index n"
+ [x n] (. clojure.lang.Numbers flipBit x n))
+
+(defn bit-test
+ "Test bit at index n"
+ [x n] (. clojure.lang.Numbers testBit x n))
+
+
+(defn bit-shift-left
+ "Bitwise shift left"
+ [x n] (. clojure.lang.Numbers shiftLeft x n))
+
+(defn bit-shift-right
+ "Bitwise shift right"
+ [x n] (. clojure.lang.Numbers shiftRight x n))
+
+(defn even?
+ "Returns true if n is even, throws an exception if n is not an integer"
+ [n] (zero? (bit-and n 1)))
+
+(defn odd?
+ "Returns true if n is odd, throws an exception if n is not an integer"
+ [n] (not (even? n)))
+
+
+;;
+
+(defn complement
+ "Takes a fn f and returns a fn that takes the same arguments as f,
+ has the same effects, if any, and returns the opposite truth value."
+ [f] (fn [& args]
+ (not (apply f args))))
+
+(defn constantly
+ "Returns a function that takes any number of arguments and returns x."
+ [x] (fn [& args] x))
+
+(defn identity
+ "Returns its argument."
+ [x] x)
+
+;;Collection stuff
+
+
+
+(defn count
+ "Returns the number of items in the collection. (count nil) returns
+ 0. Also works on strings, arrays, and Java Collections and Maps"
+ [coll] (. clojure.lang.RT (count coll)))
+
+;;list stuff
+(defn peek
+ "For a list or queue, same as first, for a vector, same as, but much
+ more efficient than, last. If the collection is empty, returns nil."
+ [coll] (. clojure.lang.RT (peek coll)))
+
+(defn pop
+ "For a list or queue, returns a new list/queue without the first
+ item, for a vector, returns a new vector without the last item. If
+ the collection is empty, throws an exception. Note - not the same
+ as rest/butlast."
+ [coll] (. clojure.lang.RT (pop coll)))
+
+(defn nth
+ "Returns the value at the index. get returns nil if index out of
+ bounds, nth throws an exception unless not-found is supplied. nth
+ also works for strings, Java arrays, regex Matchers and Lists, and,
+ in O(n) time, for sequences."
+ ([coll index] (. clojure.lang.RT (nth coll index)))
+ ([coll index not-found] (. clojure.lang.RT (nth coll index not-found))))
+
+;;map stuff
+
+(defn contains?
+ "Returns true if key is present in the given collection, otherwise + returns false. Note that for numerically indexed collections like + vectors and Java arrays, this tests if the numeric key is within the + range of indexes. 'contains?' operates constant or logarithmic time; + it will not perform a linear search for a value. See also 'some'."
+ [coll key] (. clojure.lang.RT (contains coll key))) +
+(defn get
+ "Returns the value mapped to key, not-found or nil if key not present."
+ ([map key]
+ (. clojure.lang.RT (get map key)))
+ ([map key not-found]
+ (. clojure.lang.RT (get map key not-found))))
+
+(defn dissoc
+ "dissoc[iate]. Returns a new map of the same (hashed/sorted) type,
+ that does not contain a mapping for key(s)."
+ ([map] map)
+ ([map key]
+ (. clojure.lang.RT (dissoc map key)))
+ ([map key & ks]
+ (let [ret (dissoc map key)]
+ (if ks
+ (recur ret (first ks) (rest ks))
+ ret))))
+
+(defn disj
+ "disj[oin]. Returns a new set of the same (hashed/sorted) type, that
+ does not contain key(s)."
+ ([set] set)
+ ([#^clojure.lang.IPersistentSet set key]
+ (. set (disjoin key)))
+ ([set key & ks]
+ (let [ret (disj set key)]
+ (if ks
+ (recur ret (first ks) (rest ks))
+ ret))))
+
+(defn find
+ "Returns the map entry for key, or nil if key not present."
+ [map key] (. clojure.lang.RT (find map key)))
+
+(defn select-keys
+ "Returns a map containing only those entries in map whose key is in keys"
+ [map keyseq]
+ (loop [ret {} keys (seq keyseq)]
+ (if keys
+ (let [entry (. clojure.lang.RT (find map (first keys)))]
+ (recur
+ (if entry
+ (conj ret entry)
+ ret)
+ (rest keys)))
+ ret)))
+
+(defn keys
+ "Returns a sequence of the map's keys."
+ [map] (. clojure.lang.RT (keys map)))
+
+(defn vals
+ "Returns a sequence of the map's values."
+ [map] (. clojure.lang.RT (vals map)))
+
+(defn key
+ "Returns the key of the map entry."
+ [#^clojure.lang.IMapEntry e] ;; [#^java.util.Map$Entry e]
+ (. e (key))) ;; (. e (getKey)))
+
+(defn val
+ "Returns the value in the map entry."
+ [#^clojure.lang.IMapEntry e] ;; [#^java.util.Map$Entry e]
+ (. e (val))) ;; (. e (getValue)))
+
+(defn rseq
+ "Returns, in constant time, a sequence of the items in rev (which
+ can be a vector or sorted-map), in reverse order."
+ [#^clojure.lang.Reversible rev]
+ (. rev (rseq)))
+
+(defn name
+ "Returns the name String of a symbol or keyword."
+ {:tag String}
+ [#^clojure.lang.Named x]
+ (. x (getName)))
+
+(defn namespace
+ "Returns the namespace String of a symbol or keyword, or nil if not present."
+ {:tag String}
+ [#^clojure.lang.Named x]
+ (. x (getNamespace)))
+
+(defmacro locking
+ "Executes exprs in an implicit do, while holding the monitor of x.
+ Will release the monitor of x in all circumstances."
+ [x & body]
+ `(let [lockee# ~x]
+ (try
+ (monitor-enter lockee#)
+ ~@body
+ (finally
+ (monitor-exit lockee#)))))
+
+(defmacro ..
+ "form => fieldName-symbol or (instanceMethodName-symbol args*)
+
+ Expands into a member access (.) of the first member on the first
+ argument, followed by the next member on the result, etc. For
+ instance:
+
+ (.. System (getProperties) (get \"os.name\"))
+
+ expands to:
+
+ (. (. System (getProperties)) (get \"os.name\"))
+
+ but is easier to write, read, and understand."
+ ([x form] `(. ~x ~form))
+ ([x form & more] `(.. (. ~x ~form) ~@more)))
+
+(defmacro ->
+ "Threads the expr through the forms. Inserts x as the
+ second item in the first form, making a list of it if it is not a
+ list already. If there are more forms, inserts the first form as the
+ second item in second form, etc."
+ ([x form] (if (seq? form)
+ `(~(first form) ~x ~@(rest form))
+ (list form x)))
+ ([x form & more] `(-> (-> ~x ~form) ~@more)))
+
+;;multimethods
+(def global-hierarchy)
+
+(defmacro defmulti
+ "Creates a new multimethod with the associated dispatch function.
+ The docstring and attribute-map are optional.
+
+ Options are key-value pairs and may be one of:
+ :default the default dispatch value, defaults to :default
+ :hierarchy the isa? hierarchy to use for dispatching
+ defaults to the global hierarchy"
+ {:arglists '([name docstring? attr-map? dispatch-fn & options])}
+ [mm-name & options]
+ (let [docstring (if (string? (first options))
+ (first options)
+ nil)
+ options (if (string? (first options))
+ (rest options)
+ options)
+ m (if (map? (first options))
+ (first options)
+ {})
+ options (if (map? (first options))
+ (rest options)
+ options)
+ dispatch-fn (first options)
+ options (rest options)
+ m (assoc m :tag 'clojure.lang.MultiFn)
+ m (if docstring
+ (assoc m :doc docstring)
+ m)
+ m (if (meta mm-name)
+ (conj (meta mm-name) m)
+ m)]
+ (when (= (count options) 1)
+ (throw (Exception. "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)")))
+ (let [options (apply hash-map options)
+ default (get options :default :default)
+ hierarchy (get options :hierarchy #'global-hierarchy)]
+ `(def ~(with-meta mm-name m)
+ (new clojure.lang.MultiFn ~dispatch-fn ~default ~hierarchy)))))
+
+(defmacro defmethod
+ "Creates and installs a new method of multimethod associated with dispatch-value. "
+ [multifn dispatch-val & fn-tail]
+ `(. ~multifn addMethod ~dispatch-val (fn ~@fn-tail)))
+
+(defn remove-method
+ "Removes the method of multimethod associated with dispatch-value."
+ [multifn dispatch-val]
+ (. multifn removeMethod dispatch-val))
+
+(defn prefer-method
+ "Causes the multimethod to prefer matches of dispatch-val-x over dispatch-val-y when there is a conflict"
+ [multifn dispatch-val-x dispatch-val-y]
+ (. multifn preferMethod dispatch-val-x dispatch-val-y))
+
+(defn methods
+ "Given a multimethod, returns a map of dispatch values -> dispatch fns"
+ [#^clojure.lang.MultiFn multifn] (.getMethodTable multifn))
+
+(defn prefers
+ "Given a multimethod, returns a map of preferred value -> set of other values"
+ [#^clojure.lang.MultiFn multifn] (.getMethodTable multifn))
+
+;;;;;;;;; var stuff
+
+(defmacro #^{:private true} assert-args [fnname & pairs] + `(do (when-not ~(first pairs) + (throw (ArgumentException. ;;;IllegalArgumentException. + ~(str fnname " requires " (second pairs))))) + ~(let [more (rrest pairs)] + (when more + (list* `assert-args fnname more))))) +
+(defmacro binding + "binding => var-symbol init-expr + + Creates new bindings for the (already-existing) vars, with the + supplied initial values, executes the exprs in an implicit do, then + re-establishes the bindings that existed before." + [bindings & body] + (assert-args binding + (vector? bindings) "a vector for its binding" + (even? (count bindings)) "an even number of forms in binding vector") + (let [var-ize (fn [var-vals] + (loop [ret [] vvs (seq var-vals)] + (if vvs + (recur (conj (conj ret `(var ~(first vvs))) (second vvs)) + (rest (rest vvs))) + (seq ret))))] + `(do + (. clojure.lang.Var (pushThreadBindings (hash-map ~@(var-ize bindings)))) + (try + ~@body + (finally + (. clojure.lang.Var (popThreadBindings)))))))
+
+(defn find-var
+ "Returns the global var named by the namespace-qualified symbol, or
+ nil if no var with that name."
+ [sym] (. clojure.lang.Var (find sym)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Refs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defn #^{:private true}
+ setup-reference [#^clojure.lang.ARef r options]
+ (let [opts (apply hash-map options)]
+ (when (:meta opts)
+ (.resetMeta r (:meta opts)))
+ (when (:validator opts)
+ (.setValidator r (:validator opts)))
+ r))
+
+(defn agent
+ "Creates and returns an agent with an initial value of state and
+ zero or more options (in any order):
+
+ :meta metadata-map
+
+ :validator validate-fn
+
+ If metadata-map is supplied, it will be come the metadata on the
+ agent. validate-fn must be nil or a side-effect-free fn of one
+ argument, which will be passed the intended new state on any state
+ change. If the new state is unacceptable, the validate-fn should
+ return false or throw an exception."
+ ([state] (new clojure.lang.Agent state))
+ ([state & options]
+ (setup-reference (agent state) options)))
+
+(defn send
+ "Dispatch an action to an agent. Returns the agent immediately.
+ Subsequently, in a thread from a thread pool, the state of the agent
+ will be set to the value of:
+
+ (apply action-fn state-of-agent args)"
+ [#^clojure.lang.Agent a f & args]
+ (. a (dispatch f args false)))
+
+ (defn send-off
+ "Dispatch a potentially blocking action to an agent. Returns the
+ agent immediately. Subsequently, in a separate thread, the state of
+ the agent will be set to the value of:
+
+ (apply action-fn state-of-agent args)"
+ [#^clojure.lang.Agent a f & args]
+ (. a (dispatch f args true)))
+
+(defn release-pending-sends + "Normally, actions sent directly or indirectly during another action + are held until the action completes (changes the agent's + state). This function can be used to dispatch any pending sent + actions immediately. This has no impact on actions sent during a + transaction, which are still held until commit. If no action is + occurring, does nothing. Returns the number of actions dispatched." + [] (clojure.lang.Agent/releasePendingSends)) + +(defn add-watcher + "Experimental. + Adds a watcher to an agent/atom/var/ref reference. The watcher must + be an Agent, and the action a function of the agent's state and one + additional arg, the reference. Whenever the reference's state + changes, any registered watchers will have their actions + sent. send-type must be one of :send or :send-off. The actions will + be sent after the reference's state is changed. Var watchers are + triggered only by root binding changes, not thread-local set!s" + [#^clojure.lang.IRef reference send-type watcher-agent action-fn] + (.addWatch reference watcher-agent action-fn (= send-type :send-off))) + +(defn remove-watcher + "Experimental. + Removes a watcher (set by add-watcher) from a reference" + [#^clojure.lang.IRef reference watcher-agent] + (.removeWatch reference watcher-agent)) +
+(defn agent-errors
+ "Returns a sequence of the exceptions thrown during asynchronous
+ actions of the agent."
+ [#^clojure.lang.Agent a] (. a (getErrors)))
+
+(defn clear-agent-errors
+ "Clears any exceptions thrown during asynchronous actions of the
+ agent, allowing subsequent actions to occur."
+ [#^clojure.lang.Agent a] (. a (clearErrors)))
+
+(defn shutdown-agents
+ "Initiates a shutdown of the thread pools that back the agent
+ system. Running actions will complete, but no new actions will be
+ accepted"
+ [] (. clojure.lang.Agent shutdown))
+
+(defn ref + "Creates and returns a Ref with an initial value of x and zero or + more options (in any order): + + :meta metadata-map + + :validator validate-fn + + If metadata-map is supplied, it will be come the metadata on the + ref. validate-fn must be nil or a side-effect-free fn of one + argument, which will be passed the intended new state on any state + change. If the new state is unacceptable, the validate-fn should + return false or throw an exception. validate-fn will be called on + transaction commit, when all refs have their final values." + ([x] (new clojure.lang.Ref x)) + ([x & options] (setup-reference (ref x) options)))
+
+(defn deref
+ "Also reader macro: @ref/@agent/@var/@atom/@delay/@future. Within a transaction, + returns the in-transaction-value of ref, else returns the + most-recently-committed value of ref. When applied to a var, agent + or atom, returns its current state. When applied to a delay, forces
+ it if not already forced. When applied to a future, will block if
+ computation not complete"
+ [#^clojure.lang.IDeref ref] (.deref ref))
+
+(defn atom
+ "Creates and returns an Atom with an initial value of x and zero or
+ more options (in any order):
+
+ :meta metadata-map
+
+ :validator validate-fn
+
+ If metadata-map is supplied, it will be come the metadata on the
+ atom. validate-fn must be nil or a side-effect-free fn of one
+ argument, which will be passed the intended new state on any state
+ change. If the new state is unacceptable, the validate-fn should
+ return false or throw an exception."
+ ([x] (new clojure.lang.Atom x))
+ ([x & options] (setup-reference (atom x) options)))
+
+(defn swap!
+ "Atomically swaps the value of atom to be:
+ (apply f current-value-of-atom args). Note that f may be called
+ multiple times, and thus should be free of side effects. Returns
+ the value that was swapped in."
+ ([#^clojure.lang.Atom atom f] (.swap atom f))
+ ([#^clojure.lang.Atom atom f x] (.swap atom f x))
+ ([#^clojure.lang.Atom atom f x y] (.swap atom f x y))
+ ([#^clojure.lang.Atom atom f x y & args] (.swap atom f x y args)))
+
+(defn compare-and-set!
+ "Atomically sets the value of atom to newval if and only if the
+ current value of the atom is identical to oldval. Returns true if
+ set happened, else false"
+ [#^clojure.lang.Atom atom oldval newval] (.compareAndSet atom oldval newval))
+
+(defn reset!
+ "Sets the value of atom to newval without regard for the
+ current value. Returns newval."
+ [#^clojure.lang.Atom atom newval] (.reset atom newval))
+
+(defn set-validator
+ "Sets the validator-fn for a var/ref/agent/atom. validator-fn must be nil or a + side-effect-free fn of one argument, which will be passed the intended + new state on any state change. If the new state is unacceptable, the + validator-fn should return false or throw an exception. If the current state (root + value if var) is not acceptable to the new validator, an exception + will be thrown and the validator will not be changed." + [#^clojure.lang.IRef iref validator-fn] (. iref (setValidator validator-fn))) +
+(defn get-validator
+ "Gets the validator-fn for a var/ref/agent/atom." + [#^clojure.lang.IRef iref] (. iref (getValidator)))
+
+(defn alter-meta!
+ "Atomically sets the metadata for a namespace/var/ref/agent/atom to be:
+
+ (apply f its-current-meta args)
+
+ f must be free of side-effects"
+ [#^clojure.lang.IReference iref f & args] (.alterMeta iref f args))
+
+(defn reset-meta!
+ "Atomically resets the metadata for a namespace/var/ref/agent/atom"
+ [#^clojure.lang.IReference iref metadata-map] (.resetMeta iref metadata-map))
+
+(defn commute
+ "Must be called in a transaction. Sets the in-transaction-value of
+ ref to:
+
+ (apply fun in-transaction-value-of-ref args)
+
+ and returns the in-transaction-value of ref.
+
+ At the commit point of the transaction, sets the value of ref to be:
+
+ (apply fun most-recently-committed-value-of-ref args)
+
+ Thus fun should be commutative, or, failing that, you must accept
+ last-one-in-wins behavior. commute allows for more concurrency than
+ ref-set."
+
+ [#^clojure.lang.Ref ref fun & args]
+ (. ref (commute fun args)))
+
+(defn alter
+ "Must be called in a transaction. Sets the in-transaction-value of
+ ref to:
+
+ (apply fun in-transaction-value-of-ref args)
+
+ and returns the in-transaction-value of ref."
+ [#^clojure.lang.Ref ref fun & args]
+ (. ref (alter fun args)))
+
+(defn ref-set
+ "Must be called in a transaction. Sets the value of ref.
+ Returns val."
+ [#^clojure.lang.Ref ref val]
+ (. ref (set val)))
+
+(defn ensure
+ "Must be called in a transaction. Protects the ref from modification
+ by other transactions. Returns the in-transaction-value of
+ ref. Allows for more concurrency than (ref-set ref @ref)"
+ [#^clojure.lang.Ref ref]
+ (. ref (touch))
+ (. ref (deref)))
+
+(defmacro sync
+ "transaction-flags => TBD, pass nil for now
+
+ Runs the exprs (in an implicit do) in a transaction that encompasses
+ exprs and any nested calls. Starts a transaction if none is already
+ running on this thread. Any uncaught exception will abort the
+ transaction and flow out of sync. The exprs may be run more than
+ once, but any effects on Refs will be atomic."
+ [flags-ignored-for-now & body]
+ `(. clojure.lang.LockingTransaction
+ (runInTransaction (fn [] ~@body))))
+
+
+(defmacro io! + "If an io! block occurs in a transaction, throws an + IllegalStateException, else runs body in an implicit do. If the + first expression in body is a literal string, will use that as the + exception message." + [& body] + (let [message (when (string? (first body)) (first body)) + body (if message (rest body) body)] + `(if (clojure.lang.LockingTransaction/isRunning) + (throw (new InvalidOperationException ~(or message "I/O in transaction"))) ;;; IllegalStateException + (do ~@body))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; fn stuff ;;;;;;;;;;;;;;;;
+
+
+(defn comp
+ "Takes a set of functions and returns a fn that is the composition
+ of those fns. The returned fn takes a variable number of args,
+ applies the rightmost of fns to the args, the next
+ fn (right-to-left) to the result, etc."
+ [& fs]
+ (let [fs (reverse fs)]
+ (fn [& args]
+ (loop [ret (apply (first fs) args) fs (rest fs)]
+ (if fs
+ (recur ((first fs) ret) (rest fs))
+ ret)))))
+
+(defn partial
+ "Takes a function f and fewer than the normal arguments to f, and
+ returns a fn that takes a variable number of additional args. When
+ called, the returned function calls f with args + additional args."
+ ([f arg1]
+ (fn [& args] (apply f arg1 args)))
+ ([f arg1 arg2]
+ (fn [& args] (apply f arg1 arg2 args)))
+ ([f arg1 arg2 arg3]
+ (fn [& args] (apply f arg1 arg2 arg3 args)))
+ ([f arg1 arg2 arg3 & more]
+ (fn [& args] (apply f arg1 arg2 arg3 (concat more args)))))
+
+;;;;;;;;;;;;;;;;;;; sequence fns ;;;;;;;;;;;;;;;;;;;;;;;
+
+(defn every?
+ "Returns true if (pred x) is logical true for every x in coll, else
+ false."
+ {:tag Boolean}
+ [pred coll]
+ (if (seq coll)
+ (and (pred (first coll))
+ (recur pred (rest coll)))
+ true))
+
+(def
+ #^{:tag Boolean
+ :doc "Returns false if (pred x) is logical true for every x in
+ coll, else true."
+ :arglists '([pred coll])}
+ not-every? (comp not every?))
+
+(defn some + "Returns the first logical true value of (pred x) for any x in coll, + else nil. One common idiom is to use a set as pred, for example + this will return true if :fred is in the sequence, otherwise nil: + (some #{:fred} coll)" + [pred coll] + (when (seq coll) + (or (pred (first coll)) (recur pred (rest coll)))))
+
+(def
+ #^{:tag Boolean
+ :doc "Returns false if (pred x) is logical true for any x in coll,
+ else true."
+ :arglists '([pred coll])}
+ not-any? (comp not some))
+
+(defn map
+ "Returns a lazy seq consisting of the result of applying f to the
+ set of first items of each coll, followed by applying f to the set
+ of second items in each coll, until any one of the colls is
+ exhausted. Any remaining items in other colls are ignored. Function
+ f should accept number-of-colls arguments."
+ ([f coll]
+ (when (seq coll)
+ (lazy-cons (f (first coll)) (map f (rest coll)))))
+ ([f c1 c2]
+ (when (and (seq c1) (seq c2))
+ (lazy-cons (f (first c1) (first c2))
+ (map f (rest c1) (rest c2)))))
+ ([f c1 c2 c3]
+ (when (and (seq c1) (seq c2) (seq c3))
+ (lazy-cons (f (first c1) (first c2) (first c3))
+ (map f (rest c1) (rest c2) (rest c3)))))
+ ([f c1 c2 c3 & colls]
+ (let [step (fn step [cs]
+ (when (every? seq cs)
+ (lazy-cons (map first cs) (step (map rest cs)))))]
+ (map #(apply f %) (step (conj colls c3 c2 c1))))))
+
+(defn mapcat
+ "Returns the result of applying concat to the result of applying map
+ to f and colls. Thus function f should return a collection."
+ [f & colls]
+ (apply concat (apply map f colls)))
+
+(defn filter
+ "Returns a lazy seq of the items in coll for which
+ (pred item) returns true. pred must be free of side-effects."
+ [pred coll]
+ (when (seq coll)
+ (if (pred (first coll))
+ (lazy-cons (first coll) (filter pred (rest coll)))
+ (recur pred (rest coll)))))
+
+(defn remove
+ "Returns a lazy seq of the items in coll for which
+ (pred item) returns false. pred must be free of side-effects."
+ [pred coll]
+ (when (seq coll)
+ (if (pred (first coll))
+ (recur pred (rest coll))
+ (lazy-cons (first coll) (remove pred (rest coll))))))
+
+(defn take
+ "Returns a lazy seq of the first n items in coll, or all items if
+ there are fewer than n."
+ [n coll]
+ (when (and (pos? n) (seq coll))
+ (lazy-cons (first coll) (when (> n 1) (take (dec n) (rest coll))))))
+
+(defn take-while
+ "Returns a lazy seq of successive items from coll while
+ (pred item) returns true. pred must be free of side-effects."
+ [pred coll]
+ (when (and (seq coll) (pred (first coll)))
+ (lazy-cons (first coll) (take-while pred (rest coll)))))
+
+(defn drop
+ "Returns a lazy seq of all but the first n items in coll."
+ [n coll]
+ (if (and (pos? n) (seq coll))
+ (recur (dec n) (rest coll))
+ (seq coll)))
+
+(defn drop-last
+ "Return a lazy seq of all but the last n (default 1) items in coll"
+ ([s] (drop-last 1 s))
+ ([n s] (map (fn [x _] x) (seq s) (drop n s))))
+
+(defn drop-while
+ "Returns a lazy seq of the items in coll starting from the first
+ item for which (pred item) returns nil."
+ [pred coll]
+ (if (and (seq coll) (pred (first coll)))
+ (recur pred (rest coll))
+ (seq coll)))
+
+(defn cycle
+ "Returns a lazy (infinite!) seq of repetitions of the items in
+ coll."
+ [coll]
+ (when (seq coll)
+ (let [rep (fn thisfn [xs]
+ (if xs
+ (lazy-cons (first xs) (thisfn (rest xs)))
+ (recur (seq coll))))]
+ (rep (seq coll)))))
+
+(defn split-at
+ "Returns a vector of [(take n coll) (drop n coll)]"
+ [n coll]
+ [(take n coll) (drop n coll)])
+
+(defn split-with
+ "Returns a vector of [(take-while pred coll) (drop-while pred coll)]"
+ [pred coll]
+ [(take-while pred coll) (drop-while pred coll)])
+
+(defn repeat
+ "Returns a lazy (infinite!) seq of xs."
+ [x] (lazy-cons x (repeat x)))
+
+(defn replicate
+ "Returns a lazy seq of n xs."
+ [n x] (take n (repeat x)))
+
+(defn iterate
+ "Returns a lazy seq of x, (f x), (f (f x)) etc. f must be free of side-effects"
+ [f x] (lazy-cons x (iterate f (f x))))
+
+(defn range
+ "Returns a lazy seq of nums from start (inclusive) to end
+ (exclusive), by step, where start defaults to 0 and step to 1."
+ ([end] (if (and (> end 0) (< end (. Int32 MaxValue))) ;;;(. Integer MAX_VALUE)))
+ (new clojure.lang.Range 0 end)
+ (take end (iterate inc 0))))
+ ([start end] (if (and (< start end)
+ (>= start (. Int32 MinValue)) ;;;(. Integer MIN_VALUE
+ (<= end (. Int32 MaxValue))) ;;;(. Integer MAX_VALUE)))
+ (new clojure.lang.Range start end)
+ (take (- end start) (iterate inc start))))
+ ([start end step]
+ (take-while (partial (if (pos? step) > <) end) (iterate (partial + step) start))))
+
+(defn merge
+ "Returns a map that consists of the rest of the maps conj-ed onto
+ the first. If a key occurs in more than one map, the mapping from
+ the latter (left-to-right) will be the mapping in the result."
+ [& maps]
+ (when (some identity maps)
+ (reduce #(conj (or %1 {}) %2) maps)))
+
+(defn merge-with
+ "Returns a map that consists of the rest of the maps conj-ed onto
+ the first. If a key occurs in more than one map, the mapping(s)
+ from the latter (left-to-right) will be combined with the mapping in
+ the result by calling (f val-in-result val-in-latter)."
+ [f & maps]
+ (when (some identity maps)
+ (let [merge-entry (fn [m e]
+ (let [k (key e) v (val e)]
+ (if (contains? m k)
+ (assoc m k (f (m k) v))
+ (assoc m k v))))
+ merge2 (fn [m1 m2]
+ (reduce merge-entry (or m1 {}) (seq m2)))]
+ (reduce merge2 maps))))
+
+
+
+(defn zipmap
+ "Returns a map with the keys mapped to the corresponding vals."
+ [keys vals]
+ (loop [map {}
+ ks (seq keys)
+ vs (seq vals)]
+ (if (and ks vs)
+ (recur (assoc map (first ks) (first vs))
+ (rest ks)
+ (rest vs))
+ map)))
+
+(defn line-seq
+ "Returns the lines of text from rdr as a lazy sequence of strings.
+ rdr must implement java.io.BufferedReader."
+ [#^System.IO.TextReader rdr ] ;;; [#^java.io.BufferedReader rdr]
+ (let [line (. rdr (ReadLine))] ;;; was (readLine)
+ (when line
+ (lazy-cons line (line-seq rdr)))))
+
+(defn comparator
+ "Returns an implementation of java.util.Comparator based upon pred."
+ [pred]
+ (fn [x y]
+ (cond (pred x y) -1 (pred y x) 1 :else 0)))
+
+(defn sort
+ "Returns a sorted sequence of the items in coll. If no comparator is
+ supplied, uses compare. comparator must
+ implement java.util.Comparator."
+ ([coll]
+ (sort compare coll))
+ ([comp coll] ;;; We can't pass in a Comparator directly at this point, only a ClojureRuntimeDelegate : [#^java.util.Comparator comp coll]
+ (when (and coll (not (zero? (count coll))))
+ (let [a (to-array coll)]
+ (. clojure.lang.RT (SortArray a comp)) ;;; see above: (. java.util.Arrays (sort a comp))
+ (seq a)))))
+
+(defn sort-by
+ "Returns a sorted sequence of the items in coll, where the sort
+ order is determined by comparing (keyfn item). If no comparator is
+ supplied, uses compare. comparator must
+ implement java.util.Comparator."
+ ([keyfn coll]
+ (sort-by keyfn compare coll))
+ ([keyfn comp coll] ;;; --- Can't pass a Comparator directly: [keyfn #^java.util.Comparator comp coll]
+ (sort (fn [x y] (comp (keyfn x) (keyfn y))) coll))) ;;;(sort (fn [x y] (. comp (compare (keyfn x) (keyfn y)))) coll)))
+
+(defn partition
+ "Returns a lazy sequence of lists of n items each, at offsets step
+ apart. If step is not supplied, defaults to n, i.e. the partitions
+ do not overlap."
+ ([n coll]
+ (partition n n coll))
+ ([n step coll]
+ (when (seq coll)
+ (let [p (take n coll)]
+ (when (= n (count p))
+ (lazy-cons p (partition n step (drop step coll))))))))
+
+;; evaluation
+
+(defn eval
+ "Evaluates the form data structure (not text!) and returns the result."
+ [form] (. clojure.lang.Compiler (eval form)))
+
+(defmacro doseq
+ "Repeatedly executes body (presumably for side-effects) with
+ bindings and filtering as provided by \"for\". Does not retain
+ the head of the sequence. Returns nil."
+ [seq-exprs & body]
+ (assert-args doseq + (vector? seq-exprs) "a vector for its binding" + (even? (count seq-exprs)) "an even number of forms in binding vector") + (let [groups (reduce (fn [groups p] + (if (keyword? (first p)) + (conj (pop groups) (apply assoc (peek groups) p)) + (conj groups {:bind (first p) :seq (second p)}))) + [] (partition 2 seq-exprs)) + emit (fn emit [group & more-groups] + `(loop [sq# (seq ~(:seq group))] + (when sq# + (let [~(:bind group) (first sq#)] + (when ~(or (:while group) true) + (when ~(or (:when group) true) + ~(if more-groups + (apply emit more-groups) + `(do ~@body))) + (recur (rest sq#)))))))] + (apply emit groups))) +
+(defn dorun
+ "When lazy sequences are produced via functions that have side
+ effects, any effects other than those needed to produce the first
+ element in the seq do not occur until the seq is consumed. dorun can
+ be used to force any effects. Walks through the successive rests of
+ the seq, does not retain the head and returns nil."
+ ([coll]
+ (when (and (seq coll) (or (first coll) true))
+ (recur (rest coll))))
+ ([n coll]
+ (when (and (seq coll) (pos? n) (or (first coll) true))
+ (recur (dec n) (rest coll)))))
+
+(defn doall
+ "When lazy sequences are produced via functions that have side
+ effects, any effects other than those needed to produce the first
+ element in the seq do not occur until the seq is consumed. doall can
+ be used to force any effects. Walks through the successive rests of
+ the seq, retains the head and returns it, thus causing the entire
+ seq to reside in memory at one time."
+ ([coll]
+ (dorun coll)
+ coll)
+ ([n coll]
+ (dorun n coll)
+ coll))
+; Need to figure out how to do this in CLR. Should be some kind of event handle
+;(defn await
+; "Blocks the current thread (indefinitely!) until all actions
+; dispatched thus far, from this thread or agent, to the agent(s) have
+; occurred."
+; [& agents]
+; (io! "await in transaction" +; (when *agent* +; (throw (new Exception "Can't await in agent action"))) +; (let [latch (new java.util.concurrent.CountDownLatch (count agents)) +; count-down (fn [agent] (. latch (countDown)) agent)] +; (doseq [agent agents] +; (send agent count-down)) +; (. latch (await)))))
+;
+;(defn await1 [#^clojure.lang.Agent a]
+; (when (pos? (.getQueueCount a))
+; (await a))
+; a)
+;
+;(defn await-for
+; "Blocks the current thread until all actions dispatched thus
+; far (from this thread or agent) to the agents have occurred, or the
+; timeout (in milliseconds) has elapsed. Returns nil if returning due
+; to timeout, non-nil otherwise."
+; [timeout-ms & agents]
+; (io! "await-for in transaction" +; (when *agent* +; (throw (new Exception "Can't await in agent action"))) +; (let [latch (new java.util.concurrent.CountDownLatch (count agents)) +; count-down (fn [agent] (. latch (countDown)) agent)] +; (doseq [agent agents] +; (send agent count-down)) +; (. latch (await timeout-ms (. java.util.concurrent.TimeUnit MILLISECONDS))))))
+
+(defmacro dotimes + "bindings => name n + + Repeatedly executes body (presumably for side-effects) with name + bound to integers from 0 through n-1." + [bindings & body] + (assert-args dotimes + (vector? bindings) "a vector for its binding" + (= 2 (count bindings)) "exactly 2 forms in binding vector") + (let [i (first bindings) + n (second bindings)] + `(let [n# (int ~n)] + (loop [~i (int 0)] + (when (< ~i n#) + ~@body + (recur (unchecked-inc ~i)))))))
+
+(defn import + "import-list => (package-symbol class-name-symbols*) + + For each name in class-name-symbols, adds a mapping from name to the + class named by package.name to the current namespace. Use :import in the ns + macro in preference to calling this directly." + [& import-symbols-or-lists] + (let [#^clojure.lang.Namespace ns *ns*] + (doseq [spec import-symbols-or-lists] + (if (symbol? spec) + (let [n (name spec) + dot (.lastIndexOf n (. clojure.lang.RT (intCast \.))) + c (symbol (.substring n (inc dot)))] + (. ns (importClass c (. clojure.lang.RT (classForName (name spec)))))) + (let [pkg (first spec) + classes (rest spec)] + (doseq [c classes] + (. ns (importClass c (. clojure.lang.RT (classForName (str pkg "." c))))))))))) +
+
+(defn into-array
+ "Returns an array with components set to the values in aseq. The array's
+ component type is type if provided, or the type of the first value in
+ aseq if present, or Object. All values in aseq must be compatible with
+ the component type. Class objects for the primitive types can be obtained
+ using, e.g., Integer/TYPE."
+ ([aseq]
+ (clojure.lang.RT/seqToTypedArray (seq aseq)))
+ ([type aseq]
+ (clojure.lang.RT/seqToTypedArray type (seq aseq))))
+
+(defn into
+ "Returns a new coll consisting of to-coll with all of the items of
+ from-coll conjoined."
+ [to from]
+ (let [ret to items (seq from)]
+ (if items
+ (recur (conj ret (first items)) (rest items))
+ ret)))
+
+(defn #^{:private true}
+ array [& items]
+ (into-array items))
+
+(defn #^Type class ;;;#^Class class
+ "Returns the Class of x"
+ [#^Object x] (if (nil? x) x (. x (GetType)))) ;;; getClass => GetType
+;;; Don't know what to do with this. No equivalent to Number in CLR.
+;(defn num
+; "Coerce to Number"
+; {:tag Number
+; :inline (fn [x] `(. clojure.lang.Numbers (num ~x)))}
+; [x] (. clojure.lang.Numbers (num x)))
+
+(defn int ;;; Need to make this handle args out of range
+ "Coerce to int"
+ {:tag Int32 ;;; Integer
+ :inline (fn [x] `(. clojure.lang.RT (intCast ~x)))}
+ [x] (. clojure.lang.RT (intCast x)))
+
+ (defn long
+ "Coerce to long"
+ {:tag Int64 ;;; Long
+ :inline (fn [x] `(. clojure.lang.RT (longCast ~x)))}
+ [x] (. clojure.lang.RT (longCast x))) ;;; [#^Number x] (. x (longValue)))
+
+(defn float
+ "Coerce to float"
+ {:tag Single ;;; Float
+ :inline (fn [x] `(. clojure.lang.RT (floatCast ~x)))}
+ [x] (. clojure.lang.RT (floatCast x))) ;;; [#^Number x] (. x (floatValue)))
+
+(defn double
+ "Coerce to double"
+ {:tag Double
+ :inline (fn [x] `(. clojure.lang.RT (doubleCast ~x)))}
+ [x] (. clojure.lang.RT (doubleCast x))) ;;; [#^Number x] (. x (doubleValue)))
+
+(defn short
+ "Coerce to short"
+ {:tag Int16
+ :inline (fn [x] `(. clojure.lang.RT (shortCast ~x)))}
+ [x] (. clojure.lang.RT (shortCast x))) ;;; [#^Number x] (. x (shortValue)))
+
+(defn byte
+ "Coerce to byte"
+ {:tag Byte
+ :inline (fn [x] `(. clojure.lang.RT (byteCast ~x)))}
+ [x] (. clojure.lang.RT (byteCast x))) ;;; [#^Number x] (. x (byteValue)))
+
+(defn char
+ "Coerce to char"
+ {:tag Char ;;; Character
+ :inline (fn [x] `(. clojure.lang.RT (charCast ~x)))}
+ [x] (. clojure.lang.RT (charCast x)))
+
+(defn boolean
+ "Coerce to boolean"
+ {:tag Boolean
+ :inline (fn [x] `(. clojure.lang.RT (booleanCast ~x)))}
+ [x] (if x true false))
+
+(defn number? + "Returns true if x is a Number" + [x] + (. clojure.lang.Util (IsNumeric x))) ;;; (instance? Number x)) + +(defn integer? + "Returns true if n is an integer" + [n] + (or (instance? Int32 n) ;;; Integer + (instance? Int64 n) ;;; Long + (instance? BigInteger n) (instance? Char n) ;;; added Char test + (instance? int 16 n) ;;; Short + (instance? Byte n))) + +(defn mod
+ "modulus of num and div."
+ [num div]
+ (cond
+ (or (not (integer? num)) (not (integer? div)))
+ (throw (ArgumentException. ;;; IllegalArgumentException.
+ "mod requires two integers"))
+ (or (< num 0 div) (< div 0 num)) (+ (rem num div) div)
+ :else (rem num div)))
+
+(defn ratio? + "Returns true if n is a Ratio" + [n] (instance? clojure.lang.Ratio n)) + +(defn decimal? + "Returns true if n is a BigDecimal" + [n] (instance? BigDecimal n)) + +(defn float? + "Returns true if n is a floating point number" + [n] + (or (instance? Double n) + (instance? Single n))) ;;; Float + +(defn rational? [n] + "Returns true if n is a rational number" + (or (integer? n) (ratio? n) (decimal? n))) +
+(defn bigint + "Coerce to BigInteger" + {:tag BigInteger} + [x] (cond + (instance? BigInteger x) x + (decimal? x) (.toBigInteger #^BigDecimal x) + (number? x) (BigInteger/valueOf (long x)) + :else (BigInteger. x))) + +(defn bigdec + "Coerce to BigDecimal" + {:tag BigDecimal} + [x] (cond + (decimal? x) x + (float? x) (. BigDecimal valueOf (double x)) + (ratio? x) (/ (BigDecimal. (.numerator x)) (.denominator x))
+ (instance? BigInteger x) (BigDecimal. #^BigInteger x) + (number? x) (BigDecimal/valueOf (long x)) + :else (BigDecimal. x))) +
+(def #^{:private true} print-initialized false)
+
+(defmulti print-method (fn [x writer] (class x)))
+(defmulti print-dup (fn [x writer] (class x)))
+
+(defn pr-on
+ {:private true}
+ [x w]
+ (if *print-dup*
+ (print-dup x w)
+ (print-method x w))
+ nil)
+
+(defn pr
+ "Prints the object(s) to the output stream that is the current value
+ of *out*. Prints the object(s), separated by spaces if there is
+ more than one. By default, pr and prn print in a way that objects
+ can be read by the reader"
+ ([] nil)
+ ([x]
+ (pr-on x *out*))
+ ([x & more]
+ (pr x)
+ (. *out* (Write \space)) ;; append -> Write
+ (apply pr more)))
+
+(defn newline
+ "Writes a newline to the output stream that is the current value of
+ *out*"
+ []
+ (. *out* (Write \newline)) ;; append -> Write
+ nil)
+
+(defn flush
+ "Flushes the output stream that is the current value of
+ *out*"
+ []
+ (. *out* (Flush)) ;; flush => Flush
+ nil)
+
+(defn prn
+ "Same as pr followed by (newline). Observes *flush-on-newline*"
+ [& more]
+ (apply pr more)
+ (newline)
+ (when *flush-on-newline*
+ (flush)))
+
+(defn print
+ "Prints the object(s) to the output stream that is the current value
+ of *out*. print and println produce output for human consumption."
+ [& more]
+ (binding [*print-readably* nil]
+ (apply pr more)))
+
+(defn println
+ "Same as print followed by (newline)"
+ [& more]
+ (binding [*print-readably* nil]
+ (apply prn more)))
+
+
+(defn read ;;; still have an error here, probably from leftover newline causing interference with REPL
+ "Reads the next object from stream, which must be an instance of
+ java.io.PushbackReader or some derivee. stream defaults to the
+ current value of *in* ."
+ ([]
+ (read *in*))
+ ([stream]
+ (read stream true nil))
+ ([stream eof-error? eof-value]
+ (read stream eof-error? eof-value false))
+ ([stream eof-error? eof-value recursive?]
+ (. clojure.lang.LispReader (read stream (boolean eof-error?) eof-value recursive?))))
+
+(defn read-line ;; ALSO HAS A PROBLEM -- interference from REPL?
+ "Reads the next line from stream that is the current value of *in* ."
+ [] (. #^System.IO.Reader *in* (ReadLine))) ;;; readLine => ReadLine #^java.io.BufferedReader
+
+(defn read-string
+ "Reads one object from the string s"
+ [s] (clojure.lang.RT/readString s))
+
+(defn subvec + "Returns a persistent vector of the items in vector from + start (inclusive) to end (exclusive). If end is not supplied, + defaults to (count vector). This operation is O(1) and very fast, as + the resulting vector shares structure with the original and no + trimming is done." + ([v start] + (subvec v start (count v))) + ([v start end] + (. clojure.lang.RT (subvec v start end))))
+
+ (defmacro with-open
+ "bindings => name init
+
+ Evaluates body in a try expression with names bound to the values + of the inits, and a finally clause that calls (.close name) on each + name in reverse order." + [bindings & body] + (assert-args with-open + (vector? bindings) "a vector for its binding" + (even? (count bindings)) "an even number of forms in binding vector") + (cond + (= (count bindings) 0) `(do ~@body) + (symbol? (bindings 0)) `(let ~(subvec bindings 0 2) + (try + (with-open ~(subvec bindings 2) ~@body) + (finally + (. ~(bindings 0) close)))) + :else (throw (ArgumentException. ;;;IllegalArgumentException. + "with-open only allows Symbols in bindings")))) +
+(defmacro doto
+ "Evaluates x then calls all of the methods and functions with the + value of x supplied at the from of the given arguments. The forms + are evaluated in order. Returns x. + + (doto (new java.util.HashMap) (.put \"a\" 1) (.put \"b\" 2))" + [x & forms] + (let [gx (gensym)] + `(let [~gx ~x] + ~@(map (fn [f] + (if (seq? f) + `(~(first f) ~gx ~@(rest f)) + `(~f ~gx))) + forms) + ~gx)))
+
+(defmacro memfn
+ "Expands into code that creates a fn that expects to be passed an
+ object and any args and calls the named instance method on the
+ object passing the args. Use when you want to treat a Java method as
+ a first-class fn."
+ [name & args]
+ `(fn [target# ~@args]
+ (. target# (~name ~@args))))
+
+(defmacro time
+ "Evaluates expr and prints the time it took. Returns the value of
+ expr."
+ [expr]
+ `(let [start# (. clojure.lang.RT (StartStopwatch)) ;;; (. System (nanoTime))
+ ret# ~expr]
+ (prn (str "Elapsed time: " (. clojure.lang.RT StopStopwatch) " msecs")) ;;;(/ (double (- (. System (nanoTime)) start#)) 1000000.0) " msecs"))
+ ret#))
+
+
+
+;;; Java version has: (import '(java.lang.reflect Array))
+
+(defn alength
+ "Returns the length of the Java array. Works on arrays of all
+ types."
+ {:inline (fn [a] `(. clojure.lang.RT (alength ~a)))}
+ [array] (. clojure.lang.RT (alength array)))
+
+(defn aclone
+ "Returns a clone of the Java array. Works on arrays of known
+ types."
+ {:inline (fn [a] `(. clojure.lang.RT (aclone ~a)))}
+ [array] (. clojure.lang.RT (aclone array)))
+;;; We have a real problem with aget/aset -- Java has only single dim arrays, CLR has true multidim. How to distinguish true multidim from ragged? For now, treat all as ragged.
+(defn aget
+ "Returns the value at the index/indices. Works on Java arrays of all
+ types."
+ {:inline (fn [a i] `(. clojure.lang.RT (aget ~a ~i)))
+ :inline-arities #{2}}
+ ([array idx]
+ (clojure.lang.Compiler/prepRet (. array (GetValue idx)))) ;;; was (. Array (get array idx))) also replaced clojure.lang.Reflector/prepRet
+ ([array idx & idxs]
+ (apply aget (aget array idx) idxs)))
+
+(defn aset
+ "Sets the value at the index/indices. Works on Java arrays of
+ reference types. Returns val."
+ {:inline (fn [a i v] `(. clojure.lang.RT (aset ~a ~i ~v)))
+ :inline-arities #{3}}
+ ([array idx val]
+ (. array (SetValue val idx)) ;;; was (. Array (set array idx val))
+ val)
+ ([array idx idx2 & idxv]
+ (apply aset (aget array idx) idx2 idxv)))
+;;; Do we really need to do this in CLR?
+;(defmacro
+; #^{:private true}
+; def-aset [name method coerce]
+; `(defn ~name
+; {:arglists '([~'array ~'idx ~'val] [~'array ~'idx ~'idx2 & ~'idxv])}
+; ([array# idx# val#]
+; (. Array (~method array# idx# (~coerce val#)))
+; val#)
+; ([array# idx# idx2# & idxv#]
+; (apply ~name (aget array# idx#) idx2# idxv#))))
+;
+;(def-aset
+; #^{:doc "Sets the value at the index/indices. Works on arrays of int. Returns val."}
+; aset-int setInt int)
+;
+;(def-aset
+; #^{:doc "Sets the value at the index/indices. Works on arrays of long. Returns val."}
+; aset-long setLong long)
+;
+;(def-aset
+; #^{:doc "Sets the value at the index/indices. Works on arrays of boolean. Returns val."}
+; aset-boolean setBoolean boolean)
+;
+;(def-aset
+; #^{:doc "Sets the value at the index/indices. Works on arrays of float. Returns val."}
+; aset-float setFloat float)
+;
+;(def-aset
+; #^{:doc "Sets the value at the index/indices. Works on arrays of double. Returns val."}
+; aset-double setDouble double)
+;
+;(def-aset
+; #^{:doc "Sets the value at the index/indices. Works on arrays of short. Returns val."}
+; aset-short setShort short)
+;
+;(def-aset
+; #^{:doc "Sets the value at the index/indices. Works on arrays of byte. Returns val."}
+; aset-byte setByte byte)
+;
+;(def-aset
+; #^{:doc "Sets the value at the index/indices. Works on arrays of char. Returns val."}
+; aset-char setChar char)
+;;; Another ragged versus true multidimensional array problem
+;(defn make-array
+; "Creates and returns an array of instances of the specified class of
+; the specified dimension(s). Note that a class object is required.
+; Class objects can be obtained by using their imported or
+; fully-qualified name. Class objects for the primitive types can be
+; obtained using, e.g., Integer/TYPE."
+; ([#^Class type len]
+; (. Array (newInstance type (int len))))
+; ([#^Class type dim & more-dims]
+; (let [dims (cons dim more-dims)
+; #^"[I" dimarray (make-array (. Integer TYPE) (count dims))]
+; (dotimes [i (alength dimarray)]
+; (aset-int dimarray i (nth dims i)))
+; (. Array (newInstance type dimarray)))))
+;
+;(defn to-array-2d
+; "Returns a (potentially-ragged) 2-dimensional array of Objects
+; containing the contents of coll, which can be any Collection of any
+; Collection."
+; {:tag "Object[][]" } ;;; "[[Ljava.lang.Object;"
+; [#^java.util.Collection coll]
+; (let [ret (make-array (. Class (forName "[Ljava.lang.Object;")) (. coll (size)))]
+; (loop [i 0 xs (seq coll)]
+; (when xs
+; (aset ret i (to-array (first xs)))
+; (recur (inc i) (rest xs))))
+; ret))
+
+(defn macroexpand-1
+ "If form represents a macro form, returns its expansion,
+ else returns form."
+ [form]
+ (. clojure.lang.Compiler (macroexpand1 form)))
+
+(defn macroexpand
+ "Repeatedly calls macroexpand-1 on form until it no longer
+ represents a macro form, then returns it. Note neither
+ macroexpand-1 nor macroexpand expand macros in subforms."
+ [form]
+ (let [ex (macroexpand-1 form)]
+ (if (identical? ex form)
+ form
+ (macroexpand ex))))
+
+(defn create-struct
+ "Returns a structure basis object."
+ [& keys]
+ (. clojure.lang.PersistentStructMap (createSlotMap keys)))
+
+(defmacro defstruct
+ "Same as (def name (create-struct keys...))"
+ [name & keys]
+ `(def ~name (create-struct ~@keys)))
+;;; In current java version, missing basis keys cause an error
+(defn struct-map
+ "Returns a new structmap instance with the keys of the
+ structure-basis. keyvals may contain all, some or none of the basis
+ keys - where values are not supplied they will default to nil.
+ keyvals can also contain keys not in the basis."
+ [s & inits]
+ (. clojure.lang.PersistentStructMap (create s inits)))
+
+(defn struct
+ "Returns a new structmap instance with the keys of the
+ structure-basis. vals must be supplied for basis keys in order -
+ where values are not supplied they will default to nil."
+ [s & vals]
+ (. clojure.lang.PersistentStructMap (construct s vals)))
+
+(defn accessor
+ "Returns a fn that, given an instance of a structmap with the basis,
+ returns the value at the key. The key must be in the basis. The
+ returned function should be (slightly) more efficient than using
+ get, but such use of accessors should be limited to known
+ performance-critical areas."
+ [s key]
+ (. clojure.lang.PersistentStructMap (getAccessor s key)))
+
+(defn load-reader
+ "Sequentially read and evaluate the set of forms contained in the
+ stream/file"
+ [rdr] (. clojure.lang.Compiler (load rdr)))
+
+(defn load-string
+ "Sequentially read and evaluate the set of forms contained in the
+ string"
+ [s]
+ (let [rdr (-> (System.IO.StringReader. s) ;;; was (java.io.StringReader. s)
+ (clojure.lang.Readers.LineNumberingReader.))] ;;; was (clojure.lang.LineNumberingPushbackReader.))]
+ (load-reader rdr)))
+;;; NOT CLEAR WHAT TO WORK AGAINST HERE. MAYBE THIS SHOULD NOT BE IN THE CORE?
+;(defn resultset-seq
+; "Creates and returns a lazy sequence of structmaps corresponding to
+; the rows in the java.sql.ResultSet rs"
+; [#^java.sql.ResultSet rs]
+; (let [rsmeta (. rs (getMetaData))
+; idxs (range 1 (inc (. rsmeta (getColumnCount))))
+; keys (map (comp keyword #(.ToLowerCase #^String %)) ;;; .toLowerCase
+; (map (fn [i] (. rsmeta (getColumnName i))) idxs))
+; row-struct (apply create-struct keys)
+; row-values (fn [] (map (fn [#^Integer i] (. rs (getObject i))) idxs))
+; rows (fn thisfn []
+; (when (. rs (next))
+; (lazy-cons (apply struct row-struct (row-values)) (thisfn))))]
+; (rows)))
+
+(defn set
+ "Returns a set of the distinct elements of coll."
+ [coll] (apply hash-set coll))
+
+(defn #^{:private true}
+ filter-key [keyfn pred amap]
+ (loop [ret {} es (seq amap)]
+ (if es
+ (if (pred (keyfn (first es)))
+ (recur (assoc ret (key (first es)) (val (first es))) (rest es))
+ (recur ret (rest es)))
+ ret)))
+
+(defn find-ns
+ "Returns the namespace named by the symbol or nil if it doesn't exist."
+ [sym] (clojure.lang.Namespace/find sym))
+
+(defn create-ns
+ "Create a new namespace named by the symbol if one doesn't already
+ exist, returns it or the already-existing namespace of the same
+ name."
+ [sym] (clojure.lang.Namespace/findOrCreate sym))
+
+(defn remove-ns
+ "Removes the namespace named by the symbol. Use with caution.
+ Cannot be used to remove the clojure namespace."
+ [sym] (clojure.lang.Namespace/remove sym))
+
+(defn all-ns
+ "Returns a sequence of all namespaces."
+ [] (clojure.lang.Namespace/all))
+
+(defn #^clojure.lang.Namespace the-ns
+ "If passed a namespace, returns it. Else, when passed a symbol, + returns the namespace named by it, throwing an exception if not + found."
+ [x]
+ (if (instance? clojure.lang.Namespace x)
+ x
+ (or (find-ns x) (throw (Exception. (str "No namespace: " x " found"))))))
+
+(defn ns-name
+ "Returns the name of the namespace, a symbol."
+ [ns]
+ (.getName (the-ns ns)))
+
+(defn ns-map
+ "Returns a map of all the mappings for the namespace."
+ [ns]
+ (.getMappings (the-ns ns)))
+
+(defn ns-unmap
+ "Removes the mappings for the symbol from the namespace."
+ [ns sym]
+ (.unmap (the-ns ns) sym))
+; commented out in Java original
+;(defn export [syms]
+; (doseq [sym syms]
+; (.. *ns* (intern sym) (setExported true))))
+
+(defn ns-publics
+ "Returns a map of the public intern mappings for the namespace."
+ [ns]
+ (let [ns (the-ns ns)]
+ (filter-key val (fn [#^clojure.lang.Var v] (and (instance? clojure.lang.Var v)
+ (= ns (.ns v))
+ (.isPublic v)))
+ (ns-map ns))))
+
+(defn ns-imports
+ "Returns a map of the import mappings for the namespace."
+ [ns]
+ (filter-key val (partial instance? Type) (ns-map ns))) ;;; Class => Type
+
+(defn refer
+ "refers to all public vars of ns, subject to filters.
+ filters can include at most one each of:
+
+ :exclude list-of-symbols
+ :only list-of-symbols
+ :rename map-of-fromsymbol-tosymbol
+
+ For each public interned var in the namespace named by the symbol,
+ adds a mapping from the name of the var to the var to the current
+ namespace. Throws an exception if name is already mapped to
+ something else in the current namespace. Filters can be used to
+ select a subset, via inclusion or exclusion, or to provide a mapping
+ to a symbol different from the var's name, in order to prevent
+ clashes. Use :use in the ns macro in preference to calling this directly."
+ [ns-sym & filters]
+ (let [ns (or (find-ns ns-sym) (throw (new Exception (str "No namespace: " ns-sym))))
+ fs (apply hash-map filters)
+ nspublics (ns-publics ns)
+ rename (or (:rename fs) {})
+ exclude (set (:exclude fs))
+ to-do (or (:only fs) (keys nspublics))]
+ (doseq [sym to-do]
+ (when-not (exclude sym)
+ (let [v (nspublics sym)]
+ (when-not v
+ (throw (new InvalidOperationException (str sym " is not public")))) ;;; java.lang.IllegalAccessError ==> InvalidOperationException
+ (. *ns* (refer (or (rename sym) sym) v)))))))
+
+(defn ns-refers
+ "Returns a map of the refer mappings for the namespace."
+ [ns]
+ (let [ns (the-ns ns)]
+ (filter-key val (fn [#^clojure.lang.Var v] (and (instance? clojure.lang.Var v)
+ (not= ns (.ns v))))
+ (ns-map ns))))
+
+(defn ns-interns
+ "Returns a map of the intern mappings for the namespace."
+ [ns]
+ (let [ns (the-ns ns)]
+ (filter-key val (fn [#^clojure.lang.Var v] (and (instance? clojure.lang.Var v)
+ (= ns (.ns v))))
+ (ns-map ns))))
+
+(defn alias
+ "Add an alias in the current namespace to another
+ namespace. Arguments are two symbols: the alias to be used, and
+ the symbolic name of the target namespace. Use :as in the ns macro in preference
+ to calling this directly."
+ [alias namespace-sym]
+ (.addAlias *ns* alias (find-ns namespace-sym)))
+
+(defn ns-aliases
+ "Returns a map of the aliases for the namespace."
+ [ns]
+ (.getAliases (the-ns ns)))
+
+(defn ns-unalias
+ "Removes the alias for the symbol from the namespace."
+ [ns sym]
+ (.removeAlias (the-ns ns) sym))
+
+(defn take-nth
+ "Returns a lazy seq of every nth item in coll."
+ [n coll]
+ (when (seq coll)
+ (lazy-cons (first coll) (take-nth n (drop n coll)))))
+
+(defn interleave
+ "Returns a lazy seq of the first item in each coll, then the second
+ etc."
+ [& colls]
+ (apply concat (apply map list colls)))
+
+(defn var-get
+ "Gets the value in the var object"
+ [#^clojure.lang.Var x] (. x (get)))
+
+(defn var-set
+ "Sets the value in the var object to val. The var must be
+ thread-locally bound."
+ [#^clojure.lang.Var x val] (. x (set val)))
+
+(defmacro with-local-vars
+ "varbinding=> symbol init-expr
+
+ Executes the exprs in a context in which the symbols are bound to
+ vars with per-thread bindings to the init-exprs. The symbols refer
+ to the var objects themselves, and must be accessed with var-get and
+ var-set"
+ [name-vals-vec & body] + (assert-args with-local-vars + (vector? name-vals-vec) "a vector for its binding" + (even? (count name-vals-vec)) "an even number of forms in binding vector") + `(let [~@(interleave (take-nth 2 name-vals-vec) + (repeat '(. clojure.lang.Var (create))))] + (. clojure.lang.Var (pushThreadBindings (hash-map ~@name-vals-vec))) + (try + ~@body + (finally (. clojure.lang.Var (popThreadBindings))))))
+
+(defn ns-resolve
+ "Returns the var or Class to which a symbol will be resolved in the
+ namespace, else nil. Note that if the symbol is fully qualified,
+ the var/Class to which it resolves need not be present in the
+ namespace."
+ [ns sym]
+ (clojure.lang.Compiler/maybeResolveIn (the-ns ns) sym))
+
+(defn resolve
+ "same as (ns-resolve *ns* symbol)"
+ [sym] (ns-resolve *ns* sym))
+
+(defn array-map
+ "Constructs an array-map."
+ ([] (. clojure.lang.PersistentArrayMap EMPTY))
+ ([& keyvals] (new clojure.lang.PersistentArrayMap (to-array keyvals))))
+
+(defn nthrest
+ "Returns the nth rest of coll, (seq coll) when n is 0."
+ [coll n]
+ (loop [n n xs (seq coll)]
+ (if (and xs (pos? n))
+ (recur (dec n) (rest xs))
+ xs)))
+
+
+;redefine let and loop with destructuring
+(defn destructure [bindings]
+ (let [bmap (apply array-map bindings)
+ pb (fn pb [bvec b v]
+ (let [pvec
+ (fn [bvec b val]
+ (let [gvec (gensym "vec__")]
+ (loop [ret (-> bvec (conj gvec) (conj val))
+ n 0
+ bs b
+ seen-rest? false]
+ (if (seq bs)
+ (let [firstb (first bs)]
+ (cond
+ (= firstb '&) (recur (pb ret (second bs) (list `nthrest gvec n))
+ n
+ (rrest bs)
+ true)
+ (= firstb :as) (pb ret (second bs) gvec)
+ :else (if seen-rest?
+ (throw (new Exception "Unsupported binding form, only :as can follow & parameter"))
+ (recur (pb ret firstb (list `nth gvec n nil))
+ (inc n)
+ (rest bs)
+ seen-rest?))))
+ ret))))
+ pmap
+ (fn [bvec b v]
+ (let [gmap (or (:as b) (gensym "map__"))
+ defaults (:or b)]
+ (loop [ret (-> bvec (conj gmap) (conj v))
+ bes (reduce
+ (fn [bes entry]
+ (reduce #(assoc %1 %2 ((val entry) %2))
+ (dissoc bes (key entry))
+ ((key entry) bes)))
+ (dissoc b :as :or)
+ {:keys #(keyword (str %)), :strs str, :syms #(list `quote %)})]
+ (if (seq bes)
+ (let [bb (key (first bes))
+ bk (val (first bes))
+ has-default (contains? defaults bb)]
+ (recur (pb ret bb (if has-default
+ (list `get gmap bk (defaults bb))
+ (list `get gmap bk)))
+ (rest bes)))
+ ret))))]
+ (cond
+ (symbol? b) (-> bvec (conj b) (conj v))
+ (vector? b) (pvec bvec b v)
+ (map? b) (pmap bvec b v)
+ :else (throw (new Exception (str "Unsupported binding form: " b))))))
+ process-entry (fn [bvec b] (pb bvec (key b) (val b)))]
+ (if (every? symbol? (keys bmap))
+ bindings
+ (reduce process-entry [] bmap))))
+
+(defmacro let + "Evaluates the exprs in a lexical context in which the symbols in + the binding-forms are bound to their respective init-exprs or parts + therein." + [bindings & body] + (assert-args let + (vector? bindings) "a vector for its binding" + (even? (count bindings)) "an even number of forms in binding vector") + `(let* ~(destructure bindings) ~@body))
+
+;redefine fn with destructuring
+(defmacro fn
+ "(fn name? [params* ] exprs*)
+ (fn name? ([params* ] exprs*)+)
+
+ params => positional-params* , or positional-params* & rest-param
+ positional-param => binding-form
+ rest-param => binding-form
+ name => symbol
+
+ Defines a function"
+ [& sigs]
+ (let [name (if (symbol? (first sigs)) (first sigs) nil)
+ sigs (if name (rest sigs) sigs)
+ sigs (if (vector? (first sigs)) (list sigs) sigs)
+ psig (fn [sig]
+ (let [[params & body] sig]
+ (if (every? symbol? params)
+ sig
+ (loop [params params
+ new-params []
+ lets []]
+ (if params
+ (if (symbol? (first params))
+ (recur (rest params) (conj new-params (first params)) lets)
+ (let [gparam (gensym "p__")]
+ (recur (rest params) (conj new-params gparam)
+ (-> lets (conj (first params)) (conj gparam)))))
+ `(~new-params
+ (let ~lets
+ ~@body)))))))
+ new-sigs (map psig sigs)]
+ (with-meta
+ (if name
+ (list* 'fn* name new-sigs)
+ (cons 'fn* new-sigs))
+ *macro-meta*)))
+
+(defmacro loop
+ "Evaluates the exprs in a lexical context in which the symbols in
+ the binding-forms are bound to their respective init-exprs or parts
+ therein. Acts as a recur target."
+ [bindings & body] + (assert-args loop + (vector? bindings) "a vector for its binding" + (even? (count bindings)) "an even number of forms in binding vector") + (let [db (destructure bindings)] + (if (= db bindings) + `(loop* ~bindings ~@body) + (let [vs (take-nth 2 (drop 1 bindings)) + bs (take-nth 2 bindings) + gs (map (fn [b] (if (symbol? b) b (gensym))) bs) + bfs (reduce (fn [ret [b v g]] + (if (symbol? b) + (conj ret g v) + (conj ret g v b g))) + [] (map vector bs vs gs))] + `(let ~bfs + (loop* ~(vec (interleave gs gs)) + (let ~(vec (interleave bs gs)) + ~@body)))))))
+
+(defmacro when-first + "bindings => x xs + + Same as (when (seq xs) (let [x (first xs)] body))" + [bindings & body] + (assert-args when-first + (vector? bindings) "a vector for its binding" + (= 2 (count bindings)) "exactly 2 forms in binding vector") + (let [[x xs] bindings] + `(when (seq ~xs) + (let [~x (first ~xs)] + ~@body))))
+
+(defmacro lazy-cat
+ "Expands to code which yields a lazy sequence of the concatenation
+ of the supplied colls. Each coll expr is not evaluated until it is
+ needed."
+ ([coll] `(seq ~coll))
+ ([coll & colls]
+ `(let [iter# (fn iter# [coll#]
+ (if (seq coll#)
+ (lazy-cons (first coll#) (iter# (rest coll#)))
+ (lazy-cat ~@colls)))]
+ (iter# ~coll))))
+
+(defmacro for
+ "List comprehension. Takes a vector of one or more
+ binding-form/collection-expr pairs, each followed by an optional filtering
+ :when/:while expression (:when test or :while test), and yields a
+ lazy sequence of evaluations of expr. Collections are iterated in a
+ nested fashion, rightmost fastest, and nested coll-exprs can refer to
+ bindings created in prior binding-forms.
+
+ (take 100 (for [x (range 100000000) y (range 1000000) :while (< y x)] [x y]))"
+ ([seq-exprs expr]
+ (assert-args for + (vector? seq-exprs) "a vector for its binding" + (even? (count seq-exprs)) "an even number of forms in binding vector") + (let [to-groups (fn [seq-exprs] + (reduce (fn [groups [k v]] + (if (keyword? k) + (conj (pop groups) (assoc (peek groups) k v)) + (conj groups {:bind k :seq v}))) + [] (partition 2 seq-exprs))) + emit (fn emit [[group & [{next-seq :seq} :as more-groups]]] + (let [giter (gensym "iter__") gxs (gensym "s__")] + `(fn ~giter [~gxs] + (when-first [~(:bind group) ~gxs] + (when ~(or (:while group) true) + (if ~(or (:when group) true) + ~(if more-groups + `(let [iterys# ~(emit more-groups) + fs# (iterys# ~next-seq)] + (if fs# + (lazy-cat fs# (~giter (rest ~gxs))) + (recur (rest ~gxs)))) + `(lazy-cons ~expr (~giter (rest ~gxs)))) + (recur (rest ~gxs))))))))] + `(let [iter# ~(emit (to-groups seq-exprs))] + (iter# ~(second seq-exprs))))))
+
+(defmacro comment
+ "Ignores body, yields nil"
+ [& body])
+
+(defmacro with-out-str
+ "Evaluates exprs in a context in which *out* is bound to a fresh
+ StringWriter. Returns the string created by any nested printing
+ calls."
+ [& body]
+ `(let [s# (new System.IO.StringWriter)] ;;; Was java.io.StringWriter
+ (binding [*out* s#]
+ ~@body
+ (str s#))))
+
+(defmacro with-in-str
+ "Evaluates body in a context in which *in* is bound to a fresh
+ StringReader initialized with the string s."
+ [s & body]
+ `(with-open s# (-> (System.IO.StringReader. ~s) clojure.lang.Readers.LineNumberingReader.) ;;; were java.io.StringReader & clojure.lang.LineNumberingPushbackReader
+ (binding [*in* s#]
+ ~@body)))
+
+(defn pr-str
+ "pr to a string, returning it"
+ {:tag String}
+ [& xs]
+ (with-out-str
+ (apply pr xs)))
+
+(defn prn-str
+ "prn to a string, returning it"
+ {:tag String}
+ [& xs]
+ (with-out-str
+ (apply prn xs)))
+
+(defn print-str
+ "print to a string, returning it"
+ {:tag String}
+ [& xs]
+ (with-out-str
+ (apply print xs)))
+
+(defn println-str
+ "println to a string, returning it"
+ {:tag String}
+ [& xs]
+ (with-out-str
+ (apply println xs)))
+
+(defmacro assert
+ "Evaluates expr and throws an exception if it does not evaluate to
+ logical true."
+ [x]
+ `(when-not ~x
+ (throw (new Exception (str "Assert failed: " (pr-str '~x))))))
+
+(defn test
+ "test [v] finds fn at key :test in var metadata and calls it,
+ presuming failure will throw exception"
+ [v]
+ (let [f (:test ^v)]
+ (if f
+ (do (f) :ok)
+ :no-test)))
+;;;Need to take a closer look at CLR Regex vs Java Regex
+;(defn re-pattern +; "Returns an instance of java.util.regex.Pattern, for use, e.g. in +; re-matcher." +; {:tag java.util.regex.Pattern} +; [s] (if (instance? java.util.regex.Pattern s) +; s +; (. java.util.regex.Pattern (compile s))))
+;
+;(defn re-matcher
+; "Returns an instance of java.util.regex.Matcher, for use, e.g. in
+; re-find."
+; {:tag java.util.regex.Matcher}
+; [#^java.util.regex.Pattern re s]
+; (. re (matcher s)))
+;
+;(defn re-groups
+; "Returns the groups from the most recent match/find. If there are no
+; nested groups, returns a string of the entire match. If there are
+; nested groups, returns a vector of the groups, the first element
+; being the entire match."
+; [#^java.util.regex.Matcher m]
+; (let [gc (. m (groupCount))]
+; (if (zero? gc)
+; (. m (group))
+; (loop [ret [] c 0]
+; (if (<= c gc)
+; (recur (conj ret (. m (group c))) (inc c))
+; ret)))))
+;
+;(defn re-seq
+; "Returns a lazy sequence of successive matches of pattern in string,
+; using java.util.regex.Matcher.find(), each such match processed with
+; re-groups."
+; [#^java.util.regex.Pattern re s]
+; (let [m (re-matcher re s)]
+; ((fn step []
+; (when (. m (find))
+; (lazy-cons (re-groups m) (step)))))))
+;
+;(defn re-matches
+; "Returns the match, if any, of string to pattern, using
+; java.util.regex.Matcher.matches(). Uses re-groups to return the
+; groups."
+; [#^java.util.regex.Pattern re s]
+; (let [m (re-matcher re s)]
+; (when (. m (matches))
+; (re-groups m))))
+;
+;
+;(defn re-find
+; "Returns the next regex match, if any, of string to pattern, using
+; java.util.regex.Matcher.find(). Uses re-groups to return the
+; groups."
+; ([#^java.util.regex.Matcher m]
+; (when (. m (find))
+; (re-groups m)))
+; ([#^java.util.regex.Pattern re s]
+; (let [m (re-matcher re s)]
+; (re-find m))))
+
+(defn rand
+ "Returns a random floating point number between 0 (inclusive) and
+ 1 (exclusive)."
+ ([] (. clojure.lang.RT (random))) ;;; Math ==> RT. No Math.random in CLR.
+ ([n] (* n (rand))))
+
+(defn rand-int
+ "Returns a random integer between 0 (inclusive) and n (exclusive)."
+ [n] (int (rand n)))
+
+(defmacro defn-
+ "same as defn, yielding non-public def"
+ [name & decls]
+ (list* `defn (with-meta name (assoc (meta name) :private true)) decls))
+
+(defn print-doc [v]
+ (println "-------------------------")
+ (println (str (ns-name (:ns ^v)) "/" (:name ^v)))
+ (prn (:arglists ^v))
+ (when (:macro ^v)
+ (println "Macro"))
+ (println " " (:doc ^v)))
+;;; Needs re-pattern
+;(defn find-doc
+; "Prints documentation for any var whose documentation or name
+; contains a match for re-string"
+; [re-string]
+; (let [re (re-pattern re-string)]
+; (dorun (for [ns (all-ns)
+; v (sort-by (comp :name meta) (vals (ns-interns ns)))
+; :when (and (:doc ^v)
+; (or (re-find (re-matcher re (:doc ^v)))
+; (re-find (re-matcher re (str (:name ^v))))))]
+; (print-doc v)))))
+
+(defn special-form-anchor
+ "Returns the anchor tag on http://clojure.org/special_forms for the
+ special form x, or nil"
+ [x]
+ (#{'. 'def 'do 'fn 'if 'let 'loop 'monitor-enter 'monitor-exit 'new
+ 'quote 'recur 'set! 'throw 'try 'var} x))
+
+(defn syntax-symbol-anchor
+ "Returns the anchor tag on http://clojure.org/special_forms for the
+ special form that uses syntax symbol x, or nil"
+ [x]
+ ({'& 'fn 'catch 'try 'finally 'try} x))
+
+(defn print-special-doc
+ [name type anchor]
+ (println "-------------------------")
+ (println name)
+ (println type)
+ (println (str " Please see http://clojure.org/special_forms#" anchor)))
+;;; None of the doc stuff tested yet -- need printing and RE.
+(defn print-namespace-doc
+ "Print the documentation string of a Namespace."
+ [nspace]
+ (println "-------------------------")
+ (println (str (ns-name nspace)))
+ (println " " (:doc ^nspace)))
+
+(defmacro doc
+ "Prints documentation for a var or special form given its name"
+ [name]
+ (cond
+ (special-form-anchor `~name)
+ `(print-special-doc '~name "Special Form" (special-form-anchor '~name))
+ (syntax-symbol-anchor `~name)
+ `(print-special-doc '~name "Syntax Symbol" (syntax-symbol-anchor '~name))
+ :else
+ (let [nspace (find-ns name)]
+ (if nspace
+ `(print-namespace-doc ~nspace)
+ `(print-doc (var ~name))))))
+;;; Not tested yet.
+ (defn tree-seq
+ "Returns a lazy sequence of the nodes in a tree, via a depth-first walk.
+ branch? must be a fn of one arg that returns true if passed a node
+ that can have children (but may not). children must be a fn of one
+ arg that returns a sequence of the children. Will only be called on
+ nodes for which branch? returns true. Root is the root node of the
+ tree."
+ [branch? children root]
+ (let [walk (fn walk [node]
+ (lazy-cons node
+ (when (branch? node)
+ (mapcat walk (children node)))))]
+ (walk root)))
+;;; This will be harder in the CLR
+;(defn file-seq
+; "A tree seq on java.io.Files"
+; [dir]
+; (tree-seq
+; (fn [#^java.io.File f] (. f (isDirectory)))
+; (fn [#^java.io.File d] (seq (. d (listFiles))))
+; dir))
+;;; not tested
+(defn xml-seq
+ "A tree seq on the xml elements as per xml/parse"
+ [root]
+ (tree-seq
+ (complement string?)
+ (comp seq :content)
+ root))
+
+(defn special-symbol?
+ "Returns true if s names a special form"
+ [s]
+ (contains? (. clojure.lang.Compiler _specials) s)) ;;; specials => _specials, because I'm stubborn
+
+(defn var?
+ "Returns true if v is of type clojure.lang.Var"
+ [v] (instance? clojure.lang.Var v))
+
+(defn slurp
+ "Reads the file named by f into a string and returns it."
+ [#^String f]
+ (with-open [r (new System.IO.StreamReader f)] ;;; (new java.io.BufferedReader (new java.io.FileReader f))]
+ (let [sb (new StringBuilder)]
+ (loop [c (. r (Read))] ;;; read -> Read
+ (if (neg? c)
+ (str sb)
+ (do
+ (. sb (Append (char c))) ;;; append -> Append
+ (recur (. r (Read))))))))) ;;; read -> Read
+
+(defn subs
+ "Returns the substring of s beginning at start inclusive, and ending
+ at end (defaults to length of string), exclusive."
+ ([#^String s start] (. s (Substring start))) ;; substring => Substring
+ ([#^String s start end] (. s (Substring start (- end start))))) ;; was (substring start end) -- different interpretation of second arg
+
+(defn max-key
+ "Returns the x for which (k x), a number, is greatest."
+ ([k x] x)
+ ([k x y] (if (> (k x) (k y)) x y))
+ ([k x y & more]
+ (reduce #(max-key k %1 %2) (max-key k x y) more)))
+
+(defn min-key
+ "Returns the x for which (k x), a number, is least."
+ ([k x] x)
+ ([k x y] (if (< (k x) (k y)) x y))
+ ([k x y & more]
+ (reduce #(min-key k %1 %2) (min-key k x y) more)))
+
+(defn distinct
+ "Returns a lazy sequence of the elements of coll with duplicates removed"
+ [coll]
+ (let [step (fn step [[f & r :as xs] seen]
+ (when xs
+ (if (contains? seen f) (recur r seen)
+ (lazy-cons f (step r (conj seen f))))))]
+ (step (seq coll) #{})))
+;;; NOT TESTED YET
+(defmacro if-let
+ "bindings => binding-form test
+
+ If test is true, evaluates then with binding-form bound to the value of test, if not, yields else" + ([bindings then] + `(if-let ~bindings ~then nil)) + ([bindings then else & oldform] + (assert-args if-let + (and (vector? bindings) (nil? oldform)) "a vector for its binding" + (= 2 (count bindings)) "exactly 2 forms in binding vector") + (let [[form tst] bindings] + `(let [temp# ~tst] + (if temp# + (let [~form temp#] + ~then) + ~else)))))
+;;; NOT TESTED YET
+(defmacro when-let
+ "bindings => binding-form test
+
+ When test is true, evaluates body with binding-form bound to the value of test"
+ [bindings & body]
+ (assert-args when-let + (vector? bindings) "a vector for its binding" + (= 2 (count bindings)) "exactly 2 forms in binding vector") + (let [[form tst] bindings] + `(let [temp# ~tst] + (when temp# + (let [~form temp#] + ~@body)))))
+
+(defn replace
+ "Given a map of replacement pairs and a vector/collection, returns a
+ vector/seq with any elements = a key in smap replaced with the
+ corresponding val in smap"
+ [smap coll]
+ (if (vector? coll)
+ (reduce (fn [v i]
+ (if-let [e (find smap (nth v i))]
+ (assoc v i (val e))
+ v))
+ coll (range (count coll)))
+ (map #(if-let [e (find smap %)] (val e) %) coll)))
+
+(defmacro dosync
+ "Runs the exprs (in an implicit do) in a transaction that encompasses
+ exprs and any nested calls. Starts a transaction if none is already
+ running on this thread. Any uncaught exception will abort the
+ transaction and flow out of dosync. The exprs may be run more than
+ once, but any effects on Refs will be atomic."
+ [& exprs]
+ `(sync nil ~@exprs))
+;;; Figure out equivalent for CLR
+;(defmacro with-precision
+; "Sets the precision and rounding mode to be used for BigDecimal operations.
+;
+; Usage: (with-precision 10 (/ 1M 3))
+; or: (with-precision 10 :rounding HALF_DOWN (/ 1M 3))
+;
+; The rounding mode is one of CEILING, FLOOR, HALF_UP, HALF_DOWN,
+; HALF_EVEN, UP, DOWN and UNNECESSARY; it defaults to HALF_UP."
+; [precision & exprs]
+; (let [[body rm] (if (= (first exprs) :rounding)
+; [(rest (rest exprs))
+; `((. java.math.RoundingMode ~(second exprs)))]
+; [exprs nil])]
+; `(binding [*math-context* (java.math.MathContext. ~precision ~@rm)]
+; ~@body)))
+
+(defn bound-fn
+ {:private true}
+ [#^clojure.lang.Sorted sc test key]
+ (fn [e]
+ (test (.. sc comparator (compare (. sc entryKey e) key)) 0)))
+
+(defn subseq
+ "sc must be a sorted collection, test(s) one of <, <=, > or
+ >=. Returns a seq of those entries with keys ek for
+ which (test (.. sc comparator (compare ek key)) 0) is true"
+ ([#^clojure.lang.Sorted sc test key]
+ (let [include (bound-fn sc test key)]
+ (if (#{> >=} test)
+ (when-let [[e :as s] (. sc seqFrom key true)]
+ (if (include e) s (rest s)))
+ (take-while include (. sc seq true)))))
+ ([#^clojure.lang.Sorted sc start-test start-key end-test end-key]
+ (when-let [[e :as s] (. sc seqFrom start-key true)]
+ (take-while (bound-fn sc end-test end-key)
+ (if ((bound-fn sc start-test start-key) e) s (rest s))))))
+
+(defn rsubseq
+ "sc must be a sorted collection, test(s) one of <, <=, > or
+ >=. Returns a reverse seq of those entries with keys ek for
+ which (test (.. sc comparator (compare ek key)) 0) is true"
+ ([#^clojure.lang.Sorted sc test key]
+ (let [include (bound-fn sc test key)]
+ (if (#{< <=} test)
+ (when-let [[e :as s] (. sc seqFrom key false)]
+ (if (include e) s (rest s)))
+ (take-while include (. sc seq false)))))
+ ([#^clojure.lang.Sorted sc start-test start-key end-test end-key]
+ (when-let [[e :as s] (. sc seqFrom end-key false)]
+ (take-while (bound-fn sc start-test start-key)
+ (if ((bound-fn sc end-test end-key) e) s (rest s))))))
+
+(defn repeatedly
+ "Takes a function of no args, presumably with side effects, and returns an infinite
+ lazy sequence of calls to it"
+ [f] (lazy-cons (f) (repeatedly f)))
+;;; What is CLR equivalent -- should this just be a no-op?
+;(defn add-classpath
+; "Adds the url (String or URL object) to the classpath per URLClassLoader.addURL"
+; [url] (. clojure.lang.RT addURL url))
+
+
+
+(defn hash
+ "Returns the hash code of its argument"
+ [x] (. clojure.lang.Util (hash x)))
+
+(defn interpose
+ "Returns a lazy seq of the elements of coll separated by sep"
+ [sep coll] (drop 1 (interleave (repeat sep) coll)))
+
+(defmacro definline
+ "Experimental - like defmacro, except defines a named function whose
+ body is the expansion, calls to which may be expanded inline as if
+ it were a macro. Cannot be used with variadic (&) args."
+ [name & decl]
+ (let [[pre-args [args expr]] (split-with (comp not vector?) decl)]
+ `(do
+ (defn ~name ~@pre-args ~args ~(apply (eval (list `fn args expr)) args))
+ (alter-meta! (var ~name) assoc :inline (fn ~args ~expr))
+ (var ~name))))
+
+(defn empty
+ "Returns an empty collection of the same category as coll, or nil"
+ [#^clojure.lang.IPersistentCollection coll]
+ (.empty coll))
+
+(defmacro amap
+ "Maps an expression across an array a, using an index named idx, and
+ return value named ret, initialized to a clone of a, then setting each element of
+ ret to the evaluation of expr, returning the new array ret."
+ [a idx ret expr]
+ `(let [a# ~a
+ ~ret (aclone a#)]
+ (loop [~idx (int 0)]
+ (if (< ~idx (alength a#))
+ (do
+ (aset ~ret ~idx ~expr)
+ (recur (unchecked-inc ~idx)))
+ ~ret))))
+;;; How do you use this? How can you get the value of the array at the current index?
+(defmacro areduce
+ "Reduces an expression across an array a, using an index named idx,
+ and return value named ret, initialized to init, setting ret to the evaluation of expr at
+ each step, returning ret."
+ [a idx ret init expr]
+ `(let [a# ~a]
+ (loop [~idx (int 0) ~ret ~init]
+ (if (< ~idx (alength a#))
+ (recur (unchecked-inc ~idx) ~expr)
+ ~ret))))
+;;; NOT WORTH THE EFFORT AT THE MOMENT
+;(defn float-array
+; "Creates an array of floats"
+; {:inline (fn [& args] `(. clojure.lang.Numbers float_array ~@args))
+; :inline-arities #{1 2}}
+; ([size-or-seq] (. clojure.lang.Numbers float_array size-or-seq))
+; ([size init-val-or-seq] (. clojure.lang.Numbers float_array size init-val-or-seq)))
+;
+;(defn double-array
+; "Creates an array of doubles"
+; {:inline (fn [& args] `(. clojure.lang.Numbers double_array ~@args))
+; :inline-arities #{1 2}}
+; ([size-or-seq] (. clojure.lang.Numbers double_array size-or-seq))
+; ([size init-val-or-seq] (. clojure.lang.Numbers double_array size init-val-or-seq)))
+;
+;(defn int-array
+; "Creates an array of ints"
+; {:inline (fn [& args] `(. clojure.lang.Numbers int_array ~@args))
+; :inline-arities #{1 2}}
+; ([size-or-seq] (. clojure.lang.Numbers int_array size-or-seq))
+; ([size init-val-or-seq] (. clojure.lang.Numbers int_array size init-val-or-seq)))
+;
+;(defn long-array
+; "Creates an array of ints"
+; {:inline (fn [& args] `(. clojure.lang.Numbers long_array ~@args))
+; :inline-arities #{1 2}}
+; ([size-or-seq] (. clojure.lang.Numbers long_array size-or-seq))
+; ([size init-val-or-seq] (. clojure.lang.Numbers long_array size init-val-or-seq)))
+;
+;(definline floats
+; "Casts to float[]"
+; [xs] `(. clojure.lang.Numbers floats ~xs))
+;
+;(definline ints
+; "Casts to int[]"
+; [xs] `(. clojure.lang.Numbers ints ~xs))
+;
+;(definline doubles
+; "Casts to double[]"
+; [xs] `(. clojure.lang.Numbers doubles ~xs))
+;
+;(definline longs
+; "Casts to long[]"
+; [xs] `(. clojure.lang.Numbers longs ~xs))
+;
+;(import '(java.util.concurrent BlockingQueue LinkedBlockingQueue))
+;;;NOT WORTH THE EFFORT AT THE MOMENT
+;(defn seque
+; "Creates a queued seq on another (presumably lazy) seq s. The queued
+; seq will produce a concrete seq in the background, and can get up to
+; n items ahead of the consumer. n-or-q can be an integer n buffer
+; size, or an instance of java.util.concurrent BlockingQueue. Note
+; that reading from a seque can block if the reader gets ahead of the
+; producer."
+; ([s] (seque 100 s))
+; ([n-or-q s]
+; (let [#^BlockingQueue q (if (instance? BlockingQueue n-or-q)
+; n-or-q
+; (LinkedBlockingQueue. (int n-or-q)))
+; NIL (Object.) ;nil sentinel since LBQ doesn't support nils
+; agt (agent (seq s))
+; fill (fn [s]
+; (try
+; (loop [[x & xs :as s] s]
+; (if s
+; (if (.offer q (if (nil? x) NIL x))
+; (recur xs)
+; s)
+; (.put q q))) ; q itself is eos sentinel
+; (catch Exception e
+; (.put q q)
+; (throw e))))
+; drain (fn drain []
+; (let [x (.take q)]
+; (if (identical? x q) ;q itself is eos sentinel
+; @agt ;will be nil - touch agent just to propagate errors
+; (do
+; (send-off agt fill)
+; (lazy-cons (if (identical? x NIL) nil x) (drain))))))]
+; (send-off agt fill)
+; (drain))))
+
+(defn class?
+ "Returns true if x is an instance of Class"
+ [x] (instance? Type x)) ;; Class ==> Type
+
+(defn alter-var-root
+ "Atomically alters the root binding of var v by applying f to its
+ current value plus any args"
+ [#^clojure.lang.Var v f & args] (.alterRoot v f args))
+
+(defn make-hierarchy
+ "Creates a hierarchy object for use with derive, isa? etc."
+ [] {:parents {} :descendants {} :ancestors {}})
+
+(def #^{:private true}
+ global-hierarchy (make-hierarchy))
+
+(defn not-empty
+ "If coll is empty, returns nil, else coll"
+ [coll] (when (seq coll) coll))
+
+(defn bases
+ "Returns the immediate superclass and direct interfaces of c, if any"
+ [#^Type c] ;;; Class ==> Type
+ (let [i (.GetInterfaces c) ;;; .getInterfaces ==> .GetInterfaces
+ s (.BaseType c)] ;;; .getSuperclass ==> BaseType
+ (not-empty
+ (if s (cons s i) i))))
+
+(defn supers
+ "Returns the immediate and indirect superclasses and interfaces of c, if any"
+ [#^Type class] ;;; Class ==> Type
+ (loop [ret (set (bases class)) cs ret]
+ (if (seq cs)
+ (let [c (first cs) bs (bases c)]
+ (recur (into ret bs) (into (disj cs c) bs)))
+ (not-empty ret))))
+
+(defn isa?
+ "Returns true if (= child parent), or child is directly or indirectly derived from
+ parent, either via a Java type inheritance relationship or a
+ relationship established via derive. h must be a hierarchy obtained
+ from make-hierarchy, if not supplied defaults to the global
+ hierarchy"
+ ([child parent] (isa? global-hierarchy child parent))
+ ([h child parent]
+ (or (= child parent)
+ (and (class? parent) (class? child)
+ (. #^Type parent IsAssignableFrom child)) ;;; Class ==> Type, isAssignableFrom
+ (contains? ((:ancestors h) child) parent)
+ (and (class? child) (some #(contains? ((:ancestors h) %) parent) (supers child)))
+ (and (vector? parent) (vector? child)
+ (= (count parent) (count child))
+ (loop [ret true i 0]
+ (if (or (not ret) (= i (count parent)))
+ ret
+ (recur (isa? h (child i) (parent i)) (inc i))))))))
+
+(defn parents
+ "Returns the immediate parents of tag, either via a Java type
+ inheritance relationship or a relationship established via derive. h
+ must be a hierarchy obtained from make-hierarchy, if not supplied
+ defaults to the global hierarchy"
+ ([tag] (parents global-hierarchy tag))
+ ([h tag] (not-empty
+ (let [tp (get (:parents h) tag)]
+ (if (class? tag)
+ (into (set (bases tag)) tp)
+ tp)))))
+;;; NOT TESTED YET
+(defn ancestors
+ "Returns the immediate and indirect parents of tag, either via a Java type
+ inheritance relationship or a relationship established via derive. h
+ must be a hierarchy obtained from make-hierarchy, if not supplied
+ defaults to the global hierarchy"
+ ([tag] (ancestors global-hierarchy tag))
+ ([h tag] (not-empty
+ (let [ta (get (:ancestors h) tag)]
+ (if (class? tag)
+ (into (set (supers tag)) ta)
+ ta)))))
+;;; NOT TESTED YET
+(defn descendants
+ "Returns the immediate and indirect children of tag, through a
+ relationship established via derive. h must be a hierarchy obtained
+ from make-hierarchy, if not supplied defaults to the global
+ hierarchy. Note: does not work on Java type inheritance
+ relationships."
+ ([tag] (descendants global-hierarchy tag))
+ ([h tag] (if (class? tag)
+ (throw (NotImplementedException. "Can't get descendants of classes")) ;;; java.lang.UnsupportedOperationException --> NotImplementedException
+ (not-empty (get (:descendants h) tag)))))
+;;; NOT TESTED YET
+(defn derive
+ "Establishes a parent/child relationship between parent and
+ tag. Parent must be a namespace-qualified symbol or keyword and
+ child can be either a namespace-qualified symbol or keyword or a
+ class. h must be a hierarchy obtained from make-hierarchy, if not
+ supplied defaults to, and modifies, the global hierarchy."
+ ([tag parent] + (assert (namespace parent)) + (assert (or (class? tag) (and (instance? clojure.lang.Named tag) (namespace tag)))) + + (alter-var-root #'global-hierarchy derive tag parent) nil) + ([h tag parent] + (assert (not= tag parent)) + (assert (or (class? tag) (instance? clojure.lang.Named tag))) + (assert (instance? clojure.lang.Named parent)) + + (let [tp (:parents h) + td (:descendants h) + ta (:ancestors h) + tf (fn [m source sources target targets] + (reduce (fn [ret k] + (assoc ret k + (reduce conj (get targets k #{}) (cons target (targets target))))) + m (cons source (sources source))))] + (or + (when-not (contains? (tp tag) parent) + (when (contains? (ta tag) parent) + (throw (Exception. (print-str tag "already has" parent "as ancestor")))) + (when (contains? (ta parent) tag) + (throw (Exception. (print-str "Cyclic derivation:" parent "has" tag "as ancestor")))) + {:parents (assoc (:parents h) tag (conj (get tp tag #{}) parent)) + :ancestors (tf (:ancestors h) tag td parent ta) + :descendants (tf (:descendants h) parent ta tag td)}) + h))))
+;;; NOT TESTED YET
+(defn underive
+ "Removes a parent/child relationship between parent and
+ tag. h must be a hierarchy obtained from make-hierarchy, if not
+ supplied defaults to, and modifies, the global hierarchy."
+ ([tag parent] (alter-var-root #'global-hierarchy underive tag parent) nil)
+ ([h tag parent]
+ (let [tp (:parents h)
+ td (:descendants h)
+ ta (:ancestors h)
+ tf (fn [m source sources target targets]
+ (reduce
+ (fn [ret k]
+ (assoc ret k
+ (reduce disj (get targets k) (cons target (targets target)))))
+ m (cons source (sources source))))]
+ (if (contains? (tp tag) parent)
+ {:parent (assoc (:parents h) tag (disj (get tp tag) parent))
+ :ancestors (tf (:ancestors h) tag td parent ta)
+ :descendants (tf (:descendants h) parent ta tag td)}
+ h))))
+
+
+(defn distinct?
+ "Returns true if no two of the arguments are ="
+ {:tag Boolean}
+ ([x] true)
+ ([x y] (not (= x y)))
+ ([x y & more]
+ (if (not= x y)
+ (loop [s #{x y} [x & etc :as xs] more]
+ (if xs
+ (if (contains? s x)
+ false
+ (recur (conj s x) etc))
+ true))
+ false)))
+;;; later (boring)
+;(defn iterator-seq
+; "Returns a seq on a java.util.Iterator. Note that most collections
+; providing iterators implement Iterable and thus support seq directly."
+; [iter]
+; (clojure.lang.IteratorSeq/create iter))
+;
+;(defn enumeration-seq
+; "Returns a seq on a java.lang.Enumeration"
+; [e]
+; (clojure.lang.EnumerationSeq/create e))
+;;; Should we make compatible with Java?
+(defn format
+ "Formats a string using java.lang.String.format, see java.util.Formatter for format
+ string syntax"
+ {:tag String}
+ [fmt & args]
+ (String/Format fmt (to-array args))) ;; format => Format
+
+(defn printf
+ "Prints formatted output, as per format"
+ [fmt & args]
+ (print (apply format fmt args)))
+
+(def gen-class) + +(defmacro ns + "Sets *ns* to the namespace named by name (unevaluated), creating it + if needed. references can be zero or more of: (:refer-clojure ...) + (:require ...) (:use ...) (:import ...) (:load ...) (:gen-class) + with the syntax of refer-clojure/require/use/import/load/gen-class + respectively, except the arguments are unevaluated and need not be + quoted. (:gen-class ...), when supplied, defaults to :name + corresponding to the ns name, :main true, :impl-ns same as ns, and + :init-impl-ns true. All options of gen-class are + supported. The :gen-class directive is ignored when not + compiling. If :gen-class is not supplied, when compiled only an + nsname__init.class will be generated. If :refer-clojure is not used, a + default (refer 'clojure) is used. Use of ns is preferred to + individual calls to in-ns/require/use/import: + + (ns foo.bar + (:refer-clojure :exclude [ancestors printf]) + (:require (clojure.contrib sql sql.tests)) + (:use (my.lib this that)) + (:import (java.util Date Timer Random) + (java.sql Connection Statement)))" + + [name & references] + (let [process-reference + (fn [[kname & args]] + `(~(symbol "clojure.core" (clojure.core/name kname)) + ~@(map #(list 'quote %) args))) + docstring (when (string? (first references)) (first references))
+ references (if docstring (rest references) references)
+ name (if docstring
+ (with-meta name (assoc (meta name)
+ :doc docstring))
+ name)
+ gen-class-clause (first (filter #(= :gen-class (first %)) references)) + gen-class-call + (when gen-class-clause + (list* `gen-class :name (.replace (str name) \- \_) :impl-ns name :main true (rest gen-class-clause))) + references (remove #(= :gen-class (first %)) references)] + `(do + (clojure.core/in-ns '~name) + ~@(when gen-class-call (list gen-class-call)) + ~@(when (and (not= name 'clojure.core) (not-any? #(= :refer-clojure (first %)) references)) + `((clojure.core/refer '~'clojure.core))) + ~@(map process-reference references))))
+
+(defmacro refer-clojure
+ "Same as (refer 'clojure.core <filters>)"
+ [& filters]
+ `(clojure.core/refer '~'clojure.core ~@filters))
+
+(defmacro defonce
+ "defs name to have the root value of the expr iff the named var has no root value,
+ else expr is unevaluated"
+ [name expr]
+ `(let [v# (def ~name)]
+ (when-not (.hasRoot v#)
+ (def ~name ~expr))))
+
+;;;;;;;;;;; require/use/load, contributed by Stephen C. Gilardi ;;;;;;;;;;;;;;;;;;
+
+(defonce
+ #^{:private true
+ :doc "A ref to a sorted set of symbols representing loaded libs"}
+ *loaded-libs* (ref (sorted-set)))
+
+(defonce
+ #^{:private true
+ :doc "the set of paths currently being loaded by this thread"}
+ *pending-paths* #{})
+
+(defonce
+ #^{:private true :doc
+ "True while a verbose load is pending"}
+ *loading-verbosely* false)
+
+(defn- throw-if
+ "Throws an exception with a message if pred is true"
+ [pred fmt & args]
+ (when pred
+ (let [ #^String message (apply format fmt args)
+ exception (Exception. message)
+ ;; can't set the stacktrace ---- raw-trace (.getStackTrace exception)
+ ;; ---- boring? #(not= (.getMethodName #^StackTraceElement %) "doInvoke")
+ ];; ---- trace (into-array (drop 2 (drop-while boring? raw-trace)))]
+ ;;; ---- (.setStackTrace exception trace)
+ (throw exception))))
+
+(defn- libspec?
+ "Returns true if x is a libspec"
+ [x]
+ (or (symbol? x)
+ (and (vector? x)
+ (or
+ (nil? (second x))
+ (keyword? (second x))))))
+
+(defn- prependss
+ "Prepends a symbol or a seq to coll"
+ [x coll]
+ (if (symbol? x)
+ (cons x coll)
+ (concat x coll)))
+
+(defn- root-resource
+ "Returns the root directory path for a lib"
+ {:tag String}
+ [lib]
+ (str \/
+ (.. (name lib)
+ (replace \- \_)
+ (replace \. \/))))
+
+(defn- root-directory
+ "Returns the root resource path for a lib"
+ [lib]
+ (let [d (root-resource lib)]
+ (subs d 0 (.lastIndexOf d "/"))))
+
+(def load)
+
+(defn- load-one
+ "Loads a lib given its name. If need-ns, ensures that the associated
+ namespace exists after loading. If require, records the load so any
+ duplicate loads can be skipped."
+ [lib need-ns require]
+ (load (root-resource lib))
+ (throw-if (and need-ns (not (find-ns lib)))
+ "namespace '%s' not found after loading '%s'"
+ lib (root-resource lib))
+ (when require
+ (dosync
+ (commute *loaded-libs* conj lib))))
+
+(defn- load-all
+ "Loads a lib given its name and forces a load of any libs it directly or
+ indirectly loads. If need-ns, ensures that the associated namespace
+ exists after loading. If require, records the load so any duplicate loads
+ can be skipped."
+ [lib need-ns require]
+ (dosync
+ (commute *loaded-libs* #(reduce conj %1 %2)
+ (binding [*loaded-libs* (ref (sorted-set))]
+ (load-one lib need-ns require)
+ @*loaded-libs*))))
+
+(defn- load-lib
+ "Loads a lib with options"
+ [prefix lib & options]
+ (throw-if (and prefix (pos? (.indexOf (name lib) (int \.))))
+ "lib names inside prefix lists must not contain periods")
+ (let [lib (if prefix (symbol (str prefix \. lib)) lib)
+ opts (apply hash-map options)
+ {:keys [as reload reload-all require use verbose]} opts
+ loaded (contains? @*loaded-libs* lib)
+ load (cond reload-all
+ load-all
+ (or reload (not require) (not loaded))
+ load-one)
+ need-ns (or as use)
+ filter-opts (select-keys opts '(:exclude :only :rename))]
+ (binding [*loading-verbosely* (or *loading-verbosely* verbose)]
+ (if load
+ (load lib need-ns require)
+ (throw-if (and need-ns (not (find-ns lib)))
+ "namespace '%s' not found" lib))
+ (when (and need-ns *loading-verbosely*)
+ (printf "(clojure.core/in-ns '%s)\n" (ns-name *ns*)))
+ (when as
+ (when *loading-verbosely*
+ (printf "(clojure.core/alias '%s '%s)\n" as lib))
+ (alias as lib))
+ (when use
+ (when *loading-verbosely*
+ (printf "(clojure.core/refer '%s" lib)
+ (doseq [opt filter-opts]
+ (printf " %s '%s" (key opt) (print-str (val opt))))
+ (printf ")\n"))
+ (apply refer lib (mapcat seq filter-opts))))))
+
+(defn- load-libs
+ "Loads libs, interpreting libspecs, prefix lists, and flags for
+ forwarding to load-lib"
+ [& args]
+ (let [flags (filter keyword? args)
+ opts (interleave flags (repeat true))
+ args (filter (complement keyword?) args)]
+ (doseq [arg args]
+ (if (libspec? arg)
+ (apply load-lib nil (prependss arg opts))
+ (let [[prefix & args] arg]
+ (throw-if (nil? prefix) "prefix cannot be nil")
+ (doseq [arg args]
+ (apply load-lib prefix (prependss arg opts))))))))
+
+;; Public
+
+(defn require
+ "Loads libs, skipping any that are already loaded. Each argument is
+ either a libspec that identifies a lib, a prefix list that identifies
+ multiple libs whose names share a common prefix, or a flag that modifies
+ how all the identified libs are loaded. Use :require in the ns macro
+ in preference to calling this directly.
+
+ Libs
+
+ A 'lib' is a named set of resources in classpath whose contents define a
+ library of Clojure code. Lib names are symbols and each lib is associated
+ with a Clojure namespace and a Java package that share its name. A lib's
+ name also locates its root directory within classpath using Java's
+ package name to classpath-relative path mapping. All resources in a lib
+ should be contained in the directory structure under its root directory.
+ All definitions a lib makes should be in its associated namespace.
+
+ 'require loads a lib by loading its root resource. The root resource path
+ is derived from the root directory path by repeating its last component
+ and appending '.clj'. For example, the lib 'x.y.z has root directory
+ <classpath>/x/y/z; root resource <classpath>/x/y/z/z.clj. The root
+ resource should contain code to create the lib's namespace and load any
+ additional lib resources.
+
+ Libspecs
+
+ A libspec is a lib name or a vector containing a lib name followed by
+ options expressed as sequential keywords and arguments.
+
+ Recognized options: :as
+ :as takes a symbol as its argument and makes that symbol an alias to the
+ lib's namespace in the current namespace.
+
+ Prefix Lists
+
+ It's common for Clojure code to depend on several libs whose names have
+ the same prefix. When specifying libs, prefix lists can be used to reduce
+ repetition. A prefix list contains the shared prefix followed by libspecs
+ with the shared prefix removed from the lib names. After removing the
+ prefix, the names that remain must not contain any periods.
+
+ Flags
+
+ A flag is a keyword.
+ Recognized flags: :reload, :reload-all, :verbose
+ :reload forces loading of all the identified libs even if they are
+ already loaded
+ :reload-all implies :reload and also forces loading of all libs that the
+ identified libs directly or indirectly load via require or use
+ :verbose triggers printing information about each load, alias, and refer"
+
+ [& args]
+ (apply load-libs :require args))
+
+(defn use
+ "Like 'require, but also refers to each lib's namespace using
+ clojure.core/refer. Use :use in the ns macro in preference to calling
+ this directly.
+
+ 'use accepts additional options in libspecs: :exclude, :only, :rename.
+ The arguments and semantics for :exclude, :only, and :rename are the same
+ as those documented for clojure.core/refer."
+ [& args] (apply load-libs :require :use args))
+
+(defn loaded-libs
+ "Returns a sorted set of symbols naming the currently loaded libs"
+ [] @*loaded-libs*)
+
+(defn load + "Loads Clojure code from resources in classpath. A path is interpreted as + classpath-relative if it begins with a slash or relative to the root + directory for the current namespace otherwise." + [& paths] + (doseq [#^String path paths] + (let [#^String path (if (.startsWith path "/") + path + (str (root-directory (ns-name *ns*)) \/ path))] + (when *loading-verbosely* + (printf "(clojure.core/load \"%s\")\n" path) + (flush)) +; (throw-if (*pending-paths* path) +; "cannot load '%s' again while it is loading" +; path) + (when-not (*pending-paths* path) + (binding [*pending-paths* (conj *pending-paths* path)] + (clojure.lang.RT/load (.substring path 1)))))))
+
+(defn compile + "Compiles the namespace named by the symbol lib into a set of + classfiles. The source for the lib must be in a proper + classpath-relative directory. The output files will go into the + directory specified by *compile-path*, and that directory too must + be in the classpath." + [lib] + (binding [*compile-files* true] + (load-one lib true true)) + lib)
+
+;;;;;;;;;;;;; nested associative ops ;;;;;;;;;;;
+
+(defn get-in
+ "returns the value in a nested associative structure, where ks is a sequence of keys"
+ [m ks]
+ (reduce get m ks))
+
+(defn assoc-in
+ "Associates a value in a nested associative structure, where ks is a
+ sequence of keys and v is the new value and returns a new nested structure.
+ If any levels do not exist, hash-maps will be created."
+ [m [k & ks] v]
+ (if ks
+ (assoc m k (assoc-in (get m k) ks v))
+ (assoc m k v)))
+
+(defn update-in
+ "'Updates' a value in a nested associative structure, where ks is a
+ sequence of keys and f is a function that will take the old value
+ and any supplied args and return the new value, and returns a new
+ nested structure. If any levels do not exist, hash-maps will be
+ created."
+ ([m [k & ks] f & args]
+ (if ks
+ (assoc m k (apply update-in (get m k) ks f args))
+ (assoc m k (apply f (get m k) args)))))
+
+
+(defn empty?
+ "Returns true if coll has no items - same as (not (seq coll)).
+ Please use the idiom (seq x) rather than (not (empty? x))"
+ [coll] (not (seq coll)))
+
+(defn coll?
+ "Returns true if x implements IPersistentCollection"
+ [x] (instance? clojure.lang.IPersistentCollection x))
+
+(defn list?
+ "Returns true if x implements IPersistentList"
+ [x] (instance? clojure.lang.IPersistentList x))
+
+(defn set?
+ "Returns true if x implements IPersistentSet"
+ [x] (instance? clojure.lang.IPersistentSet x))
+
+(defn ifn? + "Returns true if x implements IFn. Note that many data structures + (e.g. sets and maps) implement IFn" + [x] (instance? clojure.lang.IFn x))
+
+(defn fn? + "Returns true if x implements Fn, i.e. is an object created via fn." + [x] (instance? clojure.lang.Fn x))
+
+
+(defn associative?
+ "Returns true if coll implements Associative"
+ [coll] (instance? clojure.lang.Associative coll))
+
+(defn sequential?
+ "Returns true if coll implements Sequential"
+ [coll] (instance? clojure.lang.Sequential coll))
+
+(defn sorted?
+ "Returns true if coll implements Sorted"
+ [coll] (instance? clojure.lang.Sorted coll))
+
+(defn counted?
+ "Returns true if coll implements count in constant time"
+ [coll] (instance? clojure.lang.Counted coll))
+
+(defn reversible?
+ "Returns true if coll implements Reversible"
+ [coll] (instance? clojure.lang.Reversible coll))
+
+(def
+ #^{:doc "bound in a repl thread to the most recent value printed"}
+ *1)
+
+(def
+ #^{:doc "bound in a repl thread to the second most recent value printed"}
+ *2)
+
+(def
+ #^{:doc "bound in a repl thread to the third most recent value printed"}
+ *3)
+
+(def
+ #^{:doc "bound in a repl thread to the most recent exception caught by the repl"}
+ *e)
+
+(defmacro declare
+ "defs the supplied var names with no bindings, useful for making forward declarations."
+ [& names] `(do ~@(map #(list 'def %) names)))
+
+(defn trampoline + "trampoline can be used to convert algorithms requiring mutual + recursion without stack consumption. Calls f with supplied args, if + any. If f returns a fn, calls that fn with no arguments, and + continues to repeat, until the return value is not a fn, then + returns that non-fn value. Note that if you want to return a fn as a + final value, you must wrap it in some data structure and unpack it + after trampoline returns." + ([f] + (let [ret (f)] + (if (fn? ret) + (recur ret) + ret))) + ([f & args] + (trampoline #(apply f args)))) + +(defn intern + "Finds or creates a var named by the symbol name in the namespace + ns (which can be a symbol or a namespace), setting its root binding + to val if supplied. The namespace must exist. The var will adopt any + metadata from the name symbol. Returns the var." + ([ns #^clojure.lang.Symbol name] + (let [v (clojure.lang.Var/intern (the-ns ns) name)] + (when ^name (.setMeta v ^name)) + v)) + ([ns name val] + (let [v (clojure.lang.Var/intern (the-ns ns) name val)] + (when ^name (.setMeta v ^name)) + v))) + +(defmacro while + "Repeatedly executes body while test expression is true. Presumes + some side-effect will cause test to become false/nil. Returns nil" + [test & body] + `(loop [] + (when ~test + ~@body + (recur)))) + +(defn memoize + "Returns a memoized version of a referentially transparent function. The + memoized version of the function keeps a cache of the mapping from arguments + to results and, when calls with the same arguments are repeated often, has + higher performance at the expense of higher memory use." + [f] + (let [mem (atom {})] + (fn [& args] + (if-let [e (find @mem args)] + (val e) + (let [ret (apply f args)] + (swap! mem assoc args ret) + ret))))) + +(defmacro condp + "Takes a binary predicate, an expression, and a set of clauses. + Each clause can take the form of either: + + test-expr result-expr + + test-expr :>> result-fn + + Note :>> is an ordinary keyword. + + For each clause, (pred test-expr expr) is evaluated. If it returns + logical true, the clause is a match. If a binary clause matches, the + result-expr is returned, if a ternary clause matches, its result-fn, + which must be a unary function, is called with the result of the + predicate as its argument, the result of that call being the return + value of condp. A single default expression can follow the clauses, + and its value will be returned if no clause matches. If no default + expression is provided and no clause matches, an + IllegalArgumentException is thrown." + + [pred expr & clauses] + (let [gpred (gensym "pred__") + gexpr (gensym "expr__") + emit (fn emit [pred expr args] + (let [[[a b c :as clause] more] + (split-at (if (= :>> (second args)) 3 2) args) + n (count clause)] + (cond + (= 0 n) `(throw (IllegalArgumentException. "No matching clause")) + (= 1 n) a + (= 2 n) `(if (~pred ~a ~expr) + ~b + ~(emit pred expr more)) + :else `(if-let [p# (~pred ~a ~expr)] + (~c p#) + ~(emit pred expr more))))) + gres (gensym "res__")] + `(let [~gpred ~pred + ~gexpr ~expr] + ~(emit gpred gexpr clauses))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; var documentation ;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defmacro add-doc {:private true} [name docstring]
+ `(alter-meta! (var ~name) assoc :doc ~docstring))
+
+(add-doc *file*
+ "The path of the file being evaluated, as a String.
+
+ Evaluates to nil when there is no file, eg. in the REPL.")
+
+(add-doc *command-line-args*
+ "A sequence of the supplied command line arguments, or nil if
+ none were supplied")
+
+(add-doc *warn-on-reflection*
+ "When set to true, the compiler will emit warnings when reflection is
+ needed to resolve Java method calls or field accesses.
+
+ Defaults to false.")
+
+(add-doc *compile-path*
+ "Specifies the directory where 'compile' will write out .class
+ files. This directory must be in the classpath for 'compile' to
+ work.
+
+ Defaults to \"classes\"")
+
+(add-doc *compile-files*
+ "Set to true when compiling files, false otherwise.")
+
+(add-doc *ns*
+ "A clojure.lang.Namespace object representing the current namespace.")
+
+(add-doc *in*
+ "A java.io.Reader object representing standard input for read operations.
+
+ Defaults to System/in, wrapped in a LineNumberingPushbackReader")
+
+(add-doc *out*
+ "A java.io.Writer object representing standard output for print operations.
+
+ Defaults to System/out")
+
+(add-doc *err*
+ "A java.io.Writer object representing standard error for print operations.
+
+ Defaults to System/err, wrapped in a PrintWriter")
+
+(add-doc *flush-on-newline*
+ "When set to true, output will be flushed whenever a newline is printed.
+
+ Defaults to true.")
+
+(add-doc *print-meta*
+ "If set to logical true, when printing an object, its metadata will also
+ be printed in a form that can be read back by the reader.
+
+ Defaults to false.")
+
+(add-doc *print-dup*
+ "When set to logical true, objects will be printed in a way that preserves
+ their type when read in later.
+
+ Defaults to false.")
+
+(add-doc *print-readably*
+ "When set to logical false, strings and characters will be printed with
+ non-alphanumeric characters converted to the appropriate escape sequences.
+
+ Defaults to true")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; helper files ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(alter-meta! (find-ns 'clojure.core) assoc :doc "Fundamental library of the Clojure language")
+;(load "core_proxy")
+;(load "core_print")
+;(load "genclass")
+
+;;; Need to figure out equivalents for pooledExecutor, java.util.concurrent.Future + we need proxies.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; futures (needs proxy);;;;;;;;;;;;;;;;;;
+;(defn future-call
+; "Takes a function of no args and yields a future object that will
+; invoke the function in another thread, and will cache the result and
+; return it on all subsequent calls to deref/@. If the computation has
+; not yet finished, calls to deref/@ will block."
+; [#^Callable f]
+; (let [fut (.submit clojure.lang.Agent/pooledExecutor f)]
+; (proxy [clojure.lang.IDeref java.util.concurrent.Future] []
+; (deref [] (.get fut))
+; (get ([] (.get fut))
+; ([timeout unit] (.get fut timeout unit)))
+; (isCancelled [] (.isCancelled fut))
+; (isDone [] (.isDone fut))
+; (cancel [interrupt?] (.cancel fut interrupt?)))))
+;
+;(defmacro future
+; "Takes a body of expressions and yields a future object that will
+; invoke the body in another thread, and will cache the result and
+; return it on all subsequent calls to deref/@. If the computation has
+; not yet finished, calls to deref/@ will block."
+; [& body] `(future-call (fn [] ~@body)))
+;
+;(defn pmap
+; "Like map, except f is applied in parallel. Semi-lazy in that the
+; parallel computation stays ahead of the consumption, but doesn't
+; realize the entire result unless required. Only useful for
+; computationally intensive functions where the time of f dominates
+; the coordination overhead."
+; ([f coll]
+; (let [n (+ 2 (.. Runtime getRuntime availableProcessors))
+; rets (map #(future (f %)) coll)
+; step (fn step [[x & xs :as vs] fs]
+; (if fs
+; (lazy-cons (deref x) (step xs (rest fs)))
+; (map deref vs)))]
+; (step rets (drop n rets))))
+; ([f coll & colls]
+; (let [step (fn step [cs]
+; (when (every? seq cs)
+; (lazy-cons (map first cs) (step (map rest cs)))))]
+; (pmap #(apply f %) (step (cons coll colls))))))
+;
+;(defn pcalls
+; "Executes the no-arg fns in parallel, returning a lazy sequence of
+; their values"
+; [& fns] (pmap #(%) fns))
+;
+;(defmacro pvalues
+; "Returns a lazy sequence of the values of the exprs, which are
+; evaluated in parallel"
+; [& exprs]
+; `(pcalls ~@(map #(list `fn [] %) exprs)))
+;
\ No newline at end of file diff --git a/ClojureCLR/Clojure/Clojure/Bootstrap/test.clj b/ClojureCLR/Clojure/Clojure/Bootstrap/test.clj new file mode 100644 index 00000000..523cf9ee --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Bootstrap/test.clj @@ -0,0 +1,17 @@ +; 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.
+
+(defn f1 [l n] (if (> (count l) n) nil (recur (cons 'a l) n)))
+(defn len [x]
+ (. x Length))
+(defn len2 [#^String x]
+ (. x Length))
+
+(defn test1 [] (time (f1 nil 100000)))
+(defn test2 [] (time (reduce + (map len (replicate 100000 "asdf")))))
+(defn test3 [] (time (reduce + (map len2 (replicate 100000 "asdf")))))
diff --git a/ClojureCLR/Clojure/Clojure/Clojure.csproj b/ClojureCLR/Clojure/Clojure/Clojure.csproj new file mode 100644 index 00000000..597466e3 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Clojure.csproj @@ -0,0 +1,184 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{B8089F66-DFBD-4906-BEE0-B317689C2524}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>clojure.lang</RootNamespace>
+ <AssemblyName>Clojure</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <TargetFrameworkSubset>
+ </TargetFrameworkSubset>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ <Reference Include="vjslib" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Lib\Counted.cs" />
+ <Compile Include="Lib\IDeref.cs" />
+ <Compile Include="Readers\LineNumberingReader.cs" />
+ <Compile Include="Runtime\ClojureBinder.cs" />
+ <Compile Include="Runtime\ClojureCommandLine.cs" />
+ <Compile Include="Runtime\ClojureContext.cs" />
+ <Compile Include="Runtime\ClojureParser.cs" />
+ <Compile Include="Properties\Resources.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>Resources.resx</DependentUpon>
+ </Compile>
+ <Compile Include="Compiler\Generator.cs" />
+ <Compile Include="Lib\AFn.cs" />
+ <Compile Include="Lib\AFnImpl.cs" />
+ <Compile Include="Lib\AFunction.cs" />
+ <Compile Include="Lib\Agent.cs" />
+ <Compile Include="Lib\AMapEntry.cs" />
+ <Compile Include="Lib\APersistentMap.cs" />
+ <Compile Include="Lib\APersistentSet.cs" />
+ <Compile Include="Lib\APersistentVector.cs" />
+ <Compile Include="Lib\ARef.cs" />
+ <Compile Include="Lib\AReference.cs" />
+ <Compile Include="Lib\ArraySeq.cs" />
+ <Compile Include="Lib\ArrayStream.cs" />
+ <Compile Include="Lib\ASeq.cs" />
+ <Compile Include="Lib\Associative.cs" />
+ <Compile Include="Lib\Atom.cs" />
+ <Compile Include="Lib\AtomicInteger.cs" />
+ <Compile Include="Lib\AtomicLong.cs" />
+ <Compile Include="Lib\AtomicReference.cs" />
+ <Compile Include="Lib\Box.cs" />
+ <Compile Include="Lib\CachedSeq.cs" />
+ <Compile Include="Lib\ClojureException.cs" />
+ <Compile Include="Lib\Compiler.cs" />
+ <Compile Include="Lib\Cons.cs" />
+ <Compile Include="Lib\Delay.cs" />
+ <Compile Include="Lib\Fn.cs" />
+ <Compile Include="Lib\FnSeq.cs" />
+ <Compile Include="Lib\FuncTypes.cs" />
+ <Compile Include="Lib\IFn.cs" />
+ <Compile Include="Lib\IMapEntry.cs" />
+ <Compile Include="Lib\IMeta.cs" />
+ <Compile Include="Lib\IndexedSeq.cs" />
+ <Compile Include="Lib\IObj.cs" />
+ <Compile Include="Lib\IPersistentCollection.cs" />
+ <Compile Include="Lib\IPersistentList.cs" />
+ <Compile Include="Lib\IPersistentMap.cs" />
+ <Compile Include="Lib\IPersistentSet.cs" />
+ <Compile Include="Lib\IPersistentStack.cs" />
+ <Compile Include="Lib\IPersistentVector.cs" />
+ <Compile Include="Lib\IReduce.cs" />
+ <Compile Include="Lib\IRef.cs" />
+ <Compile Include="Lib\IReference.cs" />
+ <Compile Include="Lib\ISeq.cs" />
+ <Compile Include="Lib\IStream.cs" />
+ <Compile Include="Lib\IteratorStream.cs" />
+ <Compile Include="Lib\JavaConcurrentDictionary.cs" />
+ <Compile Include="Lib\Keyword.cs" />
+ <Compile Include="Lib\LazilyPersistentVector.cs" />
+ <Compile Include="Lib\LazyCons.cs" />
+ <Compile Include="Lib\LispReader.cs" />
+ <Compile Include="Lib\LockingTransaction.cs" />
+ <Compile Include="Lib\MapEntry.cs" />
+ <Compile Include="Lib\MapEnumerator.cs" />
+ <Compile Include="Lib\MultiFn.cs" />
+ <Compile Include="Lib\Named.cs" />
+ <Compile Include="Lib\Namespace.cs" />
+ <Compile Include="Lib\Numbers.cs" />
+ <Compile Include="Lib\Obj.cs" />
+ <Compile Include="Lib\PersistentArrayMap.cs" />
+ <Compile Include="Lib\PersistentHashMap.cs" />
+ <Compile Include="Lib\PersistentHashSet.cs" />
+ <Compile Include="Lib\PersistentList.cs" />
+ <Compile Include="Lib\PersistentQueue.cs" />
+ <Compile Include="Lib\PersistentStructMap.cs" />
+ <Compile Include="Lib\PersistentTreeMap.cs" />
+ <Compile Include="Lib\PersistentTreeSet.cs" />
+ <Compile Include="Lib\PersistentVector.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Lib\Range.cs" />
+ <Compile Include="Lib\Ratio.cs" />
+ <Compile Include="Lib\Ref.cs" />
+ <Compile Include="Lib\RestFn.cs" />
+ <Compile Include="Lib\RestFnImpl.cs" />
+ <Compile Include="Lib\Reversible.cs" />
+ <Compile Include="Lib\RT.cs" />
+ <Compile Include="Lib\SeqEnumerator.cs" />
+ <Compile Include="Lib\Sequential.cs" />
+ <Compile Include="Lib\Settable.cs" />
+ <Compile Include="Lib\Sorted.cs" />
+ <Compile Include="Lib\Streamable.cs" />
+ <Compile Include="Lib\StreamSeq.cs" />
+ <Compile Include="Lib\StringSeq.cs" />
+ <Compile Include="Lib\Symbol.cs" />
+ <Compile Include="Lib\Util.cs" />
+ <Compile Include="Lib\Var.cs" />
+ <Compile Include="Runtime\Reflector.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Properties\Resources.resx">
+ <Generator>PublicResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+ </EmbeddedResource>
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\DLR_Main\Runtime\src\Microsoft.Scripting.Core\Microsoft.Scripting.Core.csproj">
+ <Project>{2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}</Project>
+ <Name>Microsoft.Scripting.Core</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\DLR_Main\Runtime\src\Microsoft.Scripting.Core\Microsoft.Scripting.ExtensionAttribute.csproj">
+ <Project>{8B0F1074-750E-4D64-BF23-A1E0F54261E5}</Project>
+ <Name>Microsoft.Scripting.ExtensionAttribute</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\DLR_Main\Runtime\src\Microsoft.Scripting\Microsoft.Scripting.csproj">
+ <Project>{EB66B766-6354-4208-A3D4-AACBDCB5C3B3}</Project>
+ <Name>Microsoft.Scripting</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Bootstrap\ants.clj" />
+ <None Include="Bootstrap\core-print.clj" />
+ <None Include="Bootstrap\core.clj" />
+ <None Include="Bootstrap\test.clj" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
\ No newline at end of file diff --git a/ClojureCLR/Clojure/Clojure/Compiler/Generator.cs b/ClojureCLR/Clojure/Clojure/Compiler/Generator.cs new file mode 100644 index 00000000..b4439882 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Compiler/Generator.cs @@ -0,0 +1,2461 @@ +/**
+ * 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 Microsoft.Linq.Expressions;
+using System.Reflection;
+using clojure.lang;
+using Microsoft.Scripting.Ast;
+using System.IO;
+using System.Threading;
+using AstUtils = Microsoft.Scripting.Ast.Utils;
+
+using clojure.runtime;
+
+namespace clojure.compiler
+{
+ public static class Generator
+ {
+ #region Data
+
+ static readonly Symbol ISEQ = Symbol.create("clojure.lang.ISeq");
+ static readonly Symbol NS = Symbol.create("ns");
+ static readonly Symbol IN_NS = Symbol.create("in-ns");
+ static readonly Keyword INLINE_KEY = Keyword.intern(null, "inline");
+ static readonly Keyword INLINE_ARITIES_KEY = Keyword.intern(null,"inline-arities");
+
+
+ #endregion
+
+ #region MethodInfos, etc.
+
+ static readonly MethodInfo Method_ArraySeq_create_array_int = typeof(ArraySeq).GetMethod("create",new Type[] { typeof(object[]),typeof(int)});
+
+ static readonly MethodInfo Method_CGen_MakeMap = typeof(Generator).GetMethod("MakeMap");
+ static readonly MethodInfo Method_CGen_MakeSet = typeof(Generator).GetMethod("MakeSet");
+ static readonly MethodInfo Method_CGen_MakeVector = typeof(Generator).GetMethod("MakeVector");
+
+ static readonly MethodInfo Method_IObj_withMeta = typeof(IObj).GetMethod("withMeta");
+
+ static readonly MethodInfo Method_Monitor_Enter = typeof(Monitor).GetMethod("Enter");
+ static readonly MethodInfo Method_Monitor_Exit = typeof(Monitor).GetMethod("Exit");
+
+ static readonly MethodInfo Method_Reflector_CallInstanceMethod = typeof(Reflector).GetMethod("CallInstanceMethod");
+ static readonly MethodInfo Method_Reflector_CallStaticMethod = typeof(Reflector).GetMethod("CallStaticMethod");
+ static readonly MethodInfo Method_Reflector_InvokeConstructor = typeof(Reflector).GetMethod("InvokeConstructor");
+
+ static readonly MethodInfo Method_RT_ConvertToCRD = typeof(RT).GetMethod("ConvertToCRD");
+ static readonly MethodInfo Method_RT_IsTrue = typeof(RT).GetMethod("IsTrue");
+ static readonly MethodInfo Method_RT_map = typeof(RT).GetMethod("map");
+ static readonly MethodInfo Method_RT_printToConsole = typeof(RT).GetMethod("printToConsole");
+ static readonly MethodInfo Method_RT_vector = typeof(RT).GetMethod("vector");
+
+ static readonly MethodInfo Method_Var_BindRoot = typeof(Var).GetMethod("BindRoot");
+ static readonly MethodInfo Method_Var_get = typeof(Var).GetMethod("deref");
+ static readonly MethodInfo Method_Var_set = typeof(Var).GetMethod("set");
+ static readonly MethodInfo Method_Var_SetMeta = typeof(Var).GetMethod("SetMeta");
+
+ static readonly ConstructorInfo Ctor_AFnImpl_0 = typeof(AFnImpl).GetConstructor(Type.EmptyTypes);
+ static readonly ConstructorInfo Ctor_RestFnImpl_1 = typeof(RestFnImpl).GetConstructor(new Type[] {typeof(int)});
+
+ static readonly MethodInfo[] Methods_IFn_invoke = new MethodInfo[MAX_POSITIONAL_ARITY+2];
+
+ static Type[] CreateObjectTypeArray(int size)
+ {
+ Type[] typeArray = new Type[size];
+ for (int i = 0; i < size; i++)
+ typeArray[i] = typeof(Object);
+ return typeArray;
+ }
+
+
+
+ #endregion
+
+ #region Special forms map
+
+ delegate Expression ExprGenerator(ISeq form);
+
+ private static readonly Dictionary<Symbol, ExprGenerator> _specials = new Dictionary<Symbol, ExprGenerator>();
+
+ static Generator()
+ {
+ _specials.Add(Compiler.DEF, GenerateDefExpr);
+ _specials.Add(Compiler.LOOP, GenerateLetExpr);
+ _specials.Add(Compiler.RECUR, GenerateRecurExpr);
+ _specials.Add(Compiler.IF, GenerateIfExpr);
+ _specials.Add(Compiler.LET, GenerateLetExpr);
+ _specials.Add(Compiler.DO, GenerateBodyExpr);
+ _specials.Add(Compiler.FN, GenerateFnExpr);
+ _specials.Add(Compiler.QUOTE, GenerateQuoteExpr);
+ _specials.Add(Compiler.THE_VAR, GenerateTheVarExpr);
+ _specials.Add(Compiler.DOT, GenerateHostExpr);
+ _specials.Add(Compiler.ASSIGN, GenerateAssignExpr);
+ _specials.Add(Compiler.TRY, GenerateTryExpr);
+ _specials.Add(Compiler.THROW, GenerateThrowExpr);
+ _specials.Add(Compiler.MONITOR_ENTER, GenerateMonitorEnterExpr);
+ _specials.Add(Compiler.MONITOR_EXIT, GenerateMonitorExitExpr);
+ _specials.Add(Compiler.NEW, GenerateNewExpr);
+
+ for (int i = 0; i <= MAX_POSITIONAL_ARITY; i++)
+ Methods_IFn_invoke[i] = typeof(IFn).GetMethod("invoke", CreateObjectTypeArray(i));
+
+ Type[] types = new Type[MAX_POSITIONAL_ARITY + 1];
+ CreateObjectTypeArray(MAX_POSITIONAL_ARITY).CopyTo(types, 0);
+ types[MAX_POSITIONAL_ARITY ] = typeof(object[]);
+ Methods_IFn_invoke[MAX_POSITIONAL_ARITY + 1]
+ = typeof(IFn).GetMethod("invoke",
+ BindingFlags.Public | BindingFlags.InvokeMethod,
+ Type.DefaultBinder,
+ CallingConventions.VarArgs | CallingConventions.HasThis,
+ types,
+ null);
+
+
+ }
+
+ static bool HasSpecialFormGenerator(object head)
+ {
+ return head is Symbol && _specials.ContainsKey(head as Symbol);
+ }
+
+ static ExprGenerator GetSpecialFormGenerator(object head)
+ {
+ return _specials[head as Symbol];
+ }
+
+
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ public static LambdaExpression Generate(object form, bool addPrint)
+ {
+ Expression formExpr = Generate(form);
+
+ Expression finalExpr = formExpr;
+
+ if (formExpr.Type == typeof(void))
+ finalExpr = Expression.Block(formExpr, Expression.Constant(null));
+
+
+ if (addPrint)
+ {
+ finalExpr = Expression.Call(Method_RT_printToConsole, finalExpr);
+ }
+
+ return Expression.Lambda(finalExpr, "REPLCall", null);
+ }
+
+ private static string MaybeToString(object x)
+ {
+ return x == null ? string.Empty : x.ToString();
+ }
+
+ public static LambdaExpression Generate(object p, Microsoft.Scripting.SourceUnit sourceUnit)
+ {
+ // TODO: Deal with sourceUnit
+ return Generate(p,false);
+ }
+
+ public static Expression Eval(ClojureContext clc, object form)
+ {
+ return Generate(form);
+ }
+
+ public static object Macroexpand1(ClojureContext clc, object form)
+ {
+ if (!(form is ISeq))
+ return form;
+ return MacroexpandSeq1((ISeq)form);
+ }
+
+
+ public static LambdaExpression GenerateTypedDelegateExpression(ClojureContext clc, Type delegateType, Symbol name, IPersistentVector parameters, ISeq body)
+ {
+ return GenerateTypedDelegateExpression(delegateType, name, parameters, body);
+ }
+
+ #endregion
+
+ #region Entry points
+
+ private static Expression Generate(object form)
+ {
+ if (form == null)
+ return GenerateNilExpr();
+ else if (form is Boolean)
+ return ((bool)form) ? GenerateTrueExpr() : GenerateFalseExpr();
+
+ Type type = form.GetType();
+
+ if (type == typeof(Symbol))
+ return GenerateSymbolExpr((Symbol)form);
+ else if (type == typeof(Keyword))
+ return GenerateKeywordExpr((Keyword)form);
+ else if (type == typeof(String))
+ return GenerateStringExpr((String)form);
+ else if (form is IPersistentCollection && ((IPersistentCollection)form).count() == 0)
+ return GenerateEmptyExpr(form);
+ else if (form is ISeq)
+ return GenerateSeqExpr((ISeq)form);
+ else if (form is IPersistentVector)
+ return GenerateVectorExpr((IPersistentVector)form);
+ else if (form is IPersistentMap)
+ return GenerateMapExpr((IPersistentMap)form);
+ else if (form is IPersistentSet)
+ return GenerateSetExpr((IPersistentSet)form);
+ else
+ return GenerateConstExpr(form);
+ }
+
+
+
+ #endregion
+
+ #region Various constant expressions
+
+ private static ConstantExpression NIL_EXPR = Expression.Constant(null);
+ private static ConstantExpression TRUE_EXPR = Expression.Constant(RT.T);
+ private static ConstantExpression FALSE_EXPR = Expression.Constant(RT.F);
+
+ private static Expression GenerateConstExpr(object form)
+ {
+ return Expression.Constant(form);
+ }
+
+ private static Expression GenerateNilExpr()
+ {
+ return NIL_EXPR;
+ }
+
+ private static Expression GenerateTrueExpr()
+ {
+ return TRUE_EXPR;
+ }
+
+ private static Expression GenerateFalseExpr()
+ {
+ return FALSE_EXPR;
+ }
+
+ private static Expression GenerateKeywordExpr(Keyword keyword)
+ {
+ // in the Java version:
+ //if (!KEYWORDS.isBound())
+ // return new KeywordExpr(keyword);
+ //IPersistentMap keywordsMap = (IPersistentMap)KEYWORDS.get();
+ //Object id = RT.get(keywordsMap, keyword);
+ //if (id == null)
+ //{
+ // KEYWORDS.set(RT.assoc(keywordsMap, keyword, registerConstant(keyword)));
+ //}
+ //return new KeywordExpr(keyword);
+
+ return Expression.Constant(keyword);
+ }
+
+ private static Expression GenerateStringExpr(string p)
+ {
+ return Expression.Constant(String.Intern(p));
+ }
+
+
+
+ #endregion
+
+ #region Helpers
+
+ private static Namespace CurrentNamespace
+ {
+ get { return Compiler.CurrentNamespace; }
+ }
+
+ private static IPersistentMap CHAR_MAP = PersistentHashMap.create('-', "_",
+ // '.', "_DOT_",
+ ':', "_COLON_",
+ '+', "_PLUS_",
+ '>', "_GT_",
+ '<', "_LT_",
+ '=', "_EQ_",
+ '~', "_TILDE_",
+ '!', "_BANG_",
+ '@', "_CIRCA_",
+ '#', "_SHARP_",
+ '$', "_DOLLARSIGN_",
+ '%', "_PERCENT_",
+ '^', "_CARET_",
+ '&', "_AMPERSAND_",
+ '*', "_STAR_",
+ '|', "_BAR_",
+ '{', "_LBRACE_",
+ '}', "_RBRACE_",
+ '[', "_LBRACK_",
+ ']', "_RBRACK_",
+ '/', "_SLASH_",
+ '\\', "_BSLASH_",
+ '?', "_QMARK_"
+ );
+
+ public static string munge(string name)
+ {
+ StringBuilder sb = new StringBuilder();
+ foreach (char c in name)
+ {
+ string sub = (string)CHAR_MAP.valAt(c);
+ if (sub == null)
+ sb.Append(c);
+ else
+ sb.Append(sub);
+ }
+ return sb.ToString();
+ }
+
+ private static Symbol TagOf(object o)
+ {
+ //IObj iobj = o as IObj;
+ //if ( iobj != null && iobj.meta() != null )
+ //{
+ // object tag = iobj.meta().valAt(RT.TAG_KEY);
+ // if ( tag is Symbol )
+ // return (Symbol) tag;
+ // else if ( tag is string )
+ // return Symbol.intern(null, (string) tag);
+ //}
+ //return null;
+ object tag = RT.get(RT.meta(o), RT.TAG_KEY);
+ if (tag is Symbol)
+ return (Symbol)tag;
+ else if (tag is string)
+ return Symbol.intern(null, (String)tag);
+ return null;
+ }
+
+ #endregion
+
+ #region Symbols
+
+ // var > constid
+
+ // this ties into local variables and vars
+ private static Expression GenerateSymbolExpr(Symbol symbol)
+ {
+ Symbol tag = TagOf(symbol);
+
+ if (symbol.Namespace == null)
+ {
+ LocalBinding b = ReferenceLocal(symbol);
+ if (b != null)
+ return b.ParamExpression; //asdf-tag
+ }
+ else
+ {
+ if (Compiler.namespaceFor(symbol) == null)
+ {
+ Symbol nsSym = Symbol.create(symbol.Namespace);
+ Type t = MaybeType(nsSym, false);
+ if (t != null)
+ if ( Reflector.GetField(t,symbol.Name,true) != null )
+ return GenerateStaticFieldExpr(t,symbol.Name);
+ }
+ }
+
+ object o = Compiler.Resolve(symbol);
+ if (o is Var)
+ {
+ Var v = (Var)o;
+ if (IsMacro(v) != null)
+ throw new Exception("Can't take the value of a macro: " + v);
+ RegisterVar(v);
+ return GenerateVarExpr(v, tag);
+ }
+ else if (o is Type)
+ return GenerateConstExpr(o);
+ else if (o is Symbol)
+ return GenerateUnresolvedVarExpr((Symbol)o);
+
+ throw new Exception(string.Format("Unable to resolve symbol: {0} in this context", symbol));
+ }
+
+ private static Type MaybeType(object form, bool stringOk)
+ {
+ if (form is Type)
+ return (Type)form;
+
+ Type t = null;
+ if (form is Symbol)
+ {
+ Symbol sym = (Symbol)form;
+ if (sym.Namespace == null) // if ns-qualified, can't be classname
+ {
+ if (sym.Name.IndexOf('.') > 0 || sym.Name[0] == '[')
+ t = RT.classForName(sym.Name);
+ else
+ {
+ object o = CurrentNamespace.GetMapping(sym);
+ if (o is Type)
+ t = (Type)o;
+ }
+
+ }
+ }
+ else if (stringOk && form is string)
+ t = RT.classForName((string)form);
+
+ return t;
+ }
+
+
+ private static void RegisterVar(Var v)
+ {
+ // do nothing, I think, in my implementation.
+ // However, this may be needed when writing out a binary file
+ }
+
+ private static Var IsMacro(Object op)
+ {
+ if (op is Symbol && ReferenceLocal((Symbol)op) != null)
+ return null;
+ if (op is Symbol || op is Var)
+ {
+ Var v = (op is Var) ? (Var)op : lookupVar((Symbol)op, false);
+ if (v != null && v.IsMacro)
+ {
+ if (v.Namespace != CurrentNamespace && !v.IsPublic)
+ throw new InvalidOperationException(string.Format("Var: {0} is not public", v));
+ return v;
+ }
+ }
+ return null;
+ }
+
+
+ private static LocalBinding ReferenceLocal(Symbol symbol)
+ {
+ if (!LOCAL_ENV.IsBound)
+ return null;
+ LocalBinding b = (LocalBinding)((IPersistentMap)LOCAL_ENV.deref()).valAt(symbol);
+ //if (b != null)
+ //{
+ // MethodDef method = (MethodDef)METHODS.get();
+ // // here is where we might note a variable to close over.
+ // // need to move up the chain here?????????????????????????????????????????????????????
+ // // I don't think we need method.localsUsedInCatchFinally
+ // //if (method.Locals.valAt(b) != null && IN_CATCH_FINALLY.get() != null)
+ // // method.localsUsedinCatchFinally = method.localsUsedinCatchFinally.cons(b); // do we need this?
+ //}
+ return b;
+ }
+
+ static Var lookupVar(Symbol sym, bool internNew)
+ {
+ Var var = null;
+
+ // Note: ns-qualified vars in other namespaces must exist already
+ if (sym.Namespace != null)
+ {
+ Namespace ns = Compiler.NamespaceFor(sym);
+ if (ns == null)
+ return null;
+ Symbol name = Symbol.create(sym.Name);
+ if (internNew && ns == CurrentNamespace)
+ var = CurrentNamespace.intern(name);
+ else
+ var = ns.FindInternedVar(name);
+ }
+ else if (sym.Equals(NS))
+ var = RT.NS_VAR;
+ else if (sym.Equals(IN_NS))
+ var = RT.IN_NS_VAR;
+ else
+ {
+ // is it mapped?
+ Object o = CurrentNamespace.GetMapping(sym);
+ if (o == null)
+ {
+ // introduce a new var in the current ns
+ if (internNew)
+ var = CurrentNamespace.intern(Symbol.create(sym.Name));
+ }
+ else if (o is Var)
+ var = (Var)o;
+ else
+ throw new Exception(string.Format("Expecting var, but {0} is mapped to {1}", sym, o));
+ }
+ if (var != null)
+ RegisterVar(var);
+ return var;
+ }
+
+ private static Expression GenerateUnresolvedVarExpr(Symbol symbol)
+ {
+ return null; // ??????
+ }
+
+ private static Expression GenerateVarExpr(Var v, Symbol tag)
+ {
+ object tagToUse = tag ?? v.Tag;
+
+ Expression expr = Expression.Call(Expression.Constant(v), Method_Var_get); //asdf-tag
+ //if (tagToUse != null)
+ // expr = Expression.Convert(expr, TagToType(tagToUse)); // NOPE
+ return expr;
+ }
+
+ private static Expression GenerateStaticFieldExpr(Type t, string fieldName)
+ {
+ //return Expression.Field(Expression.Constant(t), fieldName);
+ return Expression.Field(null, t, fieldName);
+ }
+
+
+ #endregion
+
+ #region General collections
+
+ static Expression EMPTY_VECTOR_EXPR = Expression.Constant(PersistentVector.EMPTY);
+ static Expression EMPTY_LIST_EXPR = Expression.Constant(PersistentList.EMPTY);
+ static Expression EMPTY_HASHMAP_EXPR = Expression.Constant(PersistentArrayMap.EMPTY);
+ static Expression EMPTY_HASHSET_EXPR = Expression.Constant(PersistentHashSet.EMPTY);
+
+ private static Expression GenerateEmptyExpr(object form)
+ {
+ Expression expr = null;
+
+ if (form is IPersistentList)
+ expr = EMPTY_LIST_EXPR;
+ else if (form is IPersistentVector)
+ expr = EMPTY_VECTOR_EXPR;
+ else if (form is IPersistentMap)
+ expr = EMPTY_HASHMAP_EXPR;
+ else if (form is IPersistentSet)
+ expr = EMPTY_HASHSET_EXPR;
+ else
+ throw new InvalidOperationException("Unknown collection type.");
+
+ if (RT.meta(form) != null)
+ {
+ expr = OptionallyGenerateMetaInit(form, expr);
+ }
+ return expr;
+ }
+
+ private static Expression GenerateVectorExpr(IPersistentVector v)
+ {
+ int n = v.count();
+ Expression[] args = new Expression[v.count()];
+ for (int i = 0; i < n; i++)
+ args[i] = Generate(v.nth(i));
+
+ Expression arrayExpr = Expression.NewArrayInit(typeof(object), MaybeBox(args));
+ Expression ret = Expression.Call(Method_RT_vector, arrayExpr);
+ ret = OptionallyGenerateMetaInit(v,ret);
+
+ return ret;
+ }
+
+
+ private static Expression GenerateMapExpr(IPersistentMap m)
+ {
+ Expression[] args = new Expression[m.count() * 2];
+ int i = 0;
+ for ( ISeq s = RT.seq(m); s != null; s = s.rest(), i+=2)
+ {
+ IMapEntry me = (IMapEntry)s.first();
+ args[i] = MaybeBox(Generate(me.key()));
+ args[i + 1] = MaybeBox(Generate(me.val()));
+ }
+ Expression argArray = Expression.NewArrayInit(typeof(object), args);
+
+ Expression ret = Expression.Call(Method_RT_map,argArray);
+ ret = OptionallyGenerateMetaInit(m,ret);
+
+ return ret;
+ }
+
+ private static Expression GenerateSetExpr(IPersistentSet set)
+ {
+ Expression[] args = new Expression[set.count()];
+ int i = 0;
+ for (ISeq s = RT.seq(set); s != null; s = s.rest(), i++)
+ args[i] = MaybeBox(Generate(s.first()));
+
+ Expression argArray = Expression.NewArrayInit(typeof(object), args);
+
+ Expression ret = Expression.Call(Method_CGen_MakeSet, argArray);
+ ret = OptionallyGenerateMetaInit(set, ret);
+
+ return ret;
+ }
+
+
+ public static IPersistentVector MakeVector(params object[] elements)
+ {
+ return LazilyPersistentVector.createOwning(elements);
+ }
+
+ public static IPersistentMap MakeMap(params object[] init)
+ {
+ return( init != null && init.Length == 2 )
+ ? (IPersistentMap) new PersistentArrayMap(init)
+ : (IPersistentMap) PersistentHashMap.create(init);
+ }
+
+ public static IPersistentSet MakeSet(params object[] elements)
+ {
+ return PersistentHashSet.create(elements);
+ }
+
+ private static Expression OptionallyGenerateMetaInit(object form, Expression expr)
+ {
+ Expression ret = expr;
+
+ IObj o = form as IObj;
+ if (o != null && o.meta() != null)
+ {
+ Expression metaExpr = GenerateMapExpr(o.meta());
+ ret = Expression.Call(Expression.Convert(expr, typeof(IObj)),Method_IObj_withMeta, metaExpr);
+ }
+ return ret;
+ }
+
+
+ #endregion
+
+ #region ISeq forms = calls
+
+ private static Expression GenerateSeqExpr(ISeq form)
+ {
+ object exp = MacroexpandSeq1(form);
+ if (exp != form)
+ return Generate(exp);
+
+ object op = RT.first(form);
+
+ IFn inline = IsInline(op, RT.count(RT.rest(form)));
+
+ if (inline != null)
+ return Generate(inline.applyTo(RT.rest(form)));
+ else if (HasSpecialFormGenerator(op))
+ return GetSpecialFormGenerator(op)(form);
+ else
+ return GenerateInvoke(form);
+ }
+
+ private static Expression GenerateInvoke(ISeq form)
+ {
+ Expression fn = Generate(form.first());
+
+ fn = Expression.Convert(fn,typeof(IFn));
+
+ ISeq s = RT.seq(form.rest());
+ int n = s == null ? 0 : s.count();
+ Expression[] args = new Expression[n];
+ for (int i = 0; s != null; s = s.rest(), i++)
+ args[i] = MaybeBox(Generate(s.first()));
+
+ Type returnType = ComputeInvocationReturnType(form.first(), form);
+
+ Expression call = GenerateInvocation(returnType, fn, args);
+
+ return call;
+ }
+
+ private static Type ComputeInvocationReturnType(object op, ISeq form)
+ {
+ Symbol tag = TagOf(form);
+ if (tag == null && op is Symbol)
+ {
+ Symbol sym = (Symbol)op;
+ tag = TagOf(sym);
+ if (tag == null)
+ {
+ Var var = SymbolMapsToVar(sym);
+ if (var != null)
+ tag = var.Tag as Symbol;
+ }
+ }
+ return (tag == null)
+ ? null
+ : TagToType(tag);
+ }
+
+ // Tremendously duplicative of GenerateSymbolExpr -- maybe just cache the info somewhere.
+ static Var SymbolMapsToVar(Symbol symbol)
+ {
+ if ( symbol.Namespace == null && ReferenceLocal(symbol) != null )
+ // maps to local
+ return null;
+
+ if (symbol.Namespace != null && Compiler.namespaceFor(symbol) == null)
+ {
+ Symbol nsSym = Symbol.create(symbol.Namespace);
+ Type t = MaybeType(nsSym, false);
+ if (t != null && Reflector.GetField(t,symbol.Name,true) != null )
+ return null;
+ }
+
+ object o = Compiler.Resolve(symbol);
+
+ if ( o is Var )
+ return (Var) o;
+
+ return null;
+ }
+
+ private static Expression GenerateInvocation(Type returnType, Expression fn, Expression[] args)
+ {
+ MethodInfo mi;
+ Expression[] actualArgs;
+
+ if (args.Length <= MAX_POSITIONAL_ARITY)
+ {
+ mi = Methods_IFn_invoke[args.Length];
+ actualArgs = args;
+ }
+ else
+ {
+ // pick up the extended version.
+ mi = Methods_IFn_invoke[MAX_POSITIONAL_ARITY + 1];
+ Expression[] leftoverArgs = new Expression[args.Length-MAX_POSITIONAL_ARITY];
+ Array.ConstrainedCopy(args,MAX_POSITIONAL_ARITY,leftoverArgs,0,args.Length-MAX_POSITIONAL_ARITY);
+
+ Expression restArg = Expression.NewArrayInit(typeof(object), leftoverArgs);
+
+ actualArgs = new Expression[MAX_POSITIONAL_ARITY + 1];
+ Array.ConstrainedCopy(args, 0, actualArgs, 0, MAX_POSITIONAL_ARITY);
+ actualArgs[MAX_POSITIONAL_ARITY] = restArg;
+ }
+
+ Expression call = Expression.Call(fn, mi, actualArgs);
+ // Java version doesn't seem to do this. Instead, its InvokeExpression carries the type information so someone else can use it.
+ // Not sure if this is useful here.
+ if (returnType != null)
+ call = Expression.Convert(call, returnType);
+
+ return call;
+ }
+
+ private static object MacroexpandSeq1(ISeq form)
+ {
+ object op = RT.first(form);
+ if (Compiler.isSpecial(op))
+ return form;
+
+ // macro expansion
+ Var v = IsMacro(op);
+ if (v != null)
+ {
+ try
+ {
+ Var.pushThreadBindings(RT.map(RT.MACRO_META, RT.meta(form)));
+ return v.applyTo(form.rest());
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ }
+ else
+ {
+ if (op is Symbol)
+ {
+ Symbol sym = (Symbol)op;
+ string sname = sym.Name;
+ // (.substring s 2 5) => (. x substring 2 5)
+ if (sname[0] == '.')
+ {
+ if (form.count() < 2)
+ throw new ArgumentException("Malformed member expression, expecting (.member target ...)");
+ Symbol method = Symbol.intern(sname.Substring(1));
+ // TODO: Figure out why the following change made in Java Rev 1158 breaks ants.clj
+ // Note on that revision: force instance member interpretation of (.method ClassName), e.g. (.getMethods String) works
+ // However, when I do this, it makes ants.clj choke on: (def white-brush (new SolidBrush (.White Color)))
+ //object target = Second(form);
+ //if (MaybeType(target, false) != null)
+ // target = RT.list(Compiler.IDENTITY, target);
+ //return RT.listStar(Compiler.DOT, target, method, form.rest().rest());
+ return RT.listStar(Compiler.DOT, RT.second(form), method, form.rest().rest());
+ }
+ else if (NamesStaticMember(sym))
+ {
+ Symbol target = Symbol.intern(sym.Namespace);
+ Type t = MaybeType(target, false);
+ if (t != null)
+ {
+ Symbol method = Symbol.intern(sym.Name);
+ return RT.listStar(Compiler.DOT, target, method, form.rest());
+ }
+ }
+ else
+ {
+ // (x.substring 2 5) => (. s substring 2 5)
+ int index = sname.LastIndexOf('.');
+ if (index == sname.Length - 1)
+ return RT.listStar(Compiler.NEW, Symbol.intern(sname.Substring(0, index)), form.rest());
+ }
+ }
+
+ }
+ return form;
+ }
+
+
+ public static bool NamesStaticMember(Symbol sym)
+ {
+ return sym.Namespace != null && Compiler.NamespaceFor(sym) == null;
+ }
+
+
+ private static IFn IsInline(object op, int arity)
+ {
+ // Java: //no local inlines for now
+ if (op is Symbol && ReferenceLocal((Symbol)op) != null)
+ return null;
+ if (op is Symbol || op is Var)
+ {
+ Var v = (op is Var) ? (Var)op : lookupVar((Symbol)op, false);
+ if (v != null)
+ {
+ if (v.Namespace != CurrentNamespace && !v.isPublic())
+ throw new InvalidOperationException("var: " + v + " is not public");
+ IFn ret = (IFn)RT.get(v.meta(), INLINE_KEY);
+ if (ret != null)
+ {
+ IPersistentSet arities = (IPersistentSet)RT.get(v.meta(), INLINE_ARITIES_KEY);
+ if (arities == null || arities.contains(arity))
+ return ret;
+ }
+ }
+ }
+ return null;
+ }
+
+ #endregion
+
+ #region Special form generation
+
+ private static Expression GenerateQuoteExpr(ISeq form)
+ {
+ object v = form.rest().first();
+
+ return v == null ? GenerateNilExpr() : GenerateConstExpr(v);
+ }
+
+ private static Expression GenerateIfExpr(ISeq form)
+ {
+ if (form.count() > 4)
+ throw new Exception("Too many arguments to if");
+
+ if (form.count() < 3)
+ throw new Exception("Too few arguments to if");
+
+ //form = form.rest();
+ //object test = form.first();
+ //form = form.rest();
+ //object trueClause = form.first();
+ //form = form.rest();
+ //object falseClause = form == null ? null : form.first();
+ object test = RT.second(form);
+ object trueClause = RT.third(form);
+ object falseClause = RT.fourth(form);
+
+ // TODO: if test has Boolean type, no need to box, just test directly.
+ Expression realExpr = Expression.Call(Method_RT_IsTrue, MaybeBox(Generate(test)));
+ Expression thenExpr = Generate(trueClause);
+ Expression elseExpr = Generate(falseClause);
+
+ if (thenExpr.Type != elseExpr.Type)
+ {
+ // Try to reconcile
+ if (thenExpr.Type.IsAssignableFrom(elseExpr.Type) && elseExpr.Type != typeof(void))
+ elseExpr = Expression.Convert(elseExpr, thenExpr.Type);
+ else if (elseExpr.Type.IsAssignableFrom(thenExpr.Type) && thenExpr.Type != typeof(void))
+ thenExpr = Expression.Convert(thenExpr, elseExpr.Type);
+ else
+ {
+ if (thenExpr.Type == typeof(void))
+ thenExpr = Expression.Block(thenExpr, Expression.Default(elseExpr.Type));
+ else if (elseExpr.Type == typeof(void))
+ elseExpr = Expression.Block(elseExpr, Expression.Default(thenExpr.Type));
+ else
+ {
+ // TODO: Can we find a common ancestor? probably not.
+ thenExpr = Expression.Convert(thenExpr, typeof(object));
+ elseExpr = Expression.Convert(elseExpr, typeof(object));
+ }
+ }
+ }
+ return Expression.Condition(realExpr, thenExpr, elseExpr);
+ }
+
+ private static Expression GenerateBodyExpr(ISeq form)
+ {
+ ISeq forms = (Compiler.DO.Equals(RT.first(form))) ? RT.rest(form) : form;
+
+ Expression[] exprs;
+
+ if ( forms == null )
+ {
+ exprs = new Expression[1];
+ exprs[0] = GenerateNilExpr();
+ }
+ else
+ {
+ exprs = new Expression[forms.count()];
+ int i=0;
+ for (ISeq s = forms; s != null; s = s.rest(), i++)
+ {
+ if (s.rest() == null)
+ {
+ // in tail recurive position
+ try
+ {
+ Var.pushThreadBindings(PersistentHashMap.create(IN_TAIL_POSITION, RT.T));
+ exprs[i] = Generate(s.first());
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ }
+ else
+ exprs[i] = Generate(s.first());
+ }
+ }
+
+ return Expression.Block(exprs);
+ }
+
+ private static Expression GenerateTheVarExpr(ISeq form)
+ {
+ Symbol sym = RT.second(form) as Symbol;
+ Var v = lookupVar(sym, false);
+ if (v != null)
+ return GenerateConstExpr(v); // Really not sure on this one.
+ throw new Exception(string.Format("Unable to resolve var: {0} in this context", sym));
+ }
+
+ private static Expression GenerateDefExpr(ISeq form)
+ {
+ if (form.count() > 3)
+ throw new Exception("Too many arguments to def");
+
+ if (form.count() < 2)
+ throw new Exception("Too few arguments to def");
+
+ Symbol sym = RT.second(form) as Symbol;
+ bool initProvided = form.count() == 3;
+
+ if (sym == null)
+ throw new Exception("Second argument to def must be a Symbol.");
+
+ Var v = lookupVar(sym, true);
+
+ if (v == null)
+ throw new Exception("Can't refer to qualified var that doesn't exist");
+
+ if (!v.Namespace.Equals(CurrentNamespace))
+ {
+ if (sym.Namespace == null)
+ throw new Exception(string.Format("Name conflict, can't def {0} because namespace: {1} refers to: {2}",
+ sym, CurrentNamespace.Name, v));
+ else
+ throw new Exception("Can't create defs outside of current namespace");
+ }
+
+ IPersistentMap mm = sym.meta();
+ // TODO: add source line info metadata.
+ //mm = (IPersistentMap) RT.assoc(RT.LINE_KEY, LINE.get()).assoc(RT.FILE_KEY, SOURCE.get());
+
+ // Bizarrely, we don't have to do anything to actually create the var, the lookupVar did that for us.
+ // Will this work in a compiled class file?
+
+
+ List<Expression> exprs = new List<Expression>();
+
+ Expression varExpr = GenerateConstExpr(v);
+
+ if (initProvided)
+ exprs.Add(Expression.Call(varExpr, Method_Var_BindRoot, MaybeBox(Generate(RT.third(form))))); ;
+
+ if (mm != null)
+ exprs.Add(Expression.Call(varExpr, Method_Var_SetMeta, GenerateMapExpr(mm)));
+
+ exprs.Add(varExpr);
+
+ return Expression.Block(exprs);
+ }
+
+ static Expression MaybeBox(Expression expr)
+ {
+ if (expr.Type == typeof(void))
+ // I guess we'll pass a void. This happens when we have a throw, for example.
+ return Expression.Block(expr, Expression.Default(typeof(object)));
+
+ return expr.Type.IsValueType
+ ? Expression.Convert(expr, typeof(object))
+ : expr;
+ }
+
+
+ static Expression[] MaybeBox(Expression[] args)
+ {
+ // TODO: avoid copying array if not necessary
+ Expression[] boxedArgs = new Expression[args.Length];
+ for (int i1 = 0; i1 < args.Length; ++i1)
+ boxedArgs[i1] = MaybeBox(args[i1]);
+ return boxedArgs;
+ }
+
+ // DLR TryStatement has void type, so we must wrap it in a scope
+ // that has a target to return to.
+ private static Expression GenerateTryExpr(ISeq form)
+ {
+ // (try try-expr* catch-expr* finall-expr?)
+ // catch-expr: (catch classname sym expr*)
+ // finally-expr: (finally expr*)
+
+ IPersistentVector body = PersistentVector.EMPTY;
+ List<CatchBlock> catches = new List<CatchBlock>();
+ Expression finallyExpr = null;
+ bool caught = false;
+
+ for ( ISeq fs = form.rest(); fs != null; fs = fs.rest() )
+ {
+ object f = fs.first();
+ object op = (f is ISeq) ? ((ISeq)f).first() : null;
+ if (!Compiler.CATCH.Equals(op) && !Compiler.FINALLY.Equals(op))
+ {
+ if ( caught )
+ throw new Exception("Only catch or finally clause can follow catch in try expression");
+ body = body.cons(f);
+ }
+ else
+ {
+ if (Compiler.CATCH.Equals(op))
+ {
+ ISeq f1 = f as ISeq;
+ Type t = MaybeType(RT.second(f1),false);
+ if ( t == null )
+ throw new ArgumentException("Unable to resolve classname: " + RT.second(form));
+ if ( ! (RT.third(f1) is Symbol ))
+ throw new ArgumentException("Bad binding form, expected symbol, got: " + RT.third(f1));
+ Symbol sym = (Symbol) RT.third(f1);
+ if ( sym.Namespace != null )
+ throw new Exception("Can't bind qualified name: " + sym);
+
+ IPersistentMap dynamicBindings = RT.map( LOCAL_ENV, LOCAL_ENV.deref(),
+ IN_CATCH_FINALLY, RT.T);
+
+ try
+ {
+ Var.pushThreadBindings(dynamicBindings);
+ LocalBinding lb = RegisterLocal(sym,
+ (Symbol)(RT.second(f1) is Symbol ? RT.second(f1) : null),
+ null);
+ ParameterExpression exParam = Expression.Parameter(typeof(object),sym.Name); //asdf-tag
+ lb.ParamExpression = exParam;
+ Expression handler = GenerateBodyExpr(f1.rest().rest().rest());
+ catches.Add(Expression.Catch(t, exParam, handler));
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ caught = true;
+ }
+ else // finally
+ {
+ if ( fs.rest() != null )
+ throw new Exception("finally clause must be last in try expression");
+ try
+ {
+ Var.pushThreadBindings(RT.map(IN_CATCH_FINALLY,RT.T));
+ finallyExpr = GenerateBodyExpr(RT.rest(f));
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ }
+ }
+ }
+
+ Expression basicBody = GenerateBodyExpr(body.seq());
+ // Wrap the basic body, a Comma, in a return to a label
+ LabelTarget target = Expression.Label(basicBody.Type, "ret_label");
+ Expression tryBody = Expression.Return(target, basicBody);
+ TryExpression tryStmt = finallyExpr == null
+ ? Expression.TryCatch(tryBody,catches.ToArray())
+ : Expression.TryCatchFinally(tryBody, finallyExpr, catches.ToArray());
+ // TODO: What if basicBody.Type is typeof(void)? What if Enum?
+ //Expression defaultValue = basicBody.Type.IsValueType ? Expression.Constant(0, basicBody.Type) : Expression.Null(basicBody.Type);
+ Expression defaultValue = Expression.Default(basicBody.Type);
+ Expression whole = Expression.Block(tryStmt, Expression.Label(target, defaultValue));
+ return whole;
+ }
+
+ private static Expression GenerateThrowExpr(ISeq form)
+ {
+ return Expression.Throw(Expression.Convert(Generate(RT.second(form)), typeof(Exception)));
+ }
+
+ private static Expression GenerateMonitorEnterExpr(ISeq form)
+ {
+ return Expression.Call(Method_Monitor_Enter, Generate(RT.second(form)));
+ }
+
+ private static Expression GenerateMonitorExitExpr(ISeq form)
+ {
+ return Expression.Call(Method_Monitor_Exit, Generate(RT.second(form)));
+ }
+
+ #endregion
+
+ #region Fn generation
+
+ const int MAX_POSITIONAL_ARITY = 20;
+
+ sealed class FnDef
+ {
+ string _name;
+ public string Name
+ {
+ get { return _name; }
+ set { _name = value; }
+ }
+
+
+ string _simpleName;
+ public string SimpleName
+ {
+ get { return _simpleName; }
+ set { _simpleName = value; }
+ }
+
+ string _internalName;
+
+ public string InternalName
+ {
+ get { return _internalName; }
+ set { _internalName = value; }
+ }
+
+ string _thisName;
+ public string ThisName
+ {
+ get { return _thisName; }
+ set { _thisName = value; }
+ }
+
+ ParameterExpression _thisParam;
+ public ParameterExpression ThisParam
+ {
+ get { return _thisParam; }
+ set { _thisParam = value; }
+ }
+
+ bool _isVariadic;
+
+ public bool IsVariadic
+ {
+ get { return _isVariadic; }
+ set { _isVariadic = value; }
+ }
+
+
+ public Type ImplType
+ {
+ get { return IsVariadic ? typeof(RestFnImpl) : typeof(AFnImpl); }
+ }
+
+
+ internal void ComputeNames(ISeq form)
+ {
+ MethodDef enclosingMethod = (MethodDef)METHODS.deref();
+
+ string baseName = enclosingMethod != null
+ ? (enclosingMethod.Fn.Name + "$")
+ : (munge(CurrentNamespace.Name.Name) + "$");
+
+ if (form.rest().first() is Symbol)
+ _thisName = ((Symbol)form.rest().first()).Name;
+
+ _simpleName = (_thisName == null ? "fn" : munge(_thisName).Replace(".","_DOT_"));
+ _name = baseName + _simpleName;
+ _internalName = _name.Replace('.','/');
+ // fn.fntype = Type.getObjectType(fn.internalName) -- JAVA
+ }
+ }
+
+ sealed class MethodDef
+ {
+ FnDef _fn;
+ public FnDef Fn
+ {
+ get { return _fn; }
+ set { _fn = value; }
+ }
+
+ MethodDef _parent;
+ public MethodDef Parent
+ {
+ get { return _parent; }
+ set { _parent = value; }
+ }
+
+ LambdaExpression _lambda;
+ public LambdaExpression Lambda
+ {
+ get { return _lambda; }
+ set { _lambda = value; }
+ }
+
+ // LocalBinding => LocalBinding
+ // TODO: Why not use a set?
+ IPersistentMap _locals = PersistentHashMap.EMPTY;
+ public IPersistentMap Locals
+ {
+ get { return _locals; }
+ set { _locals = value; }
+ }
+
+ // LocalBinding => LocalBinding
+ // TODO: Why not use a set?
+ IPersistentVector _reqParms = PersistentVector.EMPTY;
+ public IPersistentVector ReqParms
+ {
+ get { return _reqParms; }
+ set { _reqParms = value; }
+ }
+
+ LocalBinding _restParm = null;
+ public LocalBinding RestParm
+ {
+ get { return _restParm; }
+ set { _restParm = value; }
+ }
+
+ IPersistentVector _argLocals;
+ public IPersistentVector ArgLocals
+ {
+ get { return _argLocals; }
+ set { _argLocals = value; }
+ }
+
+ public int RequiredArity
+ {
+ get { return _reqParms.count(); }
+ }
+
+ public bool IsVariadic
+ {
+ get { return _restParm != null; }
+ }
+
+ public int NumParams
+ {
+ get { return RequiredArity + (IsVariadic ? 1 : 0); }
+ }
+
+ internal MethodDef(FnDef fn, MethodDef parent)
+ {
+ _fn = fn;
+ _parent = parent;
+ }
+ }
+
+ sealed class LocalBinding
+ {
+ private readonly Symbol _sym;
+ public Symbol Symbol
+ {
+ get { return _sym; }
+ }
+
+ private readonly Symbol _tag;
+ public Symbol Tag
+ {
+ get { return _tag; }
+ }
+
+ private readonly Expression _init;
+ public Expression Init
+ {
+ get { return _init; }
+ }
+
+ private readonly String _name;
+ public String Name
+ {
+ get { return _name; }
+ }
+
+ private Expression _paramExpression;
+ public Expression ParamExpression
+ {
+ get { return _paramExpression; }
+ set { _paramExpression = value; }
+ }
+
+ public LocalBinding(Symbol sym, Symbol tag, Expression init)
+ {
+ // Java version:
+ //if(maybePrimitiveType(init) != null && tag != null)
+ // throw new UnsupportedOperationException("Can't type hint a local with a primitive initializer");
+
+ _sym = sym;
+ _tag = tag;
+ _init = init;
+ _name = munge(sym.Name);
+ }
+ }
+
+ private static LocalBinding RegisterLocal(Symbol sym, Symbol tag, Expression init )
+ {
+ LocalBinding b = new LocalBinding(sym,tag,init);
+ IPersistentMap localsMap = (IPersistentMap) LOCAL_ENV.deref();
+ LOCAL_ENV.set(localsMap.assoc(b.Symbol,b));
+ MethodDef method = (MethodDef)METHODS.deref();
+ if ( method != null )
+ method.Locals = (IPersistentMap)method.Locals.assoc(b,b);
+ return b;
+ }
+
+ private static readonly Var METHODS = Var.create(null);
+ private static readonly Var LOCAL_ENV = Var.create(PersistentHashMap.EMPTY);
+ private static readonly Var LOOP_LOCALS = Var.create(null);
+
+
+ // We need to pass the 'this' parameter to the methods when they are analyzed.
+ // The type of the 'this' parameter depends on whether there is an [ ... & .] signature.
+ // Do a quick scan to determine.
+ static bool ComputeIsVariadicQuickly(ISeq body)
+ {
+ for (ISeq s = body; s != null; s = s.rest())
+ {
+ if (!(((ISeq)s.first()).first() is IPersistentVector)) // bad syntax -- will be caught later
+ return false;
+ IPersistentVector paramList = (IPersistentVector)((ISeq)s.first()).first();
+ for (int i = 0; i < paramList.count(); i++)
+ if (Compiler._AMP_.Equals(paramList.nth(i)))
+ return true;
+ }
+ return false;
+ }
+
+ private static Expression GenerateFnExpr(ISeq form)
+ {
+ // This naming convention drawn from the Java code.
+ FnDef fn = new FnDef();
+ fn.ComputeNames(form);
+
+ Symbol name = null;
+ if ( RT.second(form) is Symbol )
+ {
+ name = (Symbol)RT.second(form);
+ form = RT.cons(Compiler.FN, RT.rest(RT.rest(form)));
+ }
+
+ // Normalize body
+ // If it is (fn [arg...] body ...), turn it into
+ // (fn ([arg...] body...))
+ // so that we can treat uniformly as (fn ([arg...] body...) ([arg...] body...) ... )
+ if (RT.second(form) is IPersistentVector)
+ form = RT.list(Compiler.FN, RT.rest(form));
+
+ // needs to be called after normalization
+ fn.IsVariadic = ComputeIsVariadicQuickly(RT.rest(form));
+
+
+ // Create the 'this' parameter needed for recursion
+ // we no longer need the name (second element) if it is given
+ if (name != null )
+ {
+ // ThisName will be non-null;
+ fn.ThisParam = Expression.Parameter(fn.ImplType, fn.ThisName);
+ }
+
+
+ MethodDef variadicMethod = null;
+ SortedDictionary<int, MethodDef> methods = new SortedDictionary<int, MethodDef>();
+
+ for (ISeq s = RT.rest(form); s != null; s = s.rest())
+ {
+ MethodDef method = GenerateFnMethod(fn, (ISeq) s.first());
+ if (method.IsVariadic)
+ {
+ if (variadicMethod == null)
+ variadicMethod = method;
+ else
+ throw new Exception("Can't have more than 1 variadic overload");
+ }
+ else if (! methods.ContainsKey(method.RequiredArity))
+ methods[method.RequiredArity] = method;
+ else
+ throw new Exception("Can't have 2 overloads with the same arity.");
+ }
+
+ if ( variadicMethod != null && methods.Count > 0 && methods.Keys.Max() >= variadicMethod.NumParams )
+ throw new Exception("Can't have fixed arity methods with more params than the variadic method.");
+
+ if (fn.IsVariadic != (variadicMethod != null))
+ throw new Exception("Internal error: ComputeIsVariadicQuickly failed!!!");
+
+ return GenerateFnLambda(fn, methods, variadicMethod);
+ }
+
+ enum ParamParseState { Required, Rest, Done };
+
+
+ private static MethodDef GenerateFnMethod(FnDef fn, ISeq form)
+ {
+ // form == ([args] body ... )
+ IPersistentVector parms = (IPersistentVector)RT.first(form);
+ ISeq body = RT.rest(form);
+
+ MethodDef method = new MethodDef(fn, (MethodDef)METHODS.deref());
+
+ try
+ {
+ LabelTarget loopLabel = Expression.Label();
+
+ Var.pushThreadBindings(PersistentHashMap.create(
+ METHODS, method,
+ LOOP_LABEL, loopLabel,
+ LOCAL_ENV, LOCAL_ENV.deref(),
+ LOOP_LOCALS, null));
+
+ // register 'this' as local 0
+ LocalBinding thisB = RegisterLocal(Symbol.intern(fn.ThisName ?? "fn__" + RT.nextID()), null, null); //asdf-tag
+ thisB.ParamExpression = fn.ThisParam;
+
+
+ IPersistentVector argLocals = PersistentVector.EMPTY;
+ int parmsCount = parms.count();
+ ParamParseState paramState = ParamParseState.Required;
+
+ for (int i = 0; i < parmsCount; i++)
+ {
+ if (!(parms.nth(i) is Symbol))
+ throw new ArgumentException("fn params must be Symbols");
+ Symbol p = parms.nth(i) as Symbol;
+ if (p.Namespace != null)
+ throw new Exception("Can't use qualified name as parameter: " + p);
+ if (p.Equals(Compiler._AMP_))
+ {
+ if (paramState == ParamParseState.Required)
+ paramState = ParamParseState.Rest;
+ else
+ throw new Exception("Invalid parameter list");
+ }
+ else
+ {
+ LocalBinding b = RegisterLocal(p, paramState == ParamParseState.Rest ? ISEQ : TagOf(p), null); // asdf-tag
+ //LocalBinding b = RegisterLocal(p, TagOf(p), null);
+
+ argLocals = argLocals.cons(b);
+ switch (paramState)
+ {
+ case ParamParseState.Required:
+ method.ReqParms = method.ReqParms.cons(b);
+ break;
+ case ParamParseState.Rest:
+ method.RestParm = b;
+ paramState = ParamParseState.Done;
+ break;
+ default:
+ throw new Exception("Unexpected parameter");
+ }
+ }
+ }
+
+ if (method.NumParams > MAX_POSITIONAL_ARITY)
+ throw new Exception(string.Format("Can't specify more than {0} parameters",MAX_POSITIONAL_ARITY));
+ LOOP_LOCALS.set(argLocals);
+ method.ArgLocals = argLocals;
+
+ List<ParameterExpression> parmExprs = new List<ParameterExpression>(argLocals.count());
+ List<ParameterExpression> typedParmExprs = new List<ParameterExpression>();
+ List<Expression> typedParmInitExprs = new List<Expression>();
+
+ for (int i = 0; i < argLocals.count(); i++)
+ {
+ LocalBinding b = (LocalBinding)argLocals.nth(i);
+
+ ParameterExpression pexpr = Expression.Parameter(typeof(object), b.Name); //asdf-tag
+ b.ParamExpression = pexpr;
+ parmExprs.Add(pexpr);
+
+ if (b.Tag != null)
+ {
+ // we have a type hint
+ // The ParameterExpression above will be the parameter to the function.
+ // We need to generate another local parameter that is typed.
+ // This will be the parameter tied to the LocalBinding so that the typing information is seen in the body.
+ Type t = TagToType(b.Tag);
+ ParameterExpression p2 = Expression.Parameter(t, b.Name);
+ b.ParamExpression = p2;
+ typedParmExprs.Add(p2);
+ typedParmInitExprs.Add(Expression.Assign(p2, Expression.Convert(pexpr, t)));
+ }
+ }
+
+
+ // TODO: Eventually, type this param to ISeq.
+ // This will require some reworking with signatures in various places around here.
+ //if (fn.IsVariadic)
+ // parmExprs.Add(Expression.Parameter(typeof(object), "____REST"));
+
+ // If we have any typed parameters, we need to add an extra block to do the initialization.
+
+ List<Expression> bodyExprs = new List<Expression>();
+ bodyExprs.AddRange(typedParmInitExprs);
+ bodyExprs.Add(Expression.Label(loopLabel));
+ bodyExprs.Add(MaybeBox(GenerateBodyExpr(body)));
+
+ Expression block;
+ if ( typedParmExprs.Count > 0 )
+ block = Expression.Block(typedParmExprs,bodyExprs);
+ else
+ block = Expression.Block(bodyExprs);
+
+ method.Lambda = Expression.Lambda(
+ FuncTypeHelpers.GetFFuncType(parmExprs.Count),
+ block,
+ fn.Name,
+ parmExprs);
+
+ //method.Lambda = Expression.Lambda(
+ // FuncTypeHelpers.GetFFuncType(parmExprs.Count),
+ // Expression.Block(Expression.Label(loopLabel), MaybeBox(GenerateBodyExpr(body))),
+ // fn.Name,
+ // parmExprs);
+
+ return method;
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ }
+
+ private static Expression GenerateFnLambda(FnDef fn, SortedDictionary<int, MethodDef> methods, MethodDef variadicMethod)
+ {
+ Type fnType = fn.IsVariadic ? typeof(RestFnImpl) : typeof(AFnImpl);
+
+ ParameterExpression p1 = fn.ThisParam ?? Expression.Parameter(fnType, "____x");
+ List<Expression> exprs = new List<Expression>();
+
+ if (fn.IsVariadic)
+ exprs.Add(Expression.Assign(p1, Expression.New(Ctor_RestFnImpl_1, Expression.Constant(variadicMethod.RequiredArity))));
+ else
+ exprs.Add(Expression.Assign(p1, Expression.New(Ctor_AFnImpl_0)));
+
+ foreach (KeyValuePair<int, MethodDef> pair in methods)
+ {
+ int arity = pair.Key;
+ LambdaExpression lambda = pair.Value.Lambda;
+ exprs.Add(Expression.Assign(Expression.Field(p1, "_fn" + arity), lambda));
+ }
+
+ if (fn.IsVariadic)
+ exprs.Add(Expression.Assign(Expression.Field(p1, "_fnDo" + variadicMethod.RequiredArity), variadicMethod.Lambda));
+
+ exprs.Add(p1);
+
+ Expression expr = Expression.Block(new ParameterExpression[] { p1 }, exprs);
+ return expr;
+ }
+
+
+
+ // There is a tremendous overlap between this and GenerateFnExpr+GenerateFnMethod. TODO: DRY it.
+ private static LambdaExpression GenerateTypedDelegateExpression(Type delegateType, Symbol name, IPersistentVector parms, ISeq body)
+ {
+ // Create the form that is more or less correct
+
+ ISeq form = (name == null)
+ ? RT.cons(Compiler.FN, RT.cons(parms, body))
+ : RT.cons(Compiler.FN, RT.cons(name, RT.cons(parms, body)));
+
+ FnDef fnDef = new FnDef();
+ fnDef.ComputeNames(form);
+
+ MethodDef methodDef = new MethodDef(fnDef, (MethodDef)METHODS.deref());
+
+ try
+ {
+ LabelTarget loopLabel = Expression.Label();
+
+ Var.pushThreadBindings(PersistentHashMap.create(
+ METHODS, methodDef,
+ LOOP_LABEL, loopLabel,
+ LOCAL_ENV, LOCAL_ENV.deref(),
+ LOOP_LOCALS, null));
+
+ // register 'this' as local 0
+ LocalBinding thisB = RegisterLocal(Symbol.intern(fnDef.ThisName ?? "fn__" + RT.nextID()), null, null);
+ thisB.ParamExpression = fnDef.ThisParam;
+
+ IPersistentVector argLocals = PersistentVector.EMPTY;
+ int parmsCount = parms.count();
+ ParamParseState paramState = ParamParseState.Required;
+
+ for (int i = 0; i < parmsCount; i++)
+ {
+ if (!(parms.nth(i) is Symbol))
+ throw new ArgumentException("fn params must be Symbols");
+ Symbol p = parms.nth(i) as Symbol;
+ if (p.Namespace != null)
+ throw new Exception("Can't use qualified name as parameter: " + p);
+ if (p.Equals(Compiler._AMP_))
+ {
+ if (paramState == ParamParseState.Required)
+ paramState = ParamParseState.Rest;
+ else
+ throw new Exception("Invalid parameter list");
+ }
+ else
+ {
+ // TODO: Need more type inferencing to make this work.
+ //LocalBinding b = RegisterLocal(p, paramState == ParamParseState.Rest ? ISEQ : TagOf(p), null);
+ LocalBinding b = RegisterLocal(p, TagOf(p), null);
+
+ argLocals = argLocals.cons(b);
+ switch (paramState)
+ {
+ case ParamParseState.Required:
+ methodDef.ReqParms = methodDef.ReqParms.cons(b);
+ break;
+ case ParamParseState.Rest:
+ methodDef.RestParm = b;
+ paramState = ParamParseState.Done;
+ break;
+ default:
+ throw new Exception("Unexpected parameter");
+ }
+ }
+ }
+
+ MethodInfo invokeMI = delegateType.GetMethod("Invoke");
+ Type returnType = invokeMI.ReturnType;
+ ParameterInfo[] delParams = invokeMI.GetParameters();
+
+ bool isVariadic = (invokeMI.CallingConvention & CallingConventions.VarArgs) != 0;
+ if (isVariadic != methodDef.IsVariadic)
+ throw new ArgumentException("Arglist and delegate type must agree on being variadic.");
+
+ if (delParams.Length != argLocals.count() )
+ throw new ArgumentException("Wrong number of parameters to generate typed delegate");
+
+
+ if (methodDef.NumParams > MAX_POSITIONAL_ARITY)
+ throw new Exception(string.Format("Can't specify more than {0} parameters",MAX_POSITIONAL_ARITY));
+
+ LOOP_LOCALS.set(argLocals);
+ methodDef.ArgLocals = argLocals;
+
+ List<ParameterExpression> parmExprs = new List<ParameterExpression>(argLocals.count());
+ for (int i = 0; i < argLocals.count(); i++)
+ {
+ LocalBinding b = (LocalBinding)argLocals.nth(i);
+ ParameterExpression pexpr = Expression.Parameter(delParams[i].ParameterType, b.Name); //asdf-tag
+ b.ParamExpression = pexpr;
+ parmExprs.Add(pexpr);
+ }
+
+
+ methodDef.Lambda = Expression.Lambda(
+ delegateType,
+ Expression.Block(
+ Expression.Label(loopLabel),
+ Expression.Convert(GenerateBodyExpr(body),returnType)),
+ fnDef.Name,
+ parmExprs);
+
+ return methodDef.Lambda;
+
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+
+ }
+
+ //private Expression GenerateFixedArgMethodCall(MethodDef method, ParameterExpression restParam, out Type methodType)
+ //{
+ // LambdaExpression lambda = method.Lambda;
+ // InvocationExpression ret;
+
+ // switch (method.RequiredArity)
+ // {
+ // case 0:
+ // ret = Expression.Invoke(lambda);
+ // methodType = typeof(Microsoft.Func<object>);
+ // break;
+ // case 1:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0));
+ // methodType = typeof(Microsoft.Func<object, object>);
+ // break;
+ // case 2:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1));
+ // methodType = typeof(Microsoft.Func<object, object, object>);
+ // break;
+ // case 3:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2));
+ // methodType = typeof(Microsoft.Func<object, object, object, object>);
+ // break;
+ // case 4:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object>);
+ // break;
+ // case 5:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object>);
+ // break;
+ // case 6:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object>);
+ // break;
+ // case 7:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object>);
+ // break;
+ // case 8:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6), GetParamArrayItem(restParam, 7));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object, object>);
+ // break;
+ // case 9:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6), GetParamArrayItem(restParam, 7),
+ // GetParamArrayItem(restParam, 8));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object, object, object>);
+ // break;
+ // case 10:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6), GetParamArrayItem(restParam, 7),
+ // GetParamArrayItem(restParam, 8), GetParamArrayItem(restParam, 9));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object, object, object, object>);
+ // break;
+ // case 11:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6), GetParamArrayItem(restParam, 7),
+ // GetParamArrayItem(restParam, 8), GetParamArrayItem(restParam, 9), GetParamArrayItem(restParam, 10));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object, object, object, object, object>);
+ // break;
+ // case 12:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6), GetParamArrayItem(restParam, 7),
+ // GetParamArrayItem(restParam, 8), GetParamArrayItem(restParam, 9), GetParamArrayItem(restParam, 10), GetParamArrayItem(restParam, 11));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object, object, object, object, object, object>);
+ // break;
+ // case 13:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6), GetParamArrayItem(restParam, 7),
+ // GetParamArrayItem(restParam, 8), GetParamArrayItem(restParam, 9), GetParamArrayItem(restParam, 10), GetParamArrayItem(restParam, 11),
+ // GetParamArrayItem(restParam, 12));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object, object, object, object, object, object, object>);
+ // break;
+ // case 14:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6), GetParamArrayItem(restParam, 7),
+ // GetParamArrayItem(restParam, 8), GetParamArrayItem(restParam, 9), GetParamArrayItem(restParam, 10), GetParamArrayItem(restParam, 11),
+ // GetParamArrayItem(restParam, 12), GetParamArrayItem(restParam, 13));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>);
+ // break;
+ // case 15:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6), GetParamArrayItem(restParam, 7),
+ // GetParamArrayItem(restParam, 8), GetParamArrayItem(restParam, 9), GetParamArrayItem(restParam, 10), GetParamArrayItem(restParam, 11),
+ // GetParamArrayItem(restParam, 12), GetParamArrayItem(restParam, 13), GetParamArrayItem(restParam, 14));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>);
+ // break;
+ // case 16:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6), GetParamArrayItem(restParam, 7),
+ // GetParamArrayItem(restParam, 8), GetParamArrayItem(restParam, 9), GetParamArrayItem(restParam, 10), GetParamArrayItem(restParam, 11),
+ // GetParamArrayItem(restParam, 12), GetParamArrayItem(restParam, 13), GetParamArrayItem(restParam, 14), GetParamArrayItem(restParam, 15));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>);
+ // break;
+
+
+ // default:
+ // throw new Exception("We should never have been able to get here: 20 arguments?");
+ // }
+ // return ret;
+ //}
+
+
+ //private Expression GenerateVariadicMethodCall(MethodDef variM , ParameterExpression restParam, out Type methodType)
+ //{
+ // LambdaExpression lambda = variM.Lambda;
+ // InvocationExpression ret;
+
+ // switch (variM.RequiredArity)
+ // {
+ // case 0:
+ // ret = Expression.Invoke(lambda,
+ // ConvertParamArrayToISeq(restParam, 0));
+ // methodType = typeof(Microsoft.Func<ISeq, object>);
+ // break;
+ // case 1:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0),
+ // ConvertParamArrayToISeq(restParam, 1));
+ // methodType = typeof(Microsoft.Func<object, ISeq, object>);
+ // break;
+ // case 2:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1),
+ // ConvertParamArrayToISeq(restParam, 2));
+ // methodType = typeof(Microsoft.Func< object, object, ISeq, object>);
+ // break;
+ // case 3:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2),
+ // ConvertParamArrayToISeq(restParam, 3));
+ // methodType = typeof(Microsoft.Func<object, object, object, ISeq, object>);
+ // break;
+ // case 4:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // ConvertParamArrayToISeq(restParam, 4));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, ISeq, object>);
+ // break;
+ // case 5:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4),
+ // ConvertParamArrayToISeq(restParam, 5));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, ISeq, object>);
+ // break;
+ // case 6:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5),
+ // ConvertParamArrayToISeq(restParam, 6));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, ISeq, object>);
+ // break;
+ // case 7:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6),
+ // ConvertParamArrayToISeq(restParam, 7));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, ISeq, object>);
+ // break;
+ // case 8:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6), GetParamArrayItem(restParam, 7),
+ // ConvertParamArrayToISeq(restParam, 8));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object, ISeq, object>);
+ // break;
+ // case 9:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6), GetParamArrayItem(restParam, 7),
+ // GetParamArrayItem(restParam, 8),
+ // ConvertParamArrayToISeq(restParam, 9));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object, object, ISeq, object>);
+ // break;
+ // case 10:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6), GetParamArrayItem(restParam, 7),
+ // GetParamArrayItem(restParam, 8), GetParamArrayItem(restParam, 9),
+ // ConvertParamArrayToISeq(restParam, 10));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object, object, object, ISeq, object>);
+ // break;
+ // case 11:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6), GetParamArrayItem(restParam, 7),
+ // GetParamArrayItem(restParam, 8), GetParamArrayItem(restParam, 9), GetParamArrayItem(restParam, 10),
+ // ConvertParamArrayToISeq(restParam, 11));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object, object, object, object, ISeq, object>);
+ // break;
+ // case 12:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6), GetParamArrayItem(restParam, 7),
+ // GetParamArrayItem(restParam, 8), GetParamArrayItem(restParam, 9), GetParamArrayItem(restParam, 10), GetParamArrayItem(restParam, 11),
+ // ConvertParamArrayToISeq(restParam, 12));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object, object, object, object, object, ISeq, object>);
+ // break;
+ // case 13:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6), GetParamArrayItem(restParam, 7),
+ // GetParamArrayItem(restParam, 8), GetParamArrayItem(restParam, 9), GetParamArrayItem(restParam, 10), GetParamArrayItem(restParam, 11),
+ // GetParamArrayItem(restParam, 12),
+ // ConvertParamArrayToISeq(restParam, 13));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object, object, object, object, object, object, ISeq, object>);
+ // break;
+ // case 14:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6), GetParamArrayItem(restParam, 7),
+ // GetParamArrayItem(restParam, 8), GetParamArrayItem(restParam, 9), GetParamArrayItem(restParam, 10), GetParamArrayItem(restParam, 11),
+ // GetParamArrayItem(restParam, 12), GetParamArrayItem(restParam, 13),
+ // ConvertParamArrayToISeq(restParam, 14));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object, object, object, object, object, object, object, ISeq, object>);
+ // break;
+ // case 15:
+ // ret = Expression.Invoke(lambda,
+ // GetParamArrayItem(restParam, 0), GetParamArrayItem(restParam, 1), GetParamArrayItem(restParam, 2), GetParamArrayItem(restParam, 3),
+ // GetParamArrayItem(restParam, 4), GetParamArrayItem(restParam, 5), GetParamArrayItem(restParam, 6), GetParamArrayItem(restParam, 7),
+ // GetParamArrayItem(restParam, 8), GetParamArrayItem(restParam, 9), GetParamArrayItem(restParam, 10), GetParamArrayItem(restParam, 11),
+ // GetParamArrayItem(restParam, 12), GetParamArrayItem(restParam, 13), GetParamArrayItem(restParam, 14),
+ // ConvertParamArrayToISeq(restParam, 15));
+ // methodType = typeof(Microsoft.Func<object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, ISeq, object>);
+ // break;
+ // default:
+ // throw new Exception("We should never have been able to get here: 20 arguments?");
+ // }
+ // return ret;
+ //}
+
+ private static Expression GetParamArrayItem(Expression e, int i)
+ {
+ return Expression.ArrayIndex(e, Expression.Constant(i));
+ }
+
+ private static Expression ConvertParamArrayToISeq(Expression e, int i)
+ {
+ return Expression.Call(Method_ArraySeq_create_array_int, e, Expression.Constant(i));
+ }
+
+ #endregion
+
+ #region fn-related special forms
+
+ struct BindingInit
+ {
+ private readonly LocalBinding _binding;
+ public LocalBinding Binding
+ {
+ get { return _binding; }
+ }
+
+ private readonly Expression _init;
+ public Expression Init
+ {
+ get { return _init; }
+ }
+
+ public BindingInit(LocalBinding binding, Expression init)
+ {
+ _binding = binding;
+ _init = init;
+ }
+
+ }
+
+ public static readonly Var LOOP_LABEL = Var.create(null);
+
+ private static Expression GenerateLetExpr(ISeq form)
+ {
+ // form => (let [var1 val1 var2 val2 ... ] body ... )
+ // or (loop [var1 val1 var2 val2 ... ] body ... )
+
+ bool isLoop = form.first().Equals(Compiler.LOOP);
+ IPersistentVector bindings = RT.second(form) as IPersistentVector;
+
+ if (bindings == null)
+ throw new ArgumentException("Bad binding form, expected vector");
+
+ if ((bindings.count() % 2) != 0)
+ throw new ArgumentException("Bad binding form, expected matched symbol/value pairs.");
+
+ ISeq body = RT.rest(RT.rest(form));
+
+ // TODO: This is one place where context makes a difference. Need to figure this out.
+ // Second test clause added in Rev 1216.
+ //if (ctxt == C.EVAL || (context == c.EXPRESSION && isLoop))
+ // return Generate(RT.list(RT.list(Compiler.FN, PersistentVector.EMPTY, form)));
+
+ // As of Rev 1216, I tried it out.
+ // However, it goes into an infinite loop. Still need to figure this out.
+ //if (isLoop)
+ // Generate(RT.list(RT.list(Compiler.FN, PersistentVector.EMPTY, form)));
+
+ IPersistentMap dynamicBindings = PersistentHashMap.create(LOCAL_ENV, LOCAL_ENV.deref());
+
+ if (isLoop)
+ dynamicBindings = dynamicBindings.assoc(LOOP_LOCALS, null);
+
+ try
+ {
+ Var.pushThreadBindings(dynamicBindings);
+ IPersistentVector bindingInits = PersistentVector.EMPTY;
+ IPersistentVector loopLocals = PersistentVector.EMPTY;
+
+ for ( int i=0; i<bindings.count(); i+=2 )
+ {
+ if (!(bindings.nth(i) is Symbol))
+ throw new ArgumentException("Bad binding form, expected symbol, got " + bindings.nth(i));
+ Symbol sym = (Symbol) bindings.nth(i);
+ if ( sym.Namespace != null )
+ throw new Exception("Can't let qualified name: " + sym);
+ Expression init = Generate(/*C.EXPRESSION, */ bindings.nth(i+1) /* , sym.Name */);
+ // Sequential enhancement of env (like Lisp let*)
+ LocalBinding b = RegisterLocal(sym,TagOf(sym),init);
+ b.ParamExpression = Expression.Variable(typeof(object), b.Name); //asdf-tag
+ bindingInits = bindingInits.cons(new BindingInit(b,init));
+
+ if ( isLoop )
+ loopLocals = loopLocals.cons(b);
+ }
+ if ( isLoop )
+ LOOP_LOCALS.set(loopLocals);
+
+ LabelTarget loopLabel = Expression.Label();
+
+ List<ParameterExpression> parms = new List<ParameterExpression>();
+ List<Expression> forms = new List<Expression>();
+
+ for ( int i=0; i<bindingInits.count(); i++ )
+ {
+ BindingInit bi = (BindingInit) bindingInits.nth(i);
+ ParameterExpression parmExpr = (ParameterExpression) bi.Binding.ParamExpression;
+ parms.Add(parmExpr);
+ forms.Add(Expression.Assign(parmExpr,MaybeBox(bi.Init)));
+ }
+
+
+ forms.Add(Expression.Label(loopLabel));
+
+ try
+ {
+ if ( isLoop )
+ Var.pushThreadBindings(PersistentHashMap.create(LOOP_LABEL,loopLabel));
+
+ forms.Add(GenerateBodyExpr( /* isLoop ? C.RETURN : context , */ body));
+ }
+ finally
+ {
+ if ( isLoop )
+ Var.popThreadBindings();
+ }
+
+ Expression block = Expression.Block(parms,forms);
+ return block;
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ }
+
+
+ //null or not
+ public static readonly Var IN_CATCH_FINALLY = Var.create(null);
+ public static readonly Var IN_TAIL_POSITION = Var.create(null);
+
+ // Don't do what I did the first time: Evaluate the forms/assignments sequentially.
+ // Need to evaluate all the forms, then assign them.
+
+ private static Expression GenerateRecurExpr(ISeq form)
+ {
+ IPersistentVector loopLocals = (IPersistentVector) LOOP_LOCALS.deref();
+ if ( IN_TAIL_POSITION.deref() == null || loopLocals == null )
+ throw new InvalidOperationException("Can only recur from tail position");
+ if (IN_CATCH_FINALLY.deref() != null)
+ throw new InvalidOperationException("Cannot recur from catch/finally.");
+ IPersistentVector args = PersistentVector.EMPTY;
+ for ( ISeq s = form.rest(); s != null; s = s.rest() )
+ args = args.cons(Generate(s.first()));
+ if ( args.count() != loopLocals.count())
+ throw new ArgumentException(string.Format("Mismatched argument count to recur, expected: {0} args, got {1}",loopLocals.count(),args.count()));
+
+ LabelTarget loopLabel = (LabelTarget)LOOP_LABEL.deref();
+ if (loopLabel == null)
+ throw new InvalidOperationException("Recur not in proper context.");
+
+ int argCount = args.count();
+
+ List<ParameterExpression> tempVars = new List<ParameterExpression>(argCount);
+ List<Expression> tempAssigns = new List<Expression>(2 * argCount+1);
+ List<Expression> finalAssigns = new List<Expression>(argCount);
+
+ // Evaluate all the init forms into local variables.
+ for ( int i=0; i<loopLocals.count(); i++ )
+ {
+ LocalBinding b = (LocalBinding)loopLocals.nth(i);
+ ParameterExpression tempVar = Expression.Parameter(b.ParamExpression.Type, "local" + i); //asdf-tag
+ Expression arg = (Expression) args.nth(i);
+ tempVars.Add(tempVar);
+
+ if ( tempVar.Type == typeof(Object) )
+ tempAssigns.Add(Expression.Assign(tempVar,MaybeBox(arg)));
+ else
+ tempAssigns.Add(Expression.Assign(tempVar, Expression.Convert(arg, tempVar.Type))); //asdf-tag
+
+ finalAssigns.Add(Expression.Assign(b.ParamExpression, tempVar)); //asdf-tag
+ }
+
+ List<Expression> exprs = tempAssigns;
+ exprs.AddRange(finalAssigns);
+ exprs.Add(Expression.Goto(loopLabel));
+ // need to do this to get a return value in the type inferencing -- else can't use this in a then or else clause.
+ exprs.Add(Expression.Constant(null));
+ return Expression.Block(tempVars,exprs);
+ }
+
+ #endregion
+
+ #region Assign
+
+ private static Expression GenerateAssignExpr(ISeq form)
+ {
+ if (form.count() != 3)
+ throw new ArgumentException("Malformed assignment, expecting (set! target val)");
+
+ object target = RT.second(form);
+ object init = RT.third(form);
+
+ Var v;
+
+
+ if ( (v = FindAsVar(target)) != null)
+ return GenerateVarAssignExpr(v, init);
+
+ Type t;
+
+ if ((t = FindAsDirectStaticFieldReference(target)) != null)
+ return GenerateDirectStaticFieldAssignExpr(t, (target as Symbol).Name, init);
+
+ if ( IsFieldReference(target))
+ return GenerateFieldAssignExpr(RT.second((ISeq)target),(string) RT.third((ISeq)target),init);
+
+ throw new ArgumentException("Invalid assignment target");
+
+ }
+
+ private static Var FindAsVar(object target)
+ {
+ Symbol sym = target as Symbol;
+ if (sym == null)
+ return null;
+
+ if (sym.Namespace == null && ReferenceLocal(sym) != null)
+ return null;
+
+ // There is case in GenerateSymbolExpr that deals with a symbol representing a static field reference.
+ // Should we allow that here?
+
+ object o = Compiler.Resolve(sym);
+ return o as Var;
+ }
+
+ private static Type FindAsDirectStaticFieldReference(object target)
+ {
+ Symbol sym = target as Symbol;
+ if (sym == null)
+ return null;
+
+ if (sym.Namespace == null && ReferenceLocal(sym) != null)
+ return null;
+
+ if (Compiler.namespaceFor(sym) == null)
+ {
+ Symbol nsSym = Symbol.create(sym.Namespace);
+ Type t = MaybeType(nsSym, false);
+ if (t != null)
+ return t;
+ }
+ return null;
+ }
+
+ private static bool IsFieldReference(object target)
+ {
+ ISeq form = target as ISeq;
+ if (form == null)
+ return false;
+
+ if (form.count() != 3)
+ return false;
+
+ if (!Compiler.DOT.Equals(form.first()))
+ return false;
+
+ if (!(RT.third(form) is string))
+ return false;
+
+ return true;
+ }
+
+ private static Expression GenerateVarAssignExpr(Var v, object init)
+ {
+ Expression initExpr = Generate(init);
+
+ return Expression.Call(Expression.Constant(v), Method_Var_set, MaybeBox(initExpr));
+ }
+
+
+ private static Expression GenerateDirectStaticFieldAssignExpr(Type t, string fieldName, object init)
+ {
+ Expression initExpr = Generate(init);
+
+ FieldInfo f = t.GetField(fieldName, BindingFlags.Static | BindingFlags.Public | BindingFlags.SetField);
+ if (f != null)
+ return Expression.Assign(Expression.Field(null, f), initExpr);
+
+ PropertyInfo p = t.GetProperty(fieldName, BindingFlags.Static | BindingFlags.Public | BindingFlags.SetField);
+ if (p != null)
+ return Expression.Assign(Expression.Property(null, p), initExpr);
+
+ throw new ArgumentException(string.Format("No field/property named: {0} for type: {1}", fieldName, t.Name));
+ }
+
+ private static Expression GenerateFieldAssignExpr(object classOrInstance, string fieldName, object init)
+ {
+ Type t = MaybeType(classOrInstance, false);
+ if (t != null)
+ return GenerateDirectStaticFieldAssignExpr(t, fieldName, init);
+
+ // we are an instance
+ Expression instance = Generate(classOrInstance);
+ Expression initExpr = Generate(init);
+
+ // I doubt that this will work. We will have to do a runtime determination
+ FieldInfo f = instance.Type.GetField(fieldName, BindingFlags.Public);
+ if (f != null)
+ return Expression.Field(instance, f);
+
+ PropertyInfo p = instance.Type.GetProperty(fieldName, BindingFlags.Static | BindingFlags.Public);
+ if (p != null)
+ return Expression.Property(instance, p);
+
+ throw new ArgumentException(string.Format("No field/property named: {0} ", fieldName));
+ }
+
+ #endregion
+
+ #region .NET-interop special forms
+
+ private static Expression GenerateHostExpr(ISeq form)
+ {
+ // form is one of:
+ // (. x fieldname-sym)
+ // (. x 0-ary-method)
+ // (. x propertyname-sym)
+ // (. x methodname-sym args+)
+ // (. x (methodname-sym args?))
+ if (form.count() < 3)
+ throw new ArgumentException("Malformed member expression, expecting (. target member ... )");
+ // determine static or instance
+ // static target must be symbol, either fully.qualified.Typename or Typename that has been imported
+ Type t = MaybeType(RT.second(form),false);
+ // at this point, t will be non-null if static
+ Expression instance = null;
+ if (t == null)
+ instance = Generate(RT.second(form));
+
+ if ( form.count() == 3 && RT.third(form) is Symbol )
+ {
+ Symbol sym = (Symbol) RT.third(form);
+ if ( t != null )
+ {
+ FieldInfo f = t.GetField(sym.Name, BindingFlags.Static | BindingFlags.Public);
+ if (f != null)
+ return Expression.Field(null, f);
+
+ PropertyInfo p = t.GetProperty(sym.Name, BindingFlags.Static | BindingFlags.Public);
+ if (p != null)
+ return Expression.Property(null, p);
+ }
+ else
+ {
+ // I doubt that this will work. We will have to do a runtime determination
+ FieldInfo f = instance.Type.GetField(sym.Name, BindingFlags.Instance | BindingFlags.Public);
+ if (f != null)
+ return Expression.Field(instance, f);
+
+ PropertyInfo p = instance.Type.GetProperty(sym.Name, BindingFlags.Instance | BindingFlags.Public);
+ if (p != null)
+ return Expression.Property(instance, p);
+ }
+ }
+
+ ISeq call = RT.third(form) is ISeq ? (ISeq)RT.third(form) : RT.rest(RT.rest(form));
+
+ if (!(RT.first(call) is Symbol))
+ throw new ArgumentException("Malformed member exception");
+
+ string methodName = ((Symbol)RT.first(call)).Name;
+ int numArgs = call.count() - 1;
+
+ Expression[] args = new Expression[numArgs];
+ int i = 0;
+ for (ISeq s = call.rest(); s != null; s = s.rest(), i++)
+ args[i] = Generate(s.first());
+
+ BindingFlags flags = BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.InvokeMethod;
+
+ if (t != null)
+ flags |= BindingFlags.Static;
+ else
+ flags |= BindingFlags.Instance;
+
+ Type targetType = t ?? instance.Type;
+
+ //DEBUG:
+ //IEnumerable<MethodInfo> einfo1 = targetType.GetMethods();
+ //List<MethodInfo> infos1 = new List<MethodInfo>(einfo1);
+
+ IEnumerable<MethodInfo> einfo = targetType.GetMethods(flags).Where(info => info.Name == methodName && info.GetParameters().Length == args.Length);
+ List<MethodInfo> infos = new List<MethodInfo>(einfo);
+
+ if (t != null && infos.Count == 0)
+ throw new InvalidOperationException(string.Format("No method named: {0} in type: {1}", methodName, targetType.Name));
+ else if (infos.Count == 1)
+ {
+ // TODO: if t is not null, but instance isn't typed, we may be missing overloads. So I added a t != null.
+ // We can improve this when we add better type info propagation.
+
+ // we have a unique match, generate call directly
+ if (t != null)
+ return AstUtils.SimpleCallHelper(infos[0], args);
+ else
+ //return Expression.Call(instance, infos[0], args); //asdf-tag
+ return AstUtils.SimpleCallHelper(instance,infos[0], args);
+ }
+ else
+ {
+ if (RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
+ {
+ // TODO: use DLR IO
+ ((TextWriter)RT.ERR.deref()).WriteLine(string.Format("Reflection warning, line: {0} - call to {1} can't be resolved.\n", /* line ,*/0, methodName));
+ }
+
+ Expression[] moreArgs = new Expression[3];
+ moreArgs[0] = Expression.Constant(methodName);
+ moreArgs[1] = t != null ? Expression.Constant(t) : instance;
+ moreArgs[2] = Expression.NewArrayInit(typeof(object), MaybeBox(args));
+
+ if (t != null)
+ return Expression.Call(Method_Reflector_CallStaticMethod, moreArgs);
+ else
+ return Expression.Call(Method_Reflector_CallInstanceMethod, moreArgs);
+ }
+ }
+
+
+
+ private static Expression GenerateNewExpr(ISeq form)
+ {
+ // form => (new Classname args ... )
+ if (form.count() < 2)
+ throw new Exception("wrong number of arguments, expecting: (new Classname args ...)");
+ Type t = MaybeType(RT.second(form), false);
+ if (t == null)
+ throw new ArgumentException("Unable to resolve classname: " + RT.second(form));
+
+ int numArgs = form.count() - 2;
+ Expression[] args = new Expression[numArgs];
+ int i = 0;
+ for (ISeq s = RT.rest(RT.rest(form)); s != null; s = s.rest(), i++)
+ args[i] = Generate(s.first());
+
+ List<ConstructorInfo> cinfos = new List<ConstructorInfo>(t.GetConstructors().Where(x => x.GetParameters().Length == numArgs && x.IsPublic));
+
+ if (cinfos.Count == 0)
+ throw new InvalidOperationException(string.Format("No constructor in type: {0} with {1} arguments", t.Name, numArgs));
+
+ else if (cinfos.Count == 1)
+ {
+ // we have a unique match, generate directly
+ // Need to try to convert the arguments, or the call to Expression.New will choke
+ ConstructorInfo info = cinfos[0];
+ Expression[] convArgs = new Expression[numArgs];
+ for ( i=0; i < numArgs; i++ )
+ convArgs[i] = Expression.Convert(args[i],info.GetParameters()[i].ParameterType);
+ return Expression.New(info, convArgs);
+ }
+ else
+ {
+ // we must defer to runtime
+
+ if (RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
+ {
+ // TODO: use DLR IO
+ ((TextWriter)RT.ERR.deref()).WriteLine(string.Format("Reflection warning, line: {0} - call to new can't be resolved.\n", /* line ,*/0));
+ }
+
+ Expression[] moreArgs = new Expression[2];
+ moreArgs[0] = Expression.Constant(t);
+ moreArgs[1] = Expression.NewArrayInit(typeof(object), MaybeBox(args));
+
+ return Expression.Call(Method_Reflector_InvokeConstructor, moreArgs);
+ }
+ }
+
+
+ #endregion
+
+
+ static Type TagToType(object tag)
+ {
+ Type t = MaybeType(tag, true);
+ if (tag is Symbol)
+ {
+ Symbol sym = (Symbol)tag;
+ if (sym.Namespace == null) // if ns-qualified, can't be classname
+ {
+ switch (sym.Name)
+ {
+ case "ints": t = typeof(int[]); break;
+ case "longs": t = typeof(long[]); break;
+ case "floats": t = typeof(float[]); break;
+ case "doubles": t = typeof(double[]); break;
+ case "chars": t = typeof(char[]); break;
+ case "shorts": t = typeof(short[]); break;
+ case "bytes": t = typeof(byte[]); break;
+ case "booleans":
+ case "bools": t = typeof(bool[]); break;
+ }
+ }
+ }
+ if (t != null)
+ return t;
+
+ throw new ArgumentException("Unable to resolve classname: " + tag);
+ }
+
+ }
+}
\ No newline at end of file diff --git a/ClojureCLR/Clojure/Clojure/Lib/AFn.cs b/ClojureCLR/Clojure/Clojure/Lib/AFn.cs new file mode 100644 index 00000000..4a33fe7a --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/AFn.cs @@ -0,0 +1,506 @@ +/**
+ * 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.Collections;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Provides a basic implementation of <see cref="IFn">IFn</see> interface methods.
+ /// </summary>
+ [Serializable]
+ public abstract class AFn : Obj, IFn
+ {
+ #region C-tors
+
+ /// <summary>
+ /// Initialize an <see cref="AFn">AFn</see> with the given metadata.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ public AFn(IPersistentMap meta)
+ : base(meta)
+ {
+ }
+
+ /// <summary>
+ /// Initialize an <see cref="AFn">AFn</see> with the null metadata.
+ /// </summary>
+ public AFn()
+ {
+ }
+
+ #endregion
+
+ #region IObj members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ /// <remarks>Not implemented at this level.
+ /// This just introduces the virtual method definition
+ /// for concrete classes to override, or not.
+ /// (Some may want to leave it unimplemented.)</remarks>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+
+ #region IFn Members
+
+
+ public virtual object invoke()
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4, object arg5)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4, object arg5,
+ object arg6)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4, object arg5,
+ object arg6, object arg7)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4, object arg5,
+ object arg6, object arg7, object arg8)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4, object arg5,
+ object arg6, object arg7, object arg8, object arg9)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4, object arg5,
+ object arg6, object arg7, object arg8, object arg9, object arg10)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4, object arg5,
+ object arg6, object arg7, object arg8, object arg9, object arg10, object arg11)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4, object arg5,
+ object arg6, object arg7, object arg8, object arg9, object arg10, object arg11,
+ object arg12)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4, object arg5,
+ object arg6, object arg7, object arg8, object arg9, object arg10, object arg11,
+ object arg12, object arg13)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4, object arg5,
+ object arg6, object arg7, object arg8, object arg9, object arg10, object arg11,
+ object arg12, object arg13, object arg14)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4, object arg5,
+ object arg6, object arg7, object arg8, object arg9, object arg10, object arg11,
+ object arg12, object arg13, object arg14, object arg15)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4, object arg5,
+ object arg6, object arg7, object arg8, object arg9, object arg10, object arg11,
+ object arg12, object arg13, object arg14, object arg15, object arg16)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4, object arg5,
+ object arg6, object arg7, object arg8, object arg9, object arg10, object arg11,
+ object arg12, object arg13, object arg14, object arg15, object arg16, object arg17)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4, object arg5,
+ object arg6, object arg7, object arg8, object arg9, object arg10, object arg11,
+ object arg12, object arg13, object arg14, object arg15, object arg16, object arg17,
+ object arg18)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4, object arg5,
+ object arg6, object arg7, object arg8, object arg9, object arg10, object arg11,
+ object arg12, object arg13, object arg14, object arg15, object arg16, object arg17,
+ object arg18, object arg19)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4, object arg5,
+ object arg6, object arg7, object arg8, object arg9, object arg10, object arg11,
+ object arg12, object arg13, object arg14, object arg15, object arg16, object arg17,
+ object arg18, object arg19, object arg20)
+ {
+ throw WrongArityException();
+ }
+
+ public virtual object invoke(object arg1, object arg2, object arg3, object arg4, object arg5,
+ object arg6, object arg7, object arg8, object arg9, object arg10, object arg11,
+ object arg12, object arg13, object arg14, object arg15, object arg16, object arg17,
+ object arg18, object arg19, object arg20, params object[] args)
+ {
+ throw WrongArityException();
+ }
+
+
+
+ public virtual object applyTo(ISeq arglist)
+ {
+ return ApplyToHelper(this, arglist);
+ }
+
+
+
+ public static object ApplyToHelper(IFn ifn, ISeq arglist)
+ {
+ switch (RT.BoundedLength(arglist, 20))
+ {
+ case 0:
+ return ifn.invoke();
+ case 1:
+ return ifn.invoke(arglist.first());
+ case 2:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 3:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 4:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 5:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 6:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 7:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 8:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 9:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 10:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 11:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 12:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 13:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 14:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 15:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 16:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 17:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 18:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 19:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ case 20:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ );
+ default:
+ return ifn.invoke(arglist.first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , (arglist = arglist.rest()).first()
+ , RT.SeqToArray<object>(arglist.rest()));
+ }
+ }
+
+
+ public Exception WrongArityException()
+ {
+ string name = GetType().Name;
+ int suffix = name.LastIndexOf("__"); // NOt sure if this is necessary
+ return new ArgumentException(String.Format("Wrong number of args passed to: {0}",
+ (suffix == -1 ? name : name.Substring(0, suffix)).Replace('_', '-')));
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/AFnImpl.cs b/ClojureCLR/Clojure/Clojure/Lib/AFnImpl.cs new file mode 100644 index 00000000..ca241a16 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/AFnImpl.cs @@ -0,0 +1,287 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// An implementation of AFn. Instances of this class are created by the compiler.
+ /// </summary>
+ /// <remarks>
+ /// <para>We need this at the moment as a workaround to DLR not being able to generate instance methods from lambdas.</para>
+ /// <para>Per Java Rev 1122, need to make all true functions implement the marker interface <see cref="AFunction">AFunction</see>.
+ /// In the Java version this is done by making the per-funcion generated class implement the interface.
+ /// We can do that, too, once we start generating per-function classes.
+ /// And then it goes away again in Rev 1161. Sigh.</para>
+ /// </remarks>
+ public class AFnImpl : /*AFn*/ AFunction /* per rev 1161*/ , Fn
+ {
+ public FFunc<
+ object> _fn0;
+
+ public FFunc<
+ object,
+ object> _fn1;
+
+ public FFunc<
+ object, object,
+ object> _fn2;
+
+ public FFunc<
+ object, object, object,
+ object> _fn3;
+
+ public FFunc<
+ object, object, object, object,
+ object> _fn4;
+
+ public FFunc<
+ object, object, object, object, object,
+ object> _fn5;
+
+ public FFunc<
+ object, object, object, object, object,
+ object,
+ object> _fn6;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object,
+ object> _fn7;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object,
+ object> _fn8;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object,
+ object> _fn9;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object> _fn10;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object,
+ object> _fn11;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object,
+ object> _fn12;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object,
+ object> _fn13;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object,
+ object> _fn14;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object> _fn15;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object,
+ object> _fn16;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object,
+ object> _fn17;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object,
+ object> _fn18;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object,
+ object> _fn19;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object> _fn20;
+
+ public VFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object> _fnRest;
+
+
+ public AFnImpl()
+ {
+ }
+
+ public override object invoke()
+ {
+ if (_fn0 == null) throw WrongArityException();
+ return _fn0();
+ }
+ public override object invoke(object arg1)
+ {
+ if (_fn1 == null) throw WrongArityException();
+ return _fn1(arg1);
+ }
+
+ public override object invoke(object arg1, object arg2)
+ {
+ if (_fn2 == null) throw WrongArityException();
+ return _fn2(arg1, arg2);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3)
+ {
+ if (_fn3 == null) throw WrongArityException();
+ return _fn3(arg1, arg2, arg3);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4)
+ {
+ if (_fn4 == null) throw WrongArityException();
+ return _fn4(arg1, arg2, arg3, arg4);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5)
+ {
+ if (_fn5 == null) throw WrongArityException();
+ return _fn5(arg1, arg2, arg3, arg4, arg5);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6)
+ {
+ if (_fn6 == null) throw WrongArityException();
+ return _fn6(arg1, arg2, arg3, arg4, arg5, arg6);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7)
+ {
+ if (_fn7 == null) throw WrongArityException();
+ return _fn7(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8)
+ {
+ if (_fn8 == null) throw WrongArityException();
+ return _fn8(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9)
+ {
+ if (_fn9 == null) throw WrongArityException();
+ return _fn9(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10)
+ {
+ if (_fn10 == null) throw WrongArityException();
+ return _fn10(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11)
+ {
+ if (_fn11 == null) throw WrongArityException();
+ return _fn11(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12)
+ {
+ if (_fn12 == null) throw WrongArityException();
+ return _fn12(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13)
+ {
+ if (_fn13 == null) throw WrongArityException();
+ return _fn13(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14)
+ {
+ if (_fn14 == null) throw WrongArityException();
+ return _fn14(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15)
+ {
+ if (_fn15 == null) throw WrongArityException();
+ return _fn15(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object arg16)
+ {
+ if (_fn16 == null) throw WrongArityException();
+ return _fn16(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object arg16, object arg17)
+ {
+ if (_fn17 == null) throw WrongArityException();
+ return _fn17(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object arg16, object arg17, object arg18)
+ {
+ if (_fn18 == null) throw WrongArityException();
+ return _fn18(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object arg16, object arg17, object arg18, object arg19)
+ {
+ if (_fn19 == null) throw WrongArityException();
+ return _fn19(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object arg16, object arg17, object arg18, object arg19, object arg20)
+ {
+ if (_fn20 == null) throw WrongArityException();
+ return _fn20(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object arg16, object arg17, object arg18, object arg19, object arg20, params object[] args)
+ {
+ if (_fnRest == null) throw WrongArityException();
+ return _fnRest(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, args);
+ }
+ }
+
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/AFunction.cs b/ClojureCLR/Clojure/Clojure/Lib/AFunction.cs new file mode 100644 index 00000000..68c00b4e --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/AFunction.cs @@ -0,0 +1,71 @@ +/**
+ * 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.Collections;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Base class providing IComparer implementation on top of <see cref="AFn">AFn</see>. Internal use by compiler.
+ /// </summary>
+ public abstract class AFunction : AFn, Fn, IComparer
+ {
+ #region Ctors and factory methods
+
+ /// <summary>
+ /// Initialize an <see cref="AFunction">AFunction</see> to have the given metadata.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ public AFunction(IPersistentMap meta)
+ : base(meta)
+ {
+ }
+
+ /// <summary>
+ /// Initialize an <see cref="AFunction">AFunction</see> to have the null metadata.
+ /// </summary>
+ public AFunction()
+ : base()
+ {
+ }
+
+ #endregion
+
+ #region IComparer Members
+
+ /// <summary>
+ /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.
+ /// </summary>
+ /// <param name="x">The first object to compare.</param>
+ /// <param name="y">The second object to compare.</param>
+ /// <returns>x?y:-1 if less than, 0 if equal, 1 if greater</returns>
+ /// <remarks>Uses the two-parameter invoke.
+ /// Return type can be <code>bool</code> with <value>true</value> meaning less-than, or an int.</remarks>
+ public int Compare(object x, object y)
+ {
+ Object o = invoke(x, y);
+
+ if (o is Boolean)
+ {
+ if (RT.booleanCast(o))
+ return -1;
+ return RT.booleanCast(invoke(y, x)) ? 1 : 0;
+ }
+ return Util.ConvertToInt(o);
+ }
+
+ #endregion
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/AMapEntry.cs b/ClojureCLR/Clojure/Clojure/Lib/AMapEntry.cs new file mode 100644 index 00000000..999ac95b --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/AMapEntry.cs @@ -0,0 +1,287 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+
+ // TODO: Find out why this is an APersistentVector.
+
+ /// <summary>
+ /// Provides a basic implementation of <see cref="IMapEntry">IMapEntry</see>.
+ /// </summary>
+ /// <remarks>This also provides <see cref="IPersistentVector">IPersistentVector</see> functionality.
+ /// I'm not sure why.</remarks>
+ public abstract class AMapEntry: APersistentVector, IMapEntry
+ {
+
+ #region C-tors
+
+ /// <summary>
+ /// Initialize an <see cref="AMapEntry">AMapEntry</see> with null metadata.
+ /// </summary>
+ public AMapEntry()
+ : base(null)
+ {
+ }
+
+ #endregion
+
+ #region Helpers
+
+ /// <summary>
+ /// Convert to an actual <see cref="IPersistentVector">IPersistentVector</see> with two elements.
+ /// </summary>
+ /// <returns>An <see cref="IPersistentVector">IPersistentVector</see> with two elements.</returns>
+ private IPersistentVector AsVector()
+ {
+ return LazilyPersistentVector.createOwning(key(), val());
+ }
+
+ #endregion
+
+ #region Object members
+
+ // they changed from implementing IPersistentVector to deriving from APersistentVector.
+
+ ///// <summary>
+ ///// Determines whether the specified Object is equal to the current one.
+ ///// </summary>
+ ///// <param name="obj">The Object to compare with the current one.</param>
+ ///// <returns><value>true</value> if the specified Object is equal to the current one;
+ ///// otherwise <value>false</value>.</returns>
+ ///// <remarks>Acts like a value type.</remarks>
+ //public override bool Equals(object obj)
+ //{
+ // return APersistentVector.doEquals(this, obj);
+ //}
+
+ ///// <summary>
+ ///// Computes the hash code for the object.
+ ///// </summary>
+ ///// <returns>A hash code for the current object.</returns>
+ //public override int GetHashCode()
+ //{
+ // // must match logic in APersistentVector
+ // return Util.HashCombine(Util.HashCombine(0, Util.Hash(key())), Util.Hash(val()));
+ //}
+
+ ///// <summary>
+ ///// Returns a string that represents the current object.
+ ///// </summary>
+ ///// <returns></returns>
+ //public override string ToString()
+ //{
+ // StringBuilder sb = new StringBuilder();
+ // RT.print(this, sb);
+ // return sb.ToString();
+ //}
+
+ #endregion
+
+ #region IMapEntry Members
+
+ /// <summary>
+ /// Get the key in a key/value pair.
+ /// </summary>
+ /// <returns>The key.</returns>
+ public abstract object key();
+
+ /// <summary>
+ /// Get the value in a key/value pair.
+ /// </summary>
+ /// <returns>The value.</returns>
+ public abstract object val();
+
+ #endregion
+
+ #region IPersistentVector Members
+
+ /// <summary>
+ /// Gets the number of items in the vector.
+ /// </summary>
+ /// <returns>The number of items.</returns>
+ /// <remarks>Not sure why you wouldn't use <c>count()</c> intead.</remarks>
+ public override int length()
+ {
+ return 2;
+ }
+
+ /// <summary>
+ /// Get the i-th item in the vector.
+ /// </summary>
+ /// <param name="i">The index of the item to retrieve/</param>
+ /// <returns>The i-th item</returns>
+ /// <remarks>Throws an exception if the index <c>i</c> is not in the range of the vector's elements.</remarks>
+ public override object nth(int i)
+ {
+ switch (i)
+ {
+ case 0:
+ return key();
+ case 1:
+ return val();
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+
+ /// <summary>
+ /// Return a new vector with the i-th value set to <c>val</c>.
+ /// </summary>
+ /// <param name="i">The index of the item to set.</param>
+ /// <param name="val">The new value</param>
+ /// <returns>A new (immutable) vector v with v[i] == val.</returns>
+ public override IPersistentVector assocN(int i, object val)
+ {
+ return AsVector().assocN(i, val);
+ }
+
+ /// <summary>
+ /// Creates a new vector with a new item at the end.
+ /// </summary>
+ /// <param name="o">The item to add to the vector.</param>
+ /// <returns>A new (immutable) vector with the objected added at the end.</returns>
+ public override IPersistentVector cons(object o)
+ {
+ return AsVector().cons(o);
+ }
+
+ #endregion
+
+ #region Associative Members
+
+ /// <summary>
+ /// Test if the map contains a key.
+ /// </summary>
+ /// <param name="key">The key to test for membership</param>
+ /// <returns>True if the key is in this map.</returns>
+ public override bool containsKey(object key)
+ {
+ return AsVector().containsKey(key);
+ }
+
+ /// <summary>
+ /// Returns the key/value pair for this key.
+ /// </summary>
+ /// <param name="key">The key to retrieve</param>
+ /// <returns>The key/value pair for the key, or null if the key is not in the map.</returns>
+ public override IMapEntry entryAt(object key)
+ {
+ return AsVector().entryAt(key);
+ }
+
+ /// <summary>
+ /// Add a new key/value pair.
+ /// </summary>
+ /// <param name="key">The key</param>
+ /// <param name="val">The value</param>
+ /// <returns>A new map with the key/value added.</returns>
+ public override Associative assoc(object key, object val)
+ {
+ return AsVector().assoc(key, val);
+ }
+
+ /// <summary>
+ /// Gets the value associated with a key.
+ /// </summary>
+ /// <param name="key">The key to look up.</param>
+ /// <returns>The associated value. (Throws an exception if key is not present.)</returns>
+ public override object valAt(object key)
+ {
+ return AsVector().valAt(key);
+ }
+
+ /// <summary>
+ /// Gets the value associated with a key.
+ /// </summary>
+ /// <param name="key">The key to look up.</param>
+ /// <param name="notFound">The value to return if the key is not present.</param>
+ /// <returns>The associated value (or <c>notFound</c> if the key is not present.</returns>
+ public override object valAt(object key, object notFound)
+ {
+ return AsVector().valAt(key,notFound);
+ }
+
+ #endregion
+
+ #region Reversible members
+
+ //public ISeq rseq()
+ //{
+ // return asVector().rseq();
+ //}
+
+ #endregion
+
+ #region IPersistentCollection Members
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ public override int count()
+ {
+ return 2;
+ }
+
+ /// <summary>
+ /// Gets an ISeq to allow first/rest iteration through the collection.
+ /// </summary>
+ /// <returns>An ISeq for iteration.</returns>
+ public override ISeq seq()
+ {
+ return AsVector().seq();
+ }
+
+ /// <summary>
+ /// Returns a new collection that has the given element cons'd on front of the eixsting collection.
+ /// </summary>
+ /// <param name="o">An item to put at the front of the collection.</param>
+ /// <returns>A new immutable collection with the item added.</returns>
+ public override IPersistentCollection empty()
+ {
+ return null;
+ }
+
+ //IPersistentCollection IPersistentCollection.cons(object o)
+ //{
+ // return cons(o);
+ //}
+
+ #endregion
+
+ #region IPersistentStack Members
+
+ /// <summary>
+ /// Peek at the top (first) element in the stack.
+ /// </summary>
+ /// <returns>The top (first) element.</returns>
+ public override object peek()
+ {
+ return val();
+ }
+
+ /// <summary>
+ /// Returns a new stack with the top element popped.
+ /// </summary>
+ /// <returns>The new stack</returns>
+ public override IPersistentStack pop()
+ {
+ return LazilyPersistentVector.createOwning(key());
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/APersistentMap.cs b/ClojureCLR/Clojure/Clojure/Lib/APersistentMap.cs new file mode 100644 index 00000000..d833a39c --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/APersistentMap.cs @@ -0,0 +1,488 @@ +/**
+ * 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.Reflection;
+
+using System.Collections;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Provides a basic impelmentation of <see cref="IPersistentMap">IPersistentMap</see> functionality.
+ /// </summary>
+ public abstract class APersistentMap: AFn, IPersistentMap, IDictionary, IEnumerable<IMapEntry>
+ {
+ #region Data
+
+ /// <summary>
+ /// Caches the hash code, when computed.
+ /// </summary>
+ /// <remarks>The value <value>-1</value> indicates that the hash code has not been computed yet.</remarks>
+ int _hash = -1;
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ /// <summary>
+ /// Initializes a <see cref="APersistnetMap">APersistantMap</see> to have the given metadata.
+ /// </summary>
+ /// <param name="meta">The metatdata to attach.</param>
+ protected APersistentMap(IPersistentMap meta)
+ : base(meta)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a <see cref="APersistnetMap">APersistantMap</see> to have null metadata.
+ /// </summary>
+ protected APersistentMap()
+ {
+ }
+
+ #endregion
+
+ #region object overrides
+
+ /// <summary>
+ /// Returns a string that represents the current object.
+ /// </summary>
+ /// <returns>A string that represents the current object.</returns>
+ public override string ToString()
+ {
+ return RT.printString(this);
+ }
+
+ /// <summary>
+ /// Determines whether the specified Object is equal to the current Object.
+ /// </summary>
+ /// <param name="obj">The Object to compare with the current Object. </param>
+ /// <returns>true if the specified Object is equal to the current Object;
+ /// otherwise, false.</returns>
+ public override bool Equals(object obj)
+ {
+ //if(!(obj instanceof Map))
+ // return false;
+ //Map m = (Map) obj;
+
+ IDictionary d = obj as IDictionary;
+ if (d == null)
+ return false;
+
+ // Java had the following.
+ // This works on other APersistentMap implementations, but not on
+ // arbitrary dictionaries.
+ if (d.Count != this.Count || d.GetHashCode() != this.GetHashCode())
+ return false;
+
+ //if (d.Count != this.Count)
+ // return false;
+
+ for (ISeq s = seq(); s != null; s = s.rest())
+ {
+ IMapEntry me = (IMapEntry)s.first();
+ bool found = d.Contains(me.key());
+ if (!found || !Util.equals(me.val(), d[me.key()]))
+ return false;
+ }
+ return true;
+ }
+
+
+ /// <summary>
+ /// Gets a hash code for the current object.
+ /// </summary>
+ /// <returns>A hash code for the current object.</returns>
+ /// <remarks>Valud-based = relies on all entries. Once computed, it is cached.</remarks>
+ public override int GetHashCode()
+ {
+ if (_hash == -1)
+ {
+ int hash = 0;
+ for (ISeq s = seq(); s != null; s = s.rest())
+ {
+ IMapEntry me = (IMapEntry)s.first();
+ hash += (me.key() == null ? 0 : me.key().GetHashCode())
+ ^ (me.val() == null ? 0 : me.val().GetHashCode());
+ }
+ _hash = hash;
+ }
+ return _hash;
+ }
+
+
+ #endregion
+
+ #region Associative methods
+
+ abstract public bool containsKey(object key);
+ abstract public IMapEntry entryAt(object key);
+ Associative Associative.assoc(object key, object val)
+ {
+ return assoc(key, val);
+ }
+ abstract public object valAt(object key);
+ abstract public object valAt(object key, object notFound);
+
+ #endregion
+
+ #region IPersistentCollection Members
+
+ /// <summary>
+ /// Returns a new collection that has the given element cons'd on front of the eixsting collection.
+ /// </summary>
+ /// <param name="o">An item to put at the front of the collection.</param>
+ /// <returns>A new immutable collection with the item added.</returns>
+ IPersistentCollection IPersistentCollection.cons(object o)
+ {
+ return cons(o);
+ }
+
+ abstract public int count();
+ abstract public ISeq seq();
+ abstract public IPersistentCollection empty();
+
+ /// <summary>
+ /// Determine if an object is equivalent to this (handles all collections).
+ /// </summary>
+ /// <param name="o">The object to compare.</param>
+ /// <returns><c>true</c> if the object is equivalent; <c>false</c> otherwise.</returns>
+ /// <remarks>
+ /// In Java Rev 1215, Added equiv. Same as the definition in Equals, as in they took out the hashcode comparison.
+ /// Different, as in Util.Equal above became Util.equals. and below it is Util.equiv.
+ /// </remarks>
+ public bool equiv(object obj)
+ {
+ //if(!(obj instanceof Map))
+ // return false;
+ //Map m = (Map) obj;
+
+ IDictionary d = obj as IDictionary;
+ if (d == null)
+ return false;
+
+ // Java had the following.
+ // This works on other APersistentMap implementations, but not on
+ // arbitrary dictionaries.
+ //if (d.Count != this.Count || d.GetHashCode() != this.GetHashCode())
+ // return false;
+
+ if (d.Count != this.Count)
+ return false;
+
+ for (ISeq s = seq(); s != null; s = s.rest())
+ {
+ IMapEntry me = (IMapEntry)s.first();
+ bool found = d.Contains(me.key());
+ if (!found || !Util.equiv(me.val(), d[me.key()]))
+ return false;
+ }
+ return true;
+ }
+
+
+
+ #endregion
+
+ #region IObj members
+
+ abstract public override IObj withMeta(IPersistentMap meta);
+
+ #endregion
+
+ #region IPersistentMap members
+
+ abstract public IPersistentMap assoc(object key, object val);
+ abstract public IPersistentMap assocEx(object key, object val);
+ abstract public IPersistentMap without(object key);
+
+ /// <summary>
+ /// Add a new key/value pair.
+ /// </summary>
+ /// <param name="o">The key/value pair to add.</param>
+ /// <returns>A new map with key+value pair added.</returns>
+ public IPersistentMap cons(object o)
+ {
+ IMapEntry e = o as IMapEntry;
+ if (e != null)
+ return assoc(e.key(), e.val());
+
+ if (o is DictionaryEntry)
+ {
+ DictionaryEntry de = (DictionaryEntry)o;
+ return assoc(de.Key, de.Value);
+ }
+ Type t = o.GetType();
+ if (t.IsGenericType && t.Name == "KeyValuePair`2")
+ {
+ object key = t.InvokeMember("Key", BindingFlags.GetProperty, null, o, null);
+ object val = t.InvokeMember("Value", BindingFlags.GetProperty, null, o, null);
+ return assoc(key, val);
+ }
+
+ IPersistentVector v = o as IPersistentVector;
+ if (v != null)
+ {
+ if (v.count() != 2)
+ throw new ArgumentException("Vector arg to map cons must be a pair");
+ return assoc(v.nth(0), v.nth(1));
+ }
+
+ IPersistentMap ret = this;
+ for (ISeq s = RT.seq(o); s != null; s = s.rest())
+ {
+ IMapEntry me = (IMapEntry)s.first();
+ ret = ret.assoc(me.key(), me.val());
+ }
+ return ret;
+ }
+
+ #endregion
+
+ #region IFn members
+
+ public override object invoke(object arg1)
+ {
+ return valAt(arg1);
+ }
+
+ public override object invoke(object arg1, object arg2)
+ {
+ return valAt(arg1, arg2);
+ }
+
+ #endregion
+
+ #region IDictionary Members
+
+ public void Add(object key, object value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Clear()
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool Contains(object key)
+ {
+ return this.containsKey(key);
+ }
+
+ public virtual IDictionaryEnumerator GetEnumerator()
+ {
+ return new MapEnumerator(this);
+ }
+
+ public bool IsFixedSize
+ {
+ get { return true; }
+ }
+
+ public bool IsReadOnly
+ {
+ get { return true; }
+ }
+
+ public ICollection Keys
+ {
+ get { return KeySeq.create(seq()); }
+ }
+
+ public void Remove(object key)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ICollection Values
+ {
+ get { return ValSeq.create(seq()); }
+ }
+
+ public object this[object key]
+ {
+ get
+ {
+ return valAt(key);
+ }
+ set
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ #endregion
+
+ #region ICollection Members
+
+ public void CopyTo(Array array, int index)
+ {
+ ((ICollection)seq()).CopyTo(array, index);
+ }
+
+ public int Count
+ {
+ get { return count(); }
+ }
+
+ public bool IsSynchronized
+ {
+ get { return true; }
+ }
+
+ public object SyncRoot
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Implements a sequence across the keys of map.
+ /// </summary>
+ public sealed class KeySeq : ASeq
+ {
+ #region Data
+
+ ISeq _seq;
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ static public KeySeq create(ISeq seq)
+ {
+ if (seq == null)
+ return null;
+ return new KeySeq(seq);
+ }
+
+ private KeySeq(ISeq seq)
+ {
+ _seq = seq;
+ }
+
+ private KeySeq(IPersistentMap meta, ISeq seq)
+ : base(meta)
+ {
+ _seq = seq;
+ }
+
+ #endregion
+
+ #region ISeq members
+
+ public override object first()
+ {
+ return ((IMapEntry)_seq.first()).key();
+ }
+
+ public override ISeq rest()
+ {
+ return create(_seq.rest());
+ }
+
+ #endregion
+
+ #region IObj methods
+
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return new KeySeq(meta, _seq);
+ }
+
+ #endregion
+ }
+
+ /// <summary>
+ /// Implements a sequence across the values of a map.
+ /// </summary>
+ public sealed class ValSeq : ASeq
+ {
+ #region Data
+
+ ISeq _seq;
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ static public ValSeq create(ISeq seq)
+ {
+ if (seq == null)
+ return null;
+ return new ValSeq(seq);
+ }
+
+ private ValSeq(ISeq seq)
+ {
+ _seq = seq;
+ }
+
+ private ValSeq(IPersistentMap meta, ISeq seq)
+ : base(meta)
+ {
+ _seq = seq;
+ }
+
+ #endregion
+
+ #region ISeq members
+
+ public override object first()
+ {
+ return ((IMapEntry)_seq.first()).val();
+ }
+
+ public override ISeq rest()
+ {
+ return create(_seq.rest());
+ }
+
+ #endregion
+
+ #region IObj methods
+
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return new ValSeq(meta, _seq);
+ }
+
+ #endregion
+ }
+
+
+ #region IEnumerable<IMapEntry> Members
+
+ IEnumerator<IMapEntry> IEnumerable<IMapEntry>.GetEnumerator()
+ {
+ for (ISeq s = seq(); s != null; s = s.rest())
+ yield return (IMapEntry)s.first();
+ }
+
+ #endregion
+ }
+
+
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/APersistentSet.cs b/ClojureCLR/Clojure/Clojure/Lib/APersistentSet.cs new file mode 100644 index 00000000..95b41408 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/APersistentSet.cs @@ -0,0 +1,221 @@ +/**
+ * 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.Collections;
+
+namespace clojure.lang
+{
+ public abstract class APersistentSet: AFn, IPersistentSet, ICollection // , Set -- no equivalent
+ {
+ #region Data
+
+ /// <summary>
+ /// Caches the hash code, when computed.
+ /// </summary>
+ protected int _hash = -1;
+
+ /// <summary>
+ /// The underlying map that contains the set's elements.
+ /// </summary>
+ protected readonly IPersistentMap _impl;
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ /// <summary>
+ /// Initialize an <cref see="APersistentSet">APersistentSet</cref> from the metadata map and the data map.
+ /// </summary>
+ /// <param name="meta">The metadata</param>
+ /// <param name="impl">The underlying implementation map</param>
+ protected APersistentSet(IPersistentMap meta, IPersistentMap impl)
+ : base(meta)
+ {
+ _impl = impl;
+ }
+
+ #endregion
+
+ #region Object overrides
+
+ /// <summary>
+ /// Returns a string representing the current object.
+ /// </summary>
+ /// <returns>A string representing the current object.</returns>
+ public override string ToString()
+ {
+ return RT.printString(this);
+ }
+
+ /// <summary>
+ /// Computes a hash code for the current object.
+ /// </summary>
+ /// <returns>A hash code for the current object.</returns>
+ /// <remarks>The hash code is value-based (based on the items in the set).
+ /// Once computed, the value is cached.</remarks>
+ public override int GetHashCode()
+ {
+ if (_hash == -1)
+ {
+ int hash = 0;
+ for (ISeq s = seq(); s != null; s = s.rest())
+ {
+ object e = s.first();
+ hash += Util.Hash(e);
+ }
+ _hash = hash;
+ }
+ return _hash;
+ }
+
+ /// <summary>
+ /// Determines whether the specified Object is equal to the current Object.
+ /// </summary>
+ /// <param name="obj">The Object to compare with the current Object.</param>
+ /// <returns><value>true</value> if the specified Object is equal to the current Object;
+ /// otherwise, <value>false</value>.
+ /// </returns>
+ public override bool Equals(object obj)
+ {
+ // I really can't do what the Java version does.
+ // It casts to a Set. No such thing here. We'll use IPersistentSet instead.
+
+ IPersistentSet s = obj as IPersistentSet;
+ if (s == null)
+ return false;
+
+ if (s.count() != count() || s.GetHashCode() != GetHashCode())
+ return false;
+
+ for (ISeq seq = s.seq(); seq != null; seq = seq.rest())
+ if (!contains(seq.first()))
+ return false;
+
+ return true;
+ }
+
+ #endregion
+
+ #region IPersistentSet Members
+
+ /// <summary>
+ /// Get a set with the given item removed.
+ /// </summary>
+ /// <param name="key">The item to remove.</param>
+ /// <returns>A new set with the item removed.</returns>
+ public abstract IPersistentSet disjoin(object key);
+
+ /// <summary>
+ /// Test if the set contains the key.
+ /// </summary>
+ /// <param name="key">The value to test for membership in the set.</param>
+ /// <returns>True if the item is in the collection; false, otherwise.</returns>
+ public bool contains(object key)
+ {
+ return _impl.containsKey(key);
+ }
+
+ /// <summary>
+ /// Get the value for the key (= the key itself, or null if not present).
+ /// </summary>
+ /// <param name="key">The value to test for membership in the set.</param>
+ /// <returns>the key if the key is in the set, else null.</returns>
+ public object get(object key)
+ {
+ return _impl.valAt(key);
+ }
+
+ #endregion
+
+ #region IPersistentCollection Members
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ public int count()
+ {
+ return _impl.count();
+ }
+
+
+ /// <summary>
+ /// Gets an ISeq to allow first/rest iteration through the collection.
+ /// </summary>
+ /// <returns>An ISeq for iteration.</returns>
+ public ISeq seq()
+ {
+ return APersistentMap.KeySeq.create(_impl.seq());
+ }
+
+ public abstract IPersistentCollection cons(object o);
+ public abstract IPersistentCollection empty();
+
+ /// <summary>
+ /// Determine if an object is equivalent to this (handles all collections).
+ /// </summary>
+ /// <param name="o">The object to compare.</param>
+ /// <returns><c>true</c> if the object is equivalent; <c>false</c> otherwise.</returns>
+ public bool equiv(object o)
+ {
+ return Equals(o);
+ }
+
+ #endregion
+
+ #region IFn members
+
+ public override object invoke(object arg1)
+ {
+ return get(arg1);
+ }
+
+
+ #endregion
+
+ #region ICollection Members
+
+ public void CopyTo(Array array, int index)
+ {
+ ((ICollection)seq()).CopyTo(array, index);
+ }
+
+ public int Count
+ {
+ get { return count(); }
+ }
+
+ public bool IsSynchronized
+ {
+ get { return true; }
+ }
+
+ public object SyncRoot
+ {
+ get { throw new InvalidOperationException(); }
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ public IEnumerator GetEnumerator()
+ {
+ return new SeqEnumerator(seq());
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/APersistentVector.cs b/ClojureCLR/Clojure/Clojure/Lib/APersistentVector.cs new file mode 100644 index 00000000..476fdd4d --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/APersistentVector.cs @@ -0,0 +1,1004 @@ +/**
+ * 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.Collections;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Provides a basic implementation of <see cref="IPersistentVector">IPersistentVector</see> functionality.
+ /// </summary>
+ public abstract class APersistentVector: AFn, IPersistentVector, Streamable, IList, IComparable
+ {
+ #region Data
+
+ /// <summary>
+ /// Caches the hash code, once computed.
+ /// </summary>
+ int _hash = -1;
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ /// <summary>
+ /// Initializes an <see cref="APersistentVector">APersistentVector</see> with the given metadata.
+ /// </summary>
+ /// <param name="meta">The metadata to attach</param>
+ public APersistentVector(IPersistentMap meta)
+ : base(meta)
+ {
+ }
+
+ #endregion
+
+ #region Object overrides
+
+ /// <summary>
+ /// Returns a string representing the object.
+ /// </summary>
+ /// <returns>A string representing the object.</returns>
+ public override string ToString()
+ {
+ return RT.printString(this);
+ }
+
+ /// <summary>
+ /// Determines whether the specified Object is equal to the current Object.
+ /// </summary>
+ /// <param name="obj">The Object to compare with the current Object.</param>
+ /// <returns><value>true</value> if the specified Object is equal to the current Object;
+ /// otherwise, <value>false</value>.
+ /// </returns>
+ public override bool Equals(object obj)
+ {
+ return doEquals(this, obj);
+ }
+
+ /// <summary>
+ /// Compares an <see cref="IPersistentVector">IPersistentVector</see> to another object for equality.
+ /// </summary>
+ /// <param name="v">The <see cref="IPersistentVector">IPersistentVector</see> to compare.</param>
+ /// <param name="obj">The other object to compare.</param>
+ /// <returns><value>true</value> if the specified Object is equal to the current Object;
+ /// otherwise, <value>false</value>.
+ /// </returns>
+ static public bool doEquals(IPersistentVector v, object obj)
+ {
+
+ if ( obj is IList || obj is IPersistentVector )
+ {
+ IList ma = obj as IList;
+
+ if (ma.Count != v.count() || ma.GetHashCode() != v.GetHashCode())
+ return false;
+
+ for ( int i=0; i<v.count(); i++ )
+ {
+ if (!Util.equals(v.nth(i), ma[i]))
+ return false;
+ }
+ return true;
+ }
+
+ // Example in original code of Sequential/IPersistentVector conflation.
+
+ //if(!(obj instanceof Sequential))
+ // return false;
+ // ISeq ms = ((IPersistentCollection) obj).seq();
+ // for(int i = 0; i < v.count(); i++, ms = ms.rest())
+ // {
+ // if(ms == null || !Util.equals(v.nth(i), ms.first()))
+ // return false;
+ // }
+ // if(ms != null)
+ // return false;
+ // }
+
+ //return true;
+
+ ISeq ms = obj as ISeq;
+ if (ms == null)
+ {
+ IPersistentCollection mc = obj as IPersistentCollection;
+ if (mc == null)
+ return false;
+ ms = mc.seq();
+ }
+
+ // Once we have the ISeq, we're ready to go.
+
+ for (int i = 0; i < v.count(); i++, ms = ms.rest())
+ {
+ if (ms == null || !Util.equals(v.nth(i), ms.first()))
+ return false;
+ }
+ if (ms != null)
+ return false;
+
+ return true;
+ }
+
+
+
+ /// <summary>
+ /// Compute a hash code for the current object.
+ /// </summary>
+ /// <returns>A hash code for the current object.</returns>
+ public override int GetHashCode()
+ {
+ if (_hash == -1)
+ {
+ int hash = 0;
+ for (int i = 0; i < count(); i++)
+ hash = Util.HashCombine(hash, Util.Hash(nth(i)));
+ this._hash = hash;
+ }
+ return _hash;
+ }
+
+
+ #endregion
+
+ #region IFn members
+
+ public override object invoke(object arg1)
+ {
+ return nth(Util.ConvertToInt(arg1));
+ }
+
+ #endregion
+
+ #region IPersistentCollection Members
+
+ public abstract int count();
+ public abstract IPersistentCollection empty();
+
+ /// <summary>
+ /// Gets an ISeq to allow first/rest iteration through the collection.
+ /// </summary>
+ /// <returns>An ISeq for iteration.</returns>
+ public virtual ISeq seq()
+ {
+ return count() > 0
+ ? new Seq(this, 0)
+ : null;
+ }
+
+ /// <summary>
+ /// Returns a new collection that has the given element cons'd on front of the eixsting collection.
+ /// </summary>
+ /// <param name="o">An item to put at the front of the collection.</param>
+ /// <returns>A new immutable collection with the item added.</returns>
+ IPersistentCollection IPersistentCollection.cons(object o)
+ {
+ return cons(o);
+ }
+
+
+ /// <summary>
+ /// Determine if an object is equivalent to this (handles all collections).
+ /// </summary>
+ /// <param name="o">The object to compare.</param>
+ /// <returns><c>true</c> if the object is equivalent; <c>false</c> otherwise.</returns>
+ public bool equiv(object obj)
+ {
+ return doEquiv(this, obj);
+ }
+
+
+
+ static bool doEquiv(IPersistentVector v, object obj)
+ {
+ if (obj is IList || obj is IPersistentVector)
+ {
+ ICollection ma = (ICollection)obj;
+ if (ma.Count != v.count())
+ return false;
+ IEnumerator ima = ma.GetEnumerator();
+ foreach (object ov in ((IList)v))
+ {
+ ima.MoveNext();
+ if (!Util.equiv(ov, ima.Current))
+ return false;
+ }
+ return true;
+ }
+ else
+ {
+ // Example in Java of Sequential / IPersistentCollection conflation
+ //if (!(obj is Sequential))
+ // return false;
+ //ISeq ms = ((IPersistentCollection)obj).seq();
+
+ ISeq ms = obj as ISeq;
+ if (ms == null)
+ {
+ IPersistentCollection mc = obj as IPersistentCollection;
+ if (mc == null)
+ return false;
+ ms = mc.seq();
+ }
+
+ // Once we have the ISeq, we're ready to go.
+
+ for (int i = 0; i < v.count(); i++, ms = ms.rest())
+ {
+ if (ms == null || !Util.equiv(v.nth(i), ms.first()))
+ return false;
+ }
+ if (ms != null)
+ return false;
+ }
+
+ return true;
+
+ }
+
+
+ #endregion
+
+ #region Reversible members
+
+ /// <summary>
+ /// Gets an <see cref="ISeq">ISeq</see> to travers the sequence in reverse.
+ /// </summary>
+ /// <returns>An <see cref="ISeq">ISeq</see> .</returns>
+ public ISeq rseq()
+ {
+ return count() > 0
+ ? new RSeq(this, count() - 1)
+ : null;
+ }
+
+ #endregion
+
+ #region IPersistentVector Members
+
+ abstract public int length();
+ abstract public object nth(int i);
+ abstract public IPersistentVector assocN(int i, object val);
+ abstract public IPersistentVector cons(object o);
+
+
+ #endregion
+
+ #region Associative Members
+
+ /// <summary>
+ /// Test if the map contains a key.
+ /// </summary>
+ /// <param name="key">The key to test for membership</param>
+ /// <returns>True if the key is in this map.</returns>
+ public virtual bool containsKey(object key)
+ {
+ if (!Util.IsNumeric(key))
+ return false;
+ int i = Util.ConvertToInt(key);
+ return i >= 0 && i < count();
+ }
+
+ /// <summary>
+ /// Returns the key/value pair for this key.
+ /// </summary>
+ /// <param name="key">The key to retrieve</param>
+ /// <returns>The key/value pair for the key, or null if the key is not in the map.</returns>
+ public virtual IMapEntry entryAt(object key)
+ {
+ if (Util.IsNumeric(key))
+ {
+ int i = Util.ConvertToInt(key);
+ if (i >= 0 && i < count())
+ return new MapEntry(key, nth(i));
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// Add a new key/value pair.
+ /// </summary>
+ /// <param name="key">The key</param>
+ /// <param name="val">The value</param>
+ /// <returns>A new map with the key/value added.</returns>
+ public virtual Associative assoc(object key, object val)
+ {
+ if (Util.IsNumeric(key))
+ {
+ int i = Util.ConvertToInt(key);
+ return assocN(i, val);
+ }
+ throw new ArgumentException("Key must be an integer");
+ }
+
+ /// <summary>
+ /// Gets the value associated with a key.
+ /// </summary>
+ /// <param name="key">The key to look up.</param>
+ /// <returns>The associated value. (Throws an exception if key is not present.)</returns>
+ public virtual object valAt(object key)
+ {
+ return valAt(key, null);
+ }
+
+ /// <summary>
+ /// Gets the value associated with a key.
+ /// </summary>
+ /// <param name="key">The key to look up.</param>
+ /// <param name="notFound">The value to return if the key is not present.</param>
+ /// <returns>The associated value (or <c>notFound</c> if the key is not present.</returns>
+ public virtual object valAt(object key, object notFound)
+ {
+ if (Util.IsNumeric(key))
+ {
+ int i = Util.ConvertToInt(key);
+ if (i >= 0 && i < count())
+ return nth(i);
+ }
+ return notFound;
+ }
+
+ #endregion
+
+ #region IPersistentStack Members
+
+ /// <summary>
+ /// Peek at the top (first) element in the stack.
+ /// </summary>
+ /// <returns>The top (first) element.</returns>
+ public virtual object peek()
+ {
+ return (count() > 0)
+ ? nth(count() - 1)
+ : null;
+ }
+
+ public abstract IPersistentStack pop();
+
+
+ #endregion
+
+ #region Streamable Members
+
+ /// <summary>
+ /// Internal class to implement <see cref="IStream">IStream</see> capabilities
+ /// for <see cref="APersistentVector">APersistentVector</see>.
+ /// </summary>
+ private class IPVStream : IStream
+ {
+ private readonly IPersistentVector _ipv;
+ private readonly AtomicInteger _ai;
+
+ public IPVStream(IPersistentVector ipv)
+ {
+ _ipv = ipv;
+ _ai = new AtomicInteger(0);
+ }
+
+ public object next()
+ {
+ int i = (int)_ai.getAndIncrement();
+ if (i < _ipv.count())
+ return _ipv.nth(i);
+ return RT.eos();
+ }
+
+ }
+
+ public IStream stream()
+ {
+ return new IPVStream(this);
+ }
+
+ #endregion
+
+ #region IList Members
+
+ public int Add(object value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Clear()
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool Contains(object value)
+ {
+ for (ISeq s = seq(); s != null; s = s.rest())
+ if (Util.equals(s.first(), value))
+ return true;
+ return false;
+ }
+
+ public int IndexOf(object value)
+ {
+ for (int i = 0; i < count(); i++)
+ if (Util.equals(nth(i), value))
+ return i;
+ return -1;
+ }
+
+ public void Insert(int index, object value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool IsFixedSize
+ {
+ get { return true; }
+ }
+
+ public bool IsReadOnly
+ {
+ get { return true; }
+ }
+
+ public void Remove(object value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void RemoveAt(int index)
+ {
+ throw new NotImplementedException();
+ }
+
+ public object this[int index]
+ {
+ get
+ {
+ return nth(index);
+ }
+ set
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ #endregion
+
+ #region ICollection Members
+
+ public void CopyTo(Array array, int index)
+ {
+ if ( array == null )
+ throw new ArgumentNullException();
+
+ if ( index < 0 )
+ throw new ArgumentOutOfRangeException();
+
+ if ( array.Rank > 1 )
+ throw new ArgumentException("array must be 1-dimensional");
+
+ if (index >= array.Length || array.Length - index < count())
+ throw new ArgumentException();
+
+ for (int i = 0; i < count(); i++)
+ array.SetValue(nth(i), i);
+ }
+
+ public int Count
+ {
+ get { return count(); }
+ }
+
+ public bool IsSynchronized
+ {
+ get { return true; }
+ }
+
+ public object SyncRoot
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ public IEnumerator GetEnumerator()
+ {
+ for (ISeq s = seq(); s != null; s = s.rest())
+ yield return s.first();
+ }
+
+
+ #endregion
+
+ #region IComparable Members
+
+ public int CompareTo(object obj)
+ {
+ IPersistentVector v = obj as IPersistentVector;
+ if (v == null)
+ return 1;
+
+ if (count() < v.count())
+ return -1;
+ else if (count() > v.count())
+ return 1;
+ for (int i = 0; i < count(); i++)
+ {
+ int c = Util.compare(nth(i), v.nth(i));
+ if (c != 0)
+ return c;
+ }
+ return 0;
+ }
+
+ #endregion
+
+
+ // TODO: Factor out common code from Seq/RSeq
+
+ /// <summary>
+ /// Internal class providing <see cref="ISeq">ISeq</see> functionality for <see cref="APersistentVector">APersistentVector</see>.
+ /// </summary>
+ /// <remarks>This class should be private. Public only for DLR debugging output.</remarks>
+ public sealed class Seq : ASeq, IndexedSeq, IReduce, Counted // Counted left out of Java version
+ {
+ // TODO: something more efficient (todo = from Java)
+
+ #region Data
+
+ /// <summary>
+ /// The <see cref="IPersistentVector">IPersistentVector</see> this sequence is iterating over.
+ /// </summary>
+ readonly IPersistentVector _v;
+
+ /// <summary>
+ /// The current index into the vector.
+ /// </summary>
+ readonly int _i;
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ /// <summary>
+ /// Initialize a sequence over a vector with the first element at a given index.
+ /// </summary>
+ /// <param name="v">The vector to sequence over.</param>
+ /// <param name="i">The index to start at.</param>
+ public Seq(IPersistentVector v, int i)
+ {
+ this._v = v;
+ this._i = i;
+ }
+
+ /// <summary>
+ /// Initialize a sequence over a vector with the first element at a given index, with the given metadata.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ /// <param name="v">The vector to sequence over.</param>
+ /// <param name="i">The index to start at.</param>
+ Seq(IPersistentMap meta, IPersistentVector v, int i)
+ : base(meta)
+ {
+ this._v = v;
+ this._i = i;
+ }
+
+ #endregion
+
+ #region ISeq members
+
+ /// <summary>
+ /// Gets the first item.
+ /// </summary>
+ /// <returns>The first item.</returns>
+ public override object first()
+ {
+ return _v.nth(_i);
+ }
+
+ /// <summary>
+ /// Gets the rest of the sequence.
+ /// </summary>
+ /// <returns>The rest of the sequence, or <c>null</c> if no more elements.</returns>
+ public override ISeq rest()
+ {
+ return _i + 1 < _v.count()
+ ? new Seq(_v, _i+1)
+ : null;
+ }
+
+ #endregion
+
+ #region IndexSeq members
+
+ /// <summary>
+ /// Gets the index associated with this sequence.
+ /// </summary>
+ /// <returns>The index associated with this sequence.</returns>
+ public int index()
+ {
+ return _i;
+ }
+
+ #endregion
+
+ #region IPersistentCollection members
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ public override int count()
+ {
+ return _v.count() - _i;
+ }
+
+ #endregion
+
+ #region IObj members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return new Seq(meta, _v, _i);
+ }
+
+ #endregion
+
+ #region IReduce members
+
+ /// <summary>
+ /// Reduce the collection using a function.
+ /// </summary>
+ /// <param name="f">The function to apply.</param>
+ /// <returns>The reduced value</returns>
+ public object reduce(IFn f)
+ {
+ object ret = _v.nth(_i);
+ for (int x = _i + 1; x < _v.count(); x++)
+ ret = f.invoke(ret, _v.nth(x));
+ return ret;
+ }
+
+ /// <summary>
+ /// Reduce the collection using a function.
+ /// </summary>
+ /// <param name="f">The function to apply.</param>
+ /// <param name="start">An initial value to get started.</param>
+ /// <returns>The reduced value</returns>
+ public object reduce(IFn f, object start)
+ {
+ object ret = f.invoke(start, _v.nth(_i));
+ for (int x = _i + 1; x < _v.count(); x++)
+ ret = f.invoke(ret, _v.nth(x));
+ return ret;
+ }
+
+ #endregion
+
+ } // nested class Seq
+
+ /// <summary>
+ /// Internal class providing reverse <see cref="ISeq">ISeq</see> functionality for <see cref="APersistentVector">APersistentVector</see>.
+ /// </summary>
+ /// <remarks>This class should be private. Public only for DLR debugging output.</remarks>
+ public sealed class RSeq : ASeq, IndexedSeq, IReduce, Counted // IReduce left out of Java version
+ {
+
+ #region Data
+
+ /// <summary>
+ /// The <see cref="IPersistentVector">IPersistentVector</see> this sequence is iterating over.
+ /// </summary>
+ readonly IPersistentVector _v;
+
+ /// <summary>
+ /// The current index into the vector.
+ /// </summary>
+ readonly int _i;
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ /// <summary>
+ /// Initialize a reverse sequence over a vector with the first element at a given index.
+ /// </summary>
+ /// <param name="v">The vector to sequence over.</param>
+ /// <param name="i">The index to start at.</param>
+ public RSeq(IPersistentVector v, int i)
+ {
+ this._v = v;
+ this._i = i;
+ }
+
+ /// <summary>
+ /// Initialize a reverse sequence over a vector with the first element at a given index, with the given metadata.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ /// <param name="v">The vector to sequence over.</param>
+ /// <param name="i">The index to start at.</param>
+ RSeq(IPersistentMap meta, IPersistentVector v, int i)
+ : base(meta)
+ {
+ this._v = v;
+ this._i = i;
+ }
+
+ #endregion
+
+ #region ISeq members
+
+ /// <summary>
+ /// Gets the first item.
+ /// </summary>
+ /// <returns>The first item.</returns>
+ public override object first()
+ {
+ return _v.nth(_i);
+ }
+
+ /// <summary>
+ /// Gets the rest of the sequence.
+ /// </summary>
+ /// <returns>The rest of the sequence, or <c>null</c> if no more elements.</returns>
+ public override ISeq rest()
+ {
+ return _i > 0
+ ? new RSeq(_v, _i-1)
+ : null;
+ }
+
+ #endregion
+
+ #region IndexSeq members
+
+ /// <summary>
+ /// Gets the index associated with this sequence.
+ /// </summary>
+ /// <returns>The index associated with this sequence.</returns>
+ public int index()
+ {
+ // Java original has the following. I believe this is incorrect.
+ //return _i;
+ return _v.count() - _i - 1;
+ }
+
+ #endregion
+
+ #region IPersistentCollection members
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ public override int count()
+ {
+ return _i+1;
+ }
+
+ #endregion
+
+ #region IObj members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return new RSeq(meta, _v, _i);
+ }
+
+ #endregion
+
+ #region IReduce members
+
+ // Not in Java original
+
+ /// <summary>
+ /// Reduce the collection using a function.
+ /// </summary>
+ /// <param name="f">The function to apply.</param>
+ /// <returns>The reduced value</returns>
+ public object reduce(IFn f)
+ {
+ object ret = _v.nth(_i);
+ for (int x = _i-1; x >= 0; x--)
+ ret = f.invoke(ret, _v.nth(x));
+ return ret;
+ }
+
+ /// <summary>
+ /// Reduce the collection using a function.
+ /// </summary>
+ /// <param name="f">The function to apply.</param>
+ /// <param name="start">An initial value to get started.</param>
+ /// <returns>The reduced value</returns>
+ public object reduce(IFn f, object start)
+ {
+ object ret = start;
+ for (int x = _i; x >= 0; x--)
+ ret = f.invoke(ret, _v.nth(x));
+ return ret;
+ }
+
+ #endregion
+
+ } // nested class RSeq
+
+ /// <summary>
+ /// Internal class providing subvector functionality for <see cref="APersistentVector">APersistentVector</see>.
+ /// </summary>
+ public sealed class SubVector : APersistentVector, IPersistentCollection
+ {
+ #region Data
+
+ /// <summary>
+ /// The vector being subvectored.
+ /// </summary>
+ readonly IPersistentVector _v;
+
+ /// <summary>
+ /// The start index of the subvector.
+ /// </summary>
+ readonly int _start;
+
+ /// <summary>
+ /// The end index of the subvector.
+ /// </summary>
+ readonly int _end;
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ /// <summary>
+ /// Initialize a subvector, with the given metadata and start/end indices.
+ /// </summary>
+ /// <param name="meta">The metatdata to attach.</param>
+ /// <param name="v">The vector to subvector.</param>
+ /// <param name="start">The start index of the subvector.</param>
+ /// <param name="end">The end index of the subvector.</param>
+ public SubVector(IPersistentMap meta, IPersistentVector v, int start, int end)
+ : base(meta)
+ {
+ _v = v;
+ _start = start;
+ _end = end;
+ }
+
+ #endregion
+
+ #region IObj members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return meta == _meta
+ ? this
+ : new SubVector(meta, _v, _start, _end);
+ }
+
+ #endregion
+
+ #region IPersistentCollection members
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ public override int count()
+ {
+ return _end - _start;
+ }
+
+ /// <summary>
+ /// Gets an empty collection of the same type.
+ /// </summary>
+ /// <returns>An emtpy collection.</returns>
+ public override IPersistentCollection empty()
+ {
+ return (IPersistentCollection)PersistentVector.EMPTY.withMeta(_meta);
+ }
+
+ /// <summary>
+ /// Returns a new collection that has the given element cons'd on front of the eixsting collection.
+ /// </summary>
+ /// <param name="o">An item to put at the front of the collection.</param>
+ /// <returns>A new immutable collection with the item added.</returns>
+ IPersistentCollection IPersistentCollection.cons(object o)
+ {
+ return cons(o);
+ }
+
+ #endregion
+
+ #region IPersistentVector members
+
+ /// <summary>
+ /// Gets the number of items in the vector.
+ /// </summary>
+ /// <returns>The number of items.</returns>
+ /// <remarks>Not sure why you wouldn't use <c>count()</c> intead.</remarks>
+ public override int length()
+ {
+ return count();
+ }
+
+ /// <summary>
+ /// Get the i-th item in the vector.
+ /// </summary>
+ /// <param name="i">The index of the item to retrieve/</param>
+ /// <returns>The i-th item</returns>
+ /// <remarks>Throws an exception if the index <c>i</c> is not in the range of the vector's elements.</remarks>
+ public override object nth(int i)
+ {
+ if (_start + i >= _end)
+ throw new IndexOutOfRangeException();
+ return _v.nth(_start + i);
+ }
+
+ /// <summary>
+ /// Return a new vector with the i-th value set to <c>val</c>.
+ /// </summary>
+ /// <param name="i">The index of the item to set.</param>
+ /// <param name="val">The new value</param>
+ /// <returns>A new (immutable) vector v with v[i] == val.</returns>
+ public override IPersistentVector assocN(int i, object val)
+ {
+ if (_start + i > _end)
+ throw new IndexOutOfRangeException();
+ else if (_start + i == _end)
+ return cons(val);
+ else
+ return new SubVector(_meta, _v.assocN(_start + i, val), _start, _end);
+ }
+
+ /// <summary>
+ /// Creates a new vector with a new item at the end.
+ /// </summary>
+ /// <param name="o">The item to add to the vector.</param>
+ /// <returns>A new (immutable) vector with the objected added at the end.</returns>
+ public override IPersistentVector cons(object o)
+ {
+ return new SubVector(_meta, _v.assocN(_end, o), _start, _end + 1);
+ }
+
+ #endregion
+
+ #region IPersistentStack members
+
+ /// <summary>
+ /// Returns a new stack with the top element popped.
+ /// </summary>
+ /// <returns>The new stack</returns>
+ public override IPersistentStack pop()
+ {
+ return (_end - 1 == _start)
+ ? (IPersistentStack)PersistentVector.EMPTY
+ : new SubVector(_meta, _v, _start, _end - 1);
+ }
+
+ #endregion
+ }
+ }
+}
+
diff --git a/ClojureCLR/Clojure/Clojure/Lib/ARef.cs b/ClojureCLR/Clojure/Clojure/Lib/ARef.cs new file mode 100644 index 00000000..2e6fbb2d --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/ARef.cs @@ -0,0 +1,200 @@ +/**
+ * 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.Runtime.CompilerServices;
+
+namespace clojure.lang
+{
+
+ /// <summary>
+ /// Provides basic implementation for the <see cref="IRef">IRef</see> interface methods.
+ /// </summary>
+ public abstract class ARef : AReference, IRef
+ {
+ #region Data
+
+ /// <summary>
+ /// The validator for the reference.
+ /// </summary>
+ protected volatile IFn _validator = null;
+
+ /// <summary>
+ /// The set of watchers for the reference.
+ /// </summary>
+ private volatile IPersistentMap _watchers = PersistentHashMap.EMPTY;
+
+ #endregion
+
+ #region Ctors and factory methods
+
+ /// <summary>
+ /// Initializes an <see cref="ARef">ARef</see> with null metadata.
+ /// </summary>
+ public ARef()
+ : base()
+ {
+ }
+
+ /// <summary>
+ /// Initializes an <see cref="ARef">ARef</see> with the given metadata.
+ /// </summary>
+ /// <param name="meta">The metadata to use</param>
+ public ARef(IPersistentMap meta)
+ : base(meta)
+ {
+ }
+
+
+ #endregion
+
+ #region IDeref Members
+
+ /// <summary>
+ /// Gets the (immutable) value the reference is holding.
+ /// </summary>
+ /// <returns>The value</returns>
+ public abstract object deref();
+
+ #endregion
+
+ #region IRef Members
+
+ /// <summary>
+ /// Invoke an <see cref="IFn">IFn</see> on a value to validate it.
+ /// </summary>
+ /// <param name="vf">The <see cref="IFn">IFn</see> to invoke.</param>
+ /// <param name="val">The value to validate.</param>
+ /// <remarks>Uneventful return marks a successful validation.
+ /// To indicate a failed validation, the validation function should return <value>false</value> or throw an exception.
+ /// <para>This appears in multiple places. Should find it a common home?</para></remarks>
+ protected internal static void Validate(IFn vf, object val)
+ {
+ if (vf == null)
+ return;
+
+ bool ret = false;
+
+ try
+ {
+ ret = RT.booleanCast(vf.invoke(val));
+ }
+ catch (Exception e)
+ {
+ throw new InvalidOperationException("Invalid reference state", e);
+ }
+
+ if ( ! ret )
+ throw new InvalidOperationException("Invalid reference state");
+ }
+
+ /// <summary>
+ /// Call the reference's validator on the given value.
+ /// </summary>
+ /// <param name="val">The value to validate</param>
+ protected internal void Validate(object val)
+ {
+ Validate(_validator, val);
+ }
+
+ /// <summary>
+ /// Sets the validator.
+ /// </summary>
+ /// <param name="vf">The new validtor</param>
+ /// <remarks>The current value must validate in order for this validator to be accepted. If not, an exception will be thrown.</remarks>
+ public virtual void setValidator(IFn vf)
+ {
+ Validate(vf, deref());
+ _validator = vf;
+ }
+
+ /// <summary>
+ /// Gets the validator.
+ /// </summary>
+ /// <returns>The current validator.</returns>
+ public IFn getValidator()
+ {
+ return _validator;
+ }
+
+ #endregion
+
+ #region Watches
+
+ /// <summary>
+ /// Gets a map of watchers (key=Agent, value=IFn).
+ /// </summary>
+ /// <returns>An immutable map of watchers (key=Agent, value=IFn). </returns>
+ public IPersistentMap getWatches()
+ {
+ return _watchers;
+ }
+
+
+ /// <summary>
+ /// Adds a new watcher.
+ /// </summary>
+ /// <param name="watcher">The <see cref="Agent">Agent</see> doing the watching.</param>
+ /// <param name="action">The 'message' to send when the value changes.</param>
+ /// <param name="sendOff">If true, use <see cref="Agent.sendOff">send-off</see> to send the message, else use <see cref="Agent.send()">send</see>.</param>
+ /// <returns></returns>
+ [MethodImpl( MethodImplOptions.Synchronized)]
+ public IRef addWatch(Agent watcher, IFn action, bool sendOff)
+ {
+ _watchers = _watchers.assoc(watcher, new object[] { action, sendOff });
+ return this;
+ }
+
+
+ /// <summary>
+ /// Remove a watcher.
+ /// </summary>
+ /// <param name="watcher">The <see cref="Agent">Agent</see> to be removed.</param>
+ /// <returns>This IRef (for chaining).</returns>
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public IRef removeWatch(Agent watcher)
+ {
+ _watchers = _watchers.without(watcher);
+ return this;
+ }
+
+
+ /// <summary>
+ /// Notify all watchers.
+ /// </summary>
+ public void notifyWatches()
+ {
+ IPersistentMap ws = _watchers;
+ if (ws.count() > 0)
+ {
+ ISeq args = new Cons(this, null);
+ for (ISeq s = RT.seq(ws); s != null; s = s.rest())
+ {
+ IMapEntry me = (IMapEntry)s.first();
+ object[] a = (object[])me.val();
+ Agent agent = (Agent)me.key();
+ try
+ {
+ agent.dispatch((IFn)a[0], args, (Boolean)a[1]);
+ }
+ catch (Exception)
+ {
+ // eat dispatching exceptions and continue
+ }
+ }
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/AReference.cs b/ClojureCLR/Clojure/Clojure/Lib/AReference.cs new file mode 100644 index 00000000..b1ff7fb2 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/AReference.cs @@ -0,0 +1,102 @@ +/**
+ * 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.Runtime.CompilerServices;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Provides a basic implementation of IReference functionality.
+ /// </summary>
+ /// <remarks>The JVM implementation does not make this abstract, but I see no reason to every create one of these standalone.</remarks>
+ public abstract class AReference : IReference
+ {
+ #region Data
+
+ /// <summary>
+ /// The metatdata for the object.
+ /// </summary>
+ IPersistentMap _meta;
+
+ #endregion
+
+ #region Ctors and factory methods
+
+ /// <summary>
+ /// Initializes a new instance of <see cref="AReference">AReference</see> that has null metadata.
+ /// </summary>
+ public AReference()
+ : this(null)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of <see cref="AReference">AReference</see> that has
+ /// the given <see cref="IPersistentMap">IPersistentMap</see> as its metadata.
+ /// </summary>
+ /// <param name="meta">The map used to initialize the metadata.</param>
+ public AReference(IPersistentMap meta)
+ {
+ _meta = meta;
+ }
+
+
+
+ #endregion
+
+ #region IReference Members
+
+ /// <summary>
+ /// Alter the metadata on the object.
+ /// </summary>
+ /// <param name="alter">A function to apply to generate the new metadata</param>
+ /// <param name="args">Arguments to apply the function to.</param>
+ /// <returns>The new metadata map.</returns>
+ /// <remarks>The new value will be the result of <c>(apply alter (cons currentMeta args))</c>.</remarks>
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public IPersistentMap alterMeta(IFn alter, ISeq args)
+ {
+ _meta = (IPersistentMap)alter.applyTo(new Cons(_meta, args));
+ return _meta;
+ }
+
+ /// <summary>
+ /// Set the metadata of the object.
+ /// </summary>
+ /// <param name="m">The new metadata map</param>
+ /// <returns>The new metadata map.</returns>
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public IPersistentMap resetMeta(IPersistentMap m)
+ {
+ _meta = m;
+ return m;
+ }
+
+ #endregion
+
+ #region IMeta Members
+
+ /// <summary>
+ /// Gets the metadata attached to the object.
+ /// </summary>
+ /// <returns>An immutable map representing the object's metadata.</returns>
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public IPersistentMap meta()
+ {
+ return _meta;
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/ASeq.cs b/ClojureCLR/Clojure/Clojure/Lib/ASeq.cs new file mode 100644 index 00000000..c21fc727 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/ASeq.cs @@ -0,0 +1,397 @@ +/**
+ * 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.Collections;
+using System.Runtime.CompilerServices;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Provides basic implementation of <see cref="ISeq">ISeq</see> functionality.
+ /// </summary>
+ public abstract class ASeq: Obj, ISeq, IList, Streamable
+ {
+ #region Data
+
+ /// <summary>
+ /// Holds the hash code.
+ /// </summary>
+ [NonSerialized]
+ protected int _hash = -1;
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ /// <summary>
+ /// Initializes an <see cref="ASeq">ASeq</see> with given metadata.
+ /// </summary>
+ /// <param name="meta"></param>
+ protected ASeq(IPersistentMap meta)
+ : base(meta)
+ {
+ }
+
+ /// <summary>
+ /// Initializes an <see cref="ASeq">ASeq</see> with null metadata.
+ /// </summary>
+ protected ASeq()
+ {
+ }
+
+ #endregion
+
+ #region object overrides
+
+ /// <summary>
+ /// Returns a String that represents the current object.
+ /// </summary>
+ /// <returns>A String that represents the current object.</returns>
+ public override string ToString()
+ {
+ return RT.printString(this);
+ }
+
+ /// <summary>
+ /// Determines whether the specified Object is equal to the current object.
+ /// </summary>
+ /// <param name="obj">The Object to compare to the current object.</param>
+ /// <returns><c>true</c> if the specified Object is equal to the current object; otherwise <c>false</c></returns>
+ /// <remarks>Equality is value-based, ie.e. depends on the sequence of items.</remarks>
+ public override bool Equals(object obj)
+ {
+ if (!(obj is Sequential || obj is IList))
+ return false;
+
+ ISeq ms = RT.seq(obj);
+
+ for( ISeq s = seq(); s != null; s = s.rest(), ms = ms.rest() )
+ {
+ if ( ms == null || !Util.equals(s.first(),ms.first()))
+ return false;
+ }
+
+ return ms == null; // hit end of sequence on both sequences
+ }
+
+ /// <summary>
+ /// Computes a hash code for the current object.
+ /// </summary>
+ /// <returns>A hash code for the current object.</returns>
+ /// <remarks>The hash code is cached after it is computed the first time. The hash code depends on the value (sequenc of items).</remarks>
+ public override int GetHashCode()
+ {
+ if ( _hash == -1 )
+ {
+ int h = 0;
+ for (ISeq s = seq(); s != null; s = s.rest())
+ h = 31 * h + (s.first() == null ? 0 : s.first().GetHashCode());
+ _hash = h;
+ }
+ return _hash;
+ }
+
+ #endregion
+
+ #region ISeq Members
+
+ /// <summary>
+ /// Gets the first item.
+ /// </summary>
+ /// <returns>The first item.</returns>
+ public abstract object first();
+
+ /// <summary>
+ /// Gets the rest of the sequence.
+ /// </summary>
+ /// <returns>The rest of the sequence, or <c>null</c> if no more elements.</returns>
+ public abstract ISeq rest();
+
+ /// <summary>
+ /// Adds an item to the beginning of the sequence.
+ /// </summary>
+ /// <param name="o">The item to add.</param>
+ /// <returns>A new sequence containing the new item in front of the items already in the sequence.</returns>
+ /// <remarks>This overrides the <c>cons</c> method in <see cref="IPersistentCollection">IPersistentCollection</see>
+ /// by giving an <see cref="ISeq">ISeq</see> in return.</remarks>
+ public virtual ISeq cons(object o)
+ {
+ return new Cons(o, this);
+ }
+
+ #endregion
+
+ #region IPersistentCollection Members
+
+ /// <summary>
+ /// Adds an item to the beginning of the sequence.
+ /// </summary>
+ /// <param name="o">The item to add.</param>
+ /// <returns>A new sequence containing the new item in front of the items already in the sequence.</returns>
+ /// <remarks>This overrides the <c>cons</c> method in <see cref="IPersistentCollection">IPersistentCollection</see>
+ /// by giving an <see cref="ISeq">ISeq</see> in return.</remarks>
+ /// <remarks>Explicit implementation defers to the implicit implementation for <see cref="ISeq">ISeq</see>.</remarks>
+ IPersistentCollection IPersistentCollection.cons(object o)
+ {
+ return cons(o);
+ }
+
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ /// <remarks>This implementation is has time linear in the number of items.
+ /// Derived classes will want to override to cache this value.
+ /// (Easy because the collections are immutable.)</remarks>
+ public virtual int count()
+ {
+ int i = 1; // if it is here, it is non-empty.
+ for (ISeq s = rest(); s != null; s = s.rest(), i++)
+ if (s is Counted)
+ return i + s.count();
+
+ return i;
+ }
+
+ /// <summary>
+ /// Gets an ISeq to allow first/rest iteration through the collection.
+ /// </summary>
+ /// <returns>This item itself.</returns>
+ virtual public ISeq seq()
+ {
+ return this;
+ }
+
+ /// <summary>
+ /// Gets an empty collection of the same type.
+ /// </summary>
+ /// <returns>An emtpy collection.</returns>
+ /// <remarks>An empty sequence must be null.</remarks>
+ virtual public IPersistentCollection empty()
+ {
+ return null;
+ }
+
+
+ /// <summary>
+ /// Determine if an object is equivalent to this (handles all collections).
+ /// </summary>
+ /// <param name="o">The object to compare.</param>
+ /// <returns><c>true</c> if the object is equivalent; <c>false</c> otherwise.</returns>
+ public bool equiv(object obj)
+ {
+ if (!(obj is Sequential || obj is IList))
+ return false;
+
+ ISeq ms = RT.seq(obj);
+
+ for (ISeq s = seq(); s != null; s = s.rest(), ms = ms.rest())
+ {
+ if (ms == null || !Util.equiv(s.first(), ms.first()))
+ return false;
+ }
+
+ return ms == null; // hit end of sequence on both sequences
+ }
+
+ #endregion
+
+ #region ICollection Members
+
+ /// <summary>
+ /// Copies the elements of the sequence to an Array, starting at a particular index.
+ /// </summary>
+ /// <param name="array">The Array that is the destination of the copy.</param>
+ /// <param name="index">The zero-based index in <paramref name="array"/>array</param> at which copying begins.
+ public void CopyTo(Array array, int index)
+ {
+ if (array == null)
+ throw new ArgumentException("Array cannot be null.");
+
+ if (array.Length - index < count())
+ throw new ArgumentException("The number of elements in source is greater than the available space in the array.)");
+
+ if ( array.Rank != 1 )
+ throw new ArgumentException("Array must be 1-dimensional.");
+
+ if (index < 0)
+ throw new ArgumentOutOfRangeException("Index cannot be negative.");
+
+ ISeq s = seq();
+ for (int i = index; i < array.Length && s != null; ++i, s = s.rest())
+ array.SetValue(s.first(), i);
+ }
+
+ /// <summary>
+ /// Gets the number of elements in the sequence.
+ /// </summary>
+ public int Count
+ {
+ get { return count(); }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether access to the collection is thread-safe.
+ /// </summary>
+ public bool IsSynchronized
+ {
+ get { return true; }
+ }
+
+ public object SyncRoot
+ {
+ get { throw new NotImplementedException("No need for explicit locking on immutable sequence."); }
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ /// <summary>
+ /// Returns an enumerator that iterates through a collection.
+ /// </summary>
+ /// <returns>A <see cref="SeqEnumerator">SeqEnumerator</see> that iterates through the sequence.</returns>
+ public IEnumerator GetEnumerator()
+ {
+ return new SeqEnumerator(seq());
+ }
+
+ #endregion
+
+ #region Streamable Members
+
+ /// <summary>
+ /// Internal class that implements IStream for ASeq objects.
+ /// </summary>
+ class Stream : IStream
+ {
+ ISeq _s;
+
+ public Stream(ISeq s)
+ {
+ _s = s;
+ }
+
+ #region IStream Members
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public object next()
+ {
+ if (_s != null)
+ {
+ object ret = _s.first();
+ _s = _s.rest();
+ return ret;
+ }
+ return RT.eos();
+ }
+
+ #endregion
+ }
+
+ /// <summary>
+ /// Gets an <see cref="IStream">IStream/see> for this object.
+ /// </summary>
+ /// <returns>The <see cref="IStream">IStream/see>.</returns>
+ public virtual IStream stream()
+ {
+ return new Stream(this);
+ }
+
+ #endregion
+
+ #region IList Members
+
+ public int Add(object value)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public void Clear()
+ {
+ throw new InvalidOperationException();
+ }
+
+ public bool Contains(object value)
+ {
+ for (ISeq s = seq(); s != null; s = s.rest())
+ if (Util.equiv(s.first(), value))
+ return true;
+
+ return false;
+ }
+
+ public int IndexOf(object value)
+ {
+ int i = 0;
+ for (ISeq s = seq(); s != null; s = s.rest(),i++)
+ if (Util.equiv(s.first(), value))
+ return i;
+
+ return -1;
+ }
+
+ public void Insert(int index, object value)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public bool IsFixedSize
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public bool IsReadOnly
+ {
+ get { return true; }
+ }
+
+ public void Remove(object value)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public void RemoveAt(int index)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public object this[int index]
+ {
+ get
+ {
+ //Java has this: return RT.nth(this, index);
+ // THis causes an infinite loop in my code.
+ // When this was introduces, a change was made in RT.nth that changed the List test in its type dispatch to RandomAccess.
+ // CLR does not have the equivalent notion, so I just left it at IList. BOOM!
+ // So, I have to do a sequential search, duplicating some of the code in RT.nth.
+ ISeq seq = this;
+ for (int i = 0; i <= index && seq != null; ++i, seq = seq.rest())
+ {
+ if (i == index)
+ return seq.first();
+ }
+ throw new IndexOutOfRangeException();
+ }
+ set
+ {
+ throw new InvalidOperationException();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Agent.cs b/ClojureCLR/Clojure/Clojure/Lib/Agent.cs new file mode 100644 index 00000000..78816535 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Agent.cs @@ -0,0 +1,402 @@ +/**
+ * 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.Threading;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents an Agent.
+ /// </summary>
+ /// <remarks>
+ /// <para>See the Clojure documentation for more information.</para>
+ /// <para>The Java implementation plays many more games with thread pools. The CLR does not provide such support. We need to revisit this in CLR 4.
+ /// Until then: TODO: Implement our own thread pooling?</para>
+ /// </remarks>
+ public sealed class Agent : ARef
+ {
+ #region Data
+
+ /// <summary>
+ /// The current state of the agent.
+ /// </summary>
+ private volatile object _state;
+
+ /// <summary>
+ /// The current state of the agent.
+ /// </summary>
+ public object State
+ {
+ get { return _state; }
+ }
+
+ /// <summary>
+ /// A queue of pending actions.
+ /// </summary>
+ private AtomicReference<IPersistentStack> _q = new AtomicReference<IPersistentStack>(PersistentQueue.EMPTY);
+
+ /// <summary>
+ /// Number of items in the queue.
+ /// </summary>
+ public int QueueCount
+ {
+ get
+ {
+ return _q.Get().count();
+ }
+ }
+
+
+ /// <summary>
+ /// Agent errors, a sequence of Exceptions.
+ /// </summary>
+ private volatile ISeq _errors = null;
+
+ /// <summary>
+ /// Agent errors, a sequence of Exceptions.
+ /// </summary>
+ public ISeq Errors
+ {
+ get { return _errors; }
+ }
+
+
+ /// <summary>
+ /// Add an error.
+ /// </summary>
+ /// <param name="e">The exception to add.</param>
+ public void AddError(Exception e)
+ {
+ _errors = RT.cons(e, _errors);
+ }
+
+
+ /// <summary>
+ /// A collection of agent actions enqueued during the current transaction. Per thread.
+ /// </summary>
+ [ThreadStatic]
+ private static IPersistentVector _nested;
+
+ /// <summary>
+ /// A collection of agent actions enqueued during the current transaction. Per thread.
+ /// </summary>
+ public static IPersistentVector Nested
+ {
+ get { return _nested; }
+ set { _nested = value; }
+ }
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ /// <summary>
+ /// Construct an agent with given state and null metadata.
+ /// </summary>
+ /// <param name="state">The initial state.</param>
+ public Agent(object state)
+ : this(state, null)
+ {
+ }
+
+ /// <summary>
+ /// Construct an agent with given state and metadata.
+ /// </summary>
+ /// <param name="state">The initial state.</param>
+ /// <param name="meta">The metadata to attach.</param>
+ public Agent(Object state, IPersistentMap meta)
+ :base(meta)
+ {
+ SetState(state);
+ }
+
+ #endregion
+
+ #region State manipulation
+
+ /// <summary>
+ /// Set the state.
+ /// </summary>
+ /// <param name="newState">The new state.</param>
+ /// <returns><value>true</value> if the state changed; <value>false</value> otherwise.</returns>
+ private bool SetState(object newState)
+ {
+ Validate(newState);
+ bool ret = _state != newState;
+ _state = newState;
+ return ret;
+ }
+
+ #endregion
+
+ #region Agent methods
+
+ /// <summary>
+ /// Get the agent's errors.
+ /// </summary>
+ /// <returns>A sequence of the errors.</returns>
+ /// <remarks>Lowercase-name (and is a method instead of a property) for core.clj compatibility.</remarks>
+ public ISeq getErrors()
+ {
+ return _errors;
+ }
+
+ /// <summary>
+ /// Clear the agent's errors.
+ /// </summary>
+ /// <remarks>Lowercase-name and for core.clj compatibility.</remarks>
+ public void clearErrors()
+ {
+ _errors = null;
+ }
+
+ /// <summary>
+ /// Send a message to the agent.
+ /// </summary>
+ /// <param name="fn">The function to be called on the current state and the supplied arguments.</param>
+ /// <param name="args">The extra arguments to the function.</param>
+ /// <param name="solo"><value>true</value> means execute on its own thread (send-off);
+ /// <value>false</value> means use a thread pool thread (send).</param>
+ /// <returns>This agent.</returns>
+ public object dispatch(IFn fn, ISeq args, Boolean solo)
+ {
+ if ( _errors != null )
+ throw new Exception("Agent has errors", (Exception)RT.first(_errors));
+ Action action = new Action(this,fn,args,solo);
+ DispatchAction(action);
+
+ return this;
+ }
+
+ /// <summary>
+ /// Send an action (encapsulated message).
+ /// </summary>
+ /// <param name="action">The action to execute.</param>
+ /// <remarks>
+ /// <para>If there is a transaction running on this thread,
+ /// defer execution until the transaction ends
+ /// (enqueue the action on the transaction).</para>
+ /// <para>If there is already an action running, enqueue it (nested).</para>
+ /// <para>Otherwise, queue it for execution.</para>
+ /// </remarks>
+ internal static void DispatchAction(Action action)
+ {
+ LockingTransaction trans = LockingTransaction.getRunning();
+ if (trans != null)
+ trans.enqueue(action);
+ else if (_nested != null)
+ _nested = _nested.cons(action);
+ else
+ action.Agent.Enqueue(action);
+ }
+
+ /// <summary>
+ /// Enqueue an action in the pending queue.
+ /// </summary>
+ /// <param name="action">The action to enqueue.</param>
+ /// <remarks>Spin-locks to update the queue.</remarks>
+ void Enqueue(Action action)
+ {
+ bool queued = false;
+ IPersistentStack prior = null;
+ while (!queued)
+ {
+ prior = _q.Get();
+ queued = _q.CompareAndSet(prior, (IPersistentStack)prior.cons(action));
+ }
+
+ if (prior.count() == 0)
+ action.execute();
+ }
+
+
+ #endregion
+
+ #region IDeref Members
+
+ /// <summary>
+ /// Gets the (immutable) value the reference is holding.
+ /// </summary>
+ /// <returns>The value</returns>
+ public override object deref()
+ {
+ if (_errors != null)
+ throw new Exception("Agent has errors", (Exception)RT.first(_errors));
+ return _state;
+ }
+
+ #endregion
+
+ #region core.clj compatability
+
+ /// <summary>
+ /// Shutdown all threads executing.
+ /// </summary>
+ /// <remarks>We need to work on this.</remarks>
+ public static void shutdown()
+ {
+ // JAVA: soloExecutor.shutdown();
+ // JAVA: pooledExecutor.shutdown();
+
+ // TODO: record active jobs and shut them down?
+ }
+ #endregion
+
+ /// <summary>
+ /// An encapsulated message.
+ /// </summary>
+ internal sealed class Action
+ {
+ #region Data
+
+ /// <summary>
+ /// The agent this message is for.
+ /// </summary>
+ readonly Agent _agent;
+
+ /// <summary>
+ /// The agent this message is for.
+ /// </summary>
+ public Agent Agent
+ {
+ get { return _agent; }
+ }
+
+ /// <summary>
+ /// The function to call to create the new state.
+ /// </summary>
+ readonly IFn _fn;
+
+ /// <summary>
+ /// The arguments to call (in addition to the current state).
+ /// </summary>
+ readonly ISeq _args;
+
+ /// <summary>
+ /// Should execute on its own thread (not a thread-pool thread).
+ /// </summary>
+ readonly bool _solo;
+
+ #endregion
+
+ #region Ctors
+
+ /// <summary>
+ /// Create an encapsulated message to an agent.
+ /// </summary>
+ /// <param name="agent">The agent the message is for.</param>
+ /// <param name="fn">The function to compute the new value.</param>
+ /// <param name="args">Additional arguments (in addition to the current state).</param>
+ /// <param name="solo">Execute on its own thread?</param>
+ public Action(Agent agent, IFn fn, ISeq args, bool solo)
+ {
+ _agent = agent;
+ _fn = fn;
+ _args = args;
+ _solo = solo;
+ }
+
+ #endregion
+
+ #region Executing the action
+
+ /// <summary>
+ /// Send the message.
+ /// </summary>
+ public void execute()
+ {
+ if (_solo)
+ {
+ // TODO: Reuse/cleanup these threads
+ Thread thread = new Thread(ExecuteAction);
+ //thread.Priority = ThreadPriority.Lowest;
+ thread.Start(null);
+ }
+ else
+ ThreadPool.QueueUserWorkItem(ExecuteAction);
+ }
+
+ /// <summary>
+ /// Worker method to execute the action on a thread.
+ /// </summary>
+ /// <param name="state">(not used)</param>
+ /// <remarks>corresponds to doRun in Java version</remarks>
+ void ExecuteAction(object state)
+ {
+ try
+ {
+ Var.pushThreadBindings(RT.map(RT.AGENT, _agent));
+ Agent.Nested = PersistentVector.EMPTY;
+
+ bool hadError = false;
+ bool changed = false;
+
+ try
+ {
+ changed = _agent.SetState(_fn.applyTo(RT.cons(_agent.State, _args)));
+ if (changed)
+ _agent.notifyWatches();
+ }
+ catch (Exception e)
+ {
+ // TODO: report/callback (Java TODO)
+ _agent.AddError(e);
+ hadError = true;
+ }
+
+ if (!hadError)
+ releasePendingSends();
+
+ bool popped = false;
+ IPersistentStack next = null;
+ while (!popped)
+ {
+ IPersistentStack prior = _agent._q.Get();
+ next = prior.pop();
+ popped = _agent._q.CompareAndSet(prior, next);
+ }
+
+ if (next.count() > 0)
+ ((Action)next.peek()).execute();
+ }
+ finally
+ {
+ Nested = null;
+ Var.popThreadBindings();
+ }
+ }
+
+ #endregion
+ }
+
+ /// <summary>
+ /// Enqueue nested actions.
+ /// </summary>
+ /// <returns></returns>
+ /// <remarks>lowercase for core.clj compatibility</remarks>
+ public static int releasePendingSends()
+ {
+ IPersistentVector sends = Agent.Nested;
+ if (sends == null)
+ return 0;
+ for (int i = 0; i < sends.count(); i++)
+ {
+ Action a = (Action)sends.valAt(i);
+ a.Agent.Enqueue(a);
+ }
+ Nested = PersistentVector.EMPTY;
+ return sends.count();
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/ArraySeq.cs b/ClojureCLR/Clojure/Clojure/Lib/ArraySeq.cs new file mode 100644 index 00000000..ce0cbb23 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/ArraySeq.cs @@ -0,0 +1,205 @@ +/**
+ * 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.Collections;
+
+namespace clojure.lang
+{
+ public class ArraySeq : ASeq, IndexedSeq, IReduce
+ {
+ // TODO: rethink the whole thing.
+ // Java version has separate types for numeric arrays.
+ // We could specialize for arrays using direct access versus arrays using Array.get access.
+
+ #region Data
+
+ private readonly object _a;
+ private readonly int _i;
+ private readonly object[] _oa;
+ // TODO: Check against current version. IList equivalent not in current Java version.
+ private readonly IList _ilist;
+
+ public object[] ToArray()
+ {
+ if ( _oa != null )
+ return _oa;
+
+ if (_ilist != null)
+ {
+ object[] items = new object[_ilist.Count];
+ for (int i = 0; i < _ilist.Count; i++)
+ items[i] = _ilist[i];
+ return items;
+ }
+
+ return (object[])_a;
+ }
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ static public ArraySeq create()
+ {
+ return null;
+ }
+
+ static public ArraySeq create(params object[] array)
+ {
+ return (array == null || array.Length == 0)
+ ? null
+ : new ArraySeq(array, 0);
+ }
+
+ // Not in the Java version, but I can really use this
+ static public ArraySeq create(object[] array, int firstIndex)
+ {
+ return (array == null || array.Length <= firstIndex )
+ ? null
+ : new ArraySeq(array, firstIndex);
+ }
+
+ internal static ISeq createFromObject(Object array)
+ {
+ if (array == null || ((Array)array).Length == 0)
+ return null;
+ //Type aclass = array.GetType();
+ //if(aclass == int[].class)
+ // return new ArraySeq_int(null, (int[]) array, 0);
+ //if(aclass == float[].class)
+ // return new ArraySeq_float(null, (float[]) array, 0);
+ //if(aclass == double[].class)
+ // return new ArraySeq_double(null, (double[]) array, 0);
+ //if(aclass == long[].class)
+ // return new ArraySeq_long(null, (long[]) array, 0);
+ return new ArraySeq(array, 0);
+ }
+
+
+// TODO: Really need to think about this. A lot left on the table here.
+
+ ArraySeq(object array, int i)
+ {
+ _a = array;
+ _i = i;
+ _oa = (object[])(array is object[] ? array : null);
+ _ilist = (IList)_a;
+ }
+
+ ArraySeq(IPersistentMap meta, object array, int i)
+ : base(meta)
+ {
+ _a = array;
+ _i = i;
+ _oa = (object[])(array is object[] ? array : null);
+ _ilist = (IList)_a;
+ }
+
+
+ #endregion
+
+ #region ISeq members
+
+ public override object first()
+ {
+ if (_oa != null)
+ return _oa[_i];
+ else
+ return _ilist[_i]; //rev 1112 wraps this in RT.prepRet, don't know why
+ }
+
+ public override ISeq rest()
+ {
+ if (_oa != null)
+ {
+ if (_i + 1 < _oa.Length)
+ return new ArraySeq(_a, _i + 1);
+ }
+ else
+ {
+ if (_i + 1 < _ilist.Count)
+ return new ArraySeq(_a, _i + 1);
+ }
+ return null;
+ }
+
+ #endregion
+
+ #region IPersistentCollection members
+
+ public override int count()
+ {
+ return _oa != null
+ ? _oa.Length - _i
+ : _ilist.Count - _i;
+ }
+
+ #endregion
+
+ #region IObj members
+
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ // Java version does not do identity test
+ return meta == _meta
+ ? this
+ : new ArraySeq(meta, _a, _i);
+ }
+
+ #endregion
+
+ #region IndexedSeq Members
+
+ public int index()
+ {
+ return _i;
+ }
+
+ #endregion
+
+ #region IReduce Members
+
+ public object reduce(IFn f)
+ {
+ if (_oa != null)
+ {
+ object ret = _oa[_i];
+ for (int x = _i + 1; x < _oa.Length; x++)
+ ret = f.invoke(ret, _oa[x]);
+ return ret;
+ }
+ object ret1 = _ilist[_i]; // JAVA 1112 wraps in RT.prepRet
+ for (int x = _i + 1; x < _ilist.Count; x++)
+ ret1 = f.invoke(ret1, _ilist[x]); // JAVA 1112 wraps in RT.prepRet
+ return ret1;
+ }
+
+ public object reduce(IFn f, object start)
+ {
+ if (_oa != null)
+ {
+ object ret = f.invoke(start, _oa[_i]);
+ for (int x = _i + 1; x < _oa.Length; x++)
+ ret = f.invoke(ret, _oa[x]);
+ return ret;
+ }
+ object ret1 = f.invoke(start, _ilist[_i]); // JAVA 1112 wraps in RT.prepRet
+ for (int x = _i + 1; x < _ilist.Count; x++)
+ ret1 = f.invoke(ret1, _ilist[x]); // JAVA 1112 wraps in RT.prepRet
+ return ret1;
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/ArrayStream.cs b/ClojureCLR/Clojure/Clojure/Lib/ArrayStream.cs new file mode 100644 index 00000000..cc30bc1b --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/ArrayStream.cs @@ -0,0 +1,22 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ // TODO: ArrayStream needs some thought.
+ public class ArrayStream
+ {
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Associative.cs b/ClojureCLR/Clojure/Clojure/Lib/Associative.cs new file mode 100644 index 00000000..ccdbd5d8 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Associative.cs @@ -0,0 +1,50 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents an immutable key/value mapping.
+ /// </summary>
+ public interface Associative: IPersistentCollection
+ {
+ /// <summary>
+ /// Test if the map contains a key.
+ /// </summary>
+ /// <param name="key">The key to test for membership</param>
+ /// <returns>True if the key is in this map.</returns>
+ bool containsKey(object key);
+
+ /// <summary>
+ /// Returns the key/value pair for this key.
+ /// </summary>
+ /// <param name="key">The key to retrieve</param>
+ /// <returns>The key/value pair for the key, or null if the key is not in the map.</returns>
+ IMapEntry entryAt(object key);
+
+ /// <summary>
+ /// Add a new key/value pair.
+ /// </summary>
+ /// <param name="key">The key</param>
+ /// <param name="val">The value</param>
+ /// <returns>A new map with the key/value added.</returns>
+ Associative assoc(object key, object val);
+
+ /// <summary>
+ /// Gets the value associated with a key.
+ /// </summary>
+ /// <param name="key">The key to look up.</param>
+ /// <returns>The associated value. (Throws an exception if key is not present.)</returns>
+ object valAt(object key);
+
+ /// <summary>
+ /// Gets the value associated with a key.
+ /// </summary>
+ /// <param name="key">The key to look up.</param>
+ /// <param name="notFound">The value to return if the key is not present.</param>
+ /// <returns>The associated value (or <c>notFound</c> if the key is not present.</returns>
+ object valAt(object key, object notFound);
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Atom.cs b/ClojureCLR/Clojure/Clojure/Lib/Atom.cs new file mode 100644 index 00000000..98d71cea --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Atom.cs @@ -0,0 +1,198 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Provides spin-loop synchronized access to a value. One of the reference types.
+ /// </summary>
+ public class Atom : ARef
+ {
+ #region Data
+
+ /// <summary>
+ /// The atom's value.
+ /// </summary>
+ readonly AtomicReference<object> _state;
+
+ #endregion
+
+ #region Ctors and factory methods
+
+ /// <summary>
+ /// Construct an atom with given intiial value.
+ /// </summary>
+ /// <param name="state">The initial value</param>
+ public Atom(object state)
+ {
+ _state = new AtomicReference<object>(state);
+ }
+
+ /// <summary>
+ /// Construct an atom with given initial value and metadata.
+ /// </summary>
+ /// <param name="state">The initial value.</param>
+ /// <param name="meta">The metadata to attach.</param>
+ public Atom(object state, IPersistentMap meta)
+ : base(meta)
+ {
+ _state = new AtomicReference<object>(state);
+ }
+
+
+
+ #endregion
+
+ #region IDeref methods
+
+ /// <summary>
+ /// Gets the (immutable) value the reference is holding.
+ /// </summary>
+ /// <returns>The value</returns>
+ public override object deref()
+ {
+ return _state.Get();
+ }
+
+ #endregion
+
+ #region State manipulation
+
+ /// <summary>
+ /// Compute and set a new value. Spin loop for coordination.
+ /// </summary>
+ /// <param name="f">The function to apply to the current state.</param>
+ /// <returns>The new value.</returns>
+ /// <remarks>Lowercase name for core.clj compatability.</remarks>
+ public object swap(IFn f)
+ {
+ for (; ; )
+ {
+ object v = deref();
+ object newv = f.invoke(v);
+ Validate(newv);
+ if (_state.CompareAndSet(v, newv))
+ {
+ if (v != newv)
+ notifyWatches();
+ return newv;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Compute and set a new value. Spin loop for coordination.
+ /// </summary>
+ /// <param name="f">The function to apply to current state and one additional argument.</param>
+ /// <param name="arg">Additional argument.</param>
+ /// <returns>The new value.</returns>
+ /// <remarks>Lowercase name for core.clj compatability.</remarks>
+ public object swap(IFn f, Object arg)
+ {
+ for (; ; )
+ {
+ object v = deref();
+ object newv = f.invoke(v, arg);
+ Validate(newv);
+ if (_state.CompareAndSet(v, newv))
+ {
+ if (v != newv)
+ notifyWatches();
+ return newv;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Compute and set a new value. Spin loop for coordination.
+ /// </summary>
+ /// <param name="f">The function to apply to current state and additional arguments.</param>
+ /// <param name="arg1">First additional argument.</param>
+ /// <param name="arg2">Second additional argument.</param>
+ /// <returns>The new value.</returns>
+ /// <remarks>Lowercase name for core.clj compatability.</remarks>
+ public object swap(IFn f, Object arg1, Object arg2)
+ {
+ for (; ; )
+ {
+ object v = deref();
+ object newv = f.invoke(v, arg1, arg2);
+ Validate(newv);
+ if (_state.CompareAndSet(v, newv))
+ {
+ if (v != newv)
+ notifyWatches();
+ return newv;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Compute and set a new value. Spin loop for coordination.
+ /// </summary>
+ /// <param name="f">The function to apply to current state and additional arguments.</param>
+ /// <param name="x">First additional argument.</param>
+ /// <param name="y">Second additional argument.</param>
+ /// <param name="args">Sequence of additional arguments.</param>
+ /// <returns>The new value.</returns>
+ /// <remarks>Lowercase name for core.clj compatability.</remarks>
+ public object swap(IFn f, Object x, Object y, ISeq args)
+ {
+ for (; ; )
+ {
+ object v = deref();
+ object newv = f.applyTo(RT.listStar(v, x, y, args));
+ Validate(newv);
+ if (_state.CompareAndSet(v, newv))
+ {
+ if (v != newv)
+ notifyWatches();
+ return newv;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Compare/exchange the value.
+ /// </summary>
+ /// <param name="oldv">The expected value.</param>
+ /// <param name="newv">The new value.</param>
+ /// <returns><value>true</value> if the value was set; <value>false</value> otherwise.</returns>
+ public bool compareAndSet(object oldv, object newv)
+ {
+ Validate(newv);
+ bool ret = _state.CompareAndSet(oldv, newv);
+ if (ret && oldv != newv)
+ notifyWatches();
+ return ret;
+ }
+
+
+ /// <summary>
+ /// Set the value.
+ /// </summary>
+ /// <param name="newv">The new value.</param>
+ /// <returns>The new value.</returns>
+ public object reset(object newv)
+ {
+ Validate(newv);
+ _state.Set(newv);
+ notifyWatches();
+ return newv;
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/AtomicInteger.cs b/ClojureCLR/Clojure/Clojure/Lib/AtomicInteger.cs new file mode 100644 index 00000000..8d605494 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/AtomicInteger.cs @@ -0,0 +1,125 @@ +/**
+ * 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.Threading;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Implements the Java <c>java.util.concurrent.atomic.AtomicInteger</c> class.
+ /// </summary>
+ /// <remarks>I hope. Someone with more knowledge of these things should check this out.</remarks>
+ public sealed class AtomicInteger
+ {
+ #region Data
+
+ /// <summary>
+ /// The current <see cref="Int32">integer</see> value.
+ /// </summary>
+ int _val;
+
+ #endregion
+
+ #region C-tors
+
+ /// <summary>
+ /// Initializes an <see cref="AtomicInteger">AtomicInteger</see> with value zero.
+ /// </summary>
+ public AtomicInteger()
+ {
+ _val = 0;
+ }
+
+ /// <summary>
+ /// Initializes an <see cref="AtomicInteger">AtomicInteger</see> with a given value.
+ /// </summary>
+ /// <param name="initVal">The initial value.</param>
+ public AtomicInteger(int initVal)
+ {
+ _val = initVal;
+ }
+
+ #endregion
+
+ #region Value access
+
+ /// <summary>
+ /// Gets the current value.
+ /// </summary>
+ /// <returns>The current value.</returns>
+ public int get()
+ {
+ return _val;
+ }
+
+ /// <summary>
+ /// Increments the value and returns the new value.
+ /// </summary>
+ /// <returns>The new value.</returns>
+ public int incrementAndGet()
+ {
+ return Interlocked.Increment(ref _val);
+ }
+
+ /// <summary>
+ /// Increments the value and returns the original value.
+ /// </summary>
+ /// <returns>The original value.</returns>
+ public int getAndIncrement()
+ {
+ return Interlocked.Increment(ref _val)-1;
+ }
+
+ /// <summary>
+ /// Decrements the value and returns the new value.
+ /// </summary>
+ /// <returns>The new value.</returns>
+ public int decrementAndGet()
+ {
+ return Interlocked.Decrement(ref _val);
+ }
+
+ /// <summary>
+ /// Decrements the value and returns the original value.
+ /// </summary>
+ /// <returns>The original value.</returns>
+ public int getAndDecrement()
+ {
+ return Interlocked.Decrement(ref _val) - 1;
+ }
+ /// <summary>
+ /// Sets the value if the expected value is current.
+ /// </summary>
+ /// <param name="oldVal">The expected value.</param>
+ /// <param name="newVal">The new value.</param>
+ /// <returns><value>true</value> if the value was set; <value>false</value> otherwise.</returns>
+ public bool compareAndSet(int oldVal, int newVal)
+ {
+ int origVal = Interlocked.CompareExchange(ref _val, newVal, oldVal);
+ return origVal == oldVal;
+ }
+
+ /// <summary>
+ /// Sets the value.
+ /// </summary>
+ /// <param name="newVal">The new value.</param>
+ /// <returns>The new value.</returns>
+ public int set(int newVal)
+ {
+ return Interlocked.Exchange(ref _val,newVal);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/AtomicLong.cs b/ClojureCLR/Clojure/Clojure/Lib/AtomicLong.cs new file mode 100644 index 00000000..2cc631e1 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/AtomicLong.cs @@ -0,0 +1,108 @@ +/**
+ * 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.Threading;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Implements the Java <c>java.util.concurrent.atomic.AtomicLong</c> class.
+ /// </summary>
+ /// <remarks>I hope. Someone with more knowledge of these things should check this out.</remarks>
+ public sealed class AtomicLong
+ {
+ #region Data
+
+ /// <summary>
+ /// The current <see cref="Int32">integer</see> value.
+ /// </summary>
+ long _val;
+
+ #endregion
+
+ #region Ctors
+
+ /// <summary>
+ /// Initializes an <see cref="AtomicLong">AtomicLong</see> with value zero.
+ /// </summary>
+ public AtomicLong()
+ {
+ _val = 0;
+ }
+
+ /// <summary>
+ /// Initializes an <see cref="AtomicLong">AtomicLong</see> with a given value.
+ /// </summary>
+ /// <param name="initVal">The initial value.</param>
+ public AtomicLong(long initVal)
+ {
+ _val = initVal;
+ }
+
+ #endregion
+
+ #region Value access
+
+ /// <summary>
+ /// Gets the current value.
+ /// </summary>
+ /// <returns>The current value.</returns>
+ public long get()
+ {
+ return _val;
+ }
+
+ /// <summary>
+ /// Increments the value and returns the new value.
+ /// </summary>
+ /// <returns>The new value.</returns>
+ public long incrementAndGet()
+ {
+ return Interlocked.Increment(ref _val);
+ }
+
+ /// <summary>
+ /// Increments the value and returns the original value.
+ /// </summary>
+ /// <returns>The original value.</returns>
+ public long getAndIncrement()
+ {
+ return Interlocked.Increment(ref _val)-1;
+ }
+
+ /// <summary>
+ /// Sets the value if the expected value is current.
+ /// </summary>
+ /// <param name="oldVal">The expected value.</param>
+ /// <param name="newVal">The new value.</param>
+ /// <returns><value>true</value> if the value was set; <value>false</value> otherwise.</returns>
+ public bool compareAndSet(long oldVal, long newVal)
+ {
+ long origVal = Interlocked.CompareExchange(ref _val, newVal, oldVal);
+ return origVal == oldVal;
+ }
+
+ /// <summary>
+ /// Sets the value.
+ /// </summary>
+ /// <param name="newVal">The new value.</param>
+ /// <returns>The new value.</returns>
+ public long set(long newVal)
+ {
+ return Interlocked.Exchange(ref _val,newVal);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/AtomicReference.cs b/ClojureCLR/Clojure/Clojure/Lib/AtomicReference.cs new file mode 100644 index 00000000..1be0dc96 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/AtomicReference.cs @@ -0,0 +1,118 @@ +/**
+ * 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.Threading;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Implements the Java java.util.concurrent.atomic.AtomicReference<V> class
+ /// </summary>
+ /// <remarks>I hope. Someone with more knowledge of these things should check this out.
+ /// <para>Not implemented: weakCompareAndSet</para></remarks>
+ [Serializable]
+ public sealed class AtomicReference<T> where T : class
+ {
+ #region Data
+
+ /// <summary>
+ /// The current value.
+ /// </summary>
+ private T _ref;
+
+ #endregion
+
+ #region C-tors
+
+ /// <summary>
+ /// Intializes an <see cref="AtomicReference">AtomicReference</see> to null.
+ /// </summary>
+ public AtomicReference()
+ : this(null)
+ {
+ }
+
+ /// <summary>
+ /// Intializes an <see cref="AtomicReference">AtomicReference</see> to hold a given reference.
+ /// </summary>
+ /// <param name="initVal">The initial value.</param>
+ public AtomicReference(T initVal)
+ {
+ _ref = initVal;
+ }
+
+ #endregion
+
+ #region Methods
+
+ /// <summary>
+ /// Sets the reference if the expected reference is current.
+ /// </summary>
+ /// <param name="expect">The expected value.</param>
+ /// <param name="update">The new value.</param>
+ /// <returns><value>true</value> if the value is changed; <value>false</value> otherwise.</returns>
+ /// <remarks>The Java version returns a boolean. The BCL version returns the old value.
+ /// Is ReferenceEquals the correct comparison? (That is what CompareExchange uses, so I'm guessing so.)</remarks>
+ public bool CompareAndSet(T expect, T update)
+ {
+ T oldVal = Interlocked.CompareExchange<T>(ref _ref, update, expect);
+ return Object.ReferenceEquals(oldVal,expect); // TODO: (Or does it use Equals?)
+ }
+
+ /// <summary>
+ /// Get the current value.
+ /// </summary>
+ /// <returns>The current value.</returns>
+ public T Get()
+ {
+ return _ref;
+ }
+
+ /// <summary>
+ /// Sets a new value and returns the original value.
+ /// </summary>
+ /// <param name="update">The new value.</param>
+ /// <returns>The original value.</returns>
+ public T GetAndSet(T update)
+ {
+ return Interlocked.Exchange(ref _ref, update);
+ }
+
+ /// <summary>
+ /// Sets the value.
+ /// </summary>
+ /// <param name="update">The new value.</param>
+ public void Set(T update)
+ {
+ Interlocked.Exchange(ref _ref, update);
+ }
+
+ #endregion
+
+ #region object overrides
+
+ /// <summary>
+ /// Returns string representing the value.
+ /// </summary>
+ /// <returns>A string representing the value.</returns>
+ public override string ToString()
+ {
+ return _ref.ToString();
+ }
+
+ #endregion
+ }
+}
+
+
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Box.cs b/ClojureCLR/Clojure/Clojure/Lib/Box.cs new file mode 100644 index 00000000..016943e8 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Box.cs @@ -0,0 +1,51 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+
+ // TODO: Replace Box: mostly this is used in the Java version in lieu of ref/out parameters.
+
+ /// <summary>
+ /// Boxes any value or reference.
+ /// </summary>
+ public class Box
+ {
+ /// <summary>
+ /// The value being boxed.
+ /// </summary>
+ private object _val;
+
+ /// <summary>
+ /// Gets the boxed value.
+ /// </summary>
+ public object Val
+ {
+ get { return _val; }
+ set { _val = value; }
+ }
+
+ /// <summary>
+ /// Initializes a <see cref="Box">Box</see> to the given value.
+ /// </summary>
+ /// <param name="val"></param>
+ public Box(object val)
+ {
+ _val = val;
+ }
+
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/CachedSeq.cs b/ClojureCLR/Clojure/Clojure/Lib/CachedSeq.cs new file mode 100644 index 00000000..46139402 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/CachedSeq.cs @@ -0,0 +1,134 @@ +/**
+ * 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.Runtime.CompilerServices;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents a sequence with cached values.
+ /// </summary>
+ /// <remarks>Built on top of another sequence that might be infinite or otherwise lazy.
+ /// Caches the first/rest values if they are computed.</remarks>
+ public class CachedSeq : ASeq
+ {
+ #region Data
+
+ /// <summary>
+ /// The underlying sequence.
+ /// </summary>
+ ISeq _s;
+
+ /// <summary>
+ /// The first item in the sequence.
+ /// </summary>
+ /// <remarks>Initialized to this. Holds cached value after it is computed.</remarks>
+ object _first;
+
+ /// <summary>
+ /// The rest of the sequence.
+ /// </summary>
+ /// <remarks>Initialized to this. Holds cached value after it is computed.</remarks>
+ ISeq _rest;
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ // TODO: fix parameter type when we have type inferencing
+ //public CachedSeq(ISeq s)
+ /// <summary>
+ /// Initialize from an arbitray object (must be seq-able).
+ /// </summary>
+ /// <param name="s">The object to cache.</param>
+ public CachedSeq(object s)
+ {
+ _s = RT.seq(s);
+ _first = this;
+ _rest = this;
+ }
+
+ /// <summary>
+ /// Initialize from first/rest, with metadata.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ /// <param name="first">The first element.</param>
+ /// <param name="rest">The rest of the sequence.</param>
+ CachedSeq(IPersistentMap meta, object first, ISeq rest)
+ : base(meta)
+ {
+ _first = first;
+ _rest = rest;
+ }
+
+
+ #endregion
+
+ #region IObject members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap m)
+ {
+ if (m == meta())
+ return this;
+ // force before copying
+ rest();
+ return new CachedSeq(m, _first, _rest);
+ }
+
+ #endregion
+
+ #region ISeq members
+
+ /// <summary>
+ /// Gets the first item.
+ /// </summary>
+ /// <returns>The first item.</returns>
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override object first()
+ {
+ if (_first == this)
+ _first = _s.first();
+ return _first;
+ }
+
+ /// <summary>
+ /// Gets the rest of the sequence.
+ /// </summary>
+ /// <returns>The rest of the sequence, or <c>null</c> if no more elements.</returns>
+ public override ISeq rest()
+ {
+ if (_rest == this)
+ {
+ // force sequential evaluation
+ if (_first == this)
+ first();
+ ISeq rs = _s.rest();
+ if (rs == null)
+ _rest = rs;
+ else
+ _rest = new CachedSeq(rs);
+ _s = null;
+ }
+ return _rest;
+ }
+
+ #endregion
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/ClojureException.cs b/ClojureCLR/Clojure/Clojure/Lib/ClojureException.cs new file mode 100644 index 00000000..da5bf946 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/ClojureException.cs @@ -0,0 +1,34 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ public class ClojureException : Exception
+ {
+ public ClojureException()
+ {
+ }
+
+ public ClojureException(string msg)
+ : base(msg)
+ {
+ }
+
+ public ClojureException(string msg, Exception innerException)
+ : base(msg, innerException)
+ {
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Compiler.cs b/ClojureCLR/Clojure/Clojure/Lib/Compiler.cs new file mode 100644 index 00000000..39cdef60 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Compiler.cs @@ -0,0 +1,325 @@ +/**
+ * 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 System.Threading;
+
+namespace clojure.lang
+{
+ public class Compiler
+ {
+
+ #region Symbols
+
+ public static readonly Symbol DEF = Symbol.create("def");
+ public static readonly Symbol LOOP = Symbol.create("loop*");
+ public static readonly Symbol RECUR = Symbol.create("recur");
+ public static readonly Symbol IF = Symbol.create("if");
+ public static readonly Symbol LET = Symbol.create("let*");
+ public static readonly Symbol DO = Symbol.create("do");
+ public static readonly Symbol FN = Symbol.create("fn*");
+ public static readonly Symbol QUOTE = Symbol.create("quote");
+ public static readonly Symbol THE_VAR = Symbol.create("var");
+ public static readonly Symbol DOT = Symbol.create(".");
+ public static readonly Symbol ASSIGN = Symbol.create("set!");
+ public static readonly Symbol TRY = Symbol.create("try");
+ public static readonly Symbol CATCH = Symbol.create("catch");
+ public static readonly Symbol FINALLY = Symbol.create("finally");
+ public static readonly Symbol THROW = Symbol.create("throw");
+ public static readonly Symbol MONITOR_ENTER = Symbol.create("monitor-enter");
+ public static readonly Symbol MONITOR_EXIT = Symbol.create("monitor-exit");
+ public static readonly Symbol NEW = Symbol.create("new");
+ public static readonly Symbol _AMP_ = Symbol.create("&");
+
+
+ public static readonly Symbol IDENTITY = Symbol.create("clojure.core", "identity");
+
+ static readonly Symbol NS = Symbol.create("ns");
+ static readonly Symbol IN_NS = Symbol.create("in-ns");
+
+ #endregion
+
+ #region Vars
+
+ //boolean
+ static readonly Var COMPILE_FILES = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),
+ Symbol.create("*compile-files*"), false); //JAVA: Boolean.FALSE -- changed from RT.F in rev 1108, not sure why
+
+ //String
+ static readonly Var COMPILE_PATH = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),
+ Symbol.create("*compile-path*"), null);
+ // String
+ static readonly Var SOURCE_PATH = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),
+ Symbol.create("*file*"), null);
+
+ #endregion
+
+ #region Special forms
+
+ public static readonly IPersistentSet _specials = PersistentHashSet.create(
+ DEF,
+ LOOP,
+ RECUR,
+ IF,
+ LET,
+ DO,
+ FN,
+ QUOTE,
+ THE_VAR,
+ DOT,
+ ASSIGN,
+ TRY,
+ THROW,
+ MONITOR_ENTER,
+ MONITOR_EXIT,
+ CATCH,
+ FINALLY,
+ NEW,
+ _AMP_
+ );
+
+ public static bool isSpecial(Object sym)
+ {
+ return _specials.contains(sym);
+ }
+
+ #endregion
+
+ #region Symbol/namespace resolving
+
+ // TODO: we have duplicate code below.
+
+ public static Symbol resolveSymbol(Symbol sym)
+ {
+ //already qualified or classname?
+ if (sym.Name.IndexOf('.') > 0)
+ return sym;
+ if (sym.Namespace != null)
+ {
+ Namespace ns = namespaceFor(sym);
+ if (ns == null || ns.Name.Name == sym.Namespace)
+ return sym;
+ return Symbol.create(ns.Name.Name, sym.Name);
+ }
+ Object o = CurrentNamespace.GetMapping(sym);
+ if (o == null)
+ return Symbol.intern(CurrentNamespace.Name.Name, sym.Name);
+ else if (o is Type)
+ return Symbol.intern(null, ((Type)o).Name);
+ else if (o is Var)
+ {
+ Var v = (Var)o;
+ return Symbol.create(v.Namespace.Name.Name, v.Symbol.Name);
+ }
+ return null;
+
+ }
+
+
+ public static Namespace namespaceFor(Symbol sym)
+ {
+ return namespaceFor(CurrentNamespace, sym);
+ }
+
+ public static Namespace namespaceFor(Namespace inns, Symbol sym)
+ {
+ //note, presumes non-nil sym.ns
+ // first check against currentNS' aliases...
+ Symbol nsSym = Symbol.create(sym.Namespace);
+ Namespace ns = inns.LookupAlias(nsSym);
+ if (ns == null)
+ {
+ // ...otherwise check the Namespaces map.
+ ns = Namespace.find(nsSym);
+ }
+ return ns;
+ }
+
+ public static Namespace CurrentNamespace
+ {
+ get { return (Namespace)RT.CURRENT_NS.deref(); }
+ }
+
+
+
+ public static object Resolve(Symbol symbol, bool allowPrivate)
+ {
+ return ResolveIn(CurrentNamespace, symbol, allowPrivate);
+ }
+
+ public static object Resolve(Symbol symbol)
+ {
+ return ResolveIn(CurrentNamespace, symbol, false);
+ }
+
+ private static object ResolveIn(Namespace n, Symbol symbol, bool allowPrivate)
+ {
+ // note: ns-qualified vars must already exist
+ if (symbol.Namespace != null)
+ {
+ Namespace ns = NamespaceFor(n, symbol);
+ if (ns == null)
+ throw new Exception("No such namespace: " + symbol.Namespace);
+
+ Var v = ns.FindInternedVar(Symbol.create(symbol.Name));
+ if (v == null)
+ throw new Exception("No such var: " + symbol);
+ else if (v.Namespace != CurrentNamespace && !v.IsPublic && !allowPrivate)
+ throw new InvalidOperationException(string.Format("var: {0} is not public", symbol));
+ return v;
+ }
+ else if (symbol.Name.IndexOf('.') > 0 || symbol.Name[0] == '[')
+ return RT.classForName(symbol.Name);
+ else if (symbol.Equals(NS))
+ return RT.NS_VAR;
+ else if (symbol.Equals(IN_NS))
+ return RT.IN_NS_VAR;
+ else
+ {
+ object o = n.GetMapping(symbol);
+ if (o == null)
+ {
+ if (RT.booleanCast(RT.ALLOW_UNRESOLVED_VARS.deref()))
+ return symbol;
+ else
+ throw new Exception(string.Format("Unable to resolve symbol: {0} in this context", symbol));
+ }
+ return o;
+ }
+ }
+
+ // core.clj compatibility
+ public static object maybeResolveIn(Namespace n, Symbol symbol)
+ {
+ // note: ns-qualified vars must already exist
+ if (symbol.Namespace != null)
+ {
+ Namespace ns = NamespaceFor(n, symbol);
+ if (ns == null)
+ return null;
+
+ Var v = ns.FindInternedVar(Symbol.create(symbol.Name));
+ if (v == null)
+ return null;
+ return v;
+ }
+ else if (symbol.Name.IndexOf('.') > 0 || symbol.Name[0] == '[')
+ return RT.classForName(symbol.Name);
+ else if (symbol.Equals(NS))
+ return RT.NS_VAR;
+ else if (symbol.Equals(IN_NS))
+ return RT.IN_NS_VAR;
+ else
+ {
+ object o = n.GetMapping(symbol);
+ return o;
+ }
+ }
+
+ public static Namespace NamespaceFor(Symbol symbol)
+ {
+ return NamespaceFor(CurrentNamespace, symbol);
+ }
+
+ public static Namespace NamespaceFor(Namespace n, Symbol symbol)
+ {
+ // Note: presumes non-nil sym.ns
+ // first check against CurrentNamespace's aliases
+ Symbol nsSym = Symbol.create(symbol.Namespace);
+ Namespace ns = n.LookupAlias(nsSym);
+ if (ns == null)
+ // otherwise, check the namespaces map
+ ns = Namespace.find(nsSym);
+ return ns;
+ }
+
+ #endregion
+
+ #region Hooks from execution engine
+
+ public interface EEHooks
+ {
+ object Eval(object form);
+ object Macroexpand1(object form);
+ object LoadFromStream(TextReader rdr);
+ object LoadFile(string filename);
+ Delegate GenerateTypedDelegate(Type delegateType, Symbol optName, IPersistentVector argList, ISeq body);
+ }
+
+ // Needs to be initialized by the execution engine.
+ private static EEHooks _hooks;
+
+ public static bool SetHooks(EEHooks hooks)
+ {
+ return null == Interlocked.CompareExchange(ref _hooks,hooks,null);
+ }
+
+
+ // The following methods are named (and initial LC) for core.clj compatibility
+
+ public static object eval(object form)
+ {
+ ValidateHooks();
+
+ return _hooks.Eval(form);
+ }
+
+ public static object macroexpand1(object form)
+ {
+ ValidateHooks();
+
+ return _hooks.Macroexpand1(form);
+ }
+
+ public static object load(TextReader rdr)
+ {
+ ValidateHooks();
+ return _hooks.LoadFromStream(rdr);
+ }
+
+
+ // This one is mine.
+ public static object GenerateTypedDelegate(Type delegateType, Symbol optName, IPersistentVector argList, ISeq body)
+ {
+ ValidateHooks();
+ return _hooks.GenerateTypedDelegate(delegateType, optName, argList, body);
+ }
+
+ private static void ValidateHooks()
+ {
+ if ( _hooks == null )
+ throw new InvalidOperationException("Hooks from execution engine not set yet. Major blowage.");
+ }
+
+ #endregion
+
+ // Added in revision 1108, not sure where it is used. (Made public in rev 1109)
+ public static void pushNS()
+ {
+ Var.pushThreadBindings(PersistentHashMap.create(Var.intern(Symbol.create("clojure.core"),
+ Symbol.create("*ns*")), null));
+ }
+
+
+ // Java version has this in Reflector, but that is in my SimpleREPL. DOn't want to embed calls there.
+ public static Object prepRet(Object x)
+ {
+ // if(c == boolean.class)
+ // return ((Boolean) x).booleanValue() ? RT.T : null;
+ if (x is Boolean)
+ return ((Boolean)x) ? RT.T : RT.F;
+ return x;
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Cons.cs b/ClojureCLR/Clojure/Clojure/Lib/Cons.cs new file mode 100644 index 00000000..d88b5754 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Cons.cs @@ -0,0 +1,121 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Implements an immutable cons cell.
+ /// </summary>
+ public class Cons: ASeq
+ {
+ // Any reason not to seal this class?
+
+ #region Data
+
+ /// <summary>
+ /// Holds the first value. (= CAR)
+ /// </summary>
+ private readonly object _first;
+
+ /// <summary>
+ /// Holds the rest value. (= CDR)
+ /// </summary>
+ private readonly ISeq _rest;
+
+ #endregion
+
+ #region C-tors
+
+ /// <summary>
+ /// Initializes a <see cref="Cons">Cons</see> with the given metadata and first/rest.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ /// <param name="first">The first value.</param>
+ /// <param name="rest">The rest of the sequence.</param>
+ public Cons(IPersistentMap meta, object first, ISeq rest)
+ : base(meta)
+ {
+ _first = first;
+ _rest = rest;
+ }
+
+ /// <summary>
+ /// Initializes a <see cref="Cons">Cons</see> with null metadata and given first/rest.
+ /// </summary>
+ /// <param name="first">The first value.</param>
+ /// <param name="rest">The rest of the sequence.</param>
+ public Cons(object first, ISeq rest)
+ {
+ _first = first;
+ _rest = rest;
+ }
+
+
+
+ #endregion
+
+ #region IObj members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ // Java doesn't make the identity test: return new Cons(meta, _first, _rest);
+ return (meta == _meta)
+ ? this
+ : new Cons(meta, _first, _rest);
+ }
+
+ #endregion
+
+ #region ISeq members
+
+ /// <summary>
+ /// Gets the first item.
+ /// </summary>
+ /// <returns>The first item.</returns>
+ public override Object first()
+ {
+ return _first;
+ }
+
+ /// <summary>
+ /// Gets the rest of the sequence.
+ /// </summary>
+ /// <returns>The rest of the sequence, or <c>null</c> if no more elements.</returns>
+ public override ISeq rest()
+ {
+ return _rest;
+ }
+
+ #endregion
+
+ #region IPersistentCollection members
+
+ /// <summary>
+ /// Gets an ISeq to allow first/rest iteration through the collection.
+ /// </summary>
+ /// <returns>An ISeq for iteration.</returns>
+ public override ISeq seq()
+ {
+ return this;
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Counted.cs b/ClojureCLR/Clojure/Clojure/Lib/Counted.cs new file mode 100644 index 00000000..e08ace56 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Counted.cs @@ -0,0 +1,29 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// A promise that the class is a collection that implements a constant-time <see cref="Counted.count()">count()</see>.
+ /// </summary>
+ public interface Counted
+ {
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ int count();
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Delay.cs b/ClojureCLR/Clojure/Clojure/Lib/Delay.cs new file mode 100644 index 00000000..d771dd30 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Delay.cs @@ -0,0 +1,89 @@ +/**
+ * 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.Runtime.CompilerServices;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Implements a delay of a function call.
+ /// </summary>
+ public class Delay : IDeref
+ {
+ #region Data
+
+ /// <summary>
+ /// The value, after it has been computed.
+ /// </summary>
+ object _val;
+
+ /// <summary>
+ /// The function being delayed.
+ /// </summary>
+ IFn _fn;
+
+ #endregion
+
+ #region C-tors
+
+ /// <summary>
+ /// Construct a delay for a function.
+ /// </summary>
+ /// <param name="fn">The function to delay.</param>
+ public Delay(IFn fn)
+ {
+ _fn = fn;
+ _val = null;
+ }
+
+ #endregion
+
+ #region Delay operations
+
+ /// <summary>
+ /// Force a delay (or identity if not a delay).
+ /// </summary>
+ /// <param name="x">The object to force.</param>
+ /// <returns>The computed valued (if a delay); the object itself (if not a delay).</returns>
+ public static object force(object x)
+ {
+ return (x is Delay)
+ ? ((Delay)x).deref()
+ : x;
+ }
+
+ #endregion
+
+ #region IDeref Members
+
+ /// <summary>
+ /// Get the value.
+ /// </summary>
+ /// <returns>The value</returns>
+ /// <remarks>Forces the computation if it has not happened yet.</remarks>
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public object deref()
+ {
+ if (_fn != null)
+ {
+ _val = _fn.invoke();
+ _fn = null;
+ }
+ return _val;
+ }
+
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Fn.cs b/ClojureCLR/Clojure/Clojure/Lib/Fn.cs new file mode 100644 index 00000000..34227e91 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Fn.cs @@ -0,0 +1,25 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Marks an object that can be used in a functional position in a Clojure form.
+ /// </summary>
+ public interface Fn
+ {
+ // marker interface
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/FnSeq.cs b/ClojureCLR/Clojure/Clojure/Lib/FnSeq.cs new file mode 100644 index 00000000..022e46ee --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/FnSeq.cs @@ -0,0 +1,129 @@ +/**
+ * 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.Runtime.CompilerServices;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// A sequence that computes its first/rest from functional calls. Caches.
+ /// </summary>
+ public class FnSeq: ASeq
+ {
+ #region Data
+
+ /// <summary>
+ /// The first item.
+ /// </summary>
+ protected readonly object _first;
+
+ /// <summary>
+ /// The rest of the sequence.
+ /// </summary>
+ protected ISeq _rest;
+
+ /// <summary>
+ /// Function to compute first/rest.
+ /// </summary>
+ protected IFn _restFn;
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ // TODO: When we get typing, fix the parameter type
+ //public FnSeq(object first, IFn restFn)
+ /// <summary>
+ /// Initialize from given first and a restFn.
+ /// </summary>
+ /// <param name="first"></param>
+ /// <param name="restFn"></param>
+ public FnSeq(object first, object restFn)
+ {
+ _first = first;
+ _restFn = (IFn)restFn;
+ _rest = this;
+ }
+
+ /// <summary>
+ /// Initialize from given metatadata, plus first, restFn, rest.
+ /// </summary>
+ /// <param name="meta">The metadata to attach</param>
+ /// <param name="first">The first of the sequence.</param>
+ /// <param name="restFn">The function to generate the next value.</param>
+ /// <param name="rest">The rest of the sequence..</param>
+ FnSeq(IPersistentMap meta, object first, IFn restFn, ISeq rest)
+ : base(meta)
+ {
+ _first = first;
+ _restFn = restFn;
+ _rest = rest;
+ }
+
+ #endregion
+
+ #region ISeq methods
+
+ /// <summary>
+ /// Gets the first item.
+ /// </summary>
+ /// <returns>The first item.</returns>
+ public override object first()
+ {
+ return _first;
+ }
+
+ /// <summary>
+ /// Gets the rest of the sequence.
+ /// </summary>
+ /// <returns>The rest of the sequence, or <c>null</c> if no more elements.</returns>
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override ISeq rest()
+ {
+ if ( _restFn != null )
+ {
+ try
+ {
+ _rest = (ISeq)_restFn.invoke();
+ }
+ catch ( Exception ex )
+ {
+ throw new InvalidOperationException("Error invoking rest function in FnSeq: " + ex.Message, ex);
+ }
+ _restFn = null;
+ }
+ return _rest;
+ }
+
+ #endregion
+
+ #region IObj methods
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ if (meta == _meta)
+ return this;
+ //force eval of restFn before copying
+ rest();
+ return new FnSeq(meta, _first, _restFn, _rest);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/FuncTypes.cs b/ClojureCLR/Clojure/Clojure/Lib/FuncTypes.cs new file mode 100644 index 00000000..78eb90af --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/FuncTypes.cs @@ -0,0 +1,100 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ public delegate TResult FFunc<TResult>();
+ public delegate TResult FFunc<T, TResult>(T arg);
+ public delegate TResult FFunc<T1, T2, TResult>(T1 arg1, T2 arg2);
+ public delegate TResult FFunc<T1, T2, T3, TResult>(T1 arg1, T2 arg2, T3 arg3);
+ public delegate TResult FFunc<T1, T2, T3, T4, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
+ public delegate TResult FFunc<T1, T2, T3, T4, T5, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
+ public delegate TResult FFunc<T1, T2, T3, T4, T5, T6, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
+ public delegate TResult FFunc<T1, T2, T3, T4, T5, T6, T7, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
+ public delegate TResult FFunc<T1, T2, T3, T4, T5, T6, T7, T8, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
+ public delegate TResult FFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9);
+ public delegate TResult FFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10);
+ public delegate TResult FFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11);
+ public delegate TResult FFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12);
+ public delegate TResult FFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13);
+ public delegate TResult FFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14);
+ public delegate TResult FFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15);
+ public delegate TResult FFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16);
+ public delegate TResult FFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17);
+ public delegate TResult FFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18);
+ public delegate TResult FFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19);
+ public delegate TResult FFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19, T20 arg20);
+ public delegate TResult FFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19, T20 arg20, T21 arg21);
+
+
+
+ public delegate TResult VFunc<V, TResult>(params V[] argrest);
+ public delegate TResult VFunc<T1, V, TResult>(T1 arg1, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, V, TResult>(T1 arg1, T2 arg2, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, V, TResult>(T1 arg1, T2 arg2, T3 arg3, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, T4, V, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, T4, T5, V, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, T4, T5, T6, V, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, T4, T5, T6, T7, V, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, T4, T5, T6, T7, T8, V, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, V, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, V, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, V, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, V, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, V, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, V, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, V, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, V, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, V, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, V, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, V, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19, params V[] argrest);
+ public delegate TResult VFunc<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, V, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19, T20 arg20, params V[] argrest);
+
+
+ public sealed class FuncTypeHelpers
+ {
+ public static Type GetFFuncType(int numArgs)
+ {
+ switch (numArgs)
+ {
+ case 0: return typeof(FFunc<object>);
+ case 1: return typeof(FFunc<object,object>);
+ case 2: return typeof(FFunc<object, object, object>);
+ case 3: return typeof(FFunc<object, object, object, object>);
+ case 4: return typeof(FFunc<object, object, object, object, object>);
+ case 5: return typeof(FFunc<object, object, object, object, object, object>);
+ case 6: return typeof(FFunc<object, object, object, object, object, object, object>);
+ case 7: return typeof(FFunc<object, object, object, object, object, object, object, object>);
+ case 8: return typeof(FFunc<object, object, object, object, object, object, object, object, object>);
+ case 9: return typeof(FFunc<object, object, object, object, object, object, object, object, object, object>);
+ case 10: return typeof(FFunc<object, object, object, object, object, object, object, object, object, object, object>);
+ case 11: return typeof(FFunc<object, object, object, object, object, object, object, object, object, object, object, object>);
+ case 12: return typeof(FFunc<object, object, object, object, object, object, object, object, object, object, object, object, object>);
+ case 13: return typeof(FFunc<object, object, object, object, object, object, object, object, object, object, object, object, object, object>);
+ case 14: return typeof(FFunc<object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>);
+ case 15: return typeof(FFunc<object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>);
+ case 16: return typeof(FFunc<object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>);
+ case 17: return typeof(FFunc<object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>);
+ case 18: return typeof(FFunc<object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>);
+ case 19: return typeof(FFunc<object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>);
+ case 20: return typeof(FFunc<object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>);
+
+ default: return typeof(FFunc<object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object[], object>);
+;
+ }
+ }
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/IDeref.cs b/ClojureCLR/Clojure/Clojure/Lib/IDeref.cs new file mode 100644 index 00000000..f6c89bc7 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/IDeref.cs @@ -0,0 +1,29 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents something that can dereference.
+ /// </summary>
+ public interface IDeref
+ {
+ /// <summary>
+ /// Gets the (immutable) value the reference is holding.
+ /// </summary>
+ /// <returns>The value</returns>
+ object deref();
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/IFn.cs b/ClojureCLR/Clojure/Clojure/Lib/IFn.cs new file mode 100644 index 00000000..61e0947d --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/IFn.cs @@ -0,0 +1,77 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents an object that can be used a function.
+ /// </summary>
+ public interface IFn // Callable, Runnable -- are there Java equivalents? --
+ // there is a notion in DLR of an attribute to method when the object is used in a functional position.
+ {
+
+ object invoke();
+ object invoke(object arg1);
+ object invoke(object arg1, object arg2);
+ object invoke(object arg1, object arg2, object arg3);
+ object invoke(object arg1, object arg2, object arg3, object arg4);
+ object invoke(object arg1, object arg2, object arg3, object arg4, object arg5);
+ object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6);
+ object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7);
+ object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8);
+ object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9);
+ object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10);
+ object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11);
+ object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12);
+ object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13);
+ object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14);
+ object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15);
+
+ // I'm not sure how far down the list we should go. There may be some DLR limits.
+ // Also the Microsoft.Func<...> delegate types only go so far.
+
+ object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16);
+ object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17);
+ object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17, object arg18);
+ object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17, object arg18, object arg19);
+ object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17, object arg18, object arg19, object arg20);
+
+ object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17, object arg18, object arg19, object arg20,
+ params object[] args);
+
+ object applyTo(ISeq arglist);
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/IMapEntry.cs b/ClojureCLR/Clojure/Clojure/Lib/IMapEntry.cs new file mode 100644 index 00000000..26427e85 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/IMapEntry.cs @@ -0,0 +1,41 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Defines a key/value pair. Immutable.
+ /// </summary>
+ /// <remarks>
+ /// <para>Lowercase-named methods for JVM compatibility.</para>
+ /// <para>In JVM version, this interface extends Map.Entry. The equivalent BCL type is either <c>KeyValuePair<object,object></c>
+ /// or <c>DictionaryEntry</c>
+ /// both of which are structs and hence can't be derived from.</para>
+ /// </remarks>
+ public interface IMapEntry
+ {
+ /// <summary>
+ /// Get the key in a key/value pair.
+ /// </summary>
+ /// <returns>The key.</returns>
+ object key();
+
+ /// <summary>
+ /// Get the value in a key/value pair.
+ /// </summary>
+ /// <returns>The value.</returns>
+ object val();
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/IMeta.cs b/ClojureCLR/Clojure/Clojure/Lib/IMeta.cs new file mode 100644 index 00000000..d8bf27b4 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/IMeta.cs @@ -0,0 +1,29 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents an object that can have metadata attached.
+ /// </summary>
+ public interface IMeta
+ {
+ /// <summary>
+ /// Gets the metadata attached to the object.
+ /// </summary>
+ /// <returns>An immutable map representing the object's metadata.</returns>
+ IPersistentMap meta();
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/IObj.cs b/ClojureCLR/Clojure/Clojure/Lib/IObj.cs new file mode 100644 index 00000000..324538cf --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/IObj.cs @@ -0,0 +1,30 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents an object that creates a copy with new metadata.
+ /// </summary>
+ public interface IObj : IMeta
+ {
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ IObj withMeta(IPersistentMap meta);
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/IPersistentCollection.cs b/ClojureCLR/Clojure/Clojure/Lib/IPersistentCollection.cs new file mode 100644 index 00000000..b420e9a0 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/IPersistentCollection.cs @@ -0,0 +1,58 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents an immutable collection.
+ /// </summary>
+ /// <remarks>
+ /// <para>Lowercase-named methods for compatibility with JVM code.</para>
+ /// </remarks>
+ public interface IPersistentCollection
+ {
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ int count();
+
+ /// <summary>
+ /// Gets an <see cref="ISeq">ISeq</see> to allow first/rest iteration through the collection.
+ /// </summary>
+ /// <returns>An <see cref="ISeq">ISeq</see> for iteration.</returns>
+ ISeq seq();
+
+ /// <summary>
+ /// Returns a new collection that has the given element cons'd on front of the existing collection.
+ /// </summary>
+ /// <param name="o">An item to put at the front of the collection.</param>
+ /// <returns>A new immutable collection with the item added.</returns>
+ IPersistentCollection cons(object o);
+
+ /// <summary>
+ /// Gets an empty collection of the same type.
+ /// </summary>
+ /// <returns>An emtpy collection.</returns>
+ IPersistentCollection empty();
+
+ /// <summary>
+ /// Determine if an object is equivalent to this (handles all collections).
+ /// </summary>
+ /// <param name="o">The object to compare.</param>
+ /// <returns><c>true</c> if the object is equivalent; <c>false</c> otherwise.</returns>
+ bool equiv(object o);
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/IPersistentList.cs b/ClojureCLR/Clojure/Clojure/Lib/IPersistentList.cs new file mode 100644 index 00000000..50790f9e --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/IPersistentList.cs @@ -0,0 +1,25 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents an immutable list. (sequential + stack + collection)
+ /// </summary>
+ public interface IPersistentList: Sequential, IPersistentStack
+ {
+ // empty
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/IPersistentMap.cs b/ClojureCLR/Clojure/Clojure/Lib/IPersistentMap.cs new file mode 100644 index 00000000..e5b4c2f4 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/IPersistentMap.cs @@ -0,0 +1,68 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents an immutable map (key/value collection).
+ /// </summary>
+ public interface IPersistentMap: Associative, IEnumerable<IMapEntry>, Counted
+ {
+ /// <summary>
+ /// Add a new key/value pair.
+ /// </summary>
+ /// <param name="key">The key</param>
+ /// <param name="val">The value</param>
+ /// <returns>A new map with key+value added.</returns>
+ /// <remarks>Overwrites an exising value for the <paramref name="key"/>, if present.</remarks>
+ new IPersistentMap assoc(object key, object val);
+
+ /// <summary>
+ /// Add a new key/value pair.
+ /// </summary>
+ /// <param name="key">The key</param>
+ /// <param name="val">The value</param>
+ /// <returns>A new map with key+value added.</returns>
+ /// <remarks>Throws an exception if <paramref name="key"/> has a value already.</remarks>
+ IPersistentMap assocEx(object key, object val);
+
+ /// <summary>
+ /// Remove a key entry.
+ /// </summary>
+ /// <param name="key">The key to remove</param>
+ /// <returns>A new map with the key removed (or the same map if the key is not contained).</returns>
+ IPersistentMap without(object key);
+
+ /// <summary>
+ /// Add a new key/value pair.
+ /// </summary>
+ /// <param name="obj">The key/value pair to add.</param>
+ /// <returns>A new map with key+value pair added.</returns>
+ /// <remarks><para>Overrides <c>cons</c> in <see cref="IPersistentCollection">IPersistentCollection</see>
+ /// to specialize the return value.</para>
+ /// <para>The object can be many things representing a key/value pair, including <c>DictionaryEntry</c>s,
+ /// <c>KeyValuePair<,></c>, an <see cref="IMapEntry">IMapEntry</see>, an <see cref="IPersistentVector">IPersistentVector</see>
+ /// of two elements, etc.</para></remarks>
+ new IPersistentMap cons(object obj);
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ /// <remarks>Overrides <c>count()</c> in both <see cref="IPersistentCollection">IPersistentCollection</see>
+ /// and <see cref="Counted">Counted</see> to resolve ambiguity for callers.</remarks>
+ new int count();
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/IPersistentSet.cs b/ClojureCLR/Clojure/Clojure/Lib/IPersistentSet.cs new file mode 100644 index 00000000..4559ee5e --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/IPersistentSet.cs @@ -0,0 +1,52 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents an immutable set (collection of unique elements).
+ /// </summary>
+ public interface IPersistentSet: IPersistentCollection, Counted
+ {
+ /// <summary>
+ /// Get a set with the given item removed.
+ /// </summary>
+ /// <param name="key">The item to remove.</param>
+ /// <returns>A new set with the item removed.</returns>
+ IPersistentSet disjoin(object key);
+
+ /// <summary>
+ /// Test if the set contains the key.
+ /// </summary>
+ /// <param name="key">The value to test for membership in the set.</param>
+ /// <returns>True if the item is in the collection; false, otherwise.</returns>
+ bool contains(object key);
+
+ /// <summary>
+ /// Get the value for the key (= the key itself, or null if not present).
+ /// </summary>
+ /// <param name="key">The value to test for membership in the set.</param>
+ /// <returns>the key if the key is in the set, else null.</returns>
+ object get(object key);
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ /// <remarks>Overrides <c>count()</c> in both <see cref="IPersistentCollection">IPersistentCollection</see>
+ /// and <see cref="Counted">Counted</see> to resolve ambiguity for callers.</remarks>
+ new int count();
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/IPersistentStack.cs b/ClojureCLR/Clojure/Clojure/Lib/IPersistentStack.cs new file mode 100644 index 00000000..63015566 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/IPersistentStack.cs @@ -0,0 +1,35 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents an immutable collection with stack operations.
+ /// </summary>
+ public interface IPersistentStack: IPersistentCollection
+ {
+ /// <summary>
+ /// Peek at the top (first) element in the stack.
+ /// </summary>
+ /// <returns>The top (first) element.</returns>
+ object peek();
+
+ /// <summary>
+ /// Returns a new stack with the top element popped.
+ /// </summary>
+ /// <returns>The new stack.</returns>
+ IPersistentStack pop();
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/IPersistentVector.cs b/ClojureCLR/Clojure/Clojure/Lib/IPersistentVector.cs new file mode 100644 index 00000000..1ea614c1 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/IPersistentVector.cs @@ -0,0 +1,62 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents an immutable vector (int-indexing).
+ /// </summary>
+ public interface IPersistentVector: Associative, Sequential, IPersistentStack, Reversible, Counted
+ {
+ /// <summary>
+ /// Gets the number of items in the vector.
+ /// </summary>
+ /// <returns>The number of items.</returns>
+ /// <remarks>Not sure why you wouldn't use <c>count()</c> intead.</remarks>
+ int length();
+
+ /// <summary>
+ /// Get the i-th item in the vector.
+ /// </summary>
+ /// <param name="i">The index of the item to retrieve/</param>
+ /// <returns>The i-th item</returns>
+ /// <remarks>Throws an exception if the index <c>i</c> is not in the range of the vector's elements.</remarks>
+ object nth(int i);
+
+ /// <summary>
+ /// Return a new vector with the i-th value set to <c>val</c>.
+ /// </summary>
+ /// <param name="i">The index of the item to set.</param>
+ /// <param name="val">The new value</param>
+ /// <returns>A new (immutable) vector v with v[i] == val.</returns>
+ IPersistentVector assocN(int i, object val);
+
+ /// <summary>
+ /// Creates a new vector with a new item at the end.
+ /// </summary>
+ /// <param name="o">The item to add to the vector.</param>
+ /// <returns>A new (immutable) vector with the objected added at the end.</returns>
+ /// <remarks>Overrides <c>cons</c> in <see cref="IPersistentCollection">IPersistentCollection</see> to specialize the return value.</remarks>
+ new IPersistentVector cons(Object o);
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ /// <remarks>Overrides <c>count()</c> in both <see cref="IPersistentCollection">IPersistentCollection</see>
+ /// and <see cref="Counted">Counted</see> to resolve ambiguity for callers.</remarks>
+ new int count();
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/IReduce.cs b/ClojureCLR/Clojure/Clojure/Lib/IReduce.cs new file mode 100644 index 00000000..b1fac022 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/IReduce.cs @@ -0,0 +1,41 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents a collection that supports function mapping/reduction.
+ /// </summary>
+ public interface IReduce
+ {
+ /// <summary>
+ /// Reduce the collection using a function.
+ /// </summary>
+ /// <param name="f">The function to apply.</param>
+ /// <returns>The reduced value</returns>
+ /// <remarks>Computes f(...f(f(f(i0,i1),i2),i3),...).</remarks>
+ object reduce(IFn f);
+
+
+ /// <summary>
+ /// Reduce the collection using a function.
+ /// </summary>
+ /// <param name="f">The function to apply.</param>
+ /// <param name="start">An initial value to get started.</param>
+ /// <returns>The reduced value</returns>
+ /// <remarks>Computes f(...f(f(f(start,i0),i1),i2),...).</remarks>
+ object reduce(IFn f, object start);
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/IRef.cs b/ClojureCLR/Clojure/Clojure/Lib/IRef.cs new file mode 100644 index 00000000..f4b2a094 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/IRef.cs @@ -0,0 +1,78 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents a reference to a value.
+ /// </summary>
+ /// <remarks>
+ /// <para><see cref="IRef">IRef</see> is the basic interface supported
+ /// by <see cref="Ref">Ref</see>s, <see cref="Agent">Agent</see>s,
+ /// <see cref="Atom">Atom</see>s, <see cref="Var">Var</see>s, and
+ /// other references to values. Many</para>
+ /// <para>This interface supports
+ /// getting/setting the validator for the value, and getting/setting watchers.
+ /// Dereferencing is supplied in interface <see cref="IDeref">IDeref</see>.
+ /// This interface does not support changes to values. Changes are the responsibility of the implementations of this interface,
+ /// and often have to be done in concert with <see cref="LockingTransaction">LockingTransaction</see>.</para>
+ /// <para>The validator function will be applied to any new value before that value is applied.
+ /// If the validator throws an exception or returns false, changing the reference to the new value is aborted.
+ /// When setting a new validator, it must validate the current value.</para>
+ /// <para>A reference can be watched by one or more <see cref="Agent">Agent</see>s. The agent will be sent a message when the value changes.</para>
+ /// </remarks>
+ public interface IRef : IDeref
+ {
+ /// <summary>
+ /// Sets the validator.
+ /// </summary>
+ /// <param name="vf">The new validtor</param>
+ void setValidator(IFn vf);
+
+ /// <summary>
+ /// Gets the validator.
+ /// </summary>
+ /// <returns>The current validator.</returns>
+ IFn getValidator();
+
+
+ /// <summary>
+ /// Gets a map of watchers (key=Agent, value=IFn).
+ /// </summary>
+ /// <returns>A (immutable) map of watchers (key=Agent, value=IFn). </returns>
+ IPersistentMap getWatches();
+
+ /// <summary>
+ /// Adds a new watcher.
+ /// </summary>
+ /// <param name="watcher">The <see cref="Agent">Agent</see> doing the watching.</param>
+ /// <param name="action">The 'message' to send when the value changes.</param>
+ /// <param name="sendOff">If true, use <see cref="Agent.sendOff">send-off</see> to send the message, else use <see cref="Agent.send()">send</see>.</param>
+ /// <returns></returns>
+ IRef addWatch(Agent watcher, IFn action, bool sendOff);
+
+ /// <summary>
+ /// Remove a watcher.
+ /// </summary>
+ /// <param name="watcher">The <see cref="Agent">Agent</see> to be removed.</param>
+ /// <returns>This IRef (for chaining).</returns>
+ IRef removeWatch(Agent watcher);
+
+ /// <summary>
+ /// Notify all watchers.
+ /// </summary>
+ void notifyWatches();
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/IReference.cs b/ClojureCLR/Clojure/Clojure/Lib/IReference.cs new file mode 100644 index 00000000..9950f0ab --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/IReference.cs @@ -0,0 +1,39 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents an object with settable metadata.
+ /// </summary>
+ public interface IReference : IMeta
+ {
+ /// <summary>
+ /// Alter the metadata on the object.
+ /// </summary>
+ /// <param name="alter">A function to apply to generate the new metadata</param>
+ /// <param name="args">Arguments to apply the function to.</param>
+ /// <returns>The new metadata map.</returns>
+ /// <remarks>The new value will be the result of <c>(apply alter (cons currentMeta args))</c>.</remarks>
+ IPersistentMap alterMeta(IFn alter, ISeq args);
+
+ /// <summary>
+ /// Set the metadata of the object.
+ /// </summary>
+ /// <param name="m">The new metadata map</param>
+ /// <returns>The new metadata map.</returns>
+ IPersistentMap resetMeta(IPersistentMap m);
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/ISeq.cs b/ClojureCLR/Clojure/Clojure/Lib/ISeq.cs new file mode 100644 index 00000000..6624011e --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/ISeq.cs @@ -0,0 +1,54 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents an immutable first/rest sequence.
+ /// </summary>
+ /// <remarks><para>Being a non-null ISeq implies that there is at least one element.
+ /// A null value for <c>rest()</c> implies the end of the sequence.</para>
+ /// A standard iteration is of the form:
+ /// <code>
+ /// for ( ISeq s = init; s != null; s = s.rest() )
+ /// {
+ /// ... s.first() ...
+ /// }
+ /// </code>
+ /// </remarks>
+ public interface ISeq: IPersistentCollection, Sequential
+ {
+ /// <summary>
+ /// Gets the first item.
+ /// </summary>
+ /// <returns>The first item.</returns>
+ object first();
+
+ /// <summary>
+ /// Gets the rest of the sequence.
+ /// </summary>
+ /// <returns>The rest of the sequence, or <c>null</c> if no more elements.</returns>
+ ISeq rest();
+
+ /// <summary>
+ /// Adds an item to the beginning of the sequence.
+ /// </summary>
+ /// <param name="o">The item to add.</param>
+ /// <returns>A new sequence containing the new item in front of the items already in the sequence.</returns>
+ /// <remarks>This overrides the <c>cons</c> method in <see cref="IPersistentCollection">IPersistentCollection</see>
+ /// by giving an <see cref="ISeq">ISeq</see> in return.</remarks>
+ new ISeq cons(object o);
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/IStream.cs b/ClojureCLR/Clojure/Clojure/Lib/IStream.cs new file mode 100644 index 00000000..fe1e328b --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/IStream.cs @@ -0,0 +1,29 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents a stream of values.
+ /// </summary>
+ public interface IStream
+ {
+ /// <summary>
+ /// Get the next value in the stream.
+ /// </summary>
+ /// <returns>The next value.</returns>
+ object next();
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/IndexedSeq.cs b/ClojureCLR/Clojure/Clojure/Lib/IndexedSeq.cs new file mode 100644 index 00000000..e86fc654 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/IndexedSeq.cs @@ -0,0 +1,29 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Indicates a sequence that has a current index.
+ /// </summary>
+ public interface IndexedSeq : ISeq, Counted
+ {
+ /// <summary>
+ /// Gets the index associated with this sequence.
+ /// </summary>
+ /// <returns>The index associated with this sequence.</returns>
+ int index();
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/IteratorStream.cs b/ClojureCLR/Clojure/Clojure/Lib/IteratorStream.cs new file mode 100644 index 00000000..7011e969 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/IteratorStream.cs @@ -0,0 +1,66 @@ +/**
+ * 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.Collections;
+using System.Runtime.CompilerServices;
+
+namespace clojure.lang
+{
+
+ /// <summary>
+ /// Implements a stream running over an IEnumerator.
+ /// </summary>
+ public class IteratorStream : IStream
+ {
+ #region Data
+
+ /// <summary>
+ /// The IEnumerator being streamed.
+ /// </summary>
+ private readonly IEnumerator _iter;
+
+ #endregion
+
+ #region Ctors and factory methods
+
+ /// <summary>
+ /// Constructs an <see cref="IteratorStream">IteratorStream</see> over an IEnumerator.
+ /// </summary>
+ /// <param name="iter">The IEnumerator to stream over.</param>
+ public IteratorStream(IEnumerator iter)
+ {
+ _iter = iter;
+ }
+
+ #endregion
+
+ #region IStream Members
+
+ /// <summary>
+ /// Get the next value in the stream.
+ /// </summary>
+ /// <returns>The next value.</returns>
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public object next()
+ {
+ if (_iter.MoveNext())
+ return _iter.Current;
+ return RT.eos();
+ }
+
+
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/JavaConcurrentDictionary.cs b/ClojureCLR/Clojure/Clojure/Lib/JavaConcurrentDictionary.cs new file mode 100644 index 00000000..40d6bf24 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/JavaConcurrentDictionary.cs @@ -0,0 +1,79 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Faking a few of the methods from the Java ConcurrentHashTable class.
+ /// </summary>
+ public class JavaConcurrentDictionary<TKey, TValue>
+ {
+ Dictionary<TKey, TValue> _dict = new Dictionary<TKey, TValue>();
+
+ public TValue Get(TKey key)
+ {
+ lock (_dict)
+ {
+ TValue val;
+ return _dict.TryGetValue(key, out val) ? val : default(TValue);
+ }
+ }
+
+ public TValue PutIfAbsent(TKey key, TValue val)
+ {
+ lock (_dict)
+ {
+ TValue existingVal;
+ if (_dict.TryGetValue(key, out existingVal))
+ return existingVal;
+ else
+ {
+ _dict[key] = val;
+ return default(TValue);
+ }
+ }
+ }
+
+ public TValue Remove(TKey key)
+ {
+ lock ( _dict )
+ {
+ TValue existingVal;
+ if ( _dict.TryGetValue(key,out existingVal) )
+ {
+ _dict.Remove(key);
+ return existingVal;
+ }
+ else
+ return default(TValue);
+ }
+ }
+
+ public TValue[] Values
+ {
+ get
+ {
+ lock (_dict)
+ {
+ Dictionary<TKey, TValue>.ValueCollection coll = _dict.Values;
+ TValue[] values = new TValue[coll.Count];
+ coll.CopyTo(values, 0);
+ return values;
+ }
+ }
+ }
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Keyword.cs b/ClojureCLR/Clojure/Clojure/Lib/Keyword.cs new file mode 100644 index 00000000..435af737 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Keyword.cs @@ -0,0 +1,206 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents a keyword
+ /// </summary>
+ public class Keyword: AFn, Named, IComparable // ??JAVA only used IFn, not AFn. NOt sure why.
+ {
+ #region Data
+
+ /// <summary>
+ /// The symbol giving the namespace/name (without :) of the keyword.
+ /// </summary>
+ private readonly Symbol _sym;
+
+ /// <summary>
+ /// Map from symbol to keyword to uniquify keywords.
+ /// </summary>
+ /// <remarks>Why introduce the JavaConcurrentDictionary?
+ /// We really only need a synchronized hash table with one operation: PutIfAbsent.</remarks>
+ private static JavaConcurrentDictionary<Symbol,Keyword> _symKeyMap
+ = new JavaConcurrentDictionary<Symbol,Keyword>();
+
+ protected Symbol Symbol
+ {
+ get { return _sym; }
+ }
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ /// <summary>
+ /// Create (or find existing) keyword with given symbol's namespace/name.
+ /// </summary>
+ /// <param name="sym">The symbol giving the keyword's namespace/name.</param>
+ /// <returns>A keyword</returns>
+ public static Keyword intern(Symbol sym)
+ {
+ Keyword k = new Keyword(sym);
+ Keyword existing = _symKeyMap.PutIfAbsent(sym, k);
+ return existing == null ? k : existing;
+ }
+
+ /// <summary>
+ /// Create (or find existing) keyword with given namespace/name.
+ /// </summary>
+ /// <param name="ns">The keyword's namespace name.</param>
+ /// <param name="name">The keyword's name.</param>
+ /// <returns>A keyword</returns>
+ public static Keyword intern(string ns, string name)
+ {
+ return intern(Symbol.intern(ns, name));
+ }
+
+ /// <summary>
+ /// Construct a keyword based on a symbol.
+ /// </summary>
+ /// <param name="sym">A symbol giving namespace/name.</param>
+ private Keyword(Symbol sym)
+ {
+ this._sym = sym;
+ }
+
+
+ #endregion
+
+ #region Object overrides
+
+ /// <summary>
+ /// Returns a string representing the keyword.
+ /// </summary>
+ /// <returns>A string representing the keyword.</returns>
+ public override string ToString()
+ {
+ return ":" + _sym.ToString();
+ }
+
+ /// <summary>
+ /// Determines if an object is equal to this keyword. Value semantics.
+ /// </summary>
+ /// <param name="obj">The object to compare to.</param>
+ /// <returns><value>true</value> if equal; <value>false</value> otherwise.</returns>
+ public override bool Equals(object obj)
+ {
+ if ( obj == this )
+ return true;
+
+ Keyword keyword = obj as Keyword;
+
+ if (keyword == null)
+ return false;
+
+ return _sym.Equals(keyword.Symbol);
+ }
+
+ /// <summary>
+ /// Gets a hash code for the keyword.
+ /// </summary>
+ /// <returns>A hash code.</returns>
+ public override int GetHashCode()
+ {
+ return Util.HashCombine(0,_sym.GetHashCode());
+ }
+
+ #endregion
+
+ #region Named Members
+
+ // I prefer to use the following internally.
+
+ /// <summary>
+ /// The namespace name.
+ /// </summary>
+ public string Namespace
+ {
+ get { return _sym.Namespace; }
+ }
+
+ /// <summary>
+ /// The name.
+ /// </summary>
+ public string Name
+ {
+ get { return _sym.Name; }
+ }
+
+ // the following are in the interface
+
+
+ /// <summary>
+ /// Gets the namespace name.
+ /// </summary>
+ /// <returns>The namespace name.</returns>
+ public string getNamespace()
+ {
+ return _sym.Namespace;
+ }
+
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <returns>The name.</returns>
+ public string getName()
+ {
+ return _sym.Name;
+ }
+
+ #endregion
+
+ #region IFn Members
+
+ /// <summary>
+ /// (:keyword arg) => (get arg :keyword)
+ /// </summary>
+ /// <param name="arg1">The object to access.</param>
+ /// <returns>The value mapped to the keyword.</returns>
+ public override object invoke(object arg1)
+ {
+ return RT.get(arg1, this);
+ }
+
+
+ /// <summary>
+ /// (:keyword arg default) => (get arg :keyword default)
+ /// </summary>
+ /// <param name="arg1">The object to access.</param>
+ /// <param name="arg2">Default value if not found.</param>
+ /// <returns></returns>
+ public override object invoke(object arg1, object arg2)
+ {
+ return RT.get(arg1, this, arg2);
+ }
+
+
+ #endregion
+
+ #region IComparable Members
+
+ /// <summary>
+ /// Compare this to another object.
+ /// </summary>
+ /// <param name="obj">The object to compare to.</param>
+ /// <returns>neg,zero,pos for < = ></returns>
+ public int CompareTo(object obj)
+ {
+ return _sym.CompareTo(((Keyword)obj)._sym);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/LazilyPersistentVector.cs b/ClojureCLR/Clojure/Clojure/Lib/LazilyPersistentVector.cs new file mode 100644 index 00000000..9022becd --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/LazilyPersistentVector.cs @@ -0,0 +1,200 @@ +/**
+ * 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.Runtime.CompilerServices;
+
+namespace clojure.lang
+{
+
+ /// <summary>
+ /// A persistent vector based on an array. Holds a lazily-allocated <see cref="PersistentVector">PersistentVector</see>
+ /// if operations such as <see cref="LazilyPersistentVector.assoc()">assoc()</see>
+ /// are called that require a true persistent collection.
+ /// </summary>
+ public class LazilyPersistentVector: APersistentVector
+ {
+ #region Data
+
+ /// <summary>
+ /// The array with the items.
+ /// </summary>
+ protected readonly object[] _array;
+
+ /// <summary>
+ /// The lazily-allocated persistent vector for generative operations.
+ /// </summary>
+ PersistentVector _v = null;
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ /// <summary>
+ /// Create a <see cref="LazilyPersistentVector">LazilyPersistentVector</see> for an array of items.
+ /// </summary>
+ /// <param name="items">An array of items</param>
+ /// <returns>A <see cref="LazilyPersistentVector">LazilyPersistentVector</see>.</returns>
+ static public IPersistentVector createOwning(params object[] items)
+ {
+ return items.Length == 0
+ ? (IPersistentVector) PersistentVector.EMPTY
+ : new LazilyPersistentVector(null, items, null);
+ }
+
+ /// <summary>
+ /// Create a <see cref="LazilyPersistentVector">LazilyPersistentVector</see> from an ICollection of items.
+ /// </summary>
+ /// <param name="coll">The collection of items.</param>
+ /// <returns>A <see cref="LazilyPersistentVector">LazilyPersistentVector</see>.</returns>
+ static public IPersistentVector create(System.Collections.ICollection coll)
+ {
+ object[] array = new object[coll.Count];
+ coll.CopyTo(array, 0);
+ return createOwning(array);
+ }
+
+ /// <summary>
+ /// Constructs a lazily persistent vector.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ /// <param name="array">The array of values.</param>
+ /// <param name="v">The accompanying persistent vector.</param>
+ LazilyPersistentVector(IPersistentMap meta, object[] array, PersistentVector v)
+ : base(meta)
+ {
+ _array = array;
+ _v = v;
+ }
+
+ #endregion
+
+ #region IObj members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return (meta == _meta)
+ ? this
+ : new LazilyPersistentVector(meta, _array, _v);
+ }
+
+ #endregion
+
+ #region IPersistentVector members
+
+ /// <summary>
+ /// Get the i-th item in the vector.
+ /// </summary>
+ /// <param name="i">The index of the item to retrieve/</param>
+ /// <returns>The i-th item</returns>
+ /// <remarks>Throws an exception if the index <c>i</c> is not in the range of the vector's elements.</remarks>
+ public override object nth(int i)
+ {
+ return _array[i];
+ }
+
+ /// <summary>
+ /// Return a new vector with the i-th value set to <c>val</c>.
+ /// </summary>
+ /// <param name="i">The index of the item to set.</param>
+ /// <param name="val">The new value</param>
+ /// <returns>A new (immutable) vector v with v[i] == val.</returns>
+ public override IPersistentVector assocN(int i, object val)
+ {
+ return (IPersistentVector)Vec.assoc(i, val);
+ }
+
+
+ /// <summary>
+ /// Creates a new vector with a new item at the end.
+ /// </summary>
+ /// <param name="o">The item to add to the vector.</param>
+ /// <returns>A new (immutable) vector with the objected added at the end.</returns>
+ /// <remarks>Overrides <c>cons</c> in <see cref="IPersistentCollection">IPersistentCollection</see> to specialize the return value.</remarks>
+ public override IPersistentVector cons(object o)
+ {
+ return Vec.cons(o);
+ }
+
+ /// <summary>
+ /// Gets the number of items in the vector.
+ /// </summary>
+ /// <returns>The number of items.</returns>
+ /// <remarks>Not sure why you wouldn't use <c>count()</c> intead.</remarks>
+ public override int length()
+ {
+ return count();
+ }
+
+ #endregion
+
+ #region IPersistentCollection members
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ public override int count()
+ {
+ return _array.Length;
+ }
+
+ /// <summary>
+ /// Gets an empty collection of the same type.
+ /// </summary>
+ /// <returns>An emtpy collection.</returns>
+ public override IPersistentCollection empty()
+ {
+ return (IPersistentCollection)PersistentVector.EMPTY.withMeta(meta());
+ }
+
+ #endregion
+
+ #region IPersistentStack members
+
+ /// <summary>
+ /// Peek at the top (first) element in the stack.
+ /// </summary>
+ /// <returns>The top (first) element.</returns>
+ public override IPersistentStack pop()
+ {
+ return Vec.pop();
+ }
+
+ #endregion
+
+ #region Internals
+
+ /// <summary>
+ /// Gets the lazy vector, creating it if necessary.
+ /// </summary>
+ private IPersistentVector Vec
+ {
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ get
+ {
+ if (_v == null)
+ _v = PersistentVector.create(_array);
+ return _v;
+ }
+ }
+
+ #endregion
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/LazyCons.cs b/ClojureCLR/Clojure/Clojure/Lib/LazyCons.cs new file mode 100644 index 00000000..fd9804c9 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/LazyCons.cs @@ -0,0 +1,141 @@ +/**
+ * 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.Runtime.CompilerServices;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// A cons cell constructed lazily by a call to an <see cref="Ifn">IFn</see>.
+ /// </summary>
+ /// <remarks>
+ /// The 'first' is computed by calling <c>f()</c>. The 'rest' is computed by calling <c>f(null)</c>.
+ /// </remarks>
+ public class LazyCons: ASeq
+ {
+ #region Data
+
+ /// <summary>
+ /// A special value indicating that first/rest have not been initialized.
+ /// </summary>
+ private static readonly ISeq sentinel = new Cons(null, null);
+
+ /// <summary>
+ /// The function generating the first and rest.
+ /// </summary>
+ protected IFn _f;
+
+ /// <summary>
+ /// The first item of the sequence, when computed.
+ /// </summary>
+ protected object _first;
+
+ /// <summary>
+ /// The rest of the sequence, when computed.
+ /// </summary>
+ protected ISeq _rest;
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ /// <summary>
+ /// Construct a lazy cons on a function.
+ /// </summary>
+ /// <param name="f">The function that will create first/rest.</param>
+ public LazyCons(IFn f)
+ {
+ this._f = f;
+ this._first = sentinel;
+ this._rest = sentinel;
+ }
+
+ /// <summary>
+ /// Construct a lazy cons on a function, with attached metadata.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ /// <param name="first">The first of the sequence.</param>
+ /// <param name="rest">The rest of the sequence.</param>
+ private LazyCons(IPersistentMap meta, object first, ISeq rest)
+ : base(meta)
+ {
+ this._first = first;
+ this._rest = rest;
+ }
+
+ #endregion
+
+ #region ISeq methods
+
+ /// <summary>
+ /// Gets the first item.
+ /// </summary>
+ /// <returns>The first item.</returns>
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override object first()
+ {
+ if (_first == sentinel)
+ {
+ // Java code wraps this in a try/catch, where the catch wraps the Exception with a RuntimeException.
+ // We can just let it pass through.
+
+ _first = _f.invoke();
+
+ }
+ return _first;
+ }
+
+
+ /// <summary>
+ /// Gets the rest of the sequence.
+ /// </summary>
+ /// <returns>The rest of the sequence, or <c>null</c> if no more elements.</returns>
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public override ISeq rest()
+ {
+ if (_rest == sentinel)
+ {
+ // Java code wraps this in a try/catch, where the catch wraps the Exception with a RuntimeException.
+ // We can just let it pass through.
+
+ // force sequential evaluation
+ if (_first == sentinel)
+ first();
+ _rest = RT.seq(_f.invoke(null));
+ _f = null;
+ }
+ return _rest;
+ }
+
+ #endregion
+
+ #region IObj methods
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ if (meta == _meta)
+ return this;
+ //force eval before copying
+ rest();
+ return new LazyCons(meta, _first, _rest);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/LispReader.cs b/ClojureCLR/Clojure/Clojure/Lib/LispReader.cs new file mode 100644 index 00000000..4fd21031 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/LispReader.cs @@ -0,0 +1,1115 @@ +/**
+ * 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.Text.RegularExpressions;
+using java.math;
+using System.IO;
+using System.Collections;
+using clojure.lang.Readers;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Implements the Lisp reader, a marvel to behold.
+ /// </summary>
+ public class LispReader
+ {
+ #region Symbol definitions
+
+ static readonly Symbol QUOTE = Symbol.create("quote");
+ static readonly Symbol THE_VAR = Symbol.create("var");
+ static readonly Symbol UNQUOTE = Symbol.create("clojure.core", "unquote");
+ static readonly Symbol UNQUOTE_SPLICING = Symbol.create("clojure.core", "unquote-splicing");
+ static readonly Symbol DEREF = Symbol.create("clojure.core", "deref");
+ static readonly Symbol META = Symbol.create("clojure.core", "meta");
+ static readonly Symbol APPLY = Symbol.create("clojure.core", "apply");
+ static readonly Symbol CONCAT = Symbol.create("clojure.core", "concat");
+ static readonly Symbol HASHMAP = Symbol.create("clojure.core", "hash-map");
+ static readonly Symbol HASHSET = Symbol.create("clojure.core", "hash-set");
+ static readonly Symbol VECTOR = Symbol.create("clojure.core", "vector");
+ static readonly Symbol WITH_META = Symbol.create("clojure.core", "with-meta");
+ static readonly Symbol LIST = Symbol.create("clojure.core", "list");
+
+ static readonly Symbol SLASH = Symbol.create("/");
+ static readonly Symbol CLOJURE_SLASH = Symbol.create("clojure.core","/");
+
+ #endregion
+
+ #region Var environments
+
+ //symbol->gensymbol
+ /// <summary>
+ /// Dynamically bound var to a map from <see cref="Symbol">Symbol</see>s to ...
+ /// </summary>
+ static Var GENSYM_ENV = Var.create(null);
+
+ //sorted-map num->gensymbol
+ static Var ARG_ENV = Var.create(null);
+
+ #endregion
+
+ #region Macro characters & #-dispatch
+
+ static IFn[] _macros = new IFn[256];
+ static IFn[] _dispatchMacros = new IFn[256];
+
+ static LispReader()
+ {
+ _macros['"'] = new StringReader();
+ _macros[';'] = new CommentReader();
+ _macros['\''] = new WrappingReader(QUOTE);
+ _macros['@'] = new WrappingReader(DEREF);//new DerefReader();
+ _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 ArgVectorReader();
+ _macros['\\'] = new CharacterReader();
+ _macros['%'] = new ArgReader();
+ _macros['#'] = new DispatchReader();
+
+
+ _dispatchMacros['^'] = new MetaReader();
+ _dispatchMacros['\''] = new VarReader();
+ _dispatchMacros['"'] = new RegexReader();
+ _dispatchMacros['('] = new FnReader();
+ _dispatchMacros['{'] = new SetReader();
+ // TODO: add EvalReader
+ //dispatchMacros['='] = new EvalReader();
+ _dispatchMacros['!'] = new CommentReader();
+ _dispatchMacros['<'] = new UnreadableReader();
+ _dispatchMacros['_'] = new DiscardReader();
+ }
+
+ static bool isMacro(int ch)
+ {
+ return ch < _macros.Length && _macros[ch] != null;
+ }
+
+ static IFn getMacro(int ch)
+ {
+ return ch < _macros.Length ? _macros[ch] : null;
+ }
+
+ static bool isTerminatingMacro(int ch)
+ {
+ return (ch != '#' && ch < _macros.Length && _macros[ch] != null);
+ }
+
+ #endregion
+
+ #region main entry point -- read
+
+ // There is really no reason for the main entry point to have an isRecursive flag, is there?
+
+ public static object read(TextReader r,
+ bool eofIsError,
+ object eofValue,
+ bool isRecursive)
+ {
+ if ( eofIsError )
+ return ReadAux(r,isRecursive);
+
+ try
+ {
+ return ReadAux(r, isRecursive);
+ }
+ catch (EndOfStreamException)
+ {
+ return eofValue;
+ }
+ }
+
+ public static object ReadAux(TextReader r, bool isRecursive)
+ {
+ try
+ {
+ for (; ; )
+ {
+ int ch = r.Read();
+
+ while (isWhitespace(ch))
+ ch = r.Read();
+
+ if (ch == -1)
+ {
+ //if (eofIsError)
+ // throw new Exception("EOF while reading");
+ //return eofValue;
+ throw new EndOfStreamException("EOF while reading");
+ }
+
+ if (Char.IsDigit((char)ch))
+ {
+ object n = readNumber(r, (char)ch);
+ return RT.suppressRead() ? null : n;
+ }
+
+ IFn macroFn = getMacro(ch);
+ if (macroFn != null)
+ {
+ object ret = macroFn.invoke(r, (char)ch);
+ if (RT.suppressRead())
+ return null;
+ if (ret == r)
+ // no op macros return the reader
+ continue;
+ return ret;
+ }
+
+ if (ch == '+' || ch == '-')
+ {
+ int ch2 = r.Peek();
+ if (Char.IsDigit((char)ch2))
+ {
+ object n = readNumber(r, (char)ch);
+ return RT.suppressRead() ? null : n;
+ }
+ }
+
+ string token = readToken(r, (char)ch);
+ return RT.suppressRead() ? null : interpretToken(token);
+ }
+ }
+ catch (Exception e)
+ {
+ if (isRecursive || !(r is LineNumberingReader))
+ throw e;
+ LineNumberingReader rdr = r as LineNumberingReader;
+ throw new ReaderException(rdr.LineNumber, e);
+ }
+ }
+
+ #endregion
+
+ #region Character hacking
+
+ static bool isWhitespace(int ch)
+ {
+ return Char.IsWhiteSpace((char)ch) || ch == ',';
+ }
+
+
+
+
+ // Roughly a match to Java Character.digit(char,int),
+ // though I don't handle all unicode digits.
+ static int CharValueInRadix(int c, int radix)
+ {
+ if (char.IsDigit((char)c))
+ return c - '0' < radix ? c - '0' : -1;
+
+ if ('A' <= c && c <= 'Z')
+ return c - 'A' < radix - 10 ? c - 'A' + 10: -1;
+
+ if ('a' <= c && c <= 'z')
+ return c - 'a' < radix - 10 ? c - 'a' + 10 : -1;
+
+ return -1;
+ }
+
+ static int readUnicodeChar(string token, int offset, int length, int radix)
+ {
+ if (token.Length != offset + length)
+ throw new ArgumentException("Invalid unicode character: \\" + token);
+ int uc = 0;
+ for (int i = offset; i < offset + length; ++i)
+ {
+ int d = CharValueInRadix(token[i], radix);
+ if (d == -1)
+ throw new ArgumentException("Invalid digit: " + (char)d);
+ uc = uc * radix + d;
+ }
+ return (char)uc;
+ }
+
+ static int readUnicodeChar(TextReader r, int initch, int radix, int length, bool exact)
+ {
+
+ int uc = CharValueInRadix(initch, radix);
+ if (uc == -1)
+ throw new ArgumentException("Invalid digit: " + initch);
+ int i = 1;
+ for (; i < length; ++i)
+ {
+ int ch = r.Peek();
+ if (ch == -1 || isWhitespace(ch) || isMacro(ch))
+ {
+ break;
+ }
+ else
+ r.Read();
+ int d = CharValueInRadix(ch, radix);
+ if (d == -1)
+ throw new ArgumentException("Invalid digit: " + (char)ch);
+ uc = uc * radix + d;
+ }
+ if (i != length && exact)
+ throw new ArgumentException("Invalid character length: " + i + ", should be: " + length);
+ return uc;
+ }
+
+ #endregion
+
+ #region Other
+
+
+ public static List<Object> readDelimitedList(char delim, TextReader r, bool isRecursive)
+ {
+ List<Object> a = new List<object>();
+
+ for (; ; )
+ {
+ int ch = r.Peek();
+
+ while (isWhitespace(ch))
+ {
+ r.Read();
+ ch = r.Peek();
+ }
+
+ if (ch == -1)
+ throw new EndOfStreamException("EOF while reading");
+
+ if (ch == delim)
+ {
+ r.Read();
+ break;
+ }
+
+ IFn macroFn = getMacro(ch);
+ if (macroFn != null)
+ {
+ r.Read();
+ Object mret = macroFn.invoke(r, (char)ch);
+ //no op macros return the reader
+ if (mret != r)
+ a.Add(mret);
+ }
+ else
+ {
+ //object o = read(r, true, null, isRecursive);
+ object o = ReadAux(r, isRecursive);
+ if (o != r)
+ a.Add(o);
+ }
+ }
+
+ return a;
+ }
+
+ static Symbol garg(int n)
+ {
+ return Symbol.intern(null, (n == -1 ? "rest" : ("p" + n)) + "__" + RT.nextID());
+ }
+
+
+ #endregion
+
+ #region Reading tokens
+
+ static string readToken(TextReader r, char initch)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append(initch);
+
+ for(; ;)
+ {
+ int ch = r.Peek();
+ if(ch == -1 || isWhitespace(ch) || isTerminatingMacro(ch))
+ {
+ return sb.ToString();
+ }
+ sb.Append((char) ch);
+ r.Read();
+ }
+ }
+
+ public static object interpretToken(string s)
+ {
+
+ if (s.Equals("nil"))
+ {
+ return null;
+ }
+ else if (s.Equals("true"))
+ {
+ return RT.T;
+ }
+ else if (s.Equals("false"))
+ {
+ return RT.F;
+ }
+ else if (s.Equals("/"))
+ {
+ return SLASH;
+ }
+ else if (s.Equals("clojure.core//"))
+ {
+ return CLOJURE_SLASH;
+ }
+
+ object ret = null;
+
+ ret = matchSymbol(s);
+ if (ret != null)
+ return ret;
+
+ throw new Exception("Invalid token: " + s);
+ }
+
+
+ static Regex symbolPat = new Regex("^[:]?([\\D-[/]].*/)?([\\D-[/]][^/]*)$");
+
+ static object matchSymbol(string s)
+ {
+ Match m = symbolPat.Match(s);
+ if (m.Success)
+ {
+ int gc = m.Groups.Count;
+ string ns = m.Groups[1].Value;
+ string name = m.Groups[2].Value;
+ if (ns != null && ns.EndsWith(":/")
+ || name.EndsWith(":")
+ || s.IndexOf("::", 1) != -1)
+ return null;
+ // Maybe resolveSymbol should move here or into Namespace: resolveSymbol is not used in the compiler.
+ if (s.StartsWith("::"))
+ {
+ Symbol ks = Symbol.intern(s.Substring(2));
+ Namespace kns;
+ if (ks.Namespace != null)
+ kns = Compiler.NamespaceFor(ks);
+ else
+ kns = Compiler.CurrentNamespace;
+ //auto-resolving keyword
+ return Keyword.intern(kns.Name.Name, ks.Name);
+ }
+ bool isKeyword = s[0] == ':';
+ Symbol sym = Symbol.intern(s.Substring(isKeyword ? 1 : 0));
+ if (isKeyword)
+ return Keyword.intern(sym);
+ return sym;
+ }
+ return null;
+ }
+
+
+
+ #endregion
+
+ #region Reading numbers
+
+ 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]+)\\.?$");
+ static Regex ratioRE = new Regex("^([-+]?[0-9]+)/([0-9]+)$");
+ static Regex floatRE = new Regex("^[-+]?[0-9]+(\\.[0-9]+)?([eE][-+]?[0-9]+)?[M]?$");
+
+
+ static object readNumber(TextReader r, char initch)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append(initch);
+
+ for (; ; )
+ {
+ int ch = r.Peek();
+ if (ch == -1 || isWhitespace(ch) || isMacro(ch))
+ break;
+ r.Read();
+ sb.Append((char)ch);
+ }
+
+ string s = sb.ToString();
+ object n = matchNumber(s);
+ if (n == null)
+ throw new FormatException("Invalid number: " + s);
+ return n;
+ }
+
+ public static object matchNumber(string s)
+ {
+ Match m = intRE.Match(s);
+ if ( m.Success )
+ {
+ if ( m.Groups[2].Success )
+ // matched 0 only
+ return 0;
+ bool isNeg = m.Groups[1].Value == "-";
+ string n = null;
+ int radix = 10;
+ if (m.Groups[3].Success)
+ {
+ n = m.Groups[3].Value;
+ radix = 10;
+ }
+ else if (m.Groups[4].Success)
+ {
+ n = m.Groups[4].Value;
+ radix = 16;
+ }
+ else if (m.Groups[5].Success)
+ {
+ n = m.Groups[5].Value;
+ radix = 8;
+ }
+ else if (m.Groups[7].Success)
+ {
+ n = m.Groups[7].Value;
+ radix = Int32.Parse(m.Groups[6].Value);
+ }
+ if (n == null)
+ return null;
+
+ BigInteger bn = new BigInteger(n, radix);
+ return Numbers.reduce(isNeg ? bn.negate() : bn);
+ }
+ m = floatRE.Match(s);
+
+ if (m.Success)
+ {
+ return ( s[s.Length-1] == 'M' )
+ ? new BigDecimal( s.Substring(0,s.Length-1)) // TODO: Fix MS inadequacy
+ : (object)Double.Parse(s);
+ }
+ m = ratioRE.Match(s);
+ if (m.Success)
+ {
+ // There is a bug in the BigInteger c-tor that causes it barf on a leading +.
+ string numerString = m.Groups[1].Value;
+ string denomString = m.Groups[2].Value;
+ if (numerString[0] == '+')
+ numerString = numerString.Substring(1);
+ return Numbers.BIDivide(new BigInteger(numerString), new BigInteger(denomString));
+ }
+ return null;
+ }
+
+ #endregion
+
+
+ // Reader classes made public according to Java rev 1121
+
+ public abstract class ReaderBase : AFn
+ {
+ public override object invoke(object arg1, object arg2)
+ {
+ return Read((TextReader)arg1, (Char)arg2);
+ }
+
+ protected abstract object Read(TextReader r, char c);
+ }
+
+ public sealed class CharacterReader : ReaderBase
+ {
+ protected override object Read(TextReader r, char backslash)
+ {
+ int ch = r.Read();
+ if (ch == -1)
+ throw new EndOfStreamException("EOF while reading character");
+ String token = readToken(r, (char)ch);
+ if (token.Length == 1)
+ return token[0];
+ else if (token.Equals("newline"))
+ return '\n';
+ else if (token.Equals("space"))
+ return ' ';
+ else if (token.Equals("tab"))
+ return '\t';
+ else if (token.Equals("backspace"))
+ return '\b';
+ else if (token.Equals("formfeed"))
+ return '\f';
+ else if (token.Equals("return"))
+ return '\r';
+ else if (token.StartsWith("u"))
+ {
+ char c = (char)readUnicodeChar(token, 1, 4, 16);
+ if (c >= '\uD800' && c <= '\uDFFF') // surrogate code unit?
+ throw new Exception("Invalid character constant: \\u" + ((int)c).ToString("X"));
+ return c;
+ }
+ else if (token.StartsWith("o"))
+ {
+ int len = token.Length - 1;
+ if (len > 3)
+ throw new Exception("Invalid octal escape sequence length: " + len);
+ int uc = readUnicodeChar(token, 1, len, 8);
+ if (uc > 255) //octal377
+ throw new Exception("Octal escape sequence must be in range [0, 377].");
+ return (char)uc;
+ }
+ throw new Exception("Unsupported character: \\" + token);
+ }
+ }
+
+ public sealed class StringReader : ReaderBase
+ {
+ protected override object Read(TextReader r, char doublequote)
+ {
+ StringBuilder sb = new StringBuilder();
+
+ for (int ch = r.Read(); ch != '"'; ch = r.Read())
+ {
+ if (ch == -1)
+ throw new EndOfStreamException("EOF while reading string");
+ if (ch == '\\') //escape
+ {
+ ch = r.Read();
+ if (ch == -1)
+ throw new EndOfStreamException("EOF while reading string");
+ switch (ch)
+ {
+ case 't':
+ ch = '\t';
+ break;
+ case 'r':
+ ch = '\r';
+ break;
+ case 'n':
+ ch = '\n';
+ break;
+ case '\\':
+ break;
+ case '"':
+ break;
+ case 'b':
+ ch = '\b';
+ break;
+ case 'f':
+ ch = '\f';
+ break;
+ case 'u':
+ ch = r.Read();
+ if (CharValueInRadix(ch, 16) == -1)
+ throw new Exception("Invalid unicode escape: \\u" + (char)ch);
+ ch = readUnicodeChar((TextReader)r, ch, 16, 4, true);
+ break;
+ default:
+ {
+ if (CharValueInRadix(ch, 8) != -1)
+ {
+ ch = readUnicodeChar((TextReader)r, ch, 8, 3, false);
+ if (ch > 255) //octal377
+ throw new Exception("Octal escape sequence must be in range [0, 377].");
+ }
+ else
+ throw new Exception("Unsupported escape character: \\" + (char)ch);
+ }
+ break;
+ }
+ }
+ sb.Append((char)ch);
+ }
+ return sb.ToString();
+ }
+ }
+
+ public sealed class CommentReader : ReaderBase
+ {
+ protected override object Read(TextReader r, char semicolon)
+ {
+ int ch;
+ do
+ {
+ ch = r.Read();
+ } while (ch != -1 && ch != '\n' && ch != '\r');
+ return r;
+ }
+ }
+
+ public sealed class ListReader : ReaderBase
+ {
+ protected override object Read(TextReader r, char leftparen)
+ {
+ int line = -1;
+ if (r is LineNumberingReader)
+ line = ((LineNumberingReader)r).LineNumber;
+ IList<Object> list = readDelimitedList(')', r, true);
+ if (list.Count == 0)
+ return PersistentList.EMPTY;
+ IObj s = (IObj)PersistentList.create((IList)list);
+ // IObj s = (IObj) RT.seq(list);
+ if (line != -1)
+ return s.withMeta(RT.map(RT.LINE_KEY, line));
+ else
+ return s;
+ }
+ }
+
+ public sealed class VectorReader : ReaderBase
+ {
+ protected override object Read(TextReader r, char leftparen)
+ {
+ return LazilyPersistentVector.create(readDelimitedList(']', r, true));
+ }
+ }
+
+ public sealed class MapReader : ReaderBase
+ {
+ protected override object Read(TextReader r, char leftbrace)
+ {
+ //return PersistentHashMap.create(readDelimitedList('}', r, true));
+ return RT.map(readDelimitedList('}', r, true).ToArray());
+ }
+ }
+
+ public sealed class UnmatchedDelimiterReader : ReaderBase
+ {
+ protected override object Read(TextReader reader, char rightdelim)
+ {
+ throw new Exception("Unmatched delimiter: " + rightdelim);
+ }
+ }
+
+ public sealed class DiscardReader : ReaderBase
+ {
+ protected override object Read(TextReader r, char underscore)
+ {
+ ReadAux(r,true);
+ return r;
+ }
+ }
+
+ public sealed class WrappingReader : ReaderBase
+ {
+ readonly Symbol _sym;
+
+ public WrappingReader(Symbol sym)
+ {
+ _sym = sym;
+ }
+
+ protected override object Read(TextReader r, char quote)
+ {
+ //object o = read(r, true, null, true);
+ object o = ReadAux(r, true);
+ return RT.list(_sym, o);
+ }
+ }
+
+ public sealed class SyntaxQuoteReader : ReaderBase
+ {
+ protected override object Read(TextReader r, char backquote)
+ {
+ try
+ {
+ Var.pushThreadBindings(RT.map(GENSYM_ENV, PersistentHashMap.EMPTY));
+ //object form = read(r, true, null, true);
+ object form = ReadAux(r, true);
+ return syntaxQuote(form);
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ }
+
+ static object syntaxQuote(object form)
+ {
+ object ret;
+ if (Compiler.isSpecial(form))
+ ret = RT.list(Compiler.QUOTE, form);
+ else if (form is Symbol)
+ {
+ Symbol sym = (Symbol)form;
+ if (sym.Namespace == null && sym.Name.EndsWith("#"))
+ {
+ IPersistentMap gmap = (IPersistentMap)GENSYM_ENV.deref();
+ if (gmap == null)
+ throw new InvalidDataException("Gensym literal not in syntax-quote");
+ Symbol gs = (Symbol)gmap.valAt(sym);
+ if (gs == null)
+ GENSYM_ENV.set(gmap.assoc(sym, gs = Symbol.intern(null,
+ sym.Name.Substring(0, sym.Name.Length - 1)
+ + "__" + RT.nextID() + "__auto__")));
+ sym = gs;
+ }
+ else if (sym.Namespace == null && sym.Name.EndsWith("."))
+ {
+ Symbol csym = Symbol.intern(null, sym.Name.Substring(0, sym.Name.Length - 1));
+ csym = Compiler.resolveSymbol(csym);
+ sym = Symbol.intern(null, csym.Name + ".");
+ }
+ else if ( sym.Namespace == null && sym.Name.StartsWith("."))
+ {
+ // simply quote method names
+ }
+ else
+ sym = Compiler.resolveSymbol(sym);
+ ret = RT.list(Compiler.QUOTE, sym);
+ }
+ //else if (form is Unquote)
+ // return ((Unquote)form).Obj;
+ // Rev 1184
+ else if (isUnquote(form))
+ return RT.second(form);
+ else if (isUnquoteSplicing(form))
+ throw new ArgumentException("splice not in list");
+ else if (form is IPersistentCollection)
+ {
+ if (form is IPersistentMap)
+ {
+ IPersistentVector keyvals = flattenMap(form);
+ ret = RT.list(APPLY, HASHMAP, RT.cons(CONCAT, sqExpandList(keyvals.seq())));
+ }
+ else if (form is IPersistentVector)
+ {
+ ret = RT.list(APPLY, VECTOR, RT.cons(CONCAT, sqExpandList(((IPersistentVector)form).seq())));
+ }
+ else if (form is IPersistentSet)
+ {
+ ret = RT.list(APPLY, HASHSET, RT.cons(CONCAT, sqExpandList(((IPersistentSet)form).seq())));
+ }
+ else if (form is ISeq || form is IPersistentList)
+ {
+ ISeq seq = RT.seq(form);
+ if (seq == null)
+ ret = PersistentList.EMPTY;
+ else
+ ret = RT.cons(CONCAT, sqExpandList(seq));
+ }
+ else
+ throw new InvalidOperationException("Unknown Collection type");
+ }
+ else if (form is Keyword
+ || Util.IsNumeric(form)
+ || form is Char
+ || form is String)
+ ret = form;
+ else
+ ret = RT.list(Compiler.QUOTE, form);
+
+ if (form is IObj && RT.meta(form) != null)
+ {
+ //filter line numbers
+ IPersistentMap newMeta = ((IObj)form).meta().without(RT.LINE_KEY);
+ if (newMeta.count() > 0)
+ return RT.list(WITH_META, ret, syntaxQuote(((IObj)form).meta()));
+ }
+ return ret;
+ }
+
+ private static ISeq sqExpandList(ISeq seq)
+ {
+ IPersistentVector ret = PersistentVector.EMPTY;
+ for (; seq != null; seq = seq.rest())
+ {
+ Object item = seq.first();
+ //if (item is Unquote)
+ // ret = ret.cons(RT.list(LIST, ((Unquote)item).Obj));
+ // REV 1184
+ if (isUnquote(item))
+ ret = ret.cons(RT.list(LIST, RT.second(item)));
+ else if (isUnquoteSplicing(item))
+ ret = ret.cons(RT.second(item));
+ else
+ ret = ret.cons(RT.list(LIST, syntaxQuote(item)));
+ }
+ return ret.seq();
+ }
+
+ private static IPersistentVector flattenMap(object form)
+ {
+ IPersistentVector keyvals = PersistentVector.EMPTY;
+ for (ISeq s = RT.seq(form); s != null; s = s.rest())
+ {
+ IMapEntry e = (IMapEntry)s.first();
+ keyvals = (IPersistentVector)keyvals.cons(e.key());
+ keyvals = (IPersistentVector)keyvals.cons(e.val());
+ }
+ return keyvals;
+ }
+ }
+
+ sealed class UnquoteReader : ReaderBase
+ {
+ protected override object Read(TextReader r, char comma)
+ {
+ int ch = r.Peek();
+ if (ch == -1)
+ throw new EndOfStreamException("EOF while reading character");
+ if (ch == '@')
+ {
+ //object o = read(r, true, null, true);
+ r.Read();
+ object o = ReadAux(r, true);
+ return RT.list(UNQUOTE_SPLICING, o);
+ }
+ else
+ {
+ //object o = read(r, true, null, true);
+ object o = ReadAux(r, true);
+ //return new Unquote(o);
+ // per Rev 1184
+ return RT.list(UNQUOTE, o);
+ }
+ }
+ }
+
+ // Per rev 1184
+ static bool isUnquote(object form)
+ {
+ return form is ISeq && RT.first(form).Equals(UNQUOTE);
+ }
+
+ static bool isUnquoteSplicing(object form)
+ {
+ return form is ISeq && RT.first(form).Equals(UNQUOTE_SPLICING);
+ }
+
+ public sealed class DispatchReader : ReaderBase
+ {
+ protected override object Read(TextReader r, char hash)
+ {
+ int ch = r.Read();
+ if (ch == -1)
+ throw new EndOfStreamException("EOF while reading character");
+ IFn fn = _dispatchMacros[ch];
+ if (fn == null)
+ throw new Exception(String.Format("No dispatch macro for: %c", (char)ch));
+ return fn.invoke(r, (char)ch);
+ }
+ }
+
+ public sealed class MetaReader : ReaderBase
+ {
+ protected override object Read(TextReader r, char caret)
+ {
+ int line = -1;
+ if (r is LineNumberingReader)
+ line = ((LineNumberingReader)r).LineNumber;
+ //object meta = read(r, true, null, true);
+ object meta = ReadAux(r, true);
+ if (meta is Symbol || meta is Keyword || meta is String)
+ meta = RT.map(RT.TAG_KEY, meta);
+ else if (!(meta is IPersistentMap))
+ throw new ArgumentException("Metadata must be Symbol,Keyword,String or Map");
+
+ //object o = read(r, true, null, true);
+ object o = ReadAux(r, true);
+ if (o is IMeta)
+ {
+ if (line != -1 && o is ISeq)
+ meta = ((IPersistentMap)meta).assoc(RT.LINE_KEY, line);
+ if (o is IReference)
+ {
+ ((IReference)o).resetMeta((IPersistentMap)meta);
+ return o;
+ }
+ return ((IObj)o).withMeta((IPersistentMap)meta);
+ }
+ else
+ throw new ArgumentException("Metadata can only be applied to IMetas");
+ }
+ }
+
+ public sealed class VarReader : ReaderBase
+ {
+ protected override object Read(TextReader r, char quote)
+ {
+ //object o = read(r, true, null, true);
+ object o = ReadAux(r, true);
+ // if(o instanceof Symbol)
+ // {
+ // Object v = Compiler.maybeResolveIn(Compiler.currentNS(), (Symbol) o);
+ // if(v instanceof Var)
+ // return v;
+ // }
+ return RT.list(THE_VAR, o);
+ }
+ }
+
+ public sealed class RegexReader : ReaderBase
+ {
+ static readonly StringReader stringrdr = new StringReader();
+
+ protected override object Read(TextReader r, char doublequote)
+ {
+ StringBuilder sb = new StringBuilder();
+ for (int ch = r.Read(); ch != '"'; ch = r.Read())
+ {
+ if (ch == -1)
+ throw new EndOfStreamException("EOF while reading regex");
+ sb.Append((char)ch);
+ if (ch == '\\') //escape
+ {
+ ch = r.Read();
+ if (ch == -1)
+ throw new EndOfStreamException("EOF while reading regex");
+ sb.Append((char)ch);
+ }
+ }
+ return new Regex(sb.ToString());
+ }
+ }
+
+ public sealed class FnReader : ReaderBase
+ {
+ static ListReader _listReader = new ListReader();
+
+ protected override object Read(TextReader r, char lparen)
+ {
+ if (ARG_ENV.deref() != null)
+ throw new InvalidOperationException("Nested #()s are not allowed");
+ try
+ {
+ Var.pushThreadBindings(RT.map(ARG_ENV, PersistentTreeMap.EMPTY));
+ //r.Unread('(');
+ ////object form = ReadAux(r, true, null, true);
+ //object form = ReadAux(r, true);
+ object form = _listReader.invoke(r, '(');
+
+ IPersistentVector args = PersistentVector.EMPTY;
+ PersistentTreeMap argsyms = (PersistentTreeMap)ARG_ENV.deref();
+ ISeq rargs = argsyms.rseq();
+ if (rargs != null)
+ {
+ int higharg = (int)((IMapEntry)rargs.first()).key();
+ if (higharg > 0)
+ {
+ for (int i = 1; i <= higharg; ++i)
+ {
+ object sym = argsyms.valAt(i);
+ if (sym == null)
+ sym = garg(i);
+ args = args.cons(sym);
+ }
+ }
+ object restsym = argsyms.valAt(-1);
+ if (restsym != null)
+ {
+ args = args.cons(Compiler._AMP_);
+ args = args.cons(restsym);
+ }
+ }
+ return RT.list(Compiler.FN, args, form);
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ }
+ }
+
+ sealed class ArgReader : ReaderBase
+ {
+ protected override object Read(TextReader r, char pct)
+ {
+ int ch = r.Peek();
+ //% alone is first arg
+ if (ch == -1 || isWhitespace(ch) || isTerminatingMacro(ch))
+ {
+ return registerArg(1);
+ }
+ //object n = ReadAux(r, true, null, true);
+ object n = ReadAux(r, true);
+ if (n.Equals(Compiler._AMP_))
+ return registerArg(-1);
+ if (!Util.IsNumeric(n))
+ throw new ArgumentException("arg literal must be %, %& or %integer");
+ return registerArg(Util.ConvertToInt(n));
+ }
+
+ static Symbol registerArg(int n)
+ {
+ PersistentTreeMap argsyms = (PersistentTreeMap)ARG_ENV.deref();
+ if (argsyms == null)
+ {
+ throw new InvalidOperationException("arg literal not in #()");
+ }
+ Symbol ret = (Symbol)argsyms.valAt(n);
+ if (ret == null)
+ {
+ ret = garg(n);
+ ARG_ENV.set(argsyms.assoc(n, ret));
+ }
+ return ret;
+ }
+ }
+
+ public sealed class SetReader : ReaderBase
+ {
+ protected override object Read(TextReader r, char leftbracket)
+ {
+ return PersistentHashSet.create1(readDelimitedList('}', r, true));
+ }
+ }
+
+ //public sealed class EvalReader : ReaderBase
+ // TODO: Need to figure out who to deal with typenames in the context of multiple loaded assemblies.
+ //{
+ // object Read(TextReader r, char eq)
+ // {
+ // Object o = read(r, true, null, true);
+ // if (o is Symbol)
+ // {
+ // return RT.classForName(o.ToString());
+ // }
+ // else if (o is IPersistentList)
+ // {
+ // ISeq pl = ((IPersistentList)o).seq();
+ // Symbol fs = (Symbol)pl.first();
+ // if (fs.Equals(THE_VAR))
+ // {
+ // Symbol vs = (Symbol)pl.rest().first();
+ // return RT.var(vs.Namespace, vs.Name); //Compiler.resolve((Symbol) RT.second(o),true);
+ // }
+ // if (fs.Name.EndsWith("."))
+ // {
+ // Object[] args = RT.toArray(pl.rest());
+ // return Reflector.invokeConstructor(RT.classForName(fs.name.substring(0, fs.name.length() - 1)), args);
+ // }
+ // if (Compiler.namesStaticMember(fs))
+ // {
+ // Object[] args = RT.toArray(pl.rest());
+ // return Reflector.invokeStaticMethod(fs.ns, fs.name, args);
+ // }
+ // Object v = Compiler.maybeResolveIn(Compiler.currentNS(), fs);
+ // if (v is Var)
+ // {
+ // return ((IFn)v).applyTo(pl.rest());
+ // }
+ // throw new Exception("Can't resolve " + fs);
+ // }
+ // else
+ // throw new ArgumentException("Unsupported #= form");
+ // }
+ //}
+
+ public sealed class UnreadableReader : ReaderBase
+ {
+ protected override object Read(TextReader reader, char leftangle)
+ {
+ throw new Exception("Unreadable form");
+ }
+ }
+
+ public sealed class ReaderException : Exception
+ {
+ readonly int _line;
+
+ public ReaderException(int line, Exception e)
+ : base(null, e)
+ {
+ _line = line;
+ }
+ }
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/LockingTransaction.cs b/ClojureCLR/Clojure/Clojure/Lib/LockingTransaction.cs new file mode 100644 index 00000000..debfe946 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/LockingTransaction.cs @@ -0,0 +1,720 @@ +/**
+ * 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.Threading;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Provides transaction semantics for <see cref="Agent">Agent</see>s, <see cref="Ref">Ref</see>s, etc.
+ /// </summary>
+ public class LockingTransaction
+ {
+ #region Constants & enums
+
+ /// <summary>
+ /// The number of times to retry a transaction in case of a conflict.
+ /// </summary>
+ public const int RETRY_LIMIT = 10000;
+
+ /// <summary>
+ /// How long to wait for a lock.
+ /// </summary>
+ public const int LOCK_WAIT_MSECS = 100;
+
+ /// <summary>
+ /// How old another transaction must be before we 'barge' it.
+ /// </summary>
+ public const long BARGE_WAIT_TICKS = 100000;
+
+
+ // State constants
+ // Should be an enum, but we want Interlocked capability
+
+ /// <summary>
+ /// Value: The transaction is running.
+ /// </summary>
+ const int RUNNING = 0;
+
+ /// <summary>
+ /// Value: The transaction is committing.
+ /// </summary>
+ const int COMMITTING = 1;
+
+ /// <summary>
+ /// Value: the transaction is getting ready to retry.
+ /// </summary>
+ const int RETRY = 2;
+
+ /// <summary>
+ /// The transaction has been killed.
+ /// </summary>
+ const int KILLED = 3;
+
+ /// <summary>
+ /// The transaction has been committed.
+ /// </summary>
+ const int COMMITTED = 4;
+
+ #endregion
+
+ #region supporting classes
+
+ /// <summary>
+ /// Exception thrown when a retry is necessary.
+ /// </summary>
+ public class RetryEx : Exception
+ {
+ }
+
+ /// <summary>
+ /// The transaction has been aborted.
+ /// </summary>
+ public class AbortException : Exception
+ {
+ }
+
+ /// <summary>
+ /// The current state of a transaction.
+ /// </summary>
+ public class Info
+ {
+ #region Data
+
+ /// <summary>
+ /// The status of the transaction.
+ /// </summary>
+ readonly AtomicInteger _status;
+
+ /// <summary>
+ /// The status of the transaction.
+ /// </summary>
+ internal AtomicInteger Status
+ {
+ get { return _status; }
+ }
+
+ /// <summary>
+ /// The start point of the transaction.
+ /// </summary>
+ readonly long _startPoint;
+
+ /// <summary>
+ /// The start point of the transaction.
+ /// </summary>
+ public long StartPoint
+ {
+ get { return _startPoint; }
+ }
+
+ #endregion
+
+ #region C-tors
+
+ /// <summary>
+ /// Construct an info.
+ /// </summary>
+ /// <param name="status">Current status.</param>
+ /// <param name="startPoint">Start point.</param>
+ public Info(int status, long startPoint)
+ {
+ _status = new AtomicInteger(status);
+ _startPoint = startPoint;
+ }
+
+ #endregion
+
+ #region Other
+
+ /// <summary>
+ /// Is the transaction running?
+ /// </summary>
+ public bool IsRunning
+ {
+ get
+ {
+ long s = _status.get();
+ return s == RUNNING || s == COMMITTING;
+ }
+ }
+
+ #endregion
+ }
+
+ /// <summary>
+ /// Pending call of a function on arguments.
+ /// </summary>
+ class CFn
+ {
+ #region Data
+
+ /// <summary>
+ /// The function to be called.
+ /// </summary>
+ readonly IFn _fn;
+
+ /// <summary>
+ /// The function to be called.
+ /// </summary>
+ public IFn Fn
+ {
+ get { return _fn; }
+ }
+
+ /// <summary>
+ /// The arguments to the function.
+ /// </summary>
+ readonly ISeq _args;
+
+ /// <summary>
+ /// The arguments to the function.
+ /// </summary>
+ public ISeq Args
+ {
+ get { return _args; }
+ }
+
+ #endregion
+
+ #region C-tors
+
+ /// <summary>
+ /// Construct one.
+ /// </summary>
+ /// <param name="fn">The function to invoke.</param>
+ /// <param name="args">The arguments to invoke the function on.</param>
+ public CFn(IFn fn, ISeq args)
+ {
+ _fn = fn;
+ _args = args;
+ }
+
+ #endregion
+ }
+
+ #endregion
+
+ #region Data
+
+ /// <summary>
+ /// The transaction running on the current thread. (Thread-local.)
+ /// </summary>
+ [ThreadStatic]
+ private static LockingTransaction _transaction;
+
+ /// <summary>
+ /// The current point.
+ /// </summary>
+ /// <remarks>
+ /// <para>Used to provide a total ordering on transactions
+ /// for the purpose of determining preference on transactions
+ /// when there are conflicts.
+ /// Transactions consume a point for init, for each retry,
+ /// and on commit if writing.</para>
+ /// </remarks>
+ private static readonly AtomicLong _lastPoint = new AtomicLong();
+
+ /// <summary>
+ /// The state of the transaction.
+ /// </summary>
+ /// <remarks>Encapsulated so things like Refs can look.</remarks>
+ Info _info;
+
+ /// <summary>
+ /// The point at the start of the current retry (or first try).
+ /// </summary>
+ long _readPoint;
+
+ /// <summary>
+ /// The point at the start of the transaction.
+ /// </summary>
+ long _startPoint;
+
+ /// <summary>
+ /// The system ticks at the start of the transaction.
+ /// </summary>
+ long _startTime;
+
+ /// <summary>
+ /// Cached retry exception.
+ /// </summary>
+ readonly RetryEx _retryex = new RetryEx();
+
+ /// <summary>
+ /// Agent actions pending on this thread.
+ /// </summary>
+ readonly List<Agent.Action> _actions = new List<Agent.Action>();
+
+ /// <summary>
+ /// Ref assignments made in this transaction (both sets and commutes).
+ /// </summary>
+ readonly Dictionary<Ref, Object> _vals = new Dictionary<Ref, Object>();
+
+ /// <summary>
+ /// Refs that have been set in this transaction.
+ /// </summary>
+ readonly HashSet<Ref> _sets = new HashSet<Ref>();
+
+ /// <summary>
+ /// Ref commutes that have been made in this transaction.
+ /// </summary>
+ readonly SortedDictionary<Ref, List<CFn>> _commutes = new SortedDictionary<Ref, List<CFn>>();
+
+ #endregion
+
+ #region Debugging
+
+ //string TId()
+ //{
+ // return String.Format("<{0}:{1}>", Thread.CurrentThread.ManagedThreadId, _readPoint);
+ //}
+
+ #endregion
+
+ #region Point manipulation
+
+ /// <summary>
+ /// Get a new read point value.
+ /// </summary>
+ void getReadPoint()
+ {
+ _readPoint = _lastPoint.incrementAndGet();
+ }
+
+ /// <summary>
+ /// Get a commit point value.
+ /// </summary>
+ /// <returns></returns>
+ long getCommitpoint()
+ {
+ return _lastPoint.incrementAndGet();
+ }
+
+ #endregion
+
+ #region Actions
+
+ /// <summary>
+ /// Stop this transaction.
+ /// </summary>
+ /// <param name="status">The new status.</param>
+ void stop(int status)
+ {
+
+ if (_info != null)
+ {
+ lock (_info)
+ {
+ _info.Status.set(status);
+ Monitor.PulseAll(_info);
+ }
+ _info = null;
+ _vals.Clear();
+ _sets.Clear();
+ _commutes.Clear();
+ // Java commented out: _actions.Clear();
+ }
+ }
+
+ /// <summary>
+ /// Lock a ref.
+ /// </summary>
+ /// <param name="r">The ref to lock.</param>
+ /// <returns>The current value of the ref.</returns>
+ object Lock(Ref r)
+ {
+ bool unlocked = false;
+ try
+ {
+ r.EnterWriteLock();
+ if (r.CurrentValPoint() > _readPoint)
+ throw _retryex;
+
+ Info refinfo = r.TInfo;
+
+ // write lock conflict
+ if (refinfo != null && refinfo != _info && refinfo.IsRunning)
+ {
+ if (!barge(refinfo))
+ {
+ r.ExitWriteLock();
+ unlocked = true;
+ // stop prior to blocking
+ stop(RETRY);
+ lock (refinfo)
+ {
+ if (refinfo.IsRunning)
+ {
+ try
+ {
+ Monitor.Wait(refinfo, LOCK_WAIT_MSECS);
+ }
+ catch (ThreadInterruptedException)
+ {
+ }
+ }
+ }
+ throw _retryex;
+ }
+ }
+
+ r.TInfo = _info;
+ return r.TryGetVal();
+ }
+ finally
+ {
+ if (!unlocked)
+ {
+ r.ExitWriteLock();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Kill this transaction.
+ /// </summary>
+ void abort()
+ {
+ stop(KILLED);
+ throw new AbortException();
+ }
+
+ /// <summary>
+ /// Determine if sufficient clock time has elapsed to barge another transaction.
+ /// </summary>
+ /// <returns><value>true</value> if enough time has elapsed; <value>false</value> otherwise.</returns>
+ private bool bargeTimeElapsed()
+ {
+ return DateTime.Now.Ticks - _startTime > BARGE_WAIT_TICKS;
+ }
+
+ /// <summary>
+ /// Try to barge a conflicting transaction.
+ /// </summary>
+ /// <param name="refinfo">The info on the other transaction.</param>
+ /// <returns><value>true</value> if we killed the other transaction; <value>false</value> otherwise.</returns>
+ private bool barge(Info refinfo)
+ {
+ bool barged = false;
+ // if this transaction is older
+ // try to abort the other
+ if (bargeTimeElapsed() && _startPoint < refinfo.StartPoint)
+ {
+ lock (refinfo)
+ {
+ barged = refinfo.Status.compareAndSet(RUNNING, KILLED);
+ if (barged)
+ Monitor.PulseAll(refinfo);
+ }
+ }
+ return barged;
+ }
+
+ /// <summary>
+ /// Get the transaction running on this thread (throw exception if no transaction).
+ /// </summary>
+ /// <returns>The running transaction.</returns>
+ public static LockingTransaction getEx()
+ {
+ LockingTransaction t = _transaction;
+ if (t == null || t._info == null)
+ throw new InvalidOperationException("No transaction running");
+ return t;
+ }
+
+ /// <summary>
+ /// Get the transaction running on this thread (or null if no transaction).
+ /// </summary>
+ /// <returns>The running transaction if there is one, else <value>null</value>.</returns>
+ public static LockingTransaction getRunning()
+ {
+ LockingTransaction t = _transaction;
+ if (t == null || t._info == null)
+ return null;
+ return t;
+ }
+
+ /// <summary>
+ /// Is there a transaction running on this thread?
+ /// </summary>
+ /// <returns><value>true</value> if there is a transaction running on this thread; <value>false</value> otherwise.</returns>
+ public static bool isRunning()
+ {
+ return getRunning() != null;
+ }
+
+ /// <summary>
+ /// Invoke a function in a transaction
+ /// </summary>
+ /// <param name="fn">The function to invoke.</param>
+ /// <returns>The value computed by the function.</returns>
+ public static object runInTransaction(IFn fn)
+ {
+ LockingTransaction t = _transaction;
+ if (t == null)
+ _transaction = t = new LockingTransaction();
+
+ if (t._info != null)
+ return fn.invoke();
+
+ return t.run(fn);
+ }
+
+ /// <summary>
+ /// Start a transaction and invoke a function.
+ /// </summary>
+ /// <param name="fn">The fucntion to invoke.</param>
+ /// <returns>The value computed by the function.</returns>
+ object run(IFn fn)
+ {
+ bool done = false;
+ object ret = null;
+ List<Ref> locked = new List<Ref>();
+
+ for (int i = 0; !done && i < RETRY_LIMIT; i++)
+ {
+ try
+ {
+ getReadPoint();
+ if (i == 0)
+ {
+ _startPoint = _readPoint;
+ _startTime = DateTime.Now.Ticks;
+ }
+
+ _info = new Info(RUNNING, _startPoint);
+ ret = fn.invoke();
+
+ // make sure no one has killed us before this point,
+ // and can't from now on
+ if (_info.Status.compareAndSet(RUNNING, COMMITTING))
+ {
+ foreach (KeyValuePair<Ref, List<CFn>> pair in _commutes)
+ {
+ Ref r = pair.Key;
+ r.EnterWriteLock();
+ locked.Add(r);
+ Info refinfo = r.TInfo;
+ if (refinfo != null && refinfo != _info && refinfo.IsRunning)
+ {
+ if (!barge(refinfo))
+ {
+ throw _retryex;
+ }
+ }
+ object val = r.TryGetVal();
+ if (!_sets.Contains(r))
+ _vals[r] = val;
+ foreach (CFn f in pair.Value)
+ _vals[r] = f.Fn.applyTo(RT.cons(_vals[r], f.Args));
+ }
+ foreach (Ref r in _sets)
+ {
+ if (!_commutes.ContainsKey(r))
+ {
+ r.EnterWriteLock();
+ locked.Add(r);
+ }
+ }
+ // validate and enqueue notifications
+ foreach (KeyValuePair<Ref, object> pair in _vals)
+ {
+ Ref r = pair.Key;
+ r.Validate(pair.Value);
+ r.notifyWatches();
+ }
+
+ // at this point, all values calced, all refs to be written locked
+ // no more client code to be called
+ int msecs = System.Environment.TickCount;
+ long commitPoint = getCommitpoint();
+ foreach (KeyValuePair<Ref, object> pair in _vals)
+ {
+ Ref r = pair.Key;
+ object val = pair.Value;
+ r.SetValue(val, commitPoint, msecs);
+ }
+
+ done = true;
+ _info.Status.set(COMMITTED);
+ }
+ }
+ catch (RetryEx)
+ {
+ // eat this so we retry rather than fall out
+ }
+ catch (Exception ex)
+ {
+ if (ContainsNestedRetryEx(ex))
+ {
+ // Wrapped exception, eat it.
+ }
+ else
+ {
+ throw ex;
+ }
+ }
+ finally
+ {
+ for (int k = locked.Count - 1; k >= 0; --k)
+ {
+ locked[k].ExitWriteLock();
+ }
+ locked.Clear();
+ stop(done ? COMMITTED : RETRY);
+ if (done) // re-dispatch out of transaction
+ {
+ foreach (Agent.Action action in _actions)
+ {
+ Agent.DispatchAction(action);
+ }
+ }
+ _actions.Clear();
+ }
+ }
+ if (!done)
+ throw new Exception("Transaction failed after reaching retry limit");
+ return ret;
+ }
+
+ /// <summary>
+ /// Determine if the exception wraps a <see cref="RetryEx">RetryEx</see> at some level.
+ /// </summary>
+ /// <param name="ex">The exception to test.</param>
+ /// <returns><value>true</value> if there is a nested <see cref="RetryEx">RetryEx</see>; <value>false</value> otherwise.</returns>
+ /// <remarks>Needed because sometimes our retry exceptions get wrapped. You do not want to know how long it took to track down this problem.</remarks>
+ private static bool ContainsNestedRetryEx(Exception ex)
+ {
+ for (Exception e = ex; e != null; e = e.InnerException)
+ if (e is RetryEx)
+ return true;
+ return false;
+ }
+
+ /// <summary>
+ /// Add an agent action sent during the transaction to a queue.
+ /// </summary>
+ /// <param name="action">The action that was sent.</param>
+ internal void enqueue(Agent.Action action)
+ {
+ _actions.Add(action);
+ }
+
+ /// <summary>
+ /// Get the value of a ref most recently set in this transaction (or prior to entering).
+ /// </summary>
+ /// <param name="r"></param>
+ /// <param name="tvals"></param>
+ /// <returns>The value.</returns>
+ internal object doGet(Ref r, Ref.TVal tvals)
+ {
+ if (!_info.IsRunning)
+ throw _retryex;
+ if (_vals.ContainsKey(r))
+ {
+ return _vals[r];
+ }
+ try
+ {
+ r.EnterReadLock();
+ if (tvals == null)
+ throw new InvalidOperationException(r.ToString() + " is not bound.");
+ Ref.TVal ver = tvals;
+ do
+ {
+ if (ver.Point <= _readPoint)
+ {
+ return ver.Val;
+ }
+ } while ((ver = ver.Prior) != tvals);
+ }
+ finally
+ {
+ r.ExitReadLock();
+ }
+ // no version of val precedes the read point
+ r.AddFault();
+ throw _retryex;
+ }
+
+ /// <summary>
+ /// Set the value of a ref inside the transaction.
+ /// </summary>
+ /// <param name="r">The ref to set.</param>
+ /// <param name="val">The value.</param>
+ /// <returns>The value.</returns>
+ internal object doSet(Ref r, object val)
+ {
+ if (!_info.IsRunning)
+ throw _retryex;
+ if (_commutes.ContainsKey(r))
+ throw new InvalidOperationException("Can't set after commute");
+ if (!_sets.Contains(r))
+ {
+ _sets.Add(r);
+ Lock(r);
+ }
+ _vals[r] = val;
+ return val;
+ }
+
+ /// <summary>
+ /// Touch a ref. (Lock it.)
+ /// </summary>
+ /// <param name="r">The ref to touch.</param>
+ internal void doTouch(Ref r)
+ {
+ if (!_info.IsRunning)
+ throw _retryex;
+ Lock(r);
+ }
+
+ /// <summary>
+ /// Post a commute on a ref in this transaction.
+ /// </summary>
+ /// <param name="r">The ref.</param>
+ /// <param name="fn">The commuting function.</param>
+ /// <param name="args">Additional arguments to the function.</param>
+ /// <returns>The computed value.</returns>
+ internal object doCommute(Ref r, IFn fn, ISeq args)
+ {
+ if (!_info.IsRunning)
+ throw _retryex;
+ if (!_vals.ContainsKey(r))
+ {
+ object val = null;
+ try
+ {
+ r.EnterReadLock();
+ val = r.TryGetVal();
+ }
+ finally
+ {
+ r.ExitReadLock();
+ }
+ _vals[r] = val;
+ }
+ List<CFn> fns = _commutes[r];
+ if (fns == null)
+ _commutes[r] = fns = new List<CFn>();
+ fns.Add(new CFn(fn, args));
+ object ret = fn.applyTo(RT.cons(_vals[r], args));
+ _vals[r] = ret;
+
+ return ret;
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/MapEntry.cs b/ClojureCLR/Clojure/Clojure/Lib/MapEntry.cs new file mode 100644 index 00000000..b97fe293 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/MapEntry.cs @@ -0,0 +1,81 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Implements an (immutable) entry in a persistent map.
+ /// </summary>
+ /// <remarks>
+ /// <para>In built-in BCL collections, this is a struct. We cannot do this here
+ /// because of all the other baggage this carries
+ /// (see <see cref="AMapEntry">AMapEntry</see>.</para>
+ /// <para>Provides storage for a key and a value. What more can be said?</para></remarks>
+ public class MapEntry: AMapEntry
+ {
+ //Should we make this a sealed class? Any reason we need to derive from this?
+
+ #region Data
+
+ /// <summary>
+ /// The key.
+ /// </summary>
+ protected readonly object _key;
+
+ /// <summary>
+ /// The value.
+ /// </summary>
+ protected readonly object _val;
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ /// <summary>
+ /// Initialize a <see cref="MapEntry">MapEntry</see> with a key and a value.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <param name="val">The value.</param>
+ public MapEntry(object key, object val)
+ {
+ this._key = key;
+ this._val = val;
+ }
+
+ #endregion
+
+ #region IMapEntry members
+
+ /// <summary>
+ /// Get the key in a key/value pair.
+ /// </summary>
+ /// <returns>The key.</returns>
+ public override object key()
+ {
+ return _key;
+ }
+
+ /// <summary>
+ /// Get the value in a key/value pair.
+ /// </summary>
+ /// <returns>The value.</returns>
+ public override object val()
+ {
+ return _val;
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/MapEnumerator.cs b/ClojureCLR/Clojure/Clojure/Lib/MapEnumerator.cs new file mode 100644 index 00000000..22ca572a --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/MapEnumerator.cs @@ -0,0 +1,127 @@ +/**
+ * 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.Collections;
+
+namespace clojure.lang
+{
+
+ /// <summary>
+ /// Provides an IDictionaryEnumerator running over a <see cref="SeqEnumerator">SeqEnumerator</see>.
+ /// </summary>
+ /// <remarks>
+ /// <para>Mirrors <see cref="SeqEnumerator">SeqEnumerator</see>.</para>
+ /// <para>No equivalent in Java version.</para>
+ /// <para>The map provides <see cref="IMapEntry">IMapEntry</see> key/value pairs.
+ /// This converts an <see cref="IMapEntry">IMapEntry</see> to a DictionaryEntry.
+ /// </para>
+ /// </remarks>
+ public sealed class MapEnumerator : IDictionaryEnumerator
+ {
+ #region Data
+
+ /// <summary>
+ /// The <see cref="SeqEnumerator">SeqEnumerator</see> to iterate over.
+ /// </summary>
+ private readonly SeqEnumerator _seqEnum;
+
+ /// <summary>
+ /// The key of the current entry.
+ /// </summary>
+ private object CurrentKey
+ {
+ get { return ((IMapEntry)_seqEnum.Current).key(); }
+ }
+
+ /// <summary>
+ /// The value of the current entry.
+ /// </summary>
+ private object CurrentVal
+ {
+ get { return ((IMapEntry)_seqEnum.Current).val(); }
+ }
+
+ #endregion
+
+ #region C-tor
+
+ /// <summary>
+ /// Construct a <see cref="MapEnumerator">MapEnumerator</see> from a persistent map.
+ /// </summary>
+ /// <param name="map">The map to iterate over.</param>
+ public MapEnumerator(IPersistentMap map)
+ {
+ _seqEnum = new SeqEnumerator(map.seq());
+ }
+
+ #endregion
+
+ #region IDictionaryEnumerator Members
+
+ /// <summary>
+ /// The current entry.
+ /// </summary>
+ public DictionaryEntry Entry
+ {
+ get { return new DictionaryEntry(CurrentKey, CurrentVal); }
+ }
+
+ /// <summary>
+ /// The current key.
+ /// </summary>
+ public object Key
+ {
+ get { return CurrentKey; }
+ }
+
+ /// <summary>
+ /// The current value.
+ /// </summary>
+ public object Value
+ {
+ get { return CurrentVal; }
+ }
+
+ #endregion
+
+ #region IEnumerator Members
+
+ /// <summary>
+ /// The current entry.
+ /// </summary>
+ public object Current
+ {
+ get { return Entry; }
+ }
+
+ /// <summary>
+ /// Advance to the next item.
+ /// </summary>
+ /// <returns><value>true</value> if there is a next value; <value>false</value> otherwise.</returns>
+ public bool MoveNext()
+ {
+ return _seqEnum.MoveNext();
+ }
+
+ /// <summary>
+ /// Reset the enumerator.
+ /// </summary>
+ public void Reset()
+ {
+ _seqEnum.Reset();
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/MultiFn.cs b/ClojureCLR/Clojure/Clojure/Lib/MultiFn.cs new file mode 100644 index 00000000..d2138334 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/MultiFn.cs @@ -0,0 +1,484 @@ +/**
+ * 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.Runtime.CompilerServices;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents a multifunction.
+ /// </summary>
+ /// <remarks>See the Clojure documentation for more details.</remarks>
+ public class MultiFn : AFn
+ {
+ #region Data
+
+ /// <summary>
+ /// The function that dispatches calls to the correct method.
+ /// </summary>
+ readonly IFn _dispatchFn;
+
+ /// <summary>
+ /// The default dispatch value.
+ /// </summary>
+ readonly object _defaultDispatchVal;
+
+ /// <summary>
+ /// The hierarchy for this defmulti.
+ /// </summary>
+ readonly IRef _hierarchy;
+
+ /// <summary>
+ /// The methods defined for this multifunction.
+ /// </summary>
+ IPersistentMap _methodTable;
+
+ /// <summary>
+ /// The methods defined for this multifunction.
+ /// </summary>
+ public IPersistentMap MethodTable
+ {
+ get { return _methodTable; }
+ }
+
+ /// <summary>
+ /// Method preferences.
+ /// </summary>
+ IPersistentMap _preferTable;
+
+ /// <summary>
+ /// Method preferences.
+ /// </summary>
+ public IPersistentMap PreferTable
+ {
+ get { return _preferTable; }
+ }
+
+ /// <summary>
+ /// Cache of previously encountered dispatch-value to method mappings.
+ /// </summary>
+ IPersistentMap _methodCache;
+
+ /// <summary>
+ /// Hierarchy on which cached computations are based.
+ /// </summary>
+ object _cachedHierarchy;
+
+ static readonly Var _assoc = RT.var("clojure.core", "assoc");
+ static readonly Var _dissoc = RT.var("clojure.core", "dissoc");
+ static readonly Var _isa = RT.var("clojure.core", "isa?", null);
+ static readonly Var _parents = RT.var("clojure.core", "parents");
+ //static readonly Var _hierarchy = RT.var("clojure.core", "global-hierarchy", null);
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ /// <summary>
+ /// Construct a multifunction.
+ /// </summary>
+ /// <param name="dispatchFn">The dispatch function.</param>
+ /// <param name="defaultDispatchVal">The default dispatch value.</param>
+ public MultiFn(IFn dispatchFn, object defaultDispatchVal, IRef hierarchy)
+ {
+ _dispatchFn = dispatchFn;
+ _defaultDispatchVal = defaultDispatchVal;
+ _methodTable = PersistentHashMap.EMPTY;
+ _methodCache = MethodTable;
+ _preferTable = PersistentHashMap.EMPTY;
+ _hierarchy = hierarchy;
+ _cachedHierarchy = null;
+ }
+
+ #endregion
+
+ #region External interface
+
+ /// <summary>
+ /// Add a new method to this multimethod.
+ /// </summary>
+ /// <param name="dispatchVal">The discriminator value for this method.</param>
+ /// <param name="method">The method code.</param>
+ /// <returns>This multifunction.</returns>
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public MultiFn addMethod(object dispatchVal, IFn method)
+ {
+ return addMethodImpl(dispatchVal, method);
+ }
+
+
+ /// <summary>
+ /// Implements adding a new method to this multimethod
+ /// </summary>
+ /// <param name="dispatchVal">The discriminator value for this method.</param>
+ /// <param name="method">The method code.</param>
+ /// <returns>This multifunction.</returns>
+ MultiFn addMethodImpl(object dispatchVal, IFn method)
+ {
+ _methodTable = MethodTable.assoc(dispatchVal, method);
+ ResetCache();
+ return this;
+ }
+
+ /// <summary>
+ /// Remove a method.
+ /// </summary>
+ /// <param name="dispatchVal">The dispatch value for the multimethod.</param>
+ /// <returns>This multifunction.</returns>
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public MultiFn removeMethod(object dispatchVal)
+ {
+ _methodTable = MethodTable.without(dispatchVal);
+ ResetCache();
+ return this;
+ }
+
+ /// <summary>
+ /// Add a preference for one method over another.
+ /// </summary>
+ /// <param name="dispatchValX">The more preferred dispatch value.</param>
+ /// <param name="dispatchValY">The less preferred dispatch value.</param>
+ /// <returns>This multifunction.</returns>
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public MultiFn preferMethod(object dispatchValX, object dispatchValY)
+ {
+ if (Prefers(dispatchValY, dispatchValX))
+ throw new InvalidOperationException(String.Format("Preference conflict: {0} is already preferred to {1}", dispatchValY, dispatchValX));
+ _preferTable = PreferTable.assoc(dispatchValX,
+ RT.conj((IPersistentCollection)RT.get(_preferTable, dispatchValX, PersistentHashSet.EMPTY),
+ dispatchValY));
+ ResetCache();
+ return this;
+ }
+
+ #endregion
+
+ #region Implementation details
+
+ /// <summary>
+ /// Is one value preferred over another?
+ /// </summary>
+ /// <param name="x">The first dispatch value.</param>
+ /// <param name="y">The second dispatch value.</param>
+ /// <returns><value>true</value> if <paramref name="x"/> is preferred over <paramref name="y"/></returns>
+ private bool Prefers(object x, object y)
+ {
+ IPersistentSet xprefs = (IPersistentSet)PreferTable.valAt(x);
+ if (xprefs != null && xprefs.contains(y))
+ return true;
+ for (ISeq ps = RT.seq(_parents.invoke(y)); ps != null; ps = ps.rest())
+ if (Prefers(x, ps.first()))
+ return true;
+ for (ISeq ps = RT.seq(_parents.invoke(x)); ps != null; ps = ps.rest())
+ if (Prefers(ps.first(), y))
+ return true;
+ return false;
+ }
+
+ /// <summary>
+ /// Check the hierarchy.
+ /// </summary>
+ /// <param name="x"></param>
+ /// <param name="y"></param>
+ /// <returns></returns>
+ private bool IsA(object x, object y)
+ {
+ return RT.booleanCast(_isa.invoke(_hierarchy.deref(),x, y));
+ }
+
+ /// <summary>
+ /// Determine if one dispatch is preferred over another.
+ /// </summary>
+ /// <param name="x"></param>
+ /// <param name="y"></param>
+ /// <returns></returns>
+ private bool Dominates(object x, object y)
+ {
+ return Prefers(x, y) || IsA(x, y);
+ }
+
+
+ /// <summary>
+ /// Reset the method cache.
+ /// </summary>
+ /// <returns></returns>
+ private IPersistentMap ResetCache()
+ {
+ _methodCache = MethodTable;
+ _cachedHierarchy = _hierarchy.deref();
+ return _methodCache;
+ }
+
+ /// <summary>
+ /// Get the method for a dispatch value.
+ /// </summary>
+ /// <param name="dispatchVal">The dispatch value.</param>
+ /// <returns>The preferred method for the value.</returns>
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ private IFn GetFn(object dispatchVal)
+ {
+ if (_cachedHierarchy != _hierarchy.deref())
+ ResetCache();
+
+ IFn targetFn = (IFn)_methodCache.valAt(dispatchVal);
+ if (targetFn != null)
+ return targetFn;
+
+ targetFn = FindAndCacheBestMethod(dispatchVal);
+ if (targetFn != null)
+ return targetFn;
+
+ targetFn = (IFn)MethodTable.valAt(_defaultDispatchVal);
+ if (targetFn != null)
+ return targetFn;
+
+ throw new ArgumentException(String.Format("No method for dispatch value: {0}", dispatchVal));
+ }
+
+ /// <summary>
+ /// Get the method for a dispatch value and cache it.
+ /// </summary>
+ /// <param name="dispatchVal">The disaptch value.</param>
+ /// <returns>The mest method.</returns>
+ private IFn FindAndCacheBestMethod(object dispatchVal)
+ {
+ IMapEntry bestEntry = null;
+ foreach (IMapEntry me in MethodTable)
+ {
+ if (IsA(dispatchVal, me.key()))
+ {
+ if (bestEntry == null || Dominates(me.key(), bestEntry.key()))
+ bestEntry = me;
+ if (!Dominates(bestEntry.key(), me.key()))
+ throw new ArgumentException(String.Format("Multiple methods match dispatch value: {0} -> {1} and {2}, and neither is preferred",
+ dispatchVal, me.key(), bestEntry.key()));
+ }
+ }
+ if (bestEntry == null)
+ return null;
+
+ // ensure basis has stayed stable throughout, else redo
+ if (_cachedHierarchy == _hierarchy.deref())
+ {
+ // place in cache
+ _methodCache = _methodCache.assoc(dispatchVal, bestEntry.val());
+ return (IFn)bestEntry.val();
+ }
+ else
+ {
+ ResetCache();
+ return FindAndCacheBestMethod(dispatchVal);
+ }
+ }
+
+ #endregion
+
+ #region core.clj compatibility
+
+ /// <summary>
+ /// Get the map of dispatch values to dispatch fns.
+ /// </summary>
+ /// <returns>The map of dispatch values to dispatch fns.</returns>
+ public IPersistentMap getMethodTable()
+ {
+ return MethodTable;
+ }
+
+
+ /// <summary>
+ /// Get the map of preferred value to set of other values.
+ /// </summary>
+ /// <returns>The map of preferred value to set of other values.</returns>
+ public IPersistentMap getPreferTable()
+ {
+ return PreferTable;
+ }
+
+ #endregion
+
+ #region IFn members
+
+
+ public override object invoke()
+ {
+ return GetFn(_dispatchFn.invoke()).invoke();
+ }
+
+ public override object invoke(object arg1)
+ {
+ return GetFn(_dispatchFn.invoke(arg1)).invoke(arg1);
+ }
+
+ public override object invoke(object arg1, object arg2)
+ {
+ return GetFn(_dispatchFn.invoke(arg1, arg2)).invoke(arg1, arg2);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3)
+ {
+ return GetFn(_dispatchFn.invoke(arg1, arg2, arg3)).invoke(arg1, arg2, arg3);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4)
+ {
+ return GetFn(_dispatchFn.invoke(arg1, arg2, arg3, arg4)).invoke(arg1, arg2, arg3, arg4);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5)
+ {
+ return GetFn(_dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5)).invoke(arg1, arg2, arg3, arg4, arg5);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6)
+ {
+ return GetFn(_dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6)).invoke(arg1, arg2, arg3, arg4, arg5, arg6);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7)
+ {
+ return GetFn(_dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7))
+ .invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8)
+ {
+ return GetFn(_dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)).
+ invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9)
+ {
+ return GetFn(_dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)).
+ invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10)
+ {
+ return GetFn(_dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)).
+ invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11)
+ {
+ return GetFn(_dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11)).
+ invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12)
+ {
+ return GetFn(_dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12)).
+ invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13)
+ {
+ return GetFn(_dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13)).
+ invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14)
+ {
+ return GetFn(
+ _dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14)).
+ invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15)
+ {
+ return GetFn(
+ _dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15))
+ .invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16)
+ {
+ return GetFn(
+ _dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16))
+ .invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17)
+ {
+ return GetFn(
+ _dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17))
+ .invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17, object arg18)
+ {
+ return GetFn(
+ _dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, arg18)).
+ invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, arg18);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17, object arg18, object arg19)
+ {
+ return GetFn(
+ _dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, arg18, arg19)).
+ invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, arg18, arg19);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17, object arg18, object arg19, object arg20)
+ {
+ return GetFn(
+ _dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, arg18, arg19, arg20)).
+ invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, arg18, arg19, arg20);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17, object arg18, object arg19, object arg20, params object[] args)
+ {
+ return GetFn(
+ _dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, arg18, arg19, arg20, args)).
+ invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, arg18, arg19, arg20, args);
+ }
+
+
+ #endregion
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Named.cs b/ClojureCLR/Clojure/Clojure/Lib/Named.cs new file mode 100644 index 00000000..53ff358b --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Named.cs @@ -0,0 +1,36 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents an object that has a namespace/name.
+ /// </summary>
+ /// <remarks>Lowercase-named methods for compatibility with the JVM implementation.</remarks>
+ public interface Named
+ {
+ /// <summary>
+ /// Gets the namespace name for the object.
+ /// </summary>
+ /// <returns>The namespace name.</returns>
+ string getNamespace();
+
+ /// <summary>
+ /// Gets the name of the object
+ /// </summary>
+ /// <returns>The name.</returns>
+ string getName();
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Namespace.cs b/ClojureCLR/Clojure/Clojure/Lib/Namespace.cs new file mode 100644 index 00000000..39b2fcde --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Namespace.cs @@ -0,0 +1,384 @@ +/**
+ * 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.Threading;
+
+namespace clojure.lang
+{
+
+ /// <summary>
+ /// Represents a namespace for holding symbol->reference mappings.
+ /// </summary>
+ /// <remarks>
+ /// <para>Symbol to reference mappings come in several flavors:
+ /// <list>
+ /// <item><b>Simple:</b> <see cref="Symbol">Symbol</see> to a <see cref="Var">Var</see> in the namespace.</item>
+ /// <item><b>Use/refer:</b> <see cref="Symbol">Symbol</see> to a <see cref="Var">Var</see> that is homed in another namespace.</item>
+ /// <item>Import:</item> <see cref="Symbol">Symbol</see> to a Type
+ /// </list>
+ /// </para>
+ /// <para>One namespace can also refer to another namespace by an alias.</para>
+ /// </remarks>
+ public class Namespace : AReference
+ {
+ #region Data
+
+ /// <summary>
+ /// The namespace's name.
+ /// </summary>
+ private readonly Symbol _name;
+
+ /// <summary>
+ /// The namespace's name.
+ /// </summary>
+ public Symbol Name
+ {
+ get { return _name; }
+ }
+
+ /// <summary>
+ /// Maps <see cref="Symbol">Symbol</see>s to their values (Types, <see cref="Var">Var</see>s, or arbitrary).
+ /// </summary>
+ private readonly AtomicReference<IPersistentMap> _mappings = new AtomicReference<IPersistentMap>();
+
+ /// <summary>
+ /// Maps <see cref="Symbol">Symbol</see>s to other namespaces (aliases).
+ /// </summary>
+ private readonly AtomicReference<IPersistentMap> _aliases = new AtomicReference<IPersistentMap>();
+
+
+ // Why not use one of the IPersistentMap implementations?
+ /// <summary>
+ /// All namespaces, keyed by <see cref="Symbol">Symbol</see>.
+ /// </summary>
+ private static JavaConcurrentDictionary<Symbol, Namespace> _namespaces
+ = new JavaConcurrentDictionary<Symbol, Namespace>();
+
+
+ /// <summary>
+ /// Get the variable-to-value map.
+ /// </summary>
+ private IPersistentMap Mappings
+ {
+ get { return _mappings.Get(); }
+ //set { mappings = value; }
+ }
+ /// <summary>
+ /// Get the variable-to-namespace alias map.
+ /// </summary>
+ private IPersistentMap Aliases
+ {
+ get { return _aliases.Get(); }
+ }
+
+ /// <summary>
+ /// Get all namespaces.
+ /// </summary>
+ public static ISeq all
+ {
+ get
+ { return RT.seq(_namespaces.Values); }
+ }
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ /// <summary>
+ /// Find or create a namespace named by the symbol.
+ /// </summary>
+ /// <param name="name">The symbol naming the namespace.</param>
+ /// <returns>An existing or new namespace</returns>
+ public static Namespace findOrCreate(Symbol name)
+ {
+ Namespace ns = _namespaces.Get(name);
+ if (ns != null)
+ return ns;
+ Namespace newns = new Namespace(name);
+ ns = _namespaces.PutIfAbsent(name, newns);
+ return ns == null ? newns : ns;
+ }
+
+ /// <summary>
+ /// Remove a namespace (by name).
+ /// </summary>
+ /// <param name="name">The (Symbol) name of the namespace to remove.</param>
+ /// <returns>The namespace that was removed.</returns>
+ /// <remarks>Trying to remove the clomure namespace throws an exception.</remarks>
+ public static Namespace remove(Symbol name)
+ {
+ if (name.Equals(RT.CLOJURE_NS.Name))
+ throw new ArgumentException("Cannot remove clojure namespace");
+ return _namespaces.Remove(name);
+ }
+
+ /// <summary>
+ /// Find the namespace with a given name.
+ /// </summary>
+ /// <param name="name">The name of the namespace to find.</param>
+ /// <returns>The namespace with the given name, or <value>null</value> if no such namespace exists.</returns>
+ public static Namespace find(Symbol name)
+ {
+ return _namespaces.Get(name);
+ }
+
+
+ /// <summary>
+ /// Construct a namespace with a given name.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ Namespace(Symbol name)
+ : base(name.meta())
+ {
+ _name = name;
+ _mappings.Set(RT.DEFAULT_IMPORTS);
+ _aliases.Set(RT.map());
+ }
+
+ #endregion
+
+ #region Object overrides
+
+ /// <summary>
+ /// Returns a string representing the namespace.
+ /// </summary>
+ /// <returns>A string representing the namespace.</returns>
+ public override string ToString()
+ {
+ return "#<Namespace: " + _name + ">";
+ }
+
+ #endregion
+
+ #region Interning
+
+ /// <summary>
+ /// Intern a <see cref="Symbol">Symbol</see> in the namespace, with a (new) <see cref="Var">Var</see> as its value.
+ /// </summary>
+ /// <param name="sym">The symbol to intern.</param>
+ /// <returns>The <see cref="Var">Var</see> associated with the symbol.</returns>
+ /// <remarks>
+ /// <para>It is an error to intern a symbol with a namespace.</para>
+ /// <para>This has to deal with other threads also interning.</para>
+ /// </remarks>
+ public Var intern(Symbol sym)
+ {
+ if (sym.Namespace != null)
+ throw new ArgumentException("Can't intern a namespace-qualified symbol");
+
+ IPersistentMap map = Mappings;
+ object o;
+ Var v = null;
+ // race condition
+ while ((o = map.valAt(sym)) == null)
+ {
+ if (v == null)
+ v = new Var(this, sym);
+ IPersistentMap newMap = map.assoc(sym, v);
+ _mappings.CompareAndSet(map, newMap);
+ map = Mappings;
+ }
+ if ((o is Var) && ((Var)o).Namespace == this)
+ return (Var)o;
+
+ // race condition
+ throw new InvalidOperationException(String.Format("{0} already refers to: {1} in namespace: {2}", sym, o, _name));
+ }
+
+ /// <summary>
+ /// Intern a symbol with a specified value.
+ /// </summary>
+ /// <param name="sym">The symbol to intern.</param>
+ /// <param name="val">The value to associate with the symbol.</param>
+ /// <returns>The value that is associated. (only guaranteed == to the value given).</returns>
+ object reference(Symbol sym, object val)
+ {
+ if ( sym.Namespace != null )
+ throw new ArgumentException("Can't intern a namespace-qualified symbol");
+
+ IPersistentMap map = Mappings;
+ object o;
+
+ // race condition
+ while ((o = map.valAt(sym)) == null)
+ {
+ IPersistentMap newMap = map.assoc(sym, val);
+ _mappings.CompareAndSet(map, newMap);
+ map = Mappings;
+ }
+
+ if ( o == val )
+ return o;
+
+ throw new InvalidOperationException(String.Format("{0} already refers to: {1} in namespace: {2}", sym, o, _name));
+ }
+
+ /// <summary>
+ /// Remove a symbol mapping from the namespace.
+ /// </summary>
+ /// <param name="sym">The symbol to remove.</param>
+ public void unmap(Symbol sym)
+ {
+ if (sym.Namespace != null)
+ throw new ArgumentException("Can't unintern a namespace-qualified symbol");
+
+ IPersistentMap map = Mappings;
+ while (map.containsKey(sym))
+ {
+ IPersistentMap newMap = map.without(sym);
+ _mappings.CompareAndSet(map, newMap);
+ map = Mappings;
+ }
+ }
+
+ /// <summary>
+ /// Map a symbol to a Type (import).
+ /// </summary>
+ /// <param name="sym">The symbol to associate with a Type.</param>
+ /// <param name="t">The type to associate with the symbol.</param>
+ /// <returns>The Type.</returns>
+ /// <remarks>Named importClass instead of ImportType for core.clj compatibility.</remarks>
+ public Type importClass(Symbol sym, Type t)
+ {
+ return (Type)reference(sym, t);
+ }
+
+ /// <summary>
+ /// Add a <see cref="Symbol">Symbol</see> to <see cref="Var">Var</see> reference.
+ /// </summary>
+ /// <param name="sym"></param>
+ /// <param name="var"></param>
+ /// <returns></returns>
+ public Var refer(Symbol sym, Var var)
+ {
+ return (Var)reference(sym, var);
+ }
+
+ #endregion
+
+ #region Mappings
+
+ /// <summary>
+ /// Get the value mapped to a symbol.
+ /// </summary>
+ /// <param name="name">The symbol to look up.</param>
+ /// <returns>The mapped value.</returns>
+ public object GetMapping(Symbol name)
+ {
+ return Mappings.valAt(name);
+ }
+
+ /// <summary>
+ /// Find the <see cref="Var">Var</see> mapped to a <see cref="Symbol">Symbol</see>.
+ /// </summary>
+ /// <param name="sym">The symbol to look up.</param>
+ /// <returns>The mapped var.</returns>
+ public Var FindInternedVar(Symbol sym)
+ {
+ Var v = Mappings.valAt(sym) as Var;
+ return (v != null && v.Namespace == this) ? v : null;
+ }
+
+ #endregion
+
+ #region Aliases
+
+ /// <summary>
+ /// Find the <see cref="Namespace">Namespace</see> aliased by a <see cref="Symbol">Symbol</see>.
+ /// </summary>
+ /// <param name="alias">The symbol alias.</param>
+ /// <returns>The aliased namespace</returns>
+ public Namespace LookupAlias(Symbol alias)
+ {
+ return (Namespace)Aliases.valAt(alias);
+ }
+
+ /// <summary>
+ /// Add an alias for a namespace.
+ /// </summary>
+ /// <param name="alias">The alias for the namespace.</param>
+ /// <param name="ns">The namespace being aliased.</param>
+ /// <remarks>Lowercase name for core.clj compatibility</remarks>
+ public void addAlias(Symbol alias, Namespace ns)
+ {
+ if (alias == null || ns == null)
+ throw new NullReferenceException("Expecting Symbol + Namespace");
+
+ IPersistentMap map = Aliases;
+
+ // race condition
+ while (!map.containsKey(alias))
+ {
+ IPersistentMap newMap = map.assoc(alias, ns);
+ _aliases.CompareAndSet(map, newMap);
+ map = Aliases;
+ }
+ // you can rebind an alias, but only to the initially-aliased namespace
+ if (!map.valAt(alias).Equals(ns))
+ throw new InvalidOperationException(String.Format("Alias {0} already exists in namespace {1}, aliasing {2}",
+ alias, _name, map.valAt(alias)));
+ }
+
+ /// <summary>
+ /// Remove an alias.
+ /// </summary>
+ /// <param name="alias">The alias name</param>
+ /// <remarks>Lowercase name for core.clj compatibility</remarks>
+ public void removeAlias(Symbol alias)
+ {
+ IPersistentMap map = Aliases;
+ while (map.containsKey(alias))
+ {
+ IPersistentMap newMap = map.without(alias);
+ _aliases.CompareAndSet(map, newMap);
+ map = Aliases;
+ }
+ }
+
+
+ #endregion
+
+ #region core.clj compatibility
+
+ /// <summary>
+ /// Get the namespace name.
+ /// </summary>
+ /// <returns>The <see cref="Symbol">Symbol</see> naming the namespace.</returns>
+ public Symbol getName()
+ {
+ return Name;
+ }
+
+ /// <summary>
+ /// Get the mappings of the namespace.
+ /// </summary>
+ /// <returns>The mappings.</returns>
+ public IPersistentMap getMappings()
+ {
+ return Mappings;
+ }
+
+ /// <summary>
+ /// Get the aliases.
+ /// </summary>
+ /// <returns>A map of aliases.</returns>
+ public IPersistentMap getAliases()
+ {
+ return Aliases;
+ }
+
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Numbers.cs b/ClojureCLR/Clojure/Clojure/Lib/Numbers.cs new file mode 100644 index 00000000..2ab8a414 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Numbers.cs @@ -0,0 +1,1737 @@ +/**
+ * 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 java.math;
+
+namespace clojure.lang
+{
+ public class Numbers
+ {
+
+ interface Ops
+ {
+ Ops combine(Ops y);
+ Ops opsWith(IntegerOps x);
+ Ops opsWith(LongOps x);
+ Ops opsWith(FloatOps x);
+ Ops opsWith(DoubleOps x);
+ Ops opsWith(RatioOps x);
+ Ops opsWith(BigIntegerOps x);
+ Ops opsWith(BigDecimalOps x);
+ bool isZero(object x);
+ bool isPos(object x);
+ bool isNeg(object x);
+ object add(object x, object y);
+ object multiply(object x, object y);
+ object divide(object x, object y);
+ object quotient(object x, object y);
+ object remainder(object x, object y);
+ bool equiv(object x, object y);
+ bool lt(object x, object y);
+ object negate(object x);
+ object inc(object x);
+ object dec(object x);
+ }
+
+ interface BitOps
+ {
+ BitOps combine(BitOps y);
+ BitOps bitOpsWith(IntegerBitOps x);
+ BitOps bitOpsWith(LongBitOps x);
+ BitOps bitOpsWith(BigIntegerBitOps x);
+ object not(object x);
+ object and(object x, object y);
+ object or(object x, object y);
+ object xor(object x, object y);
+ object andNot(object x, object y);
+ object clearBit(object x, int n);
+ object setBit(object x, int n);
+ object flipBit(object x, int n);
+ bool testBit(object x, int n);
+ object shiftLeft(object x, int n);
+ object shiftRight(object x, int n);
+ }
+
+ #region Basic Ops operations
+
+ public static bool isZero(object x)
+ {
+ return ops(x).isZero(x);
+ }
+
+ public static bool isPos(object x)
+ {
+ return ops(x).isPos(x);
+ }
+
+ public static bool isNeg(object x)
+ {
+ return ops(x).isNeg(x);
+ }
+
+ public static object minus(object x)
+ {
+ return ops(x).negate(x);
+ }
+
+ public static object inc(object x)
+ {
+ return ops(x).inc(x);
+ }
+
+ public static object dec(object x)
+ {
+ return ops(x).dec(x);
+ }
+
+ public static object add(object x, object y)
+ {
+ return ops(x).combine(ops(y)).add(x, y);
+ }
+
+ public static object minus(object x, object y)
+ {
+ Ops yops = ops(y);
+ return ops(x).combine(yops).add(x, yops.negate(y));
+ }
+
+ public static object multiply(object x, object y)
+ {
+ return ops(x).combine(ops(y)).multiply(x, y);
+ }
+
+ public static object divide(object x, object y)
+ {
+ Ops yops = ops(y);
+ if (yops.isZero(y))
+ throw new ArithmeticException("Divide by zero");
+ return ops(x).combine(yops).divide(x, y);
+ }
+
+ public static object quotient(object x, object y)
+ {
+ Ops yops = ops(y);
+ if (yops.isZero(y))
+ throw new ArithmeticException("Divide by zero");
+ return ops(x).combine(yops).quotient(x, y);
+ }
+
+ public static object remainder(object x, object y)
+ {
+ Ops yops = ops(y);
+ if (yops.isZero(y))
+ throw new ArithmeticException("Divide by zero");
+ return ops(x).combine(yops).remainder(x, y);
+ }
+
+
+ public static object DQuotient(double n, double d)
+ {
+ double q = n / d;
+ if (q <= Int32.MaxValue && q >= Int32.MinValue)
+ return (int)q;
+ else
+ // bigint quotient
+ return reduce(new BigDecimal(q).toBigInteger());
+ }
+
+ public static object DRemainder(double n, double d)
+ {
+ double q = n / d;
+ if (q <= Int32.MaxValue && q >= Int32.MinValue)
+ return n - ((int)q) * d;
+ else
+ {
+ // bigint quotient
+ object bq = reduce(new BigDecimal(q).toBigInteger());
+ return n - ((double)bq) * d;
+ }
+ }
+
+ public static Boolean equiv(object x, object y)
+ {
+ return Util.IsNumeric(x)
+ && Util.IsNumeric(y)
+ && ops(x).combine(ops(y)).equiv(x, y);
+ }
+
+ public static bool lt(object x, object y)
+ {
+ return ops(x).combine(ops(y)).lt(x, y);
+ }
+
+ public static bool lte(object x, object y)
+ {
+ return !ops(x).combine(ops(y)).lt(y, x);
+ }
+
+ public static bool gt(object x, object y)
+ {
+ return ops(x).combine(ops(y)).lt(y, x);
+ }
+
+ public static bool gte(object x, object y)
+ {
+ return !ops(x).combine(ops(y)).lt(x, y);
+ }
+
+ public static int compare(object x, object y)
+ {
+ Ops ops1 = ops(x).combine(ops(y));
+ if (ops1.lt(x, y))
+ return -1;
+ else if (ops1.lt(y, x))
+ return 1;
+ else
+ return 0;
+ }
+
+ #endregion
+
+ #region utility methods
+
+ static BigInteger toBigInteger(object x)
+ {
+ if (x is BigInteger)
+ return (BigInteger)x;
+ else
+ return BigInteger.valueOf(Convert.ToInt64(x));
+ }
+
+ static BigDecimal toBigDecimal(object x)
+ {
+ if (x is BigDecimal)
+ return (BigDecimal)x;
+ else if ( x is BigInteger)
+ return new BigDecimal((BigInteger)x);
+ else
+ return BigDecimal.valueOf(Convert.ToInt64(x));
+ }
+
+ // TODO: doublecheck toRatio
+ // Java BigDecimal has unscaledValue and pow.
+ // MS implementation does not.
+ static Ratio toRatio(object x)
+ {
+ if (x is Ratio)
+ return (Ratio)x;
+ else if (x is BigDecimal)
+ {
+ BigDecimal bx = (BigDecimal)x;
+ int scale = bx.scale();
+ if (scale < 0)
+ return new Ratio(bx.toBigInteger(), BigIntegerOne);
+ else
+ return new Ratio(bx.movePointRight(scale).toBigInteger(), BigIntegerTen.pow(scale));
+ }
+ return new Ratio(toBigInteger(x), BigIntegerOne);
+ }
+
+ // TODO: fix rationalize
+ // Java BigDecimal has .valueOf(double)
+ public static object rationalize(object x)
+ {
+ if ( x is float || x is double )
+ return rationalize(new BigDecimal( Convert.ToDouble(x) )); // wrong, should be (double)x));
+ else if ( x is BigDecimal )
+ {
+ BigDecimal bx= (BigDecimal)x;
+ int scale = bx.scale();
+ if (scale < 0)
+ return bx.toBigInteger();
+ else
+ return divide(bx.movePointRight(scale).toBigInteger(), BigIntegerTen.pow(scale));
+ }
+ return x;
+ }
+
+ public static object reduce(BigInteger val)
+ {
+ //return (val.bitLength() < 32) ? (object)val.intValue() : val;
+ int bitLength = val.bitLength();
+ return (bitLength < 32)
+ ? (object)val.intValue()
+ : (bitLength < 64)
+ ? (object)val.longValue()
+ : val;
+ }
+
+ public static object reduce(long val)
+ {
+ if (val >= Int32.MinValue && val <= Int32.MaxValue)
+ return (int)val;
+ else
+ return val;
+ }
+
+ public static object BIDivide(BigInteger n, BigInteger d)
+ {
+ if (d.Equals(BigIntegerZero))
+ throw new ArithmeticException("Divide by zero");
+ BigInteger gcd = n.gcd(d);
+ if (gcd.Equals(BigIntegerZero))
+ return 0;
+ n = n.divide(gcd);
+ d = d.divide(gcd);
+ if (d.Equals(BigIntegerOne))
+ return reduce(n);
+ return new Ratio((d.signum() < 0 ? n.negate() : n),
+ (d.signum() < 0 ? d.negate() : d));
+ }
+
+
+
+ public static BigInteger BigIntegerTen = BigInteger.valueOf(10);
+ public static BigInteger BigIntegerOne = BigInteger.valueOf(1);
+ public static BigInteger BigIntegerZero = BigInteger.valueOf(0);
+
+ public static BigDecimal BigDecimalOne = BigDecimal.valueOf(1);
+
+ #endregion
+
+ #region Basic BitOps operations
+
+ public static object not(object x)
+ {
+ return bitOps(x).not(x);
+ }
+
+ public static object and(object x, object y)
+ {
+ return bitOps(x).combine(bitOps(y)).and(x, y);
+ }
+
+ public static object or(object x, object y)
+ {
+ return bitOps(x).combine(bitOps(y)).or(x, y);
+ }
+
+ public static object xor(object x, object y)
+ {
+ return bitOps(x).combine(bitOps(y)).xor(x, y);
+ }
+
+ public static object andNot(object x, object y)
+ {
+ return bitOps(x).combine(bitOps(y)).andNot(x, y);
+ }
+
+
+ public static object clearBit(object x, int n)
+ {
+ if (n < 0)
+ throw new ArithmeticException("Negative bit index");
+ return bitOps(x).clearBit(x, n);
+ }
+
+ public static object setBit(object x, int n)
+ {
+ if (n < 0)
+ throw new ArithmeticException("Negative bit index");
+ return bitOps(x).setBit(x, n);
+ }
+
+ public static object flipBit(object x, int n)
+ {
+ if (n < 0)
+ throw new ArithmeticException("Negative bit index");
+ return bitOps(x).flipBit(x, n);
+ }
+
+ public static bool testBit(object x, int n)
+ {
+ if (n < 0)
+ throw new ArithmeticException("Negative bit index");
+ return bitOps(x).testBit(x, n);
+ }
+
+ public static object shiftLeft(object x, int n)
+ {
+ return bitOps(x).shiftLeft(x, n);
+ }
+
+ public static object shiftRight(object x, int n)
+ {
+ return bitOps(x).shiftRight(x, n);
+ }
+
+ #endregion
+
+ #region Unchecked arithmetic
+
+
+ public static int unchecked_add(int x, int y)
+ {
+ return x + y;
+ }
+
+ public static int unchecked_subtract(int x, int y)
+ {
+ return x - y;
+ }
+
+ public static int unchecked_negate(int x)
+ {
+ return -x;
+ }
+
+ public static int unchecked_inc(int x)
+ {
+ return x + 1;
+ }
+
+ public static int unchecked_dec(int x)
+ {
+ return x - 1;
+ }
+
+ public static int unchecked_multiply(int x, int y)
+ {
+ return x * y;
+ }
+
+ public static int unchecked_divide(int x, int y)
+ {
+ return x / y;
+ }
+
+ public static int unchecked_remainder(int x, int y)
+ {
+ return x % y;
+ }
+
+
+ public static long unchecked_add(long x, long y)
+ {
+ return x + y;
+ }
+
+ public static long unchecked_subtract(long x, long y)
+ {
+ return x - y;
+ }
+
+ public static long unchecked_negate(long x)
+ {
+ return -x;
+ }
+
+ public static long unchecked_inc(long x)
+ {
+ return x + 1;
+ }
+
+ public static long unchecked_dec(long x)
+ {
+ return x - 1;
+ }
+
+ public static long unchecked_multiply(long x, long y)
+ {
+ return x * y;
+ }
+
+ public static long unchecked_divide(long x, long y)
+ {
+ return x / y;
+ }
+
+ public static long unchecked_remainder(long x, long y)
+ {
+ return x % y;
+ }
+
+ #endregion
+
+ #region Ops/BitOps dispatching
+
+ static readonly IntegerOps INTEGER_OPS = new IntegerOps();
+ static readonly LongOps LONG_OPS = new LongOps();
+ static readonly FloatOps FLOAT_OPS = new FloatOps();
+ static readonly DoubleOps DOUBLE_OPS = new DoubleOps();
+ static readonly RatioOps RATIO_OPS = new RatioOps();
+ static readonly BigIntegerOps BIGINTEGER_OPS = new BigIntegerOps();
+ static readonly BigDecimalOps BIGDECIMAL_OPS = new BigDecimalOps();
+
+ static readonly IntegerBitOps INTEGER_BITOPS = new IntegerBitOps();
+ static readonly LongBitOps LONG_BITOPS = new LongBitOps();
+ static readonly BigIntegerBitOps BIGINTEGER_BITOPS = new BigIntegerBitOps();
+
+ static Ops ops(object x)
+ {
+ Type type = Util.GetNonNullableType(x.GetType());
+ if (!type.IsEnum)
+ {
+
+ switch (Type.GetTypeCode(type))
+ {
+ case TypeCode.Char:
+ case TypeCode.SByte:
+ case TypeCode.Byte:
+ case TypeCode.Int16:
+ case TypeCode.UInt16:
+ case TypeCode.Int32:
+ return INTEGER_OPS;
+ case TypeCode.Double:
+ return DOUBLE_OPS;
+ case TypeCode.Single:
+ return FLOAT_OPS;
+ case TypeCode.UInt32:
+ case TypeCode.Int64:
+ return LONG_OPS;
+ case TypeCode.UInt64:
+ return BIGINTEGER_OPS;
+
+ default:
+ if (type == typeof(BigInteger))
+ return BIGINTEGER_OPS;
+ else if (type == typeof(Ratio))
+ return RATIO_OPS;
+ else if (type == typeof(BigDecimal))
+ return BIGDECIMAL_OPS;
+ else
+ return INTEGER_OPS;
+ }
+ }
+ return INTEGER_OPS;
+ }
+
+
+ static BitOps bitOps(object x)
+ {
+ Type type = Util.GetNonNullableType(x.GetType());
+
+ if (!type.IsEnum)
+ {
+
+ switch (Type.GetTypeCode(type))
+ {
+ case TypeCode.Int32:
+ return INTEGER_BITOPS;
+ case TypeCode.Int64:
+ return LONG_BITOPS;
+ default:
+ if (type == typeof(BigInteger))
+ return BIGINTEGER_BITOPS;
+ else if (Util.IsNumeric(x) || (type == typeof(BigDecimal)) || (type == typeof(Ratio)))
+ throw new ArithmeticException("bit operation on non integer type: " + type);
+ break;
+ }
+ }
+ return INTEGER_BITOPS;
+ }
+
+ #endregion
+
+ class IntegerOps : Ops
+ {
+ #region Ops Members
+
+ public Ops combine(Ops y)
+ {
+ return y.opsWith(this);
+ }
+
+ public Ops opsWith(IntegerOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(LongOps x)
+ {
+ return LONG_OPS;
+ }
+
+ public Ops opsWith(FloatOps x)
+ {
+ return FLOAT_OPS;
+ }
+
+ public Ops opsWith(DoubleOps x)
+ {
+ return DOUBLE_OPS;
+ }
+
+ public Ops opsWith(RatioOps x)
+ {
+ return RATIO_OPS;
+ }
+
+ public Ops opsWith(BigIntegerOps x)
+ {
+ return BIGINTEGER_OPS;
+ }
+
+ public Ops opsWith(BigDecimalOps x)
+ {
+ return BIGDECIMAL_OPS;
+ }
+
+ public bool isZero(object x)
+ {
+ return Convert.ToInt32(x) == 0;
+ }
+
+ public bool isPos(object x)
+ {
+ return Convert.ToInt32(x) > 0;
+ }
+
+ public bool isNeg(object x)
+ {
+ return Convert.ToInt32(x) < 0;
+ }
+
+ public object add(object x, object y)
+ {
+ long ret = Convert.ToInt64(x) + Convert.ToInt64(y);
+ if (ret <= Int32.MaxValue && ret >= Int32.MinValue)
+ return (int)ret;
+ return ret;
+ }
+
+ public object multiply(object x, object y)
+ {
+ long ret = Convert.ToInt64(x) * Convert.ToInt64(y);
+ if (ret <= Int32.MaxValue && ret >= Int32.MinValue)
+ return (int)ret;
+ return ret;
+ }
+
+ static int gcd(int u, int v)
+ {
+ while (v != 0)
+ {
+ int r = u % v;
+ u = v;
+ v = r;
+ }
+ return u;
+ }
+
+ public object divide(object x, object y)
+ {
+ int n = Convert.ToInt32(x);
+ int val = Convert.ToInt32(y);
+ int gcd1 = gcd(n, val);
+ if (gcd1 == 0)
+ return 0;
+
+ n = n / gcd1;
+ int d = val / gcd1;
+ if (d == 1)
+ return n;
+ if (d < 0)
+ {
+ n = -n;
+ d = -d;
+ }
+ return new Ratio(BigInteger.valueOf(n), BigInteger.valueOf(d));
+ }
+
+ public object quotient(object x, object y)
+ {
+ return Convert.ToInt32(x) / Convert.ToInt32(y);
+ }
+
+ public object remainder(object x, object y)
+ {
+ return Convert.ToInt32(x) % Convert.ToInt32(y);
+ }
+
+ public bool equiv(object x, object y)
+ {
+ return Convert.ToInt32(x) == Convert.ToInt32(y);
+ }
+
+ public bool lt(object x, object y)
+ {
+ return Convert.ToInt32(x) < Convert.ToInt32(y);
+ }
+
+ public object negate(object x)
+ {
+ int val = Convert.ToInt32(x);
+ if (val > Int32.MinValue)
+ return -val;
+ return -((long)val);
+ }
+
+ public object inc(object x)
+ {
+ int val = Convert.ToInt32(x);
+ if (val < Int32.MaxValue)
+ return val + 1;
+ return BigInteger.valueOf(((long)val) + 1);
+ }
+
+ public object dec(object x)
+ {
+ int val = Convert.ToInt32(x);
+ if (val > Int32.MinValue)
+ return val - 1;
+ return BigInteger.valueOf(((long)val) - 1);
+ }
+
+ #endregion
+ }
+
+ class LongOps : Ops
+ {
+ #region Ops Members
+
+ public Ops combine(Ops y)
+ {
+ return y.opsWith(this);
+ }
+
+ public Ops opsWith(IntegerOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(LongOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(FloatOps x)
+ {
+ return FLOAT_OPS;
+ }
+
+ public Ops opsWith(DoubleOps x)
+ {
+ return DOUBLE_OPS;
+ }
+
+ public Ops opsWith(RatioOps x)
+ {
+ return RATIO_OPS;
+ }
+
+ public Ops opsWith(BigIntegerOps x)
+ {
+ return BIGINTEGER_OPS;
+ }
+
+ public Ops opsWith(BigDecimalOps x)
+ {
+ return BIGDECIMAL_OPS;
+ }
+
+ public bool isZero(object x)
+ {
+ return Convert.ToInt64(x) == 0;
+ }
+
+ public bool isPos(object x)
+ {
+ return Convert.ToInt64(x) > 0;
+ }
+
+ public bool isNeg(object x)
+ {
+ return Convert.ToInt64(x) < 0;
+ }
+
+ public object add(object x, object y)
+ {
+ long lx = Convert.ToInt64(x);
+ long ly = Convert.ToInt64(y);
+ long ret = lx + ly;
+ if ((ret ^ lx) < 0 && (ret ^ ly) < 0)
+ return BIGINTEGER_OPS.add(x, y);
+ return ret;
+ }
+
+ public object multiply(object x, object y)
+ {
+ long lx = Convert.ToInt64(x);
+ long ly = Convert.ToInt64(y);
+ long ret = lx * ly;
+ if (ly != 0 && ret / ly != lx)
+ return BIGINTEGER_OPS.multiply(x, y);
+ return ret;
+ }
+
+ static long gcd(long u, long v)
+ {
+ while (v != 0)
+ {
+ long r = u % v;
+ u = v;
+ v = r;
+ }
+ return u;
+ }
+
+ public object divide(object x, object y)
+ {
+ long n = Convert.ToInt64(x);
+ long val = Convert.ToInt64(y);
+ long gcd1 = gcd(n, val);
+ if (gcd1 == 0)
+ return 0;
+
+ n = n / gcd1;
+ long d = val / gcd1;
+ if (d == 1)
+ return n;
+ if (d < 0)
+ {
+ n = -n;
+ d = -d;
+ }
+ return new Ratio(BigInteger.valueOf(n), BigInteger.valueOf(d));
+ }
+
+ public object quotient(object x, object y)
+ {
+ return Convert.ToInt64(x) / Convert.ToInt64(y);
+ }
+
+ public object remainder(object x, object y)
+ {
+ return Convert.ToInt64(x) % Convert.ToInt64(y);
+ }
+
+ public bool equiv(object x, object y)
+ {
+ return Convert.ToInt64(x) == Convert.ToInt64(y);
+ }
+
+ public bool lt(object x, object y)
+ {
+ return Convert.ToInt64(x) < Convert.ToInt64(y);
+ }
+
+ public object negate(object x)
+ {
+ long val = Convert.ToInt64(x);
+ if (val > Int64.MinValue)
+ return -val;
+ return BigInteger.valueOf(val).negate();
+ }
+
+ public object inc(object x)
+ {
+ long val = Convert.ToInt64(x);
+ if (val < Int64.MaxValue)
+ return val + 1;
+ return BIGINTEGER_OPS.inc(x);
+ }
+
+ public object dec(object x)
+ {
+ long val = Convert.ToInt64(x);
+ if (val > Int64.MinValue)
+ return val - 1;
+ return BIGINTEGER_OPS.dec(x);
+ }
+
+ #endregion
+ }
+
+ class FloatOps : Ops
+ {
+ #region Ops Members
+
+ public Ops combine(Ops y)
+ {
+ return y.opsWith(this);
+ }
+
+ public Ops opsWith(IntegerOps x)
+ {
+ return this ;
+ }
+
+ public Ops opsWith(LongOps x)
+ {
+ return this;
+ }
+ public Ops opsWith(FloatOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(DoubleOps x)
+ {
+ return DOUBLE_OPS;
+ }
+
+ public Ops opsWith(RatioOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(BigIntegerOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(BigDecimalOps x)
+ {
+ return this;
+ }
+
+ public bool isZero(object x)
+ {
+ return Convert.ToSingle(x) == 0;
+ }
+
+ public bool isPos(object x)
+ {
+ return Convert.ToSingle(x) > 0;
+ }
+
+ public bool isNeg(object x)
+ {
+ return Convert.ToSingle(x) < 0;
+ }
+
+ public object add(object x, object y)
+ {
+ return Convert.ToSingle(x) + Convert.ToSingle(y);
+ }
+
+ public object multiply(object x, object y)
+ {
+ return Convert.ToSingle(x) * Convert.ToSingle(y);
+ }
+
+ public object divide(object x, object y)
+ {
+ return Convert.ToSingle(x) / Convert.ToSingle(y);
+ }
+
+ public object quotient(object x, object y)
+ {
+ return Numbers.DQuotient(Convert.ToDouble(x), Convert.ToDouble(y));
+ }
+
+ public object remainder(object x, object y)
+ {
+ return Numbers.DRemainder(Convert.ToDouble(x), Convert.ToDouble(y));
+ }
+
+ public bool equiv(object x, object y)
+ {
+ return Convert.ToSingle(x) == Convert.ToSingle(y);
+ }
+
+ public bool lt(object x, object y)
+ {
+ return Convert.ToSingle(x) < Convert.ToSingle(y);
+ }
+
+ public object negate(object x)
+ {
+ return -Convert.ToSingle(x);
+ }
+
+ public object inc(object x)
+ {
+ return Convert.ToSingle(x) + 1;
+ }
+
+ public object dec(object x)
+ {
+ return Convert.ToSingle(x) - 1;
+ }
+
+ #endregion
+ }
+
+ class DoubleOps : Ops
+ {
+ #region Ops Members
+
+ public Ops combine(Ops y)
+ {
+ return y.opsWith(this);
+ }
+
+ public Ops opsWith(IntegerOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(LongOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(FloatOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(DoubleOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(RatioOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(BigIntegerOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(BigDecimalOps x)
+ {
+ return this;
+ }
+
+ public bool isZero(object x)
+ {
+ return Convert.ToDouble(x) == 0;
+ }
+
+ public bool isPos(object x)
+ {
+ return Convert.ToDouble(x) > 0;
+ }
+
+ public bool isNeg(object x)
+ {
+ return Convert.ToDouble(x) < 0;
+ }
+
+ public object add(object x, object y)
+ {
+ return Convert.ToDouble(x) + Convert.ToDouble(y);
+ }
+
+ public object multiply(object x, object y)
+ {
+ return Convert.ToDouble(x) * Convert.ToDouble(y);
+ }
+
+ public object divide(object x, object y)
+ {
+ return Convert.ToDouble(x) / Convert.ToDouble(y);
+ }
+
+ public object quotient(object x, object y)
+ {
+ return Numbers.DQuotient(Convert.ToDouble(x), Convert.ToDouble(y));
+ }
+
+ public object remainder(object x, object y)
+ {
+ return Numbers.DRemainder(Convert.ToDouble(x), Convert.ToDouble(y));
+ }
+
+ public bool equiv(object x, object y)
+ {
+ return Convert.ToDouble(x) == Convert.ToDouble(y);
+ }
+
+ public bool lt(object x, object y)
+ {
+ return Convert.ToDouble(x) < Convert.ToDouble(y);
+ }
+
+ public object negate(object x)
+ {
+ return -Convert.ToDouble(x);
+ }
+
+ public object inc(object x)
+ {
+ return Convert.ToDouble(x) + 1;
+ }
+
+ public object dec(object x)
+ {
+ return Convert.ToDouble(x) + 1;
+ }
+
+ #endregion
+ }
+
+ class RatioOps : Ops
+ {
+ #region Ops Members
+
+ public Ops combine(Ops y)
+ {
+ return y.opsWith(this);
+ }
+
+ public Ops opsWith(IntegerOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(LongOps x)
+ {
+ return this;
+ }
+ public Ops opsWith(FloatOps x)
+ {
+ return FLOAT_OPS;
+ }
+
+ public Ops opsWith(DoubleOps x)
+ {
+ return DOUBLE_OPS;
+ }
+
+ public Ops opsWith(RatioOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(BigIntegerOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(BigDecimalOps x)
+ {
+ return this;
+ }
+
+ public bool isZero(object x)
+ {
+ Ratio r = toRatio(x);
+ return r.Numerator.signum()== 0;
+ }
+
+ public bool isPos(object x)
+ {
+ Ratio r = toRatio(x);
+ return r.Numerator.signum() > 0;
+ }
+
+ public bool isNeg(object x)
+ {
+ Ratio r = toRatio(x);
+ return r.Numerator.signum() < 0;
+ }
+
+ public object add(object x, object y)
+ {
+ Ratio rx = toRatio(x);
+ Ratio ry = toRatio(y);
+ return divide(ry.Numerator.multiply(rx.Denominator)
+ .add(rx.Numerator.multiply(ry.Denominator)),
+ ry.Denominator.multiply(rx.Denominator));
+ }
+
+ public object multiply(object x, object y)
+ {
+ Ratio rx = toRatio(x);
+ Ratio ry = toRatio(y);
+ return Numbers.divide(ry.Numerator.multiply(rx.Numerator),
+ ry.Denominator.multiply(rx.Denominator));
+ }
+
+ public object divide(object x, object y)
+ {
+ Ratio rx = toRatio(x);
+ Ratio ry = toRatio(y);
+ return Numbers.divide(ry.Denominator.multiply(rx.Numerator),
+ ry.Numerator.multiply(rx.Denominator));
+ }
+
+ public object quotient(object x, object y)
+ {
+ Ratio rx = toRatio(x);
+ Ratio ry = toRatio(y);
+ BigInteger q = rx.Numerator.multiply(ry.Denominator)
+ .divide(rx.Denominator.multiply(ry.Numerator));
+ return reduce(q);
+ }
+
+ public object remainder(object x, object y)
+ {
+ Ratio rx = toRatio(x);
+ Ratio ry = toRatio(y);
+ BigInteger q = rx.Numerator.multiply(ry.Denominator)
+ .divide(rx.Denominator.multiply(ry.Numerator));
+ return Numbers.minus(x, Numbers.multiply(q, y));
+ }
+
+ public bool equiv(object x, object y)
+ {
+ Ratio rx = toRatio(x);
+ Ratio ry = toRatio(y);
+ return rx.Numerator.Equals(ry.Numerator)
+ && rx.Denominator.Equals(ry.Denominator);
+ }
+
+ public bool lt(object x, object y)
+ {
+ Ratio rx = toRatio(x);
+ Ratio ry = toRatio(y);
+ return Numbers.lt(rx.Numerator.multiply(ry.Denominator),
+ ry.Numerator.multiply(rx.Denominator));
+ }
+
+ public object negate(object x)
+ {
+ Ratio rx = toRatio(x);
+ return new Ratio(rx.Numerator.negate(), rx.Denominator);
+ }
+
+ public object inc(object x)
+ {
+ return Numbers.add(x, 1);
+ }
+
+ public object dec(object x)
+ {
+ return Numbers.add(x, -1);
+ }
+
+ #endregion
+ }
+
+ class BigIntegerOps : Ops
+ {
+ #region Ops Members
+
+ public Ops combine(Ops y)
+ {
+ return y.opsWith(this);
+ }
+
+ public Ops opsWith(IntegerOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(LongOps x)
+ {
+ return this;
+ }
+ public Ops opsWith(FloatOps x)
+ {
+ return FLOAT_OPS;
+ }
+
+ public Ops opsWith(DoubleOps x)
+ {
+ return DOUBLE_OPS;
+ }
+
+ public Ops opsWith(RatioOps x)
+ {
+ return RATIO_OPS;
+ }
+
+ public Ops opsWith(BigIntegerOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(BigDecimalOps x)
+ {
+ return BIGDECIMAL_OPS;
+ }
+
+ public bool isZero(object x)
+ {
+ BigInteger bx = toBigInteger(x);
+ return bx.signum() == 0;
+ }
+
+ public bool isPos(object x)
+ {
+ BigInteger bx = toBigInteger(x);
+ return bx.signum() > 0;
+ }
+
+ public bool isNeg(object x)
+ {
+ BigInteger bx = toBigInteger(x);
+ return bx.signum() < 0;
+ }
+
+ public object add(object x, object y)
+ {
+ return reduce(toBigInteger(x).add(toBigInteger(y)));
+ }
+
+ public object multiply(object x, object y)
+ {
+ return reduce(toBigInteger(x).multiply(toBigInteger(y)));
+ }
+
+ public object divide(object x, object y)
+ {
+ return BIDivide(toBigInteger(x), toBigInteger(y));
+ }
+
+ public object quotient(object x, object y)
+ {
+ return toBigInteger(x).divide(toBigInteger(y));
+ }
+
+ public object remainder(object x, object y)
+ {
+ return toBigInteger(x).remainder(toBigInteger(y));
+ }
+
+ public bool equiv(object x, object y)
+ {
+ return toBigInteger(x).Equals(toBigInteger(y));
+ }
+
+ public bool lt(object x, object y)
+ {
+ return toBigInteger(x).compareTo(toBigInteger(y)) < 0;
+ }
+
+ public object negate(object x)
+ {
+ return toBigInteger(x).negate();
+ }
+
+ public object inc(object x)
+ {
+ BigInteger bx = toBigInteger(x);
+ return reduce(bx.add(BigIntegerOne));
+ }
+
+ public object dec(object x)
+ {
+ BigInteger bx = toBigInteger(x);
+ return reduce(bx.subtract(BigIntegerOne));
+ }
+
+ #endregion
+ }
+
+ class BigDecimalOps : Ops
+ {
+ #region Ops Members
+
+ public Ops combine(Ops y)
+ {
+ return y.opsWith(this);
+ }
+
+ public Ops opsWith(IntegerOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(LongOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(FloatOps x)
+ {
+ return FLOAT_OPS;
+ }
+
+ public Ops opsWith(DoubleOps x)
+ {
+ return DOUBLE_OPS;
+ }
+
+ public Ops opsWith(RatioOps x)
+ {
+ return RATIO_OPS;
+ }
+
+ public Ops opsWith(BigIntegerOps x)
+ {
+ return this;
+ }
+
+ public Ops opsWith(BigDecimalOps x)
+ {
+ return this;
+ }
+
+ public bool isZero(object x)
+ {
+ BigDecimal bx = toBigDecimal(x);
+ return bx.signum() == 0;
+ }
+
+ public bool isPos(object x)
+ {
+ BigDecimal bx = toBigDecimal(x);
+ return bx.signum() > 0;
+ }
+
+ public bool isNeg(object x)
+ {
+ BigDecimal bx = toBigDecimal(x);
+ return bx.signum() < 0;
+ }
+
+ public object add(object x, object y)
+ {
+ return toBigDecimal(x).add(toBigDecimal(y));
+ }
+
+ public object multiply(object x, object y)
+ {
+ return toBigDecimal(x).multiply(toBigDecimal(y));
+ }
+
+ // TODO: fiture out what the rounding mode should be
+ public object divide(object x, object y)
+ {
+ return toBigDecimal(x).divide(toBigDecimal(y), BigDecimal.ROUND_HALF_EVEN);
+ }
+
+ // TODO: this is just plain wrong;
+ // Java version uses .divideToIntegralValue
+ public object quotient(object x, object y)
+ {
+ return toBigDecimal(x).divide(toBigDecimal(y), 0);
+ }
+
+ // TODO: this is just plain wrong;
+ // Java version uses .remainder
+ public object remainder(object x, object y)
+ {
+ return toBigDecimal(x).divide(toBigDecimal(y), 0);
+ }
+
+ public bool equiv(object x, object y)
+ {
+ return toBigDecimal(x).Equals(toBigDecimal(y));
+ }
+
+ public bool lt(object x, object y)
+ {
+ return toBigDecimal(x).compareTo(toBigDecimal(y)) < 0;
+ }
+
+ public object negate(object x)
+ {
+ return toBigDecimal(x).negate();
+ }
+
+ public object inc(object x)
+ {
+ BigDecimal bx = toBigDecimal(x);
+ return bx.add(BigDecimalOne);
+ }
+
+ public object dec(object x)
+ {
+ BigDecimal bx = toBigDecimal(x);
+ return bx.subtract(BigDecimalOne);
+ }
+
+ #endregion
+ }
+
+ class IntegerBitOps : BitOps
+ {
+ #region BitOps Members
+
+ public BitOps combine(BitOps y)
+ {
+ return y.bitOpsWith(this); ;
+ }
+
+ public BitOps bitOpsWith(IntegerBitOps x)
+ {
+ return this;
+ }
+
+ public BitOps bitOpsWith(LongBitOps x)
+ {
+ return LONG_BITOPS;
+ }
+
+ public BitOps bitOpsWith(BigIntegerBitOps x)
+ {
+ return BIGINTEGER_BITOPS;
+ }
+
+ public object not(object x)
+ {
+ return ~Convert.ToInt32(x);
+ }
+
+ public object and(object x, object y)
+ {
+ return Convert.ToInt32(x) & Convert.ToInt32(y);
+ }
+
+ public object or(object x, object y)
+ {
+ return Convert.ToInt32(x) | Convert.ToInt32(y);
+ }
+
+ public object xor(object x, object y)
+ {
+ return Convert.ToInt32(x) ^ Convert.ToInt32(y);
+ }
+
+ public object andNot(object x, object y)
+ {
+ return Convert.ToInt32(x) & ~Convert.ToInt32(y);
+ }
+
+ public object clearBit(object x, int n)
+ {
+ if (n < 31)
+ return Convert.ToInt32(x) & ~(1 << n);
+ else if (n < 63)
+ return Convert.ToInt64(x) & ~(1L << n);
+ else
+ return toBigInteger(x).clearBit(n);
+ }
+
+ public object setBit(object x, int n)
+ {
+ if (n < 31)
+ return Convert.ToInt32(x) | (1 << n);
+ else if (n < 63)
+ return Convert.ToInt64(x) | (1L << n);
+ else
+ return toBigInteger(x).setBit(n);
+ }
+
+ public object flipBit(object x, int n)
+ {
+ if (n < 31)
+ return Convert.ToInt32(x) ^ (1 << n);
+ else if (n < 63)
+ return Convert.ToInt64(x) ^ (1L << n);
+ else
+ return toBigInteger(x).flipBit(n);
+ }
+
+ public bool testBit(object x, int n)
+ {
+ if (n < 31)
+ return (Convert.ToInt32(x) & (1 << n)) != 0;
+ else if (n < 63)
+ return (Convert.ToInt64(x) & (1L << n)) != 0;
+ else
+ return toBigInteger(x).testBit(n);
+ }
+
+ public object shiftLeft(object x, int n)
+ {
+ if (n < 32)
+ return (n < 0)
+ ? shiftRight(x, -n)
+ : reduce(Convert.ToInt64(x) << n);
+ else
+ return reduce(toBigInteger(x).shiftLeft(n));
+ }
+
+ public object shiftRight(object x, int n)
+ {
+ return (n < 0)
+ ? shiftLeft(x, -n)
+ : Convert.ToInt32(x) >> n;
+ }
+
+ #endregion
+ }
+
+ class LongBitOps : BitOps
+ {
+ #region BitOps Members
+
+ public BitOps combine(BitOps y)
+ {
+ return y.bitOpsWith(this);
+ }
+
+ public BitOps bitOpsWith(IntegerBitOps x)
+ {
+ return this;
+ }
+
+ public BitOps bitOpsWith(LongBitOps x)
+ {
+ return this;
+ }
+
+ public BitOps bitOpsWith(BigIntegerBitOps x)
+ {
+ return BIGINTEGER_BITOPS;
+ }
+
+ public object not(object x)
+ {
+ return ~Convert.ToInt64(x);
+ }
+
+ public object and(object x, object y)
+ {
+ return Convert.ToInt64(x) & Convert.ToInt64(y);
+ }
+
+ public object or(object x, object y)
+ {
+ return Convert.ToInt64(x) | Convert.ToInt64(y);
+ }
+
+ public object xor(object x, object y)
+ {
+ return Convert.ToInt64(x) ^ Convert.ToInt64(y);
+ }
+
+ public object andNot(object x, object y)
+ {
+ return Convert.ToInt64(x) & ~Convert.ToInt64(y);
+ }
+
+ public object clearBit(object x, int n)
+ {
+ if (n < 63)
+ return Convert.ToInt64(x) & ~(1L << n);
+ else
+ return toBigInteger(x).clearBit(n);
+ }
+
+ public object setBit(object x, int n)
+ {
+ if (n < 63)
+ return Convert.ToInt64(x) | (1L << n);
+ else
+ return toBigInteger(x).setBit(n);
+ }
+
+ public object flipBit(object x, int n)
+ {
+ if (n < 63)
+ return Convert.ToInt64(x) ^ (1L << n);
+ else
+ return toBigInteger(x).flipBit(n);
+ }
+
+ public bool testBit(object x, int n)
+ {
+ if (n < 63)
+ return (Convert.ToInt64(x) & (1L << n)) != 0;
+ else
+ return toBigInteger(x).testBit(n);
+ }
+
+ public object shiftLeft(object x, int n)
+ {
+ return n < 0
+ ? shiftRight(x,-n)
+ : reduce(toBigInteger(x).shiftLeft(n));
+ }
+
+ public object shiftRight(object x, int n)
+ {
+ return n < 0
+ ? shiftLeft(x, -n)
+ : Convert.ToInt64(x) >> n;
+ }
+
+ #endregion
+ }
+
+ class BigIntegerBitOps : BitOps
+ {
+ #region BitOps Members
+
+ public BitOps combine(BitOps y)
+ {
+ return y.bitOpsWith(this);
+ }
+
+ public BitOps bitOpsWith(IntegerBitOps x)
+ {
+ return this;
+ }
+
+ public BitOps bitOpsWith(LongBitOps x)
+ {
+ return this;
+ }
+
+ public BitOps bitOpsWith(BigIntegerBitOps x)
+ {
+ return this;
+ }
+
+ public object not(object x)
+ {
+ return toBigInteger(x).not();
+ }
+
+ public object and(object x, object y)
+ {
+ return toBigInteger(x).and(toBigInteger(y));
+ }
+
+ public object or(object x, object y)
+ {
+ return toBigInteger(x).or(toBigInteger(y));
+ }
+
+ public object xor(object x, object y)
+ {
+ return toBigInteger(x).xor(toBigInteger(y));
+ }
+
+ public object andNot(object x, object y)
+ {
+ return toBigInteger(x).andNot(toBigInteger(y));
+ }
+
+ public object clearBit(object x, int n)
+ {
+ return toBigInteger(x).clearBit(n);
+ }
+
+ public object setBit(object x, int n)
+ {
+ return toBigInteger(x).setBit(n);
+ }
+
+ public object flipBit(object x, int n)
+ {
+ return toBigInteger(x).flipBit(n);
+ }
+
+ public bool testBit(object x, int n)
+ {
+ return toBigInteger(x).testBit(n);
+ }
+
+ public object shiftLeft(object x, int n)
+ {
+ return toBigInteger(x).shiftLeft(n);
+ }
+
+ public object shiftRight(object x, int n)
+ {
+ return toBigInteger(x).shiftRight(n);
+ }
+
+ #endregion
+ }
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Obj.cs b/ClojureCLR/Clojure/Clojure/Lib/Obj.cs new file mode 100644 index 00000000..8804bebf --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Obj.cs @@ -0,0 +1,78 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Provides a basic implementation of <see cref="IObj">IObj</see> functionality.
+ /// </summary>
+ public abstract class Obj: IObj
+ {
+ #region Data
+
+ /// <summary>
+ /// The metatdata for the object.
+ /// </summary>
+ protected readonly IPersistentMap _meta;
+
+ #endregion
+
+ #region C-tors
+
+ /// <summary>
+ /// Initializes a new instance of <see cref="Obj">Obj</see> that has null metadata.
+ /// </summary>
+ public Obj()
+ {
+ _meta = null;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of <see cref="Obj">Obj</see> that has
+ /// the given <see cref="IPersistentMap">IPersistentMap</see> as its metadata.
+ /// </summary>
+ /// <param name="meta">The map used to initialize the metadata.</param>
+ public Obj(IPersistentMap meta)
+ {
+ _meta = meta;
+ }
+
+ #endregion
+
+ #region IMeta Members
+
+ /// <summary>
+ /// Gets the metadata attached to the object.
+ /// </summary>
+ /// <returns>An immutable map representing the object's metadata.</returns>
+ public IPersistentMap meta()
+ {
+ return _meta;
+ }
+
+ #endregion
+
+ #region IObj methods
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public abstract IObj withMeta(IPersistentMap meta);
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/PersistentArrayMap.cs b/ClojureCLR/Clojure/Clojure/Lib/PersistentArrayMap.cs new file mode 100644 index 00000000..7db2031c --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/PersistentArrayMap.cs @@ -0,0 +1,462 @@ +/**
+ * 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.Collections;
+
+namespace clojure.lang
+{
+
+ /// <summary>
+ /// Implements a persistent map as an array of alternating keys/values(suitable for small maps only).
+ /// </summary>
+ /// <remarks>
+ /// <para>Note that instances of this class are constant values, i.e., add/remove etc return new values.</para>
+ /// <para>Copies the array on every change, so only appropriate for <i>very small</i> maps</para>
+ /// <para><value>null</value> keys and values are okay,
+ /// but you won't be able to distinguish a <value>null</value> value via <see cref="valAt">valAt</see> --
+ /// use <see cref="contains">contains</see> or <see cref="entryAt">entryAt</see>.</para>
+ /// </remarks>
+ public class PersistentArrayMap : APersistentMap
+ {
+ // any reason not to seal this class?
+
+ #region Data
+
+ /// <summary>
+ /// The maximum number of entries to hold using this implementation.
+ /// </summary>
+ /// <remarks>
+ /// <para>Operations adding more than this number of entries should switch to another implementation.</para>
+ /// <para>The value was changed from 8 to 16 in Java Rev 1159 to improve proxy perf -- we don't have proxy yet,
+ /// but I changed it here anyway.</para>
+ /// </remarks>
+ internal const int HASHTABLE_THRESHOLD = 16;
+
+ /// <summary>
+ /// The array holding the key/value pairs.
+ /// </summary>
+ /// <remarks>The i-th pair is in _array[2*i] and _array[2*i+1].</remarks>
+ protected readonly object[] _array;
+
+ /// <summary>
+ /// An empty <see cref="PersistentArrayMap">PersistentArrayMap</see>. Constant.
+ /// </summary>
+ public static readonly PersistentArrayMap EMPTY = new PersistentArrayMap();
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ /// <summary>
+ /// Create a <see cref="PersistentArrayMap">PersistentArrayMap</see> (if small enough, else create a <see cref="PersistentHashMap">PersistentHashMap</see>.
+ /// </summary>
+ /// <param name="other">The BCL map to initialize from</param>
+ /// <returns>A new persistent map.</returns>
+ public static IPersistentMap create(IDictionary other)
+ {
+ // Java version has this. Seems wasteful.
+ //IPersistentMap ret = EMPTY;
+ //foreach (DictionaryEntry e in other)
+ //{
+ // ret = ret.assoc(e.Key, e.Value);
+ //}
+ //return ret;
+ if (other.Count > HASHTABLE_THRESHOLD / 2)
+ return PersistentHashMap.create(other);
+
+ object[] array = new object[other.Count * 2];
+ int i=0;
+ foreach (DictionaryEntry e in other)
+ {
+ array[2 * i] = e.Key;
+ array[2 * i + 1] = e.Value;
+ i++;
+ }
+ return new PersistentArrayMap(array);
+ }
+
+ /// <summary>
+ /// Create a <see cref="PersistentArrayMap">PersistentArrayMap</see> with new data but same metadata as the current object.
+ /// </summary>
+ /// <param name="init">The new key/value array</param>
+ /// <returns>A new <see cref="PersistentArrayMap">PersistentArrayMap</see>.</returns>
+ /// <remarks>The array is used directly. Do not modify externally or immutability is sacrificed.</remarks>
+ PersistentArrayMap create(params object[] init)
+ {
+ return new PersistentArrayMap(meta(), init);
+ }
+
+
+ /// <summary>
+ /// Create an empty <see cref="PersistentArrayMap">PersistentArrayMap</see>.
+ /// </summary>
+ protected PersistentArrayMap()
+ {
+ _array = new object[] { };
+ }
+
+ /// <summary>
+ /// Initializes a <see cref="PersistentArrayMap">PersistentArrayMap</see> to use the supplied key/value array.
+ /// </summary>
+ /// <param name="init">An array with alternating keys and values.</param>
+ /// <remarks>The array is used directly. Do not modify externally or immutability is sacrificed.</remarks>
+ public PersistentArrayMap(object[] init)
+ {
+ if (init.Length % 2 != 0)
+ throw new ArgumentException("Key/value array must have an even number of elements.");
+ _array = init;
+ }
+
+ /// <summary>
+ /// Initializes a <see cref="PersistentArrayMap">PersistentArrayMap</see> to use the supplied key/value array and metadata.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ /// <param name="init">An array with alternating keys and values.</param>
+ /// <remarks>The array is used directly. Do not modify externally or immutability is sacrificed.</remarks>
+ protected PersistentArrayMap(IPersistentMap meta, object[] init)
+ : base(meta)
+ {
+ if (init.Length % 2 != 0)
+ throw new ArgumentException("Key/value array must have an even number of elements.");
+
+ _array = init;
+ }
+
+ #endregion
+
+ #region IObj members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ // Java version as follows
+ //return new PersistentArrayMap(meta, _array);
+ // But the usual pattern is this:
+
+ return meta == _meta
+ ? this
+ : new PersistentArrayMap(meta, _array);
+ }
+
+
+ #endregion
+
+ #region Associative members
+
+ /// <summary>
+ /// Gets the index of the key in the array.
+ /// </summary>
+ /// <param name="key">The key to search for.</param>
+ /// <returns>The index of the key if found; -1 otherwise.</returns>
+ private int IndexOfKey(object key)
+ {
+ for (int i = 0; i < _array.Length; i += 2)
+ if (EqualKey(_array[i],key))
+ return i;
+ return -1;
+ }
+
+ /// <summary>
+ /// Compare two keys for equality.
+ /// </summary>
+ /// <param name="k1">The first key to compare.</param>
+ /// <param name="k2">The second key to compare.</param>
+ /// <returns></returns>
+ /// <remarks>Handles nulls properly.</remarks>
+ private bool EqualKey(object k1, object k2)
+ {
+ if (k1 == null)
+ return k2 == null;
+ return k1.Equals(k2);
+ }
+ /// <summary>
+ /// Test if the map contains a key.
+ /// </summary>
+ /// <param name="key">The key to test for membership</param>
+ /// <returns>True if the key is in this map.</returns>
+ public override bool containsKey(object key)
+ {
+ return IndexOfKey(key) >= 0;
+ }
+
+ /// <summary>
+ /// Returns the key/value pair for this key.
+ /// </summary>
+ /// <param name="key">The key to retrieve</param>
+ /// <returns>The key/value pair for the key, or null if the key is not in the map.</returns>
+ public override IMapEntry entryAt(object key)
+ {
+ int i = IndexOfKey(key);
+ return i >= 0
+ ? new MapEntry(_array[i], _array[i + 1])
+ : null;
+ }
+
+ /// <summary>
+ /// Gets the value associated with a key.
+ /// </summary>
+ /// <param name="key">The key to look up.</param>
+ /// <returns>The associated value. (Throws an exception if key is not present.)</returns>
+ public override object valAt(object key)
+ {
+ return valAt(key, null);
+ }
+
+ /// <summary>
+ /// Gets the value associated with a key.
+ /// </summary>
+ /// <param name="key">The key to look up.</param>
+ /// <param name="notFound">The value to return if the key is not present.</param>
+ /// <returns>The associated value (or <c>notFound</c> if the key is not present.</returns>
+ public override object valAt(object key, object notFound)
+ {
+ int i = IndexOfKey(key);
+ return i >= 0
+ ? _array[i + 1]
+ : notFound;
+ }
+
+ #endregion
+
+ #region IPersistentCollection members
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ public override int count()
+ {
+ return _array.Length / 2;
+ }
+
+ /// <summary>
+ /// Gets an ISeq to allow first/rest iteration through the collection.
+ /// </summary>
+ /// <returns>An ISeq for iteration.</returns>
+ public override ISeq seq()
+ {
+ return _array.Length > 0
+ ? new Seq(_array, 0)
+ : null;
+ }
+
+ /// <summary>
+ /// Gets an empty collection of the same type.
+ /// </summary>
+ /// <returns>An emtpy collection.</returns>
+ public override IPersistentCollection empty()
+ {
+ return (IPersistentCollection)EMPTY.withMeta(meta());
+ }
+
+ #endregion
+
+ #region IPersistentMap members
+
+ /// <summary>
+ /// Add a new key/value pair.
+ /// </summary>
+ /// <param name="key">The key</param>
+ /// <param name="val">The value</param>
+ /// <returns>A new map with key+value added.</returns>
+ /// <remarks>Overwrites an exising value for the <paramref name="key"/>, if present.</remarks>
+ public override IPersistentMap assoc(object key, object val)
+ {
+ int i = IndexOfKey(key);
+ object[] newArray;
+ if (i >= 0)
+ {
+ // already have key, same sized replacement
+ if (_array[i + 1] == val) // no change, no-op
+ return this;
+ newArray = (object[]) _array.Clone();
+ newArray[i + 1] = val;
+ }
+ else
+ {
+ // new key, grow
+ if (_array.Length > HASHTABLE_THRESHOLD)
+ return createHT(_array).assoc(key, val);
+ newArray = new object[_array.Length + 2];
+ if (_array.Length > 0)
+ Array.Copy(_array, 0, newArray, 2, _array.Length);
+ newArray[0] = key;
+ newArray[1] = val;
+ }
+ return create(newArray);
+ }
+
+ /// <summary>
+ /// Create an <see cref="IPersistentMap">IPersistentMap</see> to hold the data when
+ /// an operation causes the threshhold size to be exceeded.
+ /// </summary>
+ /// <param name="init">The array of key/value pairs.</param>
+ /// <returns>A new <see cref="IPersistentMap">IPersistentMap</see>.</returns>
+ private IPersistentMap createHT(object[] init)
+ {
+ return PersistentHashMap.create(meta(), init);
+ }
+
+ /// <summary>
+ /// Add a new key/value pair.
+ /// </summary>
+ /// <param name="key">The key</param>
+ /// <param name="val">The value</param>
+ /// <returns>A new map with key+value added.</returns>
+ /// <remarks>Throws an exception if <paramref name="key"/> has a value already.</remarks>
+ public override IPersistentMap assocEx(object key, object val)
+ {
+ int i = IndexOfKey(key);
+ if (i >= 0)
+ throw new Exception("Key already present.");
+ return assoc(key, val);
+ }
+
+ /// <summary>
+ /// Remove a key entry.
+ /// </summary>
+ /// <param name="key">The key to remove</param>
+ /// <returns>A new map with the key removed (or the same map if the key is not contained).</returns>
+ public override IPersistentMap without(object key)
+ {
+ int i = IndexOfKey(key);
+ if (i >= 0)
+ {
+ // key exists, remove
+ int newlen = _array.Length - 2;
+ if (newlen == 0)
+ return (IPersistentMap)empty();
+ object[] newArray = new object[newlen];
+ for (int s = 0, d = 0; s < _array.Length; s += 2)
+ if (s != i) // skip key to be removed
+ {
+ newArray[d] = _array[s];
+ newArray[d + 1] = _array[s + 1];
+ d += 2;
+ }
+ return create(newArray);
+ }
+ else
+ return this;
+ }
+
+ #endregion
+
+
+ /// <summary>
+ /// Internal class providing an <see cref="ISeq">ISeq</see>
+ /// for <see cref="PersistentArrayMap">PersistentArrayMap</see>s.
+ /// </summary>
+ protected sealed class Seq : ASeq, Counted
+ {
+ #region Data
+
+ /// <summary>
+ /// The array to iterate over.
+ /// </summary>
+ private readonly object[] _array;
+
+ /// <summary>
+ /// Current index position in the array.
+ /// </summary>
+ private readonly int _i;
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ /// <summary>
+ /// Initialize the sequence to a given array and index.
+ /// </summary>
+ /// <param name="array">The array being sequenced over.</param>
+ /// <param name="i">The current index.</param>
+ public Seq(object[] array, int i)
+ {
+ _array = array;
+ _i = i;
+ }
+
+ /// <summary>
+ /// Initialize the sequence with given metatdata and array/index.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ /// <param name="array">The array being sequenced over.</param>
+ /// <param name="i">The current index.</param>
+ public Seq(IPersistentMap meta, object[] array, int i)
+ : base(meta)
+ {
+ _array = array;
+ _i = i;
+ }
+
+ #endregion
+
+ #region ISeq members
+
+ /// <summary>
+ /// Gets the first item.
+ /// </summary>
+ /// <returns>The first item.</returns>
+ public override object first()
+ {
+ return new MapEntry(_array[_i], _array[_i + 1]);
+ }
+
+ /// <summary>
+ /// Gets the rest of the sequence.
+ /// </summary>
+ /// <returns>The rest of the sequence, or <c>null</c> if no more elements.</returns>
+ public override ISeq rest()
+ {
+ return _i + 2 < _array.Length
+ ? new Seq(_array, _i + 2)
+ : null;
+ }
+
+ #endregion
+
+ #region IPersistentCollection members
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ public override int count()
+ {
+ return (_array.Length - _i) / 2;
+ }
+
+ #endregion
+
+ #region IObj members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return new Seq(meta, _array, _i);
+ }
+
+ #endregion
+
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/PersistentHashMap.cs b/ClojureCLR/Clojure/Clojure/Lib/PersistentHashMap.cs new file mode 100644 index 00000000..f0f9324d --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/PersistentHashMap.cs @@ -0,0 +1,927 @@ +/**
+ * 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.Collections;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// A persistent rendition of Phil Bagwell's Hash Array Mapped Trie
+ /// <para>Uses path copying for persistence.</para>
+ /// <para>HashCollision leaves vs extended hashing</para>
+ /// <para>Node polymorphism vs conditionals</para>
+ /// <para>No sub-tree pools or root-resizing</para>
+ /// <para>Any errors are Rich Hickey's, except those that are mine.</para>
+ /// </summary>
+ /// <remarks>
+ /// <para>Uses path copying for persistence.</para>
+ /// <para>HashCollision leaves vs extended hashing</para>
+ /// <para>Node polymorphism vs conditionals</para>
+ /// <para>No sub-tree pools or root-resizing</para>
+ /// <para>Any errors are Rich Hickey's (so he says), except those that I introduced.</para>
+ /// </remarks>
+ public class PersistentHashMap: APersistentMap
+ {
+ #region Data
+
+ /// <summary>
+ /// The number of entries in the map.
+ /// </summary>
+ protected readonly int _count;
+
+ /// <summary>
+ /// The root of the trie.
+ /// </summary>
+ protected readonly INode _root;
+
+ /// <summary>
+ /// An empty <see cref="PersistentHashMap">PersistentHashMap</see>.
+ /// </summary>
+ public static readonly PersistentHashMap EMPTY = new PersistentHashMap(0, new EmptyNode());
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ // TODO: Is it worth speeding up the initializers that create so many unnecessary nodes?
+
+ /// <summary>
+ /// Create a <see cref="PersistentHashMap">PersistentHashMap</see> initialized from a CLR dictionary.
+ /// </summary>
+ /// <param name="other">The dictionary to copy from.</param>
+ /// <returns>A <see cref="PersistentHashMap">PersistentHashMap</see>.</returns>
+ public static IPersistentMap create(IDictionary other)
+ {
+ IPersistentMap ret = EMPTY;
+ foreach (DictionaryEntry e in other)
+ ret = ret.assoc(e.Key, e.Value);
+ return ret;
+ }
+
+ /// <summary>
+ /// Create a <see cref="PersistentHashMap">PersistentHashMap</see> initialized from an array of alternating keys and values.
+ /// </summary>
+ /// <param name="init">An array of alternating keys and values.</param>
+ /// <returns>A <see cref="PersistentHashMap">PersistentHashMap</see>.</returns>
+ public static PersistentHashMap create(params object[] init)
+ {
+ IPersistentMap ret = EMPTY;
+ for (int i = 0; i < init.Length; i += 2)
+ ret = ret.assoc(init[i], init[i + 1]);
+ return (PersistentHashMap)ret;
+ }
+
+ /// <summary>
+ /// Create a <see cref="PersistentHashMap">PersistentHashMap</see> initialized from an IList of alternating keys and values.
+ /// </summary>
+ /// <param name="init">An IList of alternating keys and values.</param>
+ /// <returns>A <see cref="PersistentHashMap">PersistentHashMap</see>.</returns>
+ public static PersistentHashMap create(IList init)
+ {
+ IPersistentMap ret = EMPTY;
+ for (IEnumerator i = init.GetEnumerator(); i.MoveNext(); )
+ {
+ object key = i.Current;
+ if (!i.MoveNext())
+ throw new ArgumentException(String.Format("No value supplied for key: {0}", key));
+ object val = i.Current;
+ ret = ret.assoc(key, val);
+ }
+ return (PersistentHashMap)ret;
+ }
+
+ /// <summary>
+ /// Create a <see cref="PersistentHashMap">PersistentHashMap</see> initialized from
+ /// an <see cref="ISeq">ISeq</see> of alternating keys and values.
+ /// </summary>
+ /// <param name="items">An <see cref="ISeq">ISeq</see> of alternating keys and values.</param>
+ /// <returns>A <see cref="PersistentHashMap">PersistentHashMap</see>.</returns>
+ public static PersistentHashMap create(ISeq items)
+ {
+ IPersistentMap ret = EMPTY;
+ for ( ; items != null; items = items.rest().rest() )
+ {
+ if ( items.rest() == null )
+ throw new ArgumentException(String.Format("No value supplied for key: {0}", items.first()));
+ ret = ret.assoc(items.first(), items.rest().first() );
+ }
+ return (PersistentHashMap)ret;
+ }
+
+
+ /// <summary>
+ /// Create a <see cref="PersistentHashMap">PersistentHashMap</see> with given metadata initialized from an array of alternating keys and values.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ /// <param name="init">An array of alternating keys and values.</param>
+ /// <returns>A <see cref="PersistentHashMap">PersistentHashMap</see>.</returns>
+ public static PersistentHashMap create(IPersistentMap meta, params object[] init)
+ {
+ IPersistentMap ret = (IPersistentMap)EMPTY.withMeta(meta);
+ for (int i = 0; i < init.Length; i += 2)
+ ret = ret.assoc(init[i], init[i + 1]);
+ return (PersistentHashMap)ret;
+ }
+
+ /// <summary>
+ /// Initialize a <see cref="PersistentHashMap">PersistentHashMap</see> with a given count and root node.
+ /// </summary>
+ /// <param name="count">The count.</param>
+ /// <param name="root">The root node.</param>
+ PersistentHashMap(int count, INode root)
+ {
+ _count = count;
+ _root = root;
+ }
+
+ /// <summary>
+ /// Initialize a <see cref="PersistentHashMap">PersistentHashMap</see> with given metadata, count and root node.
+ /// </summary>
+ /// <param name="meta">The metadata to attach</param>
+ /// <param name="count">The count.</param>
+ /// <param name="root">The root node.</param>
+ PersistentHashMap(IPersistentMap meta, int count, INode root)
+ : base(meta)
+ {
+ _count = count;
+ _root = root;
+ }
+
+ #endregion
+
+ #region IObj members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ // Java does not include change test
+ return meta == _meta
+ ? this
+ : new PersistentHashMap(meta, _count, _root);
+ }
+
+ #endregion
+
+ #region Associative
+
+ /// <summary>
+ /// Test if the map contains a key.
+ /// </summary>
+ /// <param name="key">The key to test for membership</param>
+ /// <returns>True if the key is in this map.</returns>
+ public override bool containsKey(object key)
+ {
+ return entryAt(key) != null;
+ }
+
+ /// <summary>
+ /// Returns the key/value pair for this key.
+ /// </summary>
+ /// <param name="key">The key to retrieve</param>
+ /// <returns>The key/value pair for the key, or null if the key is not in the map.</returns>
+ public override IMapEntry entryAt(object key)
+ {
+ return (IMapEntry)_root.find(Util.Hash(key),key);
+ }
+
+ /// <summary>
+ /// Gets the value associated with a key.
+ /// </summary>
+ /// <param name="key">The key to look up.</param>
+ /// <returns>The associated value. (Throws an exception if key is not present.)</returns>
+ public override object valAt(object key)
+ {
+ return valAt(key, null);
+ }
+
+ /// <summary>
+ /// Gets the value associated with a key.
+ /// </summary>
+ /// <param name="key">The key to look up.</param>
+ /// <param name="notFound">The value to return if the key is not present.</param>
+ /// <returns>The associated value (or <c>notFound</c> if the key is not present.</returns>
+ public override object valAt(object key, object notFound)
+ {
+ IMapEntry e = entryAt(key);
+ return e != null
+ ? e.val()
+ : notFound;
+ }
+
+ #endregion
+
+ #region IPersistentMap
+
+ /// <summary>
+ /// Add a new key/value pair.
+ /// </summary>
+ /// <param name="key">The key</param>
+ /// <param name="val">The value</param>
+ /// <returns>A new map with key+value added.</returns>
+ /// <remarks>Overwrites an exising value for the <paramref name="key"/>, if present.</remarks>
+ public override IPersistentMap assoc(object key, object val)
+ {
+ Box addedLeaf = new Box(null);
+ INode newroot = _root.assoc(0, Util.Hash(key), key, val, addedLeaf);
+ return newroot == _root
+ ? this
+ : new PersistentHashMap(meta(), addedLeaf.Val == null ? _count : _count + 1, newroot);
+ }
+
+
+ /// <summary>
+ /// Add a new key/value pair.
+ /// </summary>
+ /// <param name="key">The key</param>
+ /// <param name="val">The value</param>
+ /// <returns>A new map with key+value added.</returns>
+ /// <remarks>Throws an exception if <paramref name="key"/> has a value already.</remarks>
+ public override IPersistentMap assocEx(object key, object val)
+ {
+ if (containsKey(key))
+ throw new Exception("Key already present");
+ return assoc(key, val);
+ }
+
+
+ /// <summary>
+ /// Remove a key entry.
+ /// </summary>
+ /// <param name="key">The key to remove</param>
+ /// <returns>A new map with the key removed (or the same map if the key is not contained).</returns>
+ public override IPersistentMap without(object key)
+ {
+ INode newroot = _root.without(Util.Hash(key), key);
+
+ return newroot == _root
+ ? this
+ : (newroot == null
+ ? (IPersistentMap) EMPTY.withMeta(meta())
+ : new PersistentHashMap(meta(), _count - 1, newroot));
+
+ }
+
+ #endregion
+
+ #region IPersistentCollection
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ public override int count()
+ {
+ return _count;
+ }
+
+ /// <summary>
+ /// Gets an ISeq to allow first/rest iteration through the collection.
+ /// </summary>
+ /// <returns>An ISeq for iteration.</returns>
+ public override ISeq seq()
+ {
+ return _root.nodeSeq();
+ }
+
+ /// <summary>
+ /// Gets an empty collection of the same type.
+ /// </summary>
+ /// <returns>An emtpy collection.</returns>
+ public override IPersistentCollection empty()
+ {
+ return (IPersistentCollection) EMPTY.withMeta(meta());
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Interface for all nodes in the trie.
+ /// </summary>
+ public interface INode
+ {
+ /// <summary>
+ /// Return a trie with a new key/value pair.
+ /// </summary>
+ /// <param name="shift"></param>
+ /// <param name="hash"></param>
+ /// <param name="key"></param>
+ /// <param name="val"></param>
+ /// <param name="addedLeaf"></param>
+ /// <returns></returns>
+ INode assoc(int shift, int hash, object key, object val, Box addedLeaf);
+
+ /// <summary>
+ /// Return a trie with the given key removed.
+ /// </summary>
+ /// <param name="hash"></param>
+ /// <param name="key"></param>
+ /// <returns></returns>
+ INode without(int hash, object key);
+
+ /// <summary>
+ /// Gets the node containing a given key.
+ /// </summary>
+ /// <param name="hash"></param>
+ /// <param name="key"></param>
+ /// <returns></returns>
+ INode find(int hash, object key);
+
+ /// <summary>
+ /// Return an <see cref="ISeq">ISeq</see> with iterating the tree defined by the current node.
+ /// </summary>
+ /// <returns>An <see cref="ISeq">ISeq</see> </returns>
+ ISeq nodeSeq();
+
+ /// <summary>
+ /// Get the hash for the current ndoe.
+ /// </summary>
+ /// <returns></returns>
+ int getHash();
+ }
+
+ /// <summary>
+ /// A node with no keys. Represents the empty map.
+ /// </summary>
+ sealed class EmptyNode : INode
+ {
+ #region INode Members
+
+ public INode assoc(int shift, int hash, object key, object val, Box addedLeaf)
+ {
+ INode ret = new LeafNode(hash, key, val);
+ addedLeaf.Val = ret;
+ return ret;
+ }
+
+ public INode without(int hash, object key)
+ {
+ return this;
+ }
+
+ public INode find(int hash, object key)
+ {
+ return null;
+ }
+
+ public ISeq nodeSeq()
+ {
+ return null;
+ }
+
+ public int getHash()
+ {
+ return 0;
+ }
+
+ #endregion
+ }
+
+ /// <summary>
+ /// An internal node in the trie with all branches filled.
+ /// </summary>
+ sealed class FullNode : INode
+ {
+ #region Data
+
+ readonly INode[] _nodes;
+ readonly int _shift;
+ readonly int _hash;
+
+ #endregion
+
+ #region C-tors
+
+ internal FullNode(INode[] nodes, int shift)
+ {
+ _nodes = nodes;
+ _shift = shift;
+ _hash = nodes[0].getHash();
+ }
+
+ #endregion
+
+ #region Calculations
+
+ static int bitpos(int hash, int shift)
+ {
+ return 1 << Util.Mask(hash, shift);
+ }
+
+ #endregion
+
+ #region INode Members
+
+ public INode assoc(int shift, int hash, object key, object val, Box addedLeaf)
+ {
+ int idx = Util.Mask(hash, shift);
+
+ INode n = _nodes[idx].assoc(shift + 5, hash, key, val, addedLeaf);
+ if (n == _nodes[idx])
+ return this;
+ else
+ {
+ INode[] newNodes = (INode[])_nodes.Clone();
+ newNodes[idx] = n;
+ return new FullNode(newNodes, shift);
+ }
+ }
+
+
+ public INode without(int hash, object key)
+ {
+ int idx = Util.Mask(hash, _shift);
+ INode n = _nodes[idx].without(hash, key);
+ if (n != _nodes[idx])
+ {
+ if (n == null)
+ {
+ INode[] newnodes1 = new INode[_nodes.Length - 1];
+ Array.Copy(_nodes, 0, newnodes1, 0, idx);
+ Array.Copy(_nodes, idx + 1, newnodes1, idx, _nodes.Length - (idx + 1));
+ return new BitmapIndexedNode(~bitpos(hash, _shift), newnodes1, _shift);
+ }
+ INode[] newnodes = (INode[])_nodes.Clone();
+ newnodes[idx] = n;
+ return new FullNode(newnodes, _shift);
+ }
+ return this;
+ }
+
+ public INode find(int hash, object key)
+ {
+ return _nodes[Util.Mask(hash, _shift)].find(hash, key);
+ }
+
+ public ISeq nodeSeq()
+ {
+ return Seq.create(this, 0);
+ }
+
+ public int getHash()
+ {
+ return _hash;
+ }
+
+ #endregion
+
+ private sealed class Seq : ASeq
+ {
+ #region Data
+
+ readonly ISeq _s;
+ readonly int _i;
+ readonly FullNode _node;
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ Seq(ISeq s, int i, FullNode node)
+ {
+ _s = s;
+ _i = i;
+ _node = node;
+ }
+
+ Seq(IPersistentMap meta, ISeq s, int i, FullNode node)
+ :base(meta)
+ {
+ _s = s;
+ _i = i;
+ _node = node;
+ }
+
+ public static ISeq create(FullNode node, int i)
+ {
+ return i >= node._nodes.Length
+ ? null
+ : new Seq(node._nodes[i].nodeSeq(), i, node);
+ }
+
+ #endregion
+
+ #region ISeq members
+
+ public override object first()
+ {
+ return _s.first();
+ }
+
+ public override ISeq rest()
+ {
+ ISeq nexts = _s.rest();
+ return nexts != null
+ ? new Seq(nexts, _i, _node)
+ : create(_node, _i + 1);
+ }
+
+
+ #endregion
+
+ #region IObj members
+
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return new Seq(meta, _s, _i, _node);
+ }
+
+ #endregion
+ }
+ }
+
+ /// <summary>
+ /// Represents a leaf node in the tree, corresponding to single map entry (key/value).
+ /// </summary>
+ sealed class LeafNode : AMapEntry, INode
+ {
+ #region Data
+
+ readonly int _hash;
+ readonly object _key;
+ readonly object _val;
+
+ #endregion
+
+ #region C-tors
+
+ public LeafNode(int hash, object key, object val)
+ {
+ _hash = hash;
+ _key = key;
+ _val = val;
+ }
+
+ #endregion
+
+ #region IMapEntry members
+
+ public override object key()
+ {
+ return _key;
+ }
+
+ public override object val()
+ {
+ return _val;
+ }
+
+ #endregion
+
+ #region INode Members
+
+ public INode assoc(int shift, int hash, object key, object val, Box addedLeaf)
+ {
+ if (hash == _hash)
+ {
+ if (Util.equals(key, _key))
+ {
+ if (val == _val)
+ return this;
+ // note - do not set AddedLeaf, since we are replacing
+ else
+ return new LeafNode(hash, key, val);
+ }
+ else
+ {
+ // hash collision, same hash, different keys
+ LeafNode newLeaf = new LeafNode(hash, key, val);
+ addedLeaf.Val = newLeaf;
+ return new HashCollisionNode(hash, this, newLeaf);
+ }
+ }
+ else
+ return BitmapIndexedNode.create(shift, this, hash, key, val, addedLeaf);
+ }
+
+ public INode without(int hash, object key)
+ {
+ return (hash == _hash && Util.equals(key, _key))
+ ? null
+ : this;
+ }
+
+ public INode find(int hash, object key)
+ {
+ return (hash == _hash && Util.equals(key, _key))
+ ? this
+ : null;
+ }
+
+ public ISeq nodeSeq()
+ {
+ return (ISeq)RT.cons(this,null);
+ }
+
+ public int getHash()
+ {
+ return _hash;
+ }
+
+ #endregion
+ }
+
+ /// <summary>
+ /// Represents an internal node in the trie, not full.
+ /// </summary>
+ sealed class BitmapIndexedNode : INode
+ {
+ #region Data
+
+ readonly int _bitmap;
+ readonly INode[] _nodes;
+ readonly int _shift;
+ readonly int _hash;
+
+ #endregion
+
+ #region Calculations
+
+ static int bitpos(int hash, int shift)
+ {
+ return 1 << Util.Mask(hash, shift);
+ }
+
+ int index(int bit)
+ {
+ return Util.BitCount(_bitmap & (bit - 1));
+ }
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ internal BitmapIndexedNode(int bitmap, INode[] nodes, int shift)
+ {
+ _bitmap = bitmap;
+ _nodes = nodes;
+ _shift = shift;
+ _hash = nodes[0].getHash();
+ }
+
+ internal static INode create(int bitmap, INode[] nodes, int shift)
+ {
+ return bitmap == -1
+ ? (INode)new FullNode(nodes, shift)
+ : (INode)new BitmapIndexedNode(bitmap, nodes, shift);
+ }
+
+ internal static INode create(int shift, INode branch, int hash, object key, object val, Box addedLeaf)
+ {
+ return (new BitmapIndexedNode(bitpos(branch.getHash(), shift), new INode[] { branch }, shift))
+ .assoc(shift, hash, key, val, addedLeaf);
+ }
+
+ #endregion
+
+ #region INode Members
+
+ public INode assoc(int shift, int hash, object key, object val, Box addedLeaf)
+ {
+ int bit = bitpos(hash, shift);
+ int idx = index(bit);
+ if ((_bitmap & bit) != 0)
+ {
+ INode n = _nodes[idx].assoc(shift + 5, hash, key, val, addedLeaf);
+ if (n == _nodes[idx])
+ return this;
+ else
+ {
+ INode[] newnodes = (INode[])_nodes.Clone();
+ newnodes[idx] = n;
+ return new BitmapIndexedNode(_bitmap, newnodes, shift);
+ }
+ }
+ else
+ {
+ INode[] newnodes = new INode[_nodes.Length + 1];
+ Array.Copy(_nodes, 0, newnodes, 0, idx);
+ addedLeaf.Val = newnodes[idx] = new LeafNode(hash, key, val);
+ Array.Copy(_nodes, idx, newnodes, idx + 1, _nodes.Length - idx);
+ return create(_bitmap | bit, newnodes, shift);
+ }
+ }
+
+ public INode without(int hash, object key)
+ {
+ int bit = bitpos(hash, _shift);
+ if ((_bitmap & bit) != 0)
+ {
+ int idx = index(bit);
+ INode n = _nodes[idx].without(hash, key);
+ if (n != _nodes[idx])
+ {
+ if (n == null)
+ {
+ if (_bitmap == bit)
+ return null;
+ INode[] newnodes1 = new INode[_nodes.Length - 1];
+ Array.Copy(_nodes, 0, newnodes1, 0, idx);
+ Array.Copy(_nodes, idx + 1, newnodes1, idx, _nodes.Length - (idx + 1));
+ return new BitmapIndexedNode(_bitmap & ~bit, newnodes1, _shift);
+ }
+ INode[] newnodes = (INode[])_nodes.Clone();
+ newnodes[idx] = n;
+ return new BitmapIndexedNode(_bitmap, newnodes, _shift);
+ }
+ }
+ return this;
+ }
+
+
+ public INode find(int hash, object key)
+ {
+ int bit = bitpos(hash, _shift);
+ return ((_bitmap & bit) != 0)
+ ? _nodes[index(bit)].find(hash, key)
+ : null;
+ }
+
+ public ISeq nodeSeq()
+ {
+ return Seq.create(this,0);
+ }
+
+ public int getHash()
+ {
+ return _hash;
+ }
+
+ #endregion
+
+ sealed class Seq : ASeq
+ {
+ #region Data
+
+ readonly ISeq _s;
+ readonly int _i;
+ readonly BitmapIndexedNode _node;
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ Seq(ISeq s, int i, BitmapIndexedNode node)
+ {
+ _s = s;
+ _i = i;
+ _node = node;
+ }
+
+ Seq(IPersistentMap meta, ISeq s, int i, BitmapIndexedNode node)
+ :base(meta)
+ {
+ _s = s;
+ _i = i;
+ _node = node;
+ }
+
+ public static ISeq create(BitmapIndexedNode node, int i)
+ {
+ return i >= node._nodes.Length
+ ? null
+ : new Seq(node._nodes[i].nodeSeq(), i, node);
+ }
+
+
+ #endregion
+
+ #region ISeq members
+
+ public override object first()
+ {
+ return _s.first();
+ }
+
+ public override ISeq rest()
+ {
+ ISeq nexts = _s.rest();
+ return ( nexts != null )
+ ? new Seq(nexts,_i,_node)
+ : create(_node, _i+1);
+ }
+
+ #endregion
+
+ #region IObj members
+
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return new Seq(meta, _s, _i, _node);
+ }
+
+ #endregion
+ }
+ }
+
+ /// <summary>
+ /// Represents a leaf node corresponding to multiple map entries, all with keys that have the same hash value.
+ /// </summary>
+ sealed class HashCollisionNode : INode
+ {
+ #region Data
+
+ readonly int _hash;
+ readonly LeafNode[] _leaves;
+
+ #endregion
+
+ #region C-tors
+
+ public HashCollisionNode(int hash, params LeafNode[] leaves)
+ {
+ _hash = hash;
+ _leaves = leaves;
+ }
+
+ #endregion
+
+ #region details
+
+ int findIndex(int hash, object key)
+ {
+ for (int i = 0; i < _leaves.Length; i++)
+ {
+ if (_leaves[i].find(hash, key) != null)
+ return i;
+ }
+ return -1;
+ }
+
+ #endregion
+
+ #region INode Members
+
+ public INode assoc(int shift, int hash, object key, object val, Box addedLeaf)
+ {
+ if (_hash == hash)
+ {
+ int idx = findIndex(hash, key);
+ if (idx != -1)
+ {
+ if (_leaves[idx].val() == val)
+ return this;
+ LeafNode[] newLeaves1 = (LeafNode[])_leaves.Clone();
+ // Note: do not set addedLeaf, since we are replacing
+ newLeaves1[idx] = new LeafNode(hash, key, val);
+ return new HashCollisionNode(hash, newLeaves1);
+ }
+ LeafNode[] newLeaves = new LeafNode[_leaves.Length + 1];
+ Array.Copy(_leaves, 0, newLeaves, 0, _leaves.Length);
+ addedLeaf.Val = newLeaves[_leaves.Length] = new LeafNode(hash, key, val);
+ return new HashCollisionNode(hash, newLeaves);
+ }
+ return BitmapIndexedNode.create(shift, this, hash, key, val, addedLeaf);
+ }
+
+ public INode without(int hash, object key)
+ {
+ int idx = findIndex(hash, key);
+ if (idx == -1)
+ return this;
+ if (_leaves.Length == 2)
+ return idx == 0 ? _leaves[1] : _leaves[0];
+ LeafNode[] newLeaves = new LeafNode[_leaves.Length - 1];
+ Array.Copy(_leaves, 0, newLeaves, 0, idx);
+ Array.Copy(_leaves, idx + 1, newLeaves, idx, _leaves.Length - (idx + 1));
+ return new HashCollisionNode(hash, newLeaves);
+ }
+
+ public INode find(int hash, object key)
+ {
+ int idx = findIndex(hash, key);
+ return idx != -1
+ ? _leaves[idx]
+ : null;
+ }
+
+ public ISeq nodeSeq()
+ {
+ return ArraySeq.create((object[])_leaves);
+ }
+
+ public int getHash()
+ {
+ return _hash;
+ }
+
+ #endregion
+ }
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/PersistentHashSet.cs b/ClojureCLR/Clojure/Clojure/Lib/PersistentHashSet.cs new file mode 100644 index 00000000..b5a75232 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/PersistentHashSet.cs @@ -0,0 +1,152 @@ +/**
+ * 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.Collections;
+
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// A persistent set built on a <see cref="IPersistentMap">IPersistentMap</see>.
+ /// </summary>
+ public class PersistentHashSet: APersistentSet
+ {
+ #region Data
+
+ /// <summary>
+ /// An empty <see cref="PersistentHashSet">PersistentHashSet</see>.
+ /// </summary>
+ public static readonly PersistentHashSet EMPTY = new PersistentHashSet(null, PersistentHashMap.EMPTY);
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ /// <summary>
+ /// Create a <see cref="PersistentHashSet">PersistentHashSet</see> initialized from an array of items.
+ /// </summary>
+ /// <param name="init">An array of items.</param>
+ /// <returns>A <see cref="PersistentHashSet">PersistentHashSet</see>.</returns>
+ public static PersistentHashSet create(params object[] init)
+ {
+ PersistentHashSet ret = EMPTY;
+ for (int i = 0; i < init.Length; ++i)
+ ret = (PersistentHashSet)ret.cons(init[i]);
+ return ret;
+ }
+
+ /// <summary>
+ /// Create a <see cref="PersistentHashSet">PersistentHashSet</see> initialized from an IList of items.
+ /// </summary>
+ /// <param name="init">An IList of items.</param>
+ /// <returns>A <see cref="PersistentHashSet">PersistentHashSet</see>.</returns>
+ /// <remarks>This is called just 'create' in the Java version. CLR can't handle this overload when called on something that is
+ /// both an IList and an ISeq, such as any ASeq.</remarks>
+ public static PersistentHashSet create1(IList init)
+ {
+ // TODO: Look for other collection classes with an overload problem on create(IList) and create(ISeq).
+ PersistentHashSet ret = EMPTY;
+ foreach (object obj in init)
+ ret = (PersistentHashSet)ret.cons(obj);
+ return ret;
+ }
+
+ /// <summary>
+ /// Create a <see cref="PersistentHashSet">PersistentHashSet</see> initialized from an <see cref="ISeq">ISeq</see> of items.
+ /// </summary>
+ /// <param name="items">An <see cref="ISeq">ISeq</see> of items</param>
+ /// <returns>A <see cref="PersistentHashSet">PersistentHashSet</see>.</returns>
+ public static PersistentHashSet create(ISeq items)
+ {
+ PersistentHashSet ret = EMPTY;
+ for (; items != null; items = items.rest())
+ ret = (PersistentHashSet)ret.cons(items.first());
+ return ret;
+ }
+
+ /// <summary>
+ /// Initialize a <see cref="PersistentHashSet">PersistentHashSet</see> to use given metadata and underlying map.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ /// <param name="impl">The implementating map.</param>
+ PersistentHashSet(IPersistentMap meta, IPersistentMap impl)
+ : base(meta, impl)
+ {
+ }
+
+ #endregion
+
+ #region IObj members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return (_meta == meta)
+ ? this
+ : new PersistentHashSet(meta, _impl);
+
+ // JAVA: did not follow the usual pattern.
+ // return new PersistentHashSet(meta, _impl);
+ }
+
+ #endregion
+
+ #region IPersistentSet members
+
+ /// <summary>
+ /// Get a set with the given item removed.
+ /// </summary>
+ /// <param name="key">The item to remove.</param>
+ /// <returns>A new set with the item removed.</returns>
+ public override IPersistentSet disjoin(object key)
+ {
+ return contains(key)
+ ? new PersistentHashSet(meta(), _impl.without(key))
+ : this;
+ }
+
+
+ #endregion
+
+ #region IPersistentCollection members
+
+ /// <summary>
+ /// Returns a new collection that has the given element cons'd on front of the existing collection.
+ /// </summary>
+ /// <param name="o">An item to put at the front of the collection.</param>
+ /// <returns>A new immutable collection with the item added.</returns>
+ public override IPersistentCollection cons(object o)
+ {
+ return contains(o)
+ ? this
+ : new PersistentHashSet(meta(), _impl.assoc(o, o));
+ }
+
+ /// <summary>
+ /// Gets an empty collection of the same type.
+ /// </summary>
+ /// <returns>An emtpy collection.</returns>
+ public override IPersistentCollection empty()
+ {
+ return (IPersistentCollection) EMPTY.withMeta(meta());
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/PersistentList.cs b/ClojureCLR/Clojure/Clojure/Lib/PersistentList.cs new file mode 100644 index 00000000..6970f10d --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/PersistentList.cs @@ -0,0 +1,500 @@ +/**
+ * 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.Collections;
+using System.Linq;
+using System.Text;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents a persistent list.
+ /// </summary>
+ public class PersistentList: ASeq, IPersistentList, IReduce, IList, Counted
+ {
+
+ #region Data
+
+ /// <summary>
+ /// The first item in the list.
+ /// </summary>
+ private readonly object _first;
+
+ /// <summary>
+ /// The rest of the list.
+ /// </summary>
+ private readonly IPersistentList _rest;
+
+ /// <summary>
+ /// The number of items in the list.
+ /// </summary>
+ private readonly int _count;
+
+ /// <summary>
+ /// An empty <see cref="IPersistentList">IPersistentList</see>.
+ /// </summary>
+ public static readonly EmptyList EMPTY = new EmptyList(null);
+
+ #endregion
+
+ #region C-tors
+
+ /// <summary>
+ /// Initializes a list of one member.
+ /// </summary>
+ /// <param name="first">The one member.</param>
+ public PersistentList(object first)
+ {
+ this._first = first;
+ this._rest = null;
+ this._count = 1;
+ }
+
+ /// <summary>
+ /// Create a list initialized from a given IList.
+ /// </summary>
+ /// <param name="init">The list to initialize from.</param>
+ /// <returns>A list.</returns>
+ public static IPersistentList create(IList init)
+ {
+ IPersistentList ret = EMPTY;
+ for (int i = init.Count - 1; i >= 0; --i)
+ ret = (IPersistentList) ret.cons(init[i]);
+ return ret;
+ }
+
+ /// <summary>
+ /// Initialize a list with given metadata, first element and rest of list.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ /// <param name="first">The first element in the list.</param>
+ /// <param name="rest">The rest of the list.</param>
+ /// <param name="count">The number of elements in the list.</param>
+ PersistentList(IPersistentMap meta, Object first, IPersistentList rest, int count)
+ : base(meta)
+ {
+ this._first = first;
+ this._rest = rest;
+ this._count = count;
+ }
+
+ /// <summary>
+ /// Provides a function to create a list from a sequence of arguments. (Internal use only.)
+ /// </summary>
+ /// <remarks>Internal use only. Used to interface with core.clj.</remarks>
+ sealed class PLCreator : RestFn
+ {
+ /// <summary>
+ /// Create the function with requiredArity=0.
+ /// </summary>
+ public PLCreator()
+ : base(0)
+ {
+ }
+
+ /// <summary>
+ /// The creator method.
+ /// </summary>
+ /// <param name="args">A sequence of elements.</param>
+ /// <returns>A new list.</returns>
+ protected override object doInvoke(object args)
+ {
+ if (args is ArraySeq)
+ {
+ object[] argsarray = (object[])((ArraySeq)args).ToArray();
+ IPersistentList ret = EMPTY;
+ for (int i = argsarray.Length - 1; i >= 0; i--)
+ ret = (IPersistentList)ret.cons(argsarray[i]);
+ return ret;
+ }
+
+ List<object> list = new List<object>();
+ for (ISeq s = RT.seq(args); s != null; s = s.rest())
+ list.Add(s.first());
+ return create(list);
+ }
+ }
+
+
+ /// <summary>
+ /// An <see cref="IFn">IFn</see> to create a list from a sequence of items.
+ /// </summary>
+ /// <remarks>The name is without our usual leading underscore for compatiblity with core.clj.</remarks>
+ public static IFn creator = new PLCreator();
+
+
+ #endregion
+
+ #region IObj members
+
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return meta == _meta
+ ? this
+ : new PersistentList(meta, _first, _rest, _count);
+ }
+
+
+ #endregion
+
+ #region ISeq members
+
+ /// <summary>
+ /// Gets the first item.
+ /// </summary>
+ /// <returns>The first item.</returns>
+ public override object first()
+ {
+ return _first;
+ }
+
+ /// <summary>
+ /// Gets the rest of the sequence.
+ /// </summary>
+ /// <returns>The rest of the sequence, or <c>null</c> if no more elements.</returns>
+ public override ISeq rest()
+ {
+ if ( _count == 1 )
+ return null;
+ return _rest.seq();
+ }
+
+ /// <summary>
+ /// Adds an item to the beginning of the list.
+ /// </summary>
+ /// <param name="o">The item to add.</param>
+ /// <returns>A new list containing the new item in front of the items already in the sequence.</returns>
+ public override ISeq cons(Object o)
+ {
+ return new PersistentList(meta(), o, this, _count + 1);
+ }
+
+ #endregion
+
+ #region IPersistentStack Members
+
+ /// <summary>
+ /// Peek at the top (first) element in the stack.
+ /// </summary>
+ /// <returns>The top (first) element.</returns>
+ public object peek()
+ {
+ return _first;
+ }
+
+ /// <summary>
+ /// Returns a new stack with the top element popped.
+ /// </summary>
+ /// <returns>The new stack</returns>
+ public IPersistentStack pop()
+ {
+ return _rest == null
+ ? (IPersistentList)EMPTY.withMeta(_meta)
+ : _rest;
+ }
+
+ #endregion
+
+ #region IPersistentCollection members
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ public override int count()
+ {
+ return _count;
+ }
+
+ /// <summary>
+ /// Gets an empty collection of the same type.
+ /// </summary>
+ /// <returns>An emtpy collection.</returns>
+ public override IPersistentCollection empty()
+ {
+ return (IPersistentCollection)EMPTY.withMeta(meta());
+ }
+
+ #endregion
+
+ #region IReduce Members
+
+ /// <summary>
+ /// Reduce the collection using a function.
+ /// </summary>
+ /// <param name="f">The function to apply.</param>
+ /// <returns>The reduced value</returns>
+ public object reduce(IFn f)
+ {
+ object ret = first();
+ for (ISeq s = rest(); s != null; s = s.rest())
+ ret = f.invoke(ret, s.first());
+ return ret;
+ }
+
+ /// <summary>
+ /// Reduce the collection using a function.
+ /// </summary>
+ /// <param name="f">The function to apply.</param>
+ /// <param name="start">An initial value to get started.</param>
+ /// <returns>The reduced value</returns>
+ public object reduce(IFn f, object start)
+ {
+ object ret = f.invoke(start, first());
+ for (ISeq s = rest(); s != null; s = s.rest())
+ ret = f.invoke(ret, s.first());
+ return ret;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Represents an empty <see cref="IPersistentList">IPersistentList</see>.
+ /// </summary>
+ public class EmptyList : Obj, IPersistentList, IList, Counted
+ {
+ #region C-tors
+
+ /// <summary>
+ /// Initialize an <see cref="EmptyList">PersistentList.EmptyList</see> with given metadata.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ public EmptyList(IPersistentMap meta)
+ : base(meta)
+ {
+ }
+
+ /// <summary>
+ /// Initialize an <see cref="EmptyList">PersistentList.EmptyList</see> with null metadata.
+ /// </summary>
+ EmptyList()
+ {
+ }
+
+
+ #endregion
+
+ #region Object overrides
+
+ /// <summary>
+ /// Return the hash code for the object.
+ /// </summary>
+ /// <returns>The hash code</returns>
+ public override int GetHashCode()
+ {
+ return -1;
+ }
+
+ /// <summary>
+ /// Determines if an object is equal to the current object.
+ /// </summary>
+ /// <param name="obj">The object to compare to.</param>
+ /// <returns><value>true</value> if the object is the same; <value>false</value> otherwise.</returns>
+ /// <remarks>
+ /// Equality is value-based. Any empty sequence will do.
+ /// </remarks>
+ public override bool Equals(object obj)
+ {
+ // TODO: rethink Sequential and maybe use boundedCount.
+ return (obj is Sequential || obj is IList) && RT.count(obj) == 0;
+ }
+
+ #endregion
+
+ #region IObj methods
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return meta == _meta
+ ? this
+ : new EmptyList(meta);
+ }
+
+ #endregion
+
+ #region IPersistentStack Members
+
+ /// <summary>
+ /// Peek at the top (first) element in the stack.
+ /// </summary>
+ /// <returns>The top (first) element. )(Always null.)</returns>
+ public object peek()
+ {
+ return null;
+ }
+
+ /// <summary>
+ /// Returns a new stack with the top element popped.
+ /// </summary>
+ /// <returns>The new stack. Always throws an exception.</returns>
+ public IPersistentStack pop()
+ {
+ throw new InvalidOperationException("Can't pop empty list");
+ }
+
+ #endregion
+
+ #region IPersistentCollection Members
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection. Always zero.</returns>
+ public int count()
+ {
+ return 0;
+ }
+
+ /// <summary>
+ /// Gets an ISeq to allow first/rest iteration through the collection.
+ /// </summary>
+ /// <returns>An ISeq for iteration. The sequence is empty, so always null.</returns>
+ public ISeq seq()
+ {
+ return null;
+ }
+
+ public IPersistentCollection cons(object o)
+ {
+ return new PersistentList(meta(), o, null, 1);
+ }
+
+ /// <summary>
+ /// Gets an empty collection of the same type.
+ /// </summary>
+ /// <returns>An emtpy collection. Always returns itself.</returns>
+ public IPersistentCollection empty()
+ {
+ return this;
+ }
+
+ /// <summary>
+ /// Determine if an object is equivalent to this (handles all collections).
+ /// </summary>
+ /// <param name="o">The object to compare.</param>
+ /// <returns><c>true</c> if the object is equivalent; <c>false</c> otherwise.</returns>
+ public bool equiv(object o)
+ {
+ return Equals(o);
+ }
+
+ #endregion
+
+ #region ICollection Members
+
+ public void CopyTo(Array array, int index)
+ {
+ // no-op: no items to copy.
+ }
+
+ public int Count
+ {
+ get { return 0; }
+ }
+
+ public bool IsSynchronized
+ {
+ get { return true; }
+ }
+
+ public object SyncRoot
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ public IEnumerator GetEnumerator()
+ {
+ yield break;
+ }
+
+ #endregion
+
+ #region IList Members
+
+ public int Add(object value)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public void Clear()
+ {
+ throw new InvalidOperationException();
+ }
+
+ public bool Contains(object value)
+ {
+
+ for (ISeq s = seq(); s != null; s = s.rest())
+ if (Util.equiv(s.first(), value))
+ return true;
+ return false;
+ }
+
+ public int IndexOf(object value)
+ {
+ ISeq s = seq();
+ for (int i = 0; s != null; s = s.rest(), i++)
+ if (Util.equiv(s.first(), value))
+ return i;
+ return -1;
+ }
+
+ public void Insert(int index, object value)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public bool IsFixedSize
+ {
+ get { return true; }
+ }
+
+ public bool IsReadOnly
+ {
+ get { return true; }
+ }
+
+ public void Remove(object value)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public void RemoveAt(int index)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public object this[int index]
+ {
+ get
+ {
+ return RT.nth(this,index);
+ }
+ set
+ {
+ throw new InvalidOperationException();
+ }
+ }
+
+ #endregion
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/PersistentQueue.cs b/ClojureCLR/Clojure/Clojure/Lib/PersistentQueue.cs new file mode 100644 index 00000000..466d390e --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/PersistentQueue.cs @@ -0,0 +1,367 @@ +/**
+ * 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.Collections;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// A persistent queue. (Conses onto rear, peeks/pops from front.)
+ /// </summary>
+ /// <remarks>
+ /// <para>See Okasaki's Batched Queues.</para>
+ /// <para>This differs in that it uses an <see cref="IPersistentList">IPersistentList</see>
+ /// as the rear, which is in-order,
+ /// so no reversing or suspensions required for persistent use.</para>
+ /// </remarks>
+ public class PersistentQueue : Obj, IPersistentList, ICollection
+ {
+ #region Data
+
+ /// <summary>
+ /// An empty <see cref="PersistentQueue">PersistentQueue</see>.
+ /// </summary>
+ public static readonly PersistentQueue EMPTY = new PersistentQueue(null, null, null);
+
+ /// <summary>
+ /// The front elements of the queue.
+ /// </summary>
+ protected readonly ISeq _f;
+
+ /// <summary>
+ /// The rear elements of the queue.
+ /// </summary>
+ protected readonly IPersistentVector _r;
+
+ /// <summary>
+ /// Cached hash code.
+ /// </summary>
+ protected int _hash = -1;
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ /// <summary>
+ /// Inititalizes a <see cref="PersistentQueue">PersistentQueue</see> from given metadata, front, rear.
+ /// </summary>
+ /// <param name="meta"></param>
+ /// <param name="f"></param>
+ /// <param name="r"></param>
+ protected PersistentQueue(IPersistentMap meta, ISeq f, IPersistentVector r)
+ : base(meta)
+ {
+ _f = f;
+ _r = r;
+ }
+
+ #endregion
+
+ #region Object overrides
+
+ /// <summary>
+ /// Determines if an object is equal to the current object.
+ /// </summary>
+ /// <param name="obj">The object to compare to.</param>
+ /// <returns><value>true</value> if they are the same; <value>false</value> otherwise.</returns>
+ public override bool Equals(object obj)
+ {
+ // TODO: Another example of Sequential/IPersistentCollection identity
+ if (!(obj is Sequential))
+ return false;
+
+ ISeq ms = ((IPersistentCollection)obj).seq();
+ for (ISeq s = seq(); s != null; s = s.rest(), ms = ms.rest())
+ if (ms == null || !Util.equals(s.first(), ms.first()))
+ return false;
+ return ms.rest() == null;
+ }
+
+ /// <summary>
+ /// Get the hash code for the current object.
+ /// </summary>
+ /// <returns>The hash code.</returns>
+ /// <remarks>Result is cached.</remarks>
+ public override int GetHashCode()
+ {
+ if (_hash == -1)
+ {
+ int hash = 0;
+ for (ISeq s = seq(); s != null; s = s.rest())
+ hash = Util.HashCombine(hash, Util.Hash(s.first()));
+ _hash = hash;
+ }
+ return _hash;
+ }
+
+ #endregion
+
+ #region IObj members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return (meta == _meta)
+ ? this
+ : new PersistentQueue(meta, _f, _r);
+ // Java does not follow the pattern: return new PersistentQueue(meta, _f, _r);
+ }
+
+ #endregion
+
+ #region IPersistentStack Members
+
+ /// <summary>
+ /// Peek at the top (first) element in the stack.
+ /// </summary>
+ /// <returns>The top (first) element.</returns>
+ public object peek()
+ {
+ return RT.first(_f);
+ }
+
+ /// <summary>
+ /// Returns a new stack with the top element popped.
+ /// </summary>
+ /// <returns>The new stack.</returns>
+ public IPersistentStack pop()
+ {
+ if (_f == null) //Java code: hmmmm... pop of empty queue => empty queue?
+ return this;
+ ISeq f1 = _f.rest();
+ IPersistentVector r1 = _r;
+ if ( f1 == null )
+ {
+ f1 = RT.seq(_r);
+ r1 = null;
+ }
+ return new PersistentQueue(meta(), f1, r1);
+ }
+
+ #endregion
+
+ #region IPersistentCollection Members
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ public int count()
+ {
+ return RT.count(_f) + RT.count(_r);
+ }
+
+ /// <summary>
+ /// Gets an <see cref="ISeq">ISeq</see> to allow first/rest iteration through the collection.
+ /// </summary>
+ /// <returns>An <see cref="ISeq">ISeq</see> for iteration.</returns>
+ public ISeq seq()
+ {
+ return _f == null
+ ? null
+ : new Seq(_f, RT.seq(_r));
+ }
+
+ /// <summary>
+ /// Returns a new collection that has the given element cons'd on front of the existing collection.
+ /// </summary>
+ /// <param name="o">An item to put at the front of the collection.</param>
+ /// <returns>A new immutable collection with the item added.</returns>
+ public IPersistentCollection cons(object o)
+ {
+ // TODO: What if _f is null and _r is not?
+ return _f == null // empty
+ ? new PersistentQueue(meta(), RT.list(o), null)
+ : new PersistentQueue(meta(), _f, (_r ?? PersistentVector.EMPTY).cons(o));
+ }
+
+ public IPersistentCollection empty()
+ {
+ return (IPersistentCollection)EMPTY.withMeta(meta());
+ }
+
+ public bool equiv(object o)
+ {
+ // TODO: Another example of Sequential/IPersistentCollection identity
+ if (!(o is Sequential))
+ return false;
+
+ ISeq ms = ((IPersistentCollection)o).seq();
+ for ( ISeq s = seq(); s != null; s = s.rest(), ms = ms.rest())
+ if ( ms == null || ! Util.equiv(s.first(),ms.first()))
+ return false;
+ return ms.rest() == null;
+ }
+
+
+ #endregion
+
+ #region ICollection Members
+
+ public void CopyTo(Array array, int index)
+ {
+ int i = index;
+ ISeq s;
+ for ( s = _f; s != null; s = s.rest(), i++)
+ array.SetValue(s.first(), i);
+
+ for (s = _r.seq(); s != null; s = s.rest(), i++)
+ array.SetValue(s.first(), i);
+ }
+
+ public int Count
+ {
+ get { return count(); }
+ }
+
+ public bool IsSynchronized
+ {
+ get { return true; }
+ }
+
+ public object SyncRoot
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ public IEnumerator GetEnumerator()
+ {
+ ISeq s;
+ for (s = _f; s != null; s = s.rest())
+ yield return s.first();
+
+ for (s = _r.seq(); s != null; s = s.rest())
+ yield return s.first();
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Represents an <see cref="ISeq">ISeq</see> over a <see cref="PersistentQueue">PersistentQueue</see>.
+ /// </summary>
+ sealed class Seq : ASeq
+ {
+ #region Data
+
+ /// <summary>
+ /// The front elements.
+ /// </summary>
+ private readonly ISeq _f;
+
+ /// <summary>
+ /// The rear elements.
+ /// </summary>
+ private readonly ISeq _rseq;
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ /// <summary>
+ /// Initializes a <see cref="Seq">PersistentQueue.Seq</see> from given front and rear elements.
+ /// </summary>
+ /// <param name="f">The front elements.</param>
+ /// <param name="rseq">The rear elements.</param>
+ internal Seq(ISeq f, ISeq rseq)
+ {
+ _f = f;
+ _rseq = rseq;
+ }
+
+ /// <summary>
+ /// Initializes a <see cref="Seq">PersistentQueue.Seq</see> from given metadata and front and rear elements.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ /// <param name="f">The front elements.</param>
+ /// <param name="rseq">The rear elements.</param>
+ internal Seq(IPersistentMap meta, ISeq f, ISeq rseq)
+ : base(meta)
+ {
+ _f = f;
+ _rseq = rseq;
+ }
+
+ #endregion
+
+ #region IObj members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return new Seq(meta, _f, _rseq);
+ }
+
+ #endregion
+
+ #region IPersistentCollection members
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ public override int count()
+ {
+ return RT.count(_f) + RT.count(_rseq);
+ }
+
+ #endregion
+
+ #region ISeq members
+
+ /// <summary>
+ /// Gets the first item.
+ /// </summary>
+ /// <returns>The first item.</returns>
+ public override object first()
+ {
+ return _f.first();
+ }
+
+ /// <summary>
+ /// Gets the rest of the sequence.
+ /// </summary>
+ /// <returns>The rest of the sequence, or <c>null</c> if no more elements.</returns>
+ public override ISeq rest()
+ {
+ ISeq f1 = _f.rest();
+ ISeq r1 = _rseq;
+ if (f1 == null)
+ {
+ if (_rseq == null)
+ return null;
+ f1 = _rseq;
+ r1 = null;
+ }
+ return new Seq(f1, r1);
+ }
+
+
+
+ #endregion
+ }
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/PersistentStructMap.cs b/ClojureCLR/Clojure/Clojure/Lib/PersistentStructMap.cs new file mode 100644 index 00000000..902261fd --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/PersistentStructMap.cs @@ -0,0 +1,518 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents a structure map (map with fixed set of quickly accessible keys).
+ /// </summary>
+ /// <remarks>See the Clojure API for more information.</remarks>
+ public class PersistentStructMap : APersistentMap
+ {
+
+ #region Internal classes
+
+ /// <summary>
+ /// Holds the fixed keys and map of keys to indexes.
+ /// </summary>
+ [Serializable]
+ public sealed class Def
+ {
+ #region Data
+
+ /// <summary>
+ /// The fixed keys.
+ /// </summary>
+ readonly ISeq _keys;
+
+ /// <summary>
+ /// Get the fixed keys.
+ /// </summary>
+ public ISeq Keys
+ {
+ get { return _keys; }
+ }
+
+ /// <summary>
+ /// The map from (fixed) keys to indexes.
+ /// </summary>
+ readonly IPersistentMap _keyslots;
+
+ /// <summary>
+ /// Get the map from (fixed) keys to indexes.
+ /// </summary>
+ public IPersistentMap Keyslots
+ {
+ get { return _keyslots; }
+ }
+
+ #endregion
+
+ #region C-tors
+
+ /// <summary>
+ /// Initialize a <see cref="Def">Def</see>.
+ /// </summary>
+ /// <param name="keys">The fixed keys.</param>
+ /// <param name="keyslots">The map of keys/values for the fixed keys.</param>
+ public Def(ISeq keys, IPersistentMap keyslots)
+ {
+ _keys = keys;
+ _keyslots = keyslots;
+ }
+
+ #endregion
+ }
+
+ #endregion
+
+ #region Data
+
+ /// <summary>
+ /// The <see cref="Def">Def</see> holding the fixed key definitions.
+ /// </summary>
+ readonly Def _def;
+
+ /// <summary>
+ /// Holds the values for the fixed keys.
+ /// </summary>
+ readonly object[] _vals;
+
+ /// <summary>
+ /// The map for non-fixed keys.
+ /// </summary>
+ readonly IPersistentMap _ext;
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ /// <summary>
+ /// Creates a struct definition.
+ /// </summary>
+ /// <param name="keys">The set of fixed keys.</param>
+ /// <returns>A struct definition.</returns>
+ public static Def createSlotMap(ISeq keys)
+ {
+ if (keys == null)
+ throw new ArgumentException("Must supply keys");
+ IPersistentMap map = PersistentHashMap.EMPTY;
+ int i = 0;
+ for (ISeq s = keys; s != null; s = s.rest(), i++)
+ map = map.assoc(s.first(), i);
+ return new Def(keys, map);
+ }
+
+ /// <summary>
+ /// Create a struct from a struct definition and a sequence of alternating keys and values.
+ /// </summary>
+ /// <param name="def">The struct definition</param>
+ /// <param name="keyvals">A sequence of alternating keys and values.</param>
+ /// <returns>A <see cref="PersistentStructMap">PersistentStructMap</see>.</returns>
+ public static PersistentStructMap create(Def def, ISeq keyvals)
+ {
+ object[] vals = new object[def.Keyslots.count()];
+ IPersistentMap ext = PersistentHashMap.EMPTY;
+ for (; keyvals != null; keyvals = keyvals.rest().rest())
+ {
+ if (keyvals.rest() == null)
+ throw new ArgumentException(String.Format("No value supplied for key: {0}", keyvals.first()));
+ object k = keyvals.first();
+ object v = keyvals.rest().first();
+ IMapEntry me = def.Keyslots.entryAt(k);
+ if (me != null)
+ vals[Util.ConvertToInt(me.val())] = v;
+ else
+ ext = ext.assoc(k, v);
+ }
+ return new PersistentStructMap(null, def, vals, ext);
+ }
+
+ /// <summary>
+ /// Create a struct from a struct definition and values (in order) for the fixed keys.
+ /// </summary>
+ /// <param name="def">A struct definition</param>
+ /// <param name="valseq">A sequence of values for the fixed keys (in definition order).</param>
+ /// <returns>A <see cref="PersistentStructMap">PersistentStructMap</see>.</returns>
+ public static PersistentStructMap construct(Def def, ISeq valseq)
+ {
+ object[] vals = new object[def.Keyslots.count()];
+ IPersistentMap ext = PersistentHashMap.EMPTY;
+ for (int i = 0; i < vals.Length && valseq != null; valseq = valseq.rest(), i++)
+ {
+ vals[i] = valseq.first();
+ }
+ if (valseq != null)
+ throw new ArgumentException("Too many arguments to struct constructor");
+ return new PersistentStructMap(null, def, vals, ext);
+ }
+
+ /// <summary>
+ /// Initialize a struct from given data.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ /// <param name="def">The structure definition.</param>
+ /// <param name="vals">Values for the fixed keys.</param>
+ /// <param name="ext">Additional keys/values.</param>
+ protected PersistentStructMap(IPersistentMap meta, Def def, Object[] vals, IPersistentMap ext)
+ : base(meta)
+ {
+ _ext = ext;
+ _def = def;
+ _vals = vals;
+ }
+
+ /// <summary>
+ /// Create a structure from the given data.
+ /// </summary>
+ /// <param name="meta"></param>
+ /// <param name="def"></param>
+ /// <param name="vals"></param>
+ /// <param name="ext"></param>
+ /// <returns></returns>
+ /// <remarks>
+ /// This method is used instead of the PersistentStructMap constructor by
+ /// all methods that return a new PersistentStructMap. This is done
+ /// to allow subclasses to override this method to return instances of their own class from
+ /// all PersistentStructMap methods.
+ /// </remarks>
+ protected virtual PersistentStructMap makeNew(IPersistentMap meta, Def def, object[] vals, IPersistentMap ext)
+ {
+ return new PersistentStructMap(meta, def, vals, ext);
+ }
+
+ #endregion
+
+ #region Fast key access
+
+ /// <summary>
+ /// A function providing quick access to given fixed key of a struct.
+ /// </summary>
+ sealed class AccessorFn : AFn
+ {
+ #region Data
+
+ /// <summary>
+ /// The struct definition.
+ /// </summary>
+ private readonly Def _def;
+
+ /// <summary>
+ /// The index of the key to access.
+ /// </summary>
+ private readonly int _index;
+
+ #endregion
+
+ #region Ctors
+
+ /// <summary>
+ /// Initialize.
+ /// </summary>
+ /// <param name="def">The struct definition.</param>
+ /// <param name="index">The index of the key to access.</param>
+ public AccessorFn(Def def, int index)
+ {
+ _def = def;
+ _index = index;
+ }
+
+ #endregion
+
+ #region IFn methods
+
+ public override object invoke(object arg1)
+ {
+ PersistentStructMap m = (PersistentStructMap)arg1;
+ if (m._def != _def)
+ throw new Exception("Accessor/struct mismatch");
+ return m._vals[_index];
+ }
+
+ #endregion
+ }
+
+ /// <summary>
+ /// Get a fast accessor for a fixed key.
+ /// </summary>
+ /// <param name="def">The struct definition.</param>
+ /// <param name="key">The fixed key to access.</param>
+ /// <returns>An accessor function.</returns>
+ public static IFn getAccessor(Def def, object key)
+ {
+ IMapEntry e = def.Keyslots.entryAt(key);
+ if (e == null)
+ throw new ArgumentException("Not a key of struct");
+
+ int i = (int)e.val();
+
+ return new AccessorFn(def, i);
+ }
+
+
+ #endregion
+
+ #region IObj methods
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return (meta == _meta)
+ ? this
+ : makeNew(meta, _def, _vals, _ext);
+ }
+
+ #endregion
+
+ #region IPersistentCollection members
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ public override int count()
+ {
+ return _vals.Length + RT.count(_ext);
+ }
+
+ /// <summary>
+ /// Gets an <see cref="ISeq">ISeq</see> to allow first/rest iteration through the collection.
+ /// </summary>
+ /// <returns>An <see cref="ISeq">ISeq</see> for iteration.</returns>
+ public override ISeq seq()
+ {
+ return new Seq(null, _def.Keys, _vals, 0, _ext);
+ }
+
+
+ /// <summary>
+ /// Gets an empty collection of the same type.
+ /// </summary>
+ /// <returns>An emtpy collection.</returns>
+ public override IPersistentCollection empty()
+ {
+ return construct(_def,null);
+ }
+
+ #endregion
+
+ #region Associative members
+
+ /// <summary>
+ /// Test if the map contains a key.
+ /// </summary>
+ /// <param name="key">The key to test for membership</param>
+ /// <returns>True if the key is in this map.</returns>
+ public override bool containsKey(object key)
+ {
+ return _def.Keyslots.containsKey(key) || _ext.containsKey(key);
+ }
+
+ /// <summary>
+ /// Returns the key/value pair for this key.
+ /// </summary>
+ /// <param name="key">The key to retrieve</param>
+ /// <returns>The key/value pair for the key, or null if the key is not in the map.</returns>
+ public override IMapEntry entryAt(object key)
+ {
+ IMapEntry me = _def.Keyslots.entryAt(key);
+ return me == null
+ ? _ext.entryAt(key)
+ : new MapEntry(me.key(), _vals[Util.ConvertToInt(me.val())]);
+ }
+
+ /// <summary>
+ /// Gets the value associated with a key.
+ /// </summary>
+ /// <param name="key">The key to look up.</param>
+ /// <returns>The associated value. (Throws an exception if key is not present.)</returns>
+ public override object valAt(object key)
+ {
+ IMapEntry me = _def.Keyslots.entryAt(key);
+ return me == null
+ ? _ext.valAt(key)
+ : _vals[Util.ConvertToInt(me.val())];
+ }
+
+ /// <summary>
+ /// Gets the value associated with a key.
+ /// </summary>
+ /// <param name="key">The key to look up.</param>
+ /// <param name="notFound">The value to return if the key is not present.</param>
+ /// <returns>The associated value (or <c>notFound</c> if the key is not present.</returns>
+ public override object valAt(object key, object notFound)
+ {
+ IMapEntry me = _def.Keyslots.entryAt(key);
+ return me == null
+ ? _ext.valAt(key,notFound)
+ : _vals[Util.ConvertToInt(me.val())];
+ }
+
+ #endregion
+
+ #region IPersistentMap members
+
+ /// <summary>
+ /// Add a new key/value pair.
+ /// </summary>
+ /// <param name="key">The key</param>
+ /// <param name="val">The value</param>
+ /// <returns>A new map with key+value added.</returns>
+ /// <remarks>Overwrites an exising value for the <paramref name="key"/>, if present.</remarks>
+ public override IPersistentMap assoc(object key, object val)
+ {
+ IMapEntry me = _def.Keyslots.entryAt(key);
+ if (me != null)
+ {
+ int i = Util.ConvertToInt(me.val());
+ object[] newVals = (object[])_vals.Clone();
+ newVals[i] = val;
+ return makeNew(_meta, _def, newVals, _ext);
+ }
+ return makeNew(_meta, _def, _vals, _ext.assoc(key, val));
+ }
+
+ /// <summary>
+ /// Add a new key/value pair.
+ /// </summary>
+ /// <param name="key">The key</param>
+ /// <param name="val">The value</param>
+ /// <returns>A new map with key+value added.</returns>
+ /// <remarks>Throws an exception if <paramref name="key"/> has a value already.</remarks>
+ public override IPersistentMap assocEx(object key, object val)
+ {
+ if (containsKey(key))
+ throw new Exception("Key already present");
+ return assoc(key, val);
+ }
+
+ /// <summary>
+ /// Remove a key entry.
+ /// </summary>
+ /// <param name="key">The key to remove</param>
+ /// <returns>A new map with the key removed (or the same map if the key is not contained).</returns>
+ public override IPersistentMap without(object key)
+ {
+ IMapEntry me = _def.Keyslots.entryAt(key);
+ if (me != null)
+ throw new Exception("Can't remove struct key");
+ IPersistentMap newExt = _ext.without(key);
+ return newExt == _ext
+ ? this
+ : makeNew(_meta, _def, _vals, newExt);
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Implements an <see cref="ISeq">ISeq</see> iterating over a <see cref="PersistentStructMap">PersistentStructMap</see>.
+ /// </summary>
+ /// <remarks>Combines an index-iteration over the array of fixed keys,
+ /// followed by a regular iteration of the map of non-fixed keys.
+ /// </remarks>
+ sealed class Seq : ASeq
+ {
+ #region Data
+
+ /// <summary>
+ /// Index into the fixed keys.
+ /// </summary>
+ readonly int _i;
+
+ /// <summary>
+ /// The (remaining) fixed keys.
+ /// </summary>
+ readonly ISeq _keys;
+
+ /// <summary>
+ /// The values for the fixed keys.
+ /// </summary>
+ readonly object[] _vals;
+
+ /// <summary>
+ /// The map of non-fixed keys and their values.
+ /// </summary>
+ readonly IPersistentMap _ext;
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ /// <summary>
+ /// Initialize a <see cref="Seq">PersistentStuctMap.Seq</see>.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ /// <param name="keys">The remaining fixed keys.</param>
+ /// <param name="vals">The values for the fixed keys.</param>
+ /// <param name="i">The index of the first fixed key.</param>
+ /// <param name="ext">The non-fixed keys and their values.</param>
+ public Seq(IPersistentMap meta, ISeq keys, object[] vals, int i, IPersistentMap ext)
+ : base(meta)
+ {
+ _i = i;
+ _keys = keys;
+ _vals = vals;
+ _ext = ext;
+ }
+
+ #endregion
+
+ #region IObj members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return (meta == _meta)
+ ? this
+ : new Seq(meta, _keys, _vals, _i, _ext);
+ }
+
+
+ #endregion
+
+ #region ISeq members
+
+ /// <summary>
+ /// Gets the first item.
+ /// </summary>
+ /// <returns>The first item.</returns>
+ public override object first()
+ {
+ return new MapEntry(_keys.first(),_vals[_i]);
+ }
+
+ /// <summary>
+ /// Gets the rest of the sequence.
+ /// </summary>
+ /// <returns>The rest of the sequence, or <c>null</c> if no more elements.</returns>
+ public override ISeq rest()
+ {
+ return (_i + 1 < _vals.Length)
+ ? new Seq(_meta, _keys.rest(), _vals, _i + 1, _ext)
+ : _ext.seq();
+ }
+
+ #endregion
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/PersistentTreeMap.cs b/ClojureCLR/Clojure/Clojure/Lib/PersistentTreeMap.cs new file mode 100644 index 00000000..b7a6cebd --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/PersistentTreeMap.cs @@ -0,0 +1,1244 @@ +/**
+ * 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.Collections;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Implements a persistent Red-Black Tree.
+ /// </summary>
+ /// <remarks>
+ /// <para>>Note that instances of this class are constant values
+ /// i.e., add/remove etc return new values.</para
+ /// <para>See Okasaki, Kahrs, Larsen et al</para>
+ /// </remarks>
+ public class PersistentTreeMap : APersistentMap, Reversible, Sorted
+ {
+ #region Data
+
+ /// <summary>
+ /// The method used to compare elements for sorting.
+ /// </summary>
+ protected readonly IComparer _comp;
+
+ /// <summary>
+ /// Root node of the Red-Black tree.
+ /// </summary>
+ internal readonly Node _tree;
+
+ /// <summary>
+ /// Number of items in the tree.
+ /// </summary>
+ protected readonly int _count;
+
+ /// <summary>
+ /// An empty <see cref="PersistentTreeMap">PersistentTreeMap</see>.
+ /// </summary>
+ public static readonly PersistentTreeMap EMPTY = new PersistentTreeMap();
+
+ #endregion
+
+ #region Ctors & factory methods
+
+ /// <summary>
+ /// Create a <see cref="PersistentTreeMap">PersistentTreeMap</see> from a dictionary.
+ /// </summary>
+ /// <param name="other">The dictionary to initialize from.</param>
+ /// <returns>A <see cref="PersistentTreeMap">PersistentTreeMap</see>.</returns>
+ public static IPersistentMap create(IDictionary other)
+ {
+ IPersistentMap ret = EMPTY;
+ foreach (DictionaryEntry e in other)
+ ret = ret.assoc(e.Key, e.Value);
+ return ret;
+ }
+
+ /// <summary>
+ /// Create a <see cref="PersistentTreeMap">PersistentTreeMap</see> from
+ /// an <see cref="ISeq">ISeq</see> of alternating keys and values.
+ /// </summary>
+ /// <param name="items">The <see cref="ISeq">ISeq</see> of alternating keys and values.</param>
+ /// <returns>A <see cref="PersistentTreeMap">PersistentTreeMap</see>.</returns>
+ public static PersistentTreeMap create(ISeq items)
+ {
+ IPersistentMap ret = EMPTY;
+ for (; items != null; items = items.rest().rest())
+ {
+ if (items.rest() == null)
+ throw new ArgumentException(string.Format("No value supplied for key: %s", items.first()));
+ ret = ret.assoc(items.first(), items.rest().first());
+ }
+ return (PersistentTreeMap)ret;
+ }
+
+ /// <summary>
+ /// Create a <see cref="PersistentTreeMap">PersistentTreeMap</see> from a comparison method
+ /// an <see cref="ISeq">ISeq</see> of alternating keys and values.
+ /// </summary>
+ /// <param name="comp">A comparison method.</param>
+ /// <param name="items">The <see cref="ISeq">ISeq</see> of alternating keys and values.</param>
+ /// <returns>A <see cref="PersistentTreeMap">PersistentTreeMap</see>.</returns>
+ public static PersistentTreeMap create(IComparer comp, ISeq items)
+ {
+ IPersistentMap ret = new PersistentTreeMap(comp);
+ for (; items != null; items = items.rest().rest())
+ {
+ if (items.rest() == null)
+ throw new ArgumentException(string.Format("No value supplied for key: %s", items.first()));
+ ret = ret.assoc(items.first(), items.rest().first());
+ }
+ return (PersistentTreeMap)ret;
+ }
+
+ /// <summary>
+ /// Initialize a <see cref="PersistentTreeMap">PersistentTreeMap</see> using a default comparer.
+ /// </summary>
+ public PersistentTreeMap()
+ : this(RT.DEFAULT_COMPARER)
+ {
+ }
+
+ /// <summary>
+ /// Initialize a <see cref="PersistentTreeMap">PersistentTreeMap</see> using a given comparer.
+ /// </summary>
+ /// <param name="comp"></param>
+ private PersistentTreeMap(IComparer comp)
+ : this(null, comp)
+ {
+ }
+
+ /// <summary>
+ /// Initialize a <see cref="PersistentTreeMap">PersistentTreeMap</see> using given metadata and comparer.
+ /// </summary>
+ /// <param name="meta"></param>
+ /// <param name="comp"></param>
+ public PersistentTreeMap(IPersistentMap meta, IComparer comp)
+ : base(meta)
+ {
+ _comp = comp;
+ _tree = null;
+ _count = 0;
+ }
+
+ /// <summary>
+ /// Initialize a <see cref="PersistentTreeMap">PersistentTreeMap</see> using given internal data. (Internal use only.)
+ /// </summary>
+ /// <param name="comp">The comparer for sorting.</param>
+ /// <param name="tree">The root node of the RB tree.</param>
+ /// <param name="count">The number of elements in the tree.</param>
+ /// <param name="meta">The metadata to attach.</param>
+ PersistentTreeMap(IComparer comp, Node tree, int count, IPersistentMap meta)
+ : base(meta)
+ {
+ _comp = comp;
+ _tree = tree;
+ _count = count;
+ }
+
+ // Why we have this and the previous, I don't know.
+ PersistentTreeMap(IPersistentMap meta, IComparer comp, Node tree, int count)
+ : base(meta)
+ {
+ _comp = comp;
+ _tree = tree;
+ _count = count;
+ }
+
+
+ #endregion
+
+ #region IObj members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return meta == _meta
+ ? this
+ : new PersistentTreeMap(meta, _comp, _tree, _count);
+ // Java: return new PersistentTreeMap(meta, _comp, _tree, _count);
+ }
+
+ #endregion
+
+ #region Associative members
+
+ /// <summary>
+ /// Test if the map contains a key.
+ /// </summary>
+ /// <param name="key">The key to test for membership</param>
+ /// <returns>True if the key is in this map.</returns>
+ public override bool containsKey(object key)
+ {
+ return NodeAt(key) != null;
+ }
+
+ /// <summary>
+ /// Returns the key/value pair for this key.
+ /// </summary>
+ /// <param name="key">The key to retrieve</param>
+ /// <returns>The key/value pair for the key, or null if the key is not in the map.</returns>
+ public override IMapEntry entryAt(object key)
+ {
+ return NodeAt(key);
+ }
+
+ /// <summary>
+ /// Gets the value associated with a key.
+ /// </summary>
+ /// <param name="key">The key to look up.</param>
+ /// <returns>The associated value. (Throws an exception if key is not present.)</returns>
+ public override object valAt(object key)
+ {
+ return valAt(key, null);
+ }
+
+ /// <summary>
+ /// Gets the value associated with a key.
+ /// </summary>
+ /// <param name="key">The key to look up.</param>
+ /// <param name="notFound">The value to return if the key is not present.</param>
+ /// <returns>The associated value (or <c>notFound</c> if the key is not present.</returns>
+ public override object valAt(object key, object notFound)
+ {
+ Node n = NodeAt(key);
+ return (n != null) ? n.Val : notFound;
+ }
+
+
+ #endregion
+
+ #region IPersistentCollection members
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ public override int count()
+ {
+ return _count;
+ }
+
+ /// <summary>
+ /// Gets an <see cref="ISeq">ISeq</see> to allow first/rest iteration through the collection.
+ /// </summary>
+ /// <returns>An <see cref="ISeq">ISeq</see> for iteration.</returns>
+ public override ISeq seq()
+ {
+ return (_count > 0)
+ ? Seq.create(_tree, true, _count)
+ : null;
+ }
+
+ /// <summary>
+ /// Gets an empty collection of the same type.
+ /// </summary>
+ /// <returns>An emtpy collection.</returns>
+ public override IPersistentCollection empty()
+ {
+ return (IPersistentCollection)EMPTY.withMeta(meta());
+ }
+
+ #endregion
+
+ #region IPersistentMap members
+
+ /// <summary>
+ /// Add a new key/value pair.
+ /// </summary>
+ /// <param name="key">The key</param>
+ /// <param name="val">The value</param>
+ /// <returns>A new map with key+value added.</returns>
+ /// <remarks>Overwrites an exising value for the <paramref name="key"/>, if present.</remarks>
+ public override IPersistentMap assoc(object key, object val)
+ {
+ Box found = new Box(null);
+ Node t = Add(_tree, key, val, found);
+ if (t == null)
+ {
+ Node foundNode = (Node)found.Val;
+ if (foundNode.Val == val)
+ return this;
+ return new PersistentTreeMap(_comp, Replace(_tree, key, val), _count, meta());
+ }
+
+ return new PersistentTreeMap(_comp, t.Blacken(), _count + 1, meta());
+ }
+
+ /// <summary>
+ /// Add a new key/value pair.
+ /// </summary>
+ /// <param name="key">The key</param>
+ /// <param name="val">The value</param>
+ /// <returns>A new map with key+value added.</returns>
+ /// <remarks>Throws an exception if <paramref name="key"/> has a value already.</remarks>
+ public override IPersistentMap assocEx(object key, object val)
+ {
+ Box found = new Box(null);
+ Node t = Add(_tree, key, val, found);
+ if (t == null)
+ throw new Exception("Key already present");
+ return new PersistentTreeMap(_comp, t.Blacken(), _count + 1, meta());
+ }
+
+ /// <summary>
+ /// Remove a key entry.
+ /// </summary>
+ /// <param name="key">The key to remove</param>
+ /// <returns>A new map with the key removed (or the same map if the key is not contained).</returns>
+ public override IPersistentMap without(object key)
+ {
+ Box found = new Box(null);
+ Node t = Remove(_tree, key, found);
+ if (t == null)
+ {
+ if (found.Val == null)
+ return this;
+ return new PersistentTreeMap(meta(), _comp);
+ }
+ return new PersistentTreeMap(_comp, t.Blacken(), _count - 1, meta());
+ }
+
+ #endregion
+
+ #region Reversible Members
+
+ /// <summary>
+ /// Gets an <see cref="ISeq">ISeq</see> to travers the sequence in reverse.
+ /// </summary>
+ /// <returns>An <see cref="ISeq">ISeq</see> .</returns>
+ public ISeq rseq()
+ {
+ return (_count > 0)
+ ? Seq.create(_tree, false, _count)
+ : null;
+ }
+
+ #endregion
+
+ #region Sorted Members
+
+ /// <summary>
+ /// Returns the comparer used to sort the elements in the collection.
+ /// </summary>
+ /// <returns>The <c>IComparer</c> used to sort the items.</returns>
+ /// <remarks>Would be called <c>Comparer</c> except we need to match the JVM name.</remarks>
+ public System.Collections.IComparer comparator()
+ {
+ return _comp;
+ }
+
+ /// <summary>
+ /// Returns the key to be passed to the comparator to sort the element.
+ /// </summary>
+ /// <param name="entry">An element in the collection.</param>
+ /// <returns>The key used to sort the element.</returns>
+ public object entryKey(object entry)
+ {
+ return ((IMapEntry)entry).key();
+ }
+
+ /// <summary>
+ /// Returns an <see cref="ISeq">ISeq</see> to iterate through the collection in the designated direction.
+ /// </summary>
+ /// <param name="ascending">A flag indicating if the iteration is ascending or descending.</param>
+ /// <returns>A sequence for first/rest iteration.</returns>
+ public ISeq seq(bool ascending)
+ {
+ return (_count > 0)
+ ? Seq.create(_tree, ascending, _count)
+ : null;
+ }
+
+ /// <summary>
+ /// Returns an <see cref="ISeq">ISeq</see> to iterate through the collection in the designated direction starting from a particular key.
+ /// </summary>
+ /// <param name="key">The key at which to start the iteration.</param>
+ /// <param name="ascending">A flag indicating if the iteration is ascending or descending.</param>
+ /// <returns>A sequence for first/rest iteration.</returns>
+ /// <remarks>The key need not be in the collection. If not present, the iteration will start with
+ /// the first element with a key greater than (if asscending) or less than (if descending) the given key.</remarks>
+ public ISeq seqFrom(object key, bool ascending)
+ {
+ if (_count > 0)
+ {
+ ISeq stack = null;
+ Node t = _tree;
+ while (t != null)
+ {
+ int c = DoCompare(key, t.Key);
+ if (c == 0)
+ {
+ stack = RT.cons(t, stack);
+ return new Seq(stack, ascending);
+ }
+ else if (ascending)
+ {
+ if (c < 0)
+ {
+ stack = RT.cons(t, stack);
+ t = t.Left;
+ }
+ else
+ t = t.Right;
+ }
+ else
+ {
+ if (c > 0)
+ {
+ stack = RT.cons(t, stack);
+ t = t.Right;
+ }
+ else
+ t = t.Left;
+ }
+ }
+ if (stack != null)
+ return new Seq(stack, ascending);
+ }
+ return null;
+ }
+
+ #endregion
+
+ #region IDictionary Members
+
+ public override IDictionaryEnumerator GetEnumerator()
+ {
+ return new NodeEnumerator(_tree, true);
+ }
+
+ #endregion
+
+
+ // TODO: finish commenting RB tree operations.
+
+ #region tree operations
+
+ /// <summary>
+ /// Get the Node containing a key, or null if key not in tree.
+ /// </summary>
+ /// <param name="key">The key to find.</param>
+ /// <returns>The node containing the key, or null if key not found.</returns>
+ Node NodeAt(object key)
+ {
+ Node t = _tree;
+ while (t != null)
+ {
+ int c = DoCompare(key, t.Key);
+ if (c == 0)
+ return t;
+ else if (c < 0)
+ t = t.Left;
+ else
+ t = t.Right;
+ }
+ return t;
+ }
+
+ /// <summary>
+ /// Compare two keys.
+ /// </summary>
+ /// <param name="k1">The first key.</param>
+ /// <param name="k2">The second key.</param>
+ /// <returns>negative, zero, positive</returns>
+ public int DoCompare(object k1, object k2)
+ {
+ return _comp.Compare(k1, k2);
+ }
+
+ /// <summary>
+ /// Add a node for a key
+ /// </summary>
+ /// <param name="t"></param>
+ /// <param name="key"></param>
+ /// <param name="val"></param>
+ /// <param name="found"></param>
+ /// <returns></returns>
+ Node Add(Node t, object key, object val, Box found)
+ {
+ if (t == null)
+ return val == null
+ ? new Red(key)
+ : new RedVal(key, val);
+ int c = DoCompare(key, t.Key);
+ if (c == 0)
+ {
+ found.Val = t;
+ return null;
+ }
+ Node ins = c < 0 ? Add(t.Left, key, val, found) : Add(t.Right, key, val, found);
+ if (ins == null)
+ return null;
+ return c < 0
+ ? t.AddLeft(ins)
+ : t.AddRight(ins);
+ }
+
+ Node Remove(Node t, object key, Box found)
+ {
+ if (t == null)
+ return null;
+ int c = DoCompare(key, t.Key);
+ if (c == 0)
+ {
+ found.Val = t;
+ return Append(t.Left, t.Right);
+ }
+ Node del = c < 0 ? Remove(t.Left, key, found) : Remove(t.Right, key, found);
+ if (del == null && found.Val == null)
+ return null;
+ if (c < 0)
+ return (t.Left is Black)
+ ? BalanceLeftDel(t.Key, t.Val, del, t.Right)
+ : MakeRed(t.Key, t.Val, del, t.Right);
+ return (t.Right is Black)
+ ? BalanceRightDel(t.Key, t.Val, t.Left, del)
+ : MakeRed(t.Key, t.Val, t.Left, del);
+ }
+
+ static Node Append(Node left, Node right)
+ {
+ if (left == null)
+ return right;
+ else if (right == null)
+ return left;
+ else if (left is Red)
+ {
+ if (right is Red)
+ {
+ Node app = Append(left.Right, right.Left);
+ return app is Red
+ ? MakeRed(app.Key, app.Val,
+ MakeRed(left.Key, left.Val, left.Left, app.Left),
+ MakeRed(right.Key, right.Val, app.Right, right.Right))
+ : MakeRed(left.Key, left.Val, left.Left, MakeRed(right.Key, right.Val, app, right.Right));
+ }
+ else
+ return MakeRed(left.Key, left.Val, left.Left, Append(left.Right, right));
+ }
+ else if (right is Red)
+ return MakeRed(right.Key, right.Val, Append(left, right.Left), right.Right);
+ else
+ {
+ Node app = Append(left.Right, right.Left);
+ return (app is Red)
+ ? MakeRed(app.Key, app.Val,
+ MakeBlack(left.Key, left.Val, left.Left, app.Left),
+ MakeBlack(right.Key, right.Val, app.Right, right.Right))
+ : BalanceLeftDel(left.Key, left.Val, left.Left, MakeBlack(right.Key, right.Val, app, right.Right));
+ }
+ }
+
+ static Node BalanceLeftDel(object key, object val, Node del, Node right)
+ {
+ if (del is Red)
+ return MakeRed(key, val, del.Blacken(), right);
+ else if (right is Black)
+ return RightBalance(key, val, del, right.Redden());
+ else if (right is Red && right.Left is Black)
+ return MakeRed(right.Left.Key, right.Left.Val,
+ MakeBlack(key, val, del, right.Left.Left),
+ RightBalance(right.Key, right.Val, right.Left.Right, right.Right.Redden()));
+ else
+ throw new InvalidOperationException("Invariant violation");
+ }
+
+
+ static Node BalanceRightDel(object key, object val, Node left, Node del)
+ {
+ if (del is Red)
+ return MakeRed(key, val, left, del.Blacken());
+ else if (left is Black)
+ return LeftBalance(key, val, left.Redden(), del);
+ else if (left is Red && left.Right is Black)
+ return MakeRed(left.Right.Key, left.Right.Val,
+ LeftBalance(left.Key, left.Val, left.Left.Redden(), left.Right.Left),
+ MakeBlack(key, val, left.Right.Right, del));
+ else
+ throw new InvalidOperationException("Invariant violation");
+ }
+
+ static Node LeftBalance(object key, object val, Node ins, Node right)
+ {
+ if (ins is Red && ins.Left is Red)
+ return MakeRed(ins.Key, ins.Val, ins.Left.Blacken(), MakeBlack(key, val, ins.Right, right));
+ else if (ins is Red && ins.Right is Red)
+ return MakeRed(ins.Right.Key, ins.Right.Val,
+ MakeBlack(ins.Key, ins.Val, ins.Left, ins.Right.Left),
+ MakeBlack(key, val, ins.Right.Right, right));
+ else
+ return MakeBlack(key, val, ins, right);
+ }
+
+ static Node RightBalance(object key, object val, Node left, Node ins)
+ {
+ if (ins is Red && ins.Right is Red)
+ return MakeRed(ins.Key, ins.Val, MakeBlack(key, val, left, ins.Left), ins.Right.Blacken());
+ else if (ins is Red && ins.Left is Red)
+ return MakeRed(ins.Left.Key, ins.Left.Val,
+ MakeBlack(key, val, left, ins.Left.Left),
+ MakeBlack(ins.Key, ins.Val, ins.Left.Right, ins.Right));
+ else
+ return MakeBlack(key, val, left, ins);
+ }
+
+
+ Node Replace(Node t, object key, object val)
+ {
+ int c = DoCompare(key, t.Key);
+ return t.Replace(t.Key,
+ c == 0 ? val : t.Val,
+ c < 0 ? Replace(t.Left, key, val) : t.Left,
+ c > 0 ? Replace(t.Right, key, val) : t.Right);
+ }
+
+
+ static Red MakeRed(object key, object val, Node left, Node right)
+ {
+ if (left == null && right == null)
+ {
+ if (val == null)
+ return new Red(key);
+ return new RedVal(key, val);
+ }
+ if (val == null)
+ return new RedBranch(key, left, right);
+ return new RedBranchVal(key, val, left, right);
+ }
+
+
+ static Black MakeBlack(object key, object val, Node left, Node right)
+ {
+ if (left == null && right == null)
+ {
+ if (val == null)
+ return new Black(key);
+ return new BlackVal(key, val);
+ }
+ if (val == null)
+ return new BlackBranch(key, left, right);
+ return new BlackBranchVal(key, val, left, right);
+ }
+
+
+ #endregion
+
+ abstract internal class Node : AMapEntry
+ {
+ #region Data
+
+ protected readonly object _key;
+
+ #endregion
+
+ #region C-tors
+
+ internal Node(object key)
+ {
+ _key = key;
+ }
+
+ #endregion
+
+ #region IMapEntry members
+
+ public override object key()
+ {
+ return _key;
+ }
+
+ public override object val()
+ {
+ return null;
+ }
+
+
+ #endregion
+
+
+ protected internal object Key { get { return _key; } }
+ protected internal virtual object Val { get { return null; } }
+
+ protected internal virtual Node Left { get { return null; } }
+ protected internal virtual Node Right { get { return null; } }
+
+ abstract protected internal Node AddLeft(Node ins);
+ abstract protected internal Node AddRight(Node ins);
+ abstract protected internal Node RemoveLeft(Node del);
+ abstract protected internal Node RemoveRight(Node del);
+ abstract protected internal Node Blacken();
+ abstract protected internal Node Redden();
+
+ protected internal virtual Node BalanceLeft(Node parent)
+ {
+ return MakeBlack(parent.Key, parent.Val, this, parent.Right);
+ }
+
+ protected internal virtual Node BalanceRight(Node parent)
+ {
+ return MakeBlack(parent.Key, parent.Val, parent.Left, this);
+ }
+
+ abstract protected internal Node Replace(object key, object val, Node left, Node right);
+
+
+ } // end class Node
+
+ class Black : Node
+ {
+ public Black(object key)
+ : base(key)
+ {
+ }
+
+ protected internal override Node AddLeft(Node ins)
+ {
+ return ins.BalanceLeft(this);
+ }
+
+ protected internal override Node AddRight(Node ins)
+ {
+ return ins.BalanceRight(this);
+ }
+
+ protected internal override Node RemoveLeft(Node del)
+ {
+ return BalanceLeftDel(_key, val(), del, Right);
+ }
+
+ protected internal override Node RemoveRight(Node del)
+ {
+ return BalanceRightDel(_key, val(), Left, del);
+ }
+
+ protected internal override Node Blacken()
+ {
+ return this;
+ }
+
+ protected internal override Node Redden()
+ {
+ return new Red(_key);
+ }
+
+ protected internal override Node Replace(object key, object val, Node left, Node right)
+ {
+ return MakeBlack(key, val, left, right);
+ }
+ }
+
+ class BlackVal : Black
+ {
+ protected readonly object _val;
+
+ public BlackVal(object key, object val)
+ : base(key)
+ {
+ _val = val;
+ }
+
+ public override object val()
+ {
+ return _val;
+ }
+
+
+ override protected internal object Val { get { return _val; } }
+
+
+ protected internal override Node Redden()
+ {
+ return new RedVal(_key, _val);
+ }
+ }
+
+ class BlackBranch : Black
+ {
+
+ protected readonly Node _left;
+ protected readonly Node _right;
+
+ public BlackBranch(object key, Node left, Node right)
+ : base(key)
+ {
+ this._left = left;
+ this._right = right;
+ }
+
+ protected internal override Node Left { get { return _left; } }
+ protected internal override Node Right { get { return _right; } }
+
+ protected internal override Node Redden()
+ {
+ return new RedBranch(_key, _left, _right);
+ }
+ }
+
+ class BlackBranchVal : BlackBranch
+ {
+ readonly object _val;
+
+ public BlackBranchVal(object key, object val, Node left, Node right)
+ : base(key, left, right)
+ {
+ _val = val;
+ }
+
+ public override object val()
+ {
+ return _val;
+ }
+
+ override protected internal object Val { get { return _val; } }
+
+ protected internal override Node Redden()
+ {
+ return new RedBranchVal(_key, _val, _left, _right);
+ }
+ }
+
+ class Red : Node
+ {
+ public Red(object key)
+ : base(key)
+ {
+
+ }
+
+ protected internal override Node AddLeft(Node ins)
+ {
+ return MakeRed(_key, val(), ins, Right);
+ }
+
+ protected internal override Node AddRight(Node ins)
+ {
+ return MakeRed(_key, val(), Left, ins);
+ }
+
+ protected internal override Node RemoveLeft(Node del)
+ {
+ return MakeRed(_key, val(), del, Right);
+ }
+
+ protected internal override Node RemoveRight(Node del)
+ {
+ return MakeRed(_key, val(), Left, del);
+ }
+
+ protected internal override Node Blacken()
+ {
+ return new Black(_key);
+ }
+
+ protected internal override Node Redden()
+ {
+ throw new InvalidOperationException("Invariant violation");
+ }
+
+ protected internal override Node Replace(object key, object val, Node left, Node right)
+ {
+ return MakeRed(key, val, left, right);
+ }
+
+ }
+
+ class RedVal : Red
+ {
+ protected readonly object _val;
+
+ public RedVal(object key, object val)
+ : base(key)
+ {
+ this._val = val;
+ }
+
+ override public object val()
+ {
+ return _val;
+ }
+
+ override protected internal object Val { get { return _val; } }
+
+ protected internal override Node Blacken()
+ {
+ return new BlackVal(_key, _val);
+ }
+
+ }
+
+ class RedBranch : Red
+ {
+
+ protected readonly Node _left;
+ protected readonly Node _right;
+
+ public RedBranch(object key, Node left, Node right)
+ : base(key)
+ {
+ this._left = left;
+ this._right = right;
+ }
+
+ protected internal override Node Left { get { return _left; } }
+ protected internal override Node Right { get { return _right; } }
+
+ protected internal override Node BalanceLeft(Node parent)
+ {
+ if (_left is Red)
+ return MakeRed(_key, val(), _left.Blacken(), MakeBlack(parent.Key, parent.Val, _right, parent.Right));
+ else if (_right is Red)
+ return MakeRed(_right.Key, _right.Val, MakeBlack(_key, val(), _left, _right.Left),
+ MakeBlack(parent.Key, parent.Val, _right.Right, parent.Right));
+ else
+ return base.BalanceLeft(parent);
+
+ }
+
+ protected internal override Node BalanceRight(Node parent)
+ {
+ if (_right is Red)
+ return MakeRed(_key, val(), MakeBlack(parent.Key, parent.Val, parent.Left, _left), _right.Blacken());
+ else if (_left is Red)
+ return MakeRed(_left.Key, _left.Val, MakeBlack(parent.Key, parent.Val, parent.Left, _left.Left),
+ MakeBlack(_key, val(), _left.Right, _right));
+ else
+ return base.BalanceRight(parent);
+ }
+
+ protected internal override Node Blacken()
+ {
+ return new BlackBranch(_key, _left, _right);
+ }
+
+ }
+
+ class RedBranchVal : RedBranch
+ {
+ readonly object _val;
+
+ public RedBranchVal(object key, object val, Node left, Node right)
+ : base(key, left, right)
+ {
+ this._val = val;
+ }
+
+ override public object val()
+ {
+ return _val;
+ }
+
+ override protected internal object Val { get { return _val; } }
+
+
+ protected internal override Node Blacken()
+ {
+ return new BlackBranchVal(_key, _val, _left, _right);
+ }
+ }
+
+ class Seq : ASeq
+ {
+ #region Data
+
+ readonly ISeq _stack;
+ readonly bool _asc;
+ readonly int _cnt;
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ public Seq(ISeq stack, bool asc)
+ {
+ _stack = stack;
+ _asc = asc;
+ _cnt = -1;
+ }
+
+ public Seq(ISeq stack, bool asc, int cnt)
+ {
+ _stack = stack;
+ _asc = asc;
+ _cnt = cnt;
+ }
+
+ Seq(IPersistentMap meta, ISeq stack, bool asc, int cnt)
+ : base(meta)
+ {
+ _stack = stack;
+ _asc = asc;
+ _cnt = cnt;
+ }
+
+ internal static Seq create(Node t, bool asc, int cnt)
+ {
+ return new Seq(push(t, null, asc), asc, cnt);
+ }
+
+
+ #endregion
+
+ #region IObj members
+
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return new Seq(meta, _stack, _asc, _cnt);
+ }
+
+ #endregion
+
+ #region details
+
+ static ISeq push(Node t, ISeq stack, bool asc)
+ {
+ while (t != null)
+ {
+ stack = RT.cons(t, stack);
+ t = asc ? t.Left : t.Right;
+ }
+ return stack;
+ }
+
+ #endregion
+
+ #region ISeq members
+
+ public override object first()
+ {
+ return _stack.first();
+ }
+
+ public override ISeq rest()
+ {
+ Node t = (Node)_stack.first();
+ ISeq nextStack = push(_asc ? t.Right : t.Left, _stack.rest(), _asc);
+ return nextStack != null
+ ? new Seq(nextStack, _asc, _cnt - 1)
+ : null;
+ }
+
+ #endregion
+
+ #region IPersistentCollection members
+
+ public override int count()
+ {
+ return (_cnt < 0)
+ ? base.count()
+ : _cnt;
+ }
+
+ #endregion
+
+ }
+
+ class NodeEnumerator : IDictionaryEnumerator
+ {
+ #region Data
+
+ Stack<Node> _stack = new Stack<Node>();
+ bool _asc;
+ Node _startNode;
+
+ #endregion
+
+ #region C-tors
+
+ internal NodeEnumerator(Node t, bool asc)
+ {
+ _asc = asc;
+ _startNode = t;
+ push(t);
+ }
+
+ #endregion
+
+ #region details
+
+ void push(Node t)
+ {
+ while (t != null)
+ {
+ _stack.Push(t);
+ t = _asc ? t.Left : t.Right;
+ }
+ }
+
+ #endregion
+
+
+ #region IDictionaryEnumerator Members
+
+ public DictionaryEntry Entry
+ {
+ get { return (DictionaryEntry)Current; }
+ }
+
+ public object Key
+ {
+ get { return Entry.Key; }
+ }
+
+ public object Value
+ {
+ get { return Entry.Value; }
+ }
+
+ #endregion
+
+
+ #region IEnumerator Members
+
+ public object Current
+ {
+ get { return _stack.Peek(); }
+ }
+
+ public bool MoveNext()
+ {
+ if (_stack.Count == 0)
+ throw new InvalidOperationException("Enumerator at end.");
+ Node t = _stack.Pop();
+ push(_asc ? t.Right : t.Left);
+ return _stack.Count > 0;
+ }
+
+ public void Reset()
+ {
+ _stack.Clear();
+ push(_startNode);
+ }
+
+ #endregion
+ }
+
+ }
+}
+
+#region Java code not implemented
+
+ //public NodeIterator iterator(){
+ // return new NodeIterator(tree, true);
+ //}
+
+ //public NodeIterator reverseIterator(){
+ // return new NodeIterator(tree, false);
+ //}
+
+ //public Iterator keys(){
+ // return keys(iterator());
+ //}
+
+ //public Iterator vals(){
+ // return vals(iterator());
+ //}
+
+ //public Iterator keys(NodeIterator it){
+ // return new KeyIterator(it);
+ //}
+
+ //public Iterator vals(NodeIterator it){
+ // return new ValIterator(it);
+ //}
+
+ //public Object minKey(){
+ // Node t = min();
+ // return t != null ? t.key : null;
+ //}
+
+ //public Node min(){
+ // Node t = tree;
+ // if(t != null)
+ // {
+ // while(t.left() != null)
+ // t = t.left();
+ // }
+ // return t;
+ //}
+
+ //public Object maxKey(){
+ // Node t = max();
+ // return t != null ? t.key : null;
+ //}
+
+ //public Node max(){
+ // Node t = tree;
+ // if(t != null)
+ // {
+ // while(t.right() != null)
+ // t = t.right();
+ // }
+ // return t;
+ //}
+
+ //public int depth(){
+ // return depth(tree);
+ //}
+
+ //int depth(Node t){
+ // if(t == null)
+ // return 0;
+ // return 1 + Math.max(depth(t.left()), depth(t.right()));
+ //}
+
+ //static class KeyIterator implements Iterator{
+ // NodeIterator it;
+
+ // KeyIterator(NodeIterator it){
+ // this.it = it;
+ // }
+
+ // public boolean hasNext(){
+ // return it.hasNext();
+ // }
+
+ // public Object next(){
+ // return ((Node) it.next()).key;
+ // }
+
+ // public void remove(){
+ // throw new UnsupportedOperationException();
+ // }
+ //}
+
+ //static class ValIterator implements Iterator{
+ // NodeIterator it;
+
+ // ValIterator(NodeIterator it){
+ // this.it = it;
+ // }
+
+ // public boolean hasNext(){
+ // return it.hasNext();
+ // }
+
+ // public Object next(){
+ // return ((Node) it.next()).val();
+ // }
+
+ // public void remove(){
+ // throw new UnsupportedOperationException();
+ // }
+ //}
+
+
+
+#endregion
+
diff --git a/ClojureCLR/Clojure/Clojure/Lib/PersistentTreeSet.cs b/ClojureCLR/Clojure/Clojure/Lib/PersistentTreeSet.cs new file mode 100644 index 00000000..bbe8b1db --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/PersistentTreeSet.cs @@ -0,0 +1,206 @@ +/**
+ * 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.Collections;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Implements an persistent, ordered set.
+ /// </summary>
+ public class PersistentTreeSet : APersistentSet, Reversible, Sorted
+ {
+ #region Data
+
+ /// <summary>
+ /// An empty <see cref="PersistentTreeSet">PersistentTreeSet</see>.
+ /// </summary>
+ public static readonly PersistentTreeSet EMPTY = new PersistentTreeSet(null, PersistentTreeMap.EMPTY);
+
+ #endregion
+
+ #region Ctors and factory methods
+
+ /// <summary>
+ /// Create a <see cref="PersistentTreeSet">PersistentTreeSet</see> from arbitrary arguments.
+ /// </summary>
+ /// <param name="init">Array of elements.</param>
+ /// <returns>A <see cref="PersistentTreeSet">PersistentTreeSet</see>.</returns>
+ public static PersistentTreeSet create(params object[] init)
+ {
+ PersistentTreeSet ret = EMPTY;
+ for (int i = 0; i < init.Length; i++)
+ ret = (PersistentTreeSet)ret.cons(init[i]);
+ return ret;
+ }
+
+ /// <summary>
+ /// Create a <see cref="PersistentTreeSet">PersistentTreeSet</see> initialized from an IList.
+ /// </summary>
+ /// <param name="init">A list of elements</param>
+ /// <returns>A <see cref="PersistentTreeSet">PersistentTreeSet</see>.</returns>
+ public static PersistentTreeSet create(IList init)
+ {
+ PersistentTreeSet ret = EMPTY;
+ foreach (object o in init)
+ ret = (PersistentTreeSet)ret.cons(o);
+ return ret;
+ }
+
+ /// <summary>
+ /// Create a <see cref="PersistentTreeSet">PersistentTreeSet</see> initialized from an <see cref="ISeq">ISeq</see>.
+ /// </summary>
+ /// <param name="init">A sequence of elements.</param>
+ /// <returns>A <see cref="PersistentTreeSet">PersistentTreeSet</see>.</returns>
+ public static PersistentTreeSet create(ISeq init)
+ {
+ PersistentTreeSet ret = EMPTY;
+ for (ISeq s = init; s != null; s = s.rest() )
+ ret = (PersistentTreeSet)ret.cons(s.first());
+ return ret;
+ }
+
+ /// <summary>
+ /// Initialize a <see cref="PersistentTreeSet">PersistentTreeSet</see> using given metadata and underlying implementation map.
+ /// </summary>
+ /// <param name="meta">The metadata to attach</param>
+ /// <param name="impl">A map to implement the set.</param>
+ PersistentTreeSet(IPersistentMap meta, IPersistentMap impl)
+ : base(meta, impl)
+ {
+ }
+
+ #endregion
+
+ #region IObj members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ // Java doesn't do identity check
+ return meta == _meta
+ ? this
+ : new PersistentTreeSet(meta, _impl);
+ }
+
+ #endregion
+
+ #region IPersistentCollection members
+
+ /// <summary>
+ /// Returns a new collection that has the given element cons'd on front of the existing collection.
+ /// </summary>
+ /// <param name="o">An item to put at the front of the collection.</param>
+ /// <returns>A new immutable collection with the item added.</returns>
+ public override IPersistentCollection cons(object o)
+ {
+ return contains(o)
+ ? this
+ : new PersistentTreeSet(meta(), _impl.assoc(o, o));
+ }
+
+ /// <summary>
+ /// Gets an empty collection of the same type.
+ /// </summary>
+ /// <returns>An emtpy collection.</returns>
+ public override IPersistentCollection empty()
+ {
+ return (IPersistentCollection)EMPTY.withMeta(meta());
+ }
+
+ #endregion
+
+ #region IPersistentSet members
+
+ /// <summary>
+ /// Get a set with the given item removed.
+ /// </summary>
+ /// <param name="key">The item to remove.</param>
+ /// <returns>A new set with the item removed.</returns>
+ public override IPersistentSet disjoin(object key)
+ {
+ return (contains(key))
+ ? new PersistentTreeSet(meta(), _impl.without(key))
+ : this;
+ }
+
+ #endregion
+
+ #region Reversible Members
+
+ /// <summary>
+ /// Gets an <see cref="ISeq">ISeq</see> to travers the sequence in reverse.
+ /// </summary>
+ /// <returns>An <see cref="ISeq">ISeq</see> .</returns>
+ public ISeq rseq()
+ {
+ return APersistentMap.KeySeq.create(((Reversible)_impl).rseq());
+ }
+
+ #endregion
+
+ #region Sorted Members
+
+ /// <summary>
+ /// Returns the comparer used to sort the elements in the collection.
+ /// </summary>
+ /// <returns>The <c>IComparer</c> used to sort the items.</returns>
+ /// <remarks>Would be called <c>Comparer</c> except we need to match the JVM name.</remarks>
+ public System.Collections.IComparer comparator()
+ {
+ return ((Sorted)_impl).comparator();
+ }
+
+ /// <summary>
+ /// Returns the key to be passed to the comparator to sort the element.
+ /// </summary>
+ /// <param name="entry">An element in the collection.</param>
+ /// <returns>The key used to sort the element.</returns>
+ public object entryKey(object entry)
+ {
+ return entry;
+ }
+
+ /// <summary>
+ /// Returns an <see cref="ISeq">ISeq</see> to iterate through the collection in the designated direction.
+ /// </summary>
+ /// <param name="ascending">A flag indicating if the iteration is ascending or descending.</param>
+ /// <returns>A sequence for first/rest iteration.</returns>
+ public ISeq seq(bool ascending)
+ {
+ PersistentTreeMap m = (PersistentTreeMap)_impl;
+ return RT.keys(m.seq(ascending));
+ }
+
+ /// <summary>
+ /// Returns an <see cref="ISeq">ISeq</see> to iterate through the collection in the designated direction starting from a particular key.
+ /// </summary>
+ /// <param name="key">The key at which to start the iteration.</param>
+ /// <param name="ascending">A flag indicating if the iteration is ascending or descending.</param>
+ /// <returns>A sequence for first/rest iteration.</returns>
+ /// <remarks>The key need not be in the collection. If not present, the iteration will start with
+ /// the first element with a key greater than (if asscending) or less than (if descending) the given key.</remarks>
+ public ISeq seqFrom(object key, bool ascending)
+ {
+ PersistentTreeMap m = (PersistentTreeMap)_impl;
+ return RT.keys(m.seqFrom(key,ascending));
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/PersistentVector.cs b/ClojureCLR/Clojure/Clojure/Lib/PersistentVector.cs new file mode 100644 index 00000000..2c277361 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/PersistentVector.cs @@ -0,0 +1,338 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Implements a persistent vector using a specialized form of array-mapped hash trie.
+ /// </summary>
+ public class PersistentVector: APersistentVector
+ {
+ #region Data
+
+ protected readonly int _cnt;
+ protected readonly int _shift;
+ protected readonly object[] _root;
+ protected readonly object[] _tail;
+
+ /// <summary>
+ /// An empty <see cref="PersistentVector">PersistentVector</see>.
+ /// </summary>
+ static public readonly PersistentVector EMPTY = new PersistentVector(0,5,RT.EMPTY_ARRAY,RT.EMPTY_ARRAY);
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ /// <summary>
+ /// Create a <see cref="PersistentVector">PersistentVector</see> from an <see cref="ISeq">ISeq</see>.
+ /// </summary>
+ /// <param name="items">A sequence of items.</param>
+ /// <returns>An initialized vector.</returns>
+ static public PersistentVector create(ISeq items)
+ {
+ IPersistentVector ret = EMPTY;
+ for (; items != null; items = items.rest())
+ ret = ret.cons(items.first());
+ return (PersistentVector)ret;
+ }
+
+ /// <summary>
+ /// Create a <see cref="PersistentVector">PersistentVector</see> from an array of items.
+ /// </summary>
+ /// <param name="items"></param>
+ /// <returns></returns>
+ static public PersistentVector create(params object[] items)
+ {
+ IPersistentVector ret = EMPTY;
+ foreach (object item in items)
+ ret = ret.cons(item);
+ return (PersistentVector)ret;
+ }
+
+ /// <summary>
+ /// Initialize a <see cref="PersistentVector">PersistentVector</see> from basic components.
+ /// </summary>
+ /// <param name="cnt"></param>
+ /// <param name="shift"></param>
+ /// <param name="root"></param>
+ /// <param name="tail"></param>
+ PersistentVector(int cnt, int shift, object[] root, object[] tail)
+ : base(null)
+ {
+ _cnt = cnt;
+ _shift = shift;
+ _root = root;
+ _tail = tail;
+ }
+
+
+ /// <summary>
+ /// Initialize a <see cref="PersistentVector">PersistentVector</see> from given metadata and basic components.
+ /// </summary>
+ /// <param name="meta"></param>
+ /// <param name="cnt"></param>
+ /// <param name="shift"></param>
+ /// <param name="root"></param>
+ /// <param name="tail"></param>
+ PersistentVector(IPersistentMap meta, int cnt, int shift, object[] root, object[] tail)
+ : base(meta)
+ {
+ _cnt = cnt;
+ _shift = shift;
+ _root = root;
+ _tail = tail;
+ }
+
+ #endregion
+
+ #region IObj members
+
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ // Java version does not do identity check
+ return (meta == _meta)
+ ? this
+ : new PersistentVector(meta, _cnt, _shift, _root, _tail);
+ }
+
+ #endregion
+
+ #region IPersistentVector members
+
+ /// <summary>
+ /// Gets the number of items in the vector.
+ /// </summary>
+ /// <returns>The number of items.</returns>
+ /// <remarks>Not sure why you wouldn't use <c>count()</c> intead.</remarks>
+ public override int length()
+ {
+ return count();
+ }
+
+
+ int tailoff()
+ {
+ return _cnt - _tail.Length;
+ }
+
+
+ /// <summary>
+ /// Get the i-th item in the vector.
+ /// </summary>
+ /// <param name="i">The index of the item to retrieve/</param>
+ /// <returns>The i-th item</returns>
+ /// <remarks>Throws an exception if the index <c>i</c> is not in the range of the vector's elements.</remarks>
+ public override object nth(int i)
+ {
+ if ( i >= 0 && i < _cnt )
+ {
+ if ( i >= tailoff() )
+ return _tail[i & 0x01f];
+ object[] arr = _root;
+ for ( int level = _shift; level > 0; level -= 5)
+ arr = (object[]) arr[ (i >> level) & 0x01f];
+ return arr[i & 0x01f];
+ }
+ throw new IndexOutOfRangeException();
+ }
+
+ /// <summary>
+ /// Return a new vector with the i-th value set to <c>val</c>.
+ /// </summary>
+ /// <param name="i">The index of the item to set.</param>
+ /// <param name="val">The new value</param>
+ /// <returns>A new (immutable) vector v with v[i] == val.</returns>
+ public override IPersistentVector assocN(int i, Object val)
+ {
+ if (i >= 0 && i < _cnt)
+ {
+ if (i >= tailoff())
+ {
+ object[] newTail = new object[_tail.Length];
+ Array.Copy(_tail, newTail, _tail.Length);
+ newTail[i & 0x01f] = val;
+
+ return new PersistentVector(meta(), _cnt, _shift, _root, newTail);
+ }
+
+ return new PersistentVector(meta(), _cnt, _shift, doAssoc(_shift, _root, i, val), _tail);
+ }
+ if (i == _cnt)
+ return cons(val);
+ throw new IndexOutOfRangeException();
+ }
+
+ static private object[] doAssoc(int level, object[] arr, int i, object val)
+ {
+ object[] ret = (object[]) arr.Clone();
+ if (level == 0)
+ ret[i & 0x01f] = val;
+ else
+ {
+ int subidx = ( i >> level ) & 0x01f;
+ ret[subidx] = doAssoc(level-5,(object[]) arr[subidx], i, val);
+ }
+ return ret;
+ }
+
+ /// <summary>
+ /// Creates a new vector with a new item at the end.
+ /// </summary>
+ /// <param name="o">The item to add to the vector.</param>
+ /// <returns>A new (immutable) vector with the objected added at the end.</returns>
+ /// <remarks>Overrides <c>cons</c> in <see cref="IPersistentCollection">IPersistentCollection</see> to specialize the return value.</remarks>
+ public override IPersistentVector cons(object val)
+ {
+ if (_tail.Length < 32)
+ {
+ object[] newTail = new object[_tail.Length + 1];
+ Array.Copy(_tail, newTail, _tail.Length);
+ newTail[_tail.Length] = val;
+ return new PersistentVector(meta(), _cnt + 1, _shift, _root, newTail);
+ }
+ Box expansion = new Box(null);
+
+ object[] newroot = pushTail(_shift - 5, _root, _tail, expansion);
+ int newshift = _shift;
+ if (expansion.Val != null)
+ {
+ newroot = new object[] { newroot, expansion.Val };
+ newshift += 5;
+ }
+
+ return new PersistentVector(meta(), _cnt + 1, newshift, newroot, new object[] { val });
+ }
+
+ private object[] pushTail(int level, object[] arr, object[] tailNode, Box expansion)
+ {
+ object newchild;
+ if (level == 0)
+ newchild = tailNode;
+ else
+ {
+ newchild = pushTail(level - 5, (object[])arr[arr.Length - 1], tailNode, expansion);
+ if (expansion.Val == null)
+ {
+ object[] ret1 = (object[])arr.Clone();
+ ret1[arr.Length - 1] = newchild;
+ return ret1;
+ }
+ else
+ newchild = expansion.Val;
+ }
+ //expansion
+ if ( arr.Length == 32 )
+ {
+ expansion.Val = new object[]{newchild};
+ return arr;
+ }
+ object[] ret = new object[arr.Length + 1];
+ Array.Copy(arr, ret, arr.Length);
+ ret[arr.Length] = newchild;
+ expansion.Val = null;
+ return ret;
+ }
+
+
+
+ #endregion
+
+ #region IPersistentCollection members
+
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ public override int count()
+ {
+ return _cnt;
+ }
+
+ /// <summary>
+ /// Gets an empty collection of the same type.
+ /// </summary>
+ /// <returns>An emtpy collection.</returns>
+ public override IPersistentCollection empty()
+ {
+ return (IPersistentCollection)EMPTY.withMeta(meta());
+ }
+
+ #endregion
+
+ #region IPersistentStack members
+
+ /// <summary>
+ /// Returns a new stack with the top element popped.
+ /// </summary>
+ /// <returns>The new stack.</returns>
+ public override IPersistentStack pop()
+ {
+ if ( _cnt == 0 )
+ throw new InvalidOperationException("Can't pop empty vector");
+ if ( _cnt == 1)
+ return (IPersistentStack)EMPTY.withMeta(meta());
+ if ( _tail.Length > 1 )
+ {
+ object[] newTail = new object[_tail.Length-1];
+ Array.Copy(_tail,newTail,newTail.Length);
+ return new PersistentVector(meta(),_cnt-1,_shift,_root,newTail);
+ }
+ Box ptail = new Box(null);
+ object[] newroot = popTail(_shift-5,_root,ptail);
+ int newshift = _shift;
+ if ( newroot == null )
+ newroot = RT.EMPTY_ARRAY;
+ if ( _shift > 5 && newroot.Length == 1 )
+ {
+ newroot = (Object[])newroot[0];
+ newshift -= 5;
+ }
+ return new PersistentVector(meta(),_cnt-1,newshift,newroot,(object[])ptail.Val);
+ }
+
+ private object[] popTail(int shift, object[] arr, Box ptail)
+ {
+ if ( shift > 0 )
+ {
+ object[] newchild = popTail(shift-5,(object[])arr[arr.Length-1],ptail);
+ if ( newchild != null )
+ {
+ object[] ret1 = (object[])arr.Clone();
+ ret1[arr.Length-1] = newchild;
+ return ret1;
+ }
+ }
+ if ( shift == 0 )
+ ptail.Val = arr[arr.Length-1];
+ //contaction
+ if( arr.Length == 1 )
+ return null;
+
+ object[] ret = new Object[arr.Length-1];
+ Array.Copy(arr,ret,ret.Length);
+ return ret;
+ }
+
+ #endregion
+
+ #region IFn members
+
+
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Properties/AssemblyInfo.cs b/ClojureCLR/Clojure/Clojure/Lib/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..2c93d28a --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Data")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Data")]
+[assembly: AssemblyCopyright("Copyright © 2008")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("d1b1677b-88ab-4878-8a57-adedaeac78bd")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/ClojureCLR/Clojure/Clojure/Lib/RT.cs b/ClojureCLR/Clojure/Clojure/Lib/RT.cs new file mode 100644 index 00000000..20da5381 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/RT.cs @@ -0,0 +1,1507 @@ +/**
+ * 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.Collections;
+
+using System.Reflection;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.IO;
+using System.Diagnostics;
+
+
+namespace clojure.lang
+{
+ public class RT
+ {
+
+ #region Some constants
+
+ public static readonly Boolean T = true;//Keyword.intern(Symbol.create(null, "t"));
+ public static readonly Boolean F = false;//Keyword.intern(Symbol.create(null, "t"));
+
+ //simple-symbol->class
+ internal static readonly IPersistentMap DEFAULT_IMPORTS = map(
+ //classes
+ Symbol.create("AccessViolationException"), typeof(AccessViolationException),
+ Symbol.create("ActivationContext"), typeof(ActivationContext),
+ Symbol.create("Activator"), typeof(Activator),
+ Symbol.create("AppDomain"), typeof(AppDomain),
+ Symbol.create("AppDomainManager"), typeof(AppDomainManager),
+ Symbol.create("AppDomainSetup"), typeof(AppDomainSetup),
+ Symbol.create("AppDomainUnloadedException"), typeof(AppDomainUnloadedException),
+ Symbol.create("ApplicationException"), typeof(ApplicationException),
+ Symbol.create("ApplicationId"), typeof(ApplicationId),
+ Symbol.create("ApplicationIdentity"), typeof(ApplicationIdentity),
+ Symbol.create("ArgumentException"), typeof(ArgumentException),
+ Symbol.create("ArgumentNullException"), typeof(ArgumentNullException),
+ Symbol.create("ArgumentOutOfRangeException"), typeof(ArgumentOutOfRangeException),
+ Symbol.create("ArithmeticException"), typeof(ArithmeticException),
+ Symbol.create("Array"), typeof(Array),
+ Symbol.create("ArrayTypeMismatchException"), typeof(ArrayTypeMismatchException),
+ Symbol.create("AssemblyLoadEventArgs"), typeof(AssemblyLoadEventArgs),
+ Symbol.create("Attribute"), typeof(Attribute),
+ Symbol.create("AttributeUsageAttribute"), typeof(AttributeUsageAttribute),
+ Symbol.create("BadImageFormatException"), typeof(BadImageFormatException),
+ Symbol.create("BitConverter"), typeof(BitConverter),
+ Symbol.create("Buffer"), typeof(Buffer),
+ Symbol.create("CannotUnloadAppDomainException"), typeof(CannotUnloadAppDomainException),
+ Symbol.create("CharEnumerator"), typeof(CharEnumerator),
+ Symbol.create("CLSCompliantAttribute"), typeof(CLSCompliantAttribute),
+ Symbol.create("Console"), typeof(Console),
+ Symbol.create("ConsoleCancelEventArgs"), typeof(ConsoleCancelEventArgs),
+ Symbol.create("ContextBoundObject"), typeof(ContextBoundObject),
+ //Symbol.create("ContextMarshalException"), typeof(ContextMarshalException), -- obsolete
+ Symbol.create("ContextStaticAttribute"), typeof(ContextStaticAttribute),
+ Symbol.create("Convert"), typeof(Convert),
+ Symbol.create("DataMisalignedException"), typeof(DataMisalignedException),
+ Symbol.create("DBNull"), typeof(DBNull),
+ Symbol.create("Delegate"), typeof(Delegate),
+ Symbol.create("DivideByZeroException"), typeof(DivideByZeroException),
+ Symbol.create("DllNotFoundException"), typeof(DllNotFoundException),
+ Symbol.create("DuplicateWaitObjectException"), typeof(DuplicateWaitObjectException),
+ Symbol.create("EntryPointNotFoundException"), typeof(EntryPointNotFoundException),
+ Symbol.create("Enum"), typeof(Enum),
+ Symbol.create("Environment"), typeof(Environment),
+ Symbol.create("EventArgs"), typeof(EventArgs),
+ Symbol.create("Exception"), typeof(Exception),
+ Symbol.create("ExecutionEngineException"), typeof(ExecutionEngineException),
+ Symbol.create("FieldAccessException"), typeof(FieldAccessException),
+ Symbol.create("FileStyleUriParser"), typeof(FileStyleUriParser),
+ Symbol.create("FlagsAttribute"), typeof(FlagsAttribute),
+ Symbol.create("FormatException"), typeof(FormatException),
+ Symbol.create("FtpStyleUriParser"), typeof(FtpStyleUriParser),
+ Symbol.create("GC"), typeof(GC),
+ Symbol.create("GenericUriParser"), typeof(GenericUriParser),
+ Symbol.create("GopherStyleUriParser"), typeof(GopherStyleUriParser),
+ Symbol.create("HttpStyleUriParser"), typeof(HttpStyleUriParser),
+ Symbol.create("IndexOutOfRangeException"), typeof(IndexOutOfRangeException),
+ Symbol.create("InsufficientMemoryException"), typeof(InsufficientMemoryException),
+ Symbol.create("InvalidCastException"), typeof(InvalidCastException),
+ Symbol.create("InvalidOperationException"), typeof(InvalidOperationException),
+ Symbol.create("InvalidProgramException"), typeof(InvalidProgramException),
+ Symbol.create("InvalidTimeZoneException"), typeof(InvalidTimeZoneException),
+ Symbol.create("LdapStyleUriParser"), typeof(LdapStyleUriParser),
+ Symbol.create("LoaderOptimizationAttribute"), typeof(LoaderOptimizationAttribute),
+ Symbol.create("LocalDataStoreSlot"), typeof(LocalDataStoreSlot),
+ Symbol.create("MarshalByRefObject"), typeof(MarshalByRefObject),
+ Symbol.create("Math"), typeof(Math),
+ Symbol.create("MemberAccessException"), typeof(MemberAccessException),
+ Symbol.create("MethodAccessException"), typeof(MethodAccessException),
+ Symbol.create("MissingFieldException"), typeof(MissingFieldException),
+ Symbol.create("MissingMemberException"), typeof(MissingMemberException),
+ Symbol.create("MTAThreadAttribute"), typeof(MTAThreadAttribute),
+ Symbol.create("MulticastDelegate"), typeof(MulticastDelegate),
+ Symbol.create("MulticastNotSupportedException"), typeof(MulticastNotSupportedException),
+ Symbol.create("NetPipeStyleUriParser"), typeof(NetPipeStyleUriParser),
+ Symbol.create("NetTcpStyleUriParser"), typeof(NetTcpStyleUriParser),
+ Symbol.create("NewsStyleUriParser"), typeof(NewsStyleUriParser),
+ Symbol.create("NonSerializedAttribute"), typeof(NonSerializedAttribute),
+ Symbol.create("NotFiniteNumberException"), typeof(NotFiniteNumberException),
+ Symbol.create("NotImplementedException"), typeof(NotImplementedException),
+ Symbol.create("NotSupportedException"), typeof(NotSupportedException),
+ Symbol.create("Nullable"), typeof(Nullable),
+ Symbol.create("NullReferenceException"), typeof(NullReferenceException),
+ Symbol.create("Object"), typeof(Object),
+ Symbol.create("ObjectDisposedException"), typeof(ObjectDisposedException),
+ Symbol.create("ObsoleteAttribute"), typeof(ObsoleteAttribute),
+ Symbol.create("OperatingSystem"), typeof(OperatingSystem),
+ Symbol.create("OperationCanceledException"), typeof(OperationCanceledException),
+ Symbol.create("OutOfMemoryException"), typeof(OutOfMemoryException),
+ Symbol.create("OverflowException"), typeof(OverflowException),
+ Symbol.create("ParamArrayAttribute"), typeof(ParamArrayAttribute),
+ Symbol.create("PlatformNotSupportedException"), typeof(PlatformNotSupportedException),
+ Symbol.create("Random"), typeof(Random),
+ Symbol.create("RankException"), typeof(RankException),
+ Symbol.create("ResolveEventArgs"), typeof(ResolveEventArgs),
+ Symbol.create("SerializableAttribute"), typeof(SerializableAttribute),
+ Symbol.create("StackOverflowException"), typeof(StackOverflowException),
+ Symbol.create("STAThreadAttribute"), typeof(STAThreadAttribute),
+ Symbol.create("String"), typeof(String),
+ Symbol.create("StringComparer"), typeof(StringComparer),
+ Symbol.create("SystemException"), typeof(SystemException),
+ Symbol.create("ThreadStaticAttribute"), typeof(ThreadStaticAttribute),
+ Symbol.create("TimeoutException"), typeof(TimeoutException),
+ Symbol.create("TimeZone"), typeof(TimeZone),
+ Symbol.create("TimeZoneInfo"), typeof(TimeZoneInfo),
+ Symbol.create("TimeZoneNotFoundException"), typeof(TimeZoneNotFoundException),
+ // Symbol.create("TimeZoneInfo.AdjustmentRule"),typeof(TimeZoneInfo.AdjustmentRule),
+ Symbol.create("Type"), typeof(Type),
+ Symbol.create("TypeInitializationException"), typeof(TypeInitializationException),
+ Symbol.create("TypeLoadException"), typeof(TypeLoadException),
+ Symbol.create("TypeUnloadedException"), typeof(TypeUnloadedException),
+ Symbol.create("UnauthorizedAccessException"), typeof(UnauthorizedAccessException),
+ Symbol.create("UnhandledExceptionEventArgs"), typeof(UnhandledExceptionEventArgs),
+ Symbol.create("Uri"), typeof(Uri),
+ Symbol.create("UriBuilder"), typeof(UriBuilder),
+ Symbol.create("UriFormatException"), typeof(UriFormatException),
+ Symbol.create("UriParser"), typeof(UriParser),
+ // Symbol.create(""),typeof(UriTemplate),
+ // Symbol.create(""),typeof(UriTemplateEquivalenceComparer),
+ // Symbol.create(""),typeof(UriTemplateMatch),
+ // Symbol.create(""),typeof(UriTemplateMatchException),
+ // Symbol.create(""),typeof(UriTemplateTable),
+ Symbol.create("UriTypeConverter"), typeof(UriTypeConverter),
+ Symbol.create("ValueType"), typeof(ValueType),
+ Symbol.create("Version"), typeof(Version),
+ Symbol.create("WeakReference"), typeof(WeakReference),
+ // structures/
+ Symbol.create("ArgIterator"), typeof(ArgIterator),
+ // Symbol.create(""),typeof(ArraySegment<T>),
+ Symbol.create("Boolean"), typeof(Boolean),
+ Symbol.create("Byte"), typeof(Byte),
+ Symbol.create("Char"), typeof(Char),
+ Symbol.create("ConsoleKeyInfo"), typeof(ConsoleKeyInfo),
+ Symbol.create("DateTime"), typeof(DateTime),
+ Symbol.create("DateTimeOffset"), typeof(DateTimeOffset),
+ Symbol.create("Decimal"), typeof(Decimal),
+ Symbol.create("Double"), typeof(Double),
+ Symbol.create("Guid"), typeof(Guid),
+ Symbol.create("Int16"), typeof(Int16),
+ Symbol.create("Int32"), typeof(Int32),
+ Symbol.create("Int64"), typeof(Int64),
+ Symbol.create("IntPtr"), typeof(IntPtr),
+ Symbol.create("ModuleHandle"), typeof(ModuleHandle),
+ // Symbol.create(""),typeof(Nullable<T>),
+ Symbol.create("RuntimeArgumentHandle"), typeof(RuntimeArgumentHandle),
+ Symbol.create("RuntimeFieldHandle"), typeof(RuntimeFieldHandle),
+ Symbol.create("RuntimeMethodHandle"), typeof(RuntimeMethodHandle),
+ Symbol.create("RuntimeTypeHandle"), typeof(RuntimeTypeHandle),
+ Symbol.create("SByte"), typeof(SByte),
+ Symbol.create("Single"), typeof(Single),
+ Symbol.create("TimeSpan"), typeof(TimeSpan),
+ Symbol.create("TimeZoneInfo.TransitionTime"), typeof(TimeZoneInfo.TransitionTime),
+ Symbol.create("TypedReference"), typeof(TypedReference),
+ Symbol.create("UInt16"), typeof(UInt16),
+ Symbol.create("UInt32"), typeof(UInt32),
+ Symbol.create("UInt64"), typeof(UInt64),
+ Symbol.create("UIntPtr"), typeof(UIntPtr),
+ // Symbol.create(""),typeof(Void),
+ // interfaces/
+ Symbol.create("AppDomain"), typeof(AppDomain),
+ Symbol.create("IAppDomainSetup"), typeof(IAppDomainSetup),
+ Symbol.create("IAsyncResult"), typeof(IAsyncResult),
+ Symbol.create("ICloneable"), typeof(ICloneable),
+ Symbol.create("IComparable"), typeof(IComparable),
+ //Symbol.create(""),typeof(IComparable<T>),
+ Symbol.create("IConvertible"), typeof(IConvertible),
+ Symbol.create("ICustomFormatter"), typeof(ICustomFormatter),
+ Symbol.create("IDisposable"), typeof(IDisposable),
+ //Symbol.create(""),typeof(IEquatable<T>),
+ Symbol.create("IFormatProvider"), typeof(IFormatProvider),
+ Symbol.create("IFormattable"), typeof(IFormattable),
+ Symbol.create("IServiceProvider"), typeof(IServiceProvider),
+ // delegates/
+ Symbol.create("Action"), typeof(Action),
+ // Symbol.create(""),typeof(Action<T>/
+ // Symbol.create(""),typeof(Action<T1,T2>/
+ // Symbol.create(""),typeof(Action<T1,T2,T3>/
+ // Symbol.create(""),typeof(Action<T1,T2,T3,T4>/
+ Symbol.create("AppDomainInitializer"), typeof(AppDomainInitializer),
+ Symbol.create("AssemblyLoadEventHandler"), typeof(AssemblyLoadEventHandler),
+ Symbol.create("AsyncCallback"), typeof(AsyncCallback),
+ // Symbol.create(""),typeof(Comparison<T>),
+ Symbol.create("ConsoleCancelEventHandler"), typeof(ConsoleCancelEventHandler),
+ //Symbol.create(""),typeof(Converter<TInput,TOutput>),
+ Symbol.create("CrossAppDomainDelegate"), typeof(CrossAppDomainDelegate),
+ Symbol.create("EventHandler"), typeof(EventHandler),
+ // Symbol.create(""),typeof(EventHandler<TEventArgs>),
+ // Symbol.create(""),typeof(Func<TResult>),
+ // Symbol.create(""),typeof(Func<T,TResult>/
+ // Symbol.create(""),typeof(Func<T1, T2, TResult>/
+ // Symbol.create(""),typeof(Func<T1, T2, T3, TResult>/
+ // FSymbol.create(""),typeof(unc<T1, T2, T3, T4, TResult>/
+ // Symbol.create(""),typeof(Predicate<T>),
+ Symbol.create("ResolveEventHandler"), typeof(ResolveEventHandler),
+ Symbol.create("UnhandledExceptionEventHandler"), typeof(UnhandledExceptionEventHandler),
+ // Enumerations/
+ Symbol.create("ActivationContext.ContextForm"), typeof(ActivationContext.ContextForm),
+ Symbol.create("AppDomainManagerInitializationOptions"), typeof(AppDomainManagerInitializationOptions),
+ Symbol.create("AttributeTargets"), typeof(AttributeTargets),
+ Symbol.create("Base64FormattingOptions"), typeof(Base64FormattingOptions),
+ Symbol.create("ConsoleColor"), typeof(ConsoleColor),
+ Symbol.create("ConsoleKey"), typeof(ConsoleKey),
+ Symbol.create("ConsoleModifiers"), typeof(ConsoleModifiers),
+ Symbol.create("ConsoleSpecialKey"), typeof(ConsoleSpecialKey),
+ Symbol.create("DateTimeKind"), typeof(DateTimeKind),
+ Symbol.create("DayOfWeek"), typeof(DayOfWeek),
+ Symbol.create("Environment.SpecialFolder"), typeof(Environment.SpecialFolder),
+ Symbol.create("EnvironmentVariableTarget"), typeof(EnvironmentVariableTarget),
+ Symbol.create("GCCollectionMode"), typeof(GCCollectionMode),
+ Symbol.create("GenericUriParserOptions"), typeof(GenericUriParserOptions),
+ Symbol.create("LoaderOptimization"), typeof(LoaderOptimization),
+ Symbol.create("MidpointRounding"), typeof(MidpointRounding),
+ Symbol.create("PlatformID"), typeof(PlatformID),
+ Symbol.create("StringComparison"), typeof(StringComparison),
+ Symbol.create("StringSplitOptions"), typeof(StringSplitOptions),
+ Symbol.create("TypeCode"), typeof(TypeCode),
+ Symbol.create("UriComponents"), typeof(UriComponents),
+ Symbol.create("UriFormat"), typeof(UriFormat),
+ Symbol.create("UriHostNameType"), typeof(UriHostNameType),
+ Symbol.create("UriIdnScope"), typeof(UriIdnScope),
+ Symbol.create("UriKind"), typeof(UriKind),
+ Symbol.create("UriPartial"), typeof(UriPartial),
+ // ADDED THESE TO SUPPORT THE BOOTSTRAPPING IN THE JAVA CORE.CLJ
+ Symbol.create("StringBuilder"), typeof(StringBuilder),
+ Symbol.create("BigInteger"),typeof(java.math.BigInteger),
+ Symbol.create("BigDecimal"),typeof(java.math.BigDecimal)
+ );
+
+ public static readonly Namespace CLOJURE_NS = Namespace.findOrCreate(Symbol.create("clojure.core"));
+
+ public static readonly Keyword TAG_KEY = Keyword.intern(null, "tag");
+ public static readonly Keyword LINE_KEY = Keyword.intern(null, "line");
+
+
+ public static readonly Var CURRENT_NS = Var.intern(CLOJURE_NS, Symbol.create("*ns*"),
+ CLOJURE_NS);
+
+
+ public static readonly Var IN_NS_VAR = Var.intern(CLOJURE_NS, Symbol.create("in-ns"), F);
+ public static readonly Var NS_VAR = Var.intern(CLOJURE_NS, Symbol.create("ns"), F);
+
+ static readonly Symbol IN_NAMESPACE = Symbol.create("in-ns");
+ sealed class InNamespaceFn : AFn
+ {
+ public override object invoke(object arg1)
+ {
+ Symbol nsname = (Symbol)arg1;
+ Namespace ns = Namespace.findOrCreate(nsname);
+ CURRENT_NS.set(ns);
+ return ns;
+ }
+ }
+ static readonly Symbol NAMESPACE = Symbol.create("ns");
+
+
+ static readonly Symbol IDENTICAL = Symbol.create("identical?");
+
+ sealed class IdenticalFn : AFn
+ {
+ public override object invoke(object arg1, object arg2)
+ {
+ return Object.ReferenceEquals(arg1, arg2) ? RT.T : RT.F;
+ }
+ }
+
+
+
+ public static readonly Var ALLOW_UNRESOLVED_VARS = Var.intern(CLOJURE_NS, Symbol.create("*allow-unresolved-vars*"), F);
+ public static readonly Var WARN_ON_REFLECTION = Var.intern(CLOJURE_NS, Symbol.create("*warn-on-reflection*"), F);
+
+ public static readonly Var MACRO_META = Var.intern(CLOJURE_NS, Symbol.create("*macro-meta*"), null);
+
+ public static readonly Var MATH_CONTEXT = Var.intern(CLOJURE_NS, Symbol.create("*math-context*"), null);
+
+ public static readonly Var AGENT = Var.intern(CLOJURE_NS, Symbol.create("*agent*"), null);
+
+ public static readonly Var CMD_LINE_ARGS = Var.intern(CLOJURE_NS, Symbol.create("*command-line-args*"), null);
+
+ // TODO: These need to be tied into the DLR IO subsystem
+ public static readonly Var OUT = Var.intern(CLOJURE_NS, Symbol.create("*out*"), System.Console.Out);
+ public static readonly Var ERR = Var.intern(CLOJURE_NS, Symbol.create("*err*"), System.Console.Error);
+ public static readonly Var IN =
+ Var.intern(CLOJURE_NS, Symbol.create("*in*"),
+ new clojure.lang.Readers.LineNumberingReader(System.Console.In));
+ static readonly Var PRINT_READABLY = Var.intern(CLOJURE_NS, Symbol.create("*print-readably*"), T);
+ static readonly Var PRINT_META = Var.intern(CLOJURE_NS, Symbol.create("*print-meta*"), F);
+ static readonly Var PRINT_DUP = Var.intern(CLOJURE_NS, Symbol.create("*print-dup*"), F);
+ static readonly Var FLUSH_ON_NEWLINE = Var.intern(CLOJURE_NS, Symbol.create("*flush-on-newline*"), T);
+ static readonly Var PRINT_INITIALIZED = Var.intern(CLOJURE_NS, Symbol.create("print-initialized"));
+ static readonly Var PR_ON = Var.intern(CLOJURE_NS, Symbol.create("pr-on"));
+
+ #endregion
+
+ public static bool IsTrue(object o)
+ {
+ if ( o == null )
+ return false;
+ if (o is Boolean)
+ return (Boolean)o;
+ else
+ return true;
+ }
+
+ public static int BoundedLength(ISeq list, int limit)
+ {
+ int i = 0;
+ for (ISeq c = list; c != null && i <= limit; c = c.rest())
+ {
+ i++;
+ }
+ return i;
+ }
+
+ // TODO: Handle generic collections?
+ public static int count(Object o)
+ {
+ if (o == null)
+ return 0;
+ else if (o is Counted)
+ return ((Counted)o).count();
+ else if (o is IPersistentCollection)
+ {
+ ISeq s = seq(o);
+ o = null;
+ int i=0;
+ for ( ; s != null; s = s.rest())
+ {
+ if ( s is Counted )
+ return i + s.count();
+ i++;
+ }
+ return i;
+ }
+ else if (o is String)
+ return ((String)o).Length;
+ else if (o is ICollection)
+ return ((ICollection)o).Count;
+ else if (o is IDictionary)
+ return ((IDictionary)o).Count;
+ else if (o is Array)
+ return ((Array)o).GetLength(0);
+
+ throw new InvalidOperationException("count not supported on this type: " + o.GetType().Name);
+ }
+
+
+ public static ISeq seq(object coll)
+ {
+ if (coll == null)
+ return null;
+ else if (coll is ISeq)
+ return (ISeq)coll;
+ else if (coll is IPersistentCollection)
+ return ((IPersistentCollection)coll).seq();
+ else
+ return seqFrom(coll);
+ }
+
+ // TODO: Handle Arrays (ArraySeq), Iterable, generics, etc.
+ static private ISeq seqFrom(object coll)
+ {
+ //if(coll is Iterable)
+ // return IteratorSeq.create(((Iterable) coll).iterator());
+ //else
+ if (coll.GetType().IsArray)
+ return ArraySeq.createFromObject(coll);
+ else
+
+ if (coll is string)
+ return StringSeq.create((string)coll);
+ // else if(coll instanceof Map)
+ // return seq(((Map) coll).entrySet());
+ //// else if(coll instanceof Iterator)
+ //// return IteratorSeq.create((Iterator) coll);
+ //// else if(coll instanceof Enumeration)
+ //// return EnumerationSeq.create(((Enumeration) coll));
+ else
+ throw new ArgumentException("Don't know how to create ISeq from: " + coll.GetType().Name);
+ }
+
+ public static IPersistentCollection conj(IPersistentCollection coll, Object x)
+ {
+ if (coll == null)
+ return new PersistentList(x);
+ return coll.cons(x);
+ }
+
+ public static ISeq cons(object x, object coll)
+ {
+ ISeq y = seq(coll);
+ return y == null
+ ? new PersistentList(x)
+ : y.cons(x);
+ }
+
+ public static object first(object x)
+ {
+ if (x is ISeq)
+ return ((ISeq)x).first();
+ ISeq seq = RT.seq(x);
+ return (seq == null)
+ ? null
+ : seq.first();
+ }
+
+ public static ISeq rest(object x)
+ {
+ if (x is ISeq)
+ return ((ISeq)x).rest();
+ ISeq seq = RT.seq(x);
+ if (seq == null)
+ return null;
+ return seq.rest();
+ }
+
+ public static object second(object x)
+ {
+ return first(rest(x));
+ }
+
+ public static object third(object x)
+ {
+ return first(rest(rest(x)));
+ }
+
+ public static object fourth(object x)
+ {
+ return first(rest(rest(rest(x))));
+ }
+
+ public static ISeq rrest(object x)
+ {
+ return rest(rest(x));
+ }
+
+ public static Associative assoc(object coll, object key, Object val)
+ {
+ if (coll == null)
+ return new PersistentArrayMap(new object[] { key, val });
+ return ((Associative)coll).assoc(key, val);
+ }
+
+ // do we need this
+ //static Boolean HasTag(object o, object tag)
+ //{
+ // return Util.equals(tag,,RT.get(RT.meta(o),TAG_KEY);
+ //}
+
+
+ static public readonly object[] EMPTY_ARRAY = new Object[] { };
+
+
+
+ static public Object readString(String s)
+ {
+ TextReader r = new StringReader(s);
+ return LispReader.read(r, true, null, false);
+ }
+
+
+
+
+ static public void print(Object x, TextWriter w)
+ {
+ //call multimethod
+ if (PRINT_INITIALIZED.IsBound && RT.booleanCast(PRINT_INITIALIZED.deref()))
+ {
+ PR_ON.invoke(x, w);
+ return;
+ }
+
+ bool readably = booleanCast(PRINT_READABLY.deref());
+
+ // Print meta, if exists & should be printed
+ if ( x is Obj )
+ {
+ Obj o = x as Obj;
+ if (RT.count(o.meta()) > 0 && readably && booleanCast(PRINT_META.deref()))
+ {
+ IPersistentMap meta = o.meta();
+ w.Write("#^");
+ if ( meta.count() == 1 && meta.containsKey(TAG_KEY))
+ print(meta.valAt(TAG_KEY),w);
+ else
+ print(meta,w);
+ w.Write(' ');
+ }
+ }
+
+ if (x == null)
+ w.Write("nil");
+ else if (x is ISeq || x is IPersistentList)
+ {
+ w.Write('(');
+ printInnerSeq(seq(x), w);
+ w.Write(')');
+ }
+ else if ( x is string)
+ {
+ string s = x as string;
+ if ( !readably)
+ w.Write(s);
+ else{
+ w.Write('"');
+ foreach (char c in s)
+ {
+ switch (c)
+ {
+ case '\n':
+ w.Write("\\n");
+ break;
+ case '\t':
+ w.Write("\\t");
+ break;
+ case '\r':
+ w.Write("\\r");
+ break;
+ case '"':
+ w.Write("\\\"");
+ break;
+ case '\\':
+ w.Write("\\\\");
+ break;
+ case '\f':
+ w.Write("\\f");
+ break;
+ case '\b':
+ w.Write("\\b");
+ break;
+ default:
+ w.Write(c);
+ break;
+ }
+ }
+ w.Write('"');
+ }
+ }
+ else if ( x is IPersistentMap )
+ {
+ w.Write('{');
+ for ( ISeq s = seq(x); s != null; s = s.rest() )
+ {
+ IMapEntry e = (IMapEntry) s.first();
+ print(e.key(),w);
+ w.Write(' ');
+ print(e.val(),w);
+ if ( s.rest() != null )
+ w.Write(", ");
+ }
+ w.Write('}');
+ }
+ else if ( x is IPersistentVector )
+ {
+ IPersistentVector v = x as IPersistentVector;
+ int n = v.count();
+ w.Write('[');
+ for ( int i=0; i < n; i++ )
+ {
+ print(v.nth(i),w);
+ if ( i < n-1 )
+ w.Write(" ");
+ }
+ w.Write(']');
+ }
+ else if ( x is IPersistentSet )
+ {
+ w.Write("#{");
+ for ( ISeq s = seq(x); s != null; s = s.rest() )
+ {
+ print(s.first(),w);
+ if ( s.rest() != null )
+ w.Write(" ");
+ }
+ w.Write('}');
+ }
+ else if ( x is Char )
+ {
+ char c = (char)x;
+ if (!readably)
+ w.Write(c);
+ else{
+ w.Write('\\');
+ switch (c)
+ {
+ case '\n':
+ w.Write("newline");
+ break;
+ case '\t':
+ w.Write("tab");
+ break;
+ case ' ':
+ w.Write("space");
+ break;
+ case '\b':
+ w.Write("backspace");
+ break;
+ case '\f':
+ w.Write("formfeed");
+ break;
+ case '\r':
+ w.Write("return");
+ break;
+ default:
+ w.Write(c);
+ break;
+ }
+ }
+ }
+ else if ( x is Type )
+ {
+ w.Write("#=");
+ w.Write(((Type)x).FullName);
+ }
+ else if ( x is java.math.BigDecimal && readably )
+ {
+ w.Write(x.ToString());
+ w.Write("M");
+ }
+ else if ( x is Var )
+ {
+ Var v = x as Var;
+ w.Write("#=(var {0}/{1})", v.Namespace.Name, v.Symbol);
+ }
+ else
+ w.Write(x.ToString());
+
+ //sb.AppendFormat("<{0}: {1}>", x.GetType().Name, x.GetHashCode());
+
+ }
+
+
+ private static void printInnerSeq(ISeq x, TextWriter w)
+ {
+ for (ISeq s = x; s != null; s = s.rest())
+ {
+ print(s.first(), w);
+ if (s.rest() != null)
+ w.Write(' ');
+ }
+ }
+
+ public static string printToConsole(object x)
+ {
+ string ret = printString(x);
+ Console.WriteLine(ret);
+ return ret;
+ }
+
+ static public string printString(object x)
+ {
+ StringWriter sw = new StringWriter();
+ print(x, sw);
+ return sw.ToString();
+ }
+
+ class DefaultComparer : IComparer
+ {
+ #region IComparer Members
+
+ public int Compare(object x, object y)
+ {
+ return Util.compare(x, y); // was ((IComparable)x).CompareTo(y); -- changed in Java rev 1145
+ }
+
+ #endregion
+
+ #region core.clj compatibility
+
+ // Somewhere, there is an explicit call to compare
+ public int compare(object x, object y)
+ {
+ return Util.compare(x, y); // was ((IComparable)x).CompareTo(y);-- changed in Java rev 1145
+ }
+
+ #endregion
+ }
+
+ static public readonly IComparer DEFAULT_COMPARER = new DefaultComparer();
+
+
+ static public Object get(Object coll, Object key)
+ {
+ if (coll == null)
+ return null;
+ else if (coll is Associative)
+ return ((Associative)coll).valAt(key);
+ else if (coll is IDictionary)
+ {
+ IDictionary m = (IDictionary)coll;
+ return m[key];
+ }
+ else if (coll is IPersistentSet)
+ {
+ IPersistentSet set = (IPersistentSet)coll;
+ return set.get(key);
+
+ }
+ else if (Util.IsNumeric(key) && (coll is string || coll.GetType().IsArray))
+ {
+ int n = Util.ConvertToInt(key);
+ return n >= 0 && n < count(coll) ? nth(coll, n) : null;
+ }
+ return null;
+ }
+
+ static public Object get(Object coll, Object key, Object notFound)
+ {
+ if (coll == null)
+ return notFound;
+ else if (coll is Associative)
+ return ((Associative)coll).valAt(key, notFound);
+ else if (coll is IDictionary)
+ {
+ IDictionary m = (IDictionary)coll;
+ if (m.Contains(key))
+ return m[key];
+ return notFound;
+ }
+ else if (coll is IPersistentSet)
+ {
+ IPersistentSet set = (IPersistentSet)coll;
+ if (set.contains(key))
+ return set.get(key);
+ return notFound;
+ }
+ else if (Util.IsNumeric(key) && (coll is string || coll.GetType().IsArray))
+ {
+ int n = Util.ConvertToInt(key);
+ return n >= 0 && n < count(coll) ? nth(coll, n) : notFound;
+ }
+ return notFound;
+ }
+
+ public static object contains(object coll, object key)
+ {
+ if (coll == null)
+ return F;
+ else if (coll is Associative)
+ return ((Associative)coll).containsKey(key) ? T : F;
+ else if (coll is IPersistentSet)
+ return ((IPersistentSet)coll).contains(key) ? T : F;
+ else if (coll is IDictionary)
+ {
+ IDictionary m = (IDictionary)coll;
+ return m.Contains(key) ? T : F;
+ }
+ else if (Util.IsNumeric(key) && (coll is String || coll.GetType().IsArray))
+ {
+ int n = Util.ConvertToInt(key);
+ return n >= 0 && n < count(coll);
+ }
+ return F;
+ //throw new UnsupportedOperationException("contains not supported on this type");
+ }
+
+
+ public static object find(object coll, object key)
+ {
+ if (coll == null)
+ return null;
+ else if ( coll is Associative )
+ return ((Associative)coll).entryAt(key);
+ else
+ {
+ IDictionary m = (IDictionary)coll;
+ if (m.Contains(key))
+ return new MapEntry(key, m[key]);
+ return null;
+ }
+ }
+
+ public static object dissoc(object coll, object key)
+ {
+ return coll == null
+ ? null
+ : ((IPersistentMap)coll).without(key);
+ }
+
+ static public Object nth(Object coll, int n)
+ {
+ if (coll == null)
+ return null;
+ else if (coll is IPersistentVector)
+ return ((IPersistentVector)coll).nth(n);
+ else if (coll is String)
+ return ((string)coll)[n];
+ else if (coll.GetType().IsArray)
+ return ((Array)coll).GetValue(n); // TODO: Java has Reflector.prepRet -- check all uses.
+ else if (coll is IList) // Java has RandomAccess here. CLR has no equiv. Caused some infinite loops in places ASeq[].
+ return ((IList)coll)[n];
+ else if (coll is Match)
+ return ((Match)coll).Groups[n];
+ else if (coll is DictionaryEntry)
+ {
+ DictionaryEntry e = (DictionaryEntry)coll;
+ if (n == 0)
+ return e.Key;
+ else if (n == 1)
+ return e.Value;
+ throw new IndexOutOfRangeException();
+ }
+ else if (coll.GetType().IsGenericType && coll.GetType().Name == "KeyValuePair`2")
+ {
+ if (n == 0)
+ return coll.GetType().InvokeMember("Key", BindingFlags.GetProperty, null, coll, null);
+ else if (n == 1)
+ return coll.GetType().InvokeMember("Value", BindingFlags.GetProperty, null, coll, null);
+ throw new IndexOutOfRangeException();
+ }
+ else if (coll is Sequential)
+ {
+ // TODO: FIX: Another assumption that Sequential implies castable to IPersistentCollection
+ ISeq seq = ((IPersistentCollection)coll).seq();
+ coll = null; // release in case GC
+ for (int i = 0; i <= n && seq != null; ++i, seq = seq.rest())
+ {
+ if (i == n)
+ return seq.first();
+ }
+ throw new IndexOutOfRangeException();
+ }
+ else
+ throw new InvalidOperationException("nth not supported on this type: " + coll.GetType().Name);
+ }
+
+ // NOT SURE WHY WE NEED THIS. THIS VERSION ADDED IN REV 1112 (But in Reflector)
+ public static Object prepRet(Object x)
+ {
+ // if(c == boolean.class)
+ // return ((Boolean) x).booleanValue() ? RT.T : null;
+ if (x is Boolean)
+ return ((Boolean)x) ? RT.T : RT.F; // Java version has Boolean.TRUE and Boolean.FALSE
+ return x;
+ }
+
+ static public Object nth(Object coll, int n, Object notFound)
+ {
+ if (coll == null)
+ return notFound;
+ else if (n < 0)
+ return notFound;
+ else if (coll is IPersistentVector)
+ {
+ IPersistentVector v = (IPersistentVector)coll;
+ if (n < v.count())
+ return v.nth(n);
+ return notFound;
+ }
+ else if (coll is String)
+ {
+ String s = (String)coll;
+ if (n < s.Length)
+ return s[n];
+ return notFound;
+ }
+ else if (coll.GetType().IsArray)
+ {
+ Array a = (Array)coll;
+ if (n < a.Length)
+ return a.GetValue(n);
+ return notFound;
+ }
+ else if (coll is IList) // Changed to RandomAccess in Java Rev 1218.
+ {
+ IList list = (IList)coll;
+ if (n < list.Count)
+ return list[n];
+ return notFound;
+ }
+ else if (coll is Match)
+ {
+ Match m = (Match)coll;
+ if (n < m.Groups.Count)
+ return m.Groups[n];
+ return notFound;
+ }
+ else if (coll is DictionaryEntry)
+ {
+ DictionaryEntry e = (DictionaryEntry)coll;
+ if (n == 0)
+ return e.Key;
+ else if (n == 1)
+ return e.Value;
+ return notFound;
+ }
+ else if (coll.GetType().IsGenericType && coll.GetType().Name == "KeyValuePair`2")
+ {
+ if (n == 0)
+ return coll.GetType().InvokeMember("Key", BindingFlags.GetProperty, null, coll, null);
+ else if (n == 1)
+ return coll.GetType().InvokeMember("Value", BindingFlags.GetProperty, null, coll, null);
+ return notFound;
+ }
+ else if (coll is Sequential)
+ {
+ // TODO: FIX: ANother place where Sequential => IPersistentCollection
+ ISeq seq = ((IPersistentCollection)coll).seq();
+ coll = null; // release in case GC
+ for (int i = 0; i <= n && seq != null; ++i, seq = seq.rest())
+ {
+ if (i == n)
+ return seq.first();
+ }
+ return notFound;
+ }
+ else
+ throw new InvalidOperationException("nth not supported on this type: " + coll.GetType().Name);
+ }
+
+ public static object peek(object x)
+ {
+ return x == null
+ ? null
+ : ((IPersistentStack)x).peek();
+ }
+
+ public static object pop(object x)
+ {
+ return x == null
+ ? null
+ : ((IPersistentStack)x).pop();
+ }
+
+
+ public static ISeq keys(object coll)
+ {
+ return APersistentMap.KeySeq.create(seq(coll));
+ }
+
+ public static ISeq vals(object coll)
+ {
+ return APersistentMap.ValSeq.create(seq(coll));
+ }
+
+ public static IPersistentMap meta(object x)
+ {
+ return x is IMeta
+ ? ((IMeta)x).meta()
+ : null;
+ }
+
+
+ public static bool suppressRead()
+ {
+ // TODO: look up in suppress-read var
+ return false;
+ }
+
+ public static ISeq list(params object[] items)
+ {
+ return arrayToList(items);
+ }
+
+ public static ISeq arrayToList(object[] items)
+ {
+ ISeq ret = null;
+ for (int i = items.Length - 1; i >= 0; --i)
+ ret = (ISeq)cons(items[i], ret);
+ return ret;
+ }
+
+
+ public static object[] toArray(object coll)
+ {
+ if (coll == null)
+ return EMPTY_ARRAY;
+ else if (coll is object[])
+ return (object[])coll;
+ else if (coll is java.util.Collection)
+ return ((java.util.Collection)coll).toArray();
+ else if (coll is IEnumerable)
+ return toArray((IEnumerable)coll);
+ else if (coll is java.util.Map)
+ return ((java.util.Map)coll).entrySet().toArray();
+ else if (coll is String)
+ {
+ char[] chars = ((String)coll).ToCharArray();
+ object[] ret = new object[chars.Length];
+ for (int i = 0; i < chars.Length; i++)
+ ret[i] = chars[i];
+ return ret;
+ }
+ else if ( coll is ISeq )
+ return toArray((ISeq) coll);
+ else if ( coll is IPersistentCollection )
+ return toArray(((IPersistentCollection)coll).seq());
+ else if (coll.GetType().IsArray)
+ {
+ ISeq s = (seq(coll));
+ object[] ret = new object[count(s)];
+ for (int i = 0; i < ret.Length; i++, s = s.rest())
+ ret[i] = s.first();
+ return ret;
+ }
+ else
+ throw new Exception("Unable to convert: " + coll.GetType() + " to Object[]");
+ }
+
+ private static object[] toArray(IEnumerable e)
+ {
+ List<object> list = new List<object>();
+ foreach (object o in e)
+ list.Add(o);
+
+ return list.ToArray();
+ }
+
+ private static object[] toArray(ISeq seq)
+ {
+ object[] array = new object[seq.count()];
+ int i = 0;
+ for (ISeq s = seq; s != null; s = s.rest(), i++)
+ array[i] = s.first();
+
+ return array;
+ }
+
+
+
+ public static ISeq listStar(object arg1, ISeq rest)
+ {
+ return cons(arg1,rest);
+ }
+
+ public static ISeq listStar(object arg1, object arg2, ISeq rest)
+ {
+ return cons(arg1,cons(arg2,rest));
+ }
+
+ public static ISeq listStar(object arg1, object arg2, object arg3, ISeq rest)
+ {
+ return cons(arg1,cons(arg2,cons(arg3,rest)));
+ }
+
+
+ private static int _id;
+
+ static public int nextID()
+ {
+ return Interlocked.Increment(ref _id);
+ }
+
+ static public Var var(String ns, String name)
+ {
+ return Var.intern(Namespace.findOrCreate(Symbol.intern(null, ns)), Symbol.intern(null, name));
+ }
+
+ static public Var var(String ns, String name, Object init)
+ {
+ return Var.intern(Namespace.findOrCreate(Symbol.intern(null, ns)), Symbol.intern(null, name), init);
+ }
+
+ #region boxing/casts
+
+ static public bool booleanCast(object x)
+ {
+ if (x is Boolean)
+ return ((Boolean)x);
+ return x != null;
+ }
+
+
+ public static int intCast(object x)
+ {
+ // ToInt32 rounds. We need truncation.
+ return (int)Convert.ToDouble(x);
+ }
+
+ static public int intCast(char x)
+ {
+ return x;
+ }
+
+ static public int intCast(byte x)
+ {
+ return x;
+ }
+
+ static public int intCast(short x)
+ {
+ return x;
+ }
+
+ public static long longCast(object x)
+ {
+ return (long)Convert.ToDouble(x);
+ }
+
+ public static float floatCast(object x)
+ {
+ return Convert.ToSingle(x);
+ }
+
+ public static double doubleCast(object x)
+ {
+ return Convert.ToDouble(x);
+ }
+
+ public static short shortCast(object x)
+ {
+ return (short)Convert.ToDouble(x);
+ }
+
+ public static byte byteCast(object x)
+ {
+ return (byte)Convert.ToDouble(x);
+ }
+
+ public static char charCast(object x)
+ {
+ return Convert.ToChar(x);
+ }
+
+
+ public static IPersistentMap map(params object[] init)
+ {
+ return (init == null )
+ ? PersistentArrayMap.EMPTY
+ : (init.Length <= PersistentArrayMap.HASHTABLE_THRESHOLD )
+ ? (IPersistentMap)new PersistentArrayMap(init)
+ : (IPersistentMap)PersistentHashMap.create(init);
+ }
+
+ public static IPersistentVector vector(params object[] init)
+ {
+ return LazilyPersistentVector.createOwning(init);
+ }
+
+
+ public static IPersistentVector subvec(IPersistentVector v, int start, int end)
+ {
+ if (end < start || start < 0 || end > v.count())
+ throw new IndexOutOfRangeException();
+ if (start == end)
+ return PersistentVector.EMPTY;
+ return new APersistentVector.SubVector(null, v, start, end);
+ }
+
+ #endregion
+
+
+
+
+ public static Type classForName(string p)
+ {
+ // TODO: We're really going to have to work on this.
+ Type t = null;
+
+ t = Type.GetType(p, false);
+
+ if (t != null)
+ return t;
+
+ Assembly assy = Assembly.GetExecutingAssembly();
+ t = assy.GetType(p, false);
+ if (t != null)
+ return t;
+
+ AppDomain domain = AppDomain.CurrentDomain;
+ Assembly[] assys = domain.GetAssemblies();
+ List<Type> candidateTypes = new List<Type>();
+
+ foreach (Assembly assy1 in assys)
+ {
+ Type t1 = assy1.GetType(p, false);
+ if (t1 != null)
+ candidateTypes.Add(t1);
+ }
+
+ if (candidateTypes.Count == 0)
+ t = null;
+ else if (candidateTypes.Count == 1)
+ t = candidateTypes[0];
+ else // multiple, ambiguous
+ t = null;
+
+
+
+ // IEnumerable<Type> types1 = assy.GetTypes();
+ // List<Type> typeList1 = new List<Type>(types1);
+
+ // IEnumerable<Type> types = assy.GetTypes().Where(t1 => t1.FullName == p);
+ // List<Type> typeList = new List<Type>(types);
+ // if (typeList.Count == 1)
+ // t = typeList[0];
+ //}
+ //catch
+ //{
+ //}
+
+ return t;
+ }
+
+
+
+ #region Array interface
+
+ public static int alength(Array a)
+ {
+ return a.Length;
+ }
+
+ public static Array aclone(Array a)
+ {
+ return (Array) a.Clone();
+ }
+
+ public static object aget(Array a, int idx)
+ {
+ return a.GetValue(idx);
+ }
+
+ public static object aset(Array a, int idx, object val)
+ {
+ a.SetValue(val, idx);
+ return val;
+ }
+
+
+
+ #endregion
+
+
+ public static long nanoTime()
+ {
+ return DateTime.Now.Ticks * 100;
+ }
+
+ private static readonly Stopwatch _stopwatch = new Stopwatch();
+
+ public static object StartStopwatch()
+ {
+ _stopwatch.Reset();
+ _stopwatch.Start();
+ return null;
+ }
+
+ public static long StopStopwatch()
+ {
+ _stopwatch.Stop();
+ return _stopwatch.ElapsedMilliseconds;
+ }
+
+
+
+ // In core.clj, we see (cast Number x) in a number of numeric methods.
+ // There is no Number wrapper here.
+ // The intent is: if x is not a numeric value, throw a ClassCastException (Java),
+ // else return x
+ // And here it is:
+ public static object NumberCast(object x)
+ {
+ if (!Util.IsNumeric(x))
+ throw new InvalidCastException("Expected a number");
+ return x;
+ }
+
+ // The Java guys use Class.cast to do casting.
+ // We don't have that.
+ // Perhaps this will work.
+ // NOPE!
+ //public static object Cast(Type t, object o)
+ //{
+ // return Type.DefaultBinder.ChangeType(o, t, null);
+ //}
+
+ // The Java implementation goes through IFn, which are not yet using.
+ // We provide this instead.
+ // Now that we have gotten rid of ClojureRuntimeDelegate, we shouldn't need this anymore.
+ //public static object ApplyTo(ClojureRuntimeDelegate f, ISeq argList)
+ //{
+ // return f(SeqToArray<object>(argList));
+ //}
+
+ // This whole approach does not work due to the possibility
+ // of having an infinite sequence. !!!
+
+ public static T[] SeqToArray<T>(ISeq x)
+ {
+ if (x == null)
+ return new T[0];
+
+ T[] array = new T[x.count()];
+ int i = 0;
+ for (ISeq s = x; s != null; s = s.rest(), i++)
+ array[i] = (T)s.first();
+ return array;
+ }
+
+
+ static public object seqToTypedArray(ISeq seq)
+ {
+ Type type = (seq != null)
+ ? (seq.first() == null ? typeof(void) : seq.first().GetType())
+ : typeof(Object);
+ return seqToTypedArray(type, seq);
+ }
+
+ static public object seqToTypedArray(Type type, ISeq seq)
+ {
+ Array ret = Array.CreateInstance(type, seq == null ? 0 : seq.count());
+ for(int i = 0; seq != null; ++i, seq = seq.rest())
+ ret.SetValue(seq.first(),i);
+ return ret;
+ }
+
+ // The Java version goes through Array.sort to do this,
+ // but I don't have a way to pass a comparator.
+
+ class ComparerConverter : IComparer
+ {
+ readonly IFn _fn;
+
+ public ComparerConverter(IFn fn)
+ {
+ _fn = fn;
+ }
+
+ #region IComparer Members
+
+ public int Compare(object x, object y)
+ {
+ return (int) _fn.invoke( x,y );
+ }
+
+ #endregion
+ }
+
+ public static void SortArray(Array a, IFn fn)
+ {
+ Array.Sort(a, new ComparerConverter(fn));
+ }
+
+
+ //public static ClojureRuntimeDelegate ConvertToCRD(object o)
+ //{
+ // if (o is ClojureRuntimeDelegate)
+ // return (ClojureRuntimeDelegate)o;
+ // else if (o is IFn)
+ // return ((IFn)o).GetClojureRuntimeDelegate();
+ // throw new InvalidCastException(String.Format("Can't cast type {0} to ClojureRuntimeDelegate: {1}",o.GetType().ToString(),o));
+ // // TODO: Look for explicit or implicit cast operators
+ //}
+
+ static readonly Random _random = new Random();
+
+ public static double random()
+ {
+ lock (_random)
+ {
+ return _random.NextDouble();
+ }
+ }
+
+ // TODO: Figure out how to do a load.
+ public static object load(object pathname)
+ {
+ return null;
+ }
+
+ static RT()
+ {
+
+ Keyword dockw = Keyword.intern(null, "doc");
+ Keyword arglistskw = Keyword.intern(null, "arglists");
+ Symbol namesym = Symbol.create("name");
+
+ CURRENT_NS.Tag = Symbol.create("closure.lang.Namespace");
+ // during bootstrap, ns same as in-ns
+ Var.intern(CLOJURE_NS,NAMESPACE,new InNamespaceFn());
+
+ AGENT.SetMeta(map(dockw, "The agent currently running an action on this thread, else nil."));
+ AGENT.Tag = Symbol.create("clojure.lang.Agent");
+
+ Var v;
+ v = Var.intern(CLOJURE_NS, IN_NAMESPACE, new InNamespaceFn());
+ v.SetMeta(map(dockw,"Sets *ns* to the namespace named by the symbol, creating it if needed.",
+ arglistskw, list(vector(namesym))));
+
+ v = Var.intern(CLOJURE_NS, IDENTICAL, new IdenticalFn());
+ v.SetMeta(map(dockw, "tests if 2 arguments are the same object",
+ arglistskw, list(vector(Symbol.create("x"), Symbol.create("y")))));
+
+ // Eventually, load core.clj and other support files from here
+
+ //Var.PushThreadBindings(
+ // RT.map(CURRENT_NS, CURRENT_NS.get(),
+ // WARN_ON_REFLECTION, WARN_ON_REFLECTION.get()));
+ //try
+ //{
+ // Symbol USER = Symbol.create("user");
+ // Symbol CLOJURE = Symbol.create("clojure.core");
+
+ // Var in_ns = var("clojure.core", "in-ns");
+ // Var refer = var("cloure.core", "refer");
+ // in_ns.invoke(USER);
+ // refer.invoke(CLOJURE);
+ // //maybeLoadResourceScript("user.clj");
+ //}
+ //finally
+ //{
+ // Var.PopThreadBindings();
+ //}
+
+
+ }
+
+
+ public static void LookAtMe(object o)
+ {
+ Console.WriteLine("Here it is: {0}", o);
+ }
+
+ // TODO: streams
+
+// static public IStream stream(final Object coll) throws Exception{
+// if(coll == null)
+// return EMPTY_STREAM;
+// else if(coll instanceof IStream)
+// return (IStream) coll;
+// else if(coll instanceof Streamable)
+// return ((Streamable)coll).stream();
+// else if(coll instanceof Fn)
+// {
+// return new IStream(){
+// public Object next() throws Exception {
+// return ((IFn)coll).invoke();
+// }
+// };
+// }
+// else if(coll instanceof Iterable)
+// return new IteratorStream(((Iterable) coll).iterator());
+// else if (coll.getClass().isArray())
+// return ArrayStream.createFromObject(coll);
+// else if (coll instanceof String)
+// return ArrayStream.createFromObject(((String)coll).toCharArray());
+
+// throw new IllegalArgumentException("Don't know how to create IStream from: " + coll.getClass().getSimpleName());
+ //}
+
+
+ #region Stream support
+
+ private static readonly object EOS = new object();
+
+ public static object eos()
+ {
+ return EOS;
+ }
+
+ public static bool isEOS(object o)
+ {
+ return o == EOS;
+ }
+
+ public static readonly IStream EMPTY_STREAM = new EmptyStream();
+
+ private class EmptyStream : IStream
+ {
+ #region IStream Members
+
+ public object next()
+ {
+ return eos();
+ }
+
+ #endregion
+ }
+
+
+ #endregion
+
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Range.cs b/ClojureCLR/Clojure/Clojure/Lib/Range.cs new file mode 100644 index 00000000..b28879fc --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Range.cs @@ -0,0 +1,219 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents a (contiguous) range of integers.
+ /// </summary>
+ public class Range: ASeq, IReduce, Streamable, Counted
+ {
+ #region Data
+
+ /// <summary>
+ /// Final value in the range.
+ /// </summary>
+ private readonly int _end;
+
+ /// <summary>
+ /// First value in the range.
+ /// </summary>
+ private readonly int _n;
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ /// <summary>
+ /// Initialize a range.
+ /// </summary>
+ /// <param name="start">Start value</param>
+ /// <param name="end">End value</param>
+ /// <remarks>Needed to interface with core.clj</remarks>
+ public Range(object start, object end)
+ : this((int)start, (int)end)
+ {
+ }
+
+ /// <summary>
+ /// Initialize a range.
+ /// </summary>
+ /// <param name="start">Start value</param>
+ /// <param name="end">End value</param>
+ public Range(int start, int end)
+ {
+ this._end = end;
+ this._n = start;
+ }
+
+ /// <summary>
+ /// Initialize a range and attach metadata.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ /// <param name="start">Start value</param>
+ /// <param name="end">End value</param>
+ public Range(IPersistentMap meta, int start, int end)
+ : base (meta)
+ {
+ this._end = end;
+ this._n = start;
+ }
+
+ #endregion
+
+ #region IPersistentCollection members
+
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ public override int count()
+ {
+ return _n < _end ? _end - _n : 0;
+ }
+
+ #endregion
+
+ #region ISeq members
+
+ /// <summary>
+ /// Gets the first item.
+ /// </summary>
+ /// <returns>The first item.</returns>
+ public override object first()
+ {
+ return _n;
+ }
+
+ /// <summary>
+ /// Gets the rest of the sequence.
+ /// </summary>
+ /// <returns>The rest of the sequence, or <c>null</c> if no more elements.</returns>
+ public override ISeq rest()
+ {
+ return (_n < _end - 1)
+ ? new Range(_meta, _n + 1, _end)
+ : null;
+ }
+
+ #endregion
+
+ #region IObj members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return meta == this.meta()
+ ? this
+ : new Range(meta, _end, _n);
+ }
+
+ #endregion
+
+ #region IReduce Members
+
+ /// <summary>
+ /// Reduce the collection using a function.
+ /// </summary>
+ /// <param name="f">The function to apply.</param>
+ /// <returns>The reduced value</returns>
+ /// <remarks>Computes f(...f(f(f(i0,i1),i2),i3),...).</remarks>
+ public object reduce(IFn f)
+ {
+ object ret = _n;
+ for (int x = _n + 1; x < _end; x++)
+ ret = f.invoke(ret, x);
+ return ret;
+ }
+
+ /// <summary>
+ /// Reduce the collection using a function.
+ /// </summary>
+ /// <param name="f">The function to apply.</param>
+ /// <param name="start">An initial value to get started.</param>
+ /// <returns>The reduced value</returns>
+ /// <remarks>Computes f(...f(f(f(start,i0),i1),i2),...).</remarks>
+ public object reduce(IFn f, object start)
+ {
+ object ret = f.invoke(start, _n);
+ for (int x = _n + 1; x < _end; x++)
+ ret = f.invoke(ret, x);
+ return ret;
+ }
+
+ #endregion
+
+ #region Streamable Members
+
+ /// <summary>
+ /// Implements a stream over a <see cref="Range">Range</see>.
+ /// </summary>
+ private class RangeStream : IStream
+ {
+ /// <summary>
+ /// Current position.
+ /// </summary>
+ private readonly AtomicLong _an;
+
+ /// <summary>
+ /// Final position.
+ /// </summary>
+ private readonly long _end;
+
+ /// <summary>
+ /// Initialize a <see cref="RangeStream">RangeStream</see>.
+ /// </summary>
+ /// <param name="n">Initial position.</param>
+ /// <param name="end">Final position.</param>
+ public RangeStream(long n, long end)
+ {
+ _an = new AtomicLong(n);
+ _end = end;
+ }
+
+
+ #region IStream Members
+
+ /// <summary>
+ /// Get the next value in the stream.
+ /// </summary>
+ /// <returns>The next value.</returns>
+ public object next()
+ {
+ long i = _an.getAndIncrement();
+ return (i < _end)
+ ? i
+ : RT.eos();
+ }
+
+ #endregion
+ }
+
+ /// <summary>
+ /// Gets an <see cref="IStream">IStream/see> for this object.
+ /// </summary>
+ /// <returns>The <see cref="IStream">IStream/see>.</returns>
+ public override IStream stream()
+ {
+ return new RangeStream(_n, _end);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Ratio.cs b/ClojureCLR/Clojure/Clojure/Lib/Ratio.cs new file mode 100644 index 00000000..4771dc6a --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Ratio.cs @@ -0,0 +1,230 @@ +/**
+ * 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 java.math;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents a rational number.
+ /// </summary>
+ public sealed class Ratio: IComparable, IConvertible
+ {
+ #region Data
+
+ /// <summary>
+ /// The numerator.
+ /// </summary>
+ private readonly BigInteger _numerator;
+
+ /// <summary>
+ /// Get the numerator.
+ /// </summary>
+ public BigInteger Numerator
+ {
+ get { return _numerator; }
+ }
+
+ /// <summary>
+ /// The denominator.
+ /// </summary>
+ private readonly BigInteger _denominator;
+
+ /// <summary>
+ /// Get the denominator.
+ /// </summary>
+ public BigInteger Denominator
+ {
+ get { return _denominator; }
+ }
+
+
+ #endregion
+
+ #region C-tors
+
+ /// <summary>
+ /// Initialize a Ratio from num/denom.
+ /// </summary>
+ /// <param name="numerator">The numerator.</param>
+ /// <param name="denominator">The denominator.</param>
+ public Ratio(BigInteger numerator, BigInteger denominator)
+ {
+ _numerator = numerator;
+ _denominator = denominator;
+ }
+
+ #endregion
+
+ #region Object overrides
+
+ /// <summary>
+ /// Determines of an object is equal to this object.
+ /// </summary>
+ /// <param name="obj">The object to compare to.</param>
+ /// <returns><value>true</value> if the object is equal to this object; <value>false</value> otherwise.</returns>
+ public override bool Equals(object obj)
+ {
+ Ratio r = obj as Ratio;
+ return r != null && r._numerator.Equals(_numerator) && r._denominator.Equals(_denominator);
+ }
+
+ /// <summary>
+ /// Gets a hash code for this.
+ /// </summary>
+ /// <returns>A hash code.</returns>
+ public override int GetHashCode()
+ {
+ return _numerator.GetHashCode() ^ _denominator.GetHashCode();
+ }
+
+ /// <summary>
+ /// Returns a string representing the ratio.
+ /// </summary>
+ /// <returns>A string.</returns>
+ public override string ToString()
+ {
+ return _numerator.ToString() + "/" + _denominator.ToString();
+ }
+
+ #endregion
+
+ #region IComparable Members
+
+ public int CompareTo(object obj)
+ {
+ return Numbers.compare(this, obj);
+ }
+
+ #endregion
+
+ #region IConvertible Members
+
+ public TypeCode GetTypeCode()
+ {
+ return TypeCode.Object;
+ }
+
+ public bool ToBoolean(IFormatProvider provider)
+ {
+ return ! _numerator.Equals(Numbers.BigIntegerZero);
+ }
+
+ public byte ToByte(IFormatProvider provider)
+ {
+ return Convert.ToByte(ToDouble(provider));
+ }
+
+ public char ToChar(IFormatProvider provider)
+ {
+ return Convert.ToChar(ToDouble(provider));
+ }
+
+ public DateTime ToDateTime(IFormatProvider provider)
+ {
+ return Convert.ToDateTime(ToDouble(provider));
+ }
+
+ public decimal ToDecimal(IFormatProvider provider)
+ {
+ return Convert.ToDecimal(ToDouble(provider));
+ }
+
+ public double ToDouble(IFormatProvider provider)
+ {
+ return _numerator.ToDouble(provider) / _denominator.ToDouble(provider);
+ }
+
+ public short ToInt16(IFormatProvider provider)
+ {
+ return Convert.ToInt16(ToDouble(provider));
+ }
+
+ public int ToInt32(IFormatProvider provider)
+ {
+ return Convert.ToInt32(ToDouble(provider));
+ }
+
+ public long ToInt64(IFormatProvider provider)
+ {
+ return Convert.ToInt64(ToDouble(provider));
+ }
+
+ public sbyte ToSByte(IFormatProvider provider)
+ {
+ return Convert.ToSByte(ToDouble(provider));
+ }
+
+ public float ToSingle(IFormatProvider provider)
+ {
+ return Convert.ToSingle(ToDouble(provider));
+ }
+
+ public string ToString(IFormatProvider provider)
+ {
+ return ToString();
+ }
+
+ public object ToType(Type conversionType, IFormatProvider provider)
+ {
+ return Convert.ChangeType(ToDouble(provider), conversionType);
+ }
+
+ public ushort ToUInt16(IFormatProvider provider)
+ {
+ return Convert.ToUInt16(ToDouble(provider));
+ }
+
+ public uint ToUInt32(IFormatProvider provider)
+ {
+ return Convert.ToUInt32(ToDouble(provider));
+ }
+
+ public ulong ToUInt64(IFormatProvider provider)
+ {
+ return Convert.ToUInt64(ToDouble(provider));
+ }
+
+ // Java Rev 1256.
+ // TODO: figure out the roundingModes in this implementation.
+ // TODO: Make this an explicit cast opearator?
+ // TODO: Look at all these conversions.
+ public BigDecimal ToBigDecimal()
+ {
+ BigDecimal numerator = new BigDecimal(this.Numerator);
+ BigDecimal denominator = new BigDecimal(this.Denominator);
+ return numerator.divide(denominator, 0);
+ }
+
+ //public double doubleValue()
+ //{
+ // return decimalValue(MathContext.DECIMAL64).doubleValue();
+ //}
+
+ //public BigDecimal decimalValue()
+ //{
+ // return decimalValue(MathContext.UNLIMITED);
+ //}
+
+ //public BigDecimal decimalValue(MathContext mc)
+ //{
+ // BigDecimal numerator = new BigDecimal(this.numerator);
+ // BigDecimal denominator = new BigDecimal(this.denominator);
+
+ // return numerator.divide(denominator, mc);
+ //}
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Ref.cs b/ClojureCLR/Clojure/Clojure/Lib/Ref.cs new file mode 100644 index 00000000..018e569b --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Ref.cs @@ -0,0 +1,603 @@ +/**
+ * 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.Threading;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents a reference.
+ /// </summary>
+ public class Ref : ARef, IFn, IComparable<Ref>, IRef
+ {
+ #region Nested classes
+
+ /// <summary>
+ /// Represents the value of reference on a thread at particular point in time.
+ /// </summary>
+ public sealed class TVal
+ {
+ #region Data
+
+ /// <summary>
+ /// The value.
+ /// </summary>
+ object _val;
+
+ /// <summary>
+ /// The value.
+ /// </summary>
+ public object Val
+ {
+ get { return _val; }
+ set { _val = value; }
+ }
+
+ /// <summary>
+ /// The transaction commit/read point at which this value was set.
+ /// </summary>
+ long _point;
+
+ /// <summary>
+ /// The transaction commit/read point at which this value was set.
+ /// </summary>
+ public long Point
+ {
+ get { return _point; }
+ }
+
+ /// <summary>
+ /// The clock time. (not used?)
+ /// </summary>
+ int _msecs;
+
+ /// <summary>
+ /// The prior <see cref="TVal">TVal</see>.
+ /// </summary>
+ /// <remarks>Implements a doubly-linked circular list.</remarks>
+ TVal _prior;
+
+ /// <summary>
+ /// The prior <see cref="TVal">TVal</see>.
+ /// </summary>
+ /// <remarks>Implements a doubly-linked circular list.</remarks>
+ public TVal Prior
+ {
+ get { return _prior; }
+ //set { _prior = value; }
+ }
+
+ /// <summary>
+ /// The next <see cref="TVal">TVal</see>.
+ /// </summary>
+ /// <remarks>Implements a doubly-linked circular list.</remarks>
+ TVal _next;
+
+ /// <summary>
+ /// The next <see cref="TVal">TVal</see>.
+ /// </summary>
+ /// <remarks>Implements a doubly-linked circular list.</remarks>
+ public TVal Next
+ {
+ get { return _next; }
+ //set { _next = value; }
+ }
+
+ #endregion
+
+ #region Ctors
+
+ /// <summary>
+ /// Construct a TVal, linked to a previous TVal.
+ /// </summary>
+ /// <param name="val"></param>
+ /// <param name="point"></param>
+ /// <param name="msecs"></param>
+ /// <param name="prior"></param>
+ public TVal(object val, long point, int msecs, TVal prior)
+ {
+ _val = val;
+ _point = point;
+ _msecs = msecs;
+ _prior = prior;
+ _next = _prior._next;
+ _prior._next = this;
+ _next._prior = this;
+ }
+
+ /// <summary>
+ /// Construct a TVal, linked to itself.
+ /// </summary>
+ /// <param name="val"></param>
+ /// <param name="point"></param>
+ /// <param name="msecs"></param>
+ public TVal(object val, long point, int msecs)
+ {
+ _val = val;
+ _point = point;
+ _msecs = msecs;
+ _prior = this;
+ _next = this;
+ }
+
+ #endregion
+
+ #region other
+
+ /// <summary>
+ /// Set the value/point.
+ /// </summary>
+ /// <param name="val"></param>
+ /// <param name="point"></param>
+ /// <param name="msecs"></param>
+ public void SetValue(object val, long point, int msecs)
+ {
+ _val = val;
+ _point = point;
+ _msecs = msecs;
+ }
+
+ #endregion
+ }
+
+ #endregion
+
+ #region Data
+
+ /// <summary>
+ /// Values for this reference.
+ /// </summary>
+ TVal _tvals;
+
+ /// <summary>
+ /// Number of faults for the reference.
+ /// </summary>
+ readonly AtomicInteger _faults;
+
+ /// <summary>
+ /// Reader/writer lock for the reference.
+ /// </summary>
+ readonly ReaderWriterLockSlim _lock;
+
+ /// <summary>
+ /// Info on the transaction locking this ref.
+ /// </summary>
+ LockingTransaction.Info _tinfo;
+
+ /// <summary>
+ /// Info on the transaction locking this ref.
+ /// </summary>
+ public LockingTransaction.Info TInfo
+ {
+ get { return _tinfo; }
+ set { _tinfo = value; }
+ }
+
+ /// <summary>
+ /// An id uniquely identifying this reference.
+ /// </summary>
+ readonly long _id;
+
+
+ /// <summary>
+ /// An id uniquely identifying this reference.
+ /// </summary>
+ public long Id
+ {
+ get { return _id; }
+ }
+
+ /// <summary>
+ /// Used to generate unique ids.
+ /// </summary>
+ static readonly AtomicLong _ids = new AtomicLong();
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ /// <summary>
+ /// Construct a ref with given initial value.
+ /// </summary>
+ /// <param name="initVal">The initial value.</param>
+ public Ref(object initVal)
+ : this(initVal, null)
+ {
+ }
+
+
+ /// <summary>
+ /// Construct a ref with given initial value and metadata.
+ /// </summary>
+ /// <param name="initVal">The initial value.</param>
+ /// <param name="meta">The metadat to attach.</param>
+ public Ref(object initval, IPersistentMap meta)
+ : base(meta)
+ {
+ _id = _ids.getAndIncrement();
+ _faults = new AtomicInteger();
+ _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
+ _tvals = new TVal(initval, 0, System.Environment.TickCount);
+ }
+
+ #endregion
+
+ #region Debugging
+
+ ///// <summary>
+ ///// I was having a hard day.
+ ///// </summary>
+ ///// <returns></returns>
+ //public string DebugStr()
+ //{
+ // StringBuilder sb = new StringBuilder();
+ // sb.Append("<Ref ");
+ // sb.Append(Id);
+ // sb.Append(", ");
+ // if (_tinfo == null)
+ // sb.Append("NO");
+ // else
+ // sb.AppendFormat("{0} {1}", _tinfo.Status.get(), _tinfo.StartPoint);
+ // sb.Append(", ");
+ // if (_tvals == null)
+ // sb.Append("TVals: NO");
+ // else
+ // {
+ // sb.Append("TVals: ");
+ // TVal t = _tvals;
+ // do
+ // {
+ // sb.Append(t.Point);
+ // sb.Append(" ");
+ // } while ((t = t.Prior) != _tvals);
+ // }
+ // sb.Append(">");
+ // return sb.ToString();
+ //}
+
+ #endregion
+
+ #region IDeref Members
+
+ /// <summary>
+ /// Gets the (immutable) value the reference is holding.
+ /// </summary>
+ /// <returns>The value</returns>
+ public override object deref()
+ {
+ LockingTransaction t = LockingTransaction.getRunning();
+ if (t == null)
+ {
+ object ret = currentVal();
+ //Console.WriteLine("Thr {0}, {1}: No-trans get => {2}", Thread.CurrentThread.ManagedThreadId,DebugStr(), ret);
+ return ret;
+ }
+ return t.doGet(this, _tvals);
+ }
+
+ object currentVal()
+ {
+ try
+ {
+ _lock.EnterReadLock();
+ if (_tvals != null)
+ return _tvals.Val;
+ throw new InvalidOperationException(String.Format("{0} is unbound.", ToString()));
+ }
+ finally
+ {
+ _lock.ExitReadLock();
+ }
+ }
+
+ #endregion
+
+ #region Interface for LockingTransaction
+
+ /// <summary>
+ /// Get the read lock.
+ /// </summary>
+ public void EnterReadLock()
+ {
+ _lock.EnterReadLock();
+ }
+
+ /// <summary>
+ /// Release the read lock.
+ /// </summary>
+ public void ExitReadLock()
+ {
+ _lock.ExitReadLock();
+ }
+
+ /// <summary>
+ /// Get the write lock.
+ /// </summary>
+ public void EnterWriteLock()
+ {
+ _lock.EnterWriteLock();
+ }
+
+ /// <summary>
+ /// Release the write lock.
+ /// </summary>
+ public void ExitWriteLock()
+ {
+ _lock.ExitWriteLock();
+ }
+
+ /// <summary>
+ /// Add to the fault count.
+ /// </summary>
+ public void AddFault()
+ {
+ _faults.incrementAndGet();
+ }
+
+ /// <summary>
+ /// Get the read/commit point associated with the current value.
+ /// </summary>
+ /// <returns></returns>
+ public long CurrentValPoint()
+ {
+ return _tvals != null ? _tvals.Point : -1;
+ }
+
+ /// <summary>
+ /// Try to get the value (else null).
+ /// </summary>
+ /// <returns>The value if it has been set; <value>null</value> otherwise.</returns>
+ public object TryGetVal()
+ {
+ return _tvals == null ? null : _tvals.Val;
+ }
+
+ /// <summary>
+ /// Set the value.
+ /// </summary>
+ /// <param name="val">The new value.</param>
+ /// <param name="commitPoint">The transaction's commit point.</param>
+ /// <param name="msecs">The clock time.</param>
+ internal void SetValue(object val, long commitPoint, int msecs)
+ {
+ if (_tvals == null)
+ _tvals = new TVal(val, commitPoint, msecs);
+ else if (_faults.get() > 0)
+ {
+ _tvals = new TVal(val, commitPoint, msecs, _tvals);
+ _faults.set(0);
+ }
+ else
+ {
+ _tvals = _tvals.Next;
+ _tvals.SetValue(val, commitPoint, msecs);
+ }
+ }
+
+ #endregion
+
+ #region Ref operations
+
+ /// <summary>
+ /// Set the value (must be in a transaction).
+ /// </summary>
+ /// <param name="val">The new value.</param>
+ /// <returns>The new value.</returns>
+ public object set(object val)
+ {
+ return LockingTransaction.getEx().doSet(this, val);
+ }
+
+ /// <summary>
+ /// Apply a commute to the reference. (Must be in a transaction.)
+ /// </summary>
+ /// <param name="fn">The function to apply to the current state and additional arguments.</param>
+ /// <param name="args">Additional arguments.</param>
+ /// <returns>The computed value.</returns>
+ public object commute(IFn fn, ISeq args)
+ {
+ return LockingTransaction.getEx().doCommute(this, fn, args);
+ }
+
+ /// <summary>
+ /// Change to a computed value.
+ /// </summary>
+ /// <param name="fn">The function to apply to the current state and additional arguments.</param>
+ /// <param name="args">Additional arguments.</param>
+ /// <returns>The computed value.</returns>
+ public object alter(IFn fn, ISeq args)
+ {
+ LockingTransaction t = LockingTransaction.getEx();
+ return t.doSet(this, fn.applyTo(RT.cons(t.doGet(this, _tvals), args)));
+ }
+
+ /// <summary>
+ /// Touch the reference. (Add to the tracking list in the current transaction.)
+ /// </summary>
+ public void touch()
+ {
+ LockingTransaction.getEx().doTouch(this);
+ }
+
+ #endregion
+
+ #region IFn Members
+
+
+ public IFn fn()
+ {
+ return (IFn)deref();
+ }
+
+ public object invoke()
+ {
+ return fn().invoke();
+ }
+
+ public object invoke(object arg1)
+ {
+ return fn().invoke(arg1);
+ }
+
+ public object invoke(object arg1, object arg2)
+ {
+ return fn().invoke(arg1, arg2);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3)
+ {
+ return fn().invoke(arg1, arg2, arg3);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
+ arg16);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
+ arg16, arg17);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17, object arg18)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
+ arg16, arg17, arg18);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17, object arg18, object arg19)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
+ arg16, arg17, arg18, arg19);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17, object arg18, object arg19, object arg20)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
+ arg16, arg17, arg18, arg19, arg20);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17, object arg18, object arg19, object arg20,
+ params object[] args)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
+ arg16, arg17, arg18, arg19, arg20, args);
+ }
+
+ public object applyTo(ISeq arglist)
+ {
+ return AFn.ApplyToHelper(this, arglist);
+ }
+
+ #endregion
+
+ #region IComparable<Ref> Members
+
+ /// <summary>
+ /// Compare to another ref.
+ /// </summary>
+ /// <param name="other">The other ref.</param>
+ /// <returns><value>true</value> if they are identical; <value>false</value> otherwise.</returns>
+ public int CompareTo(Ref other)
+ {
+ return _id.CompareTo(other._id);
+ }
+
+ #endregion
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/RestFn.cs b/ClojureCLR/Clojure/Clojure/Lib/RestFn.cs new file mode 100644 index 00000000..339e9a29 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/RestFn.cs @@ -0,0 +1,1396 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ public abstract class RestFn : AFunction
+ {
+ #region Data
+
+ protected readonly int _reqArity;
+
+ #endregion
+
+ #region C-tors
+
+ public RestFn(int reqArity)
+ {
+ _reqArity = reqArity;
+ }
+
+ #endregion
+
+ #region Invokes with explicit rest arg
+
+ protected virtual object doInvoke(object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object arg4, object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13,
+ object arg14, object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13,
+ object arg14, object arg15, object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13,
+ object arg14, object arg15, object arg16, object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13,
+ object arg14, object arg15, object arg16, object arg17, object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13,
+ object arg14, object arg15, object arg16, object arg17, object arg18, object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13,
+ object arg14, object arg15, object arg16, object arg17, object arg18, object arg19,
+ object args)
+ {
+ return null;
+ }
+
+ protected virtual object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13,
+ object arg14, object arg15, object arg16, object arg17, object arg18, object arg19,
+ object arg20, object args)
+ {
+ return null;
+ }
+
+ #endregion
+
+ #region IFn members
+
+ public override object applyTo(ISeq args)
+ {
+ if (RT.BoundedLength(args, _reqArity) <= _reqArity)
+ return base.applyTo(args);
+
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(args);
+ case 1:
+ return doInvoke(args.first()
+ , args.rest());
+ case 2:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 3:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 4:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 5:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 6:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 7:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 8:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 9:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 10:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 11:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 12:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 13:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 14:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 15:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 16:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 17:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 18:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 19:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+ case 20:
+ return doInvoke(args.first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , (args = args.rest()).first()
+ , args.rest());
+
+ }
+ throw WrongArityException();
+ }
+
+
+ public override Object invoke()
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1));
+ case 1:
+ return doInvoke(arg1, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2));
+ case 2:
+ return doInvoke(arg1, arg2, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2, arg3));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3));
+ case 2:
+ return doInvoke(arg1, arg2, ArraySeq.create(arg3));
+ case 3:
+ return doInvoke(arg1, arg2, arg3, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2, arg3, arg4));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3, arg4));
+ case 2:
+ return doInvoke(arg1, arg2, ArraySeq.create(arg3, arg4));
+ case 3:
+ return doInvoke(arg1, arg2, arg3, ArraySeq.create(arg4));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2, arg3, arg4, arg5));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3, arg4, arg5));
+ case 2:
+ return doInvoke(arg1, arg2, ArraySeq.create(arg3, arg4, arg5));
+ case 3:
+ return doInvoke(arg1, arg2, arg3, ArraySeq.create(arg4, arg5));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4, ArraySeq.create(arg5));
+ case 5:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2, arg3, arg4, arg5, arg6));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3, arg4, arg5, arg6));
+ case 2:
+ return doInvoke(arg1, arg2, ArraySeq.create(arg3, arg4, arg5, arg6));
+ case 3:
+ return doInvoke(arg1, arg2, arg3, ArraySeq.create(arg4, arg5, arg6));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4, ArraySeq.create(arg5, arg6));
+ case 5:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, ArraySeq.create(arg6));
+ case 6:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2, arg3, arg4, arg5, arg6, arg7));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3, arg4, arg5, arg6, arg7));
+ case 2:
+ return doInvoke(arg1, arg2, ArraySeq.create(arg3, arg4, arg5, arg6, arg7));
+ case 3:
+ return doInvoke(arg1, arg2, arg3, ArraySeq.create(arg4, arg5, arg6, arg7));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4, ArraySeq.create(arg5, arg6, arg7));
+ case 5:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, ArraySeq.create(arg6, arg7));
+ case 6:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, ArraySeq.create(arg7));
+ case 7:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
+ Object arg8)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3, arg4, arg5, arg6, arg7, arg8));
+ case 2:
+ return doInvoke(arg1, arg2, ArraySeq.create(arg3, arg4, arg5, arg6, arg7, arg8));
+ case 3:
+ return doInvoke(arg1, arg2, arg3, ArraySeq.create(arg4, arg5, arg6, arg7, arg8));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4, ArraySeq.create(arg5, arg6, arg7, arg8));
+ case 5:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, ArraySeq.create(arg6, arg7, arg8));
+ case 6:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, ArraySeq.create(arg7, arg8));
+ case 7:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, ArraySeq.create(arg8));
+ case 8:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
+ Object arg8, Object arg9)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9));
+ case 2:
+ return doInvoke(arg1, arg2, ArraySeq.create(arg3, arg4, arg5, arg6, arg7, arg8, arg9));
+ case 3:
+ return doInvoke(arg1, arg2, arg3, ArraySeq.create(arg4, arg5, arg6, arg7, arg8, arg9));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4, ArraySeq.create(arg5, arg6, arg7, arg8, arg9));
+ case 5:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, ArraySeq.create(arg6, arg7, arg8, arg9));
+ case 6:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, ArraySeq.create(arg7, arg8, arg9));
+ case 7:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, ArraySeq.create(arg8, arg9));
+ case 8:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, ArraySeq.create(arg9));
+ case 9:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
+ Object arg8, Object arg9, Object arg10)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10));
+ case 2:
+ return doInvoke(arg1, arg2, ArraySeq.create(arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10));
+ case 3:
+ return doInvoke(arg1, arg2, arg3, ArraySeq.create(arg4, arg5, arg6, arg7, arg8, arg9, arg10));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4, ArraySeq.create(arg5, arg6, arg7, arg8, arg9, arg10));
+ case 5:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, ArraySeq.create(arg6, arg7, arg8, arg9, arg10));
+ case 6:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, ArraySeq.create(arg7, arg8, arg9, arg10));
+ case 7:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, ArraySeq.create(arg8, arg9, arg10));
+ case 8:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, ArraySeq.create(arg9, arg10));
+ case 9:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, ArraySeq.create(arg10));
+ case 10:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
+ Object arg8, Object arg9, Object arg10, Object arg11)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11));
+ case 2:
+ return doInvoke(arg1, arg2, ArraySeq.create(arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11));
+ case 3:
+ return doInvoke(arg1, arg2, arg3, ArraySeq.create(arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4, ArraySeq.create(arg5, arg6, arg7, arg8, arg9, arg10, arg11));
+ case 5:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, ArraySeq.create(arg6, arg7, arg8, arg9, arg10, arg11));
+ case 6:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, ArraySeq.create(arg7, arg8, arg9, arg10, arg11));
+ case 7:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, ArraySeq.create(arg8, arg9, arg10, arg11));
+ case 8:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, ArraySeq.create(arg9, arg10, arg11));
+ case 9:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, ArraySeq.create(arg10, arg11));
+ case 10:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, ArraySeq.create(arg11));
+ case 11:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
+ Object arg8, Object arg9, Object arg10, Object arg11, Object arg12)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12));
+ case 2:
+ return doInvoke(arg1, arg2, ArraySeq.create(arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12));
+ case 3:
+ return doInvoke(arg1, arg2, arg3, ArraySeq.create(arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4, ArraySeq.create(arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12));
+ case 5:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, ArraySeq.create(arg6, arg7, arg8, arg9, arg10, arg11, arg12));
+ case 6:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, ArraySeq.create(arg7, arg8, arg9, arg10, arg11, arg12));
+ case 7:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, ArraySeq.create(arg8, arg9, arg10, arg11, arg12));
+ case 8:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, ArraySeq.create(arg9, arg10, arg11, arg12));
+ case 9:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, ArraySeq.create(arg10, arg11, arg12));
+ case 10:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, ArraySeq.create(arg11, arg12));
+ case 11:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, ArraySeq.create(arg12));
+ case 12:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
+ Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(
+ ArraySeq.create(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13));
+ case 2:
+ return doInvoke(arg1, arg2,
+ ArraySeq.create(arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13));
+ case 3:
+ return doInvoke(arg1, arg2, arg3,
+ ArraySeq.create(arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4,
+ ArraySeq.create(arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13));
+ case 5:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5,
+ ArraySeq.create(arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13));
+ case 6:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6,
+ ArraySeq.create(arg7, arg8, arg9, arg10, arg11, arg12, arg13));
+ case 7:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+ ArraySeq.create(arg8, arg9, arg10, arg11, arg12, arg13));
+ case 8:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ ArraySeq.create(arg9, arg10, arg11, arg12, arg13));
+ case 9:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
+ ArraySeq.create(arg10, arg11, arg12, arg13));
+ case 10:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+ ArraySeq.create(arg11, arg12, arg13));
+ case 11:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11,
+ ArraySeq.create(arg12, arg13));
+ case 12:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ ArraySeq.create(arg13));
+ case 13:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
+ Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14));
+ case 2:
+ return doInvoke(arg1, arg2, ArraySeq.create(arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14));
+ case 3:
+ return doInvoke(arg1, arg2, arg3,
+ ArraySeq.create(arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4,
+ ArraySeq.create(arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14));
+ case 5:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5,
+ ArraySeq.create(arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14));
+ case 6:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6,
+ ArraySeq.create(arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14));
+ case 7:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+ ArraySeq.create(arg8, arg9, arg10, arg11, arg12, arg13, arg14));
+ case 8:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ ArraySeq.create(arg9, arg10, arg11, arg12, arg13, arg14));
+ case 9:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
+ ArraySeq.create(arg10, arg11, arg12, arg13, arg14));
+ case 10:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+ ArraySeq.create(arg11, arg12, arg13, arg14));
+ case 11:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11,
+ ArraySeq.create(arg12, arg13, arg14));
+ case 12:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ ArraySeq.create(arg13, arg14));
+ case 13:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13,
+ ArraySeq.create(arg14));
+ case 14:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
+ Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
+ Object arg15)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15));
+ case 2:
+ return doInvoke(arg1, arg2, ArraySeq.create(arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15));
+ case 3:
+ return doInvoke(arg1, arg2, arg3, ArraySeq.create(arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4,
+ ArraySeq.create(arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15));
+ case 5:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5,
+ ArraySeq.create(arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15));
+ case 6:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6,
+ ArraySeq.create(arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15));
+ case 7:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+ ArraySeq.create(arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15));
+ case 8:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ ArraySeq.create(arg9, arg10, arg11, arg12, arg13, arg14, arg15));
+ case 9:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
+ ArraySeq.create(arg10, arg11, arg12, arg13, arg14, arg15));
+ case 10:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+ ArraySeq.create(arg11, arg12, arg13, arg14, arg15));
+ case 11:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11,
+ ArraySeq.create(arg12, arg13, arg14, arg15));
+ case 12:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ ArraySeq.create(arg13, arg14, arg15));
+ case 13:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13,
+ ArraySeq.create(arg14, arg15));
+ case 14:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ ArraySeq.create(arg15));
+ case 15:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
+ Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
+ Object arg15, Object arg16)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16));
+ case 2:
+ return doInvoke(arg1, arg2, ArraySeq.create(arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16));
+ case 3:
+ return doInvoke(arg1, arg2, arg3, ArraySeq.create(arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4, ArraySeq.create(arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16));
+ case 5:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5,
+ ArraySeq.create(arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16));
+ case 6:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6,
+ ArraySeq.create(arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16));
+ case 7:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+ ArraySeq.create(arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16));
+ case 8:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ ArraySeq.create(arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16));
+ case 9:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
+ ArraySeq.create(arg10, arg11, arg12, arg13, arg14, arg15, arg16));
+ case 10:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+ ArraySeq.create(arg11, arg12, arg13, arg14, arg15, arg16));
+ case 11:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11,
+ ArraySeq.create(arg12, arg13, arg14, arg15, arg16));
+ case 12:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ ArraySeq.create(arg13, arg14, arg15, arg16));
+ case 13:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13,
+ ArraySeq.create(arg14, arg15, arg16));
+ case 14:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ ArraySeq.create(arg15, arg16));
+ case 15:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, ArraySeq.create(arg16));
+ case 16:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
+ Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
+ Object arg15, Object arg16, Object arg17)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17));
+ case 2:
+ return doInvoke(arg1, arg2, ArraySeq.create(arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17));
+ case 3:
+ return doInvoke(arg1, arg2, arg3, ArraySeq.create(arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4, ArraySeq.create(arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17));
+ case 5:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, ArraySeq.create(arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17));
+ case 6:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6,
+ ArraySeq.create(arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17));
+ case 7:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+ ArraySeq.create(arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17));
+ case 8:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ ArraySeq.create(arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17));
+ case 9:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
+ ArraySeq.create(arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17));
+ case 10:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+ ArraySeq.create(arg11, arg12, arg13, arg14, arg15, arg16, arg17));
+ case 11:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11,
+ ArraySeq.create(arg12, arg13, arg14, arg15, arg16, arg17));
+ case 12:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ ArraySeq.create(arg13, arg14, arg15, arg16, arg17));
+ case 13:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13,
+ ArraySeq.create(arg14, arg15, arg16, arg17));
+ case 14:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ ArraySeq.create(arg15, arg16, arg17));
+ case 15:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, ArraySeq.create(arg16, arg17));
+ case 16:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, ArraySeq.create(arg17));
+ case 17:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
+ Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
+ Object arg15, Object arg16, Object arg17, Object arg18)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18));
+ case 2:
+ return doInvoke(arg1, arg2, ArraySeq.create(arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18));
+ case 3:
+ return doInvoke(arg1, arg2, arg3, ArraySeq.create(arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4, ArraySeq.create(arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18));
+ case 5:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, ArraySeq.create(arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18));
+ case 6:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, ArraySeq.create(arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17,
+ arg18));
+ case 7:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+ ArraySeq.create(arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18));
+ case 8:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ ArraySeq.create(arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18));
+ case 9:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
+ ArraySeq.create(arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18));
+ case 10:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+ ArraySeq.create(arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18));
+ case 11:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11,
+ ArraySeq.create(arg12, arg13, arg14, arg15, arg16, arg17, arg18));
+ case 12:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ ArraySeq.create(arg13, arg14, arg15, arg16, arg17, arg18));
+ case 13:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13,
+ ArraySeq.create(arg14, arg15, arg16, arg17, arg18));
+ case 14:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ ArraySeq.create(arg15, arg16, arg17, arg18));
+ case 15:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, ArraySeq.create(arg16, arg17, arg18));
+ case 16:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, ArraySeq.create(arg17, arg18));
+ case 17:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, ArraySeq.create(arg18));
+ case 18:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, arg18, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
+ Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
+ Object arg15, Object arg16, Object arg17, Object arg18, Object arg19)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18, arg19));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18, arg19));
+ case 2:
+ return doInvoke(arg1, arg2, ArraySeq.create(arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18, arg19));
+ case 3:
+ return doInvoke(arg1, arg2, arg3, ArraySeq.create(arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18, arg19));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4, ArraySeq.create(arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18, arg19));
+ case 5:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, ArraySeq.create(arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18,
+ arg19));
+ case 6:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, ArraySeq.create(arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17,
+ arg18, arg19));
+ case 7:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, ArraySeq.create(arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17,
+ arg18, arg19));
+ case 8:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, ArraySeq.create(arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16,
+ arg17, arg18, arg19));
+ case 9:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,
+ ArraySeq.create(arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19));
+ case 10:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+ ArraySeq.create(arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19));
+ case 11:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11,
+ ArraySeq.create(arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19));
+ case 12:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ ArraySeq.create(arg13, arg14, arg15, arg16, arg17, arg18, arg19));
+ case 13:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13,
+ ArraySeq.create(arg14, arg15, arg16, arg17, arg18, arg19));
+ case 14:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ ArraySeq.create(arg15, arg16, arg17, arg18, arg19));
+ case 15:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, ArraySeq.create(arg16, arg17, arg18, arg19));
+ case 16:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, ArraySeq.create(arg17, arg18, arg19));
+ case 17:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, ArraySeq.create(arg18, arg19));
+ case 18:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, arg18, ArraySeq.create(arg19));
+ case 19:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, arg18, arg19, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
+ Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
+ Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(ArraySeq.create(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20));
+ case 1:
+ return doInvoke(arg1, ArraySeq.create(arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20));
+ case 2:
+ return doInvoke(arg1, arg2, ArraySeq.create(arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20));
+ case 3:
+ return doInvoke(arg1, arg2, arg3, ArraySeq.create(arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4, ArraySeq.create(arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18, arg19,
+ arg20));
+ case 5:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, ArraySeq.create(arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17, arg18,
+ arg19, arg20));
+ case 6:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, ArraySeq.create(arg7, arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17,
+ arg18, arg19, arg20));
+ case 7:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, ArraySeq.create(arg8, arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16, arg17,
+ arg18, arg19, arg20));
+ case 8:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, ArraySeq.create(arg9, arg10, arg11, arg12,
+ arg13, arg14, arg15, arg16,
+ arg17, arg18, arg19,
+ arg20));
+ case 9:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, ArraySeq.create(arg10, arg11, arg12,
+ arg13, arg14, arg15,
+ arg16, arg17, arg18,
+ arg19, arg20));
+ case 10:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+ ArraySeq.create(arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20));
+ case 11:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11,
+ ArraySeq.create(arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20));
+ case 12:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ ArraySeq.create(arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20));
+ case 13:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13,
+ ArraySeq.create(arg14, arg15, arg16, arg17, arg18, arg19, arg20));
+ case 14:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ ArraySeq.create(arg15, arg16, arg17, arg18, arg19, arg20));
+ case 15:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, ArraySeq.create(arg16, arg17, arg18, arg19, arg20));
+ case 16:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, ArraySeq.create(arg17, arg18, arg19, arg20));
+ case 17:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, ArraySeq.create(arg18, arg19, arg20));
+ case 18:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, arg18, ArraySeq.create(arg19, arg20));
+ case 19:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, arg18, arg19, ArraySeq.create(arg20));
+ case 20:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, arg18, arg19, arg20, null);
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+ public override Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
+ Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
+ Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20, params object[] args)
+ {
+ switch (_reqArity)
+ {
+ case 0:
+ return doInvoke(OntoArrayPrepend(args, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11,
+ arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20));
+ case 1:
+ return doInvoke(arg1, OntoArrayPrepend(args, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11,
+ arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20));
+ case 2:
+ return doInvoke(arg1, arg2, OntoArrayPrepend(args, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11,
+ arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19,
+ arg20));
+ case 3:
+ return doInvoke(arg1, arg2, arg3, OntoArrayPrepend(args, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11,
+ arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19,
+ arg20));
+ case 4:
+ return doInvoke(arg1, arg2, arg3, arg4, OntoArrayPrepend(args, arg5, arg6, arg7, arg8, arg9, arg10, arg11,
+ arg12, arg13, arg14, arg15, arg16, arg17, arg18,
+ arg19, arg20));
+ case 5:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, OntoArrayPrepend(args, arg6, arg7, arg8, arg9, arg10, arg11,
+ arg12, arg13, arg14, arg15, arg16, arg17,
+ arg18, arg19, arg20));
+ case 6:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, OntoArrayPrepend(args, arg7, arg8, arg9, arg10, arg11,
+ arg12, arg13, arg14, arg15, arg16,
+ arg17, arg18, arg19, arg20));
+ case 7:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, OntoArrayPrepend(args, arg8, arg9, arg10, arg11,
+ arg12, arg13, arg14, arg15,
+ arg16, arg17, arg18, arg19,
+ arg20));
+ case 8:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, OntoArrayPrepend(args, arg9, arg10, arg11,
+ arg12, arg13, arg14, arg15,
+ arg16, arg17, arg18, arg19,
+ arg20));
+ case 9:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, OntoArrayPrepend(args, arg10, arg11,
+ arg12, arg13, arg14,
+ arg15, arg16, arg17,
+ arg18, arg19,
+ arg20));
+ case 10:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, OntoArrayPrepend(args, arg11,
+ arg12, arg13,
+ arg14, arg15,
+ arg16, arg17,
+ arg18, arg19,
+ arg20));
+ case 11:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11,
+ OntoArrayPrepend(args, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20));
+ case 12:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12,
+ OntoArrayPrepend(args, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20));
+ case 13:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13,
+ OntoArrayPrepend(args, arg14, arg15, arg16, arg17, arg18, arg19, arg20));
+ case 14:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ OntoArrayPrepend(args, arg15, arg16, arg17, arg18, arg19, arg20));
+ case 15:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, OntoArrayPrepend(args, arg16, arg17, arg18, arg19, arg20));
+ case 16:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, OntoArrayPrepend(args, arg17, arg18, arg19, arg20));
+ case 17:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, OntoArrayPrepend(args, arg18, arg19, arg20));
+ case 18:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, arg18, OntoArrayPrepend(args, arg19, arg20));
+ case 19:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, arg18, arg19, OntoArrayPrepend(args, arg20));
+ case 20:
+ return doInvoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
+ arg15, arg16, arg17, arg18, arg19, arg20, ArraySeq.create(args));
+ default:
+ throw WrongArityException();
+ }
+
+ }
+
+
+ protected static ISeq OntoArrayPrepend(object[] array, params object[] args)
+ {
+ ISeq ret = ArraySeq.create(array);
+ for (int i = args.Length - 1; i >= 0; --i)
+ ret = RT.cons(args[i], ret);
+ return ret;
+ }
+
+ #endregion
+
+ #region Misc
+
+ // do we need this?
+ protected static ISeq FindKey(object key, ISeq args)
+ {
+ while (args != null)
+ {
+ if (key == args.first())
+ return args.rest();
+ args = RT.rest(args);
+ args = RT.rest(args);
+ }
+ return null;
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/RestFnImpl.cs b/ClojureCLR/Clojure/Clojure/Lib/RestFnImpl.cs new file mode 100644 index 00000000..7c98230a --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/RestFnImpl.cs @@ -0,0 +1,551 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+
+ // The problem here is that I need the functionality of both RestFn and AfnImpl.
+ // Because they are both classes, we can't derive from both.
+ // For the time being, I choose to inherit from RestFn and re-implement the AFnImpl code.
+ // Eventually, we need to do overloading to solve this problem.
+ // Overloading is not possible at the moment do to a bug in LambdaExpression.CompileToMethod
+
+ public class RestFnImpl : RestFn, Fn
+ // Per Java Rev 1122, need to make all true functions implement this marker interface,
+ // In java version this is done by making the per-funcion generated class implement the interface.
+ // We can do that, too, once we start generating per-function classes.
+ // And then it goes away again in Rev 1161. Sigh. (RestFn implements AFunction, which apparently suffices.)
+ {
+
+ public FFunc<
+ object> _fn0;
+
+ public FFunc<
+ object,
+ object> _fn1;
+
+ public FFunc<
+ object, object,
+ object> _fn2;
+
+ public FFunc<
+ object, object, object,
+ object> _fn3;
+
+ public FFunc<
+ object, object, object, object,
+ object> _fn4;
+
+ public FFunc<
+ object, object, object, object, object,
+ object> _fn5;
+
+ public FFunc<
+ object, object, object, object, object,
+ object,
+ object> _fn6;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object,
+ object> _fn7;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object,
+ object> _fn8;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object,
+ object> _fn9;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object> _fn10;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object,
+ object> _fn11;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object,
+ object> _fn12;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object,
+ object> _fn13;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object,
+ object> _fn14;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object> _fn15;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object,
+ object> _fn16;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object,
+ object> _fn17;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object,
+ object> _fn18;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object,
+ object> _fn19;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object> _fn20;
+
+ public VFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object> _fnRest;
+
+
+ public FFunc<
+ object, object> _fnDo0;
+
+ public FFunc<
+ object,
+ object, object> _fnDo1;
+
+ public FFunc<
+ object, object,
+ object, object> _fnDo2;
+
+ public FFunc<
+ object, object, object,
+ object, object> _fnDo3;
+
+ public FFunc<
+ object, object, object, object,
+ object, object> _fnDo4;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object> _fnDo5;
+
+ public FFunc<
+ object, object, object, object, object,
+ object,
+ object, object> _fnDo6;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object,
+ object, object> _fnDo7;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object,
+ object, object> _fnDo8;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object,
+ object, object> _fnDo9;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object> _fnDo10;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object,
+ object, object> _fnDo11;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object,
+ object, object> _fnDo12;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object,
+ object, object> _fnDo13;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object,
+ object, object> _fnDo14;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object> _fnDo15;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object,
+ object, object> _fnDo16;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object,
+ object, object> _fnDo17;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object,
+ object, object> _fnDo18;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object,
+ object, object> _fnDo19;
+
+ public FFunc<
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object, object, object, object,
+ object, object> _fnDo20;
+
+ public RestFnImpl(int reqArity)
+ : base(reqArity)
+ {
+ }
+
+
+
+
+ protected override object doInvoke(object args)
+ {
+ if (_fnDo0 == null) throw WrongArityException();
+ return _fnDo0(args);
+ }
+ protected override object doInvoke(object arg1, object args)
+ {
+ if (_fnDo1 == null) throw WrongArityException();
+ return _fnDo1(arg1, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object args)
+ {
+ if (_fnDo2 == null) throw WrongArityException();
+ return _fnDo2(arg1, arg2, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object args)
+ {
+ if (_fnDo3 == null) throw WrongArityException();
+ return _fnDo3(arg1, arg2, arg3, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object arg4, object args)
+ {
+ if (_fnDo4 == null) throw WrongArityException();
+ return _fnDo4(arg1, arg2, arg3, arg4, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object args)
+ {
+ if (_fnDo5 == null) throw WrongArityException();
+ return _fnDo5(arg1, arg2, arg3, arg4, arg5, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object args)
+ {
+ if (_fnDo6 == null) throw WrongArityException();
+ return _fnDo6(arg1, arg2, arg3, arg4, arg5, arg6, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object args)
+ {
+ if (_fnDo7 == null) throw WrongArityException();
+ return _fnDo7(arg1, arg2, arg3, arg4, arg5, arg6, arg7, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object args)
+ {
+ if (_fnDo8 == null) throw WrongArityException();
+ return _fnDo8(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object args)
+ {
+ if (_fnDo9 == null) throw WrongArityException();
+ return _fnDo9(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object args)
+ {
+ if (_fnDo10 == null) throw WrongArityException();
+ return _fnDo10(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object args)
+ {
+ if (_fnDo11 == null) throw WrongArityException();
+ return _fnDo11(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object args)
+ {
+ if (_fnDo12 == null) throw WrongArityException();
+ return _fnDo12(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object args)
+ {
+ if (_fnDo13 == null) throw WrongArityException();
+ return _fnDo13(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object args)
+ {
+ if (_fnDo14 == null) throw WrongArityException();
+ return _fnDo14(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object args)
+ {
+ if (_fnDo15 == null) throw WrongArityException();
+ return _fnDo15(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object arg16, object args)
+ {
+ if (_fnDo16 == null) throw WrongArityException();
+ return _fnDo16(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object arg16, object arg17, object args)
+ {
+ if (_fnDo17 == null) throw WrongArityException();
+ return _fnDo17(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object arg16, object arg17, object arg18, object args)
+ {
+ if (_fnDo18 == null) throw WrongArityException();
+ return _fnDo18(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object arg16, object arg17, object arg18, object arg19, object args)
+ {
+ if (_fnDo19 == null) throw WrongArityException();
+ return _fnDo19(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, args);
+ }
+
+ protected override object doInvoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object arg16, object arg17, object arg18, object arg19, object arg20, object args)
+ {
+ if (_fnDo20 == null) throw WrongArityException();
+ return _fnDo20(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, args);
+ }
+
+ public override object invoke()
+ {
+ return (_fn0 == null)
+ ? base.invoke()
+ : _fn0();
+ }
+ public override object invoke(object arg1)
+ {
+ return (_fn1 == null)
+ ? base.invoke(arg1)
+ : _fn1(arg1);
+ }
+
+ public override object invoke(object arg1, object arg2)
+ {
+ return (_fn2 == null)
+ ? base.invoke(arg1, arg2)
+ : _fn2(arg1, arg2);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3)
+ {
+ return (_fn3 == null)
+ ? base.invoke(arg1, arg2, arg3)
+ : _fn3(arg1, arg2, arg3);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4)
+ {
+ return (_fn4 == null)
+ ? base.invoke(arg1, arg2, arg3, arg4)
+ : _fn4(arg1, arg2, arg3, arg4);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5)
+ {
+ return (_fn5 == null)
+ ? base.invoke(arg1, arg2, arg3, arg4, arg5)
+ : _fn5(arg1, arg2, arg3, arg4, arg5);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6)
+ {
+ return (_fn6 == null)
+ ? base.invoke(arg1, arg2, arg3, arg4, arg5, arg6)
+ : _fn6(arg1, arg2, arg3, arg4, arg5, arg6);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7)
+ {
+ return (_fn7 == null)
+ ? base.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7)
+ : _fn7(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8)
+ {
+ return (_fn8 == null)
+ ? base.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
+ : _fn8(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9)
+ {
+ return (_fn9 == null)
+ ? base.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
+ : _fn9(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10)
+ {
+ return (_fn10 == null)
+ ? base.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
+ : _fn10(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11)
+ {
+ return (_fn11 == null)
+ ? base.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11)
+ : _fn11(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12)
+ {
+ return (_fn12 == null)
+ ? base.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12)
+ : _fn12(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13)
+ {
+ return (_fn13 == null)
+ ? base.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13)
+ : _fn13(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14)
+ {
+ return (_fn14 == null)
+ ? base.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14)
+ : _fn14(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15)
+ {
+ return (_fn15 == null)
+ ? base.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15)
+ : _fn15(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object arg16)
+ {
+ return (_fn16 == null)
+ ? base.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16)
+ : _fn16(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object arg16, object arg17)
+ {
+ return (_fn17 == null)
+ ? base.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17)
+ : _fn17(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object arg16, object arg17, object arg18)
+ {
+ return (_fn18 == null)
+ ? base.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18)
+ : _fn18(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object arg16, object arg17, object arg18, object arg19)
+ {
+ return (_fn19 == null)
+ ? base.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19)
+ : _fn19(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19);
+ }
+
+ public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object arg16, object arg17, object arg18, object arg19, object arg20)
+ {
+ return (_fn20 == null)
+ ? base.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20)
+ : _fn20(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20);
+ }
+
+ //public override object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15, object arg16, object arg17, object arg18, object arg19, object arg20, params object[] args)
+ //{
+ // return (_fnRest == null) ? base.invoke()
+ // : _fnRest(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, args);
+ //}
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Reversible.cs b/ClojureCLR/Clojure/Clojure/Lib/Reversible.cs new file mode 100644 index 00000000..944a7c88 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Reversible.cs @@ -0,0 +1,29 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents a sequence that can be traversed in reverse.
+ /// </summary>
+ public interface Reversible
+ {
+ /// <summary>
+ /// Gets an <see cref="ISeq">ISeq</see> to travers the sequence in reverse.
+ /// </summary>
+ /// <returns>An <see cref="ISeq">ISeq</see> .</returns>
+ ISeq rseq();
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/SeqEnumerator.cs b/ClojureCLR/Clojure/Clojure/Lib/SeqEnumerator.cs new file mode 100644 index 00000000..66067134 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/SeqEnumerator.cs @@ -0,0 +1,110 @@ +/**
+ * 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.Collections;
+
+namespace clojure.lang
+{
+
+
+ /// <summary>
+ /// Implements standard IEnumerator behavior over an <see cref="ISeq">ISeq</see>.
+ /// </summary>
+ /// <remarks>Equivalent to Java verion: SeqIterator</remarks>
+ public sealed class SeqEnumerator : IEnumerator
+ {
+ #region Data
+
+ /// <summary>
+ /// <value>true</value> if we have reached the end of the sequence.
+ /// </summary>
+ bool _isAtEnd;
+
+ /// <summary>
+ /// Current position in the sequence.
+ /// </summary>
+ private ISeq _seq;
+
+ /// <summary>
+ /// The original sequence (for resetting).
+ /// </summary>
+ private readonly ISeq _origSeq;
+
+ #endregion
+
+ #region C-tors
+
+ /// <summary>
+ /// Construct one from a given sequence.
+ /// </summary>
+ /// <param name="seq">The underlying sequence.</param>
+ public SeqEnumerator(ISeq seq)
+ {
+ _origSeq = seq;
+ _isAtEnd = _origSeq == null;
+ _seq = null;
+ }
+
+ #endregion
+
+ #region IEnumerator Members
+
+ /// <summary>
+ /// The current item.
+ /// </summary>
+ public object Current
+ {
+ get
+ {
+ if (_isAtEnd || _seq == null)
+ throw new InvalidOperationException("No current value.");
+
+ return _seq.first();
+ }
+ }
+
+ /// <summary>
+ /// Move to the next item.
+ /// </summary>
+ /// <returns><value>true</value> if there is a next item;
+ /// <value>false</value> if the sequence is already at the end.</returns>
+ public bool MoveNext()
+ {
+ if (_isAtEnd || _origSeq == null)
+ return false;
+
+ if (_seq == null)
+ _seq = _origSeq;
+ else
+ {
+ _seq = _seq.rest();
+ if (_seq == null)
+ _isAtEnd = true;
+ }
+ return !_isAtEnd;
+ }
+
+ public void Reset()
+ {
+ _isAtEnd = _origSeq == null;
+ _seq = null;
+ }
+
+ #endregion
+ }
+}
+
+
+
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Sequential.cs b/ClojureCLR/Clojure/Clojure/Lib/Sequential.cs new file mode 100644 index 00000000..0f6ec7f3 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Sequential.cs @@ -0,0 +1,25 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Marks the object as being a sequential collection.
+ /// </summary>
+ public interface Sequential
+ {
+ //empty
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Settable.cs b/ClojureCLR/Clojure/Clojure/Lib/Settable.cs new file mode 100644 index 00000000..862a73fc --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Settable.cs @@ -0,0 +1,38 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents an object with a value that can be set.
+ /// </summary>
+ public interface Settable
+ {
+ /// <summary>
+ /// Sets the value.
+ /// </summary>
+ /// <param name="val">The new value</param>
+ /// <returns>The new value.</returns>
+ /// <remarks>Can only be called in a transaction or with a binding on the stack, else throws an exception.</remarks>
+ object doSet(object val);
+
+ /// <summary>
+ /// Sets the root value.
+ /// </summary>
+ /// <param name="val">The new value</param>
+ /// <returns>The new value.</returns>
+ object doReset(object val);
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Sorted.cs b/ClojureCLR/Clojure/Clojure/Lib/Sorted.cs new file mode 100644 index 00000000..026e66ff --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Sorted.cs @@ -0,0 +1,59 @@ +/**
+ * 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.Collections;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents an immutable collection that is sorted.
+ /// </summary>
+ /// <remarks>
+ /// <para>Lowercase-named methods for compatibility with the JVM implementation.</para>
+ /// </remarks>
+ public interface Sorted
+ {
+ /// <summary>
+ /// Returns the comparer used to sort the elements in the collection.
+ /// </summary>
+ /// <returns>The <c>IComparer</c> used to sort the items.</returns>
+ /// <remarks>Would be called <c>Comparer</c> except we need to match the JVM name.</remarks>
+ IComparer comparator();
+
+ /// <summary>
+ /// Returns the key to be passed to the comparator to sort the element.
+ /// </summary>
+ /// <param name="entry">An element in the collection.</param>
+ /// <returns>The key used to sort the element.</returns>
+ object entryKey(object entry);
+
+ /// <summary>
+ /// Returns an <see cref="ISeq">ISeq</see> to iterate through the collection in the designated direction.
+ /// </summary>
+ /// <param name="ascending">A flag indicating if the iteration is ascending or descending.</param>
+ /// <returns>A sequence for first/rest iteration.</returns>
+ ISeq seq(bool ascending);
+
+ /// <summary>
+ /// Returns an <see cref="ISeq">ISeq</see> to iterate through the collection in the designated direction starting from a particular key.
+ /// </summary>
+ /// <param name="key">The key at which to start the iteration.</param>
+ /// <param name="ascending">A flag indicating if the iteration is ascending or descending.</param>
+ /// <returns>A sequence for first/rest iteration.</returns>
+ /// <remarks>The key need not be in the collection. If not present, the iteration will start with
+ /// the first element with a key greater than (if asscending) or less than (if descending) the given key.</remarks>
+ ISeq seqFrom(object key, bool ascending);
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/StreamSeq.cs b/ClojureCLR/Clojure/Clojure/Lib/StreamSeq.cs new file mode 100644 index 00000000..535e63d6 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/StreamSeq.cs @@ -0,0 +1,136 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+
+ /// <summary>
+ /// Implements a persistent sequence over a stream.
+ /// </summary>
+ /// <remarks>Caches the stream values as retrieved.</remarks>
+ public class StreamSeq : ASeq
+ {
+ #region Data
+
+ /// <summary>
+ /// The stream whose values are being sequenced.
+ /// </summary>
+ IStream _stream;
+
+ /// <summary>
+ /// The first item in the stream.
+ /// </summary>
+ readonly object _first;
+
+ /// <summary>
+ /// The rest of the stream.
+ /// </summary>
+ ISeq _rest;
+
+ #endregion
+
+ #region Ctors and factory methods
+
+ /// <summary>
+ /// Create a sequence from a stream.
+ /// </summary>
+ /// <param name="stream">The stream to sequence.</param>
+ /// <returns>The persistent sequence.</returns>
+ /// <remarks>Requires looking at the first element of the stream.
+ /// This is so that we know if the stream has any elements.
+ /// This is so because the sequence must have at least element or be null.</remarks>
+ public static StreamSeq create(IStream stream)
+ {
+ object x = stream.next();
+ return (RT.isEOS(x))
+ ? null
+ : new StreamSeq(x, stream);
+ }
+
+ /// <summary>
+ /// Construct a <see cref="StreamSeq">StreamSeq</see> from metadata and first/rest.
+ /// </summary>
+ /// <param name="meta">The metadata to attach</param>
+ /// <param name="first">The first item.</param>
+ /// <param name="rest">The rest of the sequence.</param>
+ StreamSeq(IPersistentMap meta, Object first, ISeq rest)
+ : base(meta)
+ {
+ _first = first;
+ _rest = rest;
+ _stream = null;
+ }
+
+ /// <summary>
+ /// Construct a <see cref="StreamSeq">StreamSeq</see> from a first item and a stream.
+ /// </summary>
+ /// <param name="first">The first item.</param>
+ /// <param name="stream">The stream of remaining items.</param>
+ StreamSeq(object first, IStream stream)
+ {
+ _first = first;
+ _stream = stream;
+ }
+
+ #endregion
+
+ #region IObj members
+
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ if (meta != _meta)
+ {
+ rest();
+ return new StreamSeq(meta, _first, _rest);
+ }
+ return this;
+ }
+
+ #endregion
+
+ #region ISeq members
+
+ /// <summary>
+ /// Gets the first item.
+ /// </summary>
+ /// <returns>The first item.</returns>
+ public override object first()
+ {
+ return _first;
+ }
+
+ /// <summary>
+ /// Gets the rest of the sequence.
+ /// </summary>
+ /// <returns>The rest of the sequence, or <c>null</c> if no more elements.</returns>
+ public override ISeq rest()
+ {
+ if (_stream != null)
+ {
+ _rest = create(_stream);
+ _stream = null;
+ }
+ return _rest;
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Streamable.cs b/ClojureCLR/Clojure/Clojure/Lib/Streamable.cs new file mode 100644 index 00000000..c519246b --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Streamable.cs @@ -0,0 +1,29 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents an object that has an <see cref="IStream">IStream</see>.
+ /// </summary>
+ public interface Streamable
+ {
+ /// <summary>
+ /// Gets an <see cref="IStream">IStream/see> for this object.
+ /// </summary>
+ /// <returns>The <see cref="IStream">IStream/see>.</returns>
+ IStream stream();
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/StringSeq.cs b/ClojureCLR/Clojure/Clojure/Lib/StringSeq.cs new file mode 100644 index 00000000..358d5dca --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/StringSeq.cs @@ -0,0 +1,138 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// A sequence of characters from a string.
+ /// </summary>
+ public class StringSeq: ASeq, IndexedSeq
+ {
+ #region Data
+
+ /// <summary>
+ /// The string providing the characters.
+ /// </summary>
+ private readonly string _s;
+
+ /// <summary>
+ /// Current position in the string.
+ /// </summary>
+ private readonly int _i;
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ /// <summary>
+ /// Create a <see cref="StringSeq">StringSeq</see> from a String.
+ /// </summary>
+ /// <param name="s"></param>
+ /// <returns></returns>
+ static public StringSeq create(string s)
+ {
+ return s.Length == 0
+ ? null
+ : new StringSeq(null, s, 0);
+ }
+
+ /// <summary>
+ /// Construct a <see cref="StringSeq">StringSeq</see> from given metadata, string, position.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ /// <param name="s">The string.</param>
+ /// <param name="i">The current position.</param>
+ StringSeq(IPersistentMap meta, string s, int i)
+ : base(meta)
+ {
+ this._s = s;
+ this._i = i;
+ }
+
+ #endregion
+
+ #region ISeq members
+
+ /// <summary>
+ /// Gets the first item.
+ /// </summary>
+ /// <returns>The first item.</returns>
+ public override object first()
+ {
+ return _s[_i];
+ }
+
+ /// <summary>
+ /// Gets the rest of the sequence.
+ /// </summary>
+ /// <returns>The rest of the sequence, or <c>null</c> if no more elements.</returns>
+ public override ISeq rest()
+ {
+ return _i + 1 < _s.Length
+ ? new StringSeq(_meta, _s, _i + 1)
+ : null;
+ }
+
+ #endregion
+
+ #region IPersistentCollection members
+
+ // The Java version does not define this.
+ // Defaults to ASeq's iteration method.
+ // Seems wasteful.
+ /// <summary>
+ /// Gets the number of items in the collection.
+ /// </summary>
+ /// <returns>The number of items in the collection.</returns>
+ /// <remarks>The Java version does not define this.
+ /// Defaults to <see cref="ASeq">ASeq</see>'s iteration method.
+ /// Seems wasteful</remarks>
+ public override int count()
+ {
+ return _i < _s.Length ? _s.Length - _i : 0;
+ }
+
+ #endregion
+
+ #region IObj methods
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return meta == _meta
+ ? this
+ : new StringSeq(meta, _s, _i);
+ }
+
+ #endregion
+
+ #region IndexedSeq Members
+
+ /// <summary>
+ /// Gets the index associated with this sequence.
+ /// </summary>
+ /// <returns>The index associated with this sequence.</returns>
+ public int index()
+ {
+ return _i;
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Symbol.cs b/ClojureCLR/Clojure/Clojure/Lib/Symbol.cs new file mode 100644 index 00000000..4adbd225 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Symbol.cs @@ -0,0 +1,295 @@ +/**
+ * 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;
+
+namespace clojure.lang
+{
+ /// <summary>
+ /// Represents a symbol.
+ /// </summary>
+ /// <remarks>See the Clojure documentation for more information.</remarks>
+ [Serializable]
+ public class Symbol: AFn, Named, IComparable
+ {
+ #region Instance variables
+
+ /// <summary>
+ /// The name of the namespace for this symbol (if namespace-qualified).
+ /// </summary>
+ /// <remarks>This string must be interned.</remarks>
+ protected readonly string _ns;
+
+ /// <summary>
+ /// The name of the symbol.
+ /// </summary>
+ /// <remarks>This string must be interned.</remarks>
+ protected readonly string _name;
+
+ /// <summary>
+ /// The cached hashcode.
+ /// </summary>
+ protected readonly int _hash;
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ /// <summary>
+ /// Intern a symbol with the given name and namespace-name.
+ /// </summary>
+ /// <param name="ns">The name of the namespace.</param>
+ /// <param name="name">The name of the symbol.</param>
+ /// <returns>A new symbol.</returns>
+ /// <remarks>
+ /// Interning here does not imply uniquifying.
+ /// The strings for the namespace-name and the symbol-name are uniquified.
+ /// </remarks>
+ static public Symbol intern(string ns, string name)
+ {
+ return new Symbol(ns == null ? null : String.Intern(ns), String.Intern(name));
+ }
+
+ /// <summary>
+ /// Intern a symbol with the given name (extracting the namespace if name is of the form ns/name).
+ /// </summary>
+ /// <param name="nsname">The (possibly qualified) name</param>
+ /// <returns>A new symbol.</returns>
+ static public Symbol intern(string nsname)
+ {
+ int i = nsname.LastIndexOf('/');
+ return i == -1
+ ? new Symbol(null, String.Intern(nsname))
+ : new Symbol(String.Intern(nsname.Substring(0, i)),
+ String.Intern(nsname.Substring(i + 1)));
+ }
+
+ /// <summary>
+ /// Create a symbol, no namespace, from an interned string.
+ /// </summary>
+ /// <param name="name_interned">The name (interned string)</param>
+ /// <returns></returns>
+ static public Symbol create(string name_interned)
+ {
+ return new Symbol(null, name_interned);
+ }
+
+ /// <summary>
+ /// Create a symbol with given namespace name and name, both interned.
+ /// </summary>
+ /// <param name="ns_interned">Interned namespace name.</param>
+ /// <param name="name_interned">Interned symbol name.</param>
+ /// <returns></returns>
+ static public Symbol create(string ns_interned, string name_interned)
+ {
+ return new Symbol(ns_interned, name_interned);
+ }
+
+ /// <summary>
+ /// Construct a symbol from interned namespace name and symbol name.
+ /// </summary>
+ /// <param name="ns_interned">The (interned) namespace name.</param>
+ /// <param name="name_interned">The (interned) symbol name.</param>
+ private Symbol(string ns_interned, string name_interned)
+ : base()
+ {
+ this._name = name_interned;
+ this._ns = ns_interned;
+ this._hash = ComputeHashCode();
+ }
+
+ /// <summary>
+ /// Construct a symbol from interned namespace name and symbol name, with given metadata.
+ /// </summary>
+ /// <param name="meta">The metadata to attach.</param>
+ /// <param name="ns_interned">The (interned) namespace name.</param>
+ /// <param name="name_interned">The (interned) symbol name.</param>
+ private Symbol(IPersistentMap meta, string ns_interned, string name_interned)
+ : base(meta)
+ {
+ this._name = name_interned;
+ this._ns = ns_interned;
+ this._hash = ComputeHashCode();
+ }
+
+ /// <summary>
+ /// Compute the hash code for the symbol.
+ /// </summary>
+ /// <returns>The hash code.</returns>
+ private int ComputeHashCode()
+ {
+ return Util.HashCombine(_name.GetHashCode(), Util.Hash(_ns));
+ }
+
+
+ #endregion
+
+ #region Object overrides
+
+ /// <summary>
+ /// Return a string representing the symbol.
+ /// </summary>
+ /// <returns>A string representing the symbol.</returns>
+ public override string ToString()
+ {
+ return _ns == null ? _name : _ns + "/" + _name;
+ }
+
+ /// <summary>
+ /// Determine if an object is equal to this symbol.
+ /// </summary>
+ /// <param name="obj">The object to compare to.</param>
+ /// <returns><value>true</value> if they are the same;<value>false</value> otherwise.</returns>
+ /// <remarks>Uses value semantics, value determined by namespace name and symbol name.</remarks>
+ public override bool Equals(object obj)
+ {
+ if (this == obj)
+ return true;
+
+ Symbol sym = obj as Symbol;
+
+ if (sym == null)
+ return false;
+
+ // interned strings, use identity compare
+ return (Object.ReferenceEquals(Name,sym.Name) && (Object.ReferenceEquals(Namespace,sym.Namespace)));
+ }
+
+ /// <summary>
+ /// Get the hash code.
+ /// </summary>
+ /// <returns>The hash code.</returns>
+ public override int GetHashCode()
+ {
+ return _hash;
+ }
+
+ #endregion
+
+ #region IObj members
+
+ /// <summary>
+ /// Create a copy with new metadata.
+ /// </summary>
+ /// <param name="meta">The new metadata.</param>
+ /// <returns>A copy of the object with new metadata attached.</returns>
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ // Java did not do identity test.
+ return meta == _meta
+ ? this
+ : new Symbol(meta, _ns, _name);
+ }
+
+ #endregion
+
+ #region Named members
+
+ // I prefer to use these internally.
+
+ /// <summary>
+ /// Get the namespace name.
+ /// </summary>
+ public string Namespace
+ {
+ get { return _ns; }
+ }
+
+ /// <summary>
+ /// Get the symbol name.
+ /// </summary>
+ public string Name
+ {
+ get { return _name; }
+ }
+
+ // the following are in the interface
+
+ /// <summary>
+ /// Get the namespace name.
+ /// </summary>
+ /// <returns>The namespace name.</returns>
+ public string getNamespace()
+ {
+ return _ns;
+ }
+
+ /// <summary>
+ /// Gets the symbol name.
+ /// </summary>
+ /// <returns>The symbol name.</returns>
+ public string getName()
+ {
+ return _name;
+ }
+
+ #endregion
+
+ #region IFn members
+
+
+ public override object invoke(Object obj)
+ {
+ return RT.get(obj, this);
+ }
+
+ public override object invoke(Object obj, Object notFound)
+ {
+ return RT.get(obj, this, notFound);
+ }
+
+ #endregion
+
+ #region IComparable Members
+
+ /// <summary>
+ /// Compare this symbol to another object.
+ /// </summary>
+ /// <param name="obj">The object to comapre to.</param>
+ /// <returns>neg,zero,pos semantics.</returns>
+ public int CompareTo(object obj)
+ {
+ Symbol s = obj as Symbol;
+ if (s == null)
+ throw new InvalidOperationException("Can't compare to null.");
+ if (Equals(s))
+ return 0;
+ if (_ns == null && s._ns != null)
+ return -1;
+ if (_ns != null)
+ {
+ if (s._ns == null)
+ return 1;
+ int nsc = _ns.CompareTo(s._ns);
+ if (nsc != 0)
+ return nsc;
+ }
+ return _name.CompareTo(s._name);
+ }
+
+ #endregion
+
+ #region Other
+
+ ///// <summary>
+ ///// Create a copy of this symbol.
+ ///// </summary>
+ ///// <returns>A copy of this symbol.</returns>
+ //private object readResolve()
+ //{
+ // return intern(_ns, _name);
+ //}
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Util.cs b/ClojureCLR/Clojure/Clojure/Lib/Util.cs new file mode 100644 index 00000000..3aae7c53 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Util.cs @@ -0,0 +1,185 @@ +/**
+ * 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 java.math;
+
+namespace clojure.lang
+{
+ public class Util
+ {
+
+ static public int Hash(object o)
+ {
+ return o == null ? 0 : o.GetHashCode();
+ }
+
+ static public int HashCombine(int seed, int hash)
+ {
+ //a la boost
+ return (int)(seed ^ (hash + 0x9e3779b9 + (seed << 6) + (seed >> 2)));
+
+ }
+
+
+ static public bool equiv(object k1, object k2)
+ {
+ if (k1 == k2)
+ return true;
+ if (k1 != null)
+ {
+ if (IsNumeric(k1))
+ return Numbers.equiv(k1, k2);
+ else if (k1 is IPersistentCollection && k2 is IPersistentCollection)
+ return ((IPersistentCollection)k1).equiv(k2);
+ return k1.Equals(k2);
+ }
+ return false;
+ }
+
+ public static bool equals(object k1, object k2)
+ {
+ // Changed in Rev 1215
+ //if(k1 == k2)
+ // return true;
+
+ //if(k1 != null)
+ //{
+ // if (IsNumeric(k1))
+ // return Numbers.equiv(k1, k2);
+
+ // return k1.Equals(k2);
+ //}
+
+ //return false;
+ if (k1 == k2)
+ return true;
+ return k1 != null && k1.Equals(k2);
+ }
+
+ public static int compare(object k1, object k2)
+ {
+ if (k1 == k2)
+ return 0;
+ if (k1 != null)
+ {
+ if (k2 == null)
+ return 1;
+ if (IsNumeric(k1))
+ return Numbers.compare(k1, k2);
+ return ((IComparable)k1).CompareTo(k2);
+ }
+ return -1;
+ }
+
+
+ public static int ConvertToInt(object o)
+ {
+ // ToInt32 rounds. We need truncation.
+ return (int)Convert.ToDouble(o);
+ }
+
+ public static bool IsNumeric(object o)
+ {
+ return o != null && IsNumeric(o.GetType());
+ }
+
+
+ public static int BitCount(int bitMask)
+ {
+ bitMask -= ((bitMask >> 1) & 0x55555555);
+ bitMask = (((bitMask >> 2) & 0x33333333) + (bitMask & 0x33333333));
+ bitMask = (((bitMask >> 4) + bitMask) & 0x0f0f0f0f);
+ return ((bitMask * 0x01010101) >> 24);
+ }
+
+ public static int Mask(int hash, int shift)
+ {
+ return (hash >> shift) & 0x01f;
+ }
+
+ #region core.clj compatibility
+
+ public static int hash(object o)
+ {
+ return Hash(o);
+ }
+
+ #endregion
+
+ #region Stolen code
+ // The following code is from Microsoft's DLR..
+ // It had the following notice:
+
+ /* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation.
+ *
+ * This source code is subject to terms and conditions of the Microsoft Public License. A
+ * copy of the license can be found in the License.html file at the root of this distribution. If
+ * you cannot locate the Microsoft Public License, please send an email to
+ * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
+ * by the terms of the Microsoft Public License.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ *
+ * ***************************************************************************/
+
+
+
+ internal static Type GetNonNullableType(Type type)
+ {
+ if (IsNullableType(type))
+ {
+ return type.GetGenericArguments()[0];
+ }
+ return type;
+ }
+
+ internal static bool IsNullableType(Type type)
+ {
+ return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
+ }
+
+
+ internal static bool IsNumeric(Type type)
+ {
+ type = GetNonNullableType(type);
+ if (!type.IsEnum)
+ {
+ switch (Type.GetTypeCode(type))
+ {
+ case TypeCode.Char:
+ case TypeCode.SByte:
+ case TypeCode.Byte:
+ case TypeCode.Int16:
+ case TypeCode.Int32:
+ case TypeCode.Int64:
+ case TypeCode.Double:
+ case TypeCode.Single:
+ case TypeCode.UInt16:
+ case TypeCode.UInt32:
+ case TypeCode.UInt64:
+ return true;
+ }
+ if (type == typeof(BigInteger) || type == typeof(BigDecimal))
+ return true;
+ }
+ return false;
+ }
+
+ #endregion
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Var.cs b/ClojureCLR/Clojure/Clojure/Lib/Var.cs new file mode 100644 index 00000000..35a5bb16 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Var.cs @@ -0,0 +1,839 @@ +/**
+ * 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.Threading;
+using System.Runtime.CompilerServices;
+
+namespace clojure.lang
+{
+
+ /// <summary>
+ /// Represents a Var.
+ /// </summary>
+ /// <remarks>
+ /// <para>From the Clojure documentation:</para>
+ /// <blockquote>"Vars provide a mechanism to refer to a mutable storage location
+ /// that can be dynamically rebound (to a new storage location) on a per-thread basis.
+ /// Every Var can (but needn't) have a root binding,
+ /// which is a binding that is shared by all threads that do not have a per-thread binding."</blockquote>
+ /// </remarks>
+ public sealed class Var : ARef, IFn, IRef, Settable
+ {
+ /// <summary>
+ /// Represents a set of Var bindings established at a particular point in the call stack.
+ /// </summary>
+ class Frame
+ {
+ #region Data
+
+ /// <summary>
+ /// A mapping from <see cref="Var">Var</see>s to <see cref="Box">Box</see>es holding their values.
+ /// </summary>
+ readonly Associative _bindings;
+
+ /// <summary>
+ /// Get mapping from <see cref="Var">Var</see>s to <see cref="Box">Box</see>es holding their values.
+ /// </summary>
+ public Associative Bindings
+ {
+ get { return _bindings; }
+ }
+
+ /// <summary>
+ /// The bindings of this frame only.
+ /// </summary>
+ /// <remarks>Used only to know which Vars to decrement the counts of
+ /// during an unwind/pop, i.e., only the keys matter.</remarks>
+ readonly Associative _frameBindings;
+
+ /// <summary>
+ /// Get the bindings of this frame only.
+ /// </summary>
+ public Associative FrameBindings
+ {
+ get { return _frameBindings; }
+ }
+
+ /// <summary>
+ /// The previous <see cref="Frame">Frame</see> on the stack.
+ /// </summary>
+ readonly Frame _prev;
+
+ /// <summary>
+ /// Get the previous <see cref="Frame">Frame</see> on the stack.
+ /// </summary>
+ public Frame Prev
+ {
+ get { return _prev; }
+ }
+
+ #endregion
+
+ #region Ctors
+
+ /// <summary>
+ /// Construct an empty frame.
+ /// </summary>
+ public Frame()
+ : this(PersistentHashMap.EMPTY, PersistentHashMap.EMPTY, null)
+ {
+ }
+
+ /// <summary>
+ /// Construct a frame on the stack.
+ /// </summary>
+ /// <param name="frameBindings">The bindings for this frame only.</param>
+ /// <param name="bindings">Bindings all the way down the stack.</param>
+ /// <param name="prev">The previous frame.</param>
+ public Frame(Associative frameBindings, Associative bindings, Frame prev)
+ {
+ _frameBindings = frameBindings;
+ _bindings = bindings;
+ _prev = prev;
+ }
+
+ #endregion
+ }
+
+ #region Data
+
+ /// <summary>
+ /// The current frame. Thread-local.
+ /// </summary>
+ [ThreadStatic]
+ private static Frame _currentFrame;
+
+ /// <summary>
+ /// Get/set the current frame.
+ /// </summary>
+ /// <remarks>Best to make all access to _currentFrame through this accessor.</remarks>
+ private static Frame CurrentFrame
+ {
+ get
+ {
+ if (_currentFrame == null)
+ _currentFrame = new Frame();
+ return _currentFrame;
+ }
+ set
+ {
+ _currentFrame = value;
+ }
+ }
+
+ /// <summary>
+ /// Special value for the root to indicate the root is unbound.
+ /// </summary>
+ static object _rootUnboundValue = new object();
+
+ /// <summary>
+ /// The root value.
+ /// </summary>
+ volatile object _root;
+
+ static Keyword _privateKey = Keyword.intern(null, "private");
+ static IPersistentMap _privateMeta = new PersistentArrayMap(new object[] { _privateKey, true });
+ static Keyword _macroKey = Keyword.intern(null, "macro");
+ static Keyword _nameKey = Keyword.intern(null, "name");
+ static Keyword _nsKey = Keyword.intern(null, "ns");
+
+ /// <summary>
+ /// The number of bindings for this var on the binding stack.
+ /// </summary>
+ [NonSerialized]
+ AtomicInteger _count;
+
+ /// <summary>
+ /// The symbol naming this var, if named.
+ /// </summary>
+ readonly Symbol _sym;
+
+ /// <summary>
+ /// Get the symbol naming this var, if named.
+ /// </summary>
+ internal Symbol Symbol
+ {
+ get { return _sym; }
+ }
+
+ /// <summary>
+ /// The namespace holding this var.
+ /// </summary>
+ readonly Namespace _ns;
+
+ /// <summary>
+ /// Get the namespace holding this var.
+ /// </summary>
+ public Namespace Namespace
+ {
+ get { return _ns; }
+ }
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ /// <summary>
+ /// Intern a named var in a namespace, with given value.
+ /// </summary>
+ /// <param name="ns">The namespace.</param>
+ /// <param name="sym">The name.</param>
+ /// <param name="root">The root value.</param>
+ /// <returns>The var that was found or created.</returns>
+ public static Var intern(Namespace ns, Symbol sym, object root)
+ {
+ return intern(ns, sym, root, true);
+ }
+
+ /// <summary>
+ /// Intern a named var in a namespace, with given value (if has a root value already, then change only if replaceRoot is true).
+ /// </summary>
+ /// <param name="ns">The namespace.</param>
+ /// <param name="sym">The name.</param>
+ /// <param name="root">The root value.</param>
+ /// <param name="replaceRoot">Replace an existing root value if <value>true</value>.</param>
+ /// <returns>The var that was found or created.</returns>
+ public static Var intern(Namespace ns, Symbol sym, object root, bool replaceRoot)
+ {
+ Var dvout = ns.intern(sym);
+ if (!dvout.hasRoot() || replaceRoot)
+ dvout.BindRoot(root);
+ return dvout;
+ }
+
+ /// <summary>
+ /// Intern a named var in a namespace (creating the namespece if necessary).
+ /// </summary>
+ /// <param name="nsName">The name of the namespace. (A namespace with this name will be created if not existing already.)</param>
+ /// <param name="sym">The name of the var.</param>
+ /// <returns>The var that was found or created.</returns>
+ public static Var intern(Symbol nsName, Symbol sym)
+ {
+ Namespace ns = Namespace.findOrCreate(nsName);
+ return intern(ns, sym);
+ }
+
+ /// <summary>
+ /// Intern a named var (flagged private) in a namespace (creating the namespece if necessary).
+ /// </summary>
+ /// <param name="nsName">The name of the namespace. (A namespace with this name will be created if not existing already.)</param>
+ /// <param name="sym">The name of the var.</param>
+ /// <returns>The var that was found or created.</returns>
+ /// <remarks>Added in Java Rev 1110.</remarks>
+ public static Var internPrivate(string nsName, String sym)
+ {
+ Namespace ns = Namespace.findOrCreate(Symbol.intern(nsName));
+ Var ret = intern(ns, Symbol.intern(sym));
+ ret.SetMeta(_privateMeta);
+ return ret;
+ }
+
+ /// <summary>
+ /// Intern a named var in a namespace.
+ /// </summary>
+ /// <param name="ns">The namespace.</param>
+ /// <param name="sym">The name.</param>
+ /// <returns></returns>
+ public static Var intern(Namespace ns, Symbol sym)
+ {
+ return ns.intern(sym);
+ }
+
+ /// <summary>
+ /// Create an uninterned var.
+ /// </summary>
+ /// <returns>An uninterned var.</returns>
+ public static Var create()
+ {
+ return new Var(null, null);
+ }
+
+ /// <summary>
+ /// Create an uninterned var with a root value.
+ /// </summary>
+ /// <param name="root">The root value.</param>
+ /// <returns>An uninterned var.</returns>
+ public static Var create(object root)
+ {
+ return new Var(null, null, root);
+ }
+
+ /// <summary>
+ /// Construct a var in a given namespace with a given name.
+ /// </summary>
+ /// <param name="ns">The namespace.</param>
+ /// <param name="sym">The var.</param>
+ internal Var(Namespace ns, Symbol sym)
+ {
+ _ns = ns;
+ _sym = sym;
+ _count = new AtomicInteger();
+ _root = _rootUnboundValue;
+ SetMeta(PersistentHashMap.EMPTY);
+ }
+
+
+ /// <summary>
+ /// Construct a var in a given namespace with a given name and root value.
+ /// </summary>
+ /// <param name="ns">The namespace.</param>
+ /// <param name="sym">The var.</param>
+ /// <param name="root">The root value.</param>
+ Var(Namespace ns, Symbol sym, object root)
+ : this(ns, sym)
+ {
+ _root = root;
+ }
+
+ #endregion
+
+ #region object overrides
+
+ /// <summary>
+ /// Return a string representing this var.
+ /// </summary>
+ /// <returns>A string representing this var.</returns>
+ public override string ToString()
+ {
+ return (_ns != null)
+ ? "#'" + _ns.Name + "/" + _sym
+ : "#<Var: " + (_sym != null ? _sym.ToString() : "--unnamed--") + ">";
+ }
+
+ #endregion
+
+ #region Flag management
+
+ /// <summary>
+ /// Set the metadata attached to this var.
+ /// </summary>
+ /// <param name="m">The metadata to attach.</param>
+ /// <remarks>The metadata must contain entries for the namespace and name.</remarks>
+ public void SetMeta(IPersistentMap m)
+ {
+ // ensure these basis keys
+ resetMeta(m.assoc(_nameKey, _sym).assoc(_nsKey, _ns));
+ }
+
+ /// <summary>
+ /// Add a macro=true flag to the metadata.
+ /// </summary>
+ /// <remarks>Lowercase name for core.clj compatability.</remarks>
+ public void setMacro()
+ {
+ alterMeta(_assoc,RT.list(_macroKey, RT.T));
+ }
+
+ /// <summary>
+ /// Is the var a macro?
+ /// </summary>
+ public bool IsMacro
+ {
+ get { return RT.booleanCast(meta().valAt(_macroKey)); }
+ }
+
+ /// <summary>
+ /// Is the var public?
+ /// </summary>
+ public bool IsPublic
+ {
+ get { return !RT.booleanCast(meta().valAt(_privateKey)); }
+ }
+
+ /// <summary>
+ /// Get the tag on the var.
+ /// </summary>
+ /// <remarks>In Java code, setTag takes only Symbols. Don't know why. I ran into a problem when I changed the type to Symbol.</remarks>
+ public object Tag
+ {
+ get { return meta().valAt(RT.TAG_KEY); }
+ set { alterMeta(_assoc,RT.list(RT.TAG_KEY, value)); }
+ }
+
+ #endregion
+
+ #region Value management
+
+ /// <summary>
+ /// Does the var have value?
+ /// </summary>
+ public bool IsBound
+ {
+ get { return hasRoot() || (_count.get() > 0 && CurrentFrame.Bindings.containsKey(this)); }
+ }
+
+
+ /// <summary>
+ /// Does the var have a root value?
+ /// </summary>
+ /// <returns></returns>
+ /// <remarks>core.clj compatibility (initial lowercase/ public /method-instead-of-property)</remarks>
+ public bool hasRoot()
+ {
+ return _root != _rootUnboundValue;
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <returns>The root value.</returns>
+ object getRoot()
+ {
+ return _root;
+ }
+
+ // In the Java version, haven't missed it yet.
+ //public object alter(IFn fn, ISeq args)
+ //{
+ // set(fn.applyTo(RT.cons(deref(), args)));
+ // return this;
+ //}
+
+ /// <summary>
+ /// Set the value of the var.
+ /// </summary>
+ /// <param name="val">The new value.</param>
+ /// <returns>the new value.</returns>
+ /// <remarks>It is an error to set the root binding with this method.</remarks>
+ public object set(object val)
+ {
+ Validate(getValidator(), val);
+ Box b = GetThreadBinding();
+ if (b != null)
+ return (b.Val = val);
+ throw new InvalidOperationException(String.Format("Can't change/establish root binding of: {0} with set", _sym));
+ }
+
+ /// <summary>
+ /// Change the root value. (And clear the macro flag.)
+ /// </summary>
+ /// <param name="root">The new value.</param>
+ /// <remarks>binding root clears macro flag</remarks>
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public void BindRoot(object root)
+ {
+ Validate(getValidator(), root);
+ _root = root;
+ alterMeta(_assoc,RT.list(_macroKey, RT.F));
+ notifyWatches();
+ }
+
+
+ ///// <summary>
+ ///// Change the root value.
+ ///// </summary>
+ ///// <param name="root">The new value.</param>
+ //[MethodImpl(MethodImplOptions.Synchronized)]
+ //void SwapRoot(object root)
+ //{
+ // Validate(getValidator(), root);
+ // _root = root;
+ // notifyWatches();
+ //}
+
+ ///// <summary>
+ ///// Unbind the var's root value.
+ ///// </summary>
+ //[MethodImpl(MethodImplOptions.Synchronized)]
+ //void UnbindRoot()
+ //{
+ // _root = _rootUnboundValue;
+ //}
+
+ ///// <summary>
+ ///// Set var's root to a computed value.
+ ///// </summary>
+ ///// <param name="fn">The function to apply to the current value to get the new value.</param>
+ //[MethodImpl(MethodImplOptions.Synchronized)]
+ //void CommuteRoot(IFn fn)
+ //{
+ // object newRoot = fn.invoke(_root);
+ // Validate(getValidator(), newRoot);
+ // _root = newRoot;
+ // notifyWatches();
+ //}
+
+ /// <summary>
+ /// Change the var's root to a computed value (based on current value and supplied arguments).
+ /// </summary>
+ /// <param name="fn">The function to compute the new value.</param>
+ /// <param name="args">Additional arguments.</param>
+ /// <returns>The new value.</returns>
+ /// <remarks> initial lowercase in name needed for core.clj</remarks>
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public object alterRoot(IFn fn, ISeq args)
+ {
+ object newRoot = fn.applyTo(RT.cons(_root, args));
+ Validate(getValidator(), newRoot);
+ _root = newRoot;
+ notifyWatches();
+ return newRoot;
+ }
+
+
+ #endregion
+
+ #region Binding stack
+
+ /// <summary>
+ /// Push a new frame of bindings onto the binding stack.
+ /// </summary>
+ /// <param name="bindings">The new bindings.</param>
+ /// <remarks>Lowercase name for core.clj compatability.</remarks>
+ public static void pushThreadBindings(Associative bindings)
+ {
+ Frame f = CurrentFrame;
+ Associative bmap = f.Bindings;
+ for (ISeq bs = bindings.seq(); bs != null; bs = bs.rest())
+ {
+ IMapEntry e = (IMapEntry)bs.first();
+ Var v = (Var)e.key();
+ v.Validate(e.val());
+ v._count.incrementAndGet();
+ bmap = bmap.assoc(v, new Box(e.val()));
+ }
+ CurrentFrame = new Frame(bindings, bmap, f);
+ }
+
+ /// <summary>
+ /// Pop the topmost binding frame from the stack.
+ /// </summary>
+ /// <remarks>Lowercase name for core.clj compatability.</remarks>
+ public static void popThreadBindings()
+ {
+ Frame f = CurrentFrame;
+ if (f.Prev == null)
+ throw new InvalidOperationException("Pop without matching push");
+ for (ISeq bs = RT.keys(f.FrameBindings); bs != null; bs = bs.rest())
+ {
+ Var v = (Var)bs.first();
+ v._count.decrementAndGet();
+ }
+ CurrentFrame = f.Prev;
+ }
+
+
+ /// <summary>
+ /// Pop all binding frames from teh stack.
+ /// </summary>
+ static void ReleaseThreadBindings()
+ {
+ Frame f = CurrentFrame;
+ if (f.Prev == null)
+ throw new InvalidOperationException("Release without full unwind");
+ for (ISeq bs = RT.keys(f.Bindings); bs != null; bs = bs.rest())
+ {
+ Var v = (Var)bs.first();
+ v._count.decrementAndGet(); ;
+ }
+ CurrentFrame = null;
+ }
+
+ /// <summary>
+ /// Get the box of the current binding on the stack for this var, or null if no binding.
+ /// </summary>
+ /// <returns>The box of the current binding on the stack (or null if no binding).</returns>
+ Box GetThreadBinding()
+ {
+ if (_count.get() > 0)
+ {
+ IMapEntry e = CurrentFrame.Bindings.entryAt(this);
+ if (e != null)
+ return (Box)e.val();
+ }
+ return null;
+ }
+
+
+
+ #endregion
+
+ #region IFn Members
+
+ public IFn fn()
+ {
+ return (IFn)deref();
+ }
+
+
+ public object invoke()
+ {
+ return fn().invoke();
+ }
+
+ public object invoke(object arg1)
+ {
+ return fn().invoke(arg1);
+ }
+
+ public object invoke(object arg1, object arg2)
+ {
+ return fn().invoke(arg1, arg2);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3)
+ {
+ return fn().invoke(arg1, arg2, arg3);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
+ arg16);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
+ arg16, arg17);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17, object arg18)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
+ arg16, arg17, arg18);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17, object arg18, object arg19)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
+ arg16, arg17, arg18, arg19);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17, object arg18, object arg19, object arg20)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
+ arg16, arg17, arg18, arg19, arg20);
+ }
+
+ public object invoke(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7,
+ object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14,
+ object arg15, object arg16, object arg17, object arg18, object arg19, object arg20,
+ params object[] args)
+ {
+ return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
+ arg16, arg17, arg18, arg19, arg20, args);
+ }
+
+ public object applyTo(ISeq arglist)
+ {
+ return AFn.ApplyToHelper(this, arglist);
+ }
+
+ #endregion
+
+ #region IDeref Members
+
+ ///// <summary>
+ ///// Gets the (immutable) value the reference is holding.
+ ///// </summary>
+ ///// <returns>The value</returns>
+ ///// <remarks>When IDeref was added and get() was renamed to deref(), this was put in.
+ ///// Why? Perhaps to avoid having to change Var.get() references all over.
+ ///// I don't know.
+ ///// But then they rename all uses anyway.</remarks>
+ //public object get()
+ //{
+ // return deref();
+ //}
+
+ /// <summary>
+ /// Gets the (immutable) value the reference is holding.
+ /// </summary>
+ /// <returns>The value</returns>
+ public override object deref()
+ {
+ Box b = GetThreadBinding();
+ if (b != null)
+ return b.Val;
+ if (hasRoot())
+ return _root;
+ throw new InvalidOperationException(String.Format("Var {0}/{1} is unbound.", _ns,_sym));
+ }
+
+
+ /// <summary>
+ /// Sets the validator.
+ /// </summary>
+ /// <param name="vf">The new validtor</param>
+ public override void setValidator(IFn vf)
+ {
+ if (IsBound)
+ Validate(vf, getRoot());
+ _validator = vf;
+ }
+
+ #endregion
+
+ #region core.clj compatibility methods
+
+ /// <summary>
+ /// Find the var from a namespace-qualified symbol.
+ /// </summary>
+ /// <param name="nsQualifiedSym">A namespace-qualified symbol.</param>
+ /// <returns>The var, if found.</returns>
+ public static Var find(Symbol nsQualifiedSym)
+ {
+ if (nsQualifiedSym.Namespace == null)
+ throw new ArgumentException("Symbol must be namespace-qualified");
+ Namespace ns = Namespace.find(Symbol.create(nsQualifiedSym.Namespace));
+ if (ns == null)
+ throw new ArgumentException("No such namespace: " + nsQualifiedSym.Namespace);
+ return ns.FindInternedVar(Symbol.create(nsQualifiedSym.Name));
+ }
+
+ /// <summary>
+ /// The namespace this var is interned in.
+ /// </summary>
+ /// <returns></returns>
+ public Namespace ns()
+ {
+ return Namespace;
+ }
+
+ /// <summary>
+ /// Is this var public?
+ /// </summary>
+ /// <returns></returns>
+ public bool isPublic()
+ {
+ return IsPublic;
+ }
+
+ #endregion
+
+ #region Settable Members
+
+ /// <summary>
+ /// Sets the value.
+ /// </summary>
+ /// <param name="val">The new value</param>
+ /// <returns>The new value.</returns>
+ /// <remarks>Can only be called with a binding on the stack, else throws an exception.</remarks>
+ public object doSet(object val)
+ {
+ return set(val);
+ }
+
+ /// <summary>
+ /// Sets the root value.
+ /// </summary>
+ /// <param name="val">The new value</param>
+ /// <returns>The new value.</returns>
+ public object doReset(object val)
+ {
+ BindRoot(val);
+ return val;
+ }
+
+ #endregion
+
+ #region other
+
+ class AssocFn : AFn
+ {
+ public override object invoke(object m, object k, object v)
+ {
+ return RT.assoc(m,k,v);
+ }
+ }
+
+ /// <summary>
+ /// Used in calls to alterMeta, above.
+ /// </summary>
+ static IFn _assoc = new AssocFn();
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Properties/AssemblyInfo.cs b/ClojureCLR/Clojure/Clojure/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..9b081725 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +/**
+ * 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.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Clojure")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Clojure")]
+[assembly: AssemblyCopyright("Copyright © 2009")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("92a6be2b-759d-4d62-8912-9dde0052bc33")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/ClojureCLR/Clojure/Clojure/Properties/Resources.Designer.cs b/ClojureCLR/Clojure/Clojure/Properties/Resources.Designer.cs new file mode 100644 index 00000000..9e49183f --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Properties/Resources.Designer.cs @@ -0,0 +1,137 @@ +//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.3053
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace clojure.properties {
+ using System;
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// </summary>
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("clojure.lang.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ant sim ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ///; Copyright (c) David Miller. All rights reserved. + ///; The use and distribution terms for this software are covered by the + ///; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) + ///; which can be found in the file CPL.TXT 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 [rest of string was truncated]";.
+ /// </summary>
+ public static string ants {
+ get {
+ return ResourceManager.GetString("ants", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to ; 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.
+ ///
+ ///(in-ns 'clojure.core)
+ ///
+ ///(def unquote)
+ ///
+ ///(def [rest of string was truncated]";.
+ /// </summary>
+ public static string core {
+ get {
+ return ResourceManager.GetString("core", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to ; Copyright (c) David Miller. All rights reserved.
+ ///; The use and distribution terms for this software are covered by the
+ ///; Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
+ ///; which can be found in the file CPL.TXT 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.
+ ///
+ ///(in-ns 'clojure.core)
+ ///
+ ///;;;;;;;;;;;;;;;;;;;;;;;;;;;;; [rest of string was truncated]";.
+ /// </summary>
+ public static string core_print {
+ get {
+ return ResourceManager.GetString("core_print", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to (defn f1 [l n] (if (> (count l) n) nil (recur (cons 'a l) n)))
+ ///(defn len [x]
+ /// (. x Length))
+ ///(defn len2 [#^String x]
+ /// (. x Length))
+ ///
+ ///(defn test1 [] (time (f1 nil 100000)))
+ ///(defn test2 [] (time (reduce + (map len (replicate 100000 "asdf")))))
+ ///(defn test3 [] (time (reduce + (map len2 (replicate 100000 "asdf")))))
+ ///.
+ /// </summary>
+ public static string test {
+ get {
+ return ResourceManager.GetString("test", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Properties/Resources.resx b/ClojureCLR/Clojure/Clojure/Properties/Resources.resx new file mode 100644 index 00000000..97caf9b1 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Properties/Resources.resx @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+ <data name="ants" type="System.Resources.ResXFileRef, System.Windows.Forms">
+ <value>..\Bootstrap\ants.clj;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
+ </data>
+ <data name="core" type="System.Resources.ResXFileRef, System.Windows.Forms">
+ <value>..\Bootstrap\core.clj;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
+ </data>
+ <data name="core_print" type="System.Resources.ResXFileRef, System.Windows.Forms">
+ <value>..\Bootstrap\core-print.clj;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
+ </data>
+ <data name="test" type="System.Resources.ResXFileRef, System.Windows.Forms">
+ <value>..\Bootstrap\test.clj;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
+ </data>
+</root>
\ No newline at end of file diff --git a/ClojureCLR/Clojure/Clojure/Readers/LineNumberingReader.cs b/ClojureCLR/Clojure/Clojure/Readers/LineNumberingReader.cs new file mode 100644 index 00000000..765754b7 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Readers/LineNumberingReader.cs @@ -0,0 +1,165 @@ +/**
+ * 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;
+
+namespace clojure.lang.Readers
+{
+ public class LineNumberingReader : TextReader
+ {
+ #region Data
+
+ private TextReader _baseReader;
+
+ protected TextReader BaseReader
+ {
+ get { return _baseReader; }
+ }
+
+
+ private int _lineNumber = 1;
+
+ public int LineNumber
+ {
+ get { return _lineNumber; }
+ }
+
+
+ private int _position = 0;
+
+ protected int Position
+ {
+ get { return _position; }
+ }
+
+ #endregion
+
+ #region c-tors
+
+ public LineNumberingReader(TextReader reader)
+ {
+ _baseReader = reader;
+ }
+
+ #endregion
+
+ #region Lifetime methods
+
+ public override void Close()
+ {
+ _baseReader.Close();
+ base.Close();
+ }
+
+ #endregion
+
+ #region Lookahead
+
+ public override int Peek()
+ {
+ return _baseReader.Peek();
+ }
+
+ #endregion
+
+ #region Basic reading
+
+ public override int Read()
+ {
+ int ret = _baseReader.Read();
+ switch (ret)
+ {
+ case '\n':
+ _lineNumber++;
+ _position = 0;
+ break;
+
+ case '\r':
+ if (Peek() == '\n')
+ {
+ ret = _baseReader.Read();
+ goto case '\n';
+ }
+ break;
+ }
+ return ret;
+ }
+
+ public override int Read(char[] buffer, int index, int count)
+ {
+ int numRead = _baseReader.Read(buffer, index, count);
+ HandleLines(buffer, index, numRead);
+ return numRead;
+ }
+
+ public override int ReadBlock(char[] buffer, int index, int count)
+ {
+ int numRead = _baseReader.ReadBlock(buffer, index, count);
+ HandleLines(buffer, index, numRead);
+ return numRead;
+ }
+
+
+
+ public override string ReadLine()
+ {
+ string line = _baseReader.ReadLine();
+ if (line != null)
+ {
+ _lineNumber++;
+ _position = 0;
+ }
+ return line;
+ }
+
+ public override string ReadToEnd()
+ {
+ string result = _baseReader.ReadToEnd();
+ HandleLines(result);
+ return result;
+ }
+
+
+ #endregion
+
+ #region Counting lines
+
+ private void HandleLines(char[] buffer, int index, int numRead)
+ {
+ for (int i = index; i < index + numRead; ++i)
+ if (buffer[i] == '\n')
+ {
+ ++_lineNumber;
+ _position = 0;
+ }
+ else
+ ++_position;
+ }
+
+
+ private void HandleLines(string result)
+ {
+ foreach (char c in result)
+ if (c == '\n')
+ {
+ ++_lineNumber;
+ _position = 0;
+ }
+ else
+ ++_position;
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Runtime/ClojureBinder.cs b/ClojureCLR/Clojure/Clojure/Runtime/ClojureBinder.cs new file mode 100644 index 00000000..99ca561c --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Runtime/ClojureBinder.cs @@ -0,0 +1,39 @@ +/**
+ * 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 Microsoft.Scripting.Runtime;
+using Microsoft.Scripting.Actions;
+
+namespace clojure.runtime
+{
+ //Placeholder
+ //class ClojureBinder : DefaultBinder
+ //{
+ // public ClojureBinder(ScriptDomainManager manager)
+ // : base(manager)
+ // {
+ // }
+
+ // public override Microsoft.Scripting.Actions.Calls.Candidate PreferConvert(Type t1, Type t2)
+ // {
+ // throw new NotImplementedException();
+ // }
+
+
+ // public override bool CanConvertFrom(Type fromType, Type toType, bool toNotNullable, Microsoft.Scripting.Actions.Calls.NarrowingLevel level)
+ // {
+ // throw new NotImplementedException();
+ // }
+ //}
+}
diff --git a/ClojureCLR/Clojure/Clojure/Runtime/ClojureCommandLine.cs b/ClojureCLR/Clojure/Clojure/Runtime/ClojureCommandLine.cs new file mode 100644 index 00000000..880cbb83 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Runtime/ClojureCommandLine.cs @@ -0,0 +1,36 @@ +/**
+ * 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 Microsoft.Scripting.Hosting.Shell;
+
+namespace clojure.runtime
+{
+ public class ClojureCommandLine : CommandLine
+ {
+ protected override string Logo
+ {
+ get { return "Clojure.net console ...enter forms:\n"; }
+ }
+
+ protected override string Prompt
+ {
+ get { return "CLJ> "; }
+ }
+
+ public override string PromptContinuation
+ {
+ get { return "...> "; }
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Runtime/ClojureContext.cs b/ClojureCLR/Clojure/Clojure/Runtime/ClojureContext.cs new file mode 100644 index 00000000..cd3b5387 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Runtime/ClojureContext.cs @@ -0,0 +1,65 @@ +/**
+ * 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 Microsoft.Scripting.Runtime;
+using Microsoft.Linq.Expressions;
+using Microsoft.Scripting;
+using Microsoft.Scripting.Generation;
+using clojure.lang;
+using clojure.compiler;
+
+namespace clojure.runtime
+{
+ public class ClojureContext : LanguageContext
+ {
+ public ClojureContext(ScriptDomainManager manager, IDictionary<string, object> options)
+ : base(manager)
+ {
+ //Binder = new ClojureBinder(manager);
+ manager.LoadAssembly(typeof(string).Assembly);
+ manager.LoadAssembly(typeof(ISeq).Assembly);
+
+ // Set this to true to get Snippets written.
+ //manager.Configuration.DebugMode = true;
+ }
+
+ protected override Microsoft.Scripting.ScriptCode CompileSourceCode(Microsoft.Scripting.SourceUnit sourceUnit, Microsoft.Scripting.CompilerOptions options, Microsoft.Scripting.ErrorSink errorSink)
+ {
+ ClojureParser cp = new ClojureParser(sourceUnit);
+ LambdaExpression ast;
+
+ switch (sourceUnit.Kind)
+ {
+ case SourceCodeKind.InteractiveCode:
+ {
+ ScriptCodeParseResult result;
+ object code = cp.ParseInteractiveStatement(out result);
+ sourceUnit.CodeProperties = result;
+ if (result != ScriptCodeParseResult.Complete)
+ return null;
+ ast = Generator.Generate(code, true);
+ }
+ break;
+
+ default:
+ sourceUnit.CodeProperties = ScriptCodeParseResult.Complete;
+ ast = Generator.Generate(cp.ParseFile(), sourceUnit);
+ break;
+ }
+
+ ast = new GlobalLookupRewriter().RewriteLambda(ast);
+ return new ScriptCode(ast, sourceUnit);
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Runtime/ClojureParser.cs b/ClojureCLR/Clojure/Clojure/Runtime/ClojureParser.cs new file mode 100644 index 00000000..2c9ab222 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Runtime/ClojureParser.cs @@ -0,0 +1,74 @@ +/**
+ * 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 Microsoft.Scripting;
+using clojure.lang;
+using System.IO;
+
+namespace clojure.runtime
+{
+ class ClojureParser
+ {
+ private SourceUnit _source;
+ private string _text;
+
+ public ClojureParser(SourceUnit src)
+ {
+ _source = src;
+ _text = src.GetCode();
+ }
+
+ static readonly object _eof = new object();
+
+ internal object ParseInteractiveStatement(out ScriptCodeParseResult result)
+ {
+ result = ScriptCodeParseResult.Complete;
+ object s = null;
+
+ try
+ {
+ s = LispReader.read(new StringReader(_text), false, _eof, false);
+ }
+ catch (Exception)
+ {
+ result = ScriptCodeParseResult.Invalid;
+ s = null;
+ }
+
+ if (s == _eof)
+ {
+ result = ScriptCodeParseResult.IncompleteStatement;
+ s = null;
+ }
+
+ return s;
+ }
+
+ internal object ParseFile()
+ {
+ IPersistentVector pv = PersistentVector.EMPTY;
+
+ StringReader sr = new StringReader(_text);
+
+ pv = pv.cons(Compiler.DO);
+
+ object eofVal = new object();
+ object form;
+ while ((form = LispReader.read(sr, false, eofVal, false)) != eofVal)
+ pv = pv.cons(form);
+
+ return pv.seq();
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Runtime/Reflector.cs b/ClojureCLR/Clojure/Clojure/Runtime/Reflector.cs new file mode 100644 index 00000000..bb34d2c5 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Runtime/Reflector.cs @@ -0,0 +1,302 @@ +/**
+ * 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.Reflection;
+
+namespace clojure.runtime
+{
+ public sealed class Reflector
+ {
+
+ static public FieldInfo GetField(Type t, String name, bool getStatics)
+ {
+ return getStatics
+ ? t.GetField(name,BindingFlags.Static)
+ : t.GetField(name);
+ }
+
+
+ public static object CallStaticMethod(string methodName, Type t, params object[] args)
+ {
+
+ if (args.Length == 0)
+ {
+ FieldInfo f = t.GetField(methodName, BindingFlags.Static | BindingFlags.Public);
+ if (f != null)
+ return f.GetValue(t);
+
+ PropertyInfo p = t.GetProperty(methodName, BindingFlags.Static | BindingFlags.Public);
+ if (p != null)
+ return p.GetValue(t, null);
+ }
+
+ BindingFlags flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.InvokeMethod | BindingFlags.GetField | BindingFlags.GetProperty;
+
+ return t.InvokeMember(methodName, flags, Type.DefaultBinder, null, args);
+
+
+ //IEnumerable<MethodInfo> einfo = t.GetMethods(flags).Where(mi => mi.Name == methodName && mi.GetParameters().Length == args.Length);
+ //List<MethodInfo> infos = new List<MethodInfo>(einfo);
+
+ //return InvokeMatchingMethod(methodName, infos, t, null, args);
+ }
+
+
+
+
+ public static object CallInstanceMethod(string methodName, object target, params object[] args)
+ {
+ if (args.Length == 0)
+ {
+ FieldInfo f = target.GetType().GetField(methodName, BindingFlags.Instance | BindingFlags.Public);
+ if (f != null)
+ return f.GetValue(target);
+
+ PropertyInfo p = target.GetType().GetProperty(methodName, BindingFlags.Instance | BindingFlags.Public);
+ if (p != null)
+ return p.GetValue(target, null);
+ }
+
+
+ BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.InvokeMethod;
+
+ return target.GetType().InvokeMember(methodName, flags, Type.DefaultBinder, target, args);
+
+ //IEnumerable<MethodInfo> einfo1 = target.GetType().GetMethods(flags);
+ //List<MethodInfo> infos1 = new List<MethodInfo>(einfo1);
+
+
+ //IEnumerable<MethodInfo> einfo = target.GetType().GetMethods(flags).Where(mi => mi.Name == methodName && mi.GetParameters().Length == args.Length);
+ //List<MethodInfo> infos = new List<MethodInfo>(einfo);
+
+
+
+ //return InvokeMatchingMethod(methodName, infos, null, target, args);
+ }
+
+
+ private static object InvokeMatchingMethod(string methodName, List<MethodInfo> infos, Type t, object target, object[] args)
+ {
+
+ Type targetType = t ?? target.GetType();
+
+ if (infos.Count == 0)
+ throw new InvalidOperationException(string.Format("Cannot find {0} method named: {1} for type: {2} with {3} arguments", (t == null ? "instance" : "static"), methodName, targetType.Name, args.Length));
+
+ MethodInfo info;
+
+ if (infos.Count == 1)
+ info = infos[0];
+ else
+ {
+ // More than one with correct arity. Find best match.
+ MethodInfo found = null;
+ foreach (MethodInfo mi in infos)
+ {
+ ParameterInfo[] pinfos = mi.GetParameters();
+ if (IsCongruent(pinfos, args))
+ {
+ if (found == null || Subsumes(pinfos, found.GetParameters()))
+ found = mi;
+ }
+ }
+ info = found;
+ }
+
+ if (info == null)
+ throw new InvalidOperationException(string.Format("Cannot find static method named {0} for type: {1} with the correct argument type", methodName, t.Name));
+
+ object[] boxedArgs = BoxArgs(info.GetParameters(), args);
+
+ if (info.ReturnType == typeof(void))
+ {
+ info.Invoke(target, boxedArgs);
+ return null;
+ }
+ else
+ return info.Invoke(target, boxedArgs);
+ }
+
+ public static object InvokeConstructor(Type t, object[] args)
+ {
+ IEnumerable<ConstructorInfo> einfos = t.GetConstructors().Where(ci => ci.GetParameters().Length == args.Length);
+ List<ConstructorInfo> infos = new List<ConstructorInfo>(einfos);
+
+ if (infos.Count == 0)
+ throw new ArgumentException("NO matching constructor found for " + t.Name);
+ else if (infos.Count == 1)
+ {
+ ConstructorInfo info = infos[0];
+ return info.Invoke(BoxArgs(info.GetParameters(), args));
+ }
+ else
+ {
+ ConstructorInfo info = null;
+
+ // More than one with correct arity. Find best match.
+ ConstructorInfo found = null;
+ foreach (ConstructorInfo ci in infos)
+ {
+ ParameterInfo[] pinfos = ci.GetParameters();
+ if (IsCongruent(pinfos, args))
+ {
+ if (found == null || Subsumes(pinfos, found.GetParameters()))
+ found = ci;
+ }
+ }
+ info = found;
+
+
+ if (info == null)
+ throw new InvalidOperationException(string.Format("Cannot find c-tor for type: {0} with the correct argument type", t.Name));
+
+ return info.Invoke(BoxArgs(info.GetParameters(), args));
+ }
+ }
+
+
+ private static bool Subsumes(ParameterInfo[] c1, ParameterInfo[] c2)
+ {
+ //presumes matching lengths
+ Boolean better = false;
+ for (int i = 0; i < c1.Length; i++)
+ {
+ Type t1 = c1[i].ParameterType;
+ Type t2 = c2[i].ParameterType;
+ if (t1 != t2)// || c2[i].isPrimitive() && c1[i] == Object.class))
+ {
+ if (!t1.IsPrimitive && t2.IsPrimitive
+ //|| Number.class.isAssignableFrom(c1[i]) && c2[i].isPrimitive()
+ ||
+ t2.IsAssignableFrom(t1))
+ better = true;
+ else
+ return false;
+ }
+ }
+ return better;
+ }
+
+
+
+ private static object[] BoxArgs(ParameterInfo[] pinfos, object[] args)
+ {
+ if (pinfos.Length == 0)
+ return null;
+ object[] ret = new object[pinfos.Length];
+ for (int i = 0; i < pinfos.Length; i++)
+ ret[i] = BoxArg(pinfos[i], args[i]);
+ return ret;
+ }
+
+ // I can't remember what problem we are trying to solve.
+ // Here is the code that solved the first problem.
+ // However, it was messing up an Keyword -> IFn conversion, which should be possible.
+
+ //private static object BoxArg(ParameterInfo pinfo, object arg)
+ //{
+ // Type paramType = pinfo.ParameterType;
+
+ // if (!paramType.IsPrimitive)
+ // return Convert.ChangeType(arg, pinfo.ParameterType);
+
+ // return Convert.ChangeType(arg, pinfo.ParameterType); // don't know yet what we need here
+ //}
+
+ // Here is an improved version, until we figure out the problem.
+
+ private static object BoxArg(ParameterInfo pinfo, object arg)
+ {
+ Type paramType = pinfo.ParameterType;
+ Type argType = arg.GetType();
+
+ if (!paramType.IsPrimitive)
+ return arg;
+
+ return Convert.ChangeType(arg, pinfo.ParameterType); // don't know yet what we need here
+ }
+
+
+
+
+
+ private static bool IsCongruent(ParameterInfo[] pinfos, object[] args)
+ {
+ bool ret = false;
+ if (args == null)
+ return pinfos.Length == 0;
+ if (pinfos.Length == args.Length)
+ {
+ ret = true;
+ for (int i = 0; ret && i < pinfos.Length; i++)
+ {
+ object arg = args[i];
+ Type argType = (arg == null ? null : arg.GetType());
+ Type paramType = pinfos[i].ParameterType;
+ ret = ParamArgTypeMatch(paramType, argType);
+ }
+ }
+
+ return ret;
+ }
+
+ private static bool ParamArgTypeMatch(Type paramType, Type argType)
+ {
+ if (argType == null)
+ return !paramType.IsPrimitive;
+ return AreAssignable(paramType, argType);
+ }
+
+ // Stolen from DLR TypeUtils
+ internal static bool AreAssignable(Type dest, Type src)
+ {
+ if (dest == src)
+ {
+ return true;
+ }
+ if (dest.IsAssignableFrom(src))
+ {
+ return true;
+ }
+ if (dest.IsArray && src.IsArray && dest.GetArrayRank() == src.GetArrayRank() && AreReferenceAssignable(dest.GetElementType(), src.GetElementType()))
+ {
+ return true;
+ }
+ if (src.IsArray && dest.IsGenericType &&
+ (dest.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IEnumerable<>)
+ || dest.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IList<>)
+ || dest.GetGenericTypeDefinition() == typeof(System.Collections.Generic.ICollection<>))
+ && dest.GetGenericArguments()[0] == src.GetElementType())
+ {
+ return true;
+ }
+ return false;
+ }
+
+ // Stolen from DLR TypeUtils
+ internal static bool AreReferenceAssignable(Type dest, Type src)
+ {
+ // WARNING: This actually implements "Is this identity assignable and/or reference assignable?"
+ if (dest == src)
+ {
+ return true;
+ }
+ if (!dest.IsValueType && !src.IsValueType && AreAssignable(dest, src))
+ {
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/ClojureCLR.sln b/ClojureCLR/Clojure/ClojureCLR.sln new file mode 100644 index 00000000..e22b6285 --- /dev/null +++ b/ClojureCLR/Clojure/ClojureCLR.sln @@ -0,0 +1,502 @@ +Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Clojure", "Clojure\Clojure.csproj", "{B8089F66-DFBD-4906-BEE0-B317689C2524}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Clojure.Console", "Clojure.Console\Clojure.Console.csproj", "{4AFFC540-543E-4F56-9F49-14210D6C143A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Clojure.Tests", "Clojure.Tests\Clojure.Tests.csproj", "{656E1517-B0CA-47B4-B068-3DF43DC41F8A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Languages", "Languages", "{6B97627A-A2FE-4CE2-B5CC-95D54E656988}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Runtime", "Runtime", "{491B4A6F-D38A-49CA-BDE8-A7693919CB0B}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IronPython", "IronPython", "{2F2B77E1-8C8A-4573-9D6D-A305144F3C79}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ruby", "Ruby", "{07FD28A6-B302-49AB-A2C9-23D431F1CE2D}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ToyScript", "ToyScript", "{2FAC49E5-EF8E-4580-88F0-A7C2A01E2FA4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IronPython", "..\..\DLR_Main\Languages\IronPython\Src\IronPython\IronPython.csproj", "{95289EA9-5778-489D-AB48-F81F2CE2DA32}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IronPython.Modules", "..\..\DLR_Main\Languages\IronPython\Src\IronPython.Modules\IronPython.Modules.csproj", "{155CE436-1669-4A48-8095-410F2430237F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IronPythonConsole", "..\..\DLR_Main\Languages\IronPython\Src\IronPythonConsole\IronPythonConsole.csproj", "{811AC32C-11F3-4ED8-92A7-A7E39C2BB704}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IronPythonTest", "..\..\DLR_Main\Languages\IronPython\Src\IronPythonTest\IronPythonTest.csproj", "{B6B42537-07F8-4F6C-A99A-B155CAEB124E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IronPythonWindow", "..\..\DLR_Main\Languages\IronPython\Src\IronPythonWindow\IronPythonWindow.csproj", "{81DA19C7-4FEC-47E7-981B-D9310D549F95}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Scripting.Core", "..\..\DLR_Main\Runtime\src\Microsoft.Scripting.Core\Microsoft.Scripting.Core.csproj", "{2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ruby", "..\..\DLR_Main\Languages\Ruby\Src\IronRuby\Ruby.csproj", "{7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ruby.Console", "..\..\DLR_Main\Languages\Ruby\Utils\ironruby.console\Ruby.Console.csproj", "{D6AB587D-A888-4B98-85AC-F15E36F53838}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IronRuby.Tests", "..\..\DLR_Main\Languages\Ruby\Utils\IronRuby.Tests\IronRuby.Tests.csproj", "{8103D91B-89D8-4A18-9A40-426992602EA2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ToyScript", "..\..\DLR_Main\Languages\ToyScript\Src\ToyScript\ToyScript.csproj", "{92A2631C-F9CE-4B0C-833A-64AD62AC801F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassInitGenerator", "..\..\DLR_Main\Languages\Ruby\Utils\ironruby.classinitgenerator\ClassInitGenerator.csproj", "{166940A1-2C91-4BD0-B72B-A517FBD8BF0B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IronRuby.Libraries", "..\..\DLR_Main\Languages\Ruby\Src\IronRuby.Libraries\IronRuby.Libraries.csproj", "{77323B06-15A2-4CF4-8A7A-86EAA2B66498}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ToyConsole", "..\..\DLR_Main\Languages\ToyScript\Src\ToyConsole\ToyConsole.csproj", "{157A4685-D71A-4FF9-854F-64C9CAE21F43}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Scripting", "..\..\DLR_Main\Runtime\src\Microsoft.Scripting\Microsoft.Scripting.csproj", "{EB66B766-6354-4208-A3D4-AACBDCB5C3B3}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EA0F47DF-1607-411E-8C2F-59F5B1D26ABB}"
+ ProjectSection(SolutionItems) = preProject
+ ..\..\DLR_Main\App.config = ..\..\DLR_Main\App.config
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IronRuby.Libraries.Yaml", "..\..\DLR_Main\Languages\Ruby\Src\Yaml\IronRuby.Libraries.Yaml.csproj", "{AA18A245-E342-4368-A474-83178311A742}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{96D44FD1-0623-46D7-9511-EEA27441CF36}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Scripting.ExtensionAttribute", "..\..\DLR_Main\Runtime\src\Microsoft.Scripting.Core\Microsoft.Scripting.ExtensionAttribute.csproj", "{8B0F1074-750E-4D64-BF23-A1E0F54261E5}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Hosts", "Hosts", "{6544FA5E-6109-48AD-B674-3714A6A905AD}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chiron", "..\..\DLR_Main\Languages\IronPython\Src\Chiron\Chiron.csproj", "{7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Scripting.Silverlight", "..\..\DLR_Main\Runtime\Src\Microsoft.Scripting.SilverLight\Microsoft.Scripting.Silverlight.csproj", "{C86A7A7B-3086-4136-9B56-17E8DCB38C8D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ETSample1_CS", "..\..\DLR_Main\Samples\ExpressionTree\ETSample1_CS\ETSample1_CS.csproj", "{82F00DA6-1447-42AE-A9E3-B326ACFBBFFA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ExpressionTrees", "ExpressionTrees", "{735DC44F-7E1C-41BB-ADA5-2E3E9BC93D9C}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Hosting API", "Hosting API", "{C4822375-A83F-47F0-9C90-B9123AC7B6CF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShapeScript", "..\..\DLR_Main\Samples\Hosting\ShapeScript\ShapeScript.csproj", "{769916FC-40EA-4A2D-8D8E-B14152517CE0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Dynamic", "..\..\DLR_Main\Runtime\src\Microsoft.Dynamic\Microsoft.Dynamic.csproj", "{D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ DelaySigned Release|Any CPU = DelaySigned Release|Any CPU
+ DelaySigned Silverlight Release|Any CPU = DelaySigned Silverlight Release|Any CPU
+ FxCop|Any CPU = FxCop|Any CPU
+ Release|Any CPU = Release|Any CPU
+ Silverlight Debug|Any CPU = Silverlight Debug|Any CPU
+ Silverlight Release|Any CPU = Silverlight Release|Any CPU
+ SpecSharp|Any CPU = SpecSharp|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B8089F66-DFBD-4906-BEE0-B317689C2524}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B8089F66-DFBD-4906-BEE0-B317689C2524}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B8089F66-DFBD-4906-BEE0-B317689C2524}.DelaySigned Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B8089F66-DFBD-4906-BEE0-B317689C2524}.DelaySigned Release|Any CPU.Build.0 = Release|Any CPU
+ {B8089F66-DFBD-4906-BEE0-B317689C2524}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B8089F66-DFBD-4906-BEE0-B317689C2524}.DelaySigned Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {B8089F66-DFBD-4906-BEE0-B317689C2524}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
+ {B8089F66-DFBD-4906-BEE0-B317689C2524}.FxCop|Any CPU.Build.0 = Release|Any CPU
+ {B8089F66-DFBD-4906-BEE0-B317689C2524}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B8089F66-DFBD-4906-BEE0-B317689C2524}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B8089F66-DFBD-4906-BEE0-B317689C2524}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B8089F66-DFBD-4906-BEE0-B317689C2524}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B8089F66-DFBD-4906-BEE0-B317689C2524}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B8089F66-DFBD-4906-BEE0-B317689C2524}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {B8089F66-DFBD-4906-BEE0-B317689C2524}.SpecSharp|Any CPU.ActiveCfg = Release|Any CPU
+ {B8089F66-DFBD-4906-BEE0-B317689C2524}.SpecSharp|Any CPU.Build.0 = Release|Any CPU
+ {4AFFC540-543E-4F56-9F49-14210D6C143A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4AFFC540-543E-4F56-9F49-14210D6C143A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4AFFC540-543E-4F56-9F49-14210D6C143A}.DelaySigned Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4AFFC540-543E-4F56-9F49-14210D6C143A}.DelaySigned Release|Any CPU.Build.0 = Release|Any CPU
+ {4AFFC540-543E-4F56-9F49-14210D6C143A}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4AFFC540-543E-4F56-9F49-14210D6C143A}.DelaySigned Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {4AFFC540-543E-4F56-9F49-14210D6C143A}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
+ {4AFFC540-543E-4F56-9F49-14210D6C143A}.FxCop|Any CPU.Build.0 = Release|Any CPU
+ {4AFFC540-543E-4F56-9F49-14210D6C143A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4AFFC540-543E-4F56-9F49-14210D6C143A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4AFFC540-543E-4F56-9F49-14210D6C143A}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4AFFC540-543E-4F56-9F49-14210D6C143A}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4AFFC540-543E-4F56-9F49-14210D6C143A}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4AFFC540-543E-4F56-9F49-14210D6C143A}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {4AFFC540-543E-4F56-9F49-14210D6C143A}.SpecSharp|Any CPU.ActiveCfg = Release|Any CPU
+ {4AFFC540-543E-4F56-9F49-14210D6C143A}.SpecSharp|Any CPU.Build.0 = Release|Any CPU
+ {656E1517-B0CA-47B4-B068-3DF43DC41F8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {656E1517-B0CA-47B4-B068-3DF43DC41F8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {656E1517-B0CA-47B4-B068-3DF43DC41F8A}.DelaySigned Release|Any CPU.ActiveCfg = Release|Any CPU
+ {656E1517-B0CA-47B4-B068-3DF43DC41F8A}.DelaySigned Release|Any CPU.Build.0 = Release|Any CPU
+ {656E1517-B0CA-47B4-B068-3DF43DC41F8A}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {656E1517-B0CA-47B4-B068-3DF43DC41F8A}.DelaySigned Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {656E1517-B0CA-47B4-B068-3DF43DC41F8A}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
+ {656E1517-B0CA-47B4-B068-3DF43DC41F8A}.FxCop|Any CPU.Build.0 = Release|Any CPU
+ {656E1517-B0CA-47B4-B068-3DF43DC41F8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {656E1517-B0CA-47B4-B068-3DF43DC41F8A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {656E1517-B0CA-47B4-B068-3DF43DC41F8A}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {656E1517-B0CA-47B4-B068-3DF43DC41F8A}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU
+ {656E1517-B0CA-47B4-B068-3DF43DC41F8A}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {656E1517-B0CA-47B4-B068-3DF43DC41F8A}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {656E1517-B0CA-47B4-B068-3DF43DC41F8A}.SpecSharp|Any CPU.ActiveCfg = Release|Any CPU
+ {656E1517-B0CA-47B4-B068-3DF43DC41F8A}.SpecSharp|Any CPU.Build.0 = Release|Any CPU
+ {95289EA9-5778-489D-AB48-F81F2CE2DA32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {95289EA9-5778-489D-AB48-F81F2CE2DA32}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {95289EA9-5778-489D-AB48-F81F2CE2DA32}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {95289EA9-5778-489D-AB48-F81F2CE2DA32}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {95289EA9-5778-489D-AB48-F81F2CE2DA32}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = DelaySigned Silverlight Release|Any CPU
+ {95289EA9-5778-489D-AB48-F81F2CE2DA32}.DelaySigned Silverlight Release|Any CPU.Build.0 = DelaySigned Silverlight Release|Any CPU
+ {95289EA9-5778-489D-AB48-F81F2CE2DA32}.FxCop|Any CPU.ActiveCfg = FxCop|Any CPU
+ {95289EA9-5778-489D-AB48-F81F2CE2DA32}.FxCop|Any CPU.Build.0 = FxCop|Any CPU
+ {95289EA9-5778-489D-AB48-F81F2CE2DA32}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {95289EA9-5778-489D-AB48-F81F2CE2DA32}.Release|Any CPU.Build.0 = Release|Any CPU
+ {95289EA9-5778-489D-AB48-F81F2CE2DA32}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU
+ {95289EA9-5778-489D-AB48-F81F2CE2DA32}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU
+ {95289EA9-5778-489D-AB48-F81F2CE2DA32}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {95289EA9-5778-489D-AB48-F81F2CE2DA32}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {95289EA9-5778-489D-AB48-F81F2CE2DA32}.SpecSharp|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {95289EA9-5778-489D-AB48-F81F2CE2DA32}.SpecSharp|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {155CE436-1669-4A48-8095-410F2430237F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {155CE436-1669-4A48-8095-410F2430237F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {155CE436-1669-4A48-8095-410F2430237F}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {155CE436-1669-4A48-8095-410F2430237F}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {155CE436-1669-4A48-8095-410F2430237F}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = DelaySigned Silverlight Release|Any CPU
+ {155CE436-1669-4A48-8095-410F2430237F}.DelaySigned Silverlight Release|Any CPU.Build.0 = DelaySigned Silverlight Release|Any CPU
+ {155CE436-1669-4A48-8095-410F2430237F}.FxCop|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {155CE436-1669-4A48-8095-410F2430237F}.FxCop|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {155CE436-1669-4A48-8095-410F2430237F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {155CE436-1669-4A48-8095-410F2430237F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {155CE436-1669-4A48-8095-410F2430237F}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU
+ {155CE436-1669-4A48-8095-410F2430237F}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU
+ {155CE436-1669-4A48-8095-410F2430237F}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {155CE436-1669-4A48-8095-410F2430237F}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {155CE436-1669-4A48-8095-410F2430237F}.SpecSharp|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {155CE436-1669-4A48-8095-410F2430237F}.SpecSharp|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {811AC32C-11F3-4ED8-92A7-A7E39C2BB704}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {811AC32C-11F3-4ED8-92A7-A7E39C2BB704}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {811AC32C-11F3-4ED8-92A7-A7E39C2BB704}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {811AC32C-11F3-4ED8-92A7-A7E39C2BB704}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {811AC32C-11F3-4ED8-92A7-A7E39C2BB704}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {811AC32C-11F3-4ED8-92A7-A7E39C2BB704}.DelaySigned Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {811AC32C-11F3-4ED8-92A7-A7E39C2BB704}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
+ {811AC32C-11F3-4ED8-92A7-A7E39C2BB704}.FxCop|Any CPU.Build.0 = Release|Any CPU
+ {811AC32C-11F3-4ED8-92A7-A7E39C2BB704}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {811AC32C-11F3-4ED8-92A7-A7E39C2BB704}.Release|Any CPU.Build.0 = Release|Any CPU
+ {811AC32C-11F3-4ED8-92A7-A7E39C2BB704}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {811AC32C-11F3-4ED8-92A7-A7E39C2BB704}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU
+ {811AC32C-11F3-4ED8-92A7-A7E39C2BB704}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {811AC32C-11F3-4ED8-92A7-A7E39C2BB704}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {811AC32C-11F3-4ED8-92A7-A7E39C2BB704}.SpecSharp|Any CPU.ActiveCfg = Release|Any CPU
+ {811AC32C-11F3-4ED8-92A7-A7E39C2BB704}.SpecSharp|Any CPU.Build.0 = Release|Any CPU
+ {B6B42537-07F8-4F6C-A99A-B155CAEB124E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B6B42537-07F8-4F6C-A99A-B155CAEB124E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B6B42537-07F8-4F6C-A99A-B155CAEB124E}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {B6B42537-07F8-4F6C-A99A-B155CAEB124E}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {B6B42537-07F8-4F6C-A99A-B155CAEB124E}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = DelaySigned Silverlight Release|Any CPU
+ {B6B42537-07F8-4F6C-A99A-B155CAEB124E}.DelaySigned Silverlight Release|Any CPU.Build.0 = DelaySigned Silverlight Release|Any CPU
+ {B6B42537-07F8-4F6C-A99A-B155CAEB124E}.FxCop|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {B6B42537-07F8-4F6C-A99A-B155CAEB124E}.FxCop|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {B6B42537-07F8-4F6C-A99A-B155CAEB124E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B6B42537-07F8-4F6C-A99A-B155CAEB124E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B6B42537-07F8-4F6C-A99A-B155CAEB124E}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU
+ {B6B42537-07F8-4F6C-A99A-B155CAEB124E}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU
+ {B6B42537-07F8-4F6C-A99A-B155CAEB124E}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {B6B42537-07F8-4F6C-A99A-B155CAEB124E}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {B6B42537-07F8-4F6C-A99A-B155CAEB124E}.SpecSharp|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {B6B42537-07F8-4F6C-A99A-B155CAEB124E}.SpecSharp|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {81DA19C7-4FEC-47E7-981B-D9310D549F95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {81DA19C7-4FEC-47E7-981B-D9310D549F95}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {81DA19C7-4FEC-47E7-981B-D9310D549F95}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {81DA19C7-4FEC-47E7-981B-D9310D549F95}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {81DA19C7-4FEC-47E7-981B-D9310D549F95}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {81DA19C7-4FEC-47E7-981B-D9310D549F95}.DelaySigned Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {81DA19C7-4FEC-47E7-981B-D9310D549F95}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
+ {81DA19C7-4FEC-47E7-981B-D9310D549F95}.FxCop|Any CPU.Build.0 = Release|Any CPU
+ {81DA19C7-4FEC-47E7-981B-D9310D549F95}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {81DA19C7-4FEC-47E7-981B-D9310D549F95}.Release|Any CPU.Build.0 = Release|Any CPU
+ {81DA19C7-4FEC-47E7-981B-D9310D549F95}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {81DA19C7-4FEC-47E7-981B-D9310D549F95}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU
+ {81DA19C7-4FEC-47E7-981B-D9310D549F95}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {81DA19C7-4FEC-47E7-981B-D9310D549F95}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {81DA19C7-4FEC-47E7-981B-D9310D549F95}.SpecSharp|Any CPU.ActiveCfg = Release|Any CPU
+ {81DA19C7-4FEC-47E7-981B-D9310D549F95}.SpecSharp|Any CPU.Build.0 = Release|Any CPU
+ {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = DelaySigned Silverlight Release|Any CPU
+ {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.DelaySigned Silverlight Release|Any CPU.Build.0 = DelaySigned Silverlight Release|Any CPU
+ {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.FxCop|Any CPU.ActiveCfg = FxCop|Any CPU
+ {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.FxCop|Any CPU.Build.0 = FxCop|Any CPU
+ {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU
+ {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU
+ {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.SpecSharp|Any CPU.ActiveCfg = SpecSharp|Any CPU
+ {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}.SpecSharp|Any CPU.Build.0 = SpecSharp|Any CPU
+ {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = DelaySigned Silverlight Release|Any CPU
+ {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.DelaySigned Silverlight Release|Any CPU.Build.0 = DelaySigned Silverlight Release|Any CPU
+ {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.FxCop|Any CPU.ActiveCfg = FxCop|Any CPU
+ {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.FxCop|Any CPU.Build.0 = FxCop|Any CPU
+ {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU
+ {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU
+ {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.SpecSharp|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD}.SpecSharp|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {D6AB587D-A888-4B98-85AC-F15E36F53838}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D6AB587D-A888-4B98-85AC-F15E36F53838}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D6AB587D-A888-4B98-85AC-F15E36F53838}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {D6AB587D-A888-4B98-85AC-F15E36F53838}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {D6AB587D-A888-4B98-85AC-F15E36F53838}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D6AB587D-A888-4B98-85AC-F15E36F53838}.DelaySigned Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {D6AB587D-A888-4B98-85AC-F15E36F53838}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
+ {D6AB587D-A888-4B98-85AC-F15E36F53838}.FxCop|Any CPU.Build.0 = Release|Any CPU
+ {D6AB587D-A888-4B98-85AC-F15E36F53838}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D6AB587D-A888-4B98-85AC-F15E36F53838}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D6AB587D-A888-4B98-85AC-F15E36F53838}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D6AB587D-A888-4B98-85AC-F15E36F53838}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D6AB587D-A888-4B98-85AC-F15E36F53838}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D6AB587D-A888-4B98-85AC-F15E36F53838}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {D6AB587D-A888-4B98-85AC-F15E36F53838}.SpecSharp|Any CPU.ActiveCfg = Release|Any CPU
+ {D6AB587D-A888-4B98-85AC-F15E36F53838}.SpecSharp|Any CPU.Build.0 = Release|Any CPU
+ {8103D91B-89D8-4A18-9A40-426992602EA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8103D91B-89D8-4A18-9A40-426992602EA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8103D91B-89D8-4A18-9A40-426992602EA2}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {8103D91B-89D8-4A18-9A40-426992602EA2}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {8103D91B-89D8-4A18-9A40-426992602EA2}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8103D91B-89D8-4A18-9A40-426992602EA2}.DelaySigned Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {8103D91B-89D8-4A18-9A40-426992602EA2}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
+ {8103D91B-89D8-4A18-9A40-426992602EA2}.FxCop|Any CPU.Build.0 = Release|Any CPU
+ {8103D91B-89D8-4A18-9A40-426992602EA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8103D91B-89D8-4A18-9A40-426992602EA2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8103D91B-89D8-4A18-9A40-426992602EA2}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8103D91B-89D8-4A18-9A40-426992602EA2}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8103D91B-89D8-4A18-9A40-426992602EA2}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8103D91B-89D8-4A18-9A40-426992602EA2}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {8103D91B-89D8-4A18-9A40-426992602EA2}.SpecSharp|Any CPU.ActiveCfg = Release|Any CPU
+ {8103D91B-89D8-4A18-9A40-426992602EA2}.SpecSharp|Any CPU.Build.0 = Release|Any CPU
+ {92A2631C-F9CE-4B0C-833A-64AD62AC801F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {92A2631C-F9CE-4B0C-833A-64AD62AC801F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {92A2631C-F9CE-4B0C-833A-64AD62AC801F}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {92A2631C-F9CE-4B0C-833A-64AD62AC801F}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {92A2631C-F9CE-4B0C-833A-64AD62AC801F}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = DelaySigned Silverlight Release|Any CPU
+ {92A2631C-F9CE-4B0C-833A-64AD62AC801F}.DelaySigned Silverlight Release|Any CPU.Build.0 = DelaySigned Silverlight Release|Any CPU
+ {92A2631C-F9CE-4B0C-833A-64AD62AC801F}.FxCop|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {92A2631C-F9CE-4B0C-833A-64AD62AC801F}.FxCop|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {92A2631C-F9CE-4B0C-833A-64AD62AC801F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {92A2631C-F9CE-4B0C-833A-64AD62AC801F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {92A2631C-F9CE-4B0C-833A-64AD62AC801F}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU
+ {92A2631C-F9CE-4B0C-833A-64AD62AC801F}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU
+ {92A2631C-F9CE-4B0C-833A-64AD62AC801F}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {92A2631C-F9CE-4B0C-833A-64AD62AC801F}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {92A2631C-F9CE-4B0C-833A-64AD62AC801F}.SpecSharp|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {92A2631C-F9CE-4B0C-833A-64AD62AC801F}.SpecSharp|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.DelaySigned Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
+ {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.FxCop|Any CPU.Build.0 = Release|Any CPU
+ {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU
+ {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.SpecSharp|Any CPU.ActiveCfg = Release|Any CPU
+ {166940A1-2C91-4BD0-B72B-A517FBD8BF0B}.SpecSharp|Any CPU.Build.0 = Release|Any CPU
+ {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = DelaySigned Silverlight Release|Any CPU
+ {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.DelaySigned Silverlight Release|Any CPU.Build.0 = DelaySigned Silverlight Release|Any CPU
+ {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.FxCop|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.FxCop|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Release|Any CPU.Build.0 = Release|Any CPU
+ {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU
+ {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU
+ {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.SpecSharp|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {77323B06-15A2-4CF4-8A7A-86EAA2B66498}.SpecSharp|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {157A4685-D71A-4FF9-854F-64C9CAE21F43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {157A4685-D71A-4FF9-854F-64C9CAE21F43}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {157A4685-D71A-4FF9-854F-64C9CAE21F43}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {157A4685-D71A-4FF9-854F-64C9CAE21F43}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {157A4685-D71A-4FF9-854F-64C9CAE21F43}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {157A4685-D71A-4FF9-854F-64C9CAE21F43}.DelaySigned Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {157A4685-D71A-4FF9-854F-64C9CAE21F43}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
+ {157A4685-D71A-4FF9-854F-64C9CAE21F43}.FxCop|Any CPU.Build.0 = Release|Any CPU
+ {157A4685-D71A-4FF9-854F-64C9CAE21F43}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {157A4685-D71A-4FF9-854F-64C9CAE21F43}.Release|Any CPU.Build.0 = Release|Any CPU
+ {157A4685-D71A-4FF9-854F-64C9CAE21F43}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {157A4685-D71A-4FF9-854F-64C9CAE21F43}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU
+ {157A4685-D71A-4FF9-854F-64C9CAE21F43}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {157A4685-D71A-4FF9-854F-64C9CAE21F43}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {157A4685-D71A-4FF9-854F-64C9CAE21F43}.SpecSharp|Any CPU.ActiveCfg = Release|Any CPU
+ {157A4685-D71A-4FF9-854F-64C9CAE21F43}.SpecSharp|Any CPU.Build.0 = Release|Any CPU
+ {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = DelaySigned Silverlight Release|Any CPU
+ {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.DelaySigned Silverlight Release|Any CPU.Build.0 = DelaySigned Silverlight Release|Any CPU
+ {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.FxCop|Any CPU.ActiveCfg = FxCop|Any CPU
+ {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.FxCop|Any CPU.Build.0 = FxCop|Any CPU
+ {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU
+ {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU
+ {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.SpecSharp|Any CPU.ActiveCfg = SpecSharp|Any CPU
+ {EB66B766-6354-4208-A3D4-AACBDCB5C3B3}.SpecSharp|Any CPU.Build.0 = SpecSharp|Any CPU
+ {AA18A245-E342-4368-A474-83178311A742}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AA18A245-E342-4368-A474-83178311A742}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AA18A245-E342-4368-A474-83178311A742}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {AA18A245-E342-4368-A474-83178311A742}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {AA18A245-E342-4368-A474-83178311A742}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AA18A245-E342-4368-A474-83178311A742}.DelaySigned Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {AA18A245-E342-4368-A474-83178311A742}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
+ {AA18A245-E342-4368-A474-83178311A742}.FxCop|Any CPU.Build.0 = Release|Any CPU
+ {AA18A245-E342-4368-A474-83178311A742}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AA18A245-E342-4368-A474-83178311A742}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AA18A245-E342-4368-A474-83178311A742}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AA18A245-E342-4368-A474-83178311A742}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AA18A245-E342-4368-A474-83178311A742}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AA18A245-E342-4368-A474-83178311A742}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {AA18A245-E342-4368-A474-83178311A742}.SpecSharp|Any CPU.ActiveCfg = Release|Any CPU
+ {AA18A245-E342-4368-A474-83178311A742}.SpecSharp|Any CPU.Build.0 = Release|Any CPU
+ {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = DelaySigned Silverlight Release|Any CPU
+ {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.DelaySigned Silverlight Release|Any CPU.Build.0 = DelaySigned Silverlight Release|Any CPU
+ {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.FxCop|Any CPU.ActiveCfg = FxCop|Any CPU
+ {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.FxCop|Any CPU.Build.0 = FxCop|Any CPU
+ {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU
+ {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU
+ {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.SpecSharp|Any CPU.ActiveCfg = SpecSharp|Any CPU
+ {8B0F1074-750E-4D64-BF23-A1E0F54261E5}.SpecSharp|Any CPU.Build.0 = SpecSharp|Any CPU
+ {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A}.DelaySigned Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A}.FxCop|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A}.FxCop|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A}.SpecSharp|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A}.SpecSharp|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {C86A7A7B-3086-4136-9B56-17E8DCB38C8D}.Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU
+ {C86A7A7B-3086-4136-9B56-17E8DCB38C8D}.Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU
+ {C86A7A7B-3086-4136-9B56-17E8DCB38C8D}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Silverlight Release|Any CPU
+ {C86A7A7B-3086-4136-9B56-17E8DCB38C8D}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Silverlight Release|Any CPU
+ {C86A7A7B-3086-4136-9B56-17E8DCB38C8D}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = DelaySigned Silverlight Release|Any CPU
+ {C86A7A7B-3086-4136-9B56-17E8DCB38C8D}.DelaySigned Silverlight Release|Any CPU.Build.0 = DelaySigned Silverlight Release|Any CPU
+ {C86A7A7B-3086-4136-9B56-17E8DCB38C8D}.FxCop|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {C86A7A7B-3086-4136-9B56-17E8DCB38C8D}.FxCop|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {C86A7A7B-3086-4136-9B56-17E8DCB38C8D}.Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {C86A7A7B-3086-4136-9B56-17E8DCB38C8D}.Release|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {C86A7A7B-3086-4136-9B56-17E8DCB38C8D}.Silverlight Debug|Any CPU.ActiveCfg = Silverlight Debug|Any CPU
+ {C86A7A7B-3086-4136-9B56-17E8DCB38C8D}.Silverlight Debug|Any CPU.Build.0 = Silverlight Debug|Any CPU
+ {C86A7A7B-3086-4136-9B56-17E8DCB38C8D}.Silverlight Release|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {C86A7A7B-3086-4136-9B56-17E8DCB38C8D}.Silverlight Release|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {C86A7A7B-3086-4136-9B56-17E8DCB38C8D}.SpecSharp|Any CPU.ActiveCfg = Silverlight Release|Any CPU
+ {C86A7A7B-3086-4136-9B56-17E8DCB38C8D}.SpecSharp|Any CPU.Build.0 = Silverlight Release|Any CPU
+ {82F00DA6-1447-42AE-A9E3-B326ACFBBFFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {82F00DA6-1447-42AE-A9E3-B326ACFBBFFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {82F00DA6-1447-42AE-A9E3-B326ACFBBFFA}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {82F00DA6-1447-42AE-A9E3-B326ACFBBFFA}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {82F00DA6-1447-42AE-A9E3-B326ACFBBFFA}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {82F00DA6-1447-42AE-A9E3-B326ACFBBFFA}.DelaySigned Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {82F00DA6-1447-42AE-A9E3-B326ACFBBFFA}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
+ {82F00DA6-1447-42AE-A9E3-B326ACFBBFFA}.FxCop|Any CPU.Build.0 = Release|Any CPU
+ {82F00DA6-1447-42AE-A9E3-B326ACFBBFFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {82F00DA6-1447-42AE-A9E3-B326ACFBBFFA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {82F00DA6-1447-42AE-A9E3-B326ACFBBFFA}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {82F00DA6-1447-42AE-A9E3-B326ACFBBFFA}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU
+ {82F00DA6-1447-42AE-A9E3-B326ACFBBFFA}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {82F00DA6-1447-42AE-A9E3-B326ACFBBFFA}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {82F00DA6-1447-42AE-A9E3-B326ACFBBFFA}.SpecSharp|Any CPU.ActiveCfg = Release|Any CPU
+ {82F00DA6-1447-42AE-A9E3-B326ACFBBFFA}.SpecSharp|Any CPU.Build.0 = Release|Any CPU
+ {769916FC-40EA-4A2D-8D8E-B14152517CE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {769916FC-40EA-4A2D-8D8E-B14152517CE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {769916FC-40EA-4A2D-8D8E-B14152517CE0}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {769916FC-40EA-4A2D-8D8E-B14152517CE0}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {769916FC-40EA-4A2D-8D8E-B14152517CE0}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {769916FC-40EA-4A2D-8D8E-B14152517CE0}.DelaySigned Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {769916FC-40EA-4A2D-8D8E-B14152517CE0}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
+ {769916FC-40EA-4A2D-8D8E-B14152517CE0}.FxCop|Any CPU.Build.0 = Release|Any CPU
+ {769916FC-40EA-4A2D-8D8E-B14152517CE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {769916FC-40EA-4A2D-8D8E-B14152517CE0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {769916FC-40EA-4A2D-8D8E-B14152517CE0}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {769916FC-40EA-4A2D-8D8E-B14152517CE0}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU
+ {769916FC-40EA-4A2D-8D8E-B14152517CE0}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {769916FC-40EA-4A2D-8D8E-B14152517CE0}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {769916FC-40EA-4A2D-8D8E-B14152517CE0}.SpecSharp|Any CPU.ActiveCfg = Release|Any CPU
+ {769916FC-40EA-4A2D-8D8E-B14152517CE0}.SpecSharp|Any CPU.Build.0 = Release|Any CPU
+ {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D}.DelaySigned Release|Any CPU.ActiveCfg = DelaySigned Release|Any CPU
+ {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D}.DelaySigned Release|Any CPU.Build.0 = DelaySigned Release|Any CPU
+ {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D}.DelaySigned Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D}.DelaySigned Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D}.FxCop|Any CPU.ActiveCfg = FxCop|Any CPU
+ {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D}.FxCop|Any CPU.Build.0 = FxCop|Any CPU
+ {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D}.Silverlight Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D}.Silverlight Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D}.Silverlight Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D}.Silverlight Release|Any CPU.Build.0 = Release|Any CPU
+ {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D}.SpecSharp|Any CPU.ActiveCfg = Release|Any CPU
+ {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D}.SpecSharp|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {2F2B77E1-8C8A-4573-9D6D-A305144F3C79} = {6B97627A-A2FE-4CE2-B5CC-95D54E656988}
+ {07FD28A6-B302-49AB-A2C9-23D431F1CE2D} = {6B97627A-A2FE-4CE2-B5CC-95D54E656988}
+ {2FAC49E5-EF8E-4580-88F0-A7C2A01E2FA4} = {6B97627A-A2FE-4CE2-B5CC-95D54E656988}
+ {2AE75F5A-CD1F-4925-9647-AF4D1C282FB4} = {491B4A6F-D38A-49CA-BDE8-A7693919CB0B}
+ {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} = {491B4A6F-D38A-49CA-BDE8-A7693919CB0B}
+ {8B0F1074-750E-4D64-BF23-A1E0F54261E5} = {491B4A6F-D38A-49CA-BDE8-A7693919CB0B}
+ {D4AE44AD-07B9-41DC-BB3B-1FDCDE3C987D} = {491B4A6F-D38A-49CA-BDE8-A7693919CB0B}
+ {95289EA9-5778-489D-AB48-F81F2CE2DA32} = {2F2B77E1-8C8A-4573-9D6D-A305144F3C79}
+ {155CE436-1669-4A48-8095-410F2430237F} = {2F2B77E1-8C8A-4573-9D6D-A305144F3C79}
+ {811AC32C-11F3-4ED8-92A7-A7E39C2BB704} = {2F2B77E1-8C8A-4573-9D6D-A305144F3C79}
+ {B6B42537-07F8-4F6C-A99A-B155CAEB124E} = {2F2B77E1-8C8A-4573-9D6D-A305144F3C79}
+ {81DA19C7-4FEC-47E7-981B-D9310D549F95} = {2F2B77E1-8C8A-4573-9D6D-A305144F3C79}
+ {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD} = {07FD28A6-B302-49AB-A2C9-23D431F1CE2D}
+ {D6AB587D-A888-4B98-85AC-F15E36F53838} = {07FD28A6-B302-49AB-A2C9-23D431F1CE2D}
+ {8103D91B-89D8-4A18-9A40-426992602EA2} = {07FD28A6-B302-49AB-A2C9-23D431F1CE2D}
+ {166940A1-2C91-4BD0-B72B-A517FBD8BF0B} = {07FD28A6-B302-49AB-A2C9-23D431F1CE2D}
+ {77323B06-15A2-4CF4-8A7A-86EAA2B66498} = {07FD28A6-B302-49AB-A2C9-23D431F1CE2D}
+ {AA18A245-E342-4368-A474-83178311A742} = {07FD28A6-B302-49AB-A2C9-23D431F1CE2D}
+ {92A2631C-F9CE-4B0C-833A-64AD62AC801F} = {2FAC49E5-EF8E-4580-88F0-A7C2A01E2FA4}
+ {157A4685-D71A-4FF9-854F-64C9CAE21F43} = {2FAC49E5-EF8E-4580-88F0-A7C2A01E2FA4}
+ {735DC44F-7E1C-41BB-ADA5-2E3E9BC93D9C} = {96D44FD1-0623-46D7-9511-EEA27441CF36}
+ {C4822375-A83F-47F0-9C90-B9123AC7B6CF} = {96D44FD1-0623-46D7-9511-EEA27441CF36}
+ {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A} = {6544FA5E-6109-48AD-B674-3714A6A905AD}
+ {C86A7A7B-3086-4136-9B56-17E8DCB38C8D} = {6544FA5E-6109-48AD-B674-3714A6A905AD}
+ {82F00DA6-1447-42AE-A9E3-B326ACFBBFFA} = {735DC44F-7E1C-41BB-ADA5-2E3E9BC93D9C}
+ {769916FC-40EA-4A2D-8D8E-B14152517CE0} = {C4822375-A83F-47F0-9C90-B9123AC7B6CF}
+ EndGlobalSection
+EndGlobal
|