using clojure.clr.api; using clojure.lang; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; /* This appliction shows how C# can send an arbitrary string to Clojure to be evaluated. * This works like a REPL. But it's event driven, not in a loop. And the code comes * from a C# string, not a stream. * * * Things to try: * * (+ 2 3) * Like 2 + 3 in C# * * (+ 2 (* 3 4)) * Like 2 + 3 * 4 in C# * * (import (System.Windows.Forms MessageBox)) * (MessageBox/Show "Hi from clojure-clr!" "Clojure-CLR Dialog") * Display a message box. * From http://www.myclojureadventure.com/2011/10/getting-started-with-clojure-clr.html * * (System.Windows.Forms.MessageBox/Show "Hi from clojure-clr!" "Clojure-CLR Dialog") * Same result as above, but all in one command. (An older version of this program only * executed the first command it saw and ignored the rest.) * * WindowsFormsApplication1.MainForm * Grab the main form's class. * * WindowsFormsApplication1.MainForm/Instance * Grab the main form object. * * (.-_intField WindowsFormsApplication1.MainForm/Instance) * Read the public field named _intField. * See http://clojure.org/reference/java_interop and http://clojure.org/reference/java_interop for general info about accessing C#. * * (set! (.-_intField WindowsFormsApplication1.MainForm/Instance) (* 2 3)) * Set the value of the public field named _intField to 6. * * (.GetTextBox WindowsFormsApplication1.MainForm/Instance)) * Grab the text box at the top of the main form. * The actual field textBox1 is private, so I made a public function GetTextBox() to access it. * * (macroexpand '(.GetTextBox WindowsFormsApplication1.MainForm/Instance))) * The underlying command is a single dot. That will look up a field or method. * .GetTextBox is a macro that makes things a little easier to write. * * (.Text (.GetTextBox WindowsFormsApplication1.MainForm/Instance))) * Read the text from the text box. * * (set! (.Text (.GetTextBox WindowsFormsApplication1.MainForm/Instance)) "Hello World") * Set the text in the text box. * * (. WindowsFormsApplication1.MainForm/Instance IntProperty) * (set! (. WindowsFormsApplication1.MainForm/Instance IntProperty) 99) * (set! (.-IntProperty WindowsFormsApplication1.MainForm/Instance) 199) * (.-IntProperty WindowsFormsApplication1.MainForm/Instance) * You access a property the same way you access a field. * * (do * (use '[clojure.string :only (join)]) * (let [window WindowsFormsApplication1.MainForm/Instance * input (.Text (.GetTextBox window)) * output (join ["The user said {" input "}."])] * (set! (.Text (.GetLabel window)) output)) * 'Success! * ) * This examples goes full circle. The clojure code reads a value from the .NET form, * manipulates the value, and then displays the result back on the form. This example * uses the clojure "do" command so we can execute the entire example at once, as one * big command. ("do" is no longer required here as we are now using "load-string" in * the code below.) * * (do * (use '[clojure.string :only (join)]) * (let [window WindowsFormsApplication1.MainForm/Instance * input (.. window (GetTextBox) Text) * output (join ["The user said {" input "}."])] * (set! (.. window (GetLabel) Text) output)) * 'Success! * ) * This does the same thing as the previous example. This uses clojure's .. command * to make the code look a little more like C++, C#, Java, etc. Read the value of * the variable named window, then call the GetTextBox() method on that with no * arguments. Then look for the Text property in the result of GetTextBox(). * In C# that would be: window.GetTextBox().Text */ namespace WindowsFormsApplication1 { public partial class MainForm : Form { public static MainForm Instance { get; private set; } public MainForm() { InitializeComponent(); Instance = this; } private static readonly IFn EVAL = Clojure.var("clojure.core", "eval"); private static readonly IFn READ_STRING = Clojure.var("clojure.core", "read-string"); // Note: The Clojure read-string function is not the same as the C# Clojure.read() // function. In some cases, like "(+ 1 2)" or "(quote (+ 1 2))", they give the same // result. But read-string will expand macros. For example read-string will expand // "'(+ 1 2)" into "(quote (+ 1 2))", but Clojure.read() will not. Since our goal // is to execute code, like a REPL, we need to use read-string. My solution for // reading and executing a string was inspired by // http://stackoverflow.com/questions/6672934/how-to-call-clojure-macros-from-java#6673881 // // https://clojuredocs.org/clojure.core/load-string Offers another way to do this. // It might have some advantages. read-string followed by eval will execute the first // expression it finds, ignore the rest, and return the result of that first expression. // load-string will execute all expressions in order, and return the result of the last // one. private static readonly IFn LOAD_STRING = Clojure.var("clojure.core", "load-string"); private void executeButton_Click(object sender, EventArgs e) { try { // This would execute the code. It worked as long as the input was a single // s-expression. If you had more, this would read and evaluate the first // s-expression in the string, and silently ignore the rest. The Clojure // "load-string" function, shown next, will read and execute all s-expressions, // and return the value of the last one. //object source = READ_STRING.invoke(sourceTextBox.Text); //object result = EVAL.invoke(source); object result = LOAD_STRING.invoke(sourceTextBox.Text); if (null == result) resultTextBox.Text = "null"; else resultTextBox.Text = result.ToString() + " (" + result.GetType().Name + ")"; } catch (Exception ex) { resultTextBox.Text = ex.ToString(); } } public Label GetLabel() { return label1; } public TextBox GetTextBox() { return textBox1; } public int _intField = 42; public int IntProperty { get; set; } } }