cloJS
A library for Clojure to convert Clojure code to JavaScript. It combines the simplicity of Clojure syntax with the power of JavaScript libraries. So all the function calls are of JavaScript.
Artifacts
clojs
artifacts are released to Clojars.
If you are using Maven, add the following repository definition to your pom.xml
:
<repository>
<id>clojars.org</id>
<url>http://clojars.org/repo</url>
</repository>
The Most Recent Release
With Maven:
<dependency>
<groupId>clojs</groupId>
<artifactId>clojs</artifactId>
<version>0.1.4</version>
</dependency>
Dependencies
node.js
escodegen npm package
npm
(node.js package manager) is installed with node.js
. escodegen
can be installed by typing:
npm install escodegen
in your terminal/command line.
Bugs and Enhancements
Please open issues against the cloJS repo on Github.
Mailing List
Please ask questions on the cloJS mailing list.
Who is it for?
This is for:
- JavaScript programmers who want to use simple LISP like syntax of clojure. All the JavaScript functions can be called.
- Clojure programmers who want to use JavaScript functions and libraries without learning JavaScript syntax.
Introduction
You write Clojure syntax code using JavaScript functions and it is converted into JavaScript code.
Usage
clojs.clojs
The namespace for code conversion in the clojs
library is clojs.clojs
.
(require '[clojs.clojs :refer [convert convert-string] :as cj])
Convert a code snippet as string with convert-string
. It takes a string(Clojure code) and returns a string(JavaScript code):
(convert-string "(def a 1)")
=> const a = 1;
Convert one or more files containing code with convert
. It takes the path of each input file as a string and creates the equivalent JavaScript file(.js) in the same folder as the input file:
(convert "a.clj" "resources/b.clj")
=> nil
This will create a.js
in the folder where a.clj
is present and b.js
in the resources
folder where b.clj
is present.
Note: If a.js
or b.js
are already present in the respective folders, they will be overwritten.
Here's a sample Clojure app that uses clojs
Syntax
Here is all the syntax that clojs
supports:
Legend: a -> b
means a
in Clojure is converted to b
in JavaScript
Literals
Input literal type | Input | Output | Output literal type |
---|---|---|---|
String | "abc" |
'abc' |
String |
Number |
123 , 123.456
|
123 , 123.456
|
Number |
Nil | nil |
null |
Null |
Boolean |
true , false
|
true , false
|
Boolean |
Note
-
null
in JavaScript isnil
in Clojure. - Strings in Clojure use only
""
as delimiters.
Identifiers
Identifiers like variable names, function names are converted as it is.
Note to Clojure programmers: Do not use -
in variable/function names like string-length
because running the resulting JavaScript code will error out as JavaScript use infix notation and -
is not allowed in identifiers. Use _
instead like string_length
.
Vectors -> Arrays
["abc" 123 nil true false [1 2 3]]
->
['abc', 123, null, true, false, [1, 2, 3]]
No commas in Clojure. Nesting is supported.
Maps -> Objects
{"a" 1 "b" {10 "t" 20 "f"} c 3 "d" [1 2 3]}
->
{'a': 1, 'b': { 10: 't', 20: 'f'}, c: 3, 'd': [1, 2, 3]}
No semicolons or commas in Clojure. Nesting is supported.
Note: Literals, identifiers, vectors, objects are not supported at the top-level. They have to be inside parentheses.
def
-> const
(def a 1 b "xyz" c nil d true e [1 2 3] f a g {a 1})
->
const a = 1, b = 'xyz', c = null, d = true, e = [1, 2, 3], f = a, g = {a: 1}
Array element access and Object property access
(def h e[0] i g.a j g["a"])
->
const h = e[0], i = g.a, j = g['a'];
Note: Currently only 1D arrays are supported i.e. a[1][2]
wont work.
Operators
Prefix Operators -> Binary Operators
As Clojure uses prefix notation you can give any number of arguments to the operators. clojs
requires them to be greater than or equal to two.
Input | Output |
---|---|
(+ a b 1 1.2 c) |
a + b + 1 + 1.2 + c |
(- a b 1 1.2 c) |
a - b - 1 - 1.2 - c |
(* a b 1 1.2 c) |
a * b * 1 * 1.2 * c |
(/ a b 1 1.2 c) |
a / b / 1 / 1.2 / c |
(mod a b 1 1.2 c) |
a % b % 1 % 1.2 % c |
(< a b 1 1.2 c) |
a < b < 1 < 1.2 < c |
(<= a b 1 1.2 c) |
a <= b <= 1 <= 1.2 <= c |
(> a b 1 1.2 c) |
a > b > 1 > 1.2 > c |
(>= a b 1 1.2 c) |
a >= b >= 1 >= 1.2 >= c |
(= a b 1 1.2 c) |
a === b === 1 === 1.2 === c |
(== a b 1 1.2 c) |
a == b == 1 == 1.2 == c |
(!== a b 1 1.2 c) |
a !== b !== 1 !== 1.2 !== c |
(!= a b 1 1.2 c) |
a != b != 1 != 1.2 != c |
(in a b 1 1.2 c) |
a in b in 1 in 1.2 in c |
(instanceof a b 1 1.2 c) |
a instanceof b instanceof 1 instanceof 1.2 instanceof c |
(and a b 1 1.2 c) |
a && b && 1 && 1.2 && c |
(or a b 1 1.2 c) |
a || b || 1 || 1.2 || c
|
(assign a b) |
a = b |
Unary Operators
Input | Output |
---|---|
(not a) |
!a |
(typeof a) |
typeof a |
if
statement
(if (= n 0)
true
false)
if (n === 0)
true;
else
false;
Function Call
(console.log 1 2 "abc" a b)
->
console.log(1, 2, "abc", a, b);
No commas in Clojure.
do
Block Statement - (if (= 1 1)
(do (console.log "x :" x)
(console.log "y :" y)
(console.log "z :" z))
(do (console.log "a :" a)
(console.log "b :" b)
(console.log "c :" c))
->
if (1 === 1) {
console.log('x :', x);
console.log('y :', y);
console.log('z :', z);;
} else {
console.log('a :', a);
console.log('b :', b);
console.log('c :', c);
}
defn
Function Definition - (defn factorial [n]
(if (= n 0)
1
(* n (factorial (- n 1)))))
->
const factorial = n => {
if (n === 0)
return 1;
else
return n * factorial(n - 1);
};
Return is implicit. The last statement is the return statement.
Lambda -> Anonymous functions
(fn [x]
(console.log x)
(console.log (+ 5 x)))
->
x => {
console.log(x);
return console.log(5 + x);
};
let
(fn [x]
(console.log x)
(let a 2 b 5)
(console.log (+ 5 x a b)))
->
x => {
console.log(x);
let a = 2, b = 5;
return console.log(5 + x + a + b);
};
return
statement
(return a)
->
return a;
chaining
(.attr (.parent ($ this)) "id")
->
$(this).parent().attr('id');
cond
-> if-else
chain
(fn [x]
(cond
(is_array_member form) (do (get_array_member form) (+ 1 2))
(is_defn form) (get_defn form)
(is_def form) (get_const form)
(is_if form) (get_if form)
(is_do form) (get_do form)
(is_vec form) (get_vec form)
(is_lambda form) (get_lambda form)
(is_map_ds form) (get_map_ds form)
(is_literal form) (get_literal form)
(is_operator form) (get_operator form)
(is_fn_call form) (get_fn_call form)
true nil))
->
x => {
if (is_array_member(form)) {
get_array_member(form);
return 1 + 2;
} else if (is_defn(form))
return get_defn(form);
else if (is_def(form))
return get_const(form);
else if (is_if(form))
return get_if(form);
else if (is_do(form))
return get_do(form);
else if (is_vec(form))
return get_vec(form);
else if (is_lambda(form))
return get_lambda(form);
else if (is_map_ds(form))
return get_map_ds(form);
else if (is_literal(form))
return get_literal(form);
else if (is_operator(form))
return get_operator(form);
else if (is_fn_call(form))
return get_fn_call(form);
else if (true)
return null;
};
Macros
(defmacro m-bind [mv mf]
(conj (list ~mv test) ~mf))
(m-bind mvv mff)
->
mff(mvv, test);
Samples
You can see a converted sample containing all the syntax: all.clj
-> all.js
Examples
A todo app written using cloJS.
Components
- Ramshreyas's seqingclojure - it makes the AST for the input code.
-
clojs
uses this AST to convert it into an equivalent JavaScript AST in JSON format. - estool's npm package escodegen - it converts the JavaScript AST into JavaScript code.
License
Released under the Eclipse Public License: https://github.com/puneetpahuja/cloJS/blob/master/LICENSE