lisp2js

    0.0.36 • Public • Published

    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

    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 = (=== void 0 ? 12 : a);
            b = (=== void 0 ? 3 : b);
            return (+ 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 (+ 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 (+ 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 (+ 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 (+ 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 = (=== void 0 ? 13 : b);
            return (+ 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 = (+ y);
            var z = 4;
            return (+ y + z)
        })());
        (((function() {
            var x = 1;
            var y = 2;
            return (- y)
        })()) + 3);
        function test() {
            return ((function() {
                var x = 1;
                var y = 2;
                return (+ 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 ((=== 0)) {
                return acc
            } else {
                return __lisp__recur__$0((- 1), (* 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 (+ 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 (+ 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 ((=== 0)) {
            return 0;
          } else if (1) {
            return test((- 2));
          } else {
            return test((- 1));
          };
        };
        (function __lisp__recur__$0(n, acc) {
          if ((=== 0)) {
            return acc;
          } else {
            return __lisp__recur__$0((- 1), (* 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.
    • 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 (+ 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 (+ 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 ;)

    Keywords

    Install

    npm i lisp2js

    DownloadsWeekly Downloads

    2

    Version

    0.0.36

    License

    MIT

    Last publish

    Collaborators

    • shd101wyy