A tiny lisp interpreter (tbh, not even close)

I have written a Lisp parser before, and I am thinking about writing a Lisp interpreter.

Tl;DR: it is not even close to being an interpreter, but I reviewed and learned something in Rust instead.

Lexer/Parser part

I have written something like this several times before, so it isn’t that hard. This time I want to try a “router” design, which I use a lot in my work.

fn read_router(
    token: &str,
) -> Result<fn(&mut VecDeque<String>) -> Result<Atom, ParserError>, ParserError> {
    match token {
        "(" => Ok(read_exp),
        "'" => Ok(read_quote),
        "\"" => Ok(read_string),
        _ => Ok(read_sym),
    }
}

After finishing this, the major point of this blog just revealed itself: fn(&mut VecDeque<String>) -> Result<Atom, ParserError> is the type. I’m pretty sure I learned it before, but I hadn’t used it for a long time, and I put myself into “trait magic” too much. I even forgot that fn is the primary type.

Expression

Then I need to write the expression evaluation logic. I want to make a HashMap<function name, function> inside the context.

type Lmda = fn(&mut Context, &Args) -> Result<Value, ExpressionError>;

I even tried to use HRTB in this type alias, but actually, I don’t need it.

After all, I can run one tiny Lisp-like expression:

#[test]
fn test_eval() {
    let mut t = tokenize(Cursor::new(r#"(add 1 2 3)"#.as_bytes()));
    let atom = &read_root(&mut t).unwrap();
 //dbg!(atom);
 let mut ctx = Context {
        ..Default::default()
    };
 ctx.fnTable.insert("add".to_string(), functions::math::add);
 let exp = Expr::from_atom(atom, Rc::new(RefCell::new(ctx)));
 assert_eq!(exp.eval(), Ok(Value::Number(6)));
 //
 let mut t = tokenize(Cursor::new(r#"(add 1 2 (add 3 1))"#.as_bytes()));
    let atom = &read_root(&mut t).unwrap();
 let mut ctx = Context {
        ..Default::default()
    };
 ctx.fnTable.insert("add".to_string(), functions::math::add);
    let exp = Expr::from_atom(atom, Rc::new(RefCell::new(ctx)));
 assert_eq!(exp.eval(), Ok(Value::Number(7)));
}

Wrap up

It is not even an interpreter; it cannot create new functions, and functions have to be written in Rust.

But the joy I got after recalling that fn is a primary type was good.

Written on May 28, 2025