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.
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);
}
}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.
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.
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.
| 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 |
TulispContext— interpreter state, evaluation methods, and function registrationTulispObject— the core Lisp value typeTulispConvertible— how Rust types map to Lisp valuesbuiltin— all built-in functions and macros