/eisen

A Language for Programmable Applications

Primary LanguageClojure

Eisen

Eisen is a toolkit that supports the development of programmable applications and domain-specific languages (DSL).

The Language

Eisen is designed to be an embedded language with the semantics of Clojure and a syntax similar to Standard ML.

The Compiler

Eisen compiles to Clojure, which compiles to Java bytecode.

The Library

The Eisen library offers the following facilities:

  • Access to the host data and functions from user code.
  • Running user code from a host program.
  • Functions for parsing and running Eisen code at the Clojure REPL.
  • A simple Eisen REPL.
  • The compiler's parsing and translation functions.
  • Compiler hooks for extending Eisen or writing DSLs.

Setup

Leiningen:

[org.blancas/eisen "0.1.0"]

Maven:

<dependency>
  <groupId>org.blancas</groupId>
  <artifactId>eisen</artifactId>
  <version>0.1.0</version>
</dependency>

Sample Usage

The following sections show parts of two samples in order to highlight some of Eisen's features.

The Snake Game

The Snake sample is a modified version of the game in the book Programming Clojure that may be changed and extended. The host program declares a mutable model for the user code to read and change.

(host-model
  width       75
  height      50
  win-length   2
  point-size  10
  body        (list [1 1])
  dir         [1 0]
  add-points  (fn [& pts] (vec (apply map + pts))))

The game reads the user code pasted on a simple text field and evaluates it.

(defn run-eisen []
  (let [code (JOptionPane/showInputDialog nil
               "Paste your code here:" "Change the game"
               JOptionPane/PLAIN_MESSAGE)]
    (when (seq code)
      (let [result (eisen code)]
        (when-not (:ok result)
          (JOptionPane/showMessageDialog nil (:error result)))))))

On the Eisen side, this code makes the snake wrap around the borders.

fun add p1 p2 =
    let p = map `+` p1 p2;
        x = first p;
        y = second p
    in
        cond x > _width  => #[1, y];
             x < 0       => #[_width, y];
             y > _height => #[x, 1];
             y < 0       => #[x, _height];
             :else       => #[x, y]
        end
    end

The following line installs the function add above, which the host calls as add-points.

setv add-points = add

A Buddhabrot Fractal

The fractal sample is a modified version of Nurullah Akkaya's program that delegates the computation to user code written in Eisen.

The main program defines the model shared with the user code. Function start was modified to setup the Eisen library and evaluate the user code.

(host-model
  sqrt     #(java.lang.Math/sqrt %)  ;; Package Java interop for user code.
  generate identity)                 ;; Generation function to be replaced.

;; Start the computation using user code.

(defn start [fractal]
  (init-eisen)
  (eisenf user-code)
  (future (call generate fractal)))

These are some of the functions that compute the fractal. (Note that destructuring is not currently supported.)

(* Computes a point's path. *)

fun calcPath x y max =
  let c = #[x, y]
  in
      loop z    = c;
           path = #[];
           iter = 0
      in
           if iter > max then #[]
           else
             if abs z > 2.0 then z : path
             else recur (add c (mult z z)) (z : path) (inc iter)
      end
  end

(* Translates a point into a screen coordinate. *)

fun point2coord size c =
  let real = first c;
      imag = second c
  in
      #[ int (0.3 * size * (real + 0.5) + size / 2), 
         int (0.3 * size * imag + size / 2) ]
  end

(* Sets a point into the fractal's buffer. *)

fun bufferSet fractal point =
  let size   = get fractal :size;
      buffer = get fractal :buffer;
      p      = point2coord size point;
      x      = first p;
      y      = second p
  in
      if x > 0 && y > 0 && x < size && y < size then
         `aset-int` buffer y x (aget buffer y x  + 1)
  end

(* Generates the fractal image. *)

fun compute fractal =
  let buffer = get fractal :buffer;
      iter   = get fractal :iteration
  in
      doseq [point <- iterate inc 1]
          let x    = rand 6 - 3;
              y    = rand 6 - 3;
              path = calcPath x y iter;
          in
              if point % 1000000 == 0 then
                 println "Point:" point;

              doseq [p <- path] bufferSet fractal p end
          end
      end
  end

(* Installs the local function. *)

setv generate = compute

To Do

  • Destructuring.
  • Pattern matching.
  • A uniform mechanism for function calls.
  • Peephole optimizations.

Documentation

Eisen is documented in the Wiki.

Browse the Codox Eisen v0.1.0 API.

To generate the API docs (in the codox directory):

lein doc

License

Copyright © 2013 Armando Blancas.

Licensed under the Eclipse Public License.