Skip to content

shsms/tulisp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

953 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tulisp

docs.rs Crates.io

Tulisp is an embeddable Lisp interpreter for Rust with Emacs Lisp-compatible syntax. It is designed as a configuration and scripting layer for Rust applications — zero external dependencies, low startup cost, and a clean API for exposing Rust functions to Lisp code.

Quick start

Requires Rust 1.88 or higher.

use std::process;
use tulisp::{TulispContext, Error};

fn run(ctx: &mut TulispContext) -> Result<(), Error> {
    ctx.defun("add-round", |a: f64, b: f64| -> i64 {
        (a + b).round() as i64
    });

    let result: i64 = ctx.eval_string("(add-round 10.2 20.0)")?.try_into()?;
    assert_eq!(result, 30);
    Ok(())
}

fn main() {
    let mut ctx = TulispContext::new();
    if let Err(e) = run(&mut ctx) {
        println!("{}", e.format(&ctx));
        process::exit(-1);
    }
}

Exposing Rust functions

TulispContext::defun handles argument evaluation, arity checking, and type conversion automatically. Built-in arg/return types include i64, f64, bool, String, Number, Vec<T>, and TulispObject. Use Option<T> for &optional parameters, Rest<T> for &rest, a Result<T, Error> return type for fallible functions, and &mut TulispContext as the first parameter to access the interpreter from the function body. Custom Rust types become passable by implementing TulispConvertible — most commonly via opaque Shared<dyn TulispAny> storage for arbitrary Clone + Display values.

For raw argument lists and code transformation, see defspecial and defmacro.

Keyword-argument and alist-shaped structs

When a function accepts many keyword-style parameters, derive Plistable on a struct via AsPlist! and use Plist<T> as the parameter type:

use tulisp::{TulispContext, Plist, AsPlist};

AsPlist! {
    struct ServerConfig { host: String, port: i64 {= 8080} }
}

let mut ctx = TulispContext::new();
ctx.defun("connect", |cfg: Plist<ServerConfig>| -> String {
    format!("{}:{}", cfg.host, cfg.port)
});
// (connect :host "example.com" :port 443)  =>  "example.com:443"

{= expr} provides a default, field<":custom-key"> overrides the keyword name, and Option<T> fields read explicit nil as None.

AsAlist! / Alistable mirror the design for alist-shaped values that arrive as a single argument (a list of dotted pairs). For converting a plist or alist held in a free variable rather than from a defun call, both traits also expose from_plist / from_alist standalone.

Built-in Lisp features

Tulisp covers the standard Emacs Lisp shapes — control flow, bindings, functions and macros, list / string / arithmetic / hash-table operations, threading macros, backquote / unquote, error handling (error, catch, throw, condition-case), tail-call optimisation, and lexical scoping. See the builtin module for the full list of forms and functions.

Cargo features

Feature Description
sync Makes the interpreter thread-safe (Arc/RwLock instead of Rc/RefCell)
big_functions Increases the maximum number of defun parameters from 5 to 10
etags Enables TAGS file generation for Lisp source files

Next steps

Projects using Tulisp

  • slippy — a configuration tool for the Sway window manager
  • microsim — a microgrid simulator

About

An embeddable lisp interpreter written in Rust.

Topics

Resources

License

Stars

Watchers

Forks

Contributors