lisp2js
v0.0.36
Published
A simple lisp to javascript translater
Downloads
15
Readme
lisp2js 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
Installation
npm -g install lisp2js
How to run
lisp2js # repl
lisp2js [file1.lisp] # run file1.lisp
lisp2js [file1.lisp] [file2.js] # compile [file1.lisp] to js file [file2.js]
Use lisp2js in Browser
<script src="lisp2js.js"></script>
<script>
var output = lisp.compile("(def x 12)"); // compile/eval expression, then return compiled result.
</script>
Click Me to Try it Online
Online REPL
all comma, tab, space will be ignored.
Examples
Basics
comment
; semicolon is used as comment
;; this is comment
define variable value
(def x 12)
(def ->this*name$invalid@in*js 13) ;; a invalid js variable name, which will be replaced with another name.
(def ** Math.pow)
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
(set! x 15)
x = 15;
- define function
(defn add [a b]
(+ a b))
function add(a, b){
return a + b;
}
- call function
(add 3 4)
add(3, 4);
- define function with default parameters
(defn add [:a 12 :b 3]
(+ a b))
function add(a, b) {
a = (a === void 0 ? 12 : a);
b = (b === void 0 ? 3 : b);
return (a + b);
};
- define function with keyword parameters
(defn add [x {:y 1 :z 2}]
(+ x y z))
(add 0) ;; 3
(add 1 :y 3) ;; 6
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
(defn add [{:a 1 :b 2}] (+ a b))
(add) ;; => 3
(add :a 3 :b 4) ;; => 7
(add :b 3) ;; => 4
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
(defn add [a & b] ;; b here is Array
(+ a b[0]))
(defn add [a . b] ;; b here is List
(+ a (car b)))
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
(fn [a :b 13 & c]
(+ a b c[0]))
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.
(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)))
(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
(if 1 2 3)
(def x (if 1 2 3))
if (1) {
2
} else {
3
};
var x = (1 ? 2 : 3);
- cond
(cond test1 (do stm1 stm2)
test2 (do stm3 stm4)
test3 stm5
else stm6)
if (test1) {
stm1;
stm2;
} else if (test2) {
stm3;
stm4;
} else if (test3) {
stm5;
} else {
stm6;
};
- case
(defn test [x]
(case x
"apple" "This is apple"
"orange" "This is orange"
else "This is nothing"))
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
(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)))
((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
(throw "Too Big")
throw "Too Big";
- yield
(defn test []
(yield 1)
(yield 2))
(def x (test))
(x.next) ;; 1
(x.next) ;; 2
(x.next) ;; stop
function test() {
yield 1;
yield 2;
return;
};
var x = test();
x.next();
x.next();
x.next();
- try/catch/finally
(try (console.log "This is try")
catch e (console.log "This is catch")
finally (console.log "This is finally"))
try {
console.log("This is try");
} catch (e) {
console.log("This is catch");
} finally {
console.log("This is finally");
};
- some operators
(= 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)
(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
(get "abcd" 'length)
(get console .log)
"abcd"["length"];
console.log;
- ->
(-> console (.log "Hello World"))
(-> $ (.post "test.php")
(.done (fn () "done"))
(.fail (fn () "fail")))
(-> "i am cool"
.length)
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)
(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
;; calculate factorial 10
(loop [i 10
acc 1]
(if (= i 0)
acc
(recur (- i 1)
(* i acc))))
(function __lisp__recur__$0(i, acc) {
if ((i === 0)) {
return acc
} else {
return __lisp__recur__$0((i - 1), (i * acc))
};
})(10, 1);
- new
(def x (new Array 1 2 3 4))
var x = (new Array(1, 2, 3, 4));
- in
(in 'a {'a 12})
("a" in {"a": 12});
- instanceof
(instanceof [1 2 3] Array)
([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.
(def x '(1 2 3))
var x = cons(1, cons(2, cons(3, null)));
- quasiquote
(def x 12)
`(~x x) ;; => (12 x)
var x = 12;
cons(x, cons("x", null));
- car, cdr, cons, list
(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)
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
(def x [1 2 3])
var x = [1, 2, 3];
- define Object
(def x {:a 12 b 13 "c" 14})
// es6
var x = {a: 12, [b]: 13, "c": 14};
- es6 define value
(def [x y z] [1 2 3])
(def {:m :n} {:m 12 :n 20})
// es6
var [x, y, z] = [1, 2, 3];
var {
m, n
} = {
m: 12,
n: 20
};
- change value
(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)
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
(def y {:a 12 :b 13 :c (fn (a b) (+ a b))})
(y.add y.a y.b)
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
- recur
(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
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)
(defmacro square [x] `(* ~x ~x))
(square 12)
(defmacro square-with-different-params
[x] `(* ~x ~x)
[x y] `(+ (* ~x ~x) (* ~y ~y)))
(square-with-different-params 12)
(square-with-different-params 15 16)
(12 * 12);
(12 * 12);
((15 * 15) + (16 * 16));
- macro-expand: expand a macro form
(defmacro square [x] `(* ~x ~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:
(=> (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:
(defmacro square (x) `(* ~x ~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:
(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:
(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:
;; 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:
(== 1 1 1) (<= 1 2 3 4 5 2)
Version 0.0.24
2015/2/22
- add -> macro
- eg:
(-> console (.log "Hello World")) (-> $ (.post "test.php") (.done (fn () "done")) (.fail (fn () "fail"))) (-> "i am cool" .length)
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
(def add (:a 1 :b 2) (+ a b)) (add) ;; => 3 (add 2) ;; => 4 (add 3 4) ;; => 7
- Keyword Parameters
(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.
- Change do function.
Version 0.0.13
- add in support.
(in 'a {'a 12})
("a" in {"a": 12});
Version 0.0.12
- fix one macro bug.
2015/1/23
- change . & for rest parameters
- . => list
- & => array
(def add (a & b) ;; b here is Array (+ a b[0])) (def add (a . b) ;; b here is List (+ a (car b)))
// 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 .
(fn add (x) (+ x y))
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 ;)