A Clojure dialect that compiles to Emacs Lisp — like ClojureScript targets JavaScript, ClojureElisp targets Emacs.
Write .cljel files using Clojure syntax, compile them to .el files that run natively in Emacs 28.1+.
(require '[clojure-elisp.core :as clel])
;; Compile a single form
(clel/emit '(defn greet [name] (str "Hello, " name "!")))
;; => "(defun greet (name)\n (clel-str \"Hello, \" name \"!\"))"
;; Compile a string of code
(clel/compile-string "(defn inc2 [x] (+ x 2))")
;; => "(defun inc2 (x)\n (+ x 2))"
;; Compile a .cljel file to .el
(clel/compile-file "src/my_package.cljel" "out/my-package.el")
;; Compile an entire project in dependency order
(clel/compile-project ["src"] "out");; my-package.cljel
(ns my.package
(:require [clojure.string :as str]))
(defn greet [name]
(let [msg (str "Hello, " name "!")]
(message msg)))
(defn process-buffer []
(-> (buffer-string)
str/upper-case
insert))Compiles to:
;;; my-package.el --- -*- lexical-binding: t; -*-
;; Generated by ClojureElisp
(require 'clojure-elisp-runtime)
;;; Code:
(defun my-package-greet (name)
(let* ((msg (clel-str "Hello, " name "!")))
(message msg)))
(defun my-package-process-buffer ()
(insert (upcase (buffer-string))))
(provide 'my-package)
;;; my-package.el ends here- Functions:
defn,fn(lambda), multi-arity, variadic (& rest), destructuring in params - Bindings:
letwith sequential bindings, vector/map destructuring,:keys,:as,:or - Control flow:
if,when,cond,case,do,and,or - Looping:
loop/recur,letfnwith mutual recursion - Macros:
defmacro(compile-time only), syntax-quote/unquote,macroexpand-1,macroexpand - Error handling:
try/catch/finally,throw,ex-info - Namespaces:
nswith:require,:as,:refer; namespace-prefixed definitions - Protocols & types:
defprotocol,defrecord,deftypewith^:mutablefields,set! - Multimethods:
defmulti/defmethodviacl-defgeneric/cl-defmethod - Lazy sequences:
lazy-seq,realized?,doall,dorun - Atoms:
atom,deref/@,reset!,swap!,add-watch,remove-watch - Elisp interop:
.methoddot-notation,elisp/fnnamespace,.-propertyaccess
Clojure core functions mapped to Elisp equivalents:
| Category | Functions |
|---|---|
| Arithmetic | +, -, *, /, mod, inc, dec |
| Comparison | =, <, >, <=, >=, not= |
| Predicates | nil?, string?, number?, zero?, pos?, neg?, even?, odd?, coll?, some? |
| Collections | first, rest, next, cons, conj, count, nth, get, assoc, dissoc, keys, vals, into, seq, empty? |
| Sequences | map, filter, remove, reduce, take, drop, concat, mapcat, sort, group-by, frequencies |
| Seq predicates | every?, some, not-every?, not-any? |
| Strings | str, subs, format, pr-str, println |
| Higher-order | apply, identity, constantly, partial, comp |
- 3-stage pipeline: Reader (Clojure's) → Analyzer (AST + env) → Emitter (codegen)
- Source location tracking with optional
;;; L<line>:C<col>comments - Incremental compilation with mtime tracking (
.clel-cache/manifest.edn) - Cross-file symbol table with compile-time warnings for missing symbols
- Dependency-aware project compilation with topological sort
- Name mangling:
valid?→valid-p,reset!→reset-bang,my.ns/foo→my-ns-foo
┌─────────────┐ ┌──────────────┐ ┌─────────────┐ ┌──────────────┐
│ Reader │───▶│ Analyzer │───▶│ Emitter │───▶│ Elisp Code │
│ (Clojure's) │ │ (AST + env) │ │ (codegen) │ │ (.el) │
└─────────────┘ └──────────────┘ └─────────────┘ └──────────────┘
| Component | File | Role |
|---|---|---|
| Analyzer | src/clojure_elisp/analyzer.clj |
Parse forms → AST nodes, macro expansion, destructuring, env tracking |
| Emitter | src/clojure_elisp/emitter.clj |
AST nodes → Elisp source strings |
| Core | src/clojure_elisp/core.clj |
Public API, file/project compilation, dependency resolution |
| Runtime | resources/clojure-elisp/clojure-elisp-runtime.el |
55+ Elisp functions implementing Clojure semantics |
| Emacs mode | resources/clojure-elisp/clojure-elisp-mode.el |
Major mode for .cljel files |
| CIDER | resources/clojure-elisp/cider-clojure-elisp.el |
nREPL middleware for CIDER integration |
Install the clel CLI with bbin:
bbin install io.github.BuddhiLW/clojure-elispThis gives you the clel command globally. Requires Babashka and the uberjar (see below).
The CLI delegates compilation to a JVM uberjar. Download from GitHub Releases or build from source:
# Build and install
make build install
# => ~/.local/lib/clel.jarThe CLI auto-detects the jar at ~/.local/lib/clel.jar or via $CLEL_JAR. If no jar is found, it falls back to clojure -M -e.
clel compile # Compile project from clel.edn
clel compile src/my_app.cljel -o out/ # Compile a single file
clel compile src/ -o out/ # Compile a directory
clel watch src/ -o out/ # Watch and recompile on changes
clel version # Print versionCompiled .el files require the ClojureElisp runtime. Install via MELPA (once available):
(package-install 'clojure-elisp)Or manually copy from the repo:
cp resources/clojure-elisp/clojure-elisp-runtime.el ~/.emacs.d/site-lisp/# Build uberjar
clojure -T:build uber
# => target/clel-<version>.jar# Start REPL with dev dependencies (nREPL, CIDER)
clojure -M:dev
# Run tests (Kaocha — 427 tests, 2398 assertions)
clojure -M:test
# Build uberjar
clojure -T:build uber- Clojure 1.12+
- Java 21+ (for building/running the uberjar)
- Emacs 28.1+ (for compiled output)
MIT