richhickey / clojure-clr Goto Github PK
View Code? Open in Web Editor NEWA port of Clojure to the CLR, part of the Clojure project
Home Page: http://clojure.org
A port of Clojure to the CLR, part of the Clojure project
Home Page: http://clojure.org
In the Clojure JVM REPL:
user=> (try (throw (Exception. "Got it.")) (catch Exception ex (println ex)))
#
nil
In the Clojure CLR REPL:
user=> (try (throw (Exception. "Got it.")) (catch Exception ex (println ex)))
.... throws an exception, snipped stack trace because of easy reproduction ...
user=> (try (do (throw (Exception. "Got it.")) 1) (catch Exception ex (println ex)))
#<Exception System.Exception: Got it.
at lambda_method(Closure )>
nil
[EDIT: changed the first line in the CLR REPL so it's identical to the line in the JVM REPL.]
With the arguments in the other order, it works okay, i.e. APersistentMap.Equals is okay. The problem is with S.C.Hashtable.Equals.
Allows us to use Invoke instead of DynamicInvoke. A very small improvement, mostly likely.
We are using java.math.BigDecimal from vjslib.dll, part of the Visual J# Redistributable package. Need to get rid of this to run on Mono. Also, it is based on an older point release of the Java library and so is missing some needed functionality.
clojure.lang.Compiler/LOCAL_ENV is public in Clojure, but internal in ClojureCLR. This means that tricks like debug-repl https://gist.github.com/256129/981be4991d4d47fe856476b4c00d046c31823bd6
are not possible using ClojureCLR.
Maybe LOCAL_ENV & Co should be public in ClojureCLR too?
(they could in theory be accesed using explicit reflection and BindingFlags.NonPublic, but that seems to be overkill).
The Java version uses java.util.concurrent.CountDownLatch. Either use the new concurrency library for the CLR or implement the equivalent directly. The concurrency lib is at http://msdn.microsoft.com/en-us/concurrency/default.aspx. Using this would require distribution with this project. I haven’t thought about how hard it would be to implement the equivalent.
If an exception is thrown from inside a dosync transaction, the stacktrace stops at the transaction call. I confirmed this with an exception thrown from the dosync form and from a function called from the dosync form. In the stracktrace below, irc.clj line 74 contains just "(dosync".
;; expecting more frames here
at clojure.lang.LockingTransaction.Run(IFn fn) in C:\Users\Shawn\dev\clojure-clr\Clojure\Clojure\Lib\LockingTransaction.cs:line 645
at clojure.lang.LockingTransaction.runInTransaction(IFn fn) in C:\Users\Shawn\dev\clojure-clr\Clojure\Clojure\Lib\LockingTransaction.cs:line 524
at irc$fn__109$user_nick__111.__invokeHelper_2(irc$fn__109$user_nick__111_base this, Object user, Object args) in irc.clj:line 74
at irc$fn__109$user_nick__111.invoke(Object , Object )
at clojure.lang.Var.invoke(Object arg1, Object arg2) in C:\Users\Shawn\dev\clojure-clr\Clojure\Clojure\Lib\Var.cs:line 628
at irc$fn__231$handle__233.__invokeHelper_3(irc$fn__231$handle__233_base this, Object user, Object transitions, Object line) in irc.clj:line 171
at irc$fn__231$handle__233.invoke(Object , Object , Object )
at irc$fn__239$user_loop__241.__invokeHelper_2(irc$fn__239$user_loop__241_base this, Object user, Object socket) in irc.clj:line 180
at irc$fn__239$user_loop__241.invoke(Object , Object )
at irc$fn__248$spawn__250$fn__251.__invokeHelper_0(irc$fn__248$spawn__250$fn__251_base this) in irc.clj:line 194
... or there is an alternative method I should use?
(I tried converting clojure.contrib.repl-utils/expression-info
(which uses Compiler/analyze
) for use on the CLR... and failed :-) ).
The format function in core.clj depends on Java’s String.format, a printf-style formatter. There is no BCL equivalent, so we will have to implement it.
There are several manual steps in the build process. Need to automate them. These include:
(1) moving *.clj files to appropriate places when changed. (2) Code a step to run Clojure.Compile on the bootstrap files to create new DLLs, which will then need to be distributed. (3) Set up a test environment for the CLJ tests.
Apparently, this is because of a weird interaction between concat
, drop
and take
.
Steps to reproduce:
user=> (concat (drop 1 [1 2]) (take 1 [1 2]))
(1 2 1)
Which is obviously incorrect.
Hi,
Overall, I managed to install and build ClojureCLR without much trouble. Cool.
My main suggestion is to give a default layout of the project in the install page, e.g. something like "download the latest dlr to DLR_Main under the folder where you installed ClojureCLR. Build the DLR solution. Now build the Clojure solution". The problem I ran into was that after linking the DLR projects to where I had installed them, I needed to remove and add some references in Visual Studio, and also it seems that some of the DLR output folders were in unexpected places if II didn't put the DLR_Main folder where the ClojureCLR solution expects it.
The bootstrap compiler crashed because it could not found junit.clj. The fix is simple: the properties of this file in Clojure.Source are not set to "Copy If Newer".
Hope this helps.
Kurt
Right now, just starting our own threads and forgetting it. We need to manage a thread pool. Also need to implement 'shutdown'.
Most likely these could be done fairly easily at this point. I just haven’t gotten to them yet.
proxy-super
is broken (calling the base method from the proxy code calls the proxy method for virtual methods).
Cause: replacement of gen.Emit(OpCodes.Call...
with ILGen.EmitCall
, which emits callvirt
if the method is a virtual one.
Proposed fix:
diff --git a/Clojure/Clojure/CljCompiler/GenProxy.cs b/Clojure/Clojure/CljCompiler/GenProxy.cs
index fab6352..7f4b0d8 100644
--- a/Clojure/Clojure/CljCompiler/GenProxy.cs
+++ b/Clojure/Clojure/CljCompiler/GenProxy.cs
@@ -320,7 +320,7 @@ namespace clojure.lang
gen.EmitLoadArg(0); // gen.Emit(OpCodes.Ldarg_0);
for (int i = 0; i < parmCount; i++)
gen.EmitLoadArg(i + 1); // gen.Emit(OpCodes.Ldarg, i + 1);
- gen.EmitCall(m); // gen.Emit(OpCodes.Call, m);
+ gen.Emit(OpCodes.Call, m);
}
else
{
(line 323 in GenProxy.cs)
Maybe there are other related issues - using ILGen.EmitCall
isn't the same as Emit(OpCodes.Call...)
.
Needs translation into CLR System.IO calls.
ClojureJVM has no problem with this:
(defn f [#^String x] x)
(f 12) => 12
At present, ClojureCLR will complain about not being able to case 12 to a String. This is due to local variables being typed in the DLR expressions. This affects other binding forms such as 'let'.
When called from ns:
(ns foo (:gen-class :name "Foo"))
Part of the standard bootstrap environment, in clojure/core_proxy.clj. Heavily tied to Java reflection and the ASM 3.0 bytecode library. Significant rewrite effort.
Might want to wait until RH finishes the new-new project.
(import ... ) of nested classes, such as Compiler+CompilerException does not work. This is actually a problem with RT.classForName that will bite us in other places eventually.
(set! var true) fails because of not boxing the bool value.
If a project is to become popular it has to offer at least a:
I would help but unfortunately am only a newbie in Clojure, but if you need me in this capacity I am available.
In CLR Clojure (1.2.0):
user=> (int (/ 4 5))
1
In Java Clojure (1.2.0):
user=> (int (/ 4 5))
0
We need an installer that installs a directory with all the DLLs (include the DLR build) + CLJ sources.
Part of the standard bootstrap environment, in clojure/genclass.clj. Heavily tied to Java reflection and the ASM 3.0 bytecode library. Significant rewrite effort.
Might want to wait until RH finishes the new-new project.
Whoops. Left out one of the pieces of core_proxy. Simple (compared to the rest of the file) translation problem.
I have used three versions of numeric conversion to implement int, long, float, etc.
(a) Convert.ToXXX
(b) switch on TypeCode
(c) if/elseif on Type
Results are mixed. More analysis and benchmarking needed.
This introduces an extra layer to analyze/compile, and messes up the class names generated for fns. The JVM version fakes this by putting a dummy FnExpr on the dynamic bindings before compiling a form.
Depends on clojure.lang.Agent/soloExecutor (need to implement), but more worryingly on java.util.concurrent.Future. The CTP concurrency runtime has this, or we implement.
Hi,
While following the Clojure wikibook, I found:
user=> 0.1
1
This is because my culture is set differently. For parsing program text culture should not be taken into account, and I managed to achieve this by changing LispReader.cs as follows:
656: radix = Int32.Parse( m.Groups[6].Value, System.Globalization.CultureInfo.InvariantCulture );
679: return (object)Double.Parse(s,System.Globalization.CultureInfo.InvariantCulture);
That works, i.e.
user=> 0.1
0,1
which looks weird but seems correct - problem is now that the output is still formatted according to culture conventions. Again, a REPL should not take culture into account when printing I guess (especially since comma is reserved for tuple syntax so i just looks weird), but I haven't been able to find where to fix this.
Kurt
Used in ClojureJVM to provide standardized values for boxed true and false. Equivalent to Boolean.TRUE and Boolean.FALSE. I had misinterpreted this at the very beginning, so I had made RT.T/F to be bool values, meaning they were worse than useless. Changing them to type Object will provide standardized boxed values for true and false. Is the static field lookup faster than the boxing? One wonders.
Part of the standard environment bootstrap. The JVM implementation relies extensively on SAX parsers as defined in org.xml.sax and javax.xml.parsers. This will require non-trivial rewriting to make it rely on BCL equivalents.
This will simplify loading dependent assemblies.
We'll need different versions of this for .Net 4
Depends on java.util.concurrent.BlockingQueue and .LinkedBlockingQueue.
We need consideration of how to handle multi-dimensional arrays versus ragged arrays. JVM only has ragged arrays.
(read (proxy [clojure.lang.PushbackTextReader] [(System.IO.StringReader. "100")]))
ends up in:
System.MissingMethodException: Method 'System.String.replace' not found.
at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)
at System.Type.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, Object target, Object[] args)
at clojure.lang.Reflector.CallInstanceMethod(String methodName, Object target, Object[] args) in C:\Temp\clojure-clr\Clojure\Clojure\Runtime\Reflector.cs:line 157
at clojure/core$fn__8346$proxy_name__8348.__invokeHelper_2(clojure/core$fn__8346$proxy_name__8348_base this, Object super, Object interfaces)
at clojure/core$fn__8370$get_proxy_class__8372.__invokeHelper_0v(clojure/core$fn__8370$get_proxy_class__8372_base this, Object bases)
at clojure.lang.RestFn.applyTo(ISeq args) in C:\Temp\clojure-clr\Clojure\Clojure\Lib\RestFn.cs:line 177
at clojure/core$fn__4990$apply__4992.__invokeHelper_2(clojure/core$fn__4990$apply__4992_base this, Object f, Object args)
at clojure/core$fn__8403$proxy__8405.__invokeHelper_2v(clojure/core$fn__8403$proxy__8405_base this, Object class_and_interfaces, Object args, Object fs)
at clojure.lang.RestFn.invoke(Object arg1, Object arg2) in C:\Temp\clojure-clr\Clojure\Clojure\Lib\RestFn.cs:line 469
at clojure.lang.Var.invoke(Object arg1, Object arg2) in C:\Temp\clojure-clr\Clojure\Clojure\Lib\Var.cs:line 607
at clojure.lang.AFn.ApplyToHelper(IFn ifn, ISeq arglist) in C:\Temp\clojure-clr\Clojure\Clojure\Lib\AFn.cs:line 223
at clojure.lang.Var.applyTo(ISeq arglist) in C:\Temp\clojure-clr\Clojure\Clojure\Lib\Var.cs:line 735
at clojure.lang.Compiler.MacroexpandSeq1(ISeq form) in C:\Temp\clojure-clr\Clojure\Clojure\CljCompiler\Compiler.cs:line 497
at clojure.lang.Compiler.AnalyzeSeq(ISeq form, String name, Boolean isRecurContext) in C:\Temp\clojure-clr\Clojure\Clojure\CljCompiler\Compiler.cs:line 432 (NO_SOURCE_FILE:230)
(on the JVM, (read (proxy [java.io.PushbackReader] [(java.io.StringReader. "100")]))
works fine).
This actually happened when trying to convert clojure.contrib.repl-utils/get-source to use the CLR, so the proxy had redefined methods, unlike the simplified example here.
Just a suggestion:
Maybe the .Close
call used in with-open should be replaced with .Dispose
.
Since we also have IDispose
, maybe a type-hint could be used to eliminate reflection.
Since this breaks the Clojure meaning of with-open
(use .close()
and reflection), maybe another macro with a similar name should be added to clojure-clr? using
? :-p
They lost to chunked seqs. Vestiges to be removed.
Check the initialization/usage of the environment variable for this.
When using NUnit and Excel Assembly.GetEntryAssembly() is not present.
RT.GetFindFilePaths() should do a null check before calling Location and yielding.
Problem with IDictionary entries versus IMapEntry. This will also affect (vals ...) and some others.
ClojureCLR should have :line available for defns and defmacros to be compatible with Clojure.
Also, :source-span is missing on defns/defmacros (apparently metadata is lost on macro expansion?).
I've run Clojure for CLR successfully before on a different machine.
Just downloaded this distribution - https://github.com/downloads/richhickey/clojure-clr/clojure-clr-1.3.0-alpha1-debug-4.0.zip
Running Clojure.Main.exe I get this:
Unhandled Exception: System.TypeInitializationException: The type initializer for 'Clojure.CljMain' threw an exception. ---> System.TypeInitializationException:
The type initializer for 'clojure.lang.RT' threw an exception. ---> System.NotSupportedException: An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch. See http://go.microsoft.com/fwlink/?LinkId=155569 for more information.
at System.Reflection.RuntimeAssembly.nLoadFile(String path, Evidence evidence
)
at System.Reflection.Assembly.LoadFile(String path)
at clojure.lang.Compiler.LoadAssembly(FileInfo assyInfo) in C:\work\clojure-c
lr\Clojure\Clojure\CljCompiler\Compiler.cs:line 1261
at clojure.lang.RT.load(String relativePath, Boolean failIfNotFound) in C:\wo
rk\clojure-clr\Clojure\Clojure\Lib\RT.cs:line 1980
at clojure.lang.RT.load(String relativePath) in C:\work\clojure-clr\Clojure\C
lojure\Lib\RT.cs:line 1960
at clojure.lang.RT.DoInit() in C:\work\clojure-clr\Clojure\Clojure\Lib\RT.cs:
line 543
at clojure.lang.RT..cctor() in C:\work\clojure-clr\Clojure\Clojure\Lib\RT.cs:
line 538
--- End of inner exception stack trace ---
at clojure.lang.RT.var(String ns, String name)
at Clojure.CljMain..cctor() in C:\work\clojure-clr\Clojure\Clojure.Main\Main.
cs:line 26
--- End of inner exception stack trace ---
at Clojure.CljMain.Main(String[] args)
Following directions on the Microsoft page, I added a Clojure.Main.exe.config file with the following, which makes it run OK:
We need to mimic more closely the JVM approach, I'm afraid.
CLR version says the following is okay (but it is not):
(defn f [x](loop [y 12]
%28if %28< y 20%29
%28do %28do %28recur %28inc y%29%29%29 y%29
y%29))
The inner do is flagging tail recursion on the recur call.
This is part of the standard bootstrap environment. This is a Swing app. This will need to be rewritten completely to make it work with WinForms or WPF.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.