7 releases

0.2.4 Oct 24, 2024
0.2.3 Oct 24, 2024
0.1.2 Oct 15, 2024

#140 in Programming languages

MIT license

130KB
3K SLoC

Rusche

ci coverage crates.io docs.rs

Overview

Rusche is a library for writing an interpreter for a Scheme-like language in Rust. It lets you embed a Scheme interpreter into your Rust applications, allowing you to use Scheme as a scripting language or to create standalone Scheme interpreters.

Features

  • Minimalistic library with zero dependency
  • Lambdas and closures
  • Lexical scopes and binding
  • Macros using special forms like qusiquote (`), unquote (,), unquote-splicing (,@)
  • Garbage collection
  • Tail-call optimization
  • Interoperability with hosting Rust application via user-defined (a.k.a native) functions and Foreign data type.
  • Span support for informative error message, for example:
    repl:01(define plus
    ....:02(lambda (x 7)   ;; 7 should be y
    ....:03(+ x y)))
    
    error: 7 is not a symbol.
      1| (define plus
      2|     (lambda (x 7)
       |                ^
    

Usage

Implementing or embedding Rusche interpreter

use rusche::{tokenize, Evaluator, Expr, Parser};

let source = "(+ 1 (% 9 2))"; // 1 + (9 % 2) = 1 + 1 = 2

// Create Evaluator with basic primitives
let evaluator = Evaluator::with_prelude();

let mut parser = Parser::new();

// Tokenize source and add tokens to parser
let tokens = tokenize(source, None).unwrap();
parser.add_tokens(tokens);

// Parse tokens into an expression
let expr = parser.parse().unwrap().unwrap();

// Evaluate the parsed expression
let result = evaluator.eval(&expr);

assert_eq!(result, Ok(Expr::from(2)));

println!("{}", result.unwrap()); // this prints out 2

To learn about how to implement a standalone interpreter with REPL, have a look at examples/rusche-cli.

Rusche language

Here's a quick example to show what's possible with the Rusche language.

(defun fizzbuzz (n)
    (defun div? (n m) (= (% n m) 0))
    (cond ((div? n 15) "FizzBuzz")
          ((div? n 3) "Fizz")
          ((div? n 5) "Buzz")
          (#t n)))

(print "Enter a number to fizzbuzz: ")

(let ((n 1)
      (m (num-parse (read)))) ; read a number from stdio and store it to `m`
    (while (<= n m)
        (println (fizzbuzz n))
        (set! n (+ n 1))))

To see more examples, please checkout *.rsc files in the examples directory.

Also, you can run rusche-cli yourself with the following command:

cargo run --example rusche-cli

Documentation

No runtime deps