Sibilant is a language that is parsed by javascript and compiles to javascript.
Sibilant is inspired by lisp and follows many lisp conventions, although it is still relatively close to the underlying javascript.
Macros can be defined in sibilant and included at compile time.
Sibilant is entirely written in sibilant and can recompile itself.
sibilantjs.info includes an in-browser as-you-type sibilant compiler and tutorial, so you can get a sense of the language without leaving your browser.
First, install node.js [ github ] and
npm [ github ].
$ npm install sibilant
-v / --version Print out a version string and exit
-h / --help This message
--repl / [no args] Sibilant interactive command prompt
--execute / -x This is a flag. Execute input files in order supplied.
--eval [optional STRING] / -e [optional STRING]
Evaluate STR if provided, otherwise evaluate standard in.
--output DIR / -o DIR Output input files to this dir, replacing .lisp with .js.
--input FILE / -i FILE / FILE
Add this file to the input files. If the execute flag is
set, input files will be executed. If an output dir is
specified, each file will be written to that dir.
Otherwise, each file will be written to STDOUT.
$ git clone git://github.com/jbr/sibilant.git
$ cd sibilant
$ npm link .
$ sibilant src/*.lisp -o lib
$ sibilant -x test/test.lisp # you're now running a sibilant you just compiled.
$ sibilant test/test.lisp
$ sibilant test/test.lisp -o . # put test.js here
or $ sibilant --input test/test.lisp --output .
$ sibilant -x test/test.lisp
$ sibilant
or $ sibilant --repl
"to the world, he said \"hello\""
Multiline strings are okay:
"this is a
multiline string"
Numbers are directly translated to javascript. Valid numbers include 10
, 98.6
, -100
, et cetera.
Variables only include lower-case letters. Hyphens are legal, and are translated into camelcase in javascript. For example, j-query
is jQuery
. Asterisks become underscores. For example *global-variable*
would become _globalVariable_
in javascript. Variables can end with question marks and exclamation marks. These are translated to javascript as “Q” and “B” (for bang). For example, ready?
becomes readyQ
and finish!
becomes finishB
.
'(a b c d e f g) ;=> ['a', 'b', 'c', 'd', 'e', 'f', 'g']
'hello ;=> "hello"
(list 1 2 3 4 5) ;=> [1,2,3,4,5]
(defvar foo "foo now has this value")
(hash key1 'value1
key2 'value2) ;=> {key1: "value1", key2: "value2"}
Let’s say we’re in node and want to use the built-in function require
.
(defvar sys (require 'sys)) ;=> var sys = require("sys")
(defun square (arg1) (* arg1 arg1))
;=> var square = function(arg1) { return arg1 * arg1 }
(square 5)
The last statement is always returned.
Sibilant supports optional arguments. If an argument is preceded by &optional
, it will be skipped if there are fewer than the total number of arguments. Optional arguments are filled in from left to right.
(defun example (a &optional b c &optional d e)
(... do something with a b c d e ...))
(example 1 2 3) ; these will be a c and e
(example 1 2) ; this will raise an exception
(example 1 2 3 4) ; these will be a b c e
(example 1 2 3 4 5) ; these will be a b c d e
Sibilant supports arbitrary-length arguments by way of the &rest label. If an argument is preceded by &rest
, any arguments beyond the named arguments will be put into the &rest
argument. The &rest
argument must be the last argument currently.
(defun another-example (a b &rest everything-else)
(... do something with a, b and the something-else array ...))
(another-example 1) ; this will raise an exception
(another-example 1 2) ; a will be 1, b will be 2, and everythingElse will be []
(another-example 1 2 3 4 5) ; a will be 1, b will be 2, and everythingElse will be [3,4,5]
If both &optional
and &rest
arguments are used, the &optionals
are filled in before arguments spill over into &rest
Lambdas are defined just like functions (with &rest
and &optional
args, if you so desire), but with the lambda keyword.
In fact, (defun name (args) body)
is functionally equivalent to (defvar name (lambda (args) body))
.
if
takes three arguments — the condition that is checked, the true body, and the false body. if
always has a return value, which is achieved by wrapping every if
statement in an anonymous self-calling function (in js).
(if (= 2 (+ 1 1)) 'yes 'no) ;=> the return value of this will be "yes"
; for multi-statement blocks, use progn:
(if (defined? foo)
(progn
(call-your-mom)
(say 'hi))
(progn
(complain "bad at examples")
(42)))
; in this horrible example, if foo is defined, the first block will be
; called and the return value of say("hi") will be returned. If foo is
; undefined, the second block will be called and 42 will be returned.
when
is like the true branch of if
, but doesn’t require progn
for multi-statement blocks.
(when (<= 2 rainbow-count)
(puts "oh my god, double rainbow")
(upload-to-youtube))
The most up to date documentation will always be sibilantjs.info which includes an as-you-type in-browser sibilant compiler.
Check out sibilant itself, which is written 100% in sibilant to get a sense of what’s possible.
Also, check out macros.lisp to see how easy it is to write new compiler macros.