GitHub - adam-mcdaniel/wisp: A light lisp written in C++
source link: https://github.com/adam-mcdaniel/wisp
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
A light lisp written in C++
Why write a lisp?
Lisp is one of those niche, beautiful languages that people only really use to either
- Write a lisp interpreter
- Show off how "code is data!!!"
So why add to the list of infinite lisp interpreters?
The answer is simple: I'm bored out of my mind in quarantine. If you were looking to find out why this particular lisp is special, you're fresh out of luck.
But isn't the fact that it's a lisp enough?
Syntax and Special Forms
Like every other lisp, this language uses s-expressions for code syntax and data syntax. So, for example, the s-expression (print 5)
is both a valid code snippet, and a valid list containing the items print
and 5
.
When the data (print 5)
is evaluated by the interpreter, it evaluates print
and 5
, and then applies print
to 5
.
Here's the result.
>>> (print 5) 5 => 5
That's super cool! But what if we want to define our own functions? We can use the builtin function defun
!
; define a function `fact` that takes an argument `n` (defun fact (n) (if (<= n 1) 1 (* n (fact (- n 1))) ))
Thats awesome! But did you notice anything different about the defun
function? It doesn't evaluate its arguments. If the atom fact
were evaluated, it would throw an error like so:
>>> fact error: the expression `fact` failed in scope { } with message "atom not defined"
This is known as a special form, where certain functions "quote" their arguments. We can quote things ourselves too, but the language automatically quotes arguments to special forms itself.
If you want to "quote" a value yourself, you can do it like this.
; quote the s-expression (1 2 3) so it's not evaluated >>> (print '(1 2 3)) (1 2 3) => (1 2 3)
As you can see, quote negates an evaluation. For example, whenever the expression ''a
is evaluated, it becomes 'a
. This can be useful for when you want to write long lists of data or variable names without wanting to evaluate them as code.
(if cond a b)
if
only evaluates its cond
argument. If cond
is truthy (non-zero), then a
is evaluated. Otherwise, b
is evaluated.
This special form is the main method of control flow.
(do a b c ...)
do
takes a list of s-expressions and evaluates them in the order they were given (in the current scope), and then returns the result of the last s-expression.
This special form allows lambda functions to have multi-step bodies.
(scope a b c ...)
scope
takes a list of s-expressions and evaluates them in the order they were given in a new scope, and then returns the result of the last s-expression.
This special form allows the user to evaluate blocks of code in new scopes.
(defun name params body)
defun
evaluates none of its arguments.
This special form allows the user to conveniently define functions.
(define name value)
define
evaluates the value
argument, which is then assigned to name
in the current scope.
This special form allows the user to bind atoms to values in a scope.
(lambda params body)
lambda
evaluates none of its arguments.
This special form allows the user to define anonymous functions.
(quote x)
quote
evaluates none of its arguments.
This is equivalent to the 'expr
syntactic sugar.
(for x list ...)
for
evaluates only its list argument.
for
iterates through the list storing each element in x
, and then evaluating all of the rest of the values in the for
body. It then returns the last value evaluated.
(while cond ...)
while
evaluates only its cond argument.
while
evaluates its condition expression every iteration before running. If it is true, it continues to evaluate every expression in the while
body. It then returns the last value evaluated.
Examples
Here are some example math-y functions to wrap your head around.
; quicksort (defun qs (l) (if (<= (len l) 1) l (do (define pivot (first l)) (+ (qs (filter (lambda (n) (> pivot n)) l)) (list pivot) (qs (tail (filter (lambda (n) (<= pivot n)) l))) )) )) ; decrement a number (defun dec (n) (- n 1)) ; increment a number (defun inc (n) (+ n 1)) ; not a bool (defun not (x) (if x 0 1)) ; negate a number (defun neg (n) (- 0 n)) ; is a number positive? (defun is-pos? (n) (> n 0)) ; is a number negative? (defun is-neg? (n) (< n 0))
Usage
Using and compiling wisp
Dependencies
Compile with your C++ compiler of choice. This is compatible with all standard versions of C++ since ANSI C++.
$ git clone https://github.com/adam-mcdaniel/wisp $ cd wisp $ g++ wisp.cpp -o wisp
Using the binary
Run wisp in interactive mode:
$ ./wisp >>> (print "Hello world!") Hello world! => "Hello world!"
Interpret a file:
$ ./wisp -f "examples/hello_world.lisp" Hello world!
Interpret from command line argument:
$ ./wisp -c '(print "Hello world!")' Hello world!
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK