diff options
author | David Miller <dmiller2718@gmail.com> | 2009-05-31 07:37:00 +0000 |
---|---|---|
committer | David Miller <dmiller2718@gmail.com> | 2009-05-31 07:37:00 +0000 |
commit | 810e1a63de383c991153b286ad677cce161de60e (patch) | |
tree | 853b80cdf11899fbe06c4c5562165c5dddd1782c /ClojureCLR/Clojure | |
parent | 7eabf5df39ab6bb0e93a53a8ddc1651d49d9ff3e (diff) |
ClojureCLR: major update, part2
Diffstat (limited to 'ClojureCLR/Clojure')
80 files changed, 11857 insertions, 0 deletions
diff --git a/ClojureCLR/Clojure/BootstrapCompile/BootstrapCompile.csproj b/ClojureCLR/Clojure/BootstrapCompile/BootstrapCompile.csproj new file mode 100644 index 00000000..fac50dff --- /dev/null +++ b/ClojureCLR/Clojure/BootstrapCompile/BootstrapCompile.csproj @@ -0,0 +1,65 @@ +<?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>{3DBF3359-43B5-47C9-9E4D-CF50D7587F20}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>BootstrapCompile</RootNamespace>
+ <AssemblyName>BootstrapCompile</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </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="Compile.cs" />
+ <Compile Include="Properties\AssemblyInfo.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/BootstrapCompile/Compile.cs b/ClojureCLR/Clojure/BootstrapCompile/Compile.cs new file mode 100644 index 00000000..72e09221 --- /dev/null +++ b/ClojureCLR/Clojure/BootstrapCompile/Compile.cs @@ -0,0 +1,75 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+using clojure.lang;
+using System.Collections;
+using System.Diagnostics;
+
+namespace BootstrapCompile
+{
+ static class Compile
+ {
+
+ const string PATH_PROP = "clojure.compile.path";
+ const string REFLECTION_WARNING_PROP = "clojure.compile.warn-on-reflection";
+
+ static void Main(string[] args)
+ {
+ TextWriter outTW = (TextWriter)RT.OUT.deref();
+ TextWriter errTW = (TextWriter)RT.ERR.deref();
+
+ string path = Environment.GetEnvironmentVariable(PATH_PROP);
+ // TODO: get rid of this when we have the full build process set up
+ path = path ?? ".";
+
+ if ( path == null )
+ {
+ errTW.WriteLine("ERROR: Must set system property {0}",PATH_PROP);
+ errTW.WriteLine("to the location for the compiled .class files.");
+ errTW.WriteLine("This directory must also be on your {0}.",RT.CLOJURE_LOAD_PATH);
+ Environment.Exit(1);
+ }
+
+ string warnVal = Environment.GetEnvironmentVariable(REFLECTION_WARNING_PROP);
+ bool warnOnReflection = warnVal == null ? false : warnVal.Equals(true);
+
+ try
+ {
+ Var.pushThreadBindings(RT.map(
+ Compiler.COMPILE_PATH,path,
+ RT.WARN_ON_REFLECTION,warnOnReflection
+ ));
+
+ Stopwatch sw = new Stopwatch();
+
+ foreach ( string lib in args )
+ {
+ sw.Reset();
+ sw.Start();
+ outTW.Write("Compiling {0} to {1}",lib,path);
+ outTW.Flush();
+ Compiler.COMPILE.invoke(Symbol.intern(lib));
+ sw.Stop();
+ outTW.WriteLine(" -- {0} milliseconds.", sw.ElapsedMilliseconds);
+ }
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ try {
+ outTW.Flush();
+ outTW.Close();
+ }
+ catch ( IOException e)
+ {
+ errTW.WriteLine(e.StackTrace);
+ }
+ }
+
+
+
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/BootstrapCompile/Properties/AssemblyInfo.cs b/ClojureCLR/Clojure/BootstrapCompile/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..d34a0349 --- /dev/null +++ b/ClojureCLR/Clojure/BootstrapCompile/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("BootstrapCompile")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("BootstrapCompile")]
+[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("5cd7f55f-06b5-4520-af3e-936776803b57")]
+
+// 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.Main/Clojure.Main.csproj b/ClojureCLR/Clojure/Clojure.Main/Clojure.Main.csproj new file mode 100644 index 00000000..3006d806 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Main/Clojure.Main.csproj @@ -0,0 +1,73 @@ +<?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>{A9B3BA9E-6955-43E6-9846-54DEFEDCFBFC}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Clojure.Main</RootNamespace>
+ <AssemblyName>Clojure.Main</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </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="Main.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\DLR_Main\Src\Runtime\Microsoft.Scripting.Core\Microsoft.Scripting.Core.csproj">
+ <Project>{2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}</Project>
+ <Name>Microsoft.Scripting.Core</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\DLR_Main\Src\Runtime\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>
+ <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.Main/Main.cs b/ClojureCLR/Clojure/Clojure.Main/Main.cs new file mode 100644 index 00000000..e98d9afc --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Main/Main.cs @@ -0,0 +1,38 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using clojure.lang;
+
+namespace Clojure
+{
+ public static class CljMain
+ {
+ private static readonly Symbol CLOJURE_MAIN = Symbol.intern("clojure.main");
+ private static readonly Var REQUIRE = RT.var("clojure.core", "require");
+ private static readonly Var LEGACY_REPL = RT.var("clojure.main", "legacy-repl");
+ private static readonly Var LEGACY_SCRIPT = RT.var("clojure.main", "legacy-script");
+ private static readonly Var MAIN = RT.var("clojure.main", "main");
+
+ static void Main(string[] args)
+ {
+ REQUIRE.invoke(CLOJURE_MAIN);
+ MAIN.applyTo(RT.seq(args));
+ }
+
+ static void legacy_repl(string[] args)
+ {
+ REQUIRE.invoke(CLOJURE_MAIN);
+ LEGACY_REPL.invoke(RT.seq(args));
+
+ }
+
+ static void legacy_script(string[] args)
+ {
+ REQUIRE.invoke(CLOJURE_MAIN);
+ LEGACY_SCRIPT.invoke(RT.seq(args));
+ }
+
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Main/Properties/AssemblyInfo.cs b/ClojureCLR/Clojure/Clojure.Main/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..e355cc52 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Main/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.Main")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Clojure.Main")]
+[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("0d3ce997-8d0d-40c6-b116-a3d2f006b912")]
+
+// 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.Tests/FixtureSetupClass.cs b/ClojureCLR/Clojure/Clojure.Tests/FixtureSetupClass.cs new file mode 100644 index 00000000..cdb2a91b --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/FixtureSetupClass.cs @@ -0,0 +1,19 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using NUnit.Framework;
+using clojure.lang;
+
+
+ [SetUpFixture]
+ public class FixtureSetupClass
+ {
+ [SetUp]
+ public void Setup()
+ {
+ RT_Bootstrap_Flag._doRTBootstrap = false;
+ }
+ }
+
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/LazySeqTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/LazySeqTests.cs new file mode 100644 index 00000000..2f2f8820 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/LazySeqTests.cs @@ -0,0 +1,12 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Clojure.Tests.LibTests
+{
+ // TODO: Add LazySeq tests
+ class LazySeqTests
+ {
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/LibTests/StreamTests.cs b/ClojureCLR/Clojure/Clojure.Tests/LibTests/StreamTests.cs new file mode 100644 index 00000000..54996342 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/LibTests/StreamTests.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 Stream
+ class StreamTests
+ {
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure.Tests/ReaderTests/LineNumberingTextReaderTests.cs b/ClojureCLR/Clojure/Clojure.Tests/ReaderTests/LineNumberingTextReaderTests.cs new file mode 100644 index 00000000..0af3ed50 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure.Tests/ReaderTests/LineNumberingTextReaderTests.cs @@ -0,0 +1,251 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using NUnit.Framework;
+
+using clojure.lang;
+using System.IO;
+
+namespace Clojure.Tests.ReaderTests
+{
+ [TestFixture]
+ public class LineNumberingTextReaderTests : AssertionHelper
+ {
+ const string _sample = "abc\nde\nfghijk\r\nlmnopq\n\nrstuv";
+ StringReader _sr;
+ LineNumberingTextReader _rdr;
+
+ [SetUp]
+ public void Setup()
+ {
+ _sr = new StringReader(_sample);
+ _rdr = new LineNumberingTextReader(_sr);
+
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ _rdr.Close();
+ }
+
+ [Test]
+ public void Initializes_properly()
+ {
+ Expect(_rdr.Position, EqualTo(0));
+ Expect(_rdr.LineNumber, EqualTo(1));
+ Expect(_rdr.Peek(), EqualTo((int)_sample[0]));
+ Expect(_rdr.AtLineStart);
+ }
+
+
+ [Test]
+ public void Reads_character_at_a_time()
+ {
+ int[] chars = new int[] {
+ 'a', 'b', 'c', '\n',
+ 'd', 'e', '\n',
+ 'f', 'g', 'h', 'i', 'j', 'k', '\n',
+ 'l', 'm', 'n', 'o', 'p', 'q', '\n',
+ '\n',
+ 'r', 's', 't', 'u', 'v' };
+
+ int[] positions = new int[] {
+ 1, 2, 3, 0,
+ 1, 2, 0,
+ 1, 2, 3, 4, 5, 6, 0,
+ 1, 2, 3, 4, 5, 6, 0,
+ 0,
+ 1, 2, 3, 4, 5 };
+
+ int[] lines = new int[] {
+ 1, 1, 1, 2,
+ 2, 2, 3,
+ 3, 3, 3, 3, 3, 3, 4,
+ 4, 4, 4, 4, 4, 4, 5,
+ 6,
+ 6, 6, 6, 6, 6 };
+
+ bool[] starts = new bool[] {
+ false, false, false, true,
+ false, false, true,
+ false, false, false, false, false, false, true,
+ false, false, false, false, false, false, true,
+ true,
+ false, false, false, false, false
+ };
+
+ int i=0;
+ int ch;
+ while ((ch = _rdr.Read()) != -1)
+ {
+ Expect(ch, EqualTo(chars[i]));
+ Expect(_rdr.Position, EqualTo(positions[i]));
+ Expect(_rdr.LineNumber, EqualTo(lines[i]));
+ Expect(_rdr.AtLineStart, EqualTo(starts[i]));
+ ++i;
+ }
+ }
+
+ [Test]
+ public void Reads_lines_at_a_time()
+ {
+ string[] lines = new string[] {
+ "abc",
+ "de",
+ "fghijk",
+ "lmnopq",
+ "",
+ "rstuv" };
+
+ int[] positions = new int[] {
+ 0,0,0,0,0,5 };
+
+ int[] lineNums = new int[] {
+ 2, 3, 4, 5, 6, 6 };
+
+ bool[] starts = new bool[] {
+ true, true, true, true, true, true
+ };
+
+ int index = 0;
+ string line;
+ while ((line = _rdr.ReadLine()) != null)
+ {
+ Expect(line, EqualTo(lines[index]));
+ Expect(_rdr.Position, EqualTo(positions[index]));
+ Expect(_rdr.LineNumber, EqualTo(lineNums[index]));
+ Expect(_rdr.AtLineStart, EqualTo(starts[index]));
+ ++index;
+ }
+ }
+
+ [Test]
+ public void Reads_blocks_just_fine()
+ {
+ char[][] buffers = new char[][] {
+ new char[] { 'a', 'b', 'c', '\n', 'd' },
+ new char[] { 'e', '\n', 'f', 'g', 'h' },
+ new char[] { 'i', 'j', 'k', '\n', 'l' },
+ new char[] { 'm', 'n', 'o', 'p', 'q' },
+ new char[] { '\n', '\n', 'r', 's', 't' },
+ new char[] { 'u', 'v' } };
+ int[] positions = new int[] { 1, 3, 1, 6, 3, 5 };
+ int[] lineNums = new int[] { 2, 3, 4, 4, 6, 6 };
+ bool[] starts = new bool[] { false, false, false, false, false, true, };
+
+ char[] buffer = new char[20];
+
+ int index = 0;
+ int count;
+ while ((count = _rdr.Read(buffer, 0, 5)) != 0)
+ {
+ //Console.WriteLine("{0} {1}/{2} {3}/{4} {5}", index, _rdr.Position, positions[index], _rdr.LineNumber, lineNums[index], _rdr.AtLineStart);
+ Expect(SameContents(buffer, buffers[index], count));
+ Expect(_rdr.Position, EqualTo(positions[index]));
+ Expect(_rdr.LineNumber, EqualTo(lineNums[index]));
+ Expect(_rdr.AtLineStart, EqualTo(starts[index]));
+ ++index;
+ }
+
+ }
+
+ bool SameContents(char[] b1, char[] b2, int count)
+ {
+ for (int i = 0; i < count; i++)
+ if (b1[i] != b2[i])
+ return false;
+
+ return true;
+ }
+
+
+ [Test]
+ [ExpectedException(typeof(IOException))]
+ public void Double_unread_fails()
+ {
+ _rdr.Unread('a');
+ _rdr.Unread('b');
+ }
+
+ [Test]
+ public void Basic_unread_works()
+ {
+ int c1 = _rdr.Read();
+ Expect(c1, EqualTo((int)'a'));
+ Expect(_rdr.Position, EqualTo(1));
+ Expect(_rdr.LineNumber, EqualTo(1));
+ Expect(_rdr.AtLineStart, False);
+
+ int c2 = _rdr.Read();
+ Expect(c2, EqualTo((int)'b'));
+ Expect(_rdr.Position, EqualTo(2));
+ Expect(_rdr.LineNumber, EqualTo(1));
+ Expect(_rdr.AtLineStart, False);
+
+ _rdr.Unread('x');
+ Expect(_rdr.Position, EqualTo(1));
+ Expect(_rdr.LineNumber, EqualTo(1));
+ Expect(_rdr.AtLineStart, False);
+
+ int c3 = _rdr.Read();
+ Expect(c3, EqualTo((int)'x'));
+ Expect(_rdr.Position, EqualTo(2));
+ Expect(_rdr.LineNumber, EqualTo(1));
+ Expect(_rdr.AtLineStart, False);
+
+ int c4 = _rdr.Read();
+ Expect(c4, EqualTo((int)'c'));
+ Expect(_rdr.Position, EqualTo(3));
+ Expect(_rdr.LineNumber, EqualTo(1));
+ Expect(_rdr.AtLineStart, False);
+ }
+
+ [Test]
+ public void UnreadingNewlineWorks()
+ {
+ int c1 = _rdr.Read();
+ Expect(c1, EqualTo((int)'a'));
+ Expect(_rdr.Position, EqualTo(1));
+ Expect(_rdr.LineNumber, EqualTo(1));
+ Expect(_rdr.AtLineStart, False);
+
+ int c2 = _rdr.Read();
+ Expect(c2, EqualTo((int)'b'));
+ Expect(_rdr.Position, EqualTo(2));
+ Expect(_rdr.LineNumber, EqualTo(1));
+ Expect(_rdr.AtLineStart, False);
+
+ int c3 = _rdr.Read();
+ Expect(c3, EqualTo((int)'c'));
+ Expect(_rdr.Position, EqualTo(3));
+ Expect(_rdr.LineNumber, EqualTo(1));
+ Expect(_rdr.AtLineStart, False);
+
+ int c4 = _rdr.Read();
+ Expect(c4, EqualTo((int)'\n'));
+ Expect(_rdr.Position, EqualTo(0));
+ Expect(_rdr.LineNumber, EqualTo(2));
+ Expect(_rdr.AtLineStart);
+
+ _rdr.Unread(c4);
+ Expect(_rdr.Position, EqualTo(3));
+ Expect(_rdr.LineNumber, EqualTo(1));
+ Expect(_rdr.AtLineStart, False);
+
+ int c5 = _rdr.Read();
+ Expect(c5, EqualTo((int)'\n'));
+ Expect(_rdr.Position, EqualTo(0));
+ Expect(_rdr.LineNumber, EqualTo(2));
+ Expect(_rdr.AtLineStart);
+
+ int c6 = _rdr.Read();
+ Expect(c6, EqualTo((int)'d'));
+ Expect(_rdr.Position, EqualTo(1));
+ Expect(_rdr.LineNumber, EqualTo(2));
+ Expect(_rdr.AtLineStart, False);
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Bootstrap/core_print.clj b/ClojureCLR/Clojure/Clojure/Bootstrap/core_print.clj new file mode 100644 index 00000000..dbecfe1e --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Bootstrap/core_print.clj @@ -0,0 +1,317 @@ +; 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 :default [o, #^System.IO.TextWriter w]
+ (print-method (vary-meta o #(dissoc % :type)) 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 Object [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.Fn [o, #^System.IO.TextWriter w]
+ (print-ctor o (fn [o w]) w))
+
+(prefer-method print-dup clojure.lang.IPersistentCollection clojure.lang.Fn)
+(prefer-method print-dup java.util.Map clojure.lang.Fn)
+(prefer-method print-dup java.util.Collection clojure.lang.Fn)
+
+(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)) "/" (.Symbol 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)
+(prefer-method print-method clojure.lang.ISeq clojure.lang.IPersistentCollection)
+(prefer-method print-dup clojure.lang.ISeq clojure.lang.IPersistentCollection)
+(prefer-method print-method clojure.lang.ISeq System.Collections.ICollection) ;; java: java.util.Collection
+(prefer-method print-dup clojure.lang.ISeq System.Collections.ICollection) ;; java: java.util.Collection
+
+(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/main.clj b/ClojureCLR/Clojure/Clojure/Bootstrap/main.clj new file mode 100644 index 00000000..766cc7f7 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Bootstrap/main.clj @@ -0,0 +1,337 @@ +;; 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. + +;; Originally contributed by Stephen C. Gilardi + +(ns clojure.main + (:import (clojure.lang Compiler Compiler+CompilerException ;;;Compiler$CompilerException + LineNumberingTextReader RT))) ;;; LineNumberingPushbackReader + +(declare main) + +(defmacro with-bindings + "Executes body in the context of thread-local bindings for several vars + that often need to be set!: *ns* *warn-on-reflection* *print-meta* + *print-length* *print-level* *compile-path* *command-line-args* *1 + *2 *3 *e" + [& body] + `(binding [*ns* *ns* + *warn-on-reflection* *warn-on-reflection* + *print-meta* *print-meta* + *print-length* *print-length* + *print-level* *print-level* + *compile-path* (or (Environment/GetEnvironmentVariable "clojure.compile.path") "classes") ;;;(System/getProperty "clojure.compile.path" "classes") + *command-line-args* *command-line-args* + *1 nil + *2 nil + *3 nil + *e nil] + ~@body)) + +(defn repl-prompt + "Default :prompt hook for repl" + [] + (print (str (ns-name *ns*) "=> "))) ;;; until we get printf defined for real: (printf "%s=> " (ns-name *ns*))) + +(defn skip-if-eol + "If the next character on stream s is a newline, skips it, otherwise + leaves the stream untouched. Returns :line-start, :stream-end, or :body + to indicate the relative location of the next character on s. The stream + must either be an instance of LineNumberingPushbackReader or duplicate + its behavior of both supporting .unread and collapsing all of CR, LF, and + CRLF to a single \\newline." + [s] + (let [c (.Read s)] ;;; .read + (cond + (= c (int \newline)) :line-start + (= c -1) :stream-end + :else (do (.Unread s c) :body)))) ;;; .unread + +(defn skip-whitespace + "Skips whitespace characters on stream s. Returns :line-start, :stream-end, + or :body to indicate the relative location of the next character on s. + Interprets comma as whitespace and semicolon as comment to end of line. + Does not interpret #! as comment to end of line because only one + character of lookahead is available. The stream must either be an + instance of LineNumberingPushbackReader or duplicate its behavior of both + supporting .unread and collapsing all of CR, LF, and CRLF to a single + \\newline." + [s] + (loop [c (.Read s)] ;;; .read + (cond + (= c (int \newline)) :line-start + (= c -1) :stream-end + (= c (int \;)) (do (.ReadLine s) :line-start) ;;; .readLine + (or (Char/IsWhiteSpace (char c)) (= c (int \,))) (recur (.Read s)) ;;; (Character/isWhitespace c) .read + :else (do (.Unread s c) :body)))) ;;; .unread + +(defn repl-read + "Default :read hook for repl. Reads from *in* which must either be an + instance of LineNumberingPushbackReader or duplicate its behavior of both + supporting .unread and collapsing all of CR, LF, and CRLF into a single + \\newline. repl-read: + - skips whitespace, then + - returns request-prompt on start of line, or + - returns request-exit on end of stream, or + - reads an object from the input stream, then + - skips the next input character if it's end of line, then + - returns the object." + [request-prompt request-exit] + (or ({:line-start request-prompt :stream-end request-exit} + (skip-whitespace *in*)) + (let [input (read)] + (skip-if-eol *in*) + input))) + +(defn- root-cause + "Returns the initial cause of an exception or error by peeling off all of + its wrappers" + [throwable] + (loop [cause throwable] + (if-let [cause (.InnerException cause)] ;;; .getCause + (recur cause) + cause))) + +(defn repl-exception + "Returns CompilerExceptions in tact, but only the root cause of other + throwables" + [throwable] + (if (instance? clojure.lang.Compiler+CompilerException throwable) ;;; Compiler$CompilerException + throwable + (root-cause throwable))) + +(defn repl-caught + "Default :caught hook for repl" + [e] + (.WriteLine *err* (repl-exception e))) ;;; .println + +(defn repl + "Generic, reusable, read-eval-print loop. By default, reads from *in*, + writes to *out*, and prints exception summaries to *err*. If you use the + default :read hook, *in* must either be an instance of + LineNumberingPushbackReader or duplicate its behavior of both supporting + .unread and collapsing CR, LF, and CRLF into a single \\newline. Options + are sequential keyword-value pairs. Available options and their defaults: + + - :init, function of no arguments, initialization hook called with + bindings for set!-able vars in place. + default: #() + + - :need-prompt, function of no arguments, called before each + read-eval-print except the first, the user will be prompted if it + returns true. + default: (if (instance? LineNumberingPushbackReader *in*) + #(.atLineStart *in*) + #(identity true)) + + - :prompt, function of no arguments, prompts for more input. + default: repl-prompt + + - :flush, function of no arguments, flushes output + default: flush + + - :read, function of two arguments, reads from *in*: + - returns its first argument to request a fresh prompt + - depending on need-prompt, this may cause the repl to prompt + before reading again + - returns its second argument to request an exit from the repl + - else returns the next object read from the input stream + default: repl-read + + - :eval, funtion of one argument, returns the evaluation of its + argument + default: eval + + - :print, function of one argument, prints its argument to the output + default: prn + + - :caught, function of one argument, a throwable, called when + read, eval, or print throws an exception or error + default: repl-caught" + [& options] + (let [{:keys [init need-prompt prompt flush read eval print caught] + :or {init #() + need-prompt (if (instance? LineNumberingTextReader *in*) ;;; LineNumberingPushbackReader + #(.AtLineStart *in*) ;;; atLineStart + #(identity true)) + prompt repl-prompt + flush flush + read repl-read + eval eval + print prn + caught repl-caught}} + (apply hash-map options) + request-prompt (Object.) + request-exit (Object.) + read-eval-print + (fn [] + (try + (let [input (read request-prompt request-exit)] + (or (#{request-prompt request-exit} input) + (let [value (eval input)] + (print value) + (set! *3 *2) + (set! *2 *1) + (set! *1 value)))) + (catch Exception e ;;; Throwable + (caught e) + (set! *e e))))] + (with-bindings + (try + (init) + (catch Exception e ;;; Throwable + (caught e) + (set! *e e))) + (prompt) + (flush) + (loop [] + (when-not (= (read-eval-print) request-exit) + (when (need-prompt) + (prompt) + (flush)) + (recur)))))) + +(defn load-script + "Loads Clojure source from a file or resource given its path. Paths + beginning with @ or @/ are considered relative to classpath." + [path] + (if (.StartsWith path "@") ;;; startsWith + (RT/LoadCljScript ;;; loadResourceScript + (.Substring path (if (.StartsWith path "@/") 2 1))) ;;; substring startsWith + (Compiler/loadFile path))) + +(defn- init-opt + "Load a script" + [path] + (load-script path)) + +(defn- eval-opt + "Evals expressions in str, prints each non-nil result using prn" + [str] + (let [eof (Object.)] + (with-in-str str + (loop [input (read *in* false eof)] + (when-not (= input eof) + (let [value (eval input)] + (when-not (nil? value) + (prn value)) + (recur (read *in* false eof)))))))) + +(defn- init-dispatch + "Returns the handler associated with an init opt" + [opt] + ({"-i" init-opt + "--init" init-opt + "-e" eval-opt + "--eval" eval-opt} opt)) + +(defn- initialize + "Common initialize routine for repl, script, and null opts" + [args inits] + (in-ns 'user) + (set! *command-line-args* args) + (doseq [[opt arg] inits] + ((init-dispatch opt) arg))) + +(defn- repl-opt + "Start a repl with args and inits. Print greeting if no eval options were + present" + [[_ & args] inits] + (when-not (some #(= eval-opt (init-dispatch (first %))) inits) + (println "Clojure" (clojure-version))) + (repl :init #(initialize args inits)) + (prn) + (Environment/Exit 0)) ;;; System.Exit + +(defn- script-opt + "Run a script from a file, resource, or standard in with args and inits" + [[path & args] inits] + (with-bindings + (initialize args inits) + (if (= path "-") + (load-reader *in*) + (load-script path)))) + +(defn- null-opt + "No repl or script opt present, just bind args and run inits" + [args inits] + (with-bindings + (initialize args inits))) + +(defn- help-opt + "Print help text for main" + [_ _] + (println (:doc (meta (var main))))) + +(defn- main-dispatch + "Returns the handler associated with a main option" + [opt] + (or + ({"-r" repl-opt + "--repl" repl-opt + nil null-opt + "-h" help-opt + "--help" help-opt + "-?" help-opt} opt) + script-opt)) + +(defn- legacy-repl + "Called by the clojure.lang.Repl.main stub to run a repl with args + specified the old way" + [args] + (let [[inits [sep & args]] (split-with (complement #{"--"}) args)] + (repl-opt (concat ["-r"] args) (map vector (repeat "-i") inits)))) + +(defn- legacy-script + "Called by the clojure.lang.Script.main stub to run a script with args + specified the old way" + [args] + (let [[inits [sep & args]] (split-with (complement #{"--"}) args)] + (null-opt args (map vector (repeat "-i") inits)))) + +(defn main + "Usage: java -cp clojure.jar clojure.main [init-opt*] [main-opt] [arg*] + + With no options or args, runs an interactive Read-Eval-Print Loop + + init options: + -i, --init path Load a file or resource + -e, --eval string Evaluate expressions in string; print non-nil values + + main options: + -r, --repl Run a repl + path Run a script from from a file or resource + - Run a script from standard input + -h, -?, --help Print this help message and exit + + operation: + + - Establishes thread-local bindings for commonly set!-able vars + - Enters the user namespace + - Binds *command-line-args* to a seq of strings containing command line + args that appear after any main option + - Runs all init options in order + - Runs a repl or script if requested + + The init options may be repeated and mixed freely, but must appear before + any main option. The appearance of any eval option before running a repl + suppresses the usual repl greeting message: \"Clojure ~(clojure-version)\". + + Paths may be absolute or relative in the filesystem or relative to + classpath. Classpath-relative paths have prefix of @ or @/" + [& args] + (try + (if args + (loop [[opt arg & more :as args] args inits []] + (if (init-dispatch opt) + (recur more (conj inits [opt arg])) + ((main-dispatch opt) args inits))) + (repl-opt nil nil)) + (finally + (flush)))) + diff --git a/ClojureCLR/Clojure/Clojure/Bootstrap/set.clj b/ClojureCLR/Clojure/Clojure/Bootstrap/set.clj new file mode 100644 index 00000000..87113508 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Bootstrap/set.clj @@ -0,0 +1,148 @@ +; 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. + +(ns clojure.set) + +(defn- bubble-max-key [k coll] + "Move a maximal element of coll according to fn k (which returns a number) + to the front of coll." + (let [max (apply max-key k coll)] + (cons max (remove #(identical? max %) coll)))) + +(defn union + "Return a set that is the union of the input sets" + ([] #{}) + ([s1] s1) + ([s1 s2] + (if (< (count s1) (count s2)) + (reduce conj s2 s1) + (reduce conj s1 s2))) + ([s1 s2 & sets] + (let [bubbled-sets (bubble-max-key count (conj sets s2 s1))] + (reduce into (first bubbled-sets) (rest bubbled-sets))))) + +(defn intersection + "Return a set that is the intersection of the input sets" + ([s1] s1) + ([s1 s2] + (if (< (count s2) (count s1)) + (recur s2 s1) + (reduce (fn [result item] + (if (contains? s2 item) + result + (disj result item))) + s1 s1))) + ([s1 s2 & sets] + (let [bubbled-sets (bubble-max-key #(- (count %)) (conj sets s2 s1))] + (reduce intersection (first bubbled-sets) (rest bubbled-sets))))) + +(defn difference + "Return a set that is the first set without elements of the remaining sets" + ([s1] s1) + ([s1 s2] + (if (< (count s1) (count s2)) + (reduce (fn [result item] + (if (contains? s2 item) + (disj result item) + result)) + s1 s1) + (reduce disj s1 s2))) + ([s1 s2 & sets] + (reduce difference s1 (conj sets s2)))) + + +(defn select + "Returns a set of the elements for which pred is true" + [pred xset] + (reduce (fn [s k] (if (pred k) s (disj s k))) + xset xset)) + +(defn project + "Returns a rel of the elements of xrel with only the keys in ks" + [xrel ks] + (set (map #(select-keys % ks) xrel))) + +(defn rename-keys + "Returns the map with the keys in kmap renamed to the vals in kmap" + [map kmap] + (reduce + (fn [m [old new]] + (if (not= old new) + (-> m (assoc new (m old)) (dissoc old)) + m)) + map kmap)) + +(defn rename + "Returns a rel of the maps in xrel with the keys in kmap renamed to the vals in kmap" + [xrel kmap] + (set (map #(rename-keys % kmap) xrel))) + +(defn index + "Returns a map of the distinct values of ks in the xrel mapped to a + set of the maps in xrel with the corresponding values of ks." + [xrel ks] + (reduce + (fn [m x] + (let [ik (select-keys x ks)] + (assoc m ik (conj (get m ik #{}) x)))) + {} xrel)) + +(defn map-invert + "Returns the map with the vals mapped to the keys." + [m] (reduce (fn [m [k v]] (assoc m v k)) {} m)) + +(defn join + "When passed 2 rels, returns the rel corresponding to the natural + join. When passed an additional keymap, joins on the corresponding + keys." + ([xrel yrel] ;natural join + (if (and (seq xrel) (seq yrel)) + (let [ks (intersection (set (keys (first xrel))) (set (keys (first yrel)))) + [r s] (if (<= (count xrel) (count yrel)) + [xrel yrel] + [yrel xrel]) + idx (index r ks)] + (reduce (fn [ret x] + (let [found (idx (select-keys x ks))] + (if found + (reduce #(conj %1 (merge %2 x)) ret found) + ret))) + #{} s)) + #{})) + ([xrel yrel km] ;arbitrary key mapping + (let [[r s k] (if (<= (count xrel) (count yrel)) + [xrel yrel (map-invert km)] + [yrel xrel km]) + idx (index r (vals k))] + (reduce (fn [ret x] + (let [found (idx (rename-keys (select-keys x (keys k)) k))] + (if found + (reduce #(conj %1 (merge %2 x)) ret found) + ret))) + #{} s)))) + +(comment +(refer 'set) +(def xs #{{:a 11 :b 1 :c 1 :d 4} + {:a 2 :b 12 :c 2 :d 6} + {:a 3 :b 3 :c 3 :d 8 :f 42}}) + +(def ys #{{:a 11 :b 11 :c 11 :e 5} + {:a 12 :b 11 :c 12 :e 3} + {:a 3 :b 3 :c 3 :e 7 }}) + +(join xs ys) +(join xs (rename ys {:b :yb :c :yc}) {:a :a}) + +(union #{:a :b :c} #{:c :d :e }) +(difference #{:a :b :c} #{:c :d :e}) +(intersection #{:a :b :c} #{:c :d :e}) + +(index ys [:b]) +) + diff --git a/ClojureCLR/Clojure/Clojure/Bootstrap/version.properties b/ClojureCLR/Clojure/Clojure/Bootstrap/version.properties new file mode 100644 index 00000000..89769621 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Bootstrap/version.properties @@ -0,0 +1,5 @@ +clojure.version.major=1
+clojure.version.minor=1
+clojure.version.incremental=0
+clojure.version.qualifier=alpha
+clojure.version.interim=true
\ No newline at end of file diff --git a/ClojureCLR/Clojure/Clojure/Bootstrap/zip.clj b/ClojureCLR/Clojure/Clojure/Bootstrap/zip.clj new file mode 100644 index 00000000..81b09060 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Bootstrap/zip.clj @@ -0,0 +1,278 @@ +; 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. + +;functional hierarchical zipper, with navigation, editing and enumeration +;see Huet + +(ns clojure.zip + (:refer-clojure :exclude (replace remove next))) + +(defn zipper + "Creates a new zipper structure. + + branch? is a fn that, given a node, returns true if can have + children, even if it currently doesn't. + + children is a fn that, given a branch node, returns a seq of its + children. + + make-node is a fn that, given an existing node and a seq of + children, returns a new branch node with the supplied children. + root is the root node." + [branch? children make-node root] + #^{:zip/branch? branch? :zip/children children :zip/make-node make-node} + [root nil]) + +(defn seq-zip + "Returns a zipper for nested sequences, given a root sequence" + [root] + (zipper seq? identity (fn [node children] children) root)) + +(defn vector-zip + "Returns a zipper for nested vectors, given a root vector" + [root] + (zipper vector? seq (fn [node children] (apply vector children)) root)) + +(defn xml-zip + "Returns a zipper for xml elements (as from xml/parse), + given a root element" + [root] + (zipper (complement string?) + (comp seq :content) + (fn [node children] + (assoc node :content (and children (apply vector children)))) + root)) + +(defn node + "Returns the node at loc" + [loc] (loc 0)) + +(defn branch? + "Returns true if the node at loc is a branch" + [loc] + ((:zip/branch? ^loc) (node loc))) + +(defn children + "Returns a seq of the children of node at loc, which must be a branch" + [loc] + ((:zip/children ^loc) (node loc))) + +(defn make-node + "Returns a new branch node, given an existing node and new + children. The loc is only used to supply the constructor." + [loc node children] + ((:zip/make-node ^loc) node children)) + +(defn path + "Returns a seq of nodes leading to this loc" + [loc] + (:pnodes (loc 1))) + +(defn lefts + "Returns a seq of the left siblings of this loc" + [loc] + (seq (:l (loc 1)))) + +(defn rights + "Returns a seq of the right siblings of this loc" + [loc] + (:r (loc 1))) + + +(defn down + "Returns the loc of the leftmost child of the node at this loc, or + nil if no children" + [loc] + (let [[node path] loc + [c & cnext :as cs] (children loc)] + (when cs + (with-meta [c {:l [] + :pnodes (if path (conj (:pnodes path) node) [node]) + :ppath path + :r cnext}] ^loc)))) + +(defn up + "Returns the loc of the parent of the node at this loc, or nil if at + the top" + [loc] + (let [[node {l :l, ppath :ppath, pnodes :pnodes r :r, changed? :changed?, :as path}] loc] + (when pnodes + (let [pnode (peek pnodes)] + (with-meta (if changed? + [(make-node loc pnode (concat l (cons node r))) + (and ppath (assoc ppath :changed? true))] + [pnode ppath]) + ^loc))))) + +(defn root + "zips all the way up and returns the root node, reflecting any + changes." + [loc] + (if (= :end (loc 1)) + (node loc) + (let [p (up loc)] + (if p + (recur p) + (node loc))))) + +(defn right + "Returns the loc of the right sibling of the node at this loc, or nil" + [loc] + (let [[node {l :l [r & rnext :as rs] :r :as path}] loc] + (when (and path rs) + (with-meta [r (assoc path :l (conj l node) :r rnext)] ^loc)))) + +(defn rightmost + "Returns the loc of the rightmost sibling of the node at this loc, or self" + [loc] + (let [[node {l :l r :r :as path}] loc] + (if (and path r) + (with-meta [(last r) (assoc path :l (apply conj l node (butlast r)) :r nil)] ^loc) + loc))) + +(defn left + "Returns the loc of the left sibling of the node at this loc, or nil" + [loc] + (let [[node {l :l r :r :as path}] loc] + (when (and path (seq l)) + (with-meta [(peek l) (assoc path :l (pop l) :r (cons node r))] ^loc)))) + +(defn leftmost + "Returns the loc of the leftmost sibling of the node at this loc, or self" + [loc] + (let [[node {l :l r :r :as path}] loc] + (if (and path (seq l)) + (with-meta [(first l) (assoc path :l [] :r (concat (rest l) [node] r))] ^loc) + loc))) + +(defn insert-left + "Inserts the item as the left sibling of the node at this loc, + without moving" + [loc item] + (let [[node {l :l :as path}] loc] + (if (nil? path) + (throw (new Exception "Insert at top")) + (with-meta [node (assoc path :l (conj l item) :changed? true)] ^loc)))) + +(defn insert-right + "Inserts the item as the right sibling of the node at this loc, + without moving" + [loc item] + (let [[node {r :r :as path}] loc] + (if (nil? path) + (throw (new Exception "Insert at top")) + (with-meta [node (assoc path :r (cons item r) :changed? true)] ^loc)))) + +(defn replace + "Replaces the node at this loc, without moving" + [loc node] + (let [[_ path] loc] + (with-meta [node (assoc path :changed? true)] ^loc))) + +(defn edit + "Replaces the node at this loc with the value of (f node args)" + [loc f & args] + (replace loc (apply f (node loc) args))) + +(defn insert-child + "Inserts the item as the leftmost child of the node at this loc, + without moving" + [loc item] + (replace loc (make-node loc (node loc) (cons item (children loc))))) + +(defn append-child + "Inserts the item as the rightmost child of the node at this loc, + without moving" + [loc item] + (replace loc (make-node loc (node loc) (concat (children loc) [item])))) + +(defn next + "Moves to the next loc in the hierarchy, depth-first. When reaching + the end, returns a distinguished loc detectable via end?. If already + at the end, stays there." + [loc] + (if (= :end (loc 1)) + loc + (or + (and (branch? loc) (down loc)) + (right loc) + (loop [p loc] + (if (up p) + (or (right (up p)) (recur (up p))) + [(node p) :end]))))) + +(defn prev + "Moves to the previous loc in the hierarchy, depth-first. If already + at the root, returns nil." + [loc] + (if-let [lloc (left loc)] + (loop [loc lloc] + (if-let [child (and (branch? loc) (down loc))] + (recur (rightmost child)) + loc)) + (up loc))) + +(defn end? + "Returns true if loc represents the end of a depth-first walk" + [loc] + (= :end (loc 1))) + +(defn remove + "Removes the node at loc, returning the loc that would have preceded + it in a depth-first walk." + [loc] + (let [[node {l :l, ppath :ppath, pnodes :pnodes, rs :r, :as path}] loc] + (if (nil? path) + (throw (new Exception "Remove at top")) + (if (pos? (count l)) + (loop [loc (with-meta [(peek l) (assoc path :l (pop l) :changed? true)] ^loc)] + (if-let [child (and (branch? loc) (down loc))] + (recur (rightmost child)) + loc)) + (with-meta [(make-node loc (peek pnodes) rs) + (and ppath (assoc ppath :changed? true))] + ^loc))))) + +(comment + +(load-file "/Users/rich/dev/clojure/src/zip.clj") +(refer 'zip) +(def data '[[a * b] + [c * d]]) +(def dz (vector-zip data)) + +(right (down (right (right (down dz))))) +(lefts (right (down (right (right (down dz)))))) +(rights (right (down (right (right (down dz)))))) +(up (up (right (down (right (right (down dz))))))) +(path (right (down (right (right (down dz)))))) + +(-> dz down right right down right) +(-> dz down right right down right (replace '/) root) +(-> dz next next (edit str) next next next (replace '/) root) +(-> dz next next next next next next next next next remove root) +(-> dz next next next next next next next next next remove (insert-right 'e) root) +(-> dz next next next next next next next next next remove up (append-child 'e) root) + +(end? (-> dz next next next next next next next next next remove next)) + +(-> dz next remove next remove root) + +(loop [loc dz] + (if (end? loc) + (root loc) + (recur (next (if (= '* (node loc)) + (replace loc '/) + loc))))) + +(loop [loc dz] + (if (end? loc) + (root loc) + (recur (next (if (= '* (node loc)) + (remove loc) + loc))))) +) diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/AFnImplGenerator.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/AFnImplGenerator.cs new file mode 100644 index 00000000..edec1159 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/AFnImplGenerator.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;
+using System.Reflection.Emit;
+using System.Reflection;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class AFnImplGenerator
+ {
+
+ internal static Type Create(GenContext context, Type baseClass)
+ {
+ ModuleBuilder mb = context.ModuleBldr;
+ string name = baseClass.Name + "_impl";
+ TypeBuilder baseTB = context.ModuleBldr.DefineType(name, TypeAttributes.Class | TypeAttributes.Public, baseClass);
+
+ baseTB.DefineDefaultConstructor(MethodAttributes.Public);
+
+ for (int i = 0; i < 20; i++ )
+ DefineDelegateFieldAndOverride(baseTB, i);
+
+ return baseTB.CreateType();
+ }
+
+ static Type[] CreateObjectTypeArray(int size)
+ {
+ Type[] typeArray = new Type[size];
+ for (int i = 0; i < size; i++)
+ typeArray[i] = typeof(Object);
+ return typeArray;
+ }
+
+ static MethodInfo Method_AFn_WrongArityException = typeof(AFn).GetMethod("WrongArityException2");
+ static MethodInfo Method_Delegate_Invoke = typeof(Delegate).GetMethod("Invoke");
+
+ static void DefineDelegateFieldAndOverride(TypeBuilder tb, int numArgs)
+ {
+ Type fieldType = FuncTypeHelpers.GetFFuncType(numArgs);
+ string fieldName = "_fn" + numArgs;
+ FieldBuilder fb = tb.DefineField(fieldName, fieldType, FieldAttributes.Public);
+
+ MethodBuilder mb = tb.DefineMethod("invoke", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual, typeof(object), CreateObjectTypeArray(numArgs));
+ ILGenerator gen = mb.GetILGenerator();
+
+ Label eqLabel = gen.DefineLabel();
+
+ // this._fni == null ?
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldfld, fb);
+ gen.Emit(OpCodes.Ldnull);
+ gen.Emit(OpCodes.Beq, eqLabel);
+ //Not equal to Null, invoke it.
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldfld, fb);
+ for (int i = 0; i < numArgs; i++)
+ gen.Emit(OpCodes.Ldarg, i+1);
+ gen.Emit(OpCodes.Call,fb.FieldType.GetMethod("Invoke"));
+
+ gen.Emit(OpCodes.Ret);
+
+ gen.MarkLabel(eqLabel);
+ // Equal to Null: throw WrongArityException
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Call, Method_AFn_WrongArityException);
+ gen.Emit(OpCodes.Throw);
+ }
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/AssignExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/AssignExpr.cs new file mode 100644 index 00000000..6a369ede --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/AssignExpr.cs @@ -0,0 +1,80 @@ +/**
+ * 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;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class AssignExpr : Expr
+ {
+ #region Data
+
+ readonly AssignableExpr _target;
+ readonly Expr _val;
+
+ #endregion
+
+ #region Ctors
+
+ public AssignExpr(AssignableExpr target, Expr val)
+ {
+ _target = target;
+ _val = val;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return _val.HasClrType; }
+ }
+
+ public override Type ClrType
+ {
+ get { return _val.ClrType; }
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public sealed class Parser : IParser
+ {
+ public Expr Parse(object frm)
+ {
+ ISeq form = (ISeq)frm;
+ if (RT.Length(form) != 3)
+ throw new ArgumentException("Malformed assignment, expecting (set! target val)");
+ Expr target = Compiler.GenerateAST(RT.second(form));
+ if (!(target is AssignableExpr))
+ throw new ArgumentException("Invalid assignment target");
+ return new AssignExpr((AssignableExpr)target,
+ Compiler.GenerateAST(RT.third(form)));
+ }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ return _target.GenAssignDlr(context, _val);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/AssignableExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/AssignableExpr.cs new file mode 100644 index 00000000..8f0b095b --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/AssignableExpr.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;
+using Microsoft.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ interface AssignableExpr
+ {
+ Expression GenAssignDlr(GenContext context, Expr val);
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/BindingInit.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/BindingInit.cs new file mode 100644 index 00000000..6e00ee9f --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/BindingInit.cs @@ -0,0 +1,42 @@ +/**
+ * 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.CljCompiler.Ast
+{
+ struct BindingInit
+ {
+ #region Data
+
+ private readonly LocalBinding _binding;
+ public LocalBinding Binding
+ {
+ get { return _binding; }
+ }
+
+ private readonly Expr _init;
+ public Expr Init
+ {
+ get { return _init; }
+ }
+
+ public BindingInit(LocalBinding binding, Expr init)
+ {
+ _binding = binding;
+ _init = init;
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/BodyExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/BodyExpr.cs new file mode 100644 index 00000000..76c52fa1 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/BodyExpr.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;
+using Microsoft.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class BodyExpr : Expr
+ {
+ #region Data
+
+ readonly IPersistentVector _exprs;
+
+ Expr LastExpr
+ {
+ get
+ {
+ return (Expr)_exprs.nth(_exprs.count() - 1);
+ }
+ }
+
+ #endregion
+
+ #region Ctors
+
+ public BodyExpr(IPersistentVector exprs)
+ {
+ _exprs = exprs;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return LastExpr.HasClrType; }
+ }
+
+ public override Type ClrType
+ {
+ get { return LastExpr.ClrType; }
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public sealed class Parser : IParser
+ {
+ public Expr Parse(object frms)
+ {
+ ISeq forms = (ISeq)frms;
+
+ if (Util.equals(RT.first(forms), Compiler.DO))
+ forms = RT.next(forms);
+
+ IPersistentVector exprs = PersistentVector.EMPTY;
+
+ for (ISeq s = forms; s != null; s = s.next())
+ {
+ if (s.next() == null)
+ {
+ // in tail recurive position
+ try
+ {
+ Var.pushThreadBindings(PersistentHashMap.create(Compiler.IN_TAIL_POSITION, RT.T));
+ exprs = exprs.cons(Compiler.GenerateAST(s.first()));
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ }
+ else
+ exprs = exprs.cons(Compiler.GenerateAST(s.first()));
+ }
+ if (exprs.count() == 0)
+ exprs = exprs.cons(Compiler.NIL_EXPR);
+
+ return new BodyExpr(exprs);
+
+ }
+ }
+
+ #endregion
+
+ #region Code generateion
+
+ public override Expression GenDlr(GenContext context)
+ {
+ List<Expression> exprs = new List<Expression>(_exprs.count());
+
+ for (int i = 0; i < _exprs.count() - 1; i++)
+ {
+ Expr e = (Expr)_exprs.nth(i);
+ exprs.Add(e.GenDlr(context));
+ }
+
+ // In Java version, this is split off because the Context in the calls above is forced to be C.STATEMENT.
+ // TODO: Wrap this into the loop above. No real need to do this way.
+ Expr last = (Expr)_exprs.nth(_exprs.count() - 1);
+ exprs.Add(last.GenDlr(context));
+
+ return Expression.Block(exprs);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/BooleanExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/BooleanExpr.cs new file mode 100644 index 00000000..e9996fd8 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/BooleanExpr.cs @@ -0,0 +1,60 @@ +/**
+ * 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;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class BooleanExpr : LiteralExpr
+ {
+ #region Data
+
+ readonly bool _val;
+
+ public override object val()
+ {
+ return _val;
+ }
+
+ #endregion
+
+ #region C-tors
+
+ public BooleanExpr(bool val)
+ {
+ _val = val;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return true; }
+ }
+
+ public override Type ClrType
+ {
+ get { return typeof(Boolean); }
+ }
+
+ public override Expression GenDlr(GenContext context)
+ {
+ return Expression.Constant(_val);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/ConstantExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/ConstantExpr.cs new file mode 100644 index 00000000..c6f19d82 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/ConstantExpr.cs @@ -0,0 +1,84 @@ +/**
+ * 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;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class ConstantExpr : LiteralExpr
+ {
+ #region Data
+
+ readonly object _v;
+ readonly int _id;
+
+ public override object val()
+ {
+ return _v;
+ }
+
+ #endregion
+
+ #region Ctors
+
+ public ConstantExpr(object v)
+ {
+ _v = v;
+ _id = Compiler.RegisterConstant(v);
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return _v.GetType().IsPublic; }
+ }
+
+ public override Type ClrType
+ {
+ get { return _v.GetType(); }
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public sealed class Parser : IParser
+ {
+ public Expr Parse(object form)
+ {
+ object v = RT.second(form);
+ if (v == null)
+ return Compiler.NIL_EXPR;
+ else
+ return new ConstantExpr(v);
+ }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ // Java: fn.emitConstant(gen,id)
+ //return Expression.Constant(_v);
+ return context.FnExpr.GenConstant(context,_id,_v);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/DefExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/DefExpr.cs new file mode 100644 index 00000000..aa494f3c --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/DefExpr.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 Microsoft.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class DefExpr : Expr
+ {
+ #region Data
+
+ readonly Var _var;
+ readonly Expr _init;
+ readonly Expr _meta;
+ readonly bool _initProvided;
+
+ #endregion
+
+ #region Ctors
+
+ public DefExpr(Var var, Expr init, Expr meta, bool initProvided)
+ {
+ _var = var;
+ _init = init;
+ _meta = meta;
+ _initProvided = initProvided;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return true; }
+ }
+
+ public override Type ClrType
+ {
+ get { return typeof(Var); }
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public sealed class Parser : IParser
+ {
+ public Expr Parse(object form)
+ {
+ // (def x) or (def x initexpr)
+ if (RT.count(form) > 3)
+ throw new Exception("Too many arguments to def");
+
+ if (RT.count(form) < 2)
+ throw new Exception("Too few arguments to def");
+
+ Symbol sym = RT.second(form) as Symbol;
+
+ if (sym == null)
+ throw new Exception("Second argument to def must be a Symbol.");
+
+ Var v = Compiler.LookupVar(sym, true);
+
+ if (v == null)
+ throw new Exception("Can't refer to qualified var that doesn't exist");
+
+ if (!v.Namespace.Equals(Compiler.CurrentNamespace))
+ {
+ if (sym.Namespace == null)
+ throw new Exception(string.Format("Name conflict, can't def {0} because namespace: {1} refers to: {2}",
+ sym, Compiler.CurrentNamespace.Name, v));
+ else
+ throw new Exception("Can't create defs outside of current namespace");
+ }
+
+ IPersistentMap mm = sym.meta();
+ // TODO: add source and line info metadata.
+ //Object source_path = SOURCE_PATH.get();
+ //source_path = source_path == null ? "NO_SOURCE_FILE" : source_path;
+ //mm = (IPersistentMap)RT.assoc(mm, RT.LINE_KEY, LINE.get()).assoc(RT.FILE_KEY, source_path);
+
+ Expr meta = mm == null ? null : Compiler.GenerateAST(mm);
+ Expr init = Compiler.GenerateAST(RT.third(form),v.Symbol.Name);
+ bool initProvided = RT.count(form) == 3;
+
+ return new DefExpr(v, init, meta, initProvided);
+ }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ List<Expression> exprs = new List<Expression>();
+
+ ParameterExpression parm = Expression.Parameter(typeof(Var), "v");
+
+ Expression varExpr = context.FnExpr.GenVar(context,_var);
+
+ exprs.Add(Expression.Assign(parm, varExpr));
+
+ if (_initProvided)
+ exprs.Add(Expression.Call(parm, Compiler.Method_Var_BindRoot, Compiler.MaybeBox(_init.GenDlr(context))));
+
+ if (_meta != null)
+ exprs.Add(Expression.Call(parm, Compiler.Method_Var_setMeta, _meta.GenDlr(context)));
+
+ exprs.Add(parm);
+
+ return Expression.Block(new ParameterExpression[] { parm }, exprs);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/EmptyExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/EmptyExpr.cs new file mode 100644 index 00000000..f4e19ba3 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/EmptyExpr.cs @@ -0,0 +1,83 @@ +/**
+ * 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;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class EmptyExpr : Expr
+ {
+ #region Data
+
+ readonly object _coll;
+
+ #endregion
+
+ #region Ctors
+
+ public EmptyExpr(object coll)
+ {
+ _coll = coll;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return true; }
+ }
+
+ public override Type ClrType
+ {
+ get {
+ if (_coll is IPersistentList)
+ return typeof(IPersistentList);
+ else if (_coll is IPersistentVector)
+ return typeof(IPersistentVector);
+ else if (_coll is IPersistentMap)
+ return typeof(IPersistentMap);
+ else if (_coll is IPersistentSet)
+ return typeof(IPersistentSet);
+ else
+ throw new InvalidOperationException("Unknown Collection type.");
+ }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ Type collType;
+
+ if (_coll is IPersistentList)
+ collType = typeof(PersistentList);
+ else if (_coll is IPersistentVector)
+ collType = typeof(PersistentVector);
+ else if (_coll is IPersistentMap)
+ collType = typeof(PersistentArrayMap);
+ else if (_coll is IPersistentSet)
+ collType = typeof(PersistentHashSet);
+ else
+ throw new InvalidOperationException("Unknown collection type.");
+
+ return Expression.Field(null, collType, "EMPTY");
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/Expr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/Expr.cs new file mode 100644 index 00000000..ff85b8f6 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/Expr.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;
+using Microsoft.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ /// <summary>
+ /// Base class for all AST expressions.
+ /// </summary>
+ abstract class Expr : Node
+ {
+ public abstract bool HasClrType { get; }
+ public abstract Type ClrType { get; }
+
+ public abstract Expression GenDlr(GenContext context);
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/FieldExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/FieldExpr.cs new file mode 100644 index 00000000..4013d351 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/FieldExpr.cs @@ -0,0 +1,27 @@ +/**
+ * 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;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ abstract class FieldExpr : HostExpr, AssignableExpr
+ {
+ #region AssignableExpr Members
+
+ public abstract Expression GenAssignDlr(GenContext context, Expr val);
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/FnExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/FnExpr.cs new file mode 100644 index 00000000..c10ed1ba --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/FnExpr.cs @@ -0,0 +1,814 @@ +/**
+ * 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.Emit;
+using System.Reflection;
+using System.Collections;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class FnExpr : Expr
+ {
+ #region Data
+
+ static readonly Type[] EMPTY_TYPE_ARRAY = new Type[0];
+
+ static readonly Keyword KW_ONCE = Keyword.intern(null, "once");
+ static readonly Keyword KW_SUPER_NAME = Keyword.intern(null, "super-name");
+
+ IPersistentCollection _methods;
+ FnMethod _variadicMethod = null;
+ string _name;
+ string _simpleName;
+ string _internalName;
+
+ string _thisName;
+ public string ThisName
+ {
+ get { return _thisName; }
+ set { _thisName = value; }
+ }
+
+ Type _fnType;
+ readonly object _tag;
+ IPersistentMap _closes = PersistentHashMap.EMPTY; // localbinding -> itself
+ public IPersistentMap Closes
+ {
+ get { return _closes; }
+ set { _closes = value; }
+ }
+ IPersistentMap _keywords = PersistentHashMap.EMPTY; // Keyword -> KeywordExpr
+ IPersistentMap _vars = PersistentHashMap.EMPTY;
+ PersistentVector _constants;
+ int _constantsID;
+ bool _onceOnly = false;
+ string _superName = null;
+
+ TypeBuilder _typeBuilder = null;
+ public TypeBuilder TypeBuilder
+ {
+ get { return _typeBuilder; }
+ }
+ TypeBuilder _baseTypeBuilder = null;
+ Type _baseType = null;
+
+ public Type BaseType
+ {
+ get { return _baseType; }
+ }
+ ParameterExpression _thisParam = null;
+ public ParameterExpression ThisParam
+ {
+ get { return _thisParam; }
+ set { _thisParam = value; }
+ }
+
+ ConstructorInfo _ctorInfo;
+
+ List<FieldBuilder> _closedOverFields;
+
+ #endregion
+
+ #region Ctors
+
+ public FnExpr(object tag)
+ {
+ _tag = tag;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return true; }
+ }
+
+ public override Type ClrType
+ {
+ get { return _tag != null ? Compiler.TagToType(_tag) : typeof(IFn); }
+ }
+
+ #endregion
+
+ #region Misc
+
+ // This naming convention drawn from the Java code.
+ internal void ComputeNames(ISeq form, string name)
+ {
+ FnMethod enclosingMethod = (FnMethod)Compiler.METHODS.deref();
+
+ string baseName = enclosingMethod != null
+ ? (enclosingMethod.Fn._name + "$")
+ : (Compiler.Munge(Compiler.CurrentNamespace.Name.Name) + "$");
+
+ if (RT.second(form) is Symbol)
+ name = ((Symbol)RT.second(form)).Name;
+
+ _simpleName = (name == null ? "fn" : Compiler.Munge(name).Replace(".", "_DOT_")) + "__" + RT.nextID();
+ _name = baseName + _simpleName;
+ _internalName = _name.Replace('.', '/');
+ _fnType = RT.classForName(_internalName);
+ // fn.fntype = Type.getObjectType(fn.internalName) -- JAVA
+ }
+
+ bool IsVariadic { get { return _variadicMethod != null; } }
+
+ #endregion
+
+ #region Parsing
+
+ public static Expr Parse(object frm, string name)
+ {
+ ISeq form = (ISeq)frm;
+
+ FnExpr fn = new FnExpr(Compiler.TagOf(form));
+
+ if (((IMeta)form.first()).meta() != null)
+ {
+ fn._onceOnly = RT.booleanCast(RT.get(RT.meta(form.first()), KW_ONCE));
+ fn._superName = (string)RT.get(RT.meta(form.first()), KW_SUPER_NAME);
+ }
+
+
+ fn.ComputeNames(form, name);
+
+ try
+ {
+ Var.pushThreadBindings(RT.map(
+ Compiler.CONSTANTS, PersistentVector.EMPTY,
+ Compiler.KEYWORDS, PersistentHashMap.EMPTY,
+ Compiler.VARS, PersistentHashMap.EMPTY));
+
+ //arglist might be preceded by symbol naming this fn
+ if (RT.second(form) is Symbol)
+ {
+ fn._thisName = ((Symbol)RT.second(form)).Name;
+ form = RT.cons(Compiler.FN, RT.next(RT.next(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.next(form));
+
+
+ FnMethod variadicMethod = null;
+ SortedDictionary<int, FnMethod> methods = new SortedDictionary<int, FnMethod>();
+
+ for (ISeq s = RT.next(form); s != null; s = RT.next(s))
+ {
+ FnMethod f = FnMethod.Parse(fn, (ISeq)RT.first(s));
+ if (f.IsVariadic)
+ {
+ if (variadicMethod == null)
+ variadicMethod = f;
+ else
+ throw new Exception("Can't have more than 1 variadic overload");
+ }
+ else if (!methods.ContainsKey(f.RequiredArity))
+ methods[f.RequiredArity] = f;
+ 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.");
+
+ IPersistentCollection allMethods = null;
+ foreach (FnMethod method in methods.Values)
+ allMethods = RT.conj(allMethods, method);
+ if (variadicMethod != null)
+ allMethods = RT.conj(allMethods, variadicMethod);
+
+ fn._methods = allMethods;
+ fn._variadicMethod = variadicMethod;
+ fn._keywords = (IPersistentMap)Compiler.KEYWORDS.deref();
+ fn._vars = (IPersistentMap)Compiler.VARS.deref();
+ fn._constants = (PersistentVector)Compiler.CONSTANTS.deref();
+ fn._constantsID = RT.nextID();
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ // JAVA: fn.compile();
+ return fn;
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ switch (context.Mode)
+ {
+ case CompilerMode.Immediate:
+ return GenDlrImmediate(context);
+ case CompilerMode.File:
+ return GenDlrForFile(context);
+ default:
+ throw Util.UnreachableCode();
+ }
+ }
+
+ #endregion
+
+ #region Immediate-mode compilation
+
+ Expression GenDlrImmediate(GenContext context)
+ {
+ Type baseClass = GetBaseClass(context,GetSuperType());
+
+ _baseType = baseClass;
+
+ return GenerateImmediateLambda(context, baseClass);
+ //MethodInfo info = baseClass.GetMethod("invoke", new Type[0]);
+ //AFunction x = (AFunction)Activator.CreateInstance(baseClass);
+ //x.invoke();
+ //return null;
+
+ }
+
+ private Expression GenerateImmediateLambda(GenContext context, Type baseClass)
+ {
+ // ParameterExpression p1 = ThisParam ?? Expression.Parameter(baseClass, "____x");
+ ParameterExpression p1 = Expression.Parameter(baseClass, "____x");
+ _thisParam = p1;
+ List<Expression> exprs = new List<Expression>();
+
+ if (baseClass == typeof(RestFnImpl))
+ exprs.Add(Expression.Assign(p1,
+ Expression.New(Compiler.Ctor_RestFnImpl_1, Expression.Constant(_variadicMethod.RequiredArity))));
+ else
+ exprs.Add(Expression.Assign(p1, Expression.New(p1.Type)));
+
+ GenContext newContext = CreateContext(context, null, baseClass);
+
+ for (ISeq s = RT.seq(_methods); s != null; s = s.next())
+ {
+ FnMethod method = (FnMethod)s.first();
+ LambdaExpression lambda = method.GenerateImmediateLambda(newContext);
+ string fieldName = IsVariadic && method.IsVariadic
+ ? "_fnDo" + method.RequiredArity
+ : "_fn" + method.NumParams;
+ exprs.Add(Expression.Assign(Expression.Field(p1, fieldName), lambda));
+ }
+
+ exprs.Add(p1);
+
+ Expression expr = Expression.Block(new ParameterExpression[] { p1 }, exprs);
+ return expr;
+ }
+
+ private static Type GetBaseClass(GenContext context,Type superType)
+ {
+ Type baseClass = LookupBaseClass(superType);
+ if (baseClass != null)
+ return baseClass;
+
+ baseClass = GenerateBaseClass(context,superType);
+ baseClass = RegisterBaseClass(superType, baseClass);
+ return baseClass;
+ }
+
+ static AtomicReference<IPersistentMap> _baseClassMapRef = new AtomicReference<IPersistentMap>(PersistentHashMap.EMPTY);
+
+ static FnExpr()
+ {
+ _baseClassMapRef.Set(_baseClassMapRef.Get().assoc(typeof(RestFn),typeof(RestFnImpl)));
+ //_baseClassMapRef.Set(_baseClassMapRef.Get().assoc(typeof(AFn),typeof(AFnImpl)));
+ }
+
+
+ private static Type LookupBaseClass(Type superType)
+ {
+ return (Type)_baseClassMapRef.Get().valAt(superType);
+ }
+
+ private static Type RegisterBaseClass(Type superType, Type baseType)
+ {
+ IPersistentMap map = _baseClassMapRef.Get();
+
+ while (!map.containsKey(superType))
+ {
+ IPersistentMap newMap = map.assoc(superType, baseType);
+ _baseClassMapRef.CompareAndSet(map, newMap);
+ map = _baseClassMapRef.Get();
+ }
+
+ return LookupBaseClass(superType); // may not be the one we defined -- race condition
+ }
+
+
+ private static Type GenerateBaseClass(GenContext context, Type superType)
+ {
+ return AFnImplGenerator.Create(context, superType);
+ }
+
+ #endregion
+
+ #region File-mode compilation
+
+ Expression GenDlrForFile(GenContext context)
+ {
+ EnsureTypeBuilt(context);
+
+ //ConstructorInfo ctorInfo = _ctorInfo;
+ ConstructorInfo ctorInfo = _fnType.GetConstructors()[0];
+
+ // The incoming context holds info on the containing function.
+ // That is the one that holds the closed-over variable values.
+
+ List<Expression> args = new List<Expression>(_closes.count());
+ for (ISeq s = RT.keys(_closes); s != null; s = s.next())
+ {
+ LocalBinding lb = (LocalBinding)s.first();
+ if (lb.PrimitiveType != null)
+ args.Add(context.FnExpr.GenUnboxedLocal(context, lb));
+ else
+ args.Add(context.FnExpr.GenLocal(context, lb));
+ }
+
+ return Expression.New(ctorInfo, args);
+ }
+
+
+ internal Expression GenLocal(GenContext context, LocalBinding lb)
+ {
+ if (context.Mode == CompilerMode.File && _closes.containsKey(lb))
+ {
+ Expression expr = Expression.Field(_thisParam,lb.Name);
+ Type primtType = lb.PrimitiveType;
+ if ( primtType != null )
+ expr = Compiler.MaybeBox(Expression.Convert(expr,primtType));
+ return expr;
+ }
+ else
+ {
+ return lb.ParamExpression;
+ }
+ }
+
+ internal Expression GenUnboxedLocal(GenContext context, LocalBinding lb)
+ {
+ Type primType = lb.PrimitiveType;
+ if (context.Mode == CompilerMode.File && _closes.containsKey(lb))
+ return Expression.Convert(Expression.Field(_thisParam, lb.Name), primType);
+ else
+ return lb.ParamExpression;
+ }
+
+ private void EnsureTypeBuilt(GenContext context)
+ {
+ if (_typeBuilder != null)
+ return;
+
+ _baseTypeBuilder = GenerateFnBaseClass(context);
+ _baseType = _baseTypeBuilder.CreateType();
+
+ GenerateFnClass(context, _baseType);
+ _fnType = _typeBuilder.CreateType();
+ }
+
+
+ #region Base class construction
+
+ private TypeBuilder GenerateFnBaseClass(GenContext context)
+ {
+ Type super = GetSuperType();
+ string baseClassName = _internalName + "_base";
+
+ TypeBuilder baseTB = context.ModuleBldr.DefineType(baseClassName, TypeAttributes.Class | TypeAttributes.Public, super);
+
+ GenerateConstantFields(baseTB);
+ GenerateClosedOverFields(baseTB);
+ GenerateBaseClassConstructor(baseTB);
+
+ return baseTB;
+ }
+
+ private void GenerateConstantFields(TypeBuilder baseTB)
+ {
+ for (int i = 0; i < _constants.count(); i++)
+ {
+ string fieldName = ConstantName(i);
+ Type fieldType = ConstantType(i);
+ FieldBuilder fb = baseTB.DefineField(fieldName, fieldType, FieldAttributes.FamORAssem | FieldAttributes.Static);
+ }
+ }
+
+ const string CONST_PREFIX = "const__";
+
+ private string ConstantName(int i)
+ {
+ return CONST_PREFIX + i;
+ }
+
+ // TODO: see if this is really what we want.
+ private Type ConstantType(int i)
+ {
+ object o = _constants.nth(i);
+ Type t = o.GetType();
+ if (t.IsPublic)
+ {
+ // Java: can't emit derived fn types due to visibility
+ if (typeof(LazySeq).IsAssignableFrom(t))
+ return typeof(ISeq);
+ else if (typeof(RestFn).IsAssignableFrom(t))
+ return typeof(RestFn);
+ else if (typeof(AFn).IsAssignableFrom(t))
+ return typeof(AFn);
+ else if (t == typeof(Var))
+ return t;
+ else if (t == typeof(String))
+ return t;
+ }
+ return typeof(object);
+ // This ends up being too specific.
+ // TODO: However, if we were to see the value returned by RT.readFromString(), we could make it work.
+ //return t;
+ }
+
+ private void GenerateClosedOverFields(TypeBuilder baseTB)
+ {
+ _closedOverFields = new List<FieldBuilder>(_closes.count());
+
+ // closed-overs map to instance fields.
+ for (ISeq s = RT.keys(_closes); s != null; s = s.next())
+ {
+ LocalBinding lb = (LocalBinding)s.first();
+ Type type = lb.PrimitiveType ?? typeof(object);
+ _closedOverFields.Add(baseTB.DefineField(lb.Name, type, FieldAttributes.FamORAssem));
+ }
+ }
+
+ static readonly ConstructorInfo AFunction_Default_Ctor = typeof(AFunction).GetConstructor(EMPTY_TYPE_ARRAY);
+ static readonly ConstructorInfo RestFn_Int_Ctor = typeof(RestFn).GetConstructor(new Type[] { typeof(int) });
+
+ private void GenerateBaseClassConstructor(TypeBuilder baseTB)
+ {
+ ConstructorBuilder cb = baseTB.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, EMPTY_TYPE_ARRAY);
+ ILGenerator gen = cb.GetILGenerator();
+ // Call base constructor
+ if (_superName != null)
+ {
+ Type parentType = Type.GetType(_superName);
+ ConstructorInfo cInfo = parentType.GetConstructor(EMPTY_TYPE_ARRAY);
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Call, cInfo);
+ }
+ else if (IsVariadic)
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldc_I4, _variadicMethod.RequiredArity);
+ gen.Emit(OpCodes.Call, RestFn_Int_Ctor);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Call, AFunction_Default_Ctor);
+ }
+ gen.Emit(OpCodes.Ret);
+ }
+
+
+ #endregion
+
+ #region Function class construction
+
+ private TypeBuilder GenerateFnClass(GenContext context, Type baseType)
+ {
+ TypeBuilder fnTB = context.ModuleBldr.DefineType(_internalName, TypeAttributes.Class | TypeAttributes.Public, baseType);
+ _typeBuilder = fnTB;
+ //_thisParam = Expression.Parameter(_baseType, _thisName);
+
+ GenerateStaticConstructor(fnTB, baseType);
+ _ctorInfo = GenerateConstructor(fnTB, baseType);
+
+ GenContext newContext = CreateContext(context, fnTB, baseType);
+ GenerateMethods(newContext);
+
+ return fnTB;
+ }
+
+ private void GenerateStaticConstructor(TypeBuilder fnTB, Type baseType)
+ {
+ if (_constants.count() > 0)
+ {
+ MethodBuilder method = GenerateConstants(fnTB,baseType);
+ ConstructorBuilder cb = fnTB.DefineConstructor(MethodAttributes.Static, CallingConventions.Standard, EMPTY_TYPE_ARRAY);
+ ILGenerator gen = cb.GetILGenerator();
+ gen.Emit(OpCodes.Call, method);
+ gen.Emit(OpCodes.Ret);
+
+ }
+ }
+
+ private Expression GenerateListAsObjectArray(object value)
+ {
+ List<Expression> items = new List<Expression>();
+ foreach ( Object item in (ICollection)value )
+ items.Add(Compiler.MaybeBox(GenerateValue(item)));
+
+ return Expression.NewArrayInit(typeof(object), items);
+ }
+
+ private Expression GenerateValue(object value)
+ {
+ bool partial = true;
+ Expression ret;
+
+ if (value is String)
+ ret = Expression.Constant((String)value);
+ else if (Util.IsPrimitive(value.GetType()) ) // or just IsNumeric?
+ ret = Expression.Constant(value);
+ else if ( value is Type )
+ ret = Expression.Call(
+ null,
+ Compiler.Method_RT_classForName,
+ Expression.Constant(((Type)value).FullName));
+ else if (value is Symbol) {
+ Symbol sym = (Symbol) value;
+ ret = Expression.Call(
+ null,
+ Compiler.Method_Symbol_create2,
+ Expression.Convert(Expression.Constant(sym.Namespace),typeof(string)), // can be null
+ Expression.Constant(sym.Name));
+ }
+ else if (value is Keyword)
+ ret = Expression.Call(
+ null,
+ Compiler.Method_Keyword_intern,
+ GenerateValue(((Keyword)value).Symbol));
+ else if (value is Var) {
+ Var var = (Var) value;
+ ret = Expression.Call(
+ null,
+ Compiler.Method_RT_var2,
+ Expression.Constant(var.Namespace.Name.ToString()),
+ Expression.Constant(var.Symbol.Name.ToString()));
+
+ }
+ else if (value is IPersistentMap) {
+ IPersistentMap map = (IPersistentMap)value;
+ List<object> entries = new List<object>(map.count()*2);
+ foreach ( IMapEntry entry in map ) {
+ entries.Add(entry.key());
+ entries.Add(entry.val());
+ }
+ Expression expr = GenerateListAsObjectArray(entries);
+ ret = Expression.Call(
+ null,
+ Compiler.Method_RT_map,
+ expr);
+ }
+ else if (value is IPersistentVector) {
+ Expression expr = GenerateListAsObjectArray(value);
+ ret = Expression.Call(
+ null,
+ Compiler.Method_RT_vector,
+ expr);
+ }
+ else if (value is ISeq || value is IPersistentList) {
+ Expression expr = GenerateListAsObjectArray(value);
+ ret = Expression.Call(
+ null,
+ Compiler.Method_PersistentList_create,
+ expr);
+ }
+ else {
+ string cs = null;
+ try
+ {
+ cs = RT.printString(value);
+ }
+ catch (Exception)
+ {
+ throw new Exception(String.Format("Can't embed object in code, maybe print-dup not defined: {0}", value));
+ }
+ if (cs.Length == 0)
+ throw new Exception(String.Format("Can't embed unreadable object in code: " + value));
+ if (cs.StartsWith("#<"))
+ throw new Exception(String.Format("Can't embed unreadable object in code: " + cs));
+
+ ret = Expression.Call(Compiler.Method_RT_readString, Expression.Constant(cs));
+ partial = false;
+ }
+
+ if (partial) {
+ if (value is Obj && RT.count(((Obj)value).meta()) > 0) {
+ Expression objExpr = Expression.Convert(ret,typeof(Obj));
+ Expression metaExpr = Expression.Convert(GenerateValue(((Obj)value).meta()),typeof(IPersistentMap));
+ ret = Expression.Call(
+ objExpr,
+ Compiler.Method_IObj_withMeta,
+ metaExpr);
+ }
+ }
+ return ret;
+ }
+
+ private MethodBuilder GenerateConstants(TypeBuilder fnTB, Type baseType)
+ {
+ try
+ {
+ Var.pushThreadBindings(RT.map(RT.PRINT_DUP, RT.T));
+
+ List<Expression> inits = new List<Expression>();
+ for (int i = 0; i < _constants.count(); i++)
+ {
+ Expression expr = GenerateValue(_constants.nth(i));
+ Expression init =
+ Expression.Assign(
+ Expression.Field(null, baseType, ConstantName(i)),
+ Expression.Convert(expr,ConstantType(i)));
+ inits.Add(init);
+ }
+ inits.Add(Expression.Default(typeof(void)));
+
+ Expression block = Expression.Block(inits);
+ LambdaExpression lambda = Expression.Lambda(block);
+ MethodBuilder methodBuilder = fnTB.DefineMethod(STATIC_CTOR_HELPER_NAME, MethodAttributes.Private | MethodAttributes.Static);
+ lambda.CompileToMethod(methodBuilder);
+ return methodBuilder;
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+
+ }
+
+
+ static readonly string STATIC_CTOR_HELPER_NAME = "__static_ctor_helper";
+
+ //private MethodBuilder GenerateConstants(TypeBuilder fnTB, Type baseType)
+ //{
+ // try
+ // {
+ // Var.pushThreadBindings(RT.map(RT.PRINT_DUP, RT.T));
+
+ // List<Expression> inits = new List<Expression>();
+ // for (int i = 0; i < _constants.count(); i++)
+ // {
+ // object o = _constants.nth(i);
+ // string stringValue = null;
+ // if (o is string)
+ // stringValue = (string)o;
+ // else
+ // {
+ // try
+ // {
+ // stringValue = RT.printString(o);
+ // }
+ // catch (Exception)
+ // {
+ // throw new Exception(String.Format("Can't embed object in code, maybe print-dup not defined: {0}", o));
+ // }
+ // if (stringValue.Length == 0)
+ // throw new Exception(String.Format("Can't embed unreadable object in code: " + o));
+ // if (stringValue.StartsWith("#<"))
+ // throw new Exception(String.Format("Can't embed unreadable object in code: " + stringValue));
+ // }
+ // Expression init =
+ // Expression.Assign(
+ // Expression.Field(null, baseType, ConstantName(i)),
+ // Expression.Convert(Expression.Call(Compiler.Method_RT_readString, Expression.Constant(stringValue)),
+ // ConstantType(i)));
+ // inits.Add(init);
+ // }
+ // inits.Add(Expression.Default(typeof(void)));
+
+ // Expression block = Expression.Block(inits);
+ // LambdaExpression lambda = Expression.Lambda(block);
+ // MethodBuilder methodBuilder = fnTB.DefineMethod(STATIC_CTOR_HELPER_NAME, MethodAttributes.Private | MethodAttributes.Static);
+ // lambda.CompileToMethod(methodBuilder);
+ // return methodBuilder;
+ // }
+ // finally
+ // {
+ // Var.popThreadBindings();
+ // }
+
+ //}
+
+ private ConstructorBuilder GenerateConstructor(TypeBuilder fnTB, Type baseType)
+ {
+ ConstructorBuilder cb = fnTB.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, CtorTypes());
+ ILGenerator gen = cb.GetILGenerator();
+ //Call base constructor
+ ConstructorInfo baseCtorInfo = baseType.GetConstructor(EMPTY_TYPE_ARRAY);
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Call, baseCtorInfo);
+
+ int a = 0;
+ for (ISeq s = RT.keys(_closes); s != null; s = s.next(), a++)
+ {
+ LocalBinding lb = (LocalBinding)s.first();
+ FieldBuilder fb = _closedOverFields[a];
+
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldarg, a + 1);
+ gen.Emit(OpCodes.Stfld, fb);
+ }
+ gen.Emit(OpCodes.Ret);
+ return cb;
+ }
+
+ private Type[] CtorTypes()
+ {
+ if (_closes.count() == 0)
+ return EMPTY_TYPE_ARRAY;
+
+ Type[] ret = new Type[_closes.count()];
+ int i = 0;
+ for (ISeq s = RT.keys(_closes); s != null; s = s.next(), i++)
+ {
+ LocalBinding lb = (LocalBinding)s.first();
+ ret[i] = lb.PrimitiveType ?? typeof(object);
+ }
+ return ret;
+ }
+
+ private void GenerateMethods(GenContext context)
+ {
+ for (ISeq s = RT.seq(_methods); s != null; s = s.next())
+ {
+ FnMethod method = (FnMethod)s.first();
+ method.GenerateCode(context);
+ }
+ }
+
+ #endregion
+
+ private GenContext CreateContext(GenContext incomingContext,TypeBuilder fnTB,Type baseType)
+ {
+ return incomingContext.CreateWithNewType(this);
+ }
+
+ private Type GetSuperType()
+ {
+ return _superName != null
+ ? Type.GetType(_superName)
+ : IsVariadic
+ ? typeof(RestFn)
+ : typeof(AFunction);
+ }
+
+ #endregion
+
+ #region Code generation support
+
+
+ internal Expression GenConstant(GenContext context, int id, object val)
+ {
+ switch (context.Mode)
+ {
+ case CompilerMode.Immediate:
+ return Expression.Constant(val);
+ case CompilerMode.File:
+ return Expression.Field(null, _baseType, ConstantName(id));
+ default:
+ throw Util.UnreachableCode();
+ }
+ }
+
+ internal Expression GenVar(GenContext context, Var var)
+ {
+ int i = (int)_vars.valAt(var);
+ return GenConstant(context,i,var);
+ }
+
+ internal Expression GenKeyword(GenContext context, Keyword kw)
+ {
+ int i = (int)_keywords.valAt(kw);
+ return GenConstant(context,i,kw);
+ }
+
+
+ internal Expression GenLetFnInits(GenContext context, ParameterExpression parm ,FnExpr fn, IPersistentSet leFnLocals)
+ {
+ // fn is the enclosing IFn, not this.
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/FnMethod.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/FnMethod.cs new file mode 100644 index 00000000..aabeebfe --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/FnMethod.cs @@ -0,0 +1,347 @@ +/**
+ * 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.Emit;
+using System.Reflection;
+using Microsoft.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class FnMethod
+ {
+
+ #region Data
+
+ // Java: when closures are defined inside other closures,
+ // the closed over locals need to be propagated to the enclosing fn
+ readonly FnMethod _parent;
+ internal FnMethod Parent
+ {
+ get { return _parent; }
+ }
+
+
+ IPersistentMap _locals = null; // localbinding => localbinding
+ public IPersistentMap Locals
+ {
+ get { return _locals; }
+ set { _locals = value; }
+ }
+
+ IPersistentMap _indexLocals = null; // num -> localbinding
+ public IPersistentMap IndexLocals
+ {
+ get { return _indexLocals; }
+ set { _indexLocals = value; }
+ }
+
+ IPersistentVector _reqParms = PersistentVector.EMPTY; // localbinding => localbinding
+
+ LocalBinding _restParm = null;
+
+ Expr _body = null;
+
+ FnExpr _fn;
+ internal FnExpr Fn
+ {
+ get { return _fn; }
+ set { _fn = value; }
+ }
+
+ IPersistentVector _argLocals;
+
+ int _maxLocal = 0;
+ public int MaxLocal
+ {
+ get { return _maxLocal; }
+ set { _maxLocal = value; }
+ }
+
+ LocalBinding _thisBinding;
+
+ // int line;
+
+ IPersistentSet _localsUsedInCatchFinally = PersistentHashSet.EMPTY;
+ public IPersistentSet LocalsUsedInCatchFinally
+ {
+ get { return _localsUsedInCatchFinally; }
+ set { _localsUsedInCatchFinally = value; }
+ }
+
+ internal bool IsVariadic
+ {
+ get { return _restParm != null; }
+ }
+
+
+ internal int NumParams
+ {
+ get { return _reqParms.count() + (IsVariadic ? 1 : 0); }
+ }
+
+ internal int RequiredArity
+ {
+ get { return _reqParms.count(); }
+ }
+
+ #endregion
+
+ #region C-tors
+
+ public FnMethod(FnExpr fn, FnMethod parent)
+ {
+ _parent = parent;
+ _fn = fn;
+ }
+
+ #endregion
+
+ #region Parsing
+
+ enum ParamParseState { Required, Rest, Done };
+
+ internal static FnMethod Parse(FnExpr fn, ISeq form)
+ {
+ // ([args] body ... )
+
+ IPersistentVector parms = (IPersistentVector)RT.first(form);
+ ISeq body = RT.next(form);
+
+ try
+ {
+ FnMethod method = new FnMethod(fn, (FnMethod)Compiler.METHODS.deref());
+ // TODO: method.line = (Integer) LINE.deref();
+
+
+ Var.pushThreadBindings(RT.map(
+ Compiler.METHODS, method,
+ Compiler.LOCAL_ENV, Compiler.LOCAL_ENV.deref(),
+ Compiler.LOOP_LOCALS, null,
+ Compiler.NEXT_LOCAL_NUM, 0));
+
+ // register 'this' as local 0
+ method._thisBinding = Compiler.RegisterLocal(Symbol.intern(fn.ThisName ?? "fn__" + RT.nextID()), null, null);
+
+ ParamParseState paramState = ParamParseState.Required;
+ IPersistentVector argLocals = PersistentVector.EMPTY;
+ int parmsCount = parms.count();
+
+ for (int i = 0; i < parmsCount; i++)
+ {
+ if (!(parms.nth(i) is Symbol))
+ throw new ArgumentException("fn params must be Symbols");
+ Symbol p = (Symbol)parms.nth(i);
+ 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 = Compiler.RegisterLocal(p,
+ paramState == ParamParseState.Rest ? Compiler.ISEQ : Compiler.TagOf(p),
+ null); // asdf-tag
+
+ 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 > Compiler.MAX_POSITIONAL_ARITY)
+ throw new Exception(string.Format("Can't specify more than {0} parameters", Compiler.MAX_POSITIONAL_ARITY));
+ Compiler.LOOP_LOCALS.set(argLocals);
+ method._argLocals = argLocals;
+ method._body = (new BodyExpr.Parser()).Parse(body);
+ return method;
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ internal void GenerateCode(GenContext context)
+ {
+ MethodBuilder mb = GenerateStaticMethod(context);
+ GenerateMethod(mb, context);
+ }
+
+ void GenerateMethod(MethodInfo staticMethodInfo, GenContext context)
+ {
+ string methodName = IsVariadic ? "doInvoke" : "invoke";
+
+ TypeBuilder tb = context.FnExpr.TypeBuilder;
+
+ // TODO: Cache all the CreateObjectTypeArray values
+ MethodBuilder mb = tb.DefineMethod(methodName, MethodAttributes.ReuseSlot | MethodAttributes.Public | MethodAttributes.Virtual, typeof(object), Compiler.CreateObjectTypeArray(NumParams));
+ ILGenerator gen = mb.GetILGenerator();
+ gen.Emit(OpCodes.Ldarg_0);
+ for (int i = 1; i <= _argLocals.count(); i++)
+ gen.Emit(OpCodes.Ldarg, i);
+ gen.Emit(OpCodes.Call, staticMethodInfo);
+ gen.Emit(OpCodes.Ret);
+ }
+
+ MethodBuilder GenerateStaticMethod(GenContext context)
+ {
+ string methodName = GetStaticMethodName();
+ FnExpr fn = context.FnExpr;
+ TypeBuilder tb = fn.TypeBuilder;
+
+ List<ParameterExpression> parms = new List<ParameterExpression>(_argLocals.count() + 1);
+
+ ParameterExpression thisParm = Expression.Parameter(fn.BaseType, "this");
+ _thisBinding.ParamExpression = thisParm;
+ fn.ThisParam = thisParm;
+ parms.Add(thisParm);
+
+ try
+ {
+ LabelTarget loopLabel = Expression.Label("top");
+
+ Var.pushThreadBindings(RT.map(Compiler.LOOP_LABEL, loopLabel, Compiler.METHODS, this));
+
+
+
+ for (int i = 0; i < _argLocals.count(); i++)
+ {
+ LocalBinding lb = (LocalBinding)_argLocals.nth(i);
+ ParameterExpression parm = Expression.Parameter(typeof(object), lb.Name);
+ lb.ParamExpression = parm;
+ parms.Add(parm);
+ }
+
+ Expression body =
+ Expression.Block(
+ Expression.Label(loopLabel),
+ Compiler.MaybeBox(_body.GenDlr(context)));
+ LambdaExpression lambda = Expression.Lambda(body, parms);
+ // TODO: Figure out why the Java code nulls all the local variables here.
+
+
+ // TODO: Cache all the CreateObjectTypeArray values
+ MethodBuilder mb = tb.DefineMethod(methodName, MethodAttributes.Static, typeof(object), Compiler.CreateObjectTypeArray(NumParams));
+
+ lambda.CompileToMethod(mb);
+ //lambda.CompileToMethod(mb, true);
+ return mb;
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+
+ }
+
+ private string GetStaticMethodName()
+ {
+ return String.Format("__invokeHelper_{0}{1}", RequiredArity, IsVariadic ? "v" : string.Empty);
+ }
+
+ #endregion
+
+ internal LambdaExpression GenerateImmediateLambda(GenContext context)
+ {
+ List<ParameterExpression> parmExprs = new List<ParameterExpression>(_argLocals.count());
+ List<ParameterExpression> typedParmExprs = new List<ParameterExpression>();
+ List<Expression> typedParmInitExprs = new List<Expression>();
+
+ //FnExpr fn = context.FnExpr;
+ //ParameterExpression thisParm = Expression.Parameter(fn.BaseType, "this");
+ //_thisBinding.ParamExpression = thisParm;
+ //fn.ThisParam = thisParm;
+ FnExpr fn = context.FnExpr;
+ _thisBinding.ParamExpression = fn.ThisParam;
+
+ try
+ {
+
+ LabelTarget loopLabel = Expression.Label("top");
+
+ Var.pushThreadBindings(RT.map(Compiler.LOOP_LABEL, loopLabel, Compiler.METHODS, this));
+
+ 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 = Compiler.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(Compiler.MaybeBox(_body.GenDlr(context)));
+
+
+ Expression block;
+ if (typedParmExprs.Count > 0)
+ block = Expression.Block(typedParmExprs, bodyExprs);
+ else
+ block = Expression.Block(bodyExprs);
+
+ return Expression.Lambda(
+ FuncTypeHelpers.GetFFuncType(parmExprs.Count),
+ block,
+ _fn.ThisName,
+ parmExprs);
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/GenContext.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/GenContext.cs new file mode 100644 index 00000000..1812a34d --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/GenContext.cs @@ -0,0 +1,115 @@ +/**
+ * 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.Emit;
+using System.Reflection;
+using Microsoft.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+
+ enum CompilerMode { Immediate, File };
+
+ class GenContext
+ {
+ #region Data
+
+ readonly CompilerMode _mode;
+
+ internal CompilerMode Mode
+ {
+ get { return _mode; }
+ }
+
+
+
+ readonly AssemblyBuilder _assyBldr;
+ public AssemblyBuilder AssyBldr
+ {
+ get { return _assyBldr; }
+ //set { _ab = value; }
+ }
+
+ readonly ModuleBuilder _moduleBldr;
+ public ModuleBuilder ModuleBldr
+ {
+ get { return _moduleBldr; }
+ //set { _moduleBldr = value; }
+ }
+
+ //TypeBuilder _typeBldr;
+ //public TypeBuilder TypeBldr
+ //{
+ // get { return _typeBldr; }
+ // //set { _tb = value; }
+ //}
+
+ FnExpr _fnExpr = null;
+ internal FnExpr FnExpr
+ {
+ get { return _fnExpr; }
+ //set { _fnExpr = value; }
+ }
+
+ //Type _baseType;
+ //public Type BaseType
+ //{
+ // get { return _baseType; }
+ // //set { _baseType = value; }
+ //}
+
+ //ParameterExpression _thisFn;
+ //public ParameterExpression ThisFn
+ //{
+ // get { return _thisFn; }
+ // //set { _thisFn = value; }
+ //}
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ public GenContext(string assyName, CompilerMode mode)
+ : this(assyName,null,mode)
+ {
+ }
+
+ public GenContext(string assyName, string directory, CompilerMode mode)
+ {
+ AssemblyName aname = new AssemblyName(assyName);
+ _assyBldr = AppDomain.CurrentDomain.DefineDynamicAssembly(aname, AssemblyBuilderAccess.RunAndSave,directory);
+ _moduleBldr = _assyBldr.DefineDynamicModule(aname.Name, aname.Name + ".dll", true);
+ _mode = mode;
+ }
+
+ private GenContext(CompilerMode mode)
+ {
+ _mode = mode;
+ }
+
+ public GenContext CreateWithNewType(FnExpr fnExpr)
+ {
+ GenContext newContext = Clone();
+ newContext._fnExpr = fnExpr;
+ return newContext;
+ }
+
+ private GenContext Clone()
+ {
+ return (GenContext) this.MemberwiseClone();
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/HostExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/HostExpr.cs new file mode 100644 index 00000000..e9758803 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/HostExpr.cs @@ -0,0 +1,248 @@ +/**
+ * 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 Microsoft.Linq.Expressions;
+using clojure.runtime;
+using System.IO;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ abstract class HostExpr : Expr, MaybePrimitiveExpr
+ {
+ #region Parsing
+
+ public sealed class Parser : IParser
+ {
+ public Expr Parse(object frm)
+ {
+ ISeq form = (ISeq)frm;
+
+ // form is one of:
+ // (. x fieldname-sym)
+ // (. x 0-ary-method)
+ // (. x propertyname-sym)
+ // (. x methodname-sym args+)
+ // (. x (methodname-sym args?))
+
+ if (RT.Length(form) < 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 = Compiler.MaybeType(RT.second(form), false);
+ // at this point, t will be non-null if static
+
+ Expr instance = null;
+ if (t == null)
+ instance = Compiler.GenerateAST(RT.second(form));
+
+ bool isFieldOrProperty = false;
+
+ if (RT.Length(form) == 3 && RT.third(form) is Symbol)
+ {
+ Symbol sym = (Symbol)RT.third(form);
+ if (t != null)
+ isFieldOrProperty =
+ t.GetField(sym.Name, BindingFlags.Static | BindingFlags.Public) != null
+ || t.GetProperty(sym.Name, BindingFlags.Static | BindingFlags.Public) != null;
+ else if (instance != null && instance.HasClrType && instance.ClrType != null)
+ {
+ Type instanceType = instance.ClrType;
+ isFieldOrProperty =
+ instanceType.GetField(sym.Name, BindingFlags.Instance | BindingFlags.Public) != null
+ || instanceType.GetProperty(sym.Name, BindingFlags.Instance | BindingFlags.Public) != null;
+ }
+ }
+
+ if (isFieldOrProperty)
+ {
+ Symbol sym = (Symbol)RT.third(form);
+ if (t != null)
+ return new StaticFieldExpr(t, sym.Name);
+ else
+ return new InstanceFieldExpr(instance, sym.Name);
+ }
+
+
+ ISeq call = RT.third(form) is ISeq ? (ISeq)RT.third(form) : RT.next(RT.next(form));
+
+ if (!(RT.first(call) is Symbol))
+ throw new ArgumentException("Malformed member exception");
+
+ string methodName = ((Symbol)RT.first(call)).Name;
+ IPersistentVector args = PersistentVector.EMPTY;
+
+ for (ISeq s = RT.next(call); s != null; s = s.next())
+ args = args.cons(Compiler.GenerateAST(s.first()));
+
+ return t != null
+ ? (MethodExpr)(new StaticMethodExpr(t, methodName, args))
+ : (MethodExpr)(new InstanceMethodExpr(instance, methodName, args));
+ }
+ }
+
+ public abstract Expression GenDlrUnboxed(GenContext context);
+
+
+ protected static List<MethodInfo> GetMethods(Type targetType, int arity, string methodName, bool getStatics)
+ {
+ BindingFlags flags = BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.InvokeMethod;
+
+ flags |= getStatics ? BindingFlags.Static : BindingFlags.Instance;
+
+ IEnumerable<MethodInfo> einfo
+ = targetType.GetMethods(flags).Where(info => info.Name == methodName && info.GetParameters().Length == arity);
+ List<MethodInfo> infos = new List<MethodInfo>(einfo);
+
+ return infos;
+ }
+
+
+ protected static MethodInfo GetMatchingMethod(Type targetType, IPersistentVector args, string methodName)
+ {
+ MethodInfo method = GetMatchingMethodAux(targetType, args, methodName, true);
+
+ MaybeReflectionWarn(method, methodName);
+
+ return method;
+ }
+
+ protected static MethodInfo GetMatchingMethod(Expr target, IPersistentVector args, string methodName)
+ {
+ MethodInfo method = target.HasClrType ? GetMatchingMethodAux(target.ClrType, args, methodName, false) : null;
+
+ MaybeReflectionWarn(method, methodName);
+
+ return method;
+ }
+
+ private static void MaybeReflectionWarn(MethodInfo method, string methodName)
+ {
+ if ( method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()) )
+ // TODO: use DLR IO
+ ((TextWriter)RT.ERR.deref()).WriteLine(string.Format("Reflection warning, {0}:{1} - call to {2} can't be resolved.\n",
+ Compiler.SOURCE_PATH.deref(), /* line ,*/0, methodName));
+ }
+
+ private static MethodInfo GetMatchingMethodAux(Type targetType, IPersistentVector args, string methodName, bool getStatics)
+ {
+ MethodInfo method = null;
+
+ List<MethodInfo> methods = HostExpr.GetMethods(targetType, args.count(), methodName, getStatics);
+
+ if (methods.Count == 0)
+ method = null;
+ else
+ {
+
+ int index = 0;
+ if (methods.Count > 1)
+ {
+ List<ParameterInfo[]> parms = new List<ParameterInfo[]>(methods.Count);
+ List<Type> rets = new List<Type>(methods.Count);
+
+ foreach (MethodInfo mi in methods)
+ {
+ parms.Add(mi.GetParameters());
+ rets.Add(mi.ReturnType);
+ }
+ index = GetMatchingParams(methodName, parms, args, rets);
+ }
+ method = (index >= 0 ? methods[index] : null);
+ }
+
+ return method;
+ }
+
+
+
+
+
+ internal static int GetMatchingParams(string methodName, List<ParameterInfo[]> parmlists, IPersistentVector argexprs, List<Type> rets)
+ {
+ // Assume matching lengths
+ int matchIndex = -1;
+ bool tied = false;
+ bool foundExact = false;
+
+ for (int i = 0; i < parmlists.Count; i++)
+ {
+ bool match = true;
+ ISeq aseq = argexprs.seq();
+ int exact = 0;
+ for (int p = 0; match && p < argexprs.count() && aseq != null; ++p, aseq = aseq.next())
+ {
+ Expr arg = (Expr)aseq.first();
+ Type atype = arg.HasClrType ? arg.ClrType : typeof(object);
+ Type ptype = parmlists[i][p].ParameterType;
+ if (arg.HasClrType && atype == ptype)
+ exact++;
+ else
+ match = Reflector.ParamArgTypeMatch(ptype, atype);
+ }
+
+ if (exact == argexprs.count())
+ {
+ if ( !foundExact || matchIndex == -1 || rets[matchIndex].IsAssignableFrom(rets[i]))
+ matchIndex = i;
+ foundExact = true;
+ }
+ else if (match && !foundExact)
+ {
+ if (matchIndex == -1)
+ matchIndex = i;
+ else
+ {
+ if (Reflector.Subsumes(parmlists[i], parmlists[matchIndex]))
+ {
+ matchIndex = i;
+ tied = false;
+ }
+ else if (Array.Equals(parmlists[i], parmlists[matchIndex]))
+ if (rets[matchIndex].IsAssignableFrom(rets[i]))
+ matchIndex = i;
+ else if (!Reflector.Subsumes(parmlists[matchIndex], parmlists[i]))
+ tied = true;
+ }
+ }
+ }
+
+ if (tied)
+ throw new ArgumentException("More than one matching method found: " + methodName);
+
+ return matchIndex;
+ }
+
+ internal static Expression[] GenTypedArgs(GenContext context, ParameterInfo[] parms, IPersistentVector args)
+ {
+ Expression[] exprs = new Expression[parms.Length];
+ for (int i = 0; i < parms.Length; i++)
+ exprs[i] = GenTypedArg(context,parms[i].ParameterType, (Expr)args.nth(i));
+ return exprs;
+ }
+
+ internal static Expression GenTypedArg(GenContext context, Type type, Expr arg)
+ {
+ if (Compiler.MaybePrimitiveType(arg) == type)
+ return ((MaybePrimitiveExpr)arg).GenDlrUnboxed(context);
+ else
+ // Java has emitUnboxArg -- should we do something similar?
+ return arg.GenDlr(context);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/IParser.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/IParser.cs new file mode 100644 index 00000000..4723388d --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/IParser.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.CljCompiler.Ast
+{
+ interface IParser
+ {
+ Expr Parse(object form);
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/IfExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/IfExpr.cs new file mode 100644 index 00000000..ab92a959 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/IfExpr.cs @@ -0,0 +1,193 @@ +/**
+ * 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 clojure.runtime;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class IfExpr : Expr
+ {
+ #region Data
+
+ readonly Expr _testExpr;
+ readonly Expr _thenExpr;
+ readonly Expr _elseExpr;
+
+ #endregion
+
+ #region Ctors
+
+ public IfExpr(Expr testExpr, Expr thenExpr, Expr elseExpr)
+ {
+ _testExpr = testExpr;
+ _thenExpr = thenExpr;
+ _elseExpr = elseExpr;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get
+ {
+ if (_elseExpr == null)
+ return _thenExpr.HasClrType;
+ else
+ return _thenExpr.HasClrType
+ && _elseExpr.HasClrType
+ && (_thenExpr.ClrType == _elseExpr.ClrType
+ || _thenExpr.ClrType == null
+ || _elseExpr.ClrType == null);
+ }
+ }
+
+ public override Type ClrType
+ {
+ get
+ {
+ Type thenType = _thenExpr.ClrType;
+
+ if (_elseExpr == null)
+ return thenType;
+ else
+ return thenType ?? _elseExpr.ClrType;
+ }
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public sealed class Parser : IParser
+ {
+ public Expr Parse(object frm)
+ {
+ ISeq form = (ISeq)frm;
+
+ // (if test then) or (if test then else)
+
+ if (form.count() > 4)
+ throw new Exception("Too many arguments to if");
+
+ if (form.count() < 3)
+ throw new Exception("Too few arguments to if");
+
+
+ Expr testExpr = Compiler.GenerateAST(RT.second(form));
+ Expr thenExpr = Compiler.GenerateAST(RT.third(form));
+ Expr elseExpr = form.count() == 4 ? Compiler.GenerateAST(RT.fourth(form)) : null;
+
+ return new IfExpr(testExpr, thenExpr, elseExpr);
+ }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ // Original code made a call to RT.IsTrue.
+ // Now we inline the test.
+ // Not clear if there is much speedup from this.
+
+ //bool testIsBool = _testExpr is MaybePrimitiveExpr && _testExpr.HasClrType && _testExpr.ClrType == typeof(bool);
+
+ //Expression testCode = testIsBool
+ // ? ((MaybePrimitiveExpr)_testExpr).GenDlrUnboxed(context)
+ // // TODO: Verify the call to MaybeBox is needed.
+ // // TODO: See if we can write the code more directly than calling RT.IsTrue.
+ // : Expression.Call(Compiler.Method_RT_IsTrue, Compiler.MaybeBox(_testExpr.GenDlr(context)));
+
+
+ bool testIsBool = _testExpr is MaybePrimitiveExpr && _testExpr.HasClrType && _testExpr.ClrType == typeof(bool);
+
+ Expression testCode;
+
+
+ if (testIsBool)
+ testCode = ((MaybePrimitiveExpr)_testExpr).GenDlrUnboxed(context);
+ else
+ {
+ ParameterExpression testVar = Expression.Parameter(typeof(object), "__test");
+ Expression assign = Expression.Assign(testVar, Compiler.MaybeBox(_testExpr.GenDlr(context)));
+ Expression boolExpr =
+ Expression.Not(
+ Expression.OrElse(
+ Expression.Equal(testVar, Expression.Constant(null)),
+ Expression.AndAlso(Expression.TypeIs(testVar, typeof(bool)), Expression.IsFalse(Expression.Unbox(testVar, typeof(bool))))));
+ //Expression.Not(Expression.AndAlso(Expression.TypeIs(testVar, typeof(bool)), Expression.IsFalse(Expression.Convert(testVar,typeof(bool))))));
+ testCode = Expression.Block(typeof(bool), new ParameterExpression[] { testVar }, assign, boolExpr);
+ }
+
+ Expression thenCode = _thenExpr.GenDlr(context);
+ Expression elseCode = _elseExpr == null
+ ? Expression.Constant(null, typeof(object))
+ : _elseExpr.GenDlr(context);
+
+ Type targetType = typeof(object);
+ if (this.HasClrType && this.ClrType != null)
+ // In this case, both _thenExpr and _elseExpr have types, and they are the same, or one is null.
+ // TODO: Not sure if this works if one has a null value.
+ targetType = this.ClrType;
+
+ if (thenCode.Type == typeof(void) && elseCode.Type != typeof(void))
+ thenCode = Expression.Block(thenCode, Expression.Default(elseCode.Type));
+ else if (elseCode.Type == typeof(void) && thenCode.Type != typeof(void))
+ elseCode = Expression.Block(elseCode, Expression.Default(thenCode.Type));
+ else if (!Reflector.AreReferenceAssignable(targetType, thenCode.Type) || !Reflector.AreReferenceAssignable(targetType, elseCode.Type))
+ // Above: this is the test that Expression.Condition does.
+ {
+ // Try to reconcile
+ if (thenCode.Type.IsAssignableFrom(elseCode.Type) && elseCode.Type != typeof(void))
+ {
+ elseCode = Expression.Convert(elseCode, thenCode.Type);
+ targetType = thenCode.Type;
+ }
+ else if (elseCode.Type.IsAssignableFrom(thenCode.Type) && thenCode.Type != typeof(void))
+ {
+ thenCode = Expression.Convert(thenCode, elseCode.Type);
+ targetType = elseCode.Type;
+ }
+ else
+ {
+ //if (thenCode.Type == typeof(void))
+ //{
+ // thenCode = Expression.Block(thenCode, Expression.Default(elseCode.Type));
+ // targetType = elseCode.Type;
+ //}
+ //else if (elseCode.Type == typeof(void))
+ //{
+ // elseCode = Expression.Block(elseCode, Expression.Default(thenCode.Type));
+ // targetType = thenCode.Type;
+ //}
+ //else
+ //{
+ // TODO: Can we find a common ancestor? probably not.
+ thenCode = Expression.Convert(thenCode, typeof(object));
+ elseCode = Expression.Convert(elseCode, typeof(object));
+ targetType = typeof(object);
+ //}
+ }
+ }
+
+ return Expression.Condition(testCode, thenCode, elseCode, targetType);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/ImportExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/ImportExpr.cs new file mode 100644 index 00000000..50fe73ed --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/ImportExpr.cs @@ -0,0 +1,76 @@ +/**
+ * 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;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class ImportExpr : Expr
+ {
+ #region Data
+
+ readonly string _c;
+
+ #endregion
+
+ #region Ctors
+
+ public ImportExpr(string c)
+ {
+ _c = c;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return false; }
+ }
+
+ public override Type ClrType
+ {
+ get { throw new ArgumentException("ImportExpr has no Java class"); }
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public sealed class Parser : IParser
+ {
+ public Expr Parse(object frm)
+ {
+ return new ImportExpr((string)RT.second(frm));
+ }
+ }
+
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ Expression getTypeExpr = Expression.Call(null, Compiler.Method_RT_classForName, Expression.Constant(_c));
+ //Expression getNsExpr = Expression.Call(null, Compiler.Method_Compiler_CurrentNamespace);
+ Expression getNsExpr = Expression.Property(null, Compiler.Method_Compiler_CurrentNamespace);
+ return Expression.Call(getNsExpr, Compiler.Method_Namespace_importClass1, getTypeExpr);
+ }
+
+ #endregion
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/InstanceFieldExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/InstanceFieldExpr.cs new file mode 100644 index 00000000..b50f7356 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/InstanceFieldExpr.cs @@ -0,0 +1,133 @@ +/**
+ * 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.IO;
+using Microsoft.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class InstanceFieldExpr : FieldExpr
+ {
+ #region Data
+
+ readonly Expr _target;
+ readonly Type _targetType;
+ readonly FieldInfo _fieldInfo;
+ readonly PropertyInfo _propertyInfo;
+ readonly string _fieldName;
+
+ #endregion
+
+ #region Ctors
+
+ public InstanceFieldExpr(Expr target, string fieldName)
+ {
+ _target = target;
+ _fieldName = fieldName;
+
+ _targetType = target.HasClrType ? target.ClrType : null;
+ _fieldInfo = _targetType != null ? _targetType.GetField(_fieldName, BindingFlags.Instance | BindingFlags.Public) : null;
+ _propertyInfo = _targetType != null ? _targetType.GetProperty(_fieldName, BindingFlags.Instance | BindingFlags.Public) : null;
+
+ if ( _fieldInfo == null && _propertyInfo == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
+ ((TextWriter)RT.ERR.deref()).WriteLine("Reflection warning {0}:{1} - reference to field/property {2} can't be resolved.",
+ Compiler.SOURCE_PATH.deref(), /* line */ 0,_fieldName);
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return _fieldInfo != null || _propertyInfo != null; }
+ }
+
+ public override Type ClrType
+ {
+ get {
+
+ return _fieldInfo != null
+ ? _fieldInfo.FieldType
+ : _propertyInfo.PropertyType;
+ }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ Expression target = _target.GenDlr(context);
+ if (_targetType != null && (_fieldInfo != null || _propertyInfo != null))
+ {
+ Expression convTarget = Expression.Convert(target, _targetType);
+ Expression access = _fieldInfo != null
+ ? Expression.Field(convTarget, _fieldInfo)
+ : Expression.Property(convTarget, _propertyInfo);
+ return Compiler.MaybeBox(access);
+ }
+ else
+ return Compiler.MaybeBox(Expression.PropertyOrField(target,_fieldName));
+ //Or maybe this should call Reflector.invokeNoArgInstanceMember
+ }
+
+ public override Expression GenDlrUnboxed(GenContext context)
+ {
+ Expression target = _target.GenDlr(context);
+ if (_targetType != null && (_fieldInfo != null || _propertyInfo != null))
+ {
+ Expression convTarget = Expression.Convert(target, _targetType);
+ Expression access = _fieldInfo != null
+ ? Expression.Field(convTarget, _fieldInfo)
+ : Expression.Property(convTarget, _propertyInfo);
+ return access;
+ }
+ else
+ throw new InvalidOperationException("Unboxed emit of unknown member.");
+ }
+
+ #endregion
+
+ #region AssignableExpr Members
+
+ public override Expression GenAssignDlr(GenContext context, Expr val)
+ {
+ Expression target = _target.GenDlr(context);
+ Expression valExpr = val.GenDlr(context);
+ if (_targetType != null)
+ {
+ Expression convTarget = Expression.Convert(target, _targetType);
+ Expression access = _fieldInfo != null
+ ? Expression.Field(convTarget, _fieldInfo)
+ : Expression.Property(convTarget, _propertyInfo);
+ return Expression.Assign(access, valExpr);
+ }
+ else
+ {
+ // TODO: Shouldn't this cause a reflection warning?
+ Expression call = Expression.Call(
+ target,
+ Compiler.Method_Reflector_SetInstanceFieldOrProperty,
+ Expression.Constant(_fieldName),
+ valExpr);
+ return call;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/InstanceMethodExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/InstanceMethodExpr.cs new file mode 100644 index 00000000..cf0438c0 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/InstanceMethodExpr.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.Reflection;
+using Microsoft.Linq.Expressions;
+using AstUtils = Microsoft.Scripting.Ast.Utils;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class InstanceMethodExpr : MethodExpr
+ {
+ #region Data
+
+ readonly Expr _target;
+ readonly string _methodName;
+ readonly IPersistentVector _args;
+ readonly MethodInfo _method;
+
+ #endregion
+
+ #region Ctors
+
+ public InstanceMethodExpr(Expr target, string methodName, IPersistentVector args)
+ {
+ _target = target;
+ _methodName = methodName;
+ _args = args;
+
+ _method = GetMatchingMethod(target, _args, _methodName);
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return _method != null; }
+ }
+
+ public override Type ClrType
+ {
+ get { return _method.ReturnType; }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ if (_method != null)
+ return Compiler.MaybeBox(GenDlrForMethod(context));
+ else
+ return GenDlrViaReflection(context);
+ }
+
+ public override Expression GenDlrUnboxed(GenContext context)
+ {
+ if (_method != null)
+ return GenDlrForMethod(context);
+ else
+ throw new InvalidOperationException("Unboxed emit of unknown member.");
+ }
+
+ private Expression GenDlrForMethod(GenContext context)
+ {
+ Expression target = _target.GenDlr(context);
+ Expression[] args = GenTypedArgs(context, _method.GetParameters(), _args);
+
+ return AstUtils.SimpleCallHelper(target,_method, args); ;
+
+ }
+
+ private Expression GenDlrViaReflection(GenContext context)
+ {
+ Expression[] parms = new Expression[_args.count()];
+ for (int i = 0; i < _args.count(); i++)
+ parms[i] = Compiler.MaybeBox(((Expr)_args.nth(i)).GenDlr(context));
+
+ Expression[] moreArgs = new Expression[3];
+ moreArgs[0] = Expression.Constant(_methodName);
+ moreArgs[1] = _target.GenDlr(context);
+ moreArgs[2] = Expression.NewArrayInit(typeof(object), parms);
+
+ return Expression.Call(Compiler.Method_Reflector_CallInstanceMethod, moreArgs);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/InvokeExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/InvokeExpr.cs new file mode 100644 index 00000000..e1dd5b05 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/InvokeExpr.cs @@ -0,0 +1,132 @@ +/**
+ * 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;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class InvokeExpr : Expr
+ {
+ #region Data
+
+ readonly Expr _fexpr;
+ readonly Object _tag;
+ readonly IPersistentVector _args;
+
+ #endregion
+
+ #region Ctors
+
+ public InvokeExpr(Symbol tag, Expr fexpr, IPersistentVector args)
+ {
+ _fexpr = fexpr;
+ _args = args;
+ _tag = tag ?? (fexpr is VarExpr ? ((VarExpr)fexpr).Tag : null);
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return _tag != null; }
+ }
+
+ public override Type ClrType
+ {
+ get { return Compiler.TagToType(_tag); }
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public static Expr Parse(ISeq form)
+ {
+ Expr fexpr = Compiler.GenerateAST(form.first());
+ IPersistentVector args = PersistentVector.EMPTY;
+ for ( ISeq s = RT.seq(form.next()); s != null; s = s.next())
+ args = args.cons(Compiler.GenerateAST(s.first()));
+ return new InvokeExpr(Compiler.TagOf(form),fexpr,args);
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ Expression fn = _fexpr.GenDlr(context);
+ fn = Expression.Convert(fn, typeof(IFn));
+
+ int argCount = _args.count();
+
+ Expression[] args = new Expression[argCount];
+
+ for (int i = 0; i < argCount; i++ )
+ args[i] = Compiler.MaybeBox(((Expr)_args.nth(i)).GenDlr(context));
+
+ Expression call = GenerateInvocation(InvocationReturnType, fn, args);
+
+ return call;
+ }
+
+ private Type InvocationReturnType
+ {
+ get
+ {
+ return (_tag == null)
+ ? null
+ : Compiler.TagToType(_tag);
+ }
+ }
+
+ private static Expression GenerateInvocation(Type returnType, Expression fn, Expression[] args)
+ {
+ MethodInfo mi;
+ Expression[] actualArgs;
+
+ if (args.Length <= Compiler.MAX_POSITIONAL_ARITY)
+ {
+ mi = Compiler.Methods_IFn_invoke[args.Length];
+ actualArgs = args;
+ }
+ else
+ {
+ // pick up the extended version.
+ mi = Compiler.Methods_IFn_invoke[Compiler.MAX_POSITIONAL_ARITY + 1];
+ Expression[] leftoverArgs = new Expression[args.Length - Compiler.MAX_POSITIONAL_ARITY];
+ Array.ConstrainedCopy(args, Compiler.MAX_POSITIONAL_ARITY, leftoverArgs, 0, args.Length - Compiler.MAX_POSITIONAL_ARITY);
+
+ Expression restArg = Expression.NewArrayInit(typeof(object), leftoverArgs);
+
+ actualArgs = new Expression[Compiler.MAX_POSITIONAL_ARITY + 1];
+ Array.ConstrainedCopy(args, 0, actualArgs, 0, Compiler.MAX_POSITIONAL_ARITY);
+ actualArgs[Compiler.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;
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/KeywordExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/KeywordExpr.cs new file mode 100644 index 00000000..729f25bc --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/KeywordExpr.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 Microsoft.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class KeywordExpr : Expr
+ {
+ #region Data
+
+ readonly Keyword _kw;
+
+ #endregion
+
+ #region Ctors
+
+ public KeywordExpr(Keyword kw)
+ {
+ _kw = kw;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return true; }
+ }
+
+ public override Type ClrType
+ {
+ get { return typeof(Keyword); }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ return context.FnExpr.GenKeyword(context,_kw);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/LetExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/LetExpr.cs new file mode 100644 index 00000000..90ac2c89 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/LetExpr.cs @@ -0,0 +1,180 @@ +/**
+ * 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;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class LetExpr : Expr
+ {
+ #region Data
+
+ readonly IPersistentVector _bindingInits;
+ readonly Expr _body;
+ readonly bool _isLoop;
+
+ #endregion
+
+ #region Ctors
+
+ public LetExpr(IPersistentVector bindingInits, Expr body, bool isLoop)
+ {
+ _bindingInits = bindingInits;
+ _body = body;
+ _isLoop = isLoop;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return _body.HasClrType; }
+ }
+
+ public override Type ClrType
+ {
+ get { return _body.ClrType; }
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public sealed class Parser : IParser
+ {
+ public Expr Parse(object frm)
+ {
+ ISeq form = (ISeq) frm;
+
+ // form => (let [var1 val1 var2 val2 ... ] body ... )
+ // or (loop [var1 val1 var2 val2 ... ] body ... )
+
+ bool isLoop = RT.first(form).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.next(RT.next(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 tjos 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 = RT.map(
+ Compiler.LOCAL_ENV, Compiler.LOCAL_ENV.deref(),
+ Compiler.NEXT_LOCAL_NUM,Compiler.NEXT_LOCAL_NUM.deref());
+
+ if (isLoop)
+ dynamicBindings = dynamicBindings.assoc(Compiler.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);
+
+ Expr init = Compiler.GenerateAST(bindings.nth(i + 1));
+ // Sequential enhancement of env (like Lisp let*)
+ LocalBinding b = Compiler.RegisterLocal(sym, Compiler.TagOf(sym), init);
+ BindingInit bi = new BindingInit(b, init);
+ bindingInits = bindingInits.cons(bi);
+
+ if (isLoop)
+ loopLocals = loopLocals.cons(b);
+ }
+ if (isLoop)
+ Compiler.LOOP_LOCALS.set(loopLocals);
+
+ return new LetExpr(bindingInits,
+ new BodyExpr.Parser().Parse(body),
+ isLoop);
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ 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);
+ Type primType = Compiler.MaybePrimitiveType(bi.Init);
+ ParameterExpression parmExpr = Expression.Parameter(primType ?? typeof(object), bi.Binding.Name);
+ bi.Binding.ParamExpression = parmExpr;
+ parms.Add(parmExpr);
+ //forms.Add(Expression.Assign(parmExpr, Compiler.MaybeBox(bi.Init.GenDlr(context))));
+ Expression initExpr = primType != null ? ((MaybePrimitiveExpr)bi.Init).GenDlrUnboxed(context) : Compiler.MaybeBox(bi.Init.GenDlr(context));
+ forms.Add(Expression.Assign(parmExpr, initExpr));
+ }
+
+
+ forms.Add(Expression.Label(loopLabel));
+
+ try
+ {
+ if (_isLoop)
+ Var.pushThreadBindings(PersistentHashMap.create(Compiler.LOOP_LABEL, loopLabel));
+
+ forms.Add(_body.GenDlr(context));
+ }
+ finally
+ {
+ if (_isLoop)
+ Var.popThreadBindings();
+ }
+
+ Expression block = Expression.Block(parms, forms);
+ return block;
+ }
+
+
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/LetFnExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/LetFnExpr.cs new file mode 100644 index 00000000..f088d26f --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/LetFnExpr.cs @@ -0,0 +1,149 @@ +/**
+ * 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;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class LetFnExpr : Expr
+ {
+ #region Data
+
+ readonly IPersistentVector _bindingInits;
+ readonly Expr _body;
+
+ #endregion
+
+ #region Ctors
+
+ public LetFnExpr(IPersistentVector bindingInits, Expr body)
+ {
+ _bindingInits = bindingInits;
+ _body = body;
+
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return _body.HasClrType; }
+ }
+
+ public override Type ClrType
+ {
+ get { return _body.ClrType; }
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public sealed class Parser : IParser
+ {
+ public Expr Parse(object frm)
+ {
+ ISeq form = (ISeq)frm;
+
+ // form => (letfn* [var1 (fn [args] body) ... ] body ... )
+
+ 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.next(RT.next(form));
+
+ // TODO: This is one place where context makes a difference. Need to figure this out.
+ // if (ctxt == C.EVAL)
+ // return Generate(RT.list(RT.list(Compiler.FN, PersistentVector.EMPTY, form)));
+
+
+ IPersistentMap dynamicBindings = RT.map(
+ Compiler.LOCAL_ENV, Compiler.LOCAL_ENV.deref(),
+ Compiler.NEXT_LOCAL_NUM, Compiler.NEXT_LOCAL_NUM.deref());
+
+ try
+ {
+ Var.pushThreadBindings(dynamicBindings);
+
+ // pre-seed env (like Lisp labels)
+ IPersistentVector lbs = 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);
+
+ LocalBinding b = Compiler.RegisterLocal(sym, Compiler.TagOf(sym), null);
+ lbs = lbs.cons(b);
+ }
+
+ IPersistentVector bindingInits = PersistentVector.EMPTY;
+
+ for (int i = 0; i < bindings.count(); i += 2)
+ {
+ Symbol sym = (Symbol)bindings.nth(i);
+ Expr init = Compiler.GenerateAST(bindings.nth(i + 1),sym.Name);
+ // Sequential enhancement of env (like Lisp let*)
+ LocalBinding b = (LocalBinding)lbs.nth(i / 2);
+ b.Init = init;
+ BindingInit bi = new BindingInit(b, init);
+ bindingInits = bindingInits.cons(bi);
+ }
+
+ return new LetFnExpr(bindingInits,new BodyExpr.Parser().Parse(body));
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ throw new NotImplementedException();
+
+ //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);
+ // Type primType = Compiler.MaybePrimitiveType(bi.Init);
+ // ParameterExpression parmExpr = Expression.Parameter(primType ?? typeof(object), bi.Binding.Name);
+ // bi.Binding.ParamExpression = parmExpr;
+ // parms.Add(parmExpr);
+ // //forms.Add(Expression.Assign(parmExpr, Compiler.MaybeBox(bi.Init.GenDlr(context))));
+ // Expression initExpr = primType != null ? ((MaybePrimitiveExpr)bi.Init).GenDlrUnboxed(context) : Compiler.MaybeBox(bi.Init.GenDlr(context));
+ // forms.Add(Expression.Assign(parmExpr, initExpr));
+ //}
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/LiteralExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/LiteralExpr.cs new file mode 100644 index 00000000..2d6764b4 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/LiteralExpr.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;
+using Microsoft.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ abstract class LiteralExpr : Expr
+ {
+ public abstract object val();
+ }
+
+
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/LocalBinding.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/LocalBinding.cs new file mode 100644 index 00000000..1d1cd866 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/LocalBinding.cs @@ -0,0 +1,114 @@ +/**
+ * 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;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ sealed class LocalBinding
+ {
+ #region Data
+
+ private readonly Symbol _sym;
+ public Symbol Symbol
+ {
+ get { return _sym; }
+ }
+
+ private readonly Symbol _tag;
+ public Symbol Tag
+ {
+ get { return _tag; }
+ }
+
+ private Expr _init;
+ public Expr Init
+ {
+ get { return _init; }
+ set { _init = value; }
+ }
+
+ private readonly String _name;
+ public String Name
+ {
+ get { return _name; }
+ }
+
+ private readonly int _index;
+ public int Index
+ {
+ get { return _index; }
+ }
+
+ private Expression _paramExpression;
+ public Expression ParamExpression
+ {
+ get { return _paramExpression; }
+ set { _paramExpression = value; }
+ }
+
+ #endregion
+
+ #region C-tors
+
+ public LocalBinding(int index, Symbol sym, Symbol tag, Expr init)
+ {
+ if (Compiler.MaybePrimitiveType(init) != null && tag != null)
+ throw new InvalidOperationException("Can't type hint a local with a primitive initializer");
+
+ _index = index;
+ _sym = sym;
+ _tag = tag;
+ _init = init;
+ _name = Compiler.Munge(sym.Name);
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public bool HasClrType
+ {
+ get
+ {
+ if (_init != null
+ && Init.HasClrType
+ && Util.IsPrimitive(_init.ClrType)
+ && !(_init is MaybePrimitiveExpr))
+ return false;
+
+ return _tag != null || (_init != null && _init.HasClrType);
+ }
+ }
+
+ public Type ClrType
+ {
+ get
+ {
+ return _tag != null
+ ? Compiler.TagToType(_tag)
+ : _init != null
+ ? _init.ClrType
+ : null;
+ }
+ }
+
+ public Type PrimitiveType
+ {
+ get { return Compiler.MaybePrimitiveType(_init); }
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/LocalBindingExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/LocalBindingExpr.cs new file mode 100644 index 00000000..cdee3342 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/LocalBindingExpr.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.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class LocalBindingExpr : Expr, MaybePrimitiveExpr
+ {
+ #region Data
+
+ readonly LocalBinding _b;
+ readonly Symbol _tag;
+
+ #endregion
+
+ #region Ctors
+
+ public LocalBindingExpr(LocalBinding b, Symbol tag)
+ {
+ if (b.PrimitiveType != null && _tag != null)
+ throw new InvalidOperationException("Can't type hint a primitive local");
+ _b = b;
+ _tag = tag;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return _tag != null || _b.HasClrType; }
+ }
+
+ public override Type ClrType
+ {
+ get
+ {
+ if (_tag != null)
+ return Compiler.TagToType(_tag);
+ return _b.ClrType;
+ }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ return context.FnExpr.GenLocal(context,_b);
+ }
+
+
+ public Expression GenDlrUnboxed(GenContext context)
+ {
+ return context.FnExpr.GenUnboxedLocal(context,_b);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MapExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MapExpr.cs new file mode 100644 index 00000000..e5731f16 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MapExpr.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;
+using Microsoft.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class MapExpr : Expr
+ {
+ #region Data
+
+ readonly IPersistentVector _keyvals;
+
+ #endregion
+
+ #region Ctors
+
+ public MapExpr(IPersistentVector keyvals)
+ {
+ _keyvals = keyvals;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return true; }
+ }
+
+ public override Type ClrType
+ {
+ get { return typeof(IPersistentMap); }
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public static Expr Parse(IPersistentMap form)
+ {
+ IPersistentVector keyvals = PersistentVector.EMPTY;
+ for (ISeq s = RT.seq(form); s != null; s = s.next())
+ {
+ IMapEntry e = (IMapEntry)s.first();
+ keyvals = (IPersistentVector)keyvals.cons(Compiler.GenerateAST(e.key()));
+ keyvals = (IPersistentVector)keyvals.cons(Compiler.GenerateAST(e.val()));
+ }
+ Expr ret = new MapExpr(keyvals);
+ return Compiler.OptionallyGenerateMetaInit(form, ret);
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ Expression argArray = Compiler.GenArgArray(context, _keyvals);
+ Expression ret = Expression.Call(Compiler.Method_RT_map, argArray);
+ return ret;
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MaybePrimitiveExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MaybePrimitiveExpr.cs new file mode 100644 index 00000000..904f78bf --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MaybePrimitiveExpr.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;
+using Microsoft.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ interface MaybePrimitiveExpr
+ {
+ Expression GenDlrUnboxed(GenContext context);
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MetaExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MetaExpr.cs new file mode 100644 index 00000000..cc8188c1 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MetaExpr.cs @@ -0,0 +1,69 @@ +/**
+ * 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;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class MetaExpr : Expr
+ {
+ #region Data
+
+ readonly Expr _expr;
+ readonly MapExpr _meta;
+
+ #endregion
+
+ #region Ctors
+
+ public MetaExpr(Expr expr, MapExpr meta)
+ {
+ _expr = expr;
+ _meta = meta;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return _expr.HasClrType; }
+ }
+
+ public override Type ClrType
+ {
+ get { return _expr.ClrType; }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ Expression objExpr = _expr.GenDlr(context);
+ Expression iobjExpr = Expression.Convert(objExpr, typeof(IObj));
+
+ Expression metaExpr = _meta.GenDlr(context);
+ // Do we need a conversion here? probably not.
+
+ Expression ret = Expression.Call(iobjExpr, Compiler.Method_IObj_withMeta, metaExpr);
+
+ return ret;
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MethodExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MethodExpr.cs new file mode 100644 index 00000000..ccb706a6 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MethodExpr.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.lang.CljCompiler.Ast
+{
+ abstract class MethodExpr : HostExpr
+ {
+
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MonitorEnterExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MonitorEnterExpr.cs new file mode 100644 index 00000000..bcc3b64e --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MonitorEnterExpr.cs @@ -0,0 +1,60 @@ +/**
+ * 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;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class MonitorEnterExpr : UntypedExpr
+ {
+ #region Data
+
+ readonly Expr _target;
+
+ #endregion
+
+ #region Ctors
+
+ public MonitorEnterExpr(Expr target)
+ {
+ _target = target;
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public sealed class Parser : IParser
+ {
+ public Expr Parse(object form)
+ {
+ return new MonitorEnterExpr(Compiler.GenerateAST(RT.second(form)));
+ }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ return Expression.Block(
+ Expression.Call(Compiler.Method_Monitor_Enter, _target.GenDlr(context)),
+ Compiler.NIL_EXPR.GenDlr(context));
+ }
+
+ #endregion
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MonitorExitExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MonitorExitExpr.cs new file mode 100644 index 00000000..350f6af0 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/MonitorExitExpr.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 Microsoft.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class MonitorExitExpr : UntypedExpr
+ {
+ #region Data
+
+ readonly Expr _target;
+
+ #endregion
+
+ #region Ctors
+
+ public MonitorExitExpr(Expr target)
+ {
+ _target = target;
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public sealed class Parser : IParser
+ {
+ public Expr Parse(object form)
+ {
+ return new MonitorExitExpr(Compiler.GenerateAST(RT.second(form)));
+ }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ return Expression.Block(
+ Expression.Call(Compiler.Method_Monitor_Exit, _target.GenDlr(context)),
+ Compiler.NIL_EXPR.GenDlr(context));
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/NewExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/NewExpr.cs new file mode 100644 index 00000000..8154df51 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/NewExpr.cs @@ -0,0 +1,139 @@ +/**
+ * 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 Microsoft.Linq.Expressions;
+using System.IO;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class NewExpr : Expr
+ {
+ #region Data
+
+ readonly IPersistentVector _args;
+ readonly ConstructorInfo _ctor;
+ readonly Type _type;
+
+ #endregion
+
+ #region Ctors
+
+ public NewExpr(Type type, IPersistentVector args)
+ {
+ _args = args;
+ _type = type;
+ _ctor = ComputeCtor();
+ }
+
+ private ConstructorInfo ComputeCtor()
+ {
+ int numArgs = _args.count();
+
+ List<ConstructorInfo> cinfos
+ = new List<ConstructorInfo>(_type.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", _type.Name, numArgs));
+
+ int index = 0;
+ if (cinfos.Count > 1)
+ {
+ List<ParameterInfo[]> parms = new List<ParameterInfo[]>(cinfos.Count);
+ List<Type> rets = new List<Type>(cinfos.Count);
+ foreach (ConstructorInfo cinfo in cinfos)
+ {
+ parms.Add(cinfo.GetParameters());
+ rets.Add(_type);
+ }
+
+ index = HostExpr.GetMatchingParams(".ctor", parms, _args, rets);
+ }
+ ConstructorInfo ctor = index >= 0 ? cinfos[index] : null;
+ if (ctor == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
+ ((TextWriter)RT.ERR.deref()).WriteLine("Reflection warning, line: {0} - call to {1} ctor can't be resolved.",
+ /* line */ 0, _type.FullName);
+ return ctor;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return true; }
+ }
+
+ public override Type ClrType
+ {
+ get { return _type; }
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public sealed class Parser : IParser
+ {
+ public Expr Parse(object frm)
+ {
+ ISeq form = (ISeq)frm;
+
+ // form => (new Typename args ... )
+
+ if (form.count() < 2)
+ throw new Exception("wrong number of arguments, expecting: (new Typename args ...)");
+
+ Type t = Compiler.MaybeType(RT.second(form), false);
+ if (t == null)
+ throw new ArgumentException("Unable to resolve classname: " + RT.second(form));
+
+ IPersistentVector args = PersistentVector.EMPTY;
+ for (ISeq s = RT.next(RT.next(form)); s != null; s = s.next())
+ args = args.cons(Compiler.GenerateAST(s.first()));
+
+ return new NewExpr(t, args);
+ }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ if ( _ctor != null )
+ {
+ // The ctor is uniquely determined.
+
+ Expression[] args = Compiler.GenTypedArgArray(context, _ctor.GetParameters(), _args);
+ return Expression.New(_ctor, args);
+
+ // JAVA: emitClearLocals
+ }
+ else
+ {
+ Expression typeExpr = Expression.Call(Compiler.Method_RT_classForName, Expression.Constant(_type.FullName));
+ Expression args = Compiler.GenArgArray(context, _args);
+ // Java: emitClearLocals
+
+ return Expression.Call(Compiler.Method_Reflector_InvokeConstructor,typeExpr,args);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/NilExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/NilExpr.cs new file mode 100644 index 00000000..0f3fa8ec --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/NilExpr.cs @@ -0,0 +1,53 @@ +/**
+ * 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;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class NilExpr : LiteralExpr
+ {
+ #region Data
+
+ public override object val()
+ {
+ return null;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return true; }
+ }
+
+ public override Type ClrType
+ {
+ get { return null; }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ return Expression.Constant(null);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/Node.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/Node.cs new file mode 100644 index 00000000..e07ae72c --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/Node.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 Microsoft.Scripting;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ /// <summary>
+ /// Base class for AST expressions in the compiler.
+ /// </summary>
+ /// <remarks>Stolen from IronPython.</remarks>
+ abstract class Node
+ {
+ #region Data
+
+ private SourceLocation _start = SourceLocation.Invalid;
+ private SourceLocation _end = SourceLocation.Invalid;
+
+ #endregion
+
+ #region Location methods
+
+ public void SetLoc(SourceLocation start, SourceLocation end)
+ {
+ _start = start;
+ _end = end;
+ }
+
+ public void SetLoc(SourceSpan span)
+ {
+ _start = span.Start;
+ _end = span.End;
+ }
+
+ public SourceLocation Start
+ {
+ get { return _start; }
+ set { _start = value; }
+ }
+
+ public SourceLocation End
+ {
+ get { return _end; }
+ set { _end = value; }
+ }
+
+ public SourceSpan Span
+ {
+ get
+ {
+ return new SourceSpan(_start, _end);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/RecurExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/RecurExpr.cs new file mode 100644 index 00000000..ded4b667 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/RecurExpr.cs @@ -0,0 +1,124 @@ +/**
+ * 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;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class RecurExpr : Expr
+ {
+ #region Data
+
+ readonly IPersistentVector _args;
+ readonly IPersistentVector _loopLocals;
+
+ #endregion
+
+ #region Ctors
+
+ public RecurExpr(IPersistentVector loopLocals, IPersistentVector args)
+ {
+ _loopLocals = loopLocals;
+ _args = args;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return true; }
+ }
+
+ public override Type ClrType
+ {
+ get { return typeof(void); } // Java: returns null.
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public sealed class Parser : IParser
+ {
+ public Expr Parse(object frm)
+ {
+ ISeq form = (ISeq)frm;
+
+ IPersistentVector loopLocals = (IPersistentVector)Compiler.LOOP_LOCALS.deref();
+
+ if (Compiler.IN_TAIL_POSITION.deref() == null || loopLocals == null)
+ throw new InvalidOperationException("Can only recur from tail position");
+
+ if (Compiler.IN_CATCH_FINALLY.deref() != null)
+ throw new InvalidOperationException("Cannot recur from catch/finally.");
+
+ IPersistentVector args = PersistentVector.EMPTY;
+
+ for (ISeq s = form.next(); s != null; s = s.next())
+ args = args.cons(Compiler.GenerateAST(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()));
+
+ return new RecurExpr(loopLocals, args);
+ }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ LabelTarget loopLabel = (LabelTarget)Compiler.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>(argCount);
+ List<Expression> finalAssigns = new List<Expression>(argCount);
+
+ // Evaluate all the init forms into local variables.
+ // TODO: Check the typing here.
+ for (int i = 0; i < _loopLocals.count(); i++)
+ {
+ LocalBinding b = (LocalBinding)_loopLocals.nth(i);
+ Expr arg = (Expr)_args.nth(i);
+ ParameterExpression tempVar = Expression.Parameter(b.ParamExpression.Type, "__local__" + i); //asdf-tag
+ Expression valExpr = ((Expr)_args.nth(i)).GenDlr(context);
+ tempVars.Add(tempVar);
+
+ if (tempVar.Type == typeof(Object))
+ tempAssigns.Add(Expression.Assign(tempVar, Compiler.MaybeBox(valExpr)));
+ else
+ tempAssigns.Add(Expression.Assign(tempVar, Expression.Convert(valExpr, 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
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/SetExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/SetExpr.cs new file mode 100644 index 00000000..59374b43 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/SetExpr.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;
+using Microsoft.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class SetExpr : Expr
+ {
+ #region Data
+
+ readonly IPersistentVector _keys;
+
+ #endregion
+
+ #region Ctors
+
+ public SetExpr(IPersistentVector keys)
+ {
+ _keys = keys;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return true; }
+ }
+
+ public override Type ClrType
+ {
+ get { return typeof(IPersistentSet); }
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public static Expr Parse(IPersistentSet form)
+ {
+ IPersistentVector keys = PersistentVector.EMPTY;
+ for (ISeq s = RT.seq(form); s != null; s = s.next())
+ {
+ object e = s.first();
+ keys = (IPersistentVector)keys.cons(Compiler.GenerateAST(e));
+ }
+ Expr ret = new SetExpr(keys);
+ return Compiler.OptionallyGenerateMetaInit(form, ret);
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ Expression argArray = Compiler.GenArgArray(context, _keys);
+ Expression ret = Expression.Call(Compiler.Method_RT_set, argArray);
+ return ret;
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/StaticFieldExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/StaticFieldExpr.cs new file mode 100644 index 00000000..391ee584 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/StaticFieldExpr.cs @@ -0,0 +1,90 @@ +/**
+ * 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 Microsoft.Linq.Expressions;
+using System.IO;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class StaticFieldExpr : FieldExpr
+ {
+ #region Data
+
+ readonly string _fieldName;
+ readonly Type _type;
+ readonly FieldInfo _field;
+ readonly PropertyInfo _property;
+
+ #endregion
+
+ #region Ctors
+
+ public StaticFieldExpr(Type type, string fieldName)
+ {
+ _fieldName = fieldName;
+ _type = type;
+ _field = type.GetField(_fieldName, BindingFlags.Static | BindingFlags.Public);
+ _property = type.GetProperty(_fieldName, BindingFlags.Static | BindingFlags.Public);
+
+ if ( _field == null && _property == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
+ ((TextWriter)RT.ERR.deref()).WriteLine("Reflection warning {0}:{1} - reference to field/property {2} can't be resolved.",
+ Compiler.SOURCE_PATH.deref(), /* line */ 0,_fieldName);
+
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return true; }
+ }
+
+ public override Type ClrType
+ {
+ get { return _field.FieldType; }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ return Compiler.MaybeBox(GenDlrUnboxed(context));
+ }
+
+ public override Expression GenDlrUnboxed(GenContext context)
+ {
+ return _property != null
+ ? Expression.Property(null, _property)
+ : Expression.Field(null, _field);
+ }
+
+ #endregion
+
+ #region AssignableExpr Members
+
+ public override Expression GenAssignDlr(GenContext context, Expr val)
+ {
+ Expression access = GenDlrUnboxed(context);
+ Expression valExpr = val.GenDlr(context);
+ return Expression.Assign(access, valExpr);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/StaticMethodExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/StaticMethodExpr.cs new file mode 100644 index 00000000..d8463610 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/StaticMethodExpr.cs @@ -0,0 +1,107 @@ +/**
+ * 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 Microsoft.Linq.Expressions;
+using AstUtils = Microsoft.Scripting.Ast.Utils;
+using System.IO;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class StaticMethodExpr : MethodExpr
+ {
+ #region Data
+
+ readonly Type _type;
+ readonly string _methodName;
+ readonly IPersistentVector _args;
+ readonly MethodInfo _method;
+
+ #endregion
+
+ #region Ctors
+
+ public StaticMethodExpr(Type type, string methodName, IPersistentVector args)
+ {
+ _type = type;
+ _methodName = methodName;
+ _args = args;
+
+ _method = GetMatchingMethod(_type, _args, _methodName);
+ }
+
+
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return _method != null; }
+ }
+
+ public override Type ClrType
+ {
+ get { return _method.ReturnType; }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ if (_method != null)
+ return Compiler.MaybeBox(GenDlrForMethod(context));
+ else
+ return GenDlrViaReflection(context);
+ }
+
+ public override Expression GenDlrUnboxed(GenContext context)
+ {
+ if (_method != null)
+ return GenDlrForMethod(context);
+ else
+ throw new InvalidOperationException("Unboxed emit of unknown member.");
+ }
+
+
+ private Expression GenDlrForMethod(GenContext context)
+ {
+ Expression[] args = GenTypedArgs(context, _method.GetParameters(), _args);
+
+ return AstUtils.SimpleCallHelper(_method, args); ;
+
+ }
+
+
+ private Expression GenDlrViaReflection(GenContext context)
+ {
+ Expression[] parms = new Expression[_args.count()];
+ for ( int i=0; i<_args.count(); i++ )
+ parms[i] = Compiler.MaybeBox(((Expr)_args.nth(i)).GenDlr(context));
+
+ Expression[] moreArgs = new Expression[3];
+ moreArgs[0] = Expression.Constant(_methodName);
+ moreArgs[1] = Expression.Constant(_type);
+ moreArgs[2] = Expression.NewArrayInit(typeof(object), parms);
+
+ return Expression.Call(Compiler.Method_Reflector_CallStaticMethod, moreArgs);
+ }
+
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/StringExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/StringExpr.cs new file mode 100644 index 00000000..53f0445b --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/StringExpr.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 Microsoft.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class StringExpr : Expr
+ {
+ #region Data
+
+ readonly string _str;
+
+ #endregion
+
+ #region Ctors
+
+ public StringExpr(string str)
+ {
+ _str = str;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return true; }
+ }
+
+ public override Type ClrType
+ {
+ get { return typeof(string); }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ return Expression.Constant(String.Intern(_str));
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/TheVarExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/TheVarExpr.cs new file mode 100644 index 00000000..2d570132 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/TheVarExpr.cs @@ -0,0 +1,75 @@ +/**
+ * 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;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class TheVarExpr : Expr
+ {
+ #region Data
+
+ readonly Var _var;
+
+ #endregion
+
+ #region Ctors
+
+ public TheVarExpr(Var var)
+ {
+ _var = var;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return true; }
+ }
+
+ public override Type ClrType
+ {
+ get { return typeof(Var); }
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public sealed class Parser : IParser
+ {
+ public Expr Parse(object form)
+ {
+ Symbol sym = (Symbol)RT.second(form);
+ Var v = Compiler.LookupVar(sym, false);
+ if (v != null)
+ return new TheVarExpr(v);
+ throw new Exception(string.Format("Unable to resolve var: {0} in this context", sym));
+ }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ return context.FnExpr.GenVar(context,_var);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/ThrowExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/ThrowExpr.cs new file mode 100644 index 00000000..bf0f3853 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/ThrowExpr.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 Microsoft.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class ThrowExpr : UntypedExpr
+ {
+ #region Data
+
+ readonly Expr _excExpr;
+
+ #endregion
+
+ #region Ctors
+
+ public ThrowExpr(Expr excExpr)
+ {
+ _excExpr = excExpr;
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public sealed class Parser : IParser
+ {
+ public Expr Parse(object form)
+ {
+ // Java:
+ // TODO: figure out if it matters
+ //if (context == C.EVAL)
+ // return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form)));
+
+ return new ThrowExpr(Compiler.GenerateAST(RT.second(form)));
+
+ }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ Expression exc = _excExpr.GenDlr(context);
+ Expression exc2 = Expression.Convert(exc, typeof(Exception));
+
+ return Expression.Throw(exc2);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/TryExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/TryExpr.cs new file mode 100644 index 00000000..f874640c --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/TryExpr.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 Microsoft.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class TryExpr : Expr
+ {
+ #region Nested classes
+
+ public sealed class CatchClause
+ {
+ readonly Type _type;
+ public Type Type
+ {
+ get { return _type; }
+ }
+
+ readonly LocalBinding _lb;
+ internal LocalBinding Lb
+ {
+ get { return _lb; }
+ }
+
+ readonly Expr _handler;
+ internal Expr Handler
+ {
+ get { return _handler; }
+ }
+
+
+ public CatchClause(Type type, LocalBinding lb, Expr handler)
+ {
+ _type = type;
+ _lb = lb;
+ _handler = handler;
+ }
+ }
+
+ #endregion
+
+ #region Data
+
+ readonly Expr _tryExpr;
+ readonly Expr _finallyExpr;
+ readonly IPersistentVector _catchExprs;
+ readonly int _retLocal;
+ readonly int _finallyLocal;
+
+ #endregion
+
+ #region Ctors
+
+ public TryExpr(Expr tryExpr, IPersistentVector catchExprs, Expr finallyExpr, int retLocal, int finallyLocal)
+ {
+ _tryExpr = tryExpr;
+ _catchExprs = catchExprs;
+ _finallyExpr = finallyExpr;
+ _retLocal = retLocal;
+ _finallyLocal = finallyLocal;
+
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return _tryExpr.HasClrType; }
+ }
+
+ public override Type ClrType
+ {
+ get { return _tryExpr.ClrType; }
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public sealed class Parser : IParser
+ {
+ public Expr Parse(object frm)
+ {
+ ISeq form = (ISeq)frm;
+
+ // Java version has this:
+ //if (context != C.RETURN)
+ // return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form)));
+ // TODO: figure out why it matters.
+
+ // (try try-expr* catch-expr* finally-expr?)
+ // catch-expr: (catch class sym expr*)
+ // finally-expr: (finally expr*)
+
+ IPersistentVector body = PersistentVector.EMPTY;
+ IPersistentVector catches = PersistentVector.EMPTY;
+ Expr finallyExpr = null;
+ bool caught = false;
+
+ int retLocal = Compiler.GetAndIncLocalNum();
+ int finallyLocal = Compiler.GetAndIncLocalNum();
+
+ for (ISeq fs = form.next(); fs != null; fs = fs.next())
+ {
+ object f = fs.first();
+ object op = (f is ISeq) ? ((ISeq)f).first() : null;
+ if (!Util.equals(op, Compiler.CATCH) && !Util.equals(op, Compiler.FINALLY))
+ {
+ if (caught)
+ throw new Exception("Only catch or finally clause can follow catch in try expression");
+ body = body.cons(f);
+ }
+ else
+ {
+ if (Util.equals(op, Compiler.CATCH))
+ {
+ Type t = Compiler.MaybeType(RT.second(f), false);
+ if (t == null)
+ throw new ArgumentException("Unable to resolve classname: " + RT.second(f));
+ if (!(RT.third(f) is Symbol))
+ throw new ArgumentException("Bad binding form, expected symbol, got: " + RT.third(f));
+ Symbol sym = (Symbol)RT.third(f);
+ if (sym.Namespace != null)
+ throw new Exception("Can't bind qualified name: " + sym);
+
+ IPersistentMap dynamicBindings = RT.map(
+ Compiler.LOCAL_ENV, Compiler.LOCAL_ENV.deref(),
+ Compiler.NEXT_LOCAL_NUM, Compiler.NEXT_LOCAL_NUM.deref(),
+ Compiler.IN_CATCH_FINALLY, RT.T);
+
+ try
+ {
+ Var.pushThreadBindings(dynamicBindings);
+ LocalBinding lb = Compiler.RegisterLocal(sym,
+ (Symbol)(RT.second(f) is Symbol ? RT.second(f) : null),
+ null);
+ Expr handler = (new BodyExpr.Parser()).Parse(RT.next(RT.next(RT.next(f))));
+ catches = catches.cons(new CatchClause(t, lb, handler)); ;
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ caught = true;
+ }
+ else // finally
+ {
+ if (fs.next() != null)
+ throw new Exception("finally clause must be last in try expression");
+ try
+ {
+ Var.pushThreadBindings(RT.map(Compiler.IN_CATCH_FINALLY, RT.T));
+ finallyExpr = (new BodyExpr.Parser()).Parse(RT.next(f));
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ }
+ }
+ }
+
+ Expr bodyExpr = (new BodyExpr.Parser()).Parse(RT.seq(body));
+ return new TryExpr(bodyExpr, catches, finallyExpr, retLocal, finallyLocal);
+ }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ Expression basicBody = _tryExpr.GenDlr(context);
+ // 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);
+ Expression tryBody = basicBody;
+
+ CatchBlock[] catches = new CatchBlock[_catchExprs.count()];
+ for ( int i=0; i<_catchExprs.count(); i++ )
+ {
+ CatchClause clause = (CatchClause) _catchExprs.nth(i);
+ ParameterExpression parmExpr = Expression.Parameter(clause.Type, clause.Lb.Name);
+ clause.Lb.ParamExpression = parmExpr;
+ catches[i] = Expression.Catch(parmExpr,clause.Handler.GenDlr(context));
+ }
+
+ TryExpression tryStmt = _finallyExpr == null
+ ? Expression.TryCatch(tryBody, catches)
+ : Expression.TryCatchFinally(tryBody, _finallyExpr.GenDlr(context), catches);
+
+ Expression defaultValue = Expression.Default(basicBody.Type);
+ Expression whole = Expression.Block(tryStmt, Expression.Label(target, defaultValue));
+ return whole;
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/UnresolvedVarExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/UnresolvedVarExpr.cs new file mode 100644 index 00000000..5f854604 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/UnresolvedVarExpr.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 Microsoft.Linq.Expressions;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class UnresolvedVarExpr : Expr
+ {
+ #region Data
+
+ readonly Symbol _symbol;
+
+ #endregion
+
+ #region Ctors
+
+ public UnresolvedVarExpr(Symbol symbol)
+ {
+ _symbol = symbol;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return false; }
+ }
+
+ public override Type ClrType
+ {
+ get { throw new InvalidOperationException("UnresolvedVarExpr has no CLR type"); }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ return Expression.Empty();
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/UntypedExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/UntypedExpr.cs new file mode 100644 index 00000000..27cd11b6 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/UntypedExpr.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.CljCompiler.Ast
+{
+ abstract class UntypedExpr : Expr
+ {
+ public override bool HasClrType
+ {
+ get { return false; }
+ }
+
+ public override Type ClrType
+ {
+ get { throw new ArgumentException("Has no CLR class"); }
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/VarExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/VarExpr.cs new file mode 100644 index 00000000..bed6a37c --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/VarExpr.cs @@ -0,0 +1,80 @@ +/**
+ * 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;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class VarExpr : Expr, AssignableExpr
+ {
+ #region Data
+
+ readonly Var _var;
+ readonly object _tag;
+
+ public object Tag
+ {
+ get { return _tag; }
+ }
+
+
+ #endregion
+
+ #region Ctors
+
+ public VarExpr(Var var, Symbol tag)
+ {
+ _var = var;
+ _tag = tag ?? var.Tag;
+ }
+
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return _tag != null; }
+ }
+
+ public override Type ClrType
+ {
+ get { return Compiler.TagToType(_tag); }
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ Expression varExpr = context.FnExpr.GenVar(context,_var);
+ return Expression.Call(varExpr, Compiler.Method_Var_get);
+ }
+
+ #endregion
+
+ #region AssignableExpr Members
+
+ public Expression GenAssignDlr(GenContext context, Expr val)
+ {
+ Expression varExpr = context.FnExpr.GenVar(context, _var);
+ Expression valExpr = val.GenDlr(context);
+ return Expression.Call(varExpr, Compiler.Method_Var_set, valExpr);
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/VectorExpr.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/VectorExpr.cs new file mode 100644 index 00000000..68f6e06e --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Ast/VectorExpr.cs @@ -0,0 +1,75 @@ +/**
+ * 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;
+
+namespace clojure.lang.CljCompiler.Ast
+{
+ class VectorExpr : Expr
+ {
+ #region Data
+
+ readonly IPersistentVector _args;
+
+ #endregion
+
+ #region Ctors
+
+ public VectorExpr(IPersistentVector args)
+ {
+ _args = args;
+ }
+
+ #endregion
+
+ #region Type mangling
+
+ public override bool HasClrType
+ {
+ get { return true; }
+ }
+
+ public override Type ClrType
+ {
+ get { return typeof(IPersistentVector); }
+ }
+
+ #endregion
+
+ #region Parsing
+
+ public static Expr Parse(IPersistentVector form)
+ {
+ IPersistentVector args = PersistentVector.EMPTY;
+ for (int i = 0; i < form.count(); i++ )
+ args = (IPersistentVector)args.cons(Compiler.GenerateAST(form.nth(i)));
+
+ Expr ret = new VectorExpr(args);
+ return Compiler.OptionallyGenerateMetaInit(form, ret);
+ }
+
+ #endregion
+
+ #region Code generation
+
+ public override Expression GenDlr(GenContext context)
+ {
+ Expression argArray = Compiler.GenArgArray(context, _args);
+ Expression ret = Expression.Call(Compiler.Method_RT_vector, argArray);
+ return ret;
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Compiler.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Compiler.cs new file mode 100644 index 00000000..1cc6ec23 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Compiler.cs @@ -0,0 +1,1297 @@ +
+/**
+ * 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;
+using Microsoft.Linq.Expressions;
+using clojure.lang.CljCompiler.Ast;
+using clojure.runtime;
+using System.Reflection;
+using System.Reflection.Emit;
+
+
+namespace clojure.lang
+{
+ /// <summary>
+ ///
+ /// </summary>
+ /// <remarks>Originally, I tried to do this as one-pass compiler, direct from SEXPRs to DLR Expression Trees. And it was working just fine.
+ /// <para>Then Rich added a change in the new lazy version of Clojure that required a variable base class for implementations of <see cref="AFn"/>.
+ /// Due to the fact that DLR can only generate lambda expressions to static methods in assemblies, some nasty workarounds are required
+ /// that force a multi-pass compiler. With that, I gave up and added an AST intermediate stage.</para>
+ /// <para>So now we go SEXPR -> AST -> ExpressionTree. As a result, once again, my code converges toward the JVM code.</para>
+ /// <para>PS: And then later the damnable :super-class went away.</para>
+ /// </remarks>
+ public static class Compiler
+ {
+ #region other constants
+
+ internal const int MAX_POSITIONAL_ARITY = 20;
+
+ #endregion
+
+ #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 LETFN = Symbol.create("letfn*");
+ 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 IMPORT = Symbol.create("clojure.core","import*");
+ 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");
+
+ internal static readonly Symbol ISEQ = Symbol.create("clojure.lang.ISeq");
+
+
+ #endregion
+
+ #region Keywords
+
+ static readonly Keyword INLINE_KEY = Keyword.intern(null, "inline");
+ static readonly Keyword INLINE_ARITIES_KEY = Keyword.intern(null, "inline-arities");
+
+ #endregion
+
+ #region Vars
+
+ //boolean
+ internal 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
+ public static readonly Var COMPILE_PATH = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),
+ Symbol.create("*compile-path*"), null);
+
+ public static readonly Var COMPILE = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),
+ Symbol.create("compile"));
+
+ // String
+ static readonly Var SOURCE = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),
+ Symbol.create("*source-path*"), "NO_SOURCE_FILE");
+ // String
+ internal static readonly Var SOURCE_PATH = Var.intern(Namespace.findOrCreate(Symbol.create("clojure.core")),
+ Symbol.create("*file*"), "NO_SOURCE_PATH");
+ //Integer
+ internal static readonly Var LINE_BEFORE = Var.create(0);
+ internal static readonly Var LINE_AFTER = Var.create(0);
+
+ internal static readonly Var METHODS = Var.create(null);
+ internal static readonly Var LOCAL_ENV = Var.create(PersistentHashMap.EMPTY);
+ //Integer
+ internal static readonly Var NEXT_LOCAL_NUM = Var.create(0);
+ internal static readonly Var LOOP_LOCALS = Var.create(null);
+ // Label
+ internal static readonly Var LOOP_LABEL = Var.create();
+
+
+ internal static readonly Var IN_CATCH_FINALLY = Var.create(null); //null or not
+ internal static readonly Var IN_TAIL_POSITION = Var.create(null); //null or not
+
+
+ internal static readonly Var VARS = Var.create(); //var->constid
+ internal static readonly Var CONSTANTS = Var.create(); //vector<object>
+ internal static readonly Var KEYWORDS = Var.create(); //keyword->constid
+
+ #endregion
+
+ #region Special forms
+
+ // TODO: Figure out why clojure's special-form? shows if as special, instead of if*.
+
+ public static readonly IPersistentMap _specials = PersistentHashMap.create(
+ DEF, new DefExpr.Parser(),
+ LOOP, new LetExpr.Parser(),
+ RECUR, new RecurExpr.Parser(),
+ IF, new IfExpr.Parser(),
+ LET, new LetExpr.Parser(),
+ LETFN, new LetFnExpr.Parser(),
+ DO, new BodyExpr.Parser(),
+ FN, null,
+ QUOTE, new ConstantExpr.Parser(),
+ THE_VAR, new TheVarExpr.Parser(),
+ IMPORT, new ImportExpr.Parser(),
+ DOT, new HostExpr.Parser(),
+ ASSIGN, new AssignExpr.Parser(),
+ TRY, new TryExpr.Parser(),
+ THROW, new ThrowExpr.Parser(),
+ MONITOR_ENTER, new MonitorEnterExpr.Parser(),
+ MONITOR_EXIT, new MonitorExitExpr.Parser(),
+ CATCH, null,
+ FINALLY, null,
+ NEW, new NewExpr.Parser(),
+ _AMP_, null
+ );
+
+ public static bool isSpecial(Object sym)
+ {
+ return _specials.containsKey(sym);
+ }
+
+ static IParser GetSpecialFormParser(object op)
+ {
+ return (IParser)_specials.valAt(op);
+ }
+
+ #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");
+
+ internal static readonly PropertyInfo Method_Compiler_CurrentNamespace = typeof(Compiler).GetProperty("CurrentNamespace");
+ internal static readonly MethodInfo Method_Compiler_PushNS = typeof(Compiler).GetMethod("PushNS");
+
+
+ internal static readonly MethodInfo Method_IObj_withMeta = typeof(IObj).GetMethod("withMeta");
+
+ internal static readonly MethodInfo Method_Keyword_intern = typeof(Keyword).GetMethod("intern", new Type[] { typeof(Symbol) });
+
+ internal static readonly MethodInfo Method_Monitor_Enter = typeof(Monitor).GetMethod("Enter");
+ internal static readonly MethodInfo Method_Monitor_Exit = typeof(Monitor).GetMethod("Exit");
+
+ internal static readonly MethodInfo Method_Namespace_importClass1 = typeof(Namespace).GetMethod("importClass", new Type[] { typeof(Type) });
+
+ internal static readonly MethodInfo Method_PersistentList_create = typeof(PersistentList).GetMethod("create", new Type[] { typeof(System.Collections.IList) });
+
+ internal static readonly MethodInfo Method_Reflector_CallInstanceMethod = typeof(Reflector).GetMethod("CallInstanceMethod");
+ internal static readonly MethodInfo Method_Reflector_CallStaticMethod = typeof(Reflector).GetMethod("CallStaticMethod");
+ internal static readonly MethodInfo Method_Reflector_InvokeConstructor = typeof(Reflector).GetMethod("InvokeConstructor");
+ internal static readonly MethodInfo Method_Reflector_SetInstanceFieldOrProperty = typeof(Reflector).GetMethod("SetInstanceFieldOrProperty");
+
+ internal static readonly MethodInfo Method_RT_arrayToList = typeof(RT).GetMethod("arrayToList");
+ internal static readonly MethodInfo Method_RT_classForName = typeof(RT).GetMethod("classForName");
+ internal static readonly MethodInfo Method_RT_IsTrue = typeof(RT).GetMethod("IsTrue");
+ internal static readonly MethodInfo Method_RT_map = typeof(RT).GetMethod("map");
+ internal static readonly MethodInfo Method_RT_printToConsole = typeof(RT).GetMethod("printToConsole");
+ internal static readonly MethodInfo Method_RT_set = typeof(RT).GetMethod("set");
+ internal static readonly MethodInfo Method_RT_vector = typeof(RT).GetMethod("vector");
+ internal static readonly MethodInfo Method_RT_readString = typeof(RT).GetMethod("readString");
+ internal static readonly MethodInfo Method_RT_var2 = typeof(RT).GetMethod("var", new Type[] { typeof(string), typeof(string) });
+
+ internal static readonly MethodInfo Method_Symbol_create2 = typeof(Symbol).GetMethod("create", new Type[] { typeof(string), typeof(string) });
+
+ internal static readonly MethodInfo Method_Var_BindRoot = typeof(Var).GetMethod("BindRoot");
+ internal static readonly MethodInfo Method_Var_get = typeof(Var).GetMethod("deref");
+ internal static readonly MethodInfo Method_Var_set = typeof(Var).GetMethod("set");
+ internal static readonly MethodInfo Method_Var_setMeta = typeof(Var).GetMethod("setMeta");
+ internal static readonly MethodInfo Method_Var_popThreadBindings = typeof(Var).GetMethod("popThreadBindings");
+
+
+ //static readonly ConstructorInfo Ctor_AFnImpl_0 = typeof(AFnImpl).GetConstructor(Type.EmptyTypes);
+ internal static readonly ConstructorInfo Ctor_RestFnImpl_1 = typeof(RestFnImpl).GetConstructor(new Type[] { typeof(int) });
+
+ internal static readonly MethodInfo[] Methods_IFn_invoke = new MethodInfo[MAX_POSITIONAL_ARITY + 2];
+
+ internal 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 C-tors & factory methods
+
+ static Compiler()
+ {
+ for (int i = 0; i <= Compiler.MAX_POSITIONAL_ARITY; i++)
+ Methods_IFn_invoke[i] = typeof(IFn).GetMethod("invoke", CreateObjectTypeArray(i));
+
+ Type[] types = new Type[Compiler.MAX_POSITIONAL_ARITY + 1];
+ CreateObjectTypeArray(Compiler.MAX_POSITIONAL_ARITY).CopyTo(types, 0);
+ types[Compiler.MAX_POSITIONAL_ARITY] = typeof(object[]);
+ Methods_IFn_invoke[Compiler.MAX_POSITIONAL_ARITY + 1]
+ = typeof(IFn).GetMethod("invoke", types);
+
+ MethodInfo[] mis = typeof(IFn).GetMethods();
+
+ }
+
+ static GenContext _context = new GenContext("eval", CompilerMode.Immediate);
+
+ static int _saveId = 0;
+ public static void SaveEvalContext()
+ {
+ _context.AssyBldr.Save("done" + _saveId++ + ".dll");
+ _context = new GenContext("eval", CompilerMode.Immediate);
+ }
+
+
+ public static LambdaExpression GenerateLambda(object form, bool addPrint)
+ {
+ return GenerateLambda(_context, form, addPrint);
+ }
+
+
+ internal static LambdaExpression GenerateLambda(GenContext context, object form, bool addPrint)
+ {
+ // TODO: Clean this up.
+ form = RT.list(FN, PersistentVector.EMPTY, form);
+
+ Expr ast = GenerateAST(form);
+ Expression formExpr = GenerateDlrExpression(context,ast);
+ Expression finalExpr = Expression.Call(formExpr, formExpr.Type.GetMethod("invoke", System.Type.EmptyTypes));
+
+ if (addPrint)
+ {
+ finalExpr = Expression.Call(Method_RT_printToConsole, finalExpr);
+ }
+
+ return Expression.Lambda(finalExpr, "REPLCall", null);
+ }
+
+
+
+
+ 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;
+ }
+
+ internal 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;
+ }
+
+ #endregion
+
+ #region Entry points
+
+
+
+ #endregion
+
+ #region AST generation
+
+ internal static LiteralExpr NIL_EXPR = new NilExpr();
+ static LiteralExpr TRUE_EXPR = new BooleanExpr(true);
+ static LiteralExpr FALSE_EXPR = new BooleanExpr(false);
+
+ // Equivalent to Java: Compiler.analyze()
+ internal static Expr GenerateAST(object form)
+ {
+ return GenerateAST(form,null);
+ }
+
+ internal static Expr GenerateAST(object form, string name)
+ {
+ if (form is LazySeq)
+ {
+ form = RT.seq(form);
+ if (form == null)
+ form = PersistentList.EMPTY;
+ }
+ if (form == null)
+ return NIL_EXPR;
+ else if (form is Boolean)
+ return ((bool)form) ? TRUE_EXPR : FALSE_EXPR;
+
+ Type type = form.GetType();
+
+ if (type == typeof(Symbol))
+ return AnalyzeSymbol((Symbol)form);
+ else if (type == typeof(Keyword))
+ return RegisterKeyword((Keyword)form);
+ else if (type == typeof(String))
+ return new StringExpr((String)form);
+ else if (form is IPersistentCollection && ((IPersistentCollection)form).count() == 0)
+ return OptionallyGenerateMetaInit(form, new EmptyExpr(form));
+ else if (form is ISeq)
+ return AnalyzeSeq((ISeq)form,name);
+ else if (form is IPersistentVector)
+ return VectorExpr.Parse((IPersistentVector)form);
+ else if (form is IPersistentMap)
+ return MapExpr.Parse((IPersistentMap)form);
+ else if (form is IPersistentSet)
+ return SetExpr.Parse((IPersistentSet)form);
+ else
+ return new ConstantExpr(form);
+ }
+
+ internal static Expr OptionallyGenerateMetaInit(object form, Expr expr)
+ {
+ Expr ret = expr;
+
+ IObj o = form as IObj;
+ if (o != null && o.meta() != null)
+ ret = new MetaExpr(ret, (MapExpr)MapExpr.Parse(o.meta()));
+
+ return ret;
+ }
+
+
+ private static Expr AnalyzeSymbol(Symbol symbol)
+ {
+ Symbol tag = TagOf(symbol);
+
+ if (symbol.Namespace == null)
+ {
+ LocalBinding b = ReferenceLocal(symbol);
+ if (b != null)
+ return new LocalBindingExpr(b, tag);
+ }
+ else
+ {
+ if (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 new StaticFieldExpr(t, symbol.Name);
+ throw new Exception(string.Format("Unable to find static field: {0} in {1}", symbol.Name, t));
+ }
+ }
+
+ 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 new VarExpr(v, tag);
+ }
+ else if (o is Type)
+ return new ConstantExpr(o);
+ else if (o is Symbol)
+ return new UnresolvedVarExpr((Symbol)o);
+
+ throw new Exception(string.Format("Unable to resolve symbol: {0} in this context", symbol));
+ }
+
+
+ private static Expr AnalyzeSeq(ISeq form, string name)
+ {
+ object exp = MacroexpandSeq1(form);
+ if (exp != form)
+ return GenerateAST(exp,name);
+
+ object op = RT.first(form);
+
+ if (op == null)
+ throw new ArgumentNullException("Can't call nil");
+
+ IFn inline = IsInline(op, RT.count(RT.next(form)));
+
+ if (inline != null)
+ return GenerateAST(inline.applyTo(RT.next(form)));
+
+ IParser p;
+ if (op.Equals(FN))
+ return FnExpr.Parse(form, name);
+ if ((p = GetSpecialFormParser(op)) != null)
+ return p.Parse(form);
+ else
+ return InvokeExpr.Parse(form);
+ }
+
+
+ static object Macroexpand1(object form)
+ {
+ return (form is ISeq)
+ ? MacroexpandSeq1((ISeq)form)
+ : form;
+ }
+
+ static object Macroexpand(object form)
+ {
+ object exf = Macroexpand1(form);
+ if (exf != form)
+ return Macroexpand(exf);
+ return form;
+ }
+
+ private static object MacroexpandSeq1(ISeq form)
+ {
+ object op = RT.first(form);
+
+ if (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.next());
+ }
+ 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 = RT.second(form);
+ if (MaybeType(target, false) != null)
+ target = RT.list(IDENTITY, target);
+ return RT.listStar(DOT, target, method, form.next().next());
+ // safe substitute: return RT.listStar(Compiler.DOT, RT.second(form), method, form.next().next());
+ }
+ 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.next());
+ }
+ }
+ 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.next());
+ }
+ }
+
+ }
+ return form;
+ }
+
+ internal static bool NamesStaticMember(Symbol sym)
+ {
+ return sym.Namespace != null && 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;
+ }
+
+ internal 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 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 void RegisterVar(Var v)
+ {
+ if (!VARS.IsBound)
+ return;
+ IPersistentMap varsMap = (IPersistentMap)VARS.deref();
+ Object id = RT.get(varsMap, v);
+ if (id == null)
+ {
+ VARS.set(RT.assoc(varsMap, v, RegisterConstant(v)));
+ }
+ }
+
+
+ internal static int RegisterConstant(Object o)
+ {
+ if (!CONSTANTS.IsBound)
+ return -1;
+ PersistentVector v = (PersistentVector)CONSTANTS.deref();
+ CONSTANTS.set(RT.conj(v, o));
+ return v.count();
+ }
+
+ internal static KeywordExpr RegisterKeyword(Keyword keyword)
+ {
+ if (!KEYWORDS.IsBound)
+ return new KeywordExpr(keyword);
+
+ IPersistentMap keywordsMap = (IPersistentMap)KEYWORDS.deref();
+ object id = RT.get(keywordsMap, keyword);
+ if (id == null)
+ KEYWORDS.set(RT.assoc(keywordsMap, keyword, RegisterConstant(keyword)));
+ return new KeywordExpr(keyword);
+ }
+
+
+ internal static LocalBinding RegisterLocal(Symbol sym, Symbol tag, Expr init)
+ {
+ int num = GetAndIncLocalNum();
+
+ LocalBinding b = new LocalBinding(num,sym, tag, init);
+
+ IPersistentMap localsMap = (IPersistentMap)LOCAL_ENV.deref();
+ LOCAL_ENV.set(RT.assoc(localsMap,b.Symbol, b));
+ FnMethod method = (FnMethod)METHODS.deref();
+ method.Locals = (IPersistentMap)RT.assoc(method.Locals,b, b);
+ method.IndexLocals = (IPersistentMap)RT.assoc(method.IndexLocals, num, b);
+ return b;
+ }
+
+ internal static int GetAndIncLocalNum()
+ {
+ int num = (int)NEXT_LOCAL_NUM.deref();
+ FnMethod m = (FnMethod)METHODS.deref();
+ if (num > m.MaxLocal)
+ m.MaxLocal = num;
+ NEXT_LOCAL_NUM.set(num + 1);
+ return num;
+ }
+
+ internal static LocalBinding ReferenceLocal(Symbol symbol)
+ {
+ if (!LOCAL_ENV.IsBound)
+ return null;
+
+ LocalBinding b = (LocalBinding)RT.get(LOCAL_ENV.deref(), symbol);
+ if (b != null)
+ {
+ FnMethod method = (FnMethod)METHODS.deref();
+ CloseOver(b, method);
+ }
+
+ return b;
+ }
+
+ static void CloseOver(LocalBinding b, FnMethod method)
+ {
+ if (b != null && method != null)
+ {
+ if (RT.get(method.Locals, b) == null)
+ {
+ method.Fn.Closes = (IPersistentMap)RT.assoc(method.Fn.Closes, b, b);
+ CloseOver(b, method.Parent);
+ }
+ else if (IN_CATCH_FINALLY.deref() != null)
+ {
+ method.LocalsUsedInCatchFinally = (PersistentHashSet)method.LocalsUsedInCatchFinally.cons(b.Index);
+ }
+ }
+ }
+
+ internal static Symbol TagOf(object o)
+ {
+ 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;
+ }
+
+
+ internal 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
+ {
+ // TODO: This uses Java [whatever notation. Figure out what to do here.
+ 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;
+ }
+
+ internal 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;
+ }
+ }
+ }
+ else if (tag is String)
+ {
+ // TODO: Find a general solution to this problem.
+ string strTag = (string)tag;
+ switch (strTag)
+ {
+ case "Object[]":
+ case "object[]":
+ t = typeof(object[]);
+ break;
+ case "Object[][]":
+ case "object[][]":
+ t = typeof(object[][]);
+ break;
+ }
+ }
+
+ if (t != null)
+ return t;
+
+ throw new ArgumentException("Unable to resolve typename: " + tag);
+ }
+
+ 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 Expr OptionallyGenerateMetaInit(object form, Expr expr)
+ //{
+ // Expr ret = expr;
+
+ // if (RT.meta(form) != null )
+ // {
+ // Expression metaExpr = new MetaExpr(expr, GenerateMapExpr(o.meta());
+ // ret = Expression.Call(Expression.Convert(expr, typeof(IObj)), Method_IObj_withMeta, metaExpr);
+ // }
+ // return ret;
+ //}
+
+ #endregion
+
+ #region Code generation
+
+ internal static Expression GenerateDlrExpression(GenContext context, Expr expr)
+ {
+
+ return expr.GenDlr(context);
+ }
+
+ #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 Interface to core.clj
+
+
+ // The following methods are named (and initial LC) for core.clj compatibility
+
+ public static object eval(object form)
+ {
+ LambdaExpression ast = Compiler.GenerateLambda(form, false);
+ return ast.Compile().DynamicInvoke();
+ }
+
+ public static object macroexpand1(object form)
+ {
+ return Macroexpand1(form);
+ }
+
+ #endregion
+
+ #region Loading
+
+ public static object loadFile(string filename)
+ {
+ FileInfo finfo = new FileInfo(filename);
+ if ( ! finfo.Exists )
+ throw new FileNotFoundException("Cannot find file to load",filename);
+
+ using (TextReader rdr = finfo.OpenText())
+ return load(rdr, finfo.FullName, finfo.Name);
+ }
+
+
+ public static object load(TextReader rdr)
+ {
+ return load(rdr, null, "NO_SOURCE_FILE");
+ }
+
+ public static object load(TextReader rdr, string sourcePath, string sourceName)
+ {
+ object ret = null;
+ object eofVal = new object();
+ object form;
+
+ LineNumberingTextReader lntr =
+ (rdr is LineNumberingTextReader) ? (LineNumberingTextReader)rdr : new LineNumberingTextReader(rdr);
+
+ Var.pushThreadBindings(RT.map(
+ //LOADER, RT.makeClassLoader(),
+ SOURCE_PATH, sourcePath,
+ SOURCE, sourceName,
+ RT.CURRENT_NS, RT.CURRENT_NS.deref(),
+ LINE_BEFORE, lntr.LineNumber,
+ LINE_AFTER, lntr.LineNumber
+ ));
+
+ try
+ {
+ while ((form = LispReader.read(lntr, false, eofVal, false)) != eofVal)
+ {
+ LINE_AFTER.set(lntr.LineNumber);
+ LambdaExpression ast = Compiler.GenerateLambda(form, false);
+ ret = ast.Compile().DynamicInvoke();
+ LINE_BEFORE.set(lntr.LineNumber);
+ }
+ }
+ catch (LispReader.ReaderException e)
+ {
+ throw new CompilerException(sourceName, e.Line, e.InnerException);
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+
+ 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;
+ //}
+ //// This one is mine.
+ //public static Delegate GenerateTypedDelegate(Type delegateType, Symbol optName, IPersistentVector argList, ISeq body)
+ //{
+
+ //}
+
+ #endregion
+
+ #region Compiling
+
+ public static object TestCompile(string filename)
+ {
+ using (TextReader rdr = File.OpenText(filename))
+ return Compile(rdr, null, filename);
+ }
+
+ internal static object Compile(TextReader rdr, string sourceDirectory, string sourceName)
+ {
+ if (COMPILE_PATH.deref() == null)
+ throw new Exception("*compile-path* not set");
+
+ object eofVal = new object();
+ object form;
+
+ string sourcePath = sourceDirectory == null ? sourceName : sourceDirectory + "\\" + sourceName;
+
+ LineNumberingTextReader lntr =
+ (rdr is LineNumberingTextReader) ? (LineNumberingTextReader)rdr : new LineNumberingTextReader(rdr);
+
+ Var.pushThreadBindings(RT.map(
+ SOURCE_PATH, sourcePath,
+ SOURCE, sourceName,
+ RT.CURRENT_NS, RT.CURRENT_NS.deref(),
+ LINE_BEFORE, lntr.LineNumber,
+ LINE_AFTER, lntr.LineNumber,
+ CONSTANTS, PersistentVector.EMPTY,
+ KEYWORDS, PersistentHashMap.EMPTY,
+ VARS, PersistentHashMap.EMPTY
+ ));
+ try
+ {
+ GenContext context = new GenContext(sourceName, sourceDirectory, CompilerMode.File);
+ TypeBuilder exprTB = context.ModuleBldr.DefineType("__REPL__", TypeAttributes.Class | TypeAttributes.Public);
+
+ List<string> names = new List<string>();
+
+ int i = 0;
+ while ((form = LispReader.read(lntr, false, eofVal, false)) != eofVal)
+ {
+ LINE_AFTER.set(lntr.LineNumber);
+ LambdaExpression ast = Compiler.GenerateLambda(context,form, false);
+
+ // Compile to assembly
+ MethodBuilder methodBuilder = exprTB.DefineMethod(String.Format("REPL_{0:0000}", i++),
+ MethodAttributes.Public | MethodAttributes.Static);
+ ast.CompileToMethod(methodBuilder);
+
+ names.Add(methodBuilder.Name);
+
+ // evaluate in this environment
+ ast.Compile().DynamicInvoke();
+ LINE_BEFORE.set(lntr.LineNumber);
+ }
+
+ Type exprType = exprTB.CreateType();
+
+ // Need to put the loader init in its own type because we can't generate calls on the MethodBuilders
+ // until after their types have been closed.
+
+ TypeBuilder initTB = context.ModuleBldr.DefineType("__Init__", TypeAttributes.Class | TypeAttributes.Public);
+
+
+ Expression pushNSExpr = Expression.Call(null, Method_Compiler_PushNS);
+ Expression popExpr = Expression.Call(null, Method_Var_popThreadBindings);
+
+ List<Expression> inits = new List<Expression>();
+ foreach (string name in names)
+ {
+ Expression call = Expression.Call(exprType, name, Type.EmptyTypes);
+ inits.Add(call);
+ }
+
+ Expression tryCatch = Expression.TryCatchFinally(Expression.Block(inits), popExpr);
+
+ Expression body = Expression.Block(pushNSExpr, tryCatch);
+
+ // create initializer call
+ MethodBuilder mbInit = initTB.DefineMethod("Initialize", MethodAttributes.Public | MethodAttributes.Static);
+ LambdaExpression initFn = Expression.Lambda(body);
+ initFn.CompileToMethod(mbInit);
+
+ initTB.CreateType();
+
+ context.AssyBldr.Save(sourceName + ".dll");
+ }
+ catch (LispReader.ReaderException e)
+ {
+ throw new CompilerException(sourceName, e.Line, e.InnerException);
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ return null;
+ }
+
+ public static void PushNS()
+ {
+ Var.pushThreadBindings(PersistentHashMap.create(Var.intern(Symbol.create("clojure.core"),
+ Symbol.create("*ns*")), null));
+ }
+
+
+ internal static bool LoadAssembly(FileInfo assyInfo)
+ {
+ Assembly assy = Assembly.LoadFile(assyInfo.FullName);
+ Type initType = assy.GetType("__Init__");
+ if (initType == null)
+ {
+ Console.WriteLine("Bad assembly");
+ return false;
+ }
+ try
+ {
+ initType.InvokeMember("Initialize", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public, Type.DefaultBinder, null, new object[0]);
+ return true;
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Error initializing {0}: {1}", assyInfo.FullName, e.Message);
+ return false;
+ }
+ }
+
+ #endregion
+
+ #region CompilerException
+
+ public sealed class CompilerException : Exception
+ {
+ public CompilerException(string source, int line, Exception cause)
+ : base(ErrorMsg(source, line, cause.ToString()), cause)
+ {
+ }
+
+ public override string ToString()
+ {
+ return Message;
+ }
+
+ static string ErrorMsg(string source, int line, string s)
+ {
+ return string.Format("{0} ({1}:{2})",s, source,line);
+ }
+
+ }
+
+ #endregion
+
+ #region Things to move elsewhere
+
+
+
+ internal static Type MaybePrimitiveType(Expr e)
+ {
+ if (e is MaybePrimitiveExpr && e.HasClrType)
+ {
+ Type t = e.ClrType;
+ if (Util.IsPrimitive(t))
+ return t;
+ }
+ return null;
+ }
+
+
+
+ internal static Expression GenArgArray(GenContext context, IPersistentVector args)
+ {
+ Expression[] exprs = new Expression[args.count()];
+
+ for (int i = 0; i < args.count(); i++)
+ {
+ Expr arg = (Expr)args.nth(i);
+ exprs[i] = Compiler.MaybeBox(arg.GenDlr(context));
+ }
+
+ Expression argArray = Expression.NewArrayInit(typeof(object), exprs);
+ return argArray;
+ }
+
+ internal static Expression[] GenTypedArgArray(GenContext context, ParameterInfo[] infos, IPersistentVector args)
+ {
+ Expression[] exprs = new Expression[args.count()];
+
+ for (int i = 0; i < infos.Length; i++)
+ {
+ Expr e = (Expr)args.nth(i);
+ // Java: this is in a try/catch, where the catch prints a stack trace
+ if (MaybePrimitiveType(e) == infos[i].ParameterType)
+ exprs[i] = ((MaybePrimitiveExpr)e).GenDlrUnboxed(context);
+ else
+ // Java follows this with: HostExpr.emitUnboxArg(fn, gen, parameterTypes[i]);
+ //exprs[i] = e.GenDlr(context);
+ exprs[i] = Expression.Convert(e.GenDlr(context), infos[i].ParameterType); ;
+ }
+ return exprs;
+ }
+
+
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/CljCompiler/Generator.cs b/ClojureCLR/Clojure/Clojure/CljCompiler/Generator.cs new file mode 100644 index 00000000..4746c3c9 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/CljCompiler/Generator.cs @@ -0,0 +1,2511 @@ +/**
+ * 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
+{
+ [Obsolete("This class will go away when I don't need to look at its code anymore")]
+ 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 is LazySeq)
+ form = RT.seq(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.next(), 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.next(), 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.next(form)));
+ IFn inline = null;
+
+ if (inline != null)
+ return Generate(inline.applyTo(RT.next(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.next());
+ int n = s == null ? 0 : s.count();
+ Expression[] args = new Expression[n];
+ for (int i = 0; s != null; s = s.next(), 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.next());
+ }
+ 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.next().next());
+ return RT.listStar(Compiler.DOT, RT.second(form), method, form.next().next());
+ }
+ 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.next());
+ }
+ }
+ 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.next());
+ }
+ }
+
+ }
+ 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 = RT.second(form);
+
+ 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");
+
+
+ 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.next(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.next(), i++)
+ {
+ if (s.next() == 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.next(); fs != null; fs = fs.next() )
+ {
+ 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(RT.next(RT.next(RT.next(f1))));
+ catches.Add(Expression.Catch(t, exParam, handler));
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ caught = true;
+ }
+ else // finally
+ {
+ if ( fs.next() != 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.next(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());
+ 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
+ {
+
+ public FnDef(object tag)
+ {
+ _tag = tag;
+ }
+
+ readonly object _tag;
+ public object Tag
+ {
+ get { return _tag; }
+ }
+
+ 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; }
+ }
+
+ bool _onceOnly = false;
+
+ public bool OnceOnly
+ {
+ get { return _onceOnly; }
+ set { _onceOnly = value; }
+ }
+
+ string _superName = null;
+
+ public string SuperName
+ {
+ get { return _superName; }
+ set { _superName = value.Replace('/','.'); }
+ }
+
+
+ public Type ImplType
+ {
+ get {
+ Type superNameType = null;
+ if ( _superName != null )
+ superNameType = RT.classForName(_superName);
+ return superNameType ?? ( IsVariadic ? typeof(RestFnImpl) : typeof(AFnImpl) );
+ }
+ }
+
+
+ // This naming convention drawn from the Java code.
+ internal void ComputeNames(ISeq form)
+ {
+ MethodDef enclosingMethod = (MethodDef)METHODS.deref();
+
+ string baseName = enclosingMethod != null
+ ? (enclosingMethod.Fn.Name + "$")
+ : (munge(CurrentNamespace.Name.Name) + "$");
+
+ if (RT.second(form) is Symbol)
+ _thisName = ((Symbol)RT.second(form)).Name;
+
+ _simpleName = (_thisName == null ? "fn" : munge(_thisName).Replace(".", "_DOT_")) + "__" + RT.nextID();
+ _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.next())
+ {
+ 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;
+ }
+
+ static readonly Keyword KW_ONCE = Keyword.intern(null,"once");
+ static readonly Keyword KW_SUPER_NAME = Keyword.intern(null,"super-name");
+
+ private static Expression GenerateFnExpr(ISeq form)
+ {
+ FnDef fn = new FnDef(TagOf(form));
+
+ if (((IMeta)form.first()).meta() != null)
+ {
+ fn.OnceOnly = RT.booleanCast(RT.get(RT.meta(form.first()), KW_ONCE));
+ fn.SuperName = (string)RT.get(RT.meta(form.first()), KW_SUPER_NAME);
+ }
+
+
+ fn.ComputeNames(form);
+
+ Symbol name = null;
+ if ( RT.second(form) is Symbol )
+ {
+ name = (Symbol)RT.second(form);
+ form = RT.cons(Compiler.FN, RT.next(RT.next(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.next(form));
+
+ // needs to be called after normalization
+ fn.IsVariadic = ComputeIsVariadicQuickly(RT.next(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.next(form); s != null; s = s.next())
+ {
+ 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.next(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)
+ {
+ // TODO: Supername metadata on form can change the implementation superclass.
+ Type fnType = fn.ImplType;
+
+ if (fn.SuperName != null)
+ Console.WriteLine("Crap");
+
+ ParameterExpression p1 = fn.ThisParam ?? Expression.Parameter(fnType, "____x");
+ List<Expression> exprs = new List<Expression>();
+
+ if ( p1.Type == typeof(AFnImpl) )
+ exprs.Add(Expression.Assign(p1, Expression.New(Ctor_AFnImpl_0)));
+ else if (p1.Type == typeof(RestFnImpl))
+ exprs.Add(Expression.Assign(p1, Expression.New(Ctor_RestFnImpl_1, Expression.Constant(variadicMethod.RequiredArity))));
+ else
+ exprs.Add(Expression.Assign(p1,Expression.New(p1.Type)));
+
+
+
+ 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(null); // compute tag from delegateType?
+ 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.next(RT.next(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.next(); s != null; s = s.next())
+ 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 (RT.Length(form) < 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.next(RT.next(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.next(); s != null; s = s.next(), 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.next(RT.next(form)); s != null; s = s.next(), 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/ArrayHelper.cs b/ClojureCLR/Clojure/Clojure/Lib/ArrayHelper.cs new file mode 100644 index 00000000..9537024e --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/ArrayHelper.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>
+ /// Adds methods that exist in Java Array class, for compatibility with core.clj.
+ /// </summary>
+ /// <remarks>The setters don't buy us much. In the JVM version, they can prevent some boxing.
+ /// Here, we don't have type-specific setters in class Array, so we'll end up boxing anywa.</remarks>
+ public static class ArrayHelper
+ {
+ //TODO: Rethink this.
+
+ public static void setInt(int[] a, int index, int value)
+ {
+ a[index] = value;
+ }
+
+ public static void setShort(short[] a, int index, short value)
+ {
+ a[index] = value;
+ }
+
+ public static void setLong(long[] a, int index, long value)
+ {
+ a[index] = value;
+ }
+
+ public static void setFloat(float[] a, int index, float value)
+ {
+ a[index] = value;
+ }
+
+ public static void setDouble(double[] a, int index, double value)
+ {
+ a[index] = value;
+ }
+
+ public static void setBoolean(bool[] a, int index, bool value)
+ {
+ a[index] = value;
+ }
+
+ public static void setChar(char[] a, int index, char value)
+ {
+ a[index] = value;
+ }
+
+ public static void setByte(byte[] a, int index, byte value)
+ {
+ a[index] = value;
+ }
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/LazySeq.cs b/ClojureCLR/Clojure/Clojure/Lib/LazySeq.cs new file mode 100644 index 00000000..0362f5b6 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/LazySeq.cs @@ -0,0 +1,272 @@ +/**
+ * 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
+{
+ public sealed class LazySeq : Obj, ISeq, ICollection, IList // Should we do IList -- has index accessor
+ {
+ #region Data
+
+ private IFn _fn;
+ private ISeq _s;
+
+ #endregion
+
+ #region C-tors & factory methods
+
+ public LazySeq(IFn fn)
+ {
+ _fn = fn;
+ }
+
+ private LazySeq(IPersistentMap meta, ISeq s)
+ : base(meta)
+ {
+ _fn = null;
+ _s = s;
+ }
+
+ #endregion
+
+ #region Object overrides
+
+ public override int GetHashCode()
+ {
+ return Util.hash(seq());
+ }
+
+ public override bool Equals(object obj)
+ {
+ ISeq s = seq();
+ if (s != null)
+ return s.equiv(obj);
+ else
+ return (obj is Sequential || obj is IList) && RT.seq(obj) == null;
+ }
+
+ #endregion
+
+ #region IObj members
+
+ public override IObj withMeta(IPersistentMap meta)
+ {
+ return new LazySeq(meta,seq());
+ }
+
+ #endregion
+
+ #region Seqable Members
+
+ /// <summary>
+ /// Gets an <see cref="ISeq"/>to allow first/rest/next iteration through the collection.
+ /// </summary>
+ /// <returns>An <see cref="ISeq"/> for iteration.</returns>
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public ISeq seq()
+ {
+ if (_fn != null)
+ {
+ _s = RT.seq(_fn.invoke());
+ _fn = null;
+ }
+ return _s;
+ }
+
+ #endregion
+
+ #region IPersistentCollection Members
+
+ public int count()
+ {
+ int c = 0;
+ for (ISeq s = seq(); s != null; s = s.next())
+ ++c;
+ return c;
+ }
+
+ IPersistentCollection IPersistentCollection.cons(object o)
+ {
+ return cons(o);
+ }
+
+ public IPersistentCollection empty()
+ {
+ return PersistentList.EMPTY;
+ }
+
+ public bool equiv(object o)
+ {
+ return Equals(o);
+ }
+
+ #endregion
+
+ #region ISeq Members
+
+ public object first()
+ {
+ seq();
+ if (_s == null)
+ return null;
+ return _s.first();
+ }
+
+ public ISeq next()
+ {
+ seq();
+ if (_s == null)
+ return null;
+ return _s.next();
+ }
+
+ public ISeq more()
+ {
+ seq();
+ if (_s == null)
+ return PersistentList.EMPTY;
+ return _s.more();
+ }
+
+ public ISeq cons(object o)
+ {
+ return RT.cons(o, seq());
+ }
+
+ #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.next())
+ 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.next(), 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
+ {
+ if ( index < 0 )
+ throw new ArgumentOutOfRangeException("Index must be non-negative.");
+
+ ISeq s = seq();
+ for (int i = 0; s != null; s = s.next(), i++)
+ if (i == index)
+ return s.first();
+ throw new ArgumentOutOfRangeException("Index past end of sequence.");
+ }
+ set
+ {
+ throw new InvalidOperationException();
+ }
+ }
+
+ #endregion
+
+ #region ICollection Members
+
+ public void CopyTo(Array array, int index)
+ {
+ if (array == null)
+ throw new ArgumentNullException("Array must not be null");
+ if (index < 0)
+ throw new ArgumentOutOfRangeException("Index must be non-negative.");
+ if (array.Rank > 1)
+ throw new ArgumentException("Array must not be multidimensional.");
+ if (index >= array.Length)
+ throw new ArgumentException("Index must be less than the length of the array.");
+ if (count() > array.Length - index)
+ throw new ArgumentException("Not enough available space from index to end of the array.");
+
+ ISeq s = seq();
+ for (int i = index; s != null; ++i, s = s.next())
+ array.SetValue(s.first(), i);
+ }
+
+ public int Count
+ {
+ get { return count(); }
+ }
+
+ public bool IsSynchronized
+ {
+ get { return true; }
+ }
+
+ public object SyncRoot
+ {
+ get { return this; }
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ public IEnumerator GetEnumerator()
+ {
+ return new SeqEnumerator(seq());
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Seqable.cs b/ClojureCLR/Clojure/Clojure/Lib/Seqable.cs new file mode 100644 index 00000000..9fb34356 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Seqable.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 produce an <see cref="ISeq"/>.
+ /// </summary>
+ public interface Seqable
+ {
+ /// <summary>
+ /// Gets an <see cref="ISeq"/>to allow first/rest/next iteration through the collection.
+ /// </summary>
+ /// <returns>An <see cref="ISeq"/> for iteration.</returns>
+ ISeq seq();
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Lib/Stream.cs b/ClojureCLR/Clojure/Clojure/Lib/Stream.cs new file mode 100644 index 00000000..e9fd531d --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Lib/Stream.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.Runtime.CompilerServices;
+
+namespace clojure.lang
+{
+ public sealed class Stream : Seqable, Streamable, Sequential
+ {
+ #region Data
+
+ static readonly ISeq NO_SEQ = new Cons(null, null);
+
+ ISeq _sequence = NO_SEQ;
+ readonly IFn _src;
+ readonly IFn _xform;
+ //Cons _pushed = null;
+ IFn _tap = null;
+
+ #endregion
+
+ #region C-tors and factory methods
+
+ public Stream(IFn src)
+ {
+ _src = src;
+ _xform = null;
+ }
+
+ public Stream(IFn xform, Stream src)
+ {
+ _src = src.tap();
+ _xform = xform;
+ }
+
+ #endregion
+
+ #region Seqable Members
+
+ public ISeq seq()
+ {
+ return sequence().seq();
+ }
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public ISeq sequence()
+ {
+ if (_sequence == NO_SEQ)
+ {
+ tap();
+ _sequence = makeSequence(_tap);
+ }
+ return _sequence;
+ }
+
+ class Seqer : AFn
+ {
+ IFn _tap;
+
+ public Seqer(IFn tap)
+ {
+ _tap = tap;
+ }
+
+ public override object invoke()
+ {
+ object v = _tap.invoke();
+ if (v == RT.EOS)
+ return null;
+ return new Cons(v, new LazySeq(this));
+ }
+ }
+
+ static ISeq makeSequence(IFn tap)
+ {
+ return RT.seq(new LazySeq(new Seqer(tap)));
+ }
+
+ #endregion
+
+ #region Streamable Members
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public Stream stream()
+ {
+ return this;
+ }
+
+ #endregion
+
+ #region Tapping
+
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public IFn tap()
+ {
+ if (_tap != null)
+ throw new InvalidOperationException("Stream already tapped");
+
+ return _tap = makeTap(_xform, _src);
+ }
+
+ class Tapper : AFn
+ {
+ IFn _xform;
+ IFn _src;
+
+ public Tapper(IFn xform, IFn src)
+ {
+ _xform = xform;
+ _src = src;
+ }
+
+ public override object invoke()
+ {
+ if (_xform == null)
+ return _src.invoke();
+
+ object v;
+ object xv;
+ do
+ {
+ v = _src.invoke();
+ if ( v == RT.EOS)
+ return v;
+ xv = _xform.invoke(v);
+ } while (xv == RT.SKIP);
+
+ return xv;
+ }
+
+ }
+
+ static IFn makeTap(IFn xform, IFn src)
+ {
+ return new Tapper(xform,src);
+ }
+
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Readers/LineNumberingTextReader.cs b/ClojureCLR/Clojure/Clojure/Readers/LineNumberingTextReader.cs new file mode 100644 index 00000000..d70d829a --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Readers/LineNumberingTextReader.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.IO;
+
+namespace clojure.lang
+{
+ /// <summary>
+ ///
+ /// </summary>
+ public class LineNumberingTextReader : PushbackTextReader, IDisposable
+ {
+ #region Data
+
+ private int _lineNumber = 1;
+ public int LineNumber
+ {
+ get { return _lineNumber; }
+ }
+
+
+ private int _prevPosition = 0;
+ private int _position = 0;
+ public int Position
+ {
+ get { return _position; }
+ }
+
+ private bool _prevLineStart = true;
+ private bool _atLineStart = true;
+ public bool AtLineStart
+ {
+ get { return _atLineStart; }
+ }
+
+
+ #endregion
+
+ #region c-tors
+
+ public LineNumberingTextReader(TextReader reader)
+ : base(reader)
+ {
+ }
+
+ #endregion
+
+ #region Basic reading
+
+ public override int Read()
+ {
+ int ret = base.Read();
+
+ _prevLineStart = _atLineStart;
+
+ if (ret == -1)
+ {
+ _atLineStart = true;
+ return ret;
+ }
+
+ _atLineStart = false;
+ ++_position;
+
+ if (ret == '\r')
+ {
+ if (Peek() == '\n')
+ ret = BaseReader.Read();
+ else
+ {
+ NoteLineAdvance();
+ }
+ }
+
+ if ( ret == '\n' )
+ NoteLineAdvance();
+
+ return ret;
+ }
+
+
+
+ private void NoteLineAdvance()
+ {
+ _atLineStart = true;
+ _lineNumber++;
+ _prevPosition = _position - 1;
+ _position = 0;
+ }
+
+
+
+ //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++;
+ // _lastLinePosition = _position;
+ // _position = 0;
+ // }
+ // return line;
+ //}
+
+ //public override string ReadToEnd()
+ //{
+ // string result = _baseReader.ReadToEnd();
+ // HandleLines(result);
+ // return result;
+ //}
+
+
+ #endregion
+
+ #region Unreading
+
+ public override void Unread(int ch)
+ {
+ base.Unread(ch);
+
+ --_position;
+
+ if (ch == '\n')
+ {
+ --_lineNumber;
+ _position = _prevPosition;
+ _atLineStart = _prevLineStart;
+ }
+ }
+
+ #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;
+ // _lastLinePosition = _position;
+ // _position = 0;
+ // }
+ // else
+ // ++_position;
+ //}
+
+
+ //private void HandleLines(string result)
+ //{
+ // foreach (char c in result)
+ // if (c == '\n')
+ // {
+ // ++_lineNumber;
+ // _lastLinePosition = _position;
+ // _position = 0;
+ // }
+ // else
+ // ++_position;
+ //}
+
+ #endregion
+
+ #region Lifetime methods
+
+ public override void Close()
+ {
+ _baseReader.Close();
+ base.Close();
+ }
+
+ void IDisposable.Dispose()
+ {
+ _baseReader.Dispose();
+ base.Dispose();
+ }
+
+ #endregion
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Readers/PushbackTextReader.cs b/ClojureCLR/Clojure/Clojure/Readers/PushbackTextReader.cs new file mode 100644 index 00000000..4761e4cc --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Readers/PushbackTextReader.cs @@ -0,0 +1,92 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+
+namespace clojure.lang
+{
+ public class PushbackTextReader : TextReader, IDisposable
+ {
+ #region Data
+
+ protected TextReader _baseReader;
+ protected TextReader BaseReader
+ {
+ get { return _baseReader; }
+ }
+
+ protected int _unreadChar;
+ protected bool _hasUnread = false;
+
+ #endregion
+
+ #region C-tors
+
+ public PushbackTextReader(TextReader reader)
+ {
+ _baseReader = reader;
+ }
+
+ #endregion
+
+ #region Lookahead
+
+ public override int Peek()
+ {
+ return _baseReader.Peek();
+ }
+
+ #endregion
+
+ #region Unreading
+
+ public virtual void Unread(int ch)
+ {
+ if (_hasUnread)
+ throw new IOException("Can't unread a second character.");
+
+ _unreadChar = ch;
+ _hasUnread = true;
+
+ }
+
+
+ #endregion
+
+ #region Basic reading
+
+ public override int Read()
+ {
+ int ret;
+ if (_hasUnread)
+ {
+ ret = _unreadChar;
+ _hasUnread = false;
+ }
+ else
+ ret = _baseReader.Read();
+
+ return ret;
+ }
+
+ #endregion
+
+ #region Lifetime methods
+
+ public override void Close()
+ {
+ _baseReader.Close();
+ base.Close();
+ }
+
+ void IDisposable.Dispose()
+ {
+ _baseReader.Dispose();
+ base.Dispose();
+ }
+
+ #endregion
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Resources/version.txt b/ClojureCLR/Clojure/Clojure/Resources/version.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Resources/version.txt diff --git a/ClojureCLR/Clojure/Clojure/Runtime/ClojureConsoleOptions.cs b/ClojureCLR/Clojure/Clojure/Runtime/ClojureConsoleOptions.cs new file mode 100644 index 00000000..144ad3e1 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Runtime/ClojureConsoleOptions.cs @@ -0,0 +1,13 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Scripting.Hosting.Shell;
+
+namespace clojure.runtime
+{
+ public sealed class ClojureConsoleOptions : ConsoleOptions
+ {
+ // we may have some options someday
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Runtime/ClojureHostUtils.cs b/ClojureCLR/Clojure/Clojure/Runtime/ClojureHostUtils.cs new file mode 100644 index 00000000..1d9fb149 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Runtime/ClojureHostUtils.cs @@ -0,0 +1,37 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Scripting.Hosting;
+
+namespace clojure.runtime
+{
+ /// <summary>
+ /// Provides helpers for interacting with ClojureCRL, especially DLR hosting.
+ /// </summary>
+ public class ClojureHostUtils
+ {
+
+
+ public static LanguageSetup/*!*/ CreateLanguageSetup(IDictionary<string, object> options)
+ {
+ var setup = new LanguageSetup(
+ typeof(ClojureContext).AssemblyQualifiedName,
+ ClojureContext.ClojureDisplayName,
+ ClojureContext.ClojureNames.Split(';'),
+ ClojureContext.ClojureFileExtensions.Split(';')
+ );
+
+ if (options != null)
+ {
+ foreach (var entry in options)
+ {
+ setup.Options.Add(entry.Key, entry.Value);
+ }
+ }
+
+ return setup;
+ }
+
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Runtime/ClojureOptionsParser.cs b/ClojureCLR/Clojure/Clojure/Runtime/ClojureOptionsParser.cs new file mode 100644 index 00000000..3bcea4e6 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Runtime/ClojureOptionsParser.cs @@ -0,0 +1,12 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Scripting.Hosting.Shell;
+
+namespace clojure.runtime
+{
+ public class ClojureOptionsParser : OptionsParser<ClojureConsoleOptions>
+ {
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/Runtime/Properties.cs b/ClojureCLR/Clojure/Clojure/Runtime/Properties.cs new file mode 100644 index 00000000..e3fdd087 --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/Runtime/Properties.cs @@ -0,0 +1,67 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+
+namespace clojure.runtime
+{
+ /// <summary>
+ /// Implements part of the functionaligy of java.util.Properties.
+ /// </summary>
+ public class Properties : Dictionary<string,string>
+ {
+
+ public string getProperty(string key)
+ {
+ string value = null;
+ TryGetValue(key, out value);
+ return value;
+ }
+
+ public void LoadFromString(string content)
+ {
+ using (TextReader rdr = new StringReader(content))
+ {
+ Load(rdr);
+ }
+ }
+
+ public void Load(string fileName)
+ {
+ using ( TextReader rdr = File.OpenText(fileName) )
+ {
+ Load(rdr);
+ }
+ }
+
+ public void Load(TextReader rdr)
+ {
+ Clear();
+
+ string line;
+ while ((line = rdr.ReadLine()) != null)
+ {
+ line = line.Trim();
+ if (string.IsNullOrEmpty(line) ||
+ line.StartsWith(";") ||
+ line.StartsWith("#") ||
+ line.StartsWith("'") ||
+ !line.Contains("="))
+ continue;
+
+ int index = line.IndexOf('=');
+ string key = line.Substring(0, index).Trim();
+ string value = line.Substring(index + 1).Trim();
+
+ if ((value.StartsWith("\"") && value.EndsWith("\"")) ||
+ (value.StartsWith("'") && value.EndsWith("'")))
+ {
+ value = value.Substring(1, value.Length - 2);
+ }
+
+ this[key] = value;
+ }
+ }
+ }
+}
diff --git a/ClojureCLR/Clojure/Clojure/app.config b/ClojureCLR/Clojure/Clojure/app.config new file mode 100644 index 00000000..9c3d200c --- /dev/null +++ b/ClojureCLR/Clojure/Clojure/app.config @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+</configuration>
\ No newline at end of file diff --git a/ClojureCLR/Clojure/Simple.Console/Properties/AssemblyInfo.cs b/ClojureCLR/Clojure/Simple.Console/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..513c88d4 --- /dev/null +++ b/ClojureCLR/Clojure/Simple.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("Simple.Console")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Simple.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("2ebfb04e-e46e-4146-9fba-0ed41df0111e")]
+
+// 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/Simple.Console/Simple.Console.csproj b/ClojureCLR/Clojure/Simple.Console/Simple.Console.csproj new file mode 100644 index 00000000..f5058bf9 --- /dev/null +++ b/ClojureCLR/Clojure/Simple.Console/Simple.Console.csproj @@ -0,0 +1,73 @@ +<?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>{B57B71C6-A952-41A1-ABF1-9B9BACDD12ED}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Simple.Console</RootNamespace>
+ <AssemblyName>Simple.Console</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </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="SimpleConsole.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\DLR_Main\Src\Runtime\Microsoft.Scripting.Core\Microsoft.Scripting.Core.csproj">
+ <Project>{2AE75F5A-CD1F-4925-9647-AF4D1C282FB4}</Project>
+ <Name>Microsoft.Scripting.Core</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\DLR_Main\Src\Runtime\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>
+ <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/Simple.Console/SimpleConsole.cs b/ClojureCLR/Clojure/Simple.Console/SimpleConsole.cs new file mode 100644 index 00000000..357cd02d --- /dev/null +++ b/ClojureCLR/Clojure/Simple.Console/SimpleConsole.cs @@ -0,0 +1,108 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using clojure.lang;
+using System.Diagnostics;
+using System.IO;
+using Microsoft.Linq.Expressions;
+using Microsoft.Scripting.Generation;
+
+namespace clojure.console
+{
+ class SimpleConsole
+ {
+ static void Main(string[] args)
+ {
+ new SimpleConsole().Run();
+ }
+
+
+
+ private void Run()
+ {
+ Initialize();
+ RunInteractiveLoop();
+ }
+
+
+ private void Initialize()
+ {
+ Stopwatch sw = new Stopwatch();
+ sw.Start();
+
+ Var.pushThreadBindings(
+ RT.map(RT.CURRENT_NS, RT.CURRENT_NS.deref()));
+ try
+ {
+
+
+ //LoadFromStream(new StringReader(clojure.lang.Properties.Resources.core),false);
+ //RT.load("/core");
+ //LoadFromStream(new StringReader(clojure.lang.Properties.Resources.core_print), false);
+ //LoadFromStream(new StringReader(clojure.lang.Properties.Resources.test), false);
+
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+
+ sw.Stop();
+ Console.WriteLine("Loading took {0} milliseconds.", sw.ElapsedMilliseconds);
+
+ }
+
+ public object LoadFromStream(PushbackTextReader rdr, bool addPrint)
+ {
+ object ret = null;
+ object eofVal = new object();
+ object form;
+ while ((form = LispReader.read(rdr, false, eofVal, false)) != eofVal)
+ {
+ try
+ {
+ LambdaExpression ast = Compiler.GenerateLambda(form, addPrint);
+ ret = ast.Compile().DynamicInvoke();
+ }
+ catch (Exception ex)
+ {
+ if (addPrint)
+ {
+ Exception root = ex;
+ while (root.InnerException != null)
+ root = root.InnerException;
+
+ Console.WriteLine("Error evaluating {0}: {1}", form, root.Message);
+ Console.WriteLine(root.StackTrace);
+ }
+ }
+ }
+ return ret;
+ }
+
+
+ private void RunInteractiveLoop()
+ {
+ Var.pushThreadBindings(RT.map(
+ RT.CURRENT_NS, RT.CURRENT_NS.deref(),
+ RT.WARN_ON_REFLECTION, RT.WARN_ON_REFLECTION.deref(),
+ RT.PRINT_META, RT.PRINT_META.deref(),
+ RT.PRINT_LENGTH, RT.PRINT_LENGTH.deref(),
+ RT.PRINT_LEVEL, RT.PRINT_LEVEL.deref(),
+ Compiler.COMPILE_PATH, Environment.GetEnvironmentVariable("clojure.compile.path" ?? "classes")
+ ));
+
+ try
+ {
+ LoadFromStream(new LineNumberingTextReader(Console.In), true);
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ }
+
+
+ }
+}
|