Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/Jaffe-/lispc
A simple Lisp interpreter in C
https://github.com/Jaffe-/lispc
Last synced: 2 months ago
JSON representation
A simple Lisp interpreter in C
- Host: GitHub
- URL: https://github.com/Jaffe-/lispc
- Owner: Jaffe-
- License: mit
- Created: 2013-05-01T09:03:09.000Z (over 11 years ago)
- Default Branch: master
- Last Pushed: 2024-07-20T08:33:08.000Z (6 months ago)
- Last Synced: 2024-08-03T18:16:46.617Z (6 months ago)
- Language: C
- Size: 47.9 KB
- Stars: 18
- Watchers: 2
- Forks: 1
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- AwesomeInterpreter - lispc
README
lispc
=====A simple Lisp interpreter written in C. It implements the most basic Lisp special forms (called operators here) as well as a small number of primitive procedures. It is lexically scoped and supports closures.
## Building
lispc should build out of the box on any system. Using gcc, simply do
> `gcc *.c -o lisp -std=c99`
Running the resulting executable will start the lispc interpreter. It does not parse files at the moment, and the REPL is very simple.
## Language
lispc is a minimalistic Lisp language. Expressions in lispc are either symbols, procedures, numbers or lists. Symbols are used as names in variable bindings, or can be used as identifiers for other purposes. A symbol evaluates to the value it is bound to. Numbers and procedures evaluate to themselves. List evaluation follows two basic rules: If the first element is a symbol and the symbol denotes one of the *operators*, evaluation rules for the particular operator is followed. If the first element does not denote one of the operators, the list expression is a function call. Every element in the list is evaluated, and the first element, if resulting in a procedure, is called with the rest of the elements as arguments.
Some examples:
Numbers evaluate to themselves:
LISP> 1234
=> 1234`def` is an operator, so the following list expression will be an operator call:
LISP> (def k 2)
=> KThe symbol `k` will after that call refer to the value 2:
LISP> k
=> 2A list where the first element is not an operator is a function call:
LISP> (+ 1 (* 1 2) 3)
=> 6### Operators
#### `\` (lambda)
`(\ (a1 a2 ...) exp)` constructs a function whose formal parameters are `a1`, `a2` and so on, and whose expression is `exp`.
Example:
(\ (x y z) (+ x y z)) ; a function which takes three arguments and sums them
((\ (x y z) (+ x y z)) 1 2 3) ; create the described procedure and call it with arguments 1, 2 and 3#### `def`
`(def a b)` introduces a new *variable binding* where the symbol `a` is bound tothe value `b`.
Example:
(def a 5) ; define a to be 5
(def square (\ (x) (* x x))) ; set square to be the function which squares its argument#### `if`
`(if test true-exp false-exp)` evaluates `test`. If the result is non-`NIL`, `true-exp` is evaluated. Otherwise, `false-exp` is evaluated.
Example:
(if (= x 2) (+ x 1) 2) ; evaluates (+ x 1) if x is equal to 2, or evaluates 2 if not
#### `'` (quote)
`(' exp)` simply evaluates to `exp`. The parser will convert expressions of the form `'x` to `(' x)`.
Example:
LISP> (' k)
=> K
LISP> 'k
=> K#### `set!`
`(set! a b)` changes the value what `a` refers to, to the value `b`.
#### `let`
`(let ((a1 b1) (a2 b2) ...) exp)` introduces *local bindings* of the symbol `a1` to the value `b1`, the symbol `a2` to the value `b2`, and so on. With these bindings, the `exp` expression is evaluated. The bindings only exist during the evaluation of `exp`. Already existant bindings outside the `let` scope will be shadowed by these bindings.
Example:
(let ((x 2) (y 3))
(+ x y)) ; x and y are only bound in this expression#### `do` or `:`
`(do exp1 exp2 ...)` evaluates the expressions `exp1`, `exp2`, ... in order, and uses the last evaluated value as its value.
Example:
(do (print 'hi)
(print 'there)
1234)This will print "HI THERE" on the screen, and the expression will evaluate to 1234.
### Primitives
### Generic functions
The standard library defines the functions `new-generic` and `implement` for using generic functions.
`(new-generic name)` creates a new generic function called `name` and returns a procedure which can call it.
`(implement name fn type)` defines a specific implementation of the generic function. `name` is the symbol of the generic function, `fn` is the implementing procedure, and `type` is a list or a symbol denoting the argument types the implementation handles. If `type` is a single symbol, the implementation is assumed to take a variable number of arguments, and every argument has to match this type. If `type` is a list, its items denote the argument types in order.
#### Example
The function + might be useful to overload for certain types. The standard library implements + for integers as follows:
(def + (new-generic '+))
(implement '+ _+ 'integer)Here, `_+` denotes the built in primitive procedure for adding integers. The symbolic type argument makes this implementation accept a variable number of arguments, all of integer type.
To add a new implementation, for example for a type `matrix`, we can do
(implement '+ add-matrix '(matrix matrix))
Here, the type argument is a list of two elements, meaning that this implementation will take two arguments, both of type `matrix`. The function `add-matrix` must be a function taking at least two such arguments.