LispyScript - Lisp-style JavaScript

* This is a translation of the article from DailyJS

Introduction


LispyScript is a tree-like programming language that compiles to JavaScript. Strictly speaking, this is something between JavaScript and Lisp.
The Lispy script consists of similar expressions:
( arg1 arg2 arg3 ...)


This expression calls the function (generally speaking, this is not entirely true, but more on that later).
The first element indicates a function. The rest are for arguments.
(console.log "abc")

Yes, I almost forgot: you can play here .

For example, a function can be called like this:
 (console.log "2 + 2 = %d" (+ 2 2)) 

Well, in JS, it looks like this:
 console.log("2 + 2 = %d", (2 + 2));


We all know the familiar HTML structure:
My Home Page

Welcome to LispyScript



In Lispy templates, this looks a little different:
(html {lang: "en"}
  (head
    (title "My Home Page"))
  (body
    (h1 "Welcome to LispyScript")))


But about templates later, it’s important now to see the tree structure.

Macros


One of the most important parts of Lispy. Macros do not compile in JS, but they can extend the compiler. For example, let's write a print macro:
(macro print (str rest...)
  (console.log ~str ~rest...))
(print "Hello print macro!")
(print "2 + 2 = %d" (+ 2 2))

console.log("Hello print macro!")
console.log("2 + 2 = %d", (2 + 2));


The above macro extends Lispy. The macro expression takes the macro name as the first parameter, followed by the parameters in brackets, and then the code into which the macro call is compiled.
The ~ operator will dereference the parameters. The variable rest ... contains all the parameters passed after str.

The compiler works in 2 stages: first, macros are converted to code. Those. of
 (print "Hello print macro!")

He creates:
 (console.log "Hello print macro!") 

Well, then it compiles in JS. Similarly:
 (print "2 + 2 = %d" (+ 2 2)) ; lispy

(console.log "2 + 2 = %d" (+ 2 2)) ; lispy

 console.log("2 + 2 = %d", (2 + 2)); // js


One may ask, why not use a function instead of a macro? Let's try:
(var print
  (function (data value)
    (console.log data value)))


Now compare the resulting code:
// макрос
console.log("2 + 2 = %d", (2 + 2));
// функция
var print = function(data,value) {
    return console.log(data,value);
};
print("2 + 2 = %d",(2 + 2));


A macro is not a function!

You should not expect from a macro that it will behave as a function. Often it is better to use a function rather than a macro.
Example: write a macro to calculate the square of a number:
(macro square (x)
  (* ~x ~x))
(console.log (square 2))

And this code will work fine, output 4. In JS, it is like this:
console.log((2 * 2));


Now try:
(var i 2)
(console.log (square i++))

And we return the number 6 instead of 9. Why so, it becomes clear if you look at the compiled code:
var i = 2;
console.log((i++ * i++));


In the case of a function, the value is calculated in advance, but in the case of a macro, no. This must be understood and remembered.

Conclusion


In general, Lispy provides an alternative way to write scripts. Macros are a very powerful tool, but they are best used with caution.

PS


There is also Javathcript . True, it is without macros.
UPD More ClojureScript , thanks for the monolithed hint

Also popular now: