A simple lisp to javascript translater
npm install lisp2jslisp2js beta (clojure like syntax)
=======
#### By Yiyi Wang (shd101wyy)
##### Simple Lisp that compiles to JavaScript ~~(targeting ECMAScript 6)~~
##### ~~So ECMAScript 5 might not work.~~
###### As es6 is not fully supported, the language will compile to es5.
---------------
#### npm / github
npm
github
#### Installation
``sh`
npm -g install lisp2js `
#### How to run sh`
lisp2js # repl
lisp2js [file1.lisp] # run file1.lisp
lisp2js [file1.lisp] [file2.js] # compile [file1.lisp] to js file [file2.js]`
----------------
#### Use lisp2js in Browserhtml``
----------------$3
$3
----------------
#### all comma, tab, space will be ignored.
----------------
#### Examples
Basics
- commentlisp`
; semicolon is used as comment
;; this is comment
- define variable value
`lisp`
(def x 12)
(def ->thisname$invalid@injs 13) ;; a invalid js variable name, which will be replaced with another name.
(def ** Math.pow)`javascript`
var x = 12;
var _$45__$62_this_$42_name$invalid_$64_in_$42_js = 13; // all invalid characters are replaced with its own charcode.
var _$42__$42_ = Math.pow;
- change variable value
`lisp`
(set! x 15) `javascript`
x = 15;
- define function
`lisp`
(defn add [a b]
(+ a b))`javascript`
function add(a, b){
return a + b;
}
- call function
`lisp`
(add 3 4)`javascript`
add(3, 4);
- define function with default parameters
`lisp`
(defn add [:a 12 :b 3]
(+ a b))`javascript`
function add(a, b) {
a = (a === void 0 ? 12 : a);
b = (b === void 0 ? 3 : b);
return (a + b);
};
- define function with keyword parameters
`lisp`
(defn add [x {:y 1 :z 2}]
(+ x y z))
(add 0) ;; 3
(add 1 :y 3) ;; 6`javascript`
function add(x, __lisp_args__) {
var __lisp_args_v__;
__lisp_args__ = (__lisp_args__ === void 0 ? {} : __lisp_args__);
var y = ((__lisp_args_v__ = __lisp_args__.y) === void 0 ? 1 : __lisp_args_v__);
var z = ((__lisp_args_v__ = __lisp_args__.z) === void 0 ? 2 : __lisp_args_v__);
return (x + y + z);
};
add(0);
add(1, {
y: 3
});
- call function with named(keyword) parameters
`lisp`
(defn add [{:a 1 :b 2}] (+ a b))
(add) ;; => 3
(add :a 3 :b 4) ;; => 7
(add :b 3) ;; => 4`javascript`
function add(__lisp_args__) {
var __lisp_args_v__;
__lisp_args__ = (__lisp_args__ === void 0 ? {} : __lisp_args__);
var a = ((__lisp_args_v__ = __lisp_args__.a) === void 0 ? 1 : __lisp_args_v__);
var b = ((__lisp_args_v__ = __lisp_args__.b) === void 0 ? 2 : __lisp_args_v__);
return (a + b);
};
add();
add({
a: 3,
b: 4
});
add({
b: 3
});
- define function with rest parameters
`lisp`
(defn add [a & b] ;; b here is Array
(+ a b[0]))
(defn add [a . b] ;; b here is List
(+ a (car b)))`javascript`
function add(a) {
for (var b = [], $__0 = 1; $__0 < arguments.length; $__0++) b[$__0 - 1] = arguments[$__0];
return (a + b[0]);
};
function add(a) {
for (var b = [], $__0 = 1; $__0 < arguments.length; $__0++) b[$__0 - 1] = arguments[$__0];
b = list.apply(null, b);
return (a + car(b));
};
- anonymous function
`lisp`
(fn [a :b 13 & c]
(+ a b c[0]))`javascript`
function (a, b){
for (var c = [], $__0 = 2; $__0 < arguments.length; $__0++) c[$__0 - 2] = arguments[$__0];
b = (b === void 0 ? 13 : b);
return (a + b + c[0]);
};
- do. run a series of exps.
`lisp
(do (+ 1 2)
(- 3 4)
(* 5 6))
(fn []
(do (+ 1 2)
(- 3 4)
(* 5 6)))
(if 1
(do (def x 1) (def y 2))
(do (def x 2) (def y 1)))
``javascript
(1 + 2);
(3 - 4);
(5 * 6);;
function() {
(1 + 2);
(3 - 4);
return (5 * 6);;
};
if (1) {
var x = 1;
var y = 2;
} else {
var x = 2;
var y = 1;
};
`
- if
`lisp`
(if 1 2 3)
(def x (if 1 2 3))`javascript`
if (1) {
2
} else {
3
};
var x = (1 ? 2 : 3);
- cond
`lisp`
(cond test1 (do stm1 stm2)
test2 (do stm3 stm4)
test3 stm5
else stm6)`javascript`
if (test1) {
stm1;
stm2;
} else if (test2) {
stm3;
stm4;
} else if (test3) {
stm5;
} else {
stm6;
};
- case
`lisp`
(defn test [x]
(case x
"apple" "This is apple"
"orange" "This is orange"
else "This is nothing"))`javascript`
function test(x) {
switch (x) {
case "apple":
return "This is apple";
case "orange":
return "This is orange";
default:
return "This is nothing";
};
};
- let (es5) // I might change this to es6 let in the future
`lisp`
(let [x 1
y 2
x (+ x y)
z 4]
(+ x y z))
(+ (let [x 1 y 2] (- x y))
3)
(defn test []
(let x 1 y 2 (+ x y)))`javascript`
((function() {
var x = 1;
var y = 2;
x = (x + y);
var z = 4;
return (x + y + z)
})());
(((function() {
var x = 1;
var y = 2;
return (x - y)
})()) + 3);
function test() {
return ((function() {
var x = 1;
var y = 2;
return (x + y)
})());
};
- throw
`lisp`
(throw "Too Big")`javascript`
throw "Too Big";
- yield
`lisp`
(defn test []
(yield 1)
(yield 2))
(def x (test))
(x.next) ;; 1
(x.next) ;; 2
(x.next) ;; stop`javascript`
function test() {
yield 1;
yield 2;
return;
};
var x = test();
x.next();
x.next();
x.next();
- try/catch/finally
`lisp`
(try (console.log "This is try")
catch e (console.log "This is catch")
finally (console.log "This is finally"))`javascript`
try {
console.log("This is try");
} catch (e) {
console.log("This is catch");
} finally {
console.log("This is finally");
};`
- some operatorslisp`
(= 1 1)
(+ 1 2 3)
(- 1 2 3)
(* 1 2 3)
(/ 1 2 3)
(* (+ 1 2) (- 3 4))
(> 1 2 3 4)
(<= 1 2 3 4)
(&& true false)
(|| 1 2)
(| 1 0x12)
(and true false)
(or true false)
(not true)`javascript`
(1 === 1);
(1 + 2 + 3);
(1 - 2 - 3);
(1 2 3);
(1 / 2 / 3);
((1 + 2) * (3 - 4));
(1 > 2 && 2 > 3 && 3 > 4);
(1 <= 2 && 2 <= 3 && 3 <= 4);
(true && false);
(1 || 2);
(1 | 0x12);
(true && false);
(true || false);
(!true);
- get
`lisp`
(get "abcd" 'length)
(get console .log)`javascript`
"abcd"["length"];
console.log;
- ->
`lisp`
(-> console (.log "Hello World"))
(-> $ (.post "test.php")
(.done (fn () "done"))
(.fail (fn () "fail")))
(-> "i am cool"
.length)`javascript`
console.log("Hello World");
$.post("test.php").done(function() {
return "done";
}).fail(function() {
return "fail";
});
"i am cool".length;
- class (this might be buggy, I will implement class in es6 in the future)
`lisp
(class Animal
:constructor (fn [age] ;; define constructor
(set! this.age age))
:showAge (fn [] ;; define method
(console.log "Called from Animal")
(console.log this.age)))
(class Dog extends Animal
:constructor (fn [age] ;; define constructor
(super age)) ;; call superclass constructor
:showAge (fn [] ;; define method
(console.log "Called from Dog")
(super.showAge)) ;; call superclass method
:bark (fn [] ;; define method
(console.log "Bark!")))
(def dog (new Dog 5))
(dog.showAge) ;; Called from Dog
;; Called from Animal
;; 5
(dog.bark) ;; Bark!
`
- loop
`lisp`
;; calculate factorial 10
(loop [i 10
acc 1]
(if (= i 0)
acc
(recur (- i 1)
(* i acc))))`javascript`
(function __lisp__recur__$0(i, acc) {
if ((i === 0)) {
return acc
} else {
return __lisp__recur__$0((i - 1), (i * acc))
};
})(10, 1);
- new
`lisp`
(def x (new Array 1 2 3 4))`javascript `
var x = (new Array(1, 2, 3, 4));`
- in lisp`
(in 'a {'a 12})`javascript `
("a" in {"a": 12});
- instanceof
`lisp`
(instanceof [1 2 3] Array)`javascript`
([1, 2, 3] instanceof Array)`
-----------------------------------------
- List functions
* To enable List datatype, include lisp.js from https://github.com/shd101wyy/List_for_FP
* after you compile your .lisp file to javascript file.
* This file will give you 4 functions: car, cdr, cons, list.
* and 1 datatype: $List
* See the link above for more information.
- define a list.lisp`
(def x '(1 2 3))`javascript`
var x = cons(1, cons(2, cons(3, null)));
- quasiquote
`lisp(~x x) ;; => (12 x)
(def x 12)
```javascript`
var x = 12;
cons(x, cons("x", null));
- car, cdr, cons, list
`lisp`
(def a 1)
(def b 2)
(def c (cons a (cons b '()))) ;; => (1 2)
(car c) ;; => 1
(cdr c) ;; => (2)
(def l (list a b)) ;; => (1 2)`javascript`
var a = 1;
var b = 2;
var c = cons(a, cons(b, null));
car(c);
cdr(c);
var l = list(a, b);
---------------------------------------
- Use JavaScript Object/Array
- define Array
`lisp`
(def x [1 2 3])`javascript`
var x = [1, 2, 3];
- define Object
`lisp`
(def x {:a 12 b 13 "c" 14})`javascript`
// es6
var x = {a: 12, [b]: 13, "c": 14};
- es6 define value
`lisp`
(def [x y z] [1 2 3])
(def {:m :n} {:m 12 :n 20})`javascript`
// es6
var [x, y, z] = [1, 2, 3];
var {
m, n
} = {
m: 12,
n: 20
};
- change value
`lisp`
(def x [1 2 3])
(set! x[0] 12)
(def y {:a 12 :b 13 :c (fn (a b) (+ a b))})
(set! y.a 13)
(set! y["a"] 13)`javascript`
var x = [1, 2, 3];
x[0] = 12;
var y = {
a: 12,
b: 13,
c: function(a, b) {
return (a + b);
}
};
y.a = 13;
y["a"] = 13;
- get value
`lisp`
(def y {:a 12 :b 13 :c (fn (a b) (+ a b))})
(y.add y.a y.b)`javascript`
var y = {
a: 12,
b: 13,
c: function(a, b) {
return (a + b);
}
};
y.add(y.a, y.b);`
---------------------------------------
#### recur
##### similar to recur in clojure
- recurlisp
(defn test [n]
(cond (= n 0) 0
1 (recur (- n 2)) ;; recur here means test
else (recur (- n 1))))
;; anonymous function recur
((fn [n acc]
(if (= n 0)
acc
(recur (- n 1) (* n acc)))) 10 1) ;; recur <=> that anonymous function
``javascript`
var test = function(n) {
if ((n === 0)) {
return 0;
} else if (1) {
return test((n - 2));
} else {
return test((n - 1));
};
};
(function __lisp__recur__$0(n, acc) {
if ((n === 0)) {
return acc;
} else {
return __lisp__recur__$0((n - 1), (n * acc));
};
})(10, 1)`
---------------------------------------
#### Macro
- define a macro (unhygienic right now)lisp(* ~x ~x))
(defmacro square [x]
(square 12)
(defmacro square-with-different-params
[x] (* ~x ~x)(+ ( ~x ~x) ( ~y ~y)))
[x y]
(square-with-different-params 12)
(square-with-different-params 15 16)
```javascript`
(12 * 12);
(12 * 12);
((15 15) + (16 16));
- macro-expand: expand a macro form
`lisp(* ~x ~x))
(defmacro square [x]
;; the macro-expand function will expand the macro until it no longer represents a macro form.
(macro-expand '(square 12)) ; => '(* 12 12)
(defmacro test [x] (test (+ 1 ~x)))`
;; macro-expand can also expand macro forms for n times.
(macro-expand '(test 1) 2) ; => '(test (+ 1 (+ 1 1))) this will expand macro twice.
(macro-expand '(test 1) 3) ; => '(test (+ 1 (+ 1 (+ 1 1)))) this will expand macro for 3 times.
###### However, the macro implementation still has errors.
---------------------------------------
#### Change Log
- Version 0.0.36
- 2015/6/14
* begin to use clojure like syntax.
- 2015/3/15
- Version 0.0.33
* fix one macro bug
* add =>
* eg:
`lisp`
(=> (x y) (+ x y)) ;; es6 (x, y) => {return x + y};
`
- Version 0.0.31 ~ 0.0.32
- 2015/3/8
* change code generation for class statement.
* add macro-expand function.
* eg:
lisp(* ~x ~x))
(defmacro square (x)
;; the macro-expand function will expand the macro until it no longer represents a macro form.
(macro-expand '(square 12)) ; => '(* 12 12)
(defmacro test (x) (test (+ 1 ~x)))`
;; macro-expand can also expand macro forms for n times.
(macro-expand '(test 1) 2) ; => '(test (+ 1 (+ 1 1))) this will expand macro twice.
(macro-expand '(test 1) 3) ; => '(test (+ 1 (+ 1 (+ 1 1)))) this will expand macro for 3 times.
`
- 2015/3/4
* fix one macro bug.
- Version 0.0.30
* add class support (this might be buggy though)
* eg:
lisp
(class Animal
:constructor (fn (age) ;; define constructor
(= this.age age))
:showAge (fn ()
(console.log "Called from Animal")
(console.log this.age)))
(class Dog extends Animal
:constructor (fn (age) ;; define constructor
(super age)) ;; call superclass constructor
:showAge (fn ()
(console.log "Called from Dog")
(super.showAge)) ;; call superclass method
:bark (fn ()
(console.log "Bark!")))
(def dog (new Dog 5))
(dog.showAge) ;; Called from Dog
;; Called from Animal
;; 5
(dog.bark) ;; Bark!
``
- Version 0.0.28
- 2015/3/1
* add case statement.
* eg:
lisp`
(def test (x)
(case x
"apple" "This is apple"
"orange" "This is orange"
else "This is nothing"))
(test "pear") ;; => This is nothing
(test "apple") ;; => This is apple
(test "orange") ;; => This is orange
`
* fix one if and cond bug.
- 2015/2/25
* fix demo link error.
- 2015/2/24
* add loop macro
* eg:
lisp`
;; calculate factorial 10
(loop i 10
acc 1
(if (== i 0)
acc
(recur (- i 1)
(* i acc))))
`
- 2015/2/23
* add REPL demo
* fix <= >= < > == != comparison operator bug, they now support multiple arguments.
* eg:
lisp`
(== 1 1 1)
(<= 1 2 3 4 5 2)
`
- Version 0.0.24
- 2015/2/22
* add -> macro
* eg:
lisp`
(-> console (.log "Hello World"))
(-> $ (.post "test.php")
(.done (fn () "done"))
(.fail (fn () "fail")))
(-> "i am cool"
.length)
`
javascript`
console.log("Hello World");
$.post("test.php").done(function() {
return "done";
}).fail(function() {
return "fail";
});
"i am cool".length;
`
- Version 0.0.20 - 0.0.22
- 2015/2/17
Happy New Year []~( ̄▽ ̄)~
* Add and, or, not macros that behave the same as && || !
* Change default parameters and keyword parameters.
* For example:
* Default Parameters
lisp`
(def add (:a 1 :b 2)
(+ a b))
(add) ;; => 3
(add 2) ;; => 4
(add 3 4) ;; => 7
`
* Keyword Parameters
*
lisp`
(def add ({:x 1 :y 2})
(+ x y))
(add) ;; => 3
(add :x 3) ;; => 5
(add :y 6) ;; => 7
(add :x 4 :y 5) ;; => 9
(add :y 1 :x 5) ;; => 6
`
- Version 0.0.18
- 2015/2/16
* Add yield and throw support.
- 2015/2/9
* Improve compatibility with es5
- 2015/2/7
* Change the way of defining the default parameters and calling function with named parameters
- 2015/1/31
* Fix one macro bug.
- 2015/1/26
* Change do function.
(do ...) will be wrapped as function when it is argument or assignment value.
- Version 0.0.13
* add in support.
lisp`
(in 'a {'a 12})
`
javascript`
("a" in {"a": 12});
`
- Version 0.0.12
* fix one macro bug.
- 2015/1/23
* change . & for rest parameters
* . => list
* & => array
lisp`
(def add (a & b) ;; b here is Array
(+ a b[0]))
(def add (a . b) ;; b here is List
(+ a (car b)))
`
javascript`
// es6
var add = function(a, ...b){
return a + b[0];
}
var add = function(a, ...b) {
b = list.apply(null, b);
return (a + car(b));
};
- 2015/1/19
* add get fn
* fix "abc".length like exp bug.
- 2015/1/14
* add cond
* add recur support
* add try/catch/finally support
* change if statement
Went snowboarding and fell down too many times. I twisted my wrist unfortunately. (T_T)
- 2015/1/7
* add support for fn with name .
`lisp`
(fn add (x) (+ x y))`javascript``
function add(x) {
return (x + y);
};
* fix one macro bug
- 2015/1/5
* add support for const
* change let . see doc above.
* fix several bugs.
- 2015/1/5 First Release
* There are still lots of bugs.
* ...
---------------------------------------
MIT License ;)