Skip to content

Lisp interpreter in Rust with macro support and okayish syntax errors

License

Notifications You must be signed in to change notification settings

logistic-bot/lwhlisp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lwhlisp

lwhlisp is a lisp interpreter written in rust, based on this tutorial.

Getting started

Install Rust and clone this repository. Then, inside the repository:

cargo run --release

This will compile the project and launch a REPL. The finished binary is target/release/lwhlisp.

NOTE: The interactive session will start by loading the small included standard library (you can find the library in lib/lib.lisp).

If no such file is found, it will fail to load and you will get an error that looks like this:

Error: 
   0: While opening library file
   1: While opening file lib/lib.lisp
   2: No such file or directory (os error 2)

You can solve this problem by manually indicating where lwhlisp can find the library file:

cargo run --release -- --library /path/to/library/file.lisp

(The -- separates arguments to cargo and arguments to lwhlisp. It can be omitted when calling the lwhlisp binary directly.)

The REPL should look something like this:

user> 

Once you are at the user> prompt, you may enter lisp code to be evaluated.

user> (+ 1 2 3)
=> 6
user> (if (> 5 4) (println "Five is bigger than Four") (println "Five is smaller than Four"))
Five is bigger than Four
=> "Five is bigger than Four"

You can also run files:

cargo run --release -- -f file.lisp

factorial.lisp:

(define (factorial x)
   (if (= x 0)
      1
      (* x (factorial (- x 1)))))

(println (factorial 10))
$ cargo run --release -- -f file.lisp
3628800

Syntax

() is converted into nil at parse time.

quote

Takes a single argument, and returns it without evaluating

user> (quote (a b c))
=> (a b c)

Since this is used frequently, a shorthand is provided:

user> '(a b c)
=> (a b c)

The shorthand gets converted into the full version at parse time.

lambda

user> (lambda (x) (* x x)) 
=> (lambda (x) (* x x))

You can have multiple s-expressions in the body:

user> (lambda (x) (println x) (* x x)) 
=> (lambda (x) (println x) (* x x))

Lambdas can be directly evaluated:

user> ((lambda (x) (* x x)) 7)
=> 49
user> ((lambda (x) (println x) (* x x)) 7)
7
=> 49

Note that only the last s-expression is returned.

define

Binds a symbol to a value.

Basic syntax:

user> (define x 7)
=> x
user> x
=> 7

Creating a function:

user> (define square (lambda (x) (* x x)))
=> square
user> (square 7)
=> 49

Since this is a frequent action, there is special syntax for this:

user> (define (square x) (* x x))
=> square
user> (square 7)
=> 49

You can choose to get the arguments as a list instead:

user> (define (x . a) a)
=> x
user> (x 1 2 3)
=> (1 2 3)

Or have one (or more) required arguments, and get the rest as a list:

user> (define (x a . b) (println a) (println b))
=> x
user> (x 1 2 3)
1
(2 3)
=> "(2 3)"

(list is a function from the standard library that constructs a list from all of its arguments)

defmacro

Macros work the same way as function, except that the arguments to macros are not evaluated.

Macros use the following syntax:

(defmacro (name arg...) body...)

For example, consider the following macro:

(defmacro (ignore x)
   (cons 'quote (cons x nil)))

If we then evaluate the expression

user> (ignore foo) 
=> foo

where foo is a (potentially unbound) symbol, the body of ignore will be evaluated with the argument x bound to the unevaluated symbol foo. The result of this is:

(quote . (foo . nil))

which is equivalent to:

(quote foo)

or

'foo

Finally, evaluating this value will give us the result of evaluating the macro body:

foo

if

The syntax is as follows:

(if test true-expr false-expr)

If test is not nil, the result of evaluating this expression will be false-expr. Else, it will be true-expr.

Example

This is a simple program that calculates factorials in a recursive fashion:

(define (factorial x)
   (if (= x 0)
      1
      (* x (factorial (- x 1)))))

The base case, if x=0, will return 1. In all other cases, we will return fact(x - 1) * x.

user> (factorial 10) 
=> 3628800

About

Lisp interpreter in Rust with macro support and okayish syntax errors

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published