All Projects → thesephist → klisp

thesephist / klisp

Licence: MIT license
A Lisp written in about 200 lines of Ink, featuring an interactive literate programming notebook

Programming Languages

javascript
184084 projects - #8 most used programming language
CSS
56736 projects
Vim Script
2826 projects
Makefile
30231 projects
HTML
75241 projects

Projects that are alternatives of or similar to klisp

maverick
Web IDE and REPL for the Ink programming language, written in pure Ink on a self-hosted compiler toolchain
Stars: ✭ 26 (-7.14%)
Mutual labels:  repl, ink, torus-dom
Janet
A dynamic language and bytecode vm
Stars: ✭ 2,216 (+7814.29%)
Mutual labels:  interpreter, repl
Gomacro
Interactive Go interpreter and debugger with REPL, Eval, generics and Lisp-like macros
Stars: ✭ 1,784 (+6271.43%)
Mutual labels:  interpreter, repl
Mond
A scripting language for .NET Core
Stars: ✭ 237 (+746.43%)
Mutual labels:  interpreter, repl
Goto
Goto is an interpreted programming language written in go.
Stars: ✭ 79 (+182.14%)
Mutual labels:  interpreter, repl
Endbasic
BASIC environment with a REPL, a web interface, and RPi support written in Rust
Stars: ✭ 106 (+278.57%)
Mutual labels:  interpreter, repl
Codi.vim
📔 The interactive scratchpad for hackers.
Stars: ✭ 2,464 (+8700%)
Mutual labels:  interpreter, repl
Red
Red is a next-generation programming language strongly inspired by Rebol, but with a broader field of usage thanks to its native-code compiler, from system programming to high-level scripting and cross-platform reactive GUI, while providing modern support for concurrency, all in a zero-install, zero-config, single 1MB file!
Stars: ✭ 4,725 (+16775%)
Mutual labels:  interpreter, repl
fundot
The Fundot programming language.
Stars: ✭ 15 (-46.43%)
Mutual labels:  interpreter, repl
gorilla-repl
A fork of Jony Epsilon's rich REPL for Clojure in the notebook style.
Stars: ✭ 22 (-21.43%)
Mutual labels:  notebook, repl
go-notebook
Go-Notebook is inspired by Jupyter Project (link) in order to document Golang code.
Stars: ✭ 33 (+17.86%)
Mutual labels:  notebook, repl
Mappy
A functional programming language. Like LISP but focused around maps rather than lists.
Stars: ✭ 10 (-64.29%)
Mutual labels:  interpreter, repl
Mico
Mico ("Monkey" in catalan). Monkey language implementation done with C++. https://interpreterbook.com/
Stars: ✭ 19 (-32.14%)
Mutual labels:  interpreter, repl
Brain
An esoteric programming language compiler on top of LLVM based on Brainfuck
Stars: ✭ 112 (+300%)
Mutual labels:  interpreter, repl
Bic
A C interpreter and API explorer.
Stars: ✭ 719 (+2467.86%)
Mutual labels:  interpreter, repl
Charly
🐈 The Charly Programming Language | Written by @KCreate
Stars: ✭ 185 (+560.71%)
Mutual labels:  interpreter, repl
Rascal
The implementation of the Rascal meta-programming language (including interpreter, type checker, parser generator, compiler and JVM based run-time system)
Stars: ✭ 284 (+914.29%)
Mutual labels:  interpreter, repl
Ok
An open-source interpreter for the K5 programming language.
Stars: ✭ 408 (+1357.14%)
Mutual labels:  interpreter, repl
Go Pry
An interactive REPL for Go that allows you to drop into your code at any point.
Stars: ✭ 2,747 (+9710.71%)
Mutual labels:  interpreter, repl
charm
A [ functional stack ] based language.
Stars: ✭ 26 (-7.14%)
Mutual labels:  interpreter, repl

Klisp 🐍

Build Status

Klisp is a very minimal LISP written in about 200 lines of InK. It's primarily a pedagogical project -- I made it to understand Lisp better. (That's another way of saying: don't use it for serious things.) Ink's semantics are already quite lispy, so Klisp builds on Ink's semantics and adds an S-expression grammar and a repl, a true read-eval-print loop.

Examples in a Klisp repl

Syntactically, Klisp borrows from Scheme and Clojure, but tries to be as simple as possible without losing power. You can find some working examples of Klisp code in...

For example, a factorial is easily defined as the product of a range of integers.

(defn fact (n)
      (prod (range 1 (inc n) 1)))
(fact 10) ; => 3628800

And the Fibonacci sequence is recursively defined.

(defn fib (n)
      (if (< n 2)
          1
          (+ (fib (dec n))
             (fib (dec (dec n))))))
(fib 10) ; => 89
(map (seq 20) fib)
; => (1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765)

About Klisp

Klisp is a true lisp-1, with a minimal core of 6 special forms (quote, do, def, if, fn, macro) operating on S-expressions. The reader, evaluator, and printer in Klisp are all implemented as single Ink functions operating directly on S-expressions and strings.

  • quote takes its argument list as a Klisp list and returns it. (quote (a b c)) => (a b c)
  • do evaluates all forms in its arguments and returns the result of the last form. (do (+ 1 2) (* 3 4)) => 12
  • def takes a name and a value, and binds the value to the name in the current environment ("scope"). Only function bodies create new scopes. (do (def x 3) (* x x)) => 9
  • if returns either a consequent or an alternative depending on a condition. (if (= 1 2) 'A' 'B') => 'B'
  • fn creates a lambda expression (a function). It takes a list of arguments and a function body. It behaves like lambda in scheme.
    (do
      (def double
           (fn (n) (* 2 n)))
      (double 12)) ; => 24
  • macro is like fn, but defines a macro instead of a normal function. For example, the list macro is implemented in this way. There is an accompanying builtin function expand, which expands a list representing a macro expression without evaluating it. Use expand-all to apply this recursively to an expression.

These special forms, along with a small set of builtin functions like arithmetic operators and car / cdr / cons, are provided in the default environment. Every other language feature, including short-circuiting binary operators like and and or and fundamental forms like let and list, is implemented in the userspace in lib/klisp.klisp as functions or macros.

Usage

If you have Ink installed in your $PATH, the simplest way to try Klisp is to clone the repository and run make repl, which will start a read-eval-print loop. You can also run ./src/cli.ink <file>.klisp to run the interpreter over a source file, like test/000.klisp.

To run the repl, you'll also want rlwrap installed for the best experience. rlwrap provides a line-editable input to the repl.

$ git clone https://github.com/thesephist/klisp
$ cd klisp
$ make repl

rlwrap ink ./src/cli.ink
Klisp interpreter v0.1.
> (+ 1 2 3)
6
> (defn add1 (n) (+ n 1))
(fn (n) (+ n 1))
> (add1 41)
42
> ; start typing...

Klisp is composed of two Ink source files, src/klisp.ink and src/cli.ink, and one library file, lib/klisp.klisp. Klisp also depends on the std and str libraries in vendor/. As long as these 5 files are accessible in those directories, running ink src/cli.ink will start the interpreter.

Non-goals

Klisp has two significant flaws that were non-goals for this project.

  1. Klisp's current interpreter does not handle errors very well. This was mostly an educational project, and I don't think I'll end up writing much Klisp code, so preferred concision over error recovery in the interpreter. If I do end up writing lots of Klisp, I might come back and add better error handling in the interpreter. As it stands today, syntactic or semantic errors in Klisp code will crash the interpreter.
  2. Klisp is not fast. Actually, it's quite slow. That's because it's written in a dynamic, interpreted language itself. The Ink interpreter I've been testing Klisp with is a tree-walk interpreter written in Go, which is itself comparable to Python on a good day. Although faster, alternative interpreters like Vanta, written in Go, are being worked on, Klisp isn't designed to be fast, just an educational prototype mostly for myself.

Implementation

As noted above, Klisp's semantics match Ink's closely, because Ink is semantically already lispy, with primitive values and one complex type (a dictionary/associative array, which is used to implement lists in Klisp).

Most Klisp values, except for the symbol (atom), the list, and the function, are implemented transparently in the interpreter as Ink values. For example, the number 42 in Klisp is also just the number 42 within the Interpreter -- no boxing or wrapper types.

Symbols in Klisp are implemented as strings, which are variable-length byte arrays. To differentiate symbols from strings, symbols are prefixed with the null character char(0).

Lists are implemented with cons cells, and cons cells are implemented with a list of length 2 ([_, _]) in the underlying Ink code.

Functions and macros are implemented using an indirection using an object in the interpreter that wraps a function taking Klisp values as arguments. You can read more about this design in the interpreter source.

The rest of the ~200 lines of the interpreter core in src/klisp.ink are well commented and written to be legible. If you're curious about Klisp's inner workings, the source code is a great starting point. If you have questions, feel free to open an issue.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].