Skip to content

A general purpose Lisp🛸 intended for use as Sage's preprocessor language

License

Notifications You must be signed in to change notification settings

adam-mcdaniel/sage-lisp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sage-Lisp

Logo

This crate implements a standalone Lisp implementation, intended for use in the Sage preprocessor.

Table of Contents

About

Sage-Lisp is a simple core lisp interpreter written in Rust, that can be expanded to include new functionality with built-in functions.

It's designed such that you can provide your own standard library of functions, implemented in Rust or in Lisp, and evaluate lisp expressions with your own standard conventions.

It works well as an embedded language for data transformation, where you can import data into the interpreter (using Serde), perform transformations on it, and then export the data back out into a format that you can use in your application.

Features

  • Simple Lisp Interpreter: A simple core lisp interpreter that can evaluate lisp expressions.
  • Built-in Functions: Extend the language with new functionality using built-in functions.
  • Symbol Interning: Symbols are interned to ensure that they are unique and fast to compare.
  • Tail Recursion: Uses tail recursion to evaluate deeply nested function calls without stack overflow.
  • Lazy Evaluation: Supports lazy evaluation of expressions, for defining special forms.
  • Serde Integration: Serialize and deserialize lisp expressions using Serde.
  • Error Handling: Provides helpful error messages for parsing and evaluation errors.
  • Expanded Syntax: Introduces infix operators, code block syntax, syntax for hashmaps and ordered maps, and more.
  • Customizable: Define your own standard library of functions and variables, and override the default behavior.

Usage

To run Sage lisp on a program, build it like so:

$ cargo build --features build-binary --release

Then, use it to run a file as a program.

$ cp ./target/release/sagel .
$ ./sagel example.lisp

Or provide a program as a command line argument.

$ ./sagel -c "(do (println hi!) (+ 1 2 3))"
hi!
6

Example Lisp Syntax

The example below shows some of the syntax that is supported by the interpreter, along with some of the built-in functions that are provided when the crate is used as an executable.

;; Define a function that calculates the factorial of a number
(defun fact (n) 
    (if n <= 0
        1
        n * (fact n - 1)))
;; Stirling's approximation for the factorial
(defun stirlings (n)
    (if n <= 0 1
        (* (sqrt 2 * 3.14159265358979323846 * n)
           ((n / 2.71828182845904523536) ^ n))))
;; Perform a quicksort on a list of numbers
(defun quicksort (lst)
    (if (<= (len lst) 1) lst {
        (define pivot (get lst (/ (len lst) 2)))
        (define less (filter (\(x) (< x pivot)) lst))
        (define equal (filter (\(x) (= x pivot)) lst))
        (define greater (filter (\(x) (> x pivot)) lst))
        (+ (quicksort less) equal (quicksort greater))}))

Example Embedding

Below is an example of how you can embed the interpreter into your application, with your own standard library of functions and variables.

// Import the necessary types and traits for the interpreter.
use sage_lisp::{Expr, Env};

// Create a new environment
fn make_core_env() -> Env {
    // Create a new environment
    let mut env = Env::new();

    // Create a function that adds two numbers
    env.bind_builtin("+", |env, exprs| {
        let mut sum = Expr::default();
        for e in exprs {
            // Evaluate the supplied argument in the environment
            let e = env.eval(e.clone());
            // Match the expression to the sum
            match (sum, e) {
                (Expr::None, b) => sum = b,
                (Expr::Int(a), Expr::Int(b)) => sum = Expr::Int(a + b),
                (Expr::Float(a), Expr::Float(b)) => sum = Expr::Float(a + b),
                (Expr::Int(a), Expr::Float(b)) => sum = Expr::Float(a as f64 + b),
                (Expr::Float(a), Expr::Int(b)) => sum = Expr::Float(a + b as f64),
                (Expr::String(a), Expr::String(b)) => sum = Expr::String(format!("{}{}", a, b)),
                (Expr::List(a), Expr::List(b)) => {
                    let mut list = a.clone();
                    list.extend(b);
                    sum = Expr::List(list);
                }
                (Expr::List(a), b) => {
                    let mut list = a.clone();
                    list.push(b);
                    sum = Expr::List(list);
                }
                // Return an error if the expression is invalid
                (a, b) => return Expr::error(format!("Invalid expr {} + {}", a, b)),
            }
        }
        sum
    });
    // Create a function that prints a string
    env.bind_builtin("println", |env, exprs| {
        for e in exprs {
            // Evaluate the supplied argument in the environment
            let e = env.eval(e.clone());
            // Print the expression
            match e {
                Expr::String(s) => print!("{}", s),
                Expr::Symbol(s) => print!("{}", s.name()),
                _ => print!("{}", e),
            }
        }
        println!();
        Expr::None
    });
    env
}

fn main() {
    // Create a new environment with our custom standard library
    let mut env = make_core_env();

    // Evaluate a lisp expression in the environment
    env.eval_str("{ (println 1 + 2) (println (+ 2 2)) }").unwrap();
}

About the Author

I'm a computer science PhD student at the University of Tennessee, Knoxville🍊. Rust is my favorite language, and I've written many other programming languages.

I'm always looking for new projects to work on, so if you have an idea for a project or a collaboration, feel free to reach out to me or join me on the Sage Discord server!

Documentation

For more information on how to use the Sage Lisp interpreter, see the documentation.

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

A general purpose Lisp🛸 intended for use as Sage's preprocessor language

Topics

Resources

License

Stars

Watchers

Forks