UNPKG

10.8 kBMarkdownView Raw
1# LispyScript
2
3## A Javascript with Lispy syntax and Macros!
4
5Lispyscript is Javascript using a 'Lispy' syntax, and compiles to Javascript.
6
7An inherent problem with Javascript is that it has no
8macro support, like other Lisp like languages. That's because macros manipulate the syntax tree while
9compiling. And this is next to impossible in a language like Javascript. In LispyScript we write Javascript
10in a tree structure. If you know Javascript and a Lisp like language, then using LispyScript will be a
11breeze. 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
19A LispyScript program is made up of expressions in parenthesis. The first element in the expression
20is a function or a LispyScript keyword. The rest of the elements (separated by space characters) are
21the arguments to the function. As you can see above we can directly access javascript functions from
22LispyScript.
23
24A more intricate Hello World!
25
26```lisp
27(if (undefined? window)
28 (console.log "Hello LispyScript!")
29 (alert "Hello LispyScript!"))
30```
31
32You can have expressions within expressions.
33
34## Functions
35
36An anonymous function in LispyScript.
37
38```lisp
39(function (x) (* x x))
40```
41
42The first element in an expression can be an anonymous function.
43
44```lisp
45((function (x) (* x x)) 2)
46```
47
48That was the anonymous function above evaluated immediately with argument 2. Functions
49return the last expression evaluated within the function.
50
51You 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
60The 'var' expression takes a variable name as the second element and sets its value to the third.
61
62## LispyScript is Javascript!
63
64All 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
72If you noticed we passed a Javascript literal array as the first argument to 'forEach'. You could just as well
73have passed in '{one: 1, two: 2, three: 3}' instead.
74
75You can access object methods and properties using the "." notation.
76
77```lisp
78(console.log (.greet {greet: "hello"}))
79```
80
81ie. If the first element of an expression starts with a ".", it's considered as a property of the
82second element, and the expresion evaluates to the property.
83
84You 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
91You 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
102The 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
117LispyScript is not a dialect of Lisp. There is no list processing in LispyScript . LispyScript
118is Javascript using a Lispy syntax (a tree syntax). This is so that we can manipulate the syntax tree
119while compiling, in order to support macros.
120
121You can define a macro.
122
123```lisp
124(macro array? (obj)
125 (= (toString.call ~obj) "[object Array]"))
126```
127
128The 'array?' conditional is defined as a macro in LispyScript. The 'macro' expression takes a name as
129its second element, a parameters list in the third element, and the fourth element is the template
130to which the macro will expand.
131
132Now 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
144The "let" macro creates lexically scoped variables with initial values. It does this by creating
145an anonymous function whose argument names are the required variable names, sets the variables to
146their initial values by calling the function immediately with the values. The macro also wraps the
147required code inside the function.
148
149Now lets look at the call to the 'let' macro. 'names' will correspond to '(name email tel)'. 'rest...'
150corresponds to '(console.log name) (console.log email) (console.log tel)', which is the rest of the
151expressions after vals. We want to dereference these values in the macro template, and we do that
152with '~names', '~rest...'. However 'vals' corresponds to ("John" "john@example.org" "555-555-5555").
153But thats not the way we want to dereference it. We need to dereference it without the parenthesis.
154For that we use '~@vals'.
155
156We don't really need 'let' in LispyScript. We have 'var'. But if you need it, you can extend LispyScript
157by adding this macro to your code. Thats the power of macros. You can
158extend the language itself or create your own domain specific language.
159
160## Installing LispyScript
161
162The compiler requires [nodejs][] and [npm][] installation. However the compiled
163code is standalone javascript that will run anywhere. To install use npm:
164
165 npm install lispyscript
166
167
168## Using LispyScript
169
1701. Typing `lispy` into the command prompt will open a simple REPL.
171
1722. Typing `lispy program.ls` will compile `program.ls` into `program.js` in
173 the same directory.
174
1753. 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
184Note: `=` and `!=` work like `===` and `!==` in Javascript.
185
186## LispyScript Statements
187
188### (str (string expression) ...)
189
190Adds 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\">
201Hello World
202</body>
203</html>"))
204```
205
206In LispyScript double quoted strings are multiline strings. As you can see above they span multiple lines.
207If you need a double quote inside the string, escape it with \".
208
209### (if (condition) (if true expression) (if false expression))
210
211If takes a conditional expression and evaluates the true expression if the condition is true, or the false
212expression otherwise.
213
214### (do (expression1) (expression2) ...)
215
216The do statement evaluates a set of expressions passed as it arguments.
217
218### (when (condition) (expression1) (expression2) ...)
219
220The when statement evaluates a set of expressions passed as it arguments when the condition is true.
221
222### (unless (condition) (expression1) (expression2) ...)
223
224The unless statement evaluates a set of expressions passed as it arguments when the condition is false.
225
226### (each object (iterator) [context])
227
228each is just a macro that expands to the native 'forEach' function. So it will not work in old browsers.
229For 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
237The 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
248map is just a macro that expands to the native 'map' function. So it will not work in old browsers.
249For backwards compatibility use a library like 'underscore.js'.
250
251### (reduce object (iterator) memo [context])
252
253reduce is just a macro that expands to the native 'reduce' function. So it will not work in old browsers.
254For backwards compatibility use a library like 'underscore.js'.
255
256### (function (arguments expression) (expression1) (expression2) ... )
257
258Creates an anonymous function.
259
260### (macro name (arguments expression) (template expression))
261
262### (try (expression1) (expression2) ... (catch function))
263
264Try takes a set of expressions and evaluates them. The last expression must be a function, that
265will 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
279template 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
283template 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'.
284Note 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
313Includes a file to be compiled with this compilation unit.
314
315### Comments
316
317Comments 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
325Santosh Rajan [santoshrajan](https://github.com/santoshrajan).
326Irakli Gozalishvili [Gozala](https://github.com/Gozala).
327
328## Change Log
329
330### Version 0.1.8, 6 Aug, 2012
331
332Changed comments from "#" to ";".
333Made LispyScript browser compliant.
334Simplified LispyScript installation.
335Added support for stdin -> compile -> stdout.
336Added 'template-repeat'.
337Added setting array/object elements.
338Added chatserver example.
339Added 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"