Skip to content

Nondv/glisp

Repository files navigation

glisp - PoC lisp in Golang

Based on a stupid-lisp PoC I’ve written a while ago. Inspired by LISP-1.5 (see LISP 1.5 Programmer’s Manual by McCarthy) and PicoLisp.

Was mainly developed to practice Golang

Usage

go build
./glisp yourcode.lisp

Features

Golang-embeddable and extendable

The interpreter simply needs a Bindigings list which can be extended with native functions via BuildNativeFn.

Code is actually data

Every function is simply a list starting with lambda symbol (or a native function which is bound directly from Golang).

This means that non-native functions can be inspected as lists and even modified (although not sure why you’d want that).

Also the way a lambda is invoked is its parameters are passed to it unevaluated. Consider this:

(let ((x 122)
      (inc (lambda ARG
             (+ ARG 1))))
  (inc x))

This will throw an error as ARG will be simply (x) which isn’t even an integer.

In order to make this work, we need to evaluate the arguments explicitly:

(let ((x 122)
      (inc (lambda ARG
             (let ((first-arg (eval (car ARG))))
               (+ first-arg 1)))))
  (inc x))

or implicitly (which is how you’d expect a lambda to work):

(let ((x 122)
      (inc (lambda (x)
             (+ x 1))))
  (inc x))

This feature has some interesting consequences:

  • No macros needed. Macros are basically lisp functions that are called at compile time and they work with their arguments unevaluated. This is a completely different plane of execution. “Code is data” is a bit of a buzzword at this point because in modern lisps it really isn’t (only at compile time).
  • Quote isn’t special. (let ((quote (lambda ARG (car ARG)))) (quote a ignored))
  • Dynamic binding. Since code is actually data, it means binding has to happen at the moment of evaluation which may be in entirely different context from where the function was created. While dynamic binding isn’t popular (only emacs and bash/etc come to mind from popular software), it does have some fun properties. Read some PicoLisp docs to learn more.
  • Symbolic programming ?

No special literals except ()

In lisps usually you expect to see nil and t. This lisp doesn’t treat them any special way and instead simply treats () as empty list. nil and t can be bound manually to () and (quote t) respectively.

Examples

mapcar and list

(let ((quote (lambda ARG (car ARG)))
      (not (lambda (x) (if x () (quote true))))
      (mapcar (lambda (f lst)
                (if (not lst)
                    lst
                    (cons (f (car lst)) (mapcar f (cdr lst))))))
      (list (lambda ARG
              (mapcar eval ARG))))
  (list 1 2 (list 4 5) list))

;; ==> (1 2 (4 5) (lambda ARG (mapcar eval ARG)))

reduce, push-last, and ->> (pipeline operator)

;; depends on: not, list

(define reduce
        (lambda (initial-value f lst)
          (if (not lst)
              initial-value
              (reduce (f initial-value (car lst)) f (cdr lst)))))

(define push-last
        (lambda (x lst)
          (cons (car lst)
                (if (cdr lst)
                    (push-last x (cdr lst))
                    (list x)))))

(define ->>
        (lambda ->>ARGS
          (eval (reduce (car ->>ARGS)
                        push-last
                        (cdr ->>ARGS)))))
(->> 100
     (+ 20)
     (+ 3)
     (list)
     (push-last 456))

;; ==> (123 456)

“Libraries”

(load "lang/core.lisp")

(->> 100
     (+ 20)
     (+ 3)
     (list)
     (push-last 456)
     (print))

;; ==> (123 456)

DSLs for embedding

Since this lisp can be embedded and extended, one could use this as a DSL. It’s also got potential for interactive programming. I made an example of a Go web server with handlers written in glisp.

See examples/embedded/webapi/webapi.go.

Run with:

go run nondv.io/glisp/examples/embedded/webapi

Unfinished business

  • Errors from lisp
  • Better string escaping. Currently, only \" is supported and there’s no way to provide \" as actual part of a string
  • format function to output text (print provides read-able output, which is different).
  • floats
  • arithmetic (currently, only + is implemented)
  • Reader macros. It’d be nice to have stuff like 'a work
  • Emacs integration
    • Requires REPL to accept multi-line input

About

Stupid Lisp in Golang

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors