Skip to content

A language that lets you write JavaScript code in Clojure syntax.

License

Notifications You must be signed in to change notification settings

puneetpahuja/cloJS

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cloJS Clojars Project

A language that lets you write JavaScript code in Clojure syntax. It combines the simplicity of Clojure syntax with the power of JavaScript libraries.

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 Leiningen

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.jsin 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 shows how to use cloJS

Examples

A todo app written in cloJS language.

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 is nil 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.

Block Statement - do

  (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);
    }

Function Definition - defn

(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

Components

  1. Ramshreyas's seqingclojure - it makes the AST for the input code.
  2. clojs uses this AST to convert it into an equivalent JavaScript AST in JSON format.
  3. 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

About

A language that lets you write JavaScript code in Clojure syntax.

Topics

Resources

License

Stars

Watchers

Forks

Languages

  • Clojure 99.5%
  • Shell 0.5%