lisp2js beta (clojure like syntax)
By Yiyi Wang (shd101wyy)
(targeting ECMAScript 6)
Simple Lisp that compiles to JavaScript 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
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_ = Mathpow;
- change variable value
(set! x 15)
x = 15;
- define function
(defn add [a b] (+ a b))
{ return a + b; }
- call function
(add 3 4)
;
- define function with default parameters
(defn add [:a 12 :b 3] (+ 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
{ 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; }; ; ;
- 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
{ 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; }; ; ; ;
- 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)))
{ for var b = $__0 = 1; $__0 < argumentslength; $__0++ b$__0 - 1 = arguments$__0; return a + b0; }; { for var b = $__0 = 1; $__0 < argumentslength; $__0++ b$__0 - 1 = arguments$__0; b = list; return a + ; };
- anonymous function
(fn [a :b 13 & c] (+ a b c[0]))
{ for var c = $__0 = 2; $__0 < argumentslength; $__0++ c$__0 - 2 = arguments$__0; b = b === void 0 ? 13 : b; return a + b + c0; };
- 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;; { 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"))
{ ; };
- 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)))
{ var x = 1; var y = 2; x = x + y; var z = 4; return x + y + z }; { var x = 1; var y = 2; return x - y } + 3; { return { 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
{ 1; 2; return; }; var x = ; xnext; xnext; xnext;
- try/catch/finally
(try (console.log "This is try") catch e (console.log "This is catch") finally (console.log "This is finally"))
try console; catch e console; finally console; ;
- 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"; consolelog;
- ->
(-> console (.log "Hello World")) (-> $ (.post "test.php") (.done (fn () "done")) (.fail (fn () "fail"))) (-> "i am cool" .length)
console; $; "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))))
{ if i === 0 return acc else return ; }10 1;
- new
(def x (new Array 1 2 3 4))
var x = 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 = ;
- quasiquote
(def x 12) `(~x x) ;; => (12 x)
var x = 12; ;
- 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 = ; ; ; var l = ;
-
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; x0 = 12; var y = a: 12 b: 13 { return a + b; } ; ya = 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 { return a + b; } ; y;
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 { if n === 0 return 0; else if 1 return ; else return ; ; }; { if n === 0 return acc; else return ; ; }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 10acc 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;$;"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)))// es6var {return a + b0;}var {b = list;return a + ;}; -
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))
{ 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 ;)