-
Notifications
You must be signed in to change notification settings - Fork 2
Go API
An instance of the GLISP interpreter is represented by a Environment struct.
You can create one using the New function.
env := glisp.New()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
- SexpBytes - []byte
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 Environment struct.
sym1 := env.MakeSymbol("foo")
sym2 := env.MakeSymbol("foo")
sym1.Name() == sym2.Name() // should be true
sym1.Number() == sym2.Number() // should be truePairs 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.
hash,_ := MakeHash(nil)
err := hash.HashSet(SexpStr("foo"), SexpInt(3))
expr, err := hash.HashGet(SexpStr("foo"))
expr, err := hash.HashGetDefault(SexpStr("bar"), SexpInt(0))
err := hash.HashDel(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 lispThe 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.Environment, args glisp.Args) (glisp.Sexp, error) {
// ...
}The first argument is the interpreter environment, the second 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(name, Function)Sexp objects can be bound to names in the current scope of the environment by calling the Bind method.
// bind "bar" to variable foo in current scope
env.Bind("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.