1 | # LispyScript
|
2 |
|
3 | ## A Javascript with Lispy syntax and Macros!
|
4 |
|
5 | Lispyscript is Javascript using a 'Lispy' syntax, and compiles to Javascript.
|
6 |
|
7 | An inherent problem with Javascript is that it has no
|
8 | macro support, like other Lisp like languages. That's because macros manipulate the syntax tree while
|
9 | compiling. And this is next to impossible in a language like Javascript. In LispyScript we write Javascript
|
10 | in a tree structure. If you know Javascript and a Lisp like language, then using LispyScript will be a
|
11 | breeze. Even if you don't know a Lispy Language, all you need to learn is to write code in a tree structure.
|
12 |
|
13 | ## Hello World! in LispyScript.
|
14 |
|
15 | ```lisp
|
16 | (console.log "Hello LispyScript!")
|
17 | ```
|
18 |
|
19 | A LispyScript program is made up of expressions in parenthesis. The first element in the expression
|
20 | is a function or a LispyScript keyword. The rest of the elements (separated by space characters) are
|
21 | the arguments to the function. As you can see above we can directly access javascript functions from
|
22 | LispyScript.
|
23 |
|
24 | A more intricate Hello World!
|
25 |
|
26 | ```lisp
|
27 | (if (undefined? window)
|
28 | (console.log "Hello LispyScript!")
|
29 | (alert "Hello LispyScript!"))
|
30 | ```
|
31 |
|
32 | You can have expressions within expressions.
|
33 |
|
34 | ## Functions
|
35 |
|
36 | An anonymous function in LispyScript.
|
37 |
|
38 | ```lisp
|
39 | (function (x) (* x x))
|
40 | ```
|
41 |
|
42 | The first element in an expression can be an anonymous function.
|
43 |
|
44 | ```lisp
|
45 | ((function (x) (* x x)) 2)
|
46 | ```
|
47 |
|
48 | That was the anonymous function above evaluated immediately with argument 2. Functions
|
49 | return the last expression evaluated within the function.
|
50 |
|
51 | You can set a variable name to a function.
|
52 |
|
53 | ```lisp
|
54 | (var square
|
55 | (function (x)
|
56 | (* x x)))
|
57 | (console.log (square 10))
|
58 | ```
|
59 |
|
60 | The 'var' expression takes a variable name as the second element and sets its value to the third.
|
61 |
|
62 | ## LispyScript is Javascript!
|
63 |
|
64 | All Javascript functions, objects and literals can be used in LispyScript.
|
65 |
|
66 | ```lisp
|
67 | (Array.prototype.forEach.call [1, 2, 3]
|
68 | (function (elem index list)
|
69 | (console.log elem)))
|
70 | ```
|
71 |
|
72 | If you noticed we passed a Javascript literal array as the first argument to 'forEach'. You could just as well
|
73 | have passed in '{one: 1, two: 2, three: 3}' instead.
|
74 |
|
75 | You can access object methods and properties using the "." notation.
|
76 |
|
77 | ```lisp
|
78 | (console.log (.greet {greet: "hello"}))
|
79 | ```
|
80 |
|
81 | ie. If the first element of an expression starts with a ".", it's considered as a property of the
|
82 | second element, and the expresion evaluates to the property.
|
83 |
|
84 | You can also use the 'get' expression to access a property of an object.
|
85 |
|
86 | ```lisp
|
87 | (console.log (get "greet" {greet: "hello"}))
|
88 | (console.log (get 1 [1, 2, 3]))
|
89 | ```
|
90 |
|
91 | You can 'set' variables too.
|
92 |
|
93 | ```lisp
|
94 | (set name "John")
|
95 | (set window.onload (function () (alert "Page Loaded")))
|
96 | ; You can set array indices using the three argument version of set
|
97 | (var foo [])
|
98 | (set 1 foo "hello") => foo[1] = "hello";
|
99 | (set "bar" foo "hello") => foo["bar'] = "hello";
|
100 | ```
|
101 |
|
102 | The node server example in LispyScript.
|
103 |
|
104 | ```lisp
|
105 | (var http (require "http"))
|
106 | (var server
|
107 | (http.createServer
|
108 | (function (request response)
|
109 | (response.writeHead 200 {'Content-Type': 'text/plain'})
|
110 | (response.end "Hello World\n"))))
|
111 | (server.listen 1337 "127.0.0.1")
|
112 | (console.log "Server running at http://127.0.0.1:1337/")
|
113 | ```
|
114 |
|
115 | ## Macros
|
116 |
|
117 | LispyScript is not a dialect of Lisp. There is no list processing in LispyScript . LispyScript
|
118 | is Javascript using a Lispy syntax (a tree syntax). This is so that we can manipulate the syntax tree
|
119 | while compiling, in order to support macros.
|
120 |
|
121 | You can define a macro.
|
122 |
|
123 | ```lisp
|
124 | (macro array? (obj)
|
125 | (= (toString.call ~obj) "[object Array]"))
|
126 | ```
|
127 |
|
128 | The 'array?' conditional is defined as a macro in LispyScript. The 'macro' expression takes a name as
|
129 | its second element, a parameters list in the third element, and the fourth element is the template
|
130 | to which the macro will expand.
|
131 |
|
132 | Now let us create a Lisp like 'let' macro in LispyScript.
|
133 |
|
134 | ```lisp
|
135 | (macro let (names vals rest...)
|
136 | ((function ~names ~rest...) ~@vals))
|
137 |
|
138 | (let (name email tel) ("John" "john@example.org" "555-555-5555")
|
139 | (console.log name)
|
140 | (console.log email)
|
141 | (console.log tel))
|
142 | ```
|
143 |
|
144 | The "let" macro creates lexically scoped variables with initial values. It does this by creating
|
145 | an anonymous function whose argument names are the required variable names, sets the variables to
|
146 | their initial values by calling the function immediately with the values. The macro also wraps the
|
147 | required code inside the function.
|
148 |
|
149 | Now lets look at the call to the 'let' macro. 'names' will correspond to '(name email tel)'. 'rest...'
|
150 | corresponds to '(console.log name) (console.log email) (console.log tel)', which is the rest of the
|
151 | expressions after vals. We want to dereference these values in the macro template, and we do that
|
152 | with '~names', '~rest...'. However 'vals' corresponds to ("John" "john@example.org" "555-555-5555").
|
153 | But thats not the way we want to dereference it. We need to dereference it without the parenthesis.
|
154 | For that we use '~@vals'.
|
155 |
|
156 | We don't really need 'let' in LispyScript. We have 'var'. But if you need it, you can extend LispyScript
|
157 | by adding this macro to your code. Thats the power of macros. You can
|
158 | extend the language itself or create your own domain specific language.
|
159 |
|
160 | ## Installing LispyScript
|
161 |
|
162 | The compiler requires [nodejs][] and [npm][] installation. However the compiled
|
163 | code is standalone javascript that will run anywhere. To install use npm:
|
164 |
|
165 | npm install lispyscript
|
166 |
|
167 |
|
168 | ## Using LispyScript
|
169 |
|
170 | 1. Typing `lispy` into the command prompt will open a simple REPL.
|
171 |
|
172 | 2. Typing `lispy program.ls` will compile `program.ls` into `program.js` in
|
173 | the same directory.
|
174 |
|
175 | 3. Type `lispy src/program.ls lib/program.js` to be more explicit.
|
176 |
|
177 | ## Reference
|
178 |
|
179 | ## Operators
|
180 |
|
181 | null?, undefined?, boolean?, number?, string?, object?, array?, function?,
|
182 | =, !=, !, >, <, <=, >=, +, -, *, /, %, &&, ||.
|
183 |
|
184 | Note: `=` and `!=` work like `===` and `!==` in Javascript.
|
185 |
|
186 | ## LispyScript Statements
|
187 |
|
188 | ### (str (string expression) ...)
|
189 |
|
190 | Adds up all the strings.
|
191 |
|
192 | ```lisp
|
193 | (var title "My Home Page")
|
194 | (console.log
|
195 | (str "<!DOCTYPE html>
|
196 | <html>
|
197 | <head>
|
198 | <title>" title "</title>
|
199 | </head>
|
200 | <body class=\"test\">
|
201 | Hello World
|
202 | </body>
|
203 | </html>"))
|
204 | ```
|
205 |
|
206 | In LispyScript double quoted strings are multiline strings. As you can see above they span multiple lines.
|
207 | If you need a double quote inside the string, escape it with \".
|
208 |
|
209 | ### (if (condition) (if true expression) (if false expression))
|
210 |
|
211 | If takes a conditional expression and evaluates the true expression if the condition is true, or the false
|
212 | expression otherwise.
|
213 |
|
214 | ### (do (expression1) (expression2) ...)
|
215 |
|
216 | The do statement evaluates a set of expressions passed as it arguments.
|
217 |
|
218 | ### (when (condition) (expression1) (expression2) ...)
|
219 |
|
220 | The when statement evaluates a set of expressions passed as it arguments when the condition is true.
|
221 |
|
222 | ### (unless (condition) (expression1) (expression2) ...)
|
223 |
|
224 | The unless statement evaluates a set of expressions passed as it arguments when the condition is false.
|
225 |
|
226 | ### (each object (iterator) [context])
|
227 |
|
228 | each is just a macro that expands to the native 'forEach' function. So it will not work in old browsers.
|
229 | For backwards compatibility use a library like 'underscore.js'.
|
230 |
|
231 | ```lips
|
232 | (each [1, 2, 3]
|
233 | (function (elem index list)
|
234 | (console.log elem)))
|
235 | ```
|
236 |
|
237 | The above example using underscore.js.
|
238 |
|
239 | ```lisp
|
240 | (var _ (require 'underscore'))
|
241 | (_.each [1, 2, 3]
|
242 | (function (elem index list)
|
243 | (console.log elem)))
|
244 | ```
|
245 |
|
246 | ### (map object (iterator) [(context)])
|
247 |
|
248 | map is just a macro that expands to the native 'map' function. So it will not work in old browsers.
|
249 | For backwards compatibility use a library like 'underscore.js'.
|
250 |
|
251 | ### (reduce object (iterator) memo [context])
|
252 |
|
253 | reduce is just a macro that expands to the native 'reduce' function. So it will not work in old browsers.
|
254 | For backwards compatibility use a library like 'underscore.js'.
|
255 |
|
256 | ### (function (arguments expression) (expression1) (expression2) ... )
|
257 |
|
258 | Creates an anonymous function.
|
259 |
|
260 | ### (macro name (arguments expression) (template expression))
|
261 |
|
262 | ### (try (expression1) (expression2) ... (catch function))
|
263 |
|
264 | Try takes a set of expressions and evaluates them. The last expression must be a function, that
|
265 | will be called in case an exception is thrown. The function is called with the error object.
|
266 |
|
267 | ```lisp
|
268 | (var fs (require 'fs'))
|
269 | (var outfile "text.txt")
|
270 | (try
|
271 | (fs.writeFileSync outfile "Hello World")
|
272 | (function (err)
|
273 | (console.log (+ "Cannot write file " outfile)
|
274 | (process.exit 1)))
|
275 | ```
|
276 |
|
277 | ### (template (argument expression) (string expressions) ... )
|
278 |
|
279 | template takes a list of arguments to be passed to the template as the first argument. The rest of the arguments are string expressions that make up the template. Template returns a compiled template function which you must call with the arguments to expand the template.
|
280 |
|
281 | ### (template-repeat (array/object expression) (string expressions) ... )
|
282 |
|
283 | template repeat takes an array or object as its first argument and the rest are string expressions that form the template. The template is iterated over for every element of the array/object and the combined expanded template is returned. Within the template you can access the current element as 'elem' and the current index as 'index'.
|
284 | Note that while 'template returns a compiled template, template repeat returns the actual expanded template.
|
285 |
|
286 | ```lisp
|
287 | (var page
|
288 | (template (title links)
|
289 | "<!DOCTYPE html>
|
290 | <html>
|
291 | <head>
|
292 | <title>" title "</title>
|
293 | </head>
|
294 | <body>
|
295 | <ul class='nav'>"
|
296 |
|
297 | (template-repeat links "<li><a href=" (.href elem) ">" (.text elem) "</a></li>\n")
|
298 |
|
299 | "</ul>
|
300 | </body>
|
301 | </html>"))
|
302 |
|
303 | (console.log
|
304 | (page
|
305 | "My Home Page"
|
306 | [{href:"/about", text:"About"},
|
307 | {href:"/products", text:"Products"},
|
308 | {href:"/contact", text:"Contact"}]))
|
309 | ```
|
310 |
|
311 | ### (include "string filename")
|
312 |
|
313 | Includes a file to be compiled with this compilation unit.
|
314 |
|
315 | ### Comments
|
316 |
|
317 | Comments in LispyScript start with a `;` and span the rest of the line.
|
318 |
|
319 | ### LispyScript was inspired by [Beating the averages](http://www.paulgraham.com/avg.html).
|
320 |
|
321 | ### Discuss LispyScript at [https://groups.google.com/forum/#!forum/lispyscript](https://groups.google.com/forum/#!forum/lispyscript).
|
322 |
|
323 | ## Contributors
|
324 |
|
325 | Santosh Rajan [santoshrajan](https://github.com/santoshrajan).
|
326 | Irakli Gozalishvili [Gozala](https://github.com/Gozala).
|
327 |
|
328 | ## Change Log
|
329 |
|
330 | ### Version 0.1.8, 6 Aug, 2012
|
331 |
|
332 | Changed comments from "#" to ";".
|
333 | Made LispyScript browser compliant.
|
334 | Simplified LispyScript installation.
|
335 | Added support for stdin -> compile -> stdout.
|
336 | Added 'template-repeat'.
|
337 | Added setting array/object elements.
|
338 | Added chatserver example.
|
339 | Added a simple Twitter example using expressjs and lispyscript templates.
|
340 |
|
341 | ### Initial Release, Version 0.1.6, Jun 20, 2012
|
342 |
|
343 | [nodejs]:http://nodejs.org/
|
344 | [npm]:http://npmjs.org/ "Node Package Manager"
|