-
Notifications
You must be signed in to change notification settings - Fork 30
Go API
An instance of the GLISP interpreter is represented by a Glisp
struct.
You can create one using the NewGlisp
function.
env := glisp.NewGlisp()
To add code to the environment, you will need to call any of the three
load methods on the environment, which will compile GLISP code into VM
instructions and add the instructions to the environment. The methods are
named LoadString, LoadFile, and LoadStream. They will take as arguments a Go
string, a file pointer, or an io.RuneReader
, respectively.
If there is a syntax error, an error will be returned by the load function.
You will probably want to print or otherwise display this error message, since
it will tell you which line the error occurred.
err := env.LoadString("(+ 3 2)")
Once you have loaded in the code you want, you can run the code by calling
the Run
method on the environment. This will run the code and return the
top of the stack at the end or an error.
expr, err := env.Run()
If an error occurs, you must do some cleanup before more code can be loaded
and run. If you want to get a stack trace of what went wrong, call the
GetStackTrace
method, and you will receive the stack trace as a string.
Unfortunately, line-level runtime error information is not available. However, since most of the language uses functions, it should not be difficult to trace down your bug by inspecting the functions in the stack trace.
To reset the environment to a sane state, just call the Clear
method.
Once you have called Clear
, you can proceed to load and run more code.
The different types of LISP values are represented by the Sexp
interface.
Most of the subtypes correspond to regular Go values.
- SexpInt - int
- SexpFloat - float
- SexpChar - rune
- SexpBool - bool
- SexpStr - string
- SexpArray - slice
The four which are special are SexpSymbol
, SexpPair
, SexpHash
, and SexpFunction
.
A GLISP symbol contains a name and a number. Two symbols created with the same
name in the same environment are guaranteed to have the same number.
In Go, symbols must be created using the MakeSymbol
method of the Glisp
struct.
sym1 := env.MakeSymbol("foo")
sym2 := env.MakeSymbol("foo")
sym1.Name() == sym2.Name() // should be true
sym1.Number() == sym2.Number() // should be true
Pairs are represented in Go by a struct containing a head and tail expression.
You can create a pair using the Cons
function and access their head and tail
using the Head
and Tail
methods.
pair := glisp.Cons(SexpInt(1), SexpInt(2))
pair.Head() == SexpInt(1)
pair.Tail() == SexpInt(2)
If you want a list, you can create one from a slice using the MakeList
function.
list := glisp.MakeList([]Sexp{SexpInt(1), SexpInt(2), SexpInt(3)})
list.Head() // should be 1
list.Tail() // should be (2 3)
You can convert a list back into a slice using the ListToArray function. This will return an error if the expression passed in is not really a list.
arr, err := glisp.ListToArray(list)
The SexpHash
type is a Go map, but it would be to difficult to actually access the keys using the normal Go syntax. The suggested way is to use the functions HashGet
, HashGetDefault
, HashSet
, and HashDelete
. The HashGet
and HashGetDefault
functions retrieve a value from the hash given a key. If the key is not found, HashGet
returns an error, whereas HashGetDefault
returns the default value given as the third argument. The HashSet
and HashDelete
functions correspond to the hset!
and hdel!
LISP functions.
var hash SexpHash
err := HashSet(hash, SexpStr("foo"), SexpInt(3))
expr, err := HashGet(hash, SexpStr("foo"))
expr, err := HashGetDefault(hash, SexpStr("bar"), SexpInt(0))
err := HashDel(hash, SexpStr("foo"))
The SexpFunction
type is an opaque struct. The one thing you can do with
it in Go is apply it to some other expressions.
expr, err := glisp.Apply(fun, []Sexp{SexpInt(1)})
// equivalent to (apply fun [1]) in lisp
The Apply function returns the result of the function or an error if something went wrong.
One last type is the SexpSentinel
type. You should only ever use the
SexpNull
constant for this type. It represents the null value.
You can write functions in Go to be called by LISP code. Your function must have a signature like the following.
func Function(env *glisp.Glisp, name string, args []glisp.Sexp) (glisp.Sexp, error) {
// ...
}
The first argument is the interpreter environment, the second argument is the name by which the function was called, and the third argument is the arguments passed into the function by the LISP code.
The function should return the result LISP expression and nil if everything
went alright. If something goes wrong, the function should return SexpNull
and an error object.
Once you've defined your function, you can add it into an environment using
the AddFunction
method.
env.AddFunction(Function)
Sexp objects can be bound to names in the global scope of the environment by calling the AddGlobal
method.
// bind "bar" to variable foo in global scope
env.AddGlobal("foo", SexpStr("bar"))
You can define your own type to be used in GLISP by creating a type
implementing the glisp.Sexp interface. The only method in this interface is
SexpString
, which should return a string representing the given type.
You must provide your own functions for dealing with these types, as none of
the builtin functions will be able to deal with user-created types.