├── .gitignore ├── TODO.txt ├── src ├── model │ ├── runtime_error.rs │ ├── symbol.rs │ ├── mod.rs │ ├── lambda.rs │ ├── env.rs │ ├── list.rs │ └── value.rs ├── main.rs ├── lib.rs ├── utils.rs ├── macros.rs ├── parser.rs ├── default_environment.rs └── interpreter.rs ├── .github └── workflows │ └── rust.yml ├── Cargo.toml ├── LICENSE ├── .vscode └── launch.json ├── tests ├── little_lisper.rs ├── parser.rs ├── default_environment.rs └── interpreter.rs ├── Cargo.lock └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | .vscode -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | - Optional arguments 2 | - Ensure reference cycles are handled 3 | - Make strings immutable? 4 | - Use plain string references for symbols 5 | - Add support to lisp! for: ' ` , ... ?predicates 6 | - Add builtin function to get system time -------------------------------------------------------------------------------- /src/model/runtime_error.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | /// An error that occurred while evaluating some lisp code 4 | #[derive(Debug, Clone, PartialEq, Eq)] 5 | pub struct RuntimeError { 6 | pub msg: String, 7 | } 8 | 9 | impl std::fmt::Display for RuntimeError { 10 | fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 11 | write!(formatter, "Runtime error: {}", self.msg) 12 | } 13 | } 14 | 15 | impl std::error::Error for RuntimeError {} 16 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose && cargo test --verbose --features=f64 && cargo test --verbose --features=bigint && cargo test --verbose --features=bigint,f64 23 | -------------------------------------------------------------------------------- /src/model/symbol.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * A String [newtype](https://rust-unofficial.github.io/patterns/patterns/behavioural/newtype.html) 3 | * representing a lisp symbol (identifier) 4 | */ 5 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 6 | pub struct Symbol(pub String); 7 | 8 | impl From<&str> for Symbol { 9 | fn from(s: &str) -> Self { 10 | Symbol(String::from(s)) 11 | } 12 | } 13 | 14 | impl std::fmt::Display for Symbol { 15 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 16 | std::fmt::Display::fmt(&self.0, f) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code, unused_macros)] 2 | 3 | mod default_environment; 4 | mod interpreter; 5 | mod model; 6 | mod parser; 7 | mod utils; 8 | #[macro_use] 9 | mod macros; 10 | 11 | use std::{cell::RefCell, rc::Rc}; 12 | 13 | use rust_lisp::{default_env, interpreter::eval_block, parser::parse, start_repl}; 14 | 15 | // 🦀 Try finding me! I'm hidden all around the code! 16 | 17 | fn main() { 18 | match std::env::args().nth(1) { 19 | Some(code) => { 20 | let env_rc = Rc::new(RefCell::new(default_env())); 21 | 22 | println!( 23 | "{}", 24 | eval_block(env_rc, parse(&code).filter_map(|a| a.ok())).unwrap() 25 | ); 26 | } 27 | None => start_repl(None), 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust_lisp" 3 | description = "A Rust-embeddable Lisp, with support for interop with native Rust functions" 4 | version = "0.18.0" 5 | authors = ["Brandon Smith"] 6 | repository = "https://github.com/brundonsmith/rust_lisp" 7 | edition = "2021" 8 | exclude = [".github", ".vscode", "TODO.txt"] 9 | license = "MIT" 10 | 11 | [lib] 12 | name = "rust_lisp" 13 | path = "src/lib.rs" 14 | test = true 15 | doc = true 16 | 17 | [[bin]] 18 | name = "rust_lisp" 19 | path = "src/main.rs" 20 | 21 | [features] 22 | # What integer to use for Value::Int 23 | bigint = ["num-bigint", "num-traits"] 24 | i128 = [] 25 | i64 = [] 26 | i16 = [] 27 | i8 = [] 28 | 29 | # Use f64 for Value::Float, if unset, use f32 30 | f64 = [] 31 | 32 | [dependencies] 33 | cfg-if = "1.0" 34 | libm = { version = "0.2", optional = true } 35 | num-traits = { version = "0.2", optional = true } 36 | num-bigint = { version = "0.4", optional = true } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Brandon Smith 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![forbid(unsafe_code)] 2 | 3 | pub mod interpreter; 4 | pub mod model; 5 | pub mod parser; 6 | pub mod utils; 7 | 8 | mod default_environment; 9 | pub use default_environment::default_env; 10 | 11 | #[macro_use] 12 | mod macros; 13 | 14 | use model::Env; 15 | use std::io::{self, prelude::*}; 16 | use std::{cell::RefCell, rc::Rc}; 17 | 18 | // 🦀 I am all over this project! 19 | /// Starts a REPL prompt at stdin/stdout. **This will block the current thread.** 20 | pub fn start_repl(env: Option) { 21 | let env_rc = Rc::new(RefCell::new(env.unwrap_or_else(default_env))); 22 | 23 | print!("> "); 24 | io::stdout().flush().unwrap(); 25 | for line in io::stdin().lock().lines() { 26 | match interpreter::eval_block( 27 | env_rc.clone(), 28 | parser::parse(&line.unwrap()).filter_map(|a| a.ok()), 29 | ) { 30 | Ok(val) => println!("{}", val), 31 | Err(e) => println!("{}", e), 32 | }; 33 | 34 | print!("> "); 35 | io::stdout().flush().unwrap(); 36 | } 37 | 38 | // Properly go to the next line after quitting 39 | println!(); 40 | } 41 | -------------------------------------------------------------------------------- /src/model/mod.rs: -------------------------------------------------------------------------------- 1 | use cfg_if::cfg_if; 2 | cfg_if! { 3 | if #[cfg(feature = "bigint")] { 4 | use num_bigint::BigInt; 5 | } 6 | } 7 | 8 | cfg_if! { 9 | if #[cfg(feature = "bigint")] { pub type IntType = BigInt; } 10 | else if #[cfg(feature = "i128")] { pub type IntType = i128; } 11 | else if #[cfg(feature = "i64")] { pub type IntType = i64; } 12 | else if #[cfg(feature = "i16")] { pub type IntType = i16; } 13 | else if #[cfg(feature = "i8")] { pub type IntType = i8; } 14 | else { 15 | /// The underlying type to use for storing lisp integers. Controlled via feature-flags. 16 | pub type IntType = i32; 17 | } 18 | } 19 | 20 | cfg_if! { 21 | if #[cfg(feature = "f64")] { pub type FloatType = f64; } 22 | else { 23 | /// The underlying type to use for storing lisp floats. Controlled via feature-flags. 24 | pub type FloatType = f32; 25 | } 26 | } 27 | 28 | mod env; 29 | mod lambda; 30 | mod list; 31 | mod runtime_error; 32 | mod symbol; 33 | mod value; 34 | 35 | pub use env::Env; 36 | pub use lambda::Lambda; 37 | pub use list::List; 38 | pub use runtime_error::RuntimeError; 39 | pub use symbol::Symbol; 40 | pub use value::{HashMapRc, NativeFunc, Value}; 41 | -------------------------------------------------------------------------------- /src/model/lambda.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::fmt::Debug; 3 | use std::rc::Rc; 4 | 5 | use super::{Env, Symbol, Value}; 6 | 7 | /// A Lisp function defined in Lisp. 8 | #[derive(Debug, Clone)] 9 | pub struct Lambda { 10 | pub closure: Rc>, 11 | pub argnames: Vec, 12 | pub body: Rc, 13 | } 14 | 15 | impl PartialEq for Lambda { 16 | fn eq(&self, other: &Self) -> bool { 17 | self.closure.as_ptr() == other.closure.as_ptr() 18 | && self.argnames == other.argnames 19 | && self.body == other.body 20 | } 21 | } 22 | 23 | impl std::hash::Hash for Lambda { 24 | fn hash(&self, state: &mut H) { 25 | self.closure.as_ptr().hash(state); 26 | self.argnames.hash(state); 27 | self.body.hash(state); 28 | } 29 | } 30 | 31 | impl std::fmt::Display for Lambda { 32 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 33 | let body_str = format!("{}", &self.body); 34 | 35 | return write!( 36 | f, 37 | "({}) {}", 38 | self.argnames 39 | .iter() 40 | .map(|sym| sym.0.as_str()) 41 | .collect::>() 42 | .join(" "), 43 | &body_str[1..body_str.chars().count() - 1] 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug executable 'rust_lisp'", 11 | "cargo": { 12 | "args": [ 13 | "build", 14 | "--bin=rust_lisp", 15 | "--package=rust_lisp" 16 | ], 17 | "filter": { 18 | "name": "rust_lisp", 19 | "kind": "bin" 20 | } 21 | }, 22 | "args": [], 23 | "cwd": "${workspaceFolder}" 24 | }, 25 | { 26 | "type": "lldb", 27 | "request": "launch", 28 | "name": "Debug unit tests in executable 'rust_lisp'", 29 | "cargo": { 30 | "args": [ 31 | "test", 32 | "--no-run", 33 | "--bin=rust_lisp", 34 | "--package=rust_lisp" 35 | ], 36 | "filter": { 37 | "name": "rust_lisp", 38 | "kind": "bin" 39 | } 40 | }, 41 | "args": [], 42 | "cwd": "${workspaceFolder}" 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /tests/little_lisper.rs: -------------------------------------------------------------------------------- 1 | use rust_lisp::{ 2 | default_env, 3 | interpreter::eval, 4 | lisp, 5 | model::{IntType, RuntimeError, Value}, 6 | parser::parse, 7 | }; 8 | use std::{cell::RefCell, rc::Rc}; 9 | 10 | #[test] 11 | fn one() { 12 | assert_eq!( 13 | eval_str("(car (list 1 2 3))"), 14 | Ok(Value::from(Into::::into(1))) 15 | ); 16 | } 17 | 18 | #[test] 19 | fn two() { 20 | assert_eq!( 21 | eval_str("(car (list (list 1 2 3) 4 5 6))"), 22 | Ok(lisp! { (1 2 3) }) 23 | ); 24 | } 25 | 26 | #[test] 27 | fn three() { 28 | assert_eq!( 29 | eval_str("(car (list))"), 30 | Err(RuntimeError { 31 | msg: "Attempted to apply car on nil".to_owned() 32 | }) 33 | ); 34 | } 35 | 36 | #[test] 37 | fn four() { 38 | assert_eq!( 39 | eval_str("(car (car (list (list \"hotdogs\") \"and\")))"), 40 | Ok(lisp! { "hotdogs" }) 41 | ); 42 | } 43 | 44 | #[test] 45 | fn five() { 46 | assert_eq!(eval_str("(cdr (list 1 2 3))"), Ok(lisp! { (2 3) })); 47 | } 48 | 49 | #[test] 50 | fn six() { 51 | assert_eq!( 52 | eval_str("(cons (list 1 2 3) 4)"), 53 | Err(RuntimeError { 54 | msg: "\"cons\" requires argument 2 to be a list; got 4".to_owned() 55 | }) 56 | ); 57 | } 58 | 59 | #[test] 60 | fn seven() { 61 | assert_eq!(eval_str("(cons 4 (list 1 2 3))"), Ok(lisp! { (4 1 2 3) })); 62 | } 63 | 64 | #[cfg(test)] 65 | fn eval_str(source: &str) -> Result { 66 | let ast = parse(source).next().unwrap().unwrap(); 67 | let env = Rc::new(RefCell::new(default_env())); 68 | return eval(env, &ast); 69 | } 70 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "autocfg" 7 | version = "1.0.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 10 | 11 | [[package]] 12 | name = "cfg-if" 13 | version = "1.0.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 16 | 17 | [[package]] 18 | name = "libm" 19 | version = "0.2.6" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" 22 | 23 | [[package]] 24 | name = "num-bigint" 25 | version = "0.4.3" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" 28 | dependencies = [ 29 | "autocfg", 30 | "num-integer", 31 | "num-traits", 32 | ] 33 | 34 | [[package]] 35 | name = "num-integer" 36 | version = "0.1.44" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 39 | dependencies = [ 40 | "autocfg", 41 | "num-traits", 42 | ] 43 | 44 | [[package]] 45 | name = "num-traits" 46 | version = "0.2.14" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 49 | dependencies = [ 50 | "autocfg", 51 | ] 52 | 53 | [[package]] 54 | name = "rust_lisp" 55 | version = "0.18.0" 56 | dependencies = [ 57 | "cfg-if", 58 | "libm", 59 | "num-bigint", 60 | "num-traits", 61 | ] 62 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::{any::Any, rc::Rc}; 2 | 3 | use crate::model::{FloatType, HashMapRc, IntType, List, RuntimeError, Symbol, Value}; 4 | 5 | /// Given a `Value` assumed to be a `Value::List()`, grab the item at `index` 6 | /// and err if there isn't one. 7 | pub fn require_arg<'a>( 8 | func_or_form_name: &str, 9 | args: &'a [Value], 10 | index: usize, 11 | ) -> Result<&'a Value, RuntimeError> { 12 | args.get(index).ok_or_else(|| RuntimeError { 13 | msg: format!( 14 | "\"{}\" requires an argument {}", 15 | func_or_form_name, 16 | index + 1 17 | ), 18 | }) 19 | } 20 | 21 | /// Given a `Value` assumed to be a `Value::List()`, and some type T, grab the 22 | /// item at `index` in the list and try converting it to type T. RuntimeError if 23 | /// the argument doesn't exist, or if it is the wrong type. 24 | pub fn require_typed_arg<'a, T>( 25 | func_or_form_name: &str, 26 | args: &'a [Value], 27 | index: usize, 28 | ) -> Result 29 | where 30 | T: TryFrom<&'a Value> + TypeName, 31 | { 32 | require_arg(func_or_form_name, args, index)? 33 | .try_into() 34 | .map_err(|_| RuntimeError { 35 | msg: format!( 36 | "\"{}\" requires argument {} to be a {}; got {}", 37 | func_or_form_name, 38 | index + 1, 39 | T::get_name(), 40 | args.get(index).unwrap_or(&Value::NIL) 41 | ), 42 | }) 43 | } 44 | 45 | pub trait TypeName { 46 | fn get_name() -> &'static str; 47 | } 48 | 49 | impl TypeName for IntType { 50 | fn get_name() -> &'static str { 51 | "int" 52 | } 53 | } 54 | 55 | impl TypeName for FloatType { 56 | fn get_name() -> &'static str { 57 | "float" 58 | } 59 | } 60 | 61 | impl TypeName for &String { 62 | fn get_name() -> &'static str { 63 | "string" 64 | } 65 | } 66 | 67 | impl TypeName for &Symbol { 68 | fn get_name() -> &'static str { 69 | "symbol" 70 | } 71 | } 72 | 73 | impl TypeName for &List { 74 | fn get_name() -> &'static str { 75 | "list" 76 | } 77 | } 78 | 79 | impl TypeName for &HashMapRc { 80 | fn get_name() -> &'static str { 81 | "hash map" 82 | } 83 | } 84 | 85 | impl TypeName for &Rc { 86 | fn get_name() -> &'static str { 87 | "foreign value" 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /tests/parser.rs: -------------------------------------------------------------------------------- 1 | use rust_lisp::{ 2 | model::{IntType, Value}, 3 | parser::{parse, ParseError}, 4 | }; 5 | 6 | #[macro_use] 7 | extern crate rust_lisp; 8 | 9 | #[test] 10 | fn parse_basic_expression() { 11 | let ast = parse( 12 | " 13 | (list 14 | (* 1 2) ;; a comment 15 | (/ 6 3 \"foo\"))", 16 | ) 17 | .next() 18 | .unwrap() 19 | .unwrap(); 20 | 21 | assert_eq!( 22 | ast, 23 | lisp! { 24 | (list 25 | (* 1 2) 26 | (/ 6 3 "foo")) 27 | } 28 | ); 29 | } 30 | 31 | #[test] 32 | fn parse_nil() { 33 | let source = "()"; 34 | let ast = parse(source).next().unwrap().unwrap(); 35 | 36 | assert_eq!(ast, Value::NIL); 37 | } 38 | 39 | #[test] 40 | fn parse_atom() { 41 | let source = "12"; 42 | let ast = parse(source).next().unwrap().unwrap(); 43 | 44 | assert_eq!(ast, Value::from(Into::::into(12))); 45 | } 46 | 47 | #[test] 48 | fn parse_negative_float() { 49 | let source = "-1.2"; 50 | let ast = parse(source).collect::>>(); 51 | 52 | assert_eq!(ast, vec![Ok(Value::from(-1.2))]); 53 | } 54 | 55 | #[test] 56 | fn parse_multiple_lines() { 57 | let ast = parse( 58 | " 59 | (print 1) 60 | (print 2) 61 | (print 3)", 62 | ) 63 | .collect::, _>>() 64 | .unwrap(); 65 | 66 | assert_eq!( 67 | ast, 68 | vec![ 69 | lisp! { (print 1) }, 70 | lisp! { (print 2) }, 71 | lisp! { (print 3) }, 72 | ] 73 | ); 74 | } 75 | 76 | // These should return a ParseError or None, but should not panic 77 | 78 | #[test] 79 | fn parse_unclosed_string() { 80 | let res = parse("\"foo").next(); 81 | 82 | assert_eq!( 83 | res, 84 | Some(Err(ParseError { 85 | msg: "Unclosed string at index 3".to_owned() 86 | })) 87 | ) 88 | } 89 | 90 | #[test] 91 | fn parse_unclosed_list() { 92 | let res = parse("(12 foo").next(); 93 | 94 | assert_eq!( 95 | res, 96 | Some(Err(ParseError { 97 | msg: "Unclosed list at index 7".to_owned() 98 | })) 99 | ) 100 | } 101 | 102 | #[test] 103 | fn parse_incomplete_float() { 104 | let res = parse("1.").next(); 105 | 106 | assert_eq!( 107 | res, 108 | Some(Err(ParseError { 109 | msg: "Expected decimal value after '.' at index 1".to_owned() 110 | })) 111 | ) 112 | } 113 | 114 | #[test] 115 | fn parse_nonsense() { 116 | let res = parse("122jkh").next(); 117 | 118 | assert_eq!(res, None) 119 | } 120 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | /// A macro for more easily creating s-expressions from within Rust code 2 | /// ```ignore 3 | /// fn parse_basic_expression() { 4 | /// let ast1 = parse( 5 | /// " 6 | /// (+ 3 1)", 7 | /// ) 8 | /// .next() 9 | /// .unwrap() 10 | /// .unwrap(); 11 | /// 12 | /// let n = 2; 13 | /// let ast2 = lisp! { 14 | /// (+ { Value::Int(n + 1) } 1) 15 | /// }; 16 | /// 17 | /// assert_eq!( 18 | /// ast1, 19 | /// ast2 20 | /// ); 21 | /// } 22 | /// ``` 23 | #[allow(unused_macros)] 24 | #[macro_export] 25 | macro_rules! lisp { 26 | 27 | 28 | // Embed a Rust expression with { } 29 | ( { $e:expr } ) => { 30 | $e 31 | }; 32 | 33 | 34 | // Lists 35 | ( ( $($val:tt)* ) ) => { 36 | $crate::model::Value::List([ $(lisp!{ $val }),* ].iter().collect::<$crate::model::List>()) 37 | }; 38 | 39 | 40 | // 🦀 Very special! 41 | // Special atoms 42 | (nil) => { $crate::model::Value::NIL }; 43 | (NIL) => { $crate::model::Value::NIL }; 44 | (t) => { $crate::model::Value::True }; 45 | (T) => { $crate::model::Value::True }; 46 | (f) => { $crate::model::Value::False }; 47 | (F) => { $crate::model::Value::False }; 48 | 49 | 50 | // Symbols 51 | ($sym:ident) => { 52 | $crate::model::Value::Symbol($crate::model::Symbol(String::from(stringify!( $sym )))) 53 | }; 54 | // these aren't valid Rust identifiers 55 | ( + ) => { $crate::model::Value::Symbol($crate::model::Symbol(String::from("+"))) }; 56 | ( - ) => { $crate::model::Value::Symbol($crate::model::Symbol(String::from("-"))) }; 57 | ( * ) => { $crate::model::Value::Symbol($crate::model::Symbol(String::from("*"))) }; 58 | ( / ) => { $crate::model::Value::Symbol($crate::model::Symbol(String::from("/"))) }; 59 | ( == ) => { $crate::model::Value::Symbol($crate::model::Symbol(String::from("=="))) }; 60 | ( != ) => { $crate::model::Value::Symbol($crate::model::Symbol(String::from("!="))) }; 61 | ( < ) => { $crate::model::Value::Symbol($crate::model::Symbol(String::from("<"))) }; 62 | ( <= ) => { $crate::model::Value::Symbol($crate::model::Symbol(String::from("<="))) }; 63 | ( > ) => { $crate::model::Value::Symbol($crate::model::Symbol(String::from(">"))) }; 64 | ( >= ) => { $crate::model::Value::Symbol($crate::model::Symbol(String::from(">="))) }; 65 | 66 | 67 | // Literals 68 | ($e:literal) => { 69 | // HACK: Macros don't have a good way to 70 | // distinguish different kinds of literals, 71 | // so we just kick those out to be parsed 72 | // at runtime. 73 | $crate::parser::parse(stringify!($e)).next().unwrap().unwrap() 74 | }; 75 | } 76 | -------------------------------------------------------------------------------- /tests/default_environment.rs: -------------------------------------------------------------------------------- 1 | use rust_lisp::{ 2 | default_env, 3 | interpreter::eval, 4 | lisp, 5 | model::{IntType, Value}, 6 | }; 7 | use std::{cell::RefCell, rc::Rc}; 8 | 9 | #[test] 10 | fn range() { 11 | assert_eq!( 12 | eval_ast(lisp! { 13 | (range 0 10) 14 | }), 15 | eval_ast(lisp! { 16 | (list 0 1 2 3 4 5 6 7 8 9) 17 | }) 18 | ); 19 | } 20 | 21 | #[test] 22 | fn nth() { 23 | assert_eq!( 24 | eval_ast(lisp! { 25 | (nth 3 (range 5 15)) 26 | }), 27 | Value::from(Into::::into(8)) 28 | ); 29 | } 30 | 31 | #[test] 32 | fn map() { 33 | assert_eq!( 34 | eval_ast(lisp! { 35 | (map 36 | (lambda (n) (* n 2)) 37 | (range 0 10)) 38 | }), 39 | eval_ast(lisp! { 40 | (list 0 2 4 6 8 10 12 14 16 18) 41 | }) 42 | ); 43 | } 44 | 45 | #[test] 46 | fn reverse() { 47 | assert_eq!( 48 | eval_ast(lisp! { 49 | (reverse (range 0 10)) 50 | }), 51 | eval_ast(lisp! { 52 | (list 9 8 7 6 5 4 3 2 1 0) 53 | }) 54 | ); 55 | } 56 | 57 | #[test] 58 | fn sort() { 59 | assert_eq!( 60 | eval_ast(lisp! { 61 | (sort (list 4 3 2 0 1)) 62 | }), 63 | eval_ast(lisp! { 64 | (list 0 1 2 3 4) 65 | }) 66 | ); 67 | } 68 | 69 | #[test] 70 | fn filter() { 71 | assert_eq!( 72 | eval_ast(lisp! { 73 | (filter 74 | (lambda (x) (== x "foo")) 75 | (list "foo" "bar" "a" "b" "foo")) 76 | }), 77 | eval_ast(lisp! { 78 | (list "foo" "foo") 79 | }) 80 | ); 81 | } 82 | 83 | #[test] 84 | fn map_list_of_lists() { 85 | let result = eval_ast(lisp! { 86 | (map (lambda (x) x) (list (list 0 1) (list 2 3))) 87 | }); 88 | 89 | assert_eq!(result, lisp! {((0 1) (2 3))}); 90 | } 91 | 92 | #[test] 93 | fn filter_list_of_lists() { 94 | let result = eval_ast(lisp! { 95 | (filter (lambda (x) (> (length x) 2)) 96 | (list 97 | (list 0) 98 | (list 0 1) 99 | (list 0 1 2) 100 | (list 0 1 2 3))) 101 | }); 102 | 103 | assert_eq!(result, lisp! {((0 1 2) (0 1 2 3))}); 104 | } 105 | 106 | #[test] 107 | fn hash_map() { 108 | let result = eval_ast(lisp! { 109 | (begin 110 | (define my_hash (hash "one" 1 "two" 2 "three" 3)) 111 | (hash_set my_hash "four" 4) 112 | (+ 113 | (hash_get my_hash "one") 114 | " " 115 | (hash_get my_hash "four"))) 116 | }); 117 | 118 | assert_eq!(result, lisp! { "1 4" }); 119 | } 120 | 121 | #[test] 122 | fn number_cast_comparisons() { 123 | assert_eq!( 124 | eval_ast(lisp! { 125 | (< 0 0.1) 126 | }), 127 | lisp! { T } 128 | ); 129 | 130 | assert_eq!( 131 | eval_ast(lisp! { 132 | (< { Value::Int((-2).into()) } 1) 133 | }), 134 | lisp! { T } 135 | ); 136 | 137 | assert_eq!( 138 | eval_ast(lisp! { 139 | (>= { Value::Float(-2.1) } 1) 140 | }), 141 | lisp! { F } 142 | ); 143 | } 144 | 145 | #[cfg(test)] 146 | fn eval_ast(ast: Value) -> Value { 147 | let env = Rc::new(RefCell::new(default_env())); 148 | return eval(env, &ast).unwrap(); 149 | } 150 | -------------------------------------------------------------------------------- /src/model/env.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::rc::Rc; 3 | use std::{collections::HashMap, fmt::Debug}; 4 | 5 | use super::{RuntimeError, Symbol, Value}; 6 | 7 | /// An environment of symbol bindings. Used for the base environment, for 8 | /// closures, for `let` statements, for function arguments, etc. 9 | #[derive(Debug)] 10 | pub struct Env { 11 | parent: Option>>, 12 | entries: HashMap, 13 | } 14 | 15 | impl Env { 16 | /// Create a new, empty environment 17 | pub fn new() -> Self { 18 | Self { 19 | parent: None, 20 | entries: HashMap::new(), 21 | } 22 | } 23 | 24 | /// Create a new environment extending the given environment 25 | pub fn extend(parent: Rc>) -> Self { 26 | Self { 27 | parent: Some(parent), 28 | entries: HashMap::new(), 29 | } 30 | } 31 | 32 | /// Walks up the environment hierarchy until it finds the symbol's value or 33 | /// runs out of environments. 34 | pub fn get(&self, key: &Symbol) -> Option { 35 | if let Some(val) = self.entries.get(&key) { 36 | Some(val.clone()) // clone the Rc 37 | } else if let Some(parent) = &self.parent { 38 | parent.borrow().get(key) 39 | } else { 40 | None 41 | } 42 | } 43 | 44 | /// Define a new key in the current environment 45 | pub fn define(&mut self, key: Symbol, value: Value) { 46 | self.entries.insert(key, value); 47 | } 48 | 49 | /// Find the environment where this key is defined, and update its value. 50 | /// Returns an Err if the symbol has not been defined anywhere in the hierarchy. 51 | pub fn set(&mut self, key: Symbol, value: Value) -> Result<(), RuntimeError> { 52 | if self.entries.contains_key(&key) { 53 | self.entries.insert(key, value); 54 | Ok(()) 55 | } else if let Some(parent) = &self.parent { 56 | parent.borrow_mut().set(key, value) 57 | } else { 58 | Err(RuntimeError { 59 | msg: format!("Tried to set value of undefined symbol \"{}\"", key), 60 | }) 61 | } 62 | } 63 | 64 | /// Delete the nearest (going upwards) definition of this key 65 | pub fn undefine(&mut self, key: &Symbol) { 66 | if self.entries.contains_key(key) { 67 | self.entries.remove(key); 68 | } else if let Some(parent) = &self.parent { 69 | parent.borrow_mut().undefine(key); 70 | } 71 | } 72 | 73 | fn display_recursive(&self, output: &mut String, depth: i32) { 74 | let indent = &(0..depth).map(|_| " ").collect::(); 75 | 76 | output.push_str(indent); 77 | output.push_str("{ "); 78 | 79 | for (symbol, value) in &self.entries { 80 | output.push_str(format!("\n{} {}: {}", indent, symbol, value).as_str()); 81 | } 82 | 83 | if let Some(parent) = &self.parent { 84 | output.push_str("\n\n"); 85 | parent 86 | .as_ref() 87 | .borrow() 88 | .display_recursive(output, depth + 1); 89 | } 90 | 91 | output.push('\n'); 92 | output.push_str(indent); 93 | output.push('}'); 94 | } 95 | } 96 | 97 | impl std::fmt::Display for Env { 98 | fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 99 | let mut output = String::new(); 100 | 101 | output.push_str("Env: "); 102 | self.display_recursive(&mut output, 0); 103 | 104 | write!(formatter, "{}", &output) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/model/list.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::fmt::Debug; 3 | use std::fmt::Display; 4 | use std::iter::FromIterator; 5 | use std::rc::Rc; 6 | 7 | use super::{RuntimeError, Value}; 8 | 9 | /** 10 | * A Lisp list, implemented as a linked-list 11 | */ 12 | #[derive(Debug, PartialEq, Eq, Clone)] 13 | pub struct List { 14 | head: Option>>, 15 | } 16 | 17 | impl List { 18 | pub const NIL: List = List { head: None }; 19 | 20 | pub fn car(&self) -> Result { 21 | self.head 22 | .as_ref() 23 | .map(|rc| rc.borrow().car.clone()) 24 | .ok_or_else(|| RuntimeError { 25 | msg: String::from("Attempted to apply car on nil"), 26 | }) 27 | } 28 | #[must_use] 29 | pub fn cdr(&self) -> List { 30 | List { 31 | head: self 32 | .head 33 | .as_ref() 34 | .and_then(|rc| rc.borrow().cdr.as_ref().cloned()), 35 | } 36 | } 37 | 38 | #[must_use] 39 | pub fn cons(&self, val: Value) -> List { 40 | List { 41 | head: Some(Rc::new(RefCell::new(ConsCell { 42 | car: val, 43 | cdr: self.head.clone(), 44 | }))), 45 | } 46 | } 47 | } 48 | 49 | impl<'a> List { 50 | pub fn into_iter(list: &'a List) -> ConsIterator { 51 | ConsIterator(list.head.clone()) 52 | } 53 | } 54 | 55 | /// A `ConsCell` is effectively a linked-list node, where the value in each node 56 | /// is a lisp `Value`. To be used as a true "list", the ConsCell must be wrapped 57 | /// in Value::List(). 58 | #[derive(Debug, PartialEq, Eq)] 59 | struct ConsCell { 60 | pub car: Value, 61 | pub cdr: Option>>, 62 | } 63 | 64 | impl std::hash::Hash for ConsCell { 65 | fn hash(&self, state: &mut H) { 66 | self.car.hash(state); 67 | self.cdr.as_ref().map(|rc| rc.as_ptr()).hash(state); 68 | } 69 | } 70 | 71 | impl Display for List { 72 | fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 73 | if let Some(head) = &self.head { 74 | write!(formatter, "({})", head.as_ref().borrow()) 75 | } else { 76 | write!(formatter, "NIL") 77 | } 78 | } 79 | } 80 | 81 | impl Display for ConsCell { 82 | fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 83 | match self.cdr.as_ref() { 84 | Some(cdr) => write!(formatter, "{} {}", self.car, cdr.borrow()), 85 | None => write!(formatter, "{}", self.car), 86 | } 87 | } 88 | } 89 | 90 | impl std::hash::Hash for List { 91 | fn hash(&self, state: &mut H) { 92 | self.head.as_ref().map(|rc| rc.as_ptr()).hash(state); 93 | } 94 | } 95 | 96 | impl<'a> IntoIterator for &'a List { 97 | type Item = Value; 98 | type IntoIter = ConsIterator; 99 | 100 | fn into_iter(self) -> Self::IntoIter { 101 | ConsIterator(self.head.clone()) 102 | } 103 | } 104 | 105 | #[derive(Clone)] 106 | pub struct ConsIterator(Option>>); 107 | 108 | impl Iterator for ConsIterator { 109 | type Item = Value; 110 | 111 | fn next(&mut self) -> Option { 112 | self.0.clone().map(|cons| { 113 | let val = cons.borrow().car.clone(); 114 | 115 | self.0 = cons.borrow().cdr.clone(); 116 | 117 | val 118 | }) 119 | } 120 | } 121 | 122 | impl ExactSizeIterator for ConsIterator { 123 | fn len(&self) -> usize { 124 | let mut length: usize = 0; 125 | 126 | self.clone().for_each(|_| length += 1); 127 | 128 | length 129 | } 130 | } 131 | 132 | impl FromIterator for List { 133 | fn from_iter>(iter: I) -> Self { 134 | let mut new_list = List { head: None }; 135 | let mut tail: Option>> = None; 136 | 137 | for val in iter { 138 | // The cons cell for the current value 139 | let new_cons = Rc::new(RefCell::new(ConsCell { 140 | car: val, 141 | cdr: None, 142 | })); 143 | 144 | // if this is the first cell, put it in the List 145 | if new_list.head.is_none() { 146 | new_list.head = Some(new_cons.clone()); 147 | // otherwise, put it in the current tail cell 148 | } else if let Some(tail_cons) = tail { 149 | tail_cons.as_ref().borrow_mut().cdr = Some(new_cons.clone()); 150 | } 151 | 152 | // the current cell is the new tail 153 | tail = Some(new_cons); 154 | } 155 | 156 | new_list 157 | } 158 | } 159 | 160 | impl<'a> FromIterator<&'a Value> for List { 161 | fn from_iter>(iter: I) -> Self { 162 | iter.into_iter().cloned().collect() 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/brundonsmith/rust_lisp/rust.yml?branch=master)](https://github.com/brundonsmith/rust_lisp/actions) 2 | [![rust_lisp shield](https://img.shields.io/crates/v/rust_lisp)](https://crates.io/crates/rust_lisp) 3 | [![docs.rs](https://img.shields.io/docsrs/rust_lisp/latest)](https://docs.rs/rust_lisp/latest/rust_lisp/) 4 | 5 | # What is this? 6 | 7 | This is a Lisp interpreter, written in Rust, intended to be embeddable as a 8 | library in a larger application for scripting purposes. Goals: 9 | 10 | - Small footprint (both code size and memory usage) 11 | - No runtime dependencies [1] 12 | - Easy, ergonomic interop with native Rust functions 13 | - Small but practical set of Lisp functionality 14 | 15 | [1] `cfg-if` is build-time, `num-traits` add (I believe) no runtime presence, 16 | and `num-bigint` is entirely opt-in (at build time) 17 | 18 | # Basic Usage 19 | 20 | ```rust 21 | [dependencies] 22 | rust_lisp = "0.18.0" 23 | ``` 24 | 25 | ```rust 26 | use std::{cell::RefCell, rc::Rc}; 27 | 28 | use rust_lisp::default_env; 29 | use rust_lisp::parser::parse; 30 | use rust_lisp::interpreter::eval; 31 | 32 | fn main() { 33 | 34 | // create a base environment 35 | let env = Rc::new(RefCell::new(default_env())); 36 | 37 | // parse into an iterator of syntax trees (one for each root) 38 | let mut ast_iter = parse("(+ \"Hello \" \"world!\")"); 39 | let first_expression = ast_iter.next().unwrap().unwrap(); 40 | 41 | // evaluate 42 | let evaluation_result = eval(env.clone(), &first_expression).unwrap(); 43 | 44 | // use result 45 | println!("{}", &evaluation_result); 46 | } 47 | ``` 48 | 49 | As you can see, the base environment is managed by the user of the library, as 50 | is the parsing stage. This is to give the user maximum control, including 51 | error-handling by way of `Result`s. 52 | 53 | # The data model 54 | 55 | The heart of the model is `Value`, an enum encompassing every type of valid Lisp 56 | value. Most of these are trivial, but `Value::List` is not. It holds a recursive 57 | `List` data structure which functions internally like a linked-list. 58 | `into_iter()` and `from_iter()` have been implemented for `List`, and there is 59 | also a `lisp!` macro (see below) which makes working with Lists, in particular, 60 | much more convenient. 61 | 62 | `Value` does not implement `Copy` because of cases like `Value::List`, so if you 63 | read the source you'll see lots of `value.clone()`. This almost always amounts 64 | to copying a primitive, except in the `Value::List` case where it means cloning 65 | an internal `Rc` pointer. In all cases, it's considered cheap enough to do 66 | liberally. 67 | 68 | # The environment and exposing Rust functions 69 | 70 | The base environment is managed by the user of the library mainly so that it can 71 | be customized. `default_env()` prepopulates the environment with a number of 72 | common functions, but these can be omitted (or pared down) if you wish. Adding 73 | an entry to the environment is also how you would expose your Rust functions to 74 | your scripts, which can take the form of either regular functions or closures: 75 | 76 | ```rust 77 | fn my_func(env: Rc>, args: &Vec) -> Result { 78 | println!("Hello world!"); 79 | return Ok(Value::NIL); 80 | } 81 | 82 | ... 83 | 84 | env.borrow_mut().define( 85 | Symbol::from("sayhello"), 86 | Value::NativeFunc(my_func) 87 | ); 88 | ``` 89 | 90 | ```rust 91 | env.borrow_mut().define( 92 | Symbol::from("sayhello"), 93 | Value::NativeFunc( 94 | |env, args| { 95 | println!("Hello world!"); 96 | return Ok(Value::NIL); 97 | }) 98 | ); 99 | ``` 100 | 101 | In either case, a native function must have the following function signature: 102 | 103 | ```rust 104 | type NativeFunc = fn(env: Rc>, args: &Vec) -> Result; 105 | ``` 106 | 107 | The first argument is the environment at the time and place of calling (closures 108 | are implemented as environment extensions). The second argument is the Vec of 109 | evaluated argument values. For convenience, utility functions (`require_arg()`, 110 | `require_int_parameter()`, etc) have been provided for doing basic argument 111 | retrieval with error messaging. See `default_environment.rs` for examples. 112 | 113 | # The `lisp!` macro 114 | 115 | A Rust macro, named `lisp!`, is provided which allows the user to embed 116 | sanitized Lisp syntax inside their Rust code, which will be converted to an AST 117 | at compile-time: 118 | 119 | ```rust 120 | fn parse_basic_expression() { 121 | let ast = parse(" 122 | (list 123 | (* 1 2) ;; a comment 124 | (/ 6 3 \"foo\"))").next().unwrap().unwrap(); 125 | 126 | assert_eq!(ast, lisp! { 127 | (list 128 | (* 1 2) 129 | (/ 6 3 "foo")) 130 | }); 131 | } 132 | ``` 133 | 134 | Note that this just gives you a syntax tree (in the form of a `Value`). If you 135 | want to actually evaluate the expression, you would need to then pass it to 136 | `eval()`. 137 | 138 | The macro also allows Rust expressions (of type `Value`) to be embedded within 139 | the lisp code using `{ }`: 140 | 141 | ```rust 142 | fn parse_basic_expression() { 143 | let ast = parse(" 144 | (+ 3 1)").next().unwrap().unwrap(); 145 | 146 | let n = 2; 147 | 148 | assert_eq!(ast, lisp! { 149 | (+ { Value::Int(n + 1) } 1) 150 | }); 151 | } 152 | ``` 153 | 154 | NOTE: When parsing lisp code from a string, dashes (`-`) are allowed to be used 155 | in identifiers. _However_, due to the limitations of declarative Rust macros, 156 | these cannot be handled correctly by `lisp! {}`. So it's recommended that you 157 | use underscores in your identifiers instead, which the macro will be able to 158 | handle correctly. The built-in functions follow this convention. 159 | 160 | NOTE 2: The macro cannot handle the syntax for negative numbers! To get around 161 | this you can insert negative numbers as Rust expressions using the escape 162 | syntax, or you can parse your code as a string. 163 | 164 | # `Value::Foreign()` 165 | 166 | Sometimes if you're wanting to script an existing system, you don't want to 167 | convert your data to and from lisp-compatible values. This can be tedious, and 168 | inefficient. 169 | 170 | If you have some type - say a struct - that you want to be able to work with 171 | directly from your lisp code, you can place it in a `Value::Foreign()` which 172 | allows lisp code to pass it around and (native) lisp functions to operate on it: 173 | 174 | ```rust 175 | struct Foo { 176 | some_prop: f32, 177 | } 178 | ``` 179 | ```rust 180 | let v: Value = Value::Foreign(Rc::new(Foo { some_prop: 1.0 })); 181 | ``` 182 | 183 | # Included functionality 184 | 185 | Special forms: `define`, `set`, `defun`, `defmacro`, `lambda`, `quote`, `let`, 186 | `begin`, `cond`, `if`, `and`, `or` 187 | 188 | Functions (in `default_env()`): `print`, `is_null`, `is_number`, `is_symbol`, 189 | `is_boolean`, `is_procedure`, `is_pair`, `car`, `cdr`, `cons`, `list`, `nth`, 190 | `sort`, `reverse`, `map`, `filter`, `length`, `range`, `hash`, `hash_get`, 191 | `hash_set`, `+`, `-`, `*`, `/`, `truncate`, `not`, `==`, `!=`, `<`, `<=`, `>`, 192 | `>=`, `apply`, `eval` 193 | 194 | Other features: 195 | 196 | - Quoting with comma-escapes 197 | - Lisp macros 198 | - Tail-call optimization 199 | -------------------------------------------------------------------------------- /tests/interpreter.rs: -------------------------------------------------------------------------------- 1 | use rust_lisp::{ 2 | default_env, 3 | interpreter::eval, 4 | lisp, 5 | model::{FloatType, IntType, RuntimeError, Symbol, Value}, 6 | parser::parse, 7 | }; 8 | use std::{cell::RefCell, rc::Rc}; 9 | 10 | #[test] 11 | fn eval_basic_expression() { 12 | let result = eval_str("(+ (* 1 2) (/ 6 3))"); 13 | 14 | assert_eq!(result, Value::from(Into::::into(4))); 15 | } 16 | 17 | #[test] 18 | fn eval_add_list() { 19 | let result = eval_str("(+ 1 2 3 4)"); 20 | 21 | assert_eq!(result, Value::from(Into::::into(10))); 22 | } 23 | 24 | #[test] 25 | fn eval_mul_list_ints() { 26 | let result = eval_str("(* 1 2 3 4)"); 27 | 28 | assert_eq!(result, Value::from(Into::::into(24))); 29 | } 30 | 31 | #[test] 32 | fn eval_mul_list_mixed() { 33 | let result = eval_str("(* 1 2.0 3.0 4)"); 34 | 35 | assert_eq!(result, Value::from(Into::::into(24.0))); 36 | } 37 | 38 | #[test] 39 | fn eval_nil() { 40 | let result = eval_str("(== nil '())"); 41 | 42 | assert_eq!(result, Value::from(true)); 43 | } 44 | 45 | #[test] 46 | fn eval_quote_1() { 47 | let result = eval_str("(quote \"stuff\")"); 48 | 49 | assert_eq!(result, Value::String(String::from("stuff"))); 50 | } 51 | 52 | #[test] 53 | fn eval_quote_2() { 54 | let result = eval_str("(quote (1 2 3))"); 55 | 56 | assert_eq!(result, lisp! { (1 2 3) }); 57 | } 58 | 59 | #[test] 60 | fn eval_quote_tick_list() { 61 | let result = eval_str("'(1 2 3)"); 62 | 63 | assert_eq!(result, lisp! { (1 2 3) }); 64 | } 65 | 66 | #[test] 67 | fn eval_quote_tick_atom() { 68 | let result = eval_str("(nth 0 (list '12))"); 69 | 70 | assert_eq!(result, Value::from(Into::::into(12))); 71 | } 72 | 73 | #[test] 74 | fn eval_quote_tick_symbol() { 75 | let result = eval_str("(nth 0 (list 'foo))"); 76 | 77 | assert_eq!(result, Value::Symbol(Symbol(String::from("foo")))); 78 | } 79 | 80 | #[test] 81 | fn eval_let() { 82 | let result = eval_str( 83 | " 84 | (let ((foo 12) 85 | (bar (+ 4 3)) 86 | (blah \"stuff\")) 87 | (print foo) 88 | (print bar) 89 | (print blah) 90 | (list (* foo bar) (+ blah \" also\")))", 91 | ); 92 | 93 | assert_eq!(result, lisp! { (84 "stuff also") }); 94 | } 95 | 96 | #[test] 97 | #[should_panic] 98 | fn eval_let_scope() { 99 | let result = eval_str( 100 | " 101 | (begin 102 | (let ((foo 12) 103 | (bar (+ 4 3)) 104 | (blah \"stuff\")) 105 | (print foo) 106 | (print bar) 107 | (print blah)) 108 | 109 | (* foo bar))", 110 | ); 111 | 112 | assert_eq!(result, lisp! { (84 "stuff also") }); 113 | } 114 | 115 | #[test] 116 | fn eval_set_global() { 117 | let result = eval_str( 118 | " 119 | (begin 120 | (define foo 12) 121 | 122 | (let ((bar 25)) 123 | (set foo 13)) 124 | 125 | foo)", 126 | ); 127 | 128 | assert_eq!(result, Value::from(Into::::into(13))); 129 | } 130 | 131 | #[test] 132 | fn eval_set_local() { 133 | let result = eval_str( 134 | " 135 | (begin 136 | (define foo 12) 137 | 138 | (let ((bar 25)) 139 | (set bar 13)) 140 | 141 | foo)", 142 | ); 143 | 144 | assert_eq!(result, Value::from(Into::::into(12))); 145 | } 146 | 147 | #[test] 148 | #[should_panic] 149 | fn eval_set_undefined() { 150 | eval_str( 151 | " 152 | (begin 153 | (let ((bar 25)) 154 | (set foo 13)))", 155 | ); 156 | } 157 | 158 | #[test] 159 | fn eval_fib() { 160 | let result = eval_str( 161 | " 162 | (begin 163 | (define fib 164 | (lambda (n) 165 | (cond ;; some comment 166 | ((== n 0) 0) 167 | ((== n 1) 1) 168 | (T (+ (fib (- n 1)) (fib (- n 2)) ))))) ;;another comment 169 | 170 | (list (fib 0) (fib 1) (fib 2) (fib 3) (fib 4) (fib 5) (fib 6) (fib 7) (fib 8)))", 171 | ); 172 | 173 | assert_eq!(result, lisp! { (0 1 1 2 3 5 8 13 21) }); 174 | } 175 | 176 | #[test] 177 | fn eval_merge_sort() { 178 | let result = eval_str( 179 | " 180 | (begin 181 | 182 | (defun list-head (lst n) 183 | (if (== n 0) 184 | (list) 185 | (cons (car lst) (list-head (cdr lst) (- n 1))))) 186 | 187 | (defun list-tail (lst n) 188 | (if (== n 0) 189 | lst 190 | (list-tail (cdr lst) (- n 1)))) 191 | 192 | (defun merge (lst-a lst-b) 193 | (cond ((not lst-a) lst-b) 194 | ((not lst-b) lst-a) 195 | ((< (car lst-a) (car lst-b)) (cons (car lst-a) (merge (cdr lst-a) lst-b))) 196 | (T (cons (car lst-b) (merge lst-a (cdr lst-b)))))) 197 | 198 | (defun mergesort (lst) 199 | (if (== (length lst) 1) 200 | lst 201 | (merge (mergesort (list-head lst (truncate (length lst) 2))) 202 | (mergesort (list-tail lst (truncate (length lst) 2)))))) 203 | 204 | (mergesort (list 7 2 5 0 1 5)))", 205 | ); 206 | 207 | assert_eq!(result, lisp! { (0 1 2 5 5 7) }); 208 | } 209 | 210 | #[test] 211 | fn tail_call_test() { 212 | let result = eval_str( 213 | " 214 | (begin 215 | (defun recurse-test (n) 216 | (if (> n 0) 217 | (begin 218 | (print n) 219 | (recurse-test (- n 1))) 220 | n)) 221 | 222 | (recurse-test 1000)) 223 | ", 224 | ); 225 | 226 | assert_eq!(result, Value::from(Into::::into(0))); 227 | } 228 | 229 | #[test] 230 | fn rest_parameters_test() { 231 | let result = eval_str( 232 | " 233 | (begin 234 | (defun foo (a b ...) 235 | ...) 236 | 237 | (foo 1 2 3 4 5))", 238 | ); 239 | 240 | assert_eq!(result, lisp! { (3 4 5) }); 241 | } 242 | 243 | #[test] 244 | fn calling_empty_fun() { 245 | let result = eval_str( 246 | " 247 | (begin 248 | (defun foo () ()) 249 | 250 | (foo))", 251 | ); 252 | 253 | assert_eq!(result, lisp! { () }); 254 | } 255 | 256 | #[test] 257 | fn closure() { 258 | let result = eval_str( 259 | " 260 | (map (let ((x 3)) (lambda (y) (+ x y))) (list 0 1 2 3 4)) 261 | ", 262 | ); 263 | 264 | assert_eq!(result, lisp! {(3 4 5 6 7)}); 265 | } 266 | 267 | #[test] 268 | fn lambda_err() { 269 | let ast = parse( 270 | " 271 | (defun foo (f) f)", 272 | ) 273 | .next() 274 | .unwrap() 275 | .unwrap(); 276 | let env = Rc::new(RefCell::new(default_env())); 277 | let result = eval(env, &ast); 278 | 279 | assert_eq!( 280 | result, 281 | Err(RuntimeError { 282 | msg: String::from("Expected list of arg names, but arg 0 is a F") 283 | }) 284 | ); 285 | } 286 | 287 | #[test] 288 | fn quote_comma() { 289 | let result = eval_str( 290 | " 291 | '(+ 1 2 3 ,(+ 2 2)) 292 | ", 293 | ); 294 | 295 | assert_eq!(result, lisp! { (+ 1 2 3 4) }) 296 | } 297 | 298 | #[test] 299 | fn defmacro() { 300 | let result = eval_str( 301 | " 302 | (begin 303 | (defmacro foo (x) 304 | '(list ,x ,x ,x)) 305 | 306 | (foo 3)) 307 | ", 308 | ); 309 | 310 | assert_eq!(result, lisp! { (3 3 3) }) 311 | } 312 | 313 | #[test] 314 | fn or_expressions() { 315 | let result = eval_str( 316 | " 317 | '( 318 | ,(or) 319 | ,(or T) 320 | ,(or F T) 321 | ,(or F F T) 322 | 323 | ,(or F) 324 | ,(or F F) 325 | ,(or F F F))", 326 | ); 327 | 328 | assert_eq!(result, lisp! { (F T T T F F F) }) 329 | } 330 | 331 | #[test] 332 | fn and_expressions() { 333 | let result = eval_str( 334 | " 335 | '( 336 | ,(and) 337 | ,(and F) 338 | ,(and T F) 339 | ,(and T T F) 340 | 341 | ,(and T) 342 | ,(and T T) 343 | ,(and T T T))", 344 | ); 345 | 346 | assert_eq!(result, lisp! { (T F F F T T T) }) 347 | } 348 | 349 | #[test] 350 | fn short_circuit() { 351 | let result = eval_str( 352 | " 353 | (begin 354 | (define foo 0) 355 | 356 | (or 357 | T 358 | (set foo 12)) 359 | 360 | foo)", 361 | ); 362 | 363 | assert_eq!(result, lisp! { 0 }) 364 | } 365 | 366 | #[test] 367 | fn native_closure() { 368 | let my_state = Rc::new(RefCell::new(0)); 369 | 370 | let expression = lisp! { 371 | (begin 372 | (my_closure) 373 | (my_closure) 374 | (my_closure)) 375 | }; 376 | 377 | let mut env = default_env(); 378 | let my_state_closure = my_state.clone(); 379 | env.define( 380 | Symbol::from("my_closure"), 381 | Value::NativeClosure(Rc::new(RefCell::new( 382 | move |_env, _args| -> Result { 383 | let current = *my_state_closure.borrow(); 384 | my_state_closure.replace(current + 1); 385 | Ok(Value::NIL) 386 | }, 387 | ))), 388 | ); 389 | let env = Rc::new(RefCell::new(env)); 390 | 391 | eval(env, &expression).unwrap(); 392 | 393 | assert_eq!(*my_state.borrow(), 3); 394 | } 395 | 396 | #[cfg(test)] 397 | fn eval_str(source: &str) -> Value { 398 | let ast = parse(source).next().unwrap().unwrap(); 399 | let env = Rc::new(RefCell::new(default_env())); 400 | return eval(env, &ast).unwrap(); 401 | } 402 | 403 | // #[bench] 404 | // #[test] 405 | // fn bench_merge_sort() { 406 | 407 | // // let mut v = vec![]; 408 | // // loop { 409 | // // v.push("Stuff"); 410 | // // } 411 | 412 | // let source = " 413 | // (begin 414 | // (define 415 | // list-head 416 | // (lambda (lst n) 417 | // (if (== n 0) 418 | // (list) 419 | // (cons (car lst) (list-head (cdr lst) (- n 1)))))) 420 | 421 | // (define 422 | // list-tail 423 | // (lambda (lst n) 424 | // (if (== n 0) 425 | // lst 426 | // (list-tail (cdr lst) (- n 1))))) 427 | 428 | // (define 429 | // merge 430 | // (lambda (lst-a lst-b) 431 | // (cond ((not lst-a) lst-b) 432 | // ((not lst-b) lst-a) 433 | // ((< (car lst-a) (car lst-b)) (cons (car lst-a) (merge (cdr lst-a) lst-b))) 434 | // (T (cons (car lst-b) (merge lst-a (cdr lst-b))))))) 435 | 436 | // (define 437 | // mergesort 438 | // (lambda (lst) 439 | // (if (== (length lst) 1) 440 | // lst 441 | // (merge (mergesort (list-head lst (truncate (length lst) 2))) 442 | // (mergesort (list-tail lst (truncate (length lst) 2))))))) 443 | 444 | // (mergesort (list 75 10 45 26 34 36 10 97 34 64 24 20 24 30 39 39 1 53 20 57 6 24 37 41 51 49 12 78 11 71 16 12 60 42 76 63 64 84 11 31 8 48 41 62 98 2 31 53 37 57 14 85 74 75 98 65 69 37 60 96 33 5 95 21 85 7 31 37 2 78 51 88 70 36 1 18 41 68 1 6 16 61 32 80 63 60 92 61 89 91 33 34 61 21 32 14 64 63 8 91)))"; 445 | // let ast = parse(source).unwrap(); 446 | 447 | // let env = Rc::new(RefCell::new(default_env())); 448 | 449 | // let start = SystemTime::now(); 450 | // // for _ in 0..10000 { 451 | // eval(env.clone(), &ast); 452 | // // } 453 | // let end = SystemTime::now(); 454 | 455 | // println!("Took {}ms", end.duration_since(start).unwrap().as_millis()); 456 | // } 457 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | lisp, 3 | model::{FloatType, IntType, List, Symbol, Value}, 4 | }; 5 | 6 | use std::fmt::Display; 7 | 8 | /// Parse a string of Lisp code into a series of s-expressions. There 9 | /// are more than one expressions when the base string has more than one 10 | /// independent parenthesized lists at its root. 11 | pub fn parse(code: &str) -> impl Iterator> + '_ { 12 | let mut index = 0; 13 | index = consume_whitespace_and_comments(code, index); 14 | 15 | std::iter::from_fn(move || { 16 | if let Some(res) = parse_expression(code, index) { 17 | if let Ok(res) = res { 18 | index = res.index; 19 | index = consume_whitespace_and_comments(code, index); 20 | 21 | Some(Ok(res.parsed.into_value())) 22 | } else { 23 | Some(Err(res.unwrap_err())) 24 | } 25 | } else { 26 | None // TODO: Err if we don't parse the whole input? 27 | } 28 | }) 29 | } 30 | 31 | /// A slightly more convenient data structure for building the parse tree, before 32 | /// eventually converting it into proper s-expressions. 33 | #[derive(Debug, Clone)] 34 | enum ParseTree { 35 | Atom(Value), 36 | List(Vec), 37 | Quoted(Box), 38 | Comma(Box), 39 | } 40 | 41 | impl ParseTree { 42 | pub fn into_value(self) -> Value { 43 | match self { 44 | ParseTree::Atom(value) => value, 45 | ParseTree::List(vec) => Value::List( 46 | vec.into_iter() 47 | .map(|parse_tree| parse_tree.into_value()) 48 | .collect::(), 49 | ), 50 | ParseTree::Quoted(inner) => lisp! { (quote {inner.into_value()}) }, 51 | ParseTree::Comma(inner) => lisp! { (comma {inner.into_value()}) }, 52 | } 53 | } 54 | } 55 | 56 | /** 57 | * An error that occurred while parsing a string as lisp code 58 | */ 59 | #[derive(Debug, Clone, PartialEq, Eq)] 60 | pub struct ParseError { 61 | pub msg: String, 62 | // pub line: i32, 63 | } 64 | 65 | impl Display for ParseError { 66 | fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 67 | return write!(formatter, "Parse error: {}", self.msg); 68 | } 69 | } 70 | 71 | impl std::error::Error for ParseError {} 72 | 73 | #[derive(Clone, Debug)] 74 | struct ParsedAndIndex { 75 | pub parsed: ParseTree, 76 | pub index: usize, 77 | } 78 | 79 | type ParseResult = Option>; 80 | type ConsumeResult = Option; 81 | 82 | fn parse_expression(code: &str, index: usize) -> ParseResult { 83 | for func in [parse_list, parse_atom] { 84 | let res = func(code, index); 85 | 86 | if res.is_some() { 87 | return res; 88 | } 89 | } 90 | 91 | None 92 | } 93 | 94 | fn parse_list(code: &str, index: usize) -> ParseResult { 95 | let mut index = consume(code, index, "(")?; 96 | let mut members = vec![]; 97 | 98 | index = consume_whitespace_and_comments(code, index); 99 | 100 | while let Some(res) = parse_expression(code, index) { 101 | if let Ok(res) = res { 102 | index = res.index; 103 | members.push(res.parsed); 104 | index = consume_whitespace_and_comments(code, index); 105 | } else { 106 | return Some(res); 107 | } 108 | } 109 | 110 | if let Some(index) = consume(code, index, ")") { 111 | Some(Ok(ParsedAndIndex { 112 | parsed: ParseTree::List(members), 113 | index, 114 | })) 115 | } else { 116 | Some(Err(ParseError { 117 | msg: format!("Unclosed list at index {}", index), 118 | })) 119 | } 120 | } 121 | 122 | fn parse_atom(code: &str, index: usize) -> ParseResult { 123 | for func in [ 124 | parse_quoted, 125 | parse_comma, 126 | parse_nil, 127 | parse_false, 128 | parse_true, 129 | parse_number, 130 | parse_string, 131 | parse_symbol, 132 | ] { 133 | let res = func(code, index); 134 | 135 | if res.is_some() { 136 | return res; 137 | } 138 | } 139 | 140 | None 141 | } 142 | 143 | fn parse_quoted(code: &str, index: usize) -> ParseResult { 144 | let index = consume(code, index, "'")?; 145 | let res = parse_expression(code, index)?; 146 | 147 | if let Ok(ParsedAndIndex { parsed, index }) = res { 148 | Some(Ok(ParsedAndIndex { 149 | parsed: ParseTree::Quoted(Box::new(parsed)), 150 | index, 151 | })) 152 | } else { 153 | Some(res) 154 | } 155 | } 156 | 157 | fn parse_comma(code: &str, index: usize) -> ParseResult { 158 | let index = consume(code, index, ",")?; 159 | let res = parse_expression(code, index)?; 160 | 161 | if let Ok(ParsedAndIndex { parsed, index }) = res { 162 | Some(Ok(ParsedAndIndex { 163 | parsed: ParseTree::Comma(Box::new(parsed)), 164 | index, 165 | })) 166 | } else { 167 | Some(res) 168 | } 169 | } 170 | 171 | fn parse_nil(code: &str, index: usize) -> ParseResult { 172 | let index = consume(code, index, "nil")?; 173 | 174 | if next_char_is_break(code, index) { 175 | Some(Ok(ParsedAndIndex { 176 | parsed: ParseTree::Atom(Value::NIL), 177 | index, 178 | })) 179 | } else { 180 | None 181 | } 182 | } 183 | 184 | fn parse_false(code: &str, index: usize) -> ParseResult { 185 | let index = consume(code, index, "f")?; 186 | 187 | if next_char_is_break(code, index) { 188 | Some(Ok(ParsedAndIndex { 189 | parsed: ParseTree::Atom(Value::False), 190 | index, 191 | })) 192 | } else { 193 | None 194 | } 195 | } 196 | 197 | fn parse_true(code: &str, index: usize) -> ParseResult { 198 | let index = consume(code, index, "t")?; 199 | 200 | if next_char_is_break(code, index) { 201 | Some(Ok(ParsedAndIndex { 202 | parsed: ParseTree::Atom(Value::True), 203 | index, 204 | })) 205 | } else { 206 | None 207 | } 208 | } 209 | 210 | fn parse_number(code: &str, index: usize) -> ParseResult { 211 | let (front_last_index, front_last_char) = consume_while(code, index, |(index, ch)| { 212 | (index == 0 && ch == '-') || ch.is_numeric() 213 | })?; 214 | 215 | if front_last_char.is_numeric() { 216 | let front_last_index = front_last_index + 1; 217 | 218 | let back_end = consume_while(code, front_last_index, |(index, ch)| { 219 | (index == 0 && ch == '.') || ch.is_numeric() 220 | }); 221 | 222 | if let Some((back_last_index, _)) = back_end { 223 | let back_last_index = back_last_index + 1; 224 | 225 | if back_last_index >= front_last_index + 2 { 226 | if next_char_is_break(code, back_last_index) { 227 | if let Ok(float) = code 228 | .get(index..back_last_index) 229 | .unwrap_or("") 230 | .parse::() 231 | { 232 | return Some(Ok(ParsedAndIndex { 233 | parsed: ParseTree::Atom(Value::Float(float)), 234 | index: back_last_index, 235 | })); 236 | } 237 | } 238 | } else if code.as_bytes().get(back_last_index - 1) == Some(&b'.') { 239 | return Some(Err(ParseError { 240 | msg: format!( 241 | "Expected decimal value after '.' at index {}", 242 | back_last_index - 1 243 | ), 244 | })); 245 | } 246 | } 247 | 248 | if next_char_is_break(code, front_last_index) { 249 | if let Ok(int) = code 250 | .get(index..front_last_index) 251 | .unwrap_or("") 252 | .parse::() 253 | { 254 | return Some(Ok(ParsedAndIndex { 255 | parsed: ParseTree::Atom(Value::Int(int)), 256 | index: front_last_index, 257 | })); 258 | } 259 | } 260 | } 261 | 262 | None 263 | } 264 | 265 | fn parse_string(code: &str, index: usize) -> ParseResult { 266 | let (last_index, _) = consume_while(code, index, |(index, ch)| { 267 | (index == 0 && ch == '"') || (index > 0 && ch != '"') 268 | })?; 269 | 270 | if last_index > index { 271 | if code.as_bytes().get(last_index + 1) == Some(&b'"') { 272 | Some(Ok(ParsedAndIndex { 273 | parsed: ParseTree::Atom(Value::String( 274 | code.get(index + 1..last_index + 1).unwrap_or("").to_owned(), 275 | )), 276 | index: last_index + 2, 277 | })) 278 | } else { 279 | Some(Err(ParseError { 280 | msg: format!("Unclosed string at index {}", last_index), 281 | })) 282 | } 283 | } else { 284 | None 285 | } 286 | } 287 | 288 | fn parse_symbol(code: &str, index: usize) -> ParseResult { 289 | let (last_index, _) = consume_while(code, index, |(index, ch)| { 290 | (index == 0 && is_symbol_start(ch)) || (index > 0 && is_symbolic(ch)) 291 | })?; 292 | let last_index = last_index + 1; 293 | 294 | if last_index > index { 295 | Some(Ok(ParsedAndIndex { 296 | parsed: ParseTree::Atom(Value::Symbol(Symbol( 297 | code.get(index..last_index).unwrap_or("").to_owned(), 298 | ))), 299 | index: last_index, 300 | })) 301 | } else { 302 | None 303 | } 304 | } 305 | 306 | fn consume(code: &str, index: usize, s: &str) -> ConsumeResult { 307 | let slice = code.get(index..).unwrap_or(""); 308 | 309 | if slice.len() >= s.len() 310 | && slice 311 | .chars() 312 | .zip(s.chars()) 313 | .all(|(a, b)| a.to_ascii_lowercase() == b.to_ascii_lowercase()) 314 | { 315 | return Some(index + s.len()); 316 | } else { 317 | return None; 318 | } 319 | } 320 | 321 | fn consume_whitespace_and_comments(code: &str, index: usize) -> usize { 322 | let mut semicolons = 0; 323 | 324 | consume_while(code, index, move |(_, ch)| { 325 | if ch == ';' { 326 | semicolons += 1; 327 | } else if semicolons < 2 || ch == '\n' { 328 | semicolons = 0; 329 | } 330 | 331 | return ch.is_whitespace() || ch == ';' || semicolons >= 2; 332 | }) 333 | .map(|(index, _)| index + 1) 334 | .unwrap_or(index) 335 | } 336 | 337 | fn consume_while bool>( 338 | code: &str, 339 | index: usize, 340 | mut pred: F, 341 | ) -> Option<(usize, char)> { 342 | code.get(index..) 343 | .unwrap_or("") 344 | .char_indices() 345 | .take_while(|(i, c)| pred((*i, *c))) 346 | .last() 347 | .map(|(last_index, ch)| (last_index + index, ch)) 348 | } 349 | 350 | fn is_symbol_start(c: char) -> bool { 351 | !c.is_numeric() && is_symbolic(c) 352 | } 353 | 354 | fn is_symbolic(c: char) -> bool { 355 | !c.is_whitespace() && !SPECIAL_TOKENS.iter().any(|t| *t == c) 356 | } 357 | 358 | fn next_char_is_break(code: &str, index: usize) -> bool { 359 | code.get(index..) 360 | .map(|s| s.chars().next()) 361 | .flatten() 362 | .map(|ch| ch.is_whitespace() || SPECIAL_TOKENS.iter().any(|t| *t == ch)) 363 | .unwrap_or(true) 364 | } 365 | 366 | const SPECIAL_TOKENS: [char; 5] = ['(', ')', '\'', ',', ';']; 367 | -------------------------------------------------------------------------------- /src/default_environment.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | interpreter::eval, 3 | lisp, 4 | model::{Env, HashMapRc, IntType, List, RuntimeError, Symbol, Value}, 5 | utils::{require_arg, require_typed_arg}, 6 | }; 7 | use cfg_if::cfg_if; 8 | use std::{cell::RefCell, collections::HashMap, convert::TryInto, rc::Rc}; 9 | cfg_if! { 10 | if #[cfg(feature = "bigint")] { 11 | use num_traits::ToPrimitive; 12 | } 13 | } 14 | 15 | /// Initialize an instance of `Env` with several core Lisp functions implemented 16 | /// in Rust. **Without this, you will only have access to the functions you 17 | /// implement yourself.** 18 | pub fn default_env() -> Env { 19 | let mut env = Env::new(); 20 | 21 | env.define( 22 | Symbol::from("print"), 23 | Value::NativeFunc(|_env, args| { 24 | let expr = require_arg("print", &args, 0)?; 25 | 26 | println!("{}", &expr); 27 | Ok(expr.clone()) 28 | }), 29 | ); 30 | 31 | env.define( 32 | Symbol::from("is_null"), 33 | Value::NativeFunc(|_env, args| { 34 | let val = require_arg("is_null", &args, 0)?; 35 | 36 | Ok(Value::from(*val == Value::NIL)) 37 | }), 38 | ); 39 | 40 | env.define( 41 | Symbol::from("is_number"), 42 | Value::NativeFunc(|_env, args| { 43 | let val = require_arg("is_number", &args, 0)?; 44 | 45 | Ok(match val { 46 | Value::Int(_) => Value::True, 47 | Value::Float(_) => Value::True, 48 | _ => Value::NIL, 49 | }) 50 | }), 51 | ); 52 | 53 | env.define( 54 | Symbol::from("is_symbol"), 55 | Value::NativeFunc(|_env, args| { 56 | let val = require_arg("is_symbol", &args, 0)?; 57 | 58 | Ok(match val { 59 | Value::Symbol(_) => Value::True, 60 | _ => Value::NIL, 61 | }) 62 | }), 63 | ); 64 | 65 | env.define( 66 | Symbol::from("is_boolean"), 67 | Value::NativeFunc(|_env, args| { 68 | let val = require_arg("is_boolean", &args, 0)?; 69 | 70 | Ok(match val { 71 | Value::True => Value::True, 72 | Value::False => Value::True, 73 | _ => Value::NIL, 74 | }) 75 | }), 76 | ); 77 | 78 | env.define( 79 | Symbol::from("is_procedure"), 80 | Value::NativeFunc(|_env, args| { 81 | let val = require_arg("is_procedure", &args, 0)?; 82 | 83 | Ok(match val { 84 | Value::Lambda(_) => Value::True, 85 | Value::NativeFunc(_) => Value::True, 86 | _ => Value::NIL, 87 | }) 88 | }), 89 | ); 90 | 91 | env.define( 92 | Symbol::from("is_pair"), 93 | Value::NativeFunc(|_env, args| { 94 | let val = require_arg("is_pair", &args, 0)?; 95 | 96 | Ok(match val { 97 | Value::List(_) => Value::True, 98 | _ => Value::NIL, 99 | }) 100 | }), 101 | ); 102 | 103 | env.define( 104 | Symbol::from("car"), 105 | Value::NativeFunc(|_env, args| { 106 | let list = require_typed_arg::<&List>("car", &args, 0)?; 107 | 108 | list.car() 109 | }), 110 | ); 111 | 112 | env.define( 113 | Symbol::from("cdr"), 114 | Value::NativeFunc(|_env, args| { 115 | let list = require_typed_arg::<&List>("cdr", &args, 0)?; 116 | 117 | Ok(Value::List(list.cdr())) 118 | }), 119 | ); 120 | 121 | env.define( 122 | Symbol::from("cons"), 123 | Value::NativeFunc(|_env, args| { 124 | let car = require_arg("cons", &args, 0)?; 125 | let cdr = require_typed_arg::<&List>("cons", &args, 1)?; 126 | 127 | Ok(Value::List(cdr.cons(car.clone()))) 128 | }), 129 | ); 130 | 131 | env.define( 132 | Symbol::from("list"), 133 | Value::NativeFunc(|_env, args| Ok(Value::List(args.iter().collect::()))), 134 | ); 135 | 136 | env.define( 137 | Symbol::from("nth"), 138 | Value::NativeFunc(|_env, args| { 139 | let index = require_typed_arg::("nth", &args, 0)?; 140 | let list = require_typed_arg::<&List>("nth", &args, 1)?; 141 | 142 | let index = TryInto::::try_into(index).map_err(|_| RuntimeError { 143 | msg: "Failed converting to `usize`".to_owned(), 144 | })?; 145 | 146 | Ok(list.into_iter().nth(index).unwrap_or(Value::NIL)) 147 | }), 148 | ); 149 | 150 | env.define( 151 | Symbol::from("sort"), 152 | Value::NativeFunc(|_env, args| { 153 | let list = require_typed_arg::<&List>("sort", &args, 0)?; 154 | 155 | let mut v: Vec = list.into_iter().collect(); 156 | 157 | v.sort(); 158 | 159 | Ok(Value::List(v.into_iter().collect())) 160 | }), 161 | ); 162 | 163 | env.define( 164 | Symbol::from("reverse"), 165 | Value::NativeFunc(|_env, args| { 166 | let list = require_typed_arg::<&List>("reverse", &args, 0)?; 167 | 168 | let mut v: Vec = list.into_iter().collect(); 169 | 170 | v.reverse(); 171 | 172 | Ok(Value::List(v.into_iter().collect())) 173 | }), 174 | ); 175 | 176 | env.define( 177 | Symbol::from("map"), 178 | Value::NativeFunc(|env, args| { 179 | let func = require_arg("map", &args, 0)?; 180 | let list = require_typed_arg::<&List>("map", &args, 1)?; 181 | 182 | list.into_iter() 183 | .map(|val| { 184 | let expr = lisp! { ({func.clone()} (quote {val})) }; 185 | 186 | eval(env.clone(), &expr) 187 | }) 188 | .collect::>() 189 | .map(Value::List) 190 | }), 191 | ); 192 | 193 | // 🦀 Oh the poor `filter`, you must feel really sad being unused. 194 | env.define( 195 | Symbol::from("filter"), 196 | Value::NativeFunc(|env, args| { 197 | let func = require_arg("filter", &args, 0)?; 198 | let list = require_typed_arg::<&List>("filter", &args, 1)?; 199 | 200 | list.into_iter() 201 | .filter_map(|val: Value| -> Option> { 202 | let expr = lisp! { ({func.clone()} (quote {val.clone()})) }; 203 | 204 | match eval(env.clone(), &expr) { 205 | Ok(matches) => { 206 | if matches.into() { 207 | Some(Ok(val)) 208 | } else { 209 | None 210 | } 211 | } 212 | Err(e) => Some(Err(e)), 213 | } 214 | }) 215 | .collect::>() 216 | .map(Value::List) 217 | }), 218 | ); 219 | 220 | env.define( 221 | Symbol::from("length"), 222 | Value::NativeFunc(|_env, args| { 223 | let list = require_typed_arg::<&List>("length", &args, 0)?; 224 | 225 | cfg_if! { 226 | if #[cfg(feature = "bigint")] { 227 | Ok(Value::Int(list.into_iter().len().into())) 228 | } else { 229 | Ok(Value::Int(list.into_iter().len() as IntType)) 230 | } 231 | } 232 | }), 233 | ); 234 | 235 | env.define( 236 | Symbol::from("range"), 237 | Value::NativeFunc(|_env, args| { 238 | let start = require_typed_arg::("range", &args, 0)?; 239 | let end = require_typed_arg::("range", &args, 1)?; 240 | 241 | let mut current = start; 242 | 243 | Ok(Value::List( 244 | std::iter::from_fn(move || { 245 | if current == end { 246 | None 247 | } else { 248 | let res = Some(current.clone()); 249 | 250 | current += 1; 251 | 252 | res 253 | } 254 | }) 255 | .map(Value::from) 256 | .collect(), 257 | )) 258 | }), 259 | ); 260 | 261 | env.define( 262 | Symbol::from("hash"), 263 | Value::NativeFunc(|_env, args| { 264 | let chunks = args.chunks(2); 265 | 266 | let mut hash = HashMap::new(); 267 | 268 | for pair in chunks { 269 | let key = pair.get(0).unwrap(); 270 | let value = pair.get(1); 271 | 272 | if let Some(value) = value { 273 | hash.insert(key.clone(), value.clone()); 274 | } else { 275 | return Err(RuntimeError { 276 | msg: format!("Must pass an even number of arguments to 'hash', because they're used as key/value pairs; found extra argument {}", key) 277 | }); 278 | } 279 | } 280 | 281 | Ok(Value::HashMap(Rc::new(RefCell::new(hash)))) 282 | }), 283 | ); 284 | 285 | env.define( 286 | Symbol::from("hash_get"), 287 | Value::NativeFunc(|_env, args| { 288 | let hash = require_typed_arg::<&HashMapRc>("hash_get", &args, 0)?; 289 | let key = require_arg("hash_get", &args, 1)?; 290 | 291 | Ok(hash 292 | .borrow() 293 | .get(key) 294 | .map(|v| v.clone()) 295 | .unwrap_or(Value::NIL)) 296 | }), 297 | ); 298 | 299 | env.define( 300 | Symbol::from("hash_set"), 301 | Value::NativeFunc(|_env, args| { 302 | let hash = require_typed_arg::<&HashMapRc>("hash_set", &args, 0)?; 303 | let key = require_arg("hash_set", &args, 1)?; 304 | let value = require_arg("hash_set", &args, 2)?; 305 | 306 | hash.borrow_mut().insert(key.clone(), value.clone()); 307 | 308 | Ok(Value::HashMap(hash.clone())) 309 | }), 310 | ); 311 | 312 | env.define( 313 | Symbol::from("+"), 314 | Value::NativeFunc(|_env, args| { 315 | let first_arg = require_arg("+", &args, 1)?; 316 | 317 | let mut total = match first_arg { 318 | Value::Int(_) => Ok(Value::Int(0.into())), 319 | Value::Float(_) => Ok(Value::Float(0.0)), 320 | Value::String(_) => Ok(Value::String("".into())), 321 | _ => Err(RuntimeError { 322 | msg: format!( 323 | "Function \"+\" requires arguments to be numbers or strings; found {}", 324 | first_arg 325 | ), 326 | }), 327 | }?; 328 | 329 | for arg in args { 330 | total = (&total + &arg).map_err(|_| RuntimeError { 331 | msg: format!( 332 | "Function \"+\" requires arguments to be numbers or strings; found {}", 333 | arg 334 | ), 335 | })?; 336 | } 337 | 338 | Ok(total) 339 | }), 340 | ); 341 | 342 | env.define( 343 | Symbol::from("-"), 344 | Value::NativeFunc(|_env, args| { 345 | let a = require_arg("-", &args, 0)?; 346 | let b = require_arg("-", &args, 1)?; 347 | 348 | (a - b).map_err(|_| RuntimeError { 349 | msg: String::from("Function \"-\" requires arguments to be numbers"), 350 | }) 351 | }), 352 | ); 353 | 354 | env.define( 355 | Symbol::from("*"), 356 | Value::NativeFunc(|_env, args| { 357 | let mut product = Value::Int(1.into()); 358 | 359 | for arg in args { 360 | product = (&product * &arg).map_err(|_| RuntimeError { 361 | msg: format!( 362 | "Function \"*\" requires arguments to be numbers; found {}", 363 | arg 364 | ), 365 | })?; 366 | } 367 | 368 | Ok(product) 369 | }), 370 | ); 371 | 372 | env.define( 373 | Symbol::from("/"), 374 | Value::NativeFunc(|_env, args| { 375 | let a = require_arg("/", &args, 0)?; 376 | let b = require_arg("/", &args, 1)?; 377 | 378 | (a / b).map_err(|_| RuntimeError { 379 | msg: String::from("Function \"/\" requires arguments to be numbers"), 380 | }) 381 | }), 382 | ); 383 | 384 | env.define( 385 | Symbol::from("truncate"), 386 | Value::NativeFunc(|_env, args| { 387 | let a = require_arg("truncate", &args, 0)?; 388 | let b = require_arg("truncate", &args, 1)?; 389 | 390 | if let (Ok(a), Ok(b)) = ( 391 | TryInto::::try_into(a), 392 | TryInto::::try_into(b), 393 | ) { 394 | return Ok(Value::Int(a / b)); 395 | } 396 | 397 | Err(RuntimeError { 398 | msg: String::from("Function \"truncate\" requires arguments to be integers"), 399 | }) 400 | }), 401 | ); 402 | 403 | env.define( 404 | Symbol::from("not"), 405 | Value::NativeFunc(|_env, args| { 406 | let a = require_arg("not", &args, 0)?; 407 | let a: bool = a.into(); 408 | 409 | Ok(Value::from(!a)) 410 | }), 411 | ); 412 | 413 | env.define( 414 | Symbol::from("=="), 415 | Value::NativeFunc(|_env, args| { 416 | let a = require_arg("==", &args, 0)?; 417 | let b = require_arg("==", &args, 1)?; 418 | 419 | Ok(Value::from(a == b)) 420 | }), 421 | ); 422 | 423 | env.define( 424 | Symbol::from("!="), 425 | Value::NativeFunc(|_env, args| { 426 | let a = require_arg("!=", &args, 0)?; 427 | let b = require_arg("!=", &args, 1)?; 428 | 429 | Ok(Value::from(a != b)) 430 | }), 431 | ); 432 | 433 | env.define( 434 | Symbol::from("<"), 435 | Value::NativeFunc(|_env, args| { 436 | let a = require_arg("<", &args, 0)?; 437 | let b = require_arg("<", &args, 1)?; 438 | 439 | Ok(Value::from(a < b)) 440 | }), 441 | ); 442 | 443 | env.define( 444 | Symbol::from("<="), 445 | Value::NativeFunc(|_env, args| { 446 | let a = require_arg("<=", &args, 0)?; 447 | let b = require_arg("<=", &args, 1)?; 448 | 449 | Ok(Value::from(a <= b)) 450 | }), 451 | ); 452 | 453 | env.define( 454 | Symbol::from(">"), 455 | Value::NativeFunc(|_env, args| { 456 | let a = require_arg(">", &args, 0)?; 457 | let b = require_arg(">", &args, 1)?; 458 | 459 | Ok(Value::from(a > b)) 460 | }), 461 | ); 462 | 463 | env.define( 464 | Symbol::from(">="), 465 | Value::NativeFunc(|_env, args| { 466 | let a = require_arg(">=", &args, 0)?; 467 | let b = require_arg(">=", &args, 1)?; 468 | 469 | Ok(Value::from(a >= b)) 470 | }), 471 | ); 472 | 473 | env.define( 474 | Symbol::from("eval"), 475 | Value::NativeFunc(|env, args| { 476 | let expr = require_arg("eval", &args, 0)?; 477 | 478 | eval(env, expr) 479 | }), 480 | ); 481 | 482 | env.define( 483 | Symbol::from("apply"), 484 | Value::NativeFunc(|env, args| { 485 | let func = require_arg("apply", &args, 0)?; 486 | let params = require_typed_arg::<&List>("apply", &args, 1)?; 487 | 488 | eval(env, &Value::List(params.cons(func.clone()))) 489 | }), 490 | ); 491 | 492 | env 493 | } 494 | -------------------------------------------------------------------------------- /src/interpreter.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | model::{Env, Lambda, List, RuntimeError, Symbol, Value}, 3 | utils::{require_arg, require_typed_arg}, 4 | }; 5 | use std::{cell::RefCell, rc::Rc}; 6 | 7 | /// Evaluate a single Lisp expression in the context of a given environment. 8 | pub fn eval(env: Rc>, expression: &Value) -> Result { 9 | eval_inner(env, expression, Context::new()) 10 | } 11 | 12 | /// Evaluate a series of s-expressions. Each expression is evaluated in 13 | /// order and the final one's return value is returned. 14 | pub fn eval_block( 15 | env: Rc>, 16 | clauses: impl Iterator, 17 | ) -> Result { 18 | eval_block_inner(env, clauses, Context::new()) 19 | } 20 | 21 | fn eval_block_inner( 22 | env: Rc>, 23 | clauses: impl Iterator, 24 | context: Context, 25 | ) -> Result { 26 | let mut current_expr: Option = None; 27 | 28 | for clause in clauses { 29 | if let Some(expr) = current_expr { 30 | match eval_inner(env.clone(), &expr, context.found_tail(true)) { 31 | Ok(_) => (), 32 | Err(e) => { 33 | return Err(e); 34 | } 35 | } 36 | } 37 | 38 | current_expr = Some(clause); 39 | } 40 | 41 | if let Some(expr) = ¤t_expr { 42 | eval_inner(env, expr, context) 43 | } else { 44 | Err(RuntimeError { 45 | msg: "Unrecognized expression".to_owned(), 46 | }) 47 | } 48 | } 49 | 50 | /// `found_tail` and `in_func` are used when locating the tail position for 51 | /// tail-call optimization. Candidates are not eligible if a) we aren't already 52 | /// inside a function call, or b) we've already found the tail inside the current 53 | /// function call. `found_tail` is currently overloaded inside special forms to 54 | /// factor out function calls in, say, the conditional slot, which are not 55 | /// eligible to be the tail-call based on their position. A future refactor hopes 56 | /// to make things a little more semantic. 57 | fn eval_inner( 58 | env: Rc>, 59 | expression: &Value, 60 | context: Context, 61 | ) -> Result { 62 | if context.quoting { 63 | match expression { 64 | Value::List(list) if *list != List::NIL => match &list.car()? { 65 | Value::Symbol(Symbol(keyword)) if keyword == "comma" => { 66 | // do nothing, handle it down below 67 | } 68 | _ => { 69 | return list 70 | .into_iter() 71 | .map(|el| eval_inner(env.clone(), &el, context)) 72 | .collect::>() 73 | .map(Value::List); 74 | } 75 | }, 76 | _ => return Ok(expression.clone()), 77 | } 78 | } 79 | 80 | match expression { 81 | // look up symbol 82 | Value::Symbol(symbol) => env.borrow().get(symbol).ok_or_else(|| RuntimeError { 83 | msg: format!("\"{}\" is not defined", symbol), 84 | }), 85 | 86 | // s-expression 87 | Value::List(list) if *list != List::NIL => { 88 | match &list.car()? { 89 | // special forms 90 | Value::Symbol(Symbol(keyword)) if keyword == "comma" => { 91 | eval_inner(env, &list.cdr().car()?, context.quoting(false)) 92 | } 93 | 94 | Value::Symbol(Symbol(keyword)) if keyword == "quote" => { 95 | eval_inner(env, &list.cdr().car()?, context.quoting(true)) 96 | } 97 | 98 | Value::Symbol(Symbol(keyword)) if keyword == "define" || keyword == "set" => { 99 | let args = &list.cdr().into_iter().collect::>(); 100 | 101 | let symbol = require_typed_arg::<&Symbol>(keyword, args, 0)?; 102 | let value_expr = require_arg(keyword, args, 1)?; 103 | 104 | let value = eval_inner(env.clone(), value_expr, context.found_tail(true))?; 105 | 106 | if keyword == "define" { 107 | env.borrow_mut().define(symbol.clone(), value.clone()); 108 | } else { 109 | env.borrow_mut().set(symbol.clone(), value.clone())?; 110 | } 111 | 112 | Ok(value) 113 | } 114 | 115 | Value::Symbol(Symbol(keyword)) if keyword == "defmacro" => { 116 | let args = &list.cdr().into_iter().collect::>(); 117 | 118 | let symbol = require_typed_arg::<&Symbol>(keyword, args, 0)?; 119 | let argnames_list = require_typed_arg::<&List>(keyword, args, 1)?; 120 | let argnames = value_to_argnames(argnames_list.clone())?; 121 | let body = Rc::new(Value::List(list.cdr().cdr().cdr())); 122 | 123 | let lambda = Value::Macro(Lambda { 124 | closure: env.clone(), 125 | argnames, 126 | body, 127 | }); 128 | 129 | env.borrow_mut().define(symbol.clone(), lambda); 130 | 131 | Ok(Value::NIL) 132 | } 133 | 134 | Value::Symbol(Symbol(keyword)) if keyword == "defun" => { 135 | let args = &list.cdr().into_iter().collect::>(); 136 | 137 | let symbol = require_typed_arg::<&Symbol>(keyword, args, 0)?; 138 | let argnames_list = require_typed_arg::<&List>(keyword, args, 1)?; 139 | let argnames = value_to_argnames(argnames_list.clone())?; 140 | let body = Rc::new(Value::List(list.cdr().cdr().cdr())); 141 | 142 | let lambda = Value::Lambda(Lambda { 143 | closure: env.clone(), 144 | argnames, 145 | body, 146 | }); 147 | 148 | env.borrow_mut().define(symbol.clone(), lambda); 149 | 150 | Ok(Value::NIL) 151 | } 152 | 153 | Value::Symbol(Symbol(keyword)) if keyword == "lambda" => { 154 | let args = &list.cdr().into_iter().collect::>(); 155 | 156 | let argnames_list = require_typed_arg::<&List>(keyword, args, 0)?; 157 | let argnames = value_to_argnames(argnames_list.clone())?; 158 | let body = Rc::new(Value::List(list.cdr().cdr())); 159 | 160 | Ok(Value::Lambda(Lambda { 161 | closure: env, 162 | argnames, 163 | body, 164 | })) 165 | } 166 | 167 | Value::Symbol(Symbol(keyword)) if keyword == "let" => { 168 | let let_env = Rc::new(RefCell::new(Env::extend(env))); 169 | 170 | let args = &list.cdr().into_iter().collect::>(); 171 | 172 | let declarations = require_typed_arg::<&List>(keyword, args, 0)?; 173 | 174 | for decl in declarations.into_iter() { 175 | let decl = &decl; 176 | 177 | let decl_cons: &List = decl.try_into().map_err(|_| RuntimeError { 178 | msg: format!("Expected declaration clause, found {}", decl), 179 | })?; 180 | let symbol = &decl_cons.car()?; 181 | let symbol: &Symbol = symbol.try_into().map_err(|_| RuntimeError { 182 | msg: format!("Expected symbol for let declaration, found {}", symbol), 183 | })?; 184 | let expr = &decl_cons.cdr().car()?; 185 | 186 | let result = eval_inner(let_env.clone(), expr, context.found_tail(true))?; 187 | let_env.borrow_mut().define(symbol.clone(), result); 188 | } 189 | 190 | let body = &Value::List(list.cdr().cdr()); 191 | let body: &List = body.try_into().map_err(|_| RuntimeError { 192 | msg: format!( 193 | "Expected expression(s) after let-declarations, found {}", 194 | body 195 | ), 196 | })?; 197 | 198 | eval_block_inner(let_env, body.into_iter(), context) 199 | } 200 | 201 | Value::Symbol(Symbol(keyword)) if keyword == "begin" => { 202 | eval_block_inner(env, list.cdr().into_iter(), context) 203 | } 204 | 205 | Value::Symbol(Symbol(keyword)) if keyword == "cond" => { 206 | let clauses = list.cdr(); 207 | 208 | for clause in clauses.into_iter() { 209 | let clause = &clause; 210 | 211 | let clause: &List = clause.try_into().map_err(|_| RuntimeError { 212 | msg: format!("Expected conditional clause, found {}", clause), 213 | })?; 214 | 215 | let condition = &clause.car()?; 216 | let then = &clause.cdr().car()?; 217 | 218 | if eval_inner(env.clone(), condition, context.found_tail(true))?.into() { 219 | return eval_inner(env, then, context); 220 | } 221 | } 222 | 223 | Ok(Value::NIL) 224 | } 225 | 226 | Value::Symbol(Symbol(keyword)) if keyword == "if" => { 227 | let args = &list.cdr().into_iter().collect::>(); 228 | 229 | let condition = require_arg(keyword, args, 0)?; 230 | let then_expr = require_arg(keyword, args, 1)?; 231 | let else_expr = require_arg(keyword, args, 2).ok(); 232 | 233 | if eval_inner(env.clone(), condition, context.found_tail(true))?.into() { 234 | eval_inner(env, then_expr, context) 235 | } else { 236 | else_expr 237 | .map(|expr| eval_inner(env, &expr, context)) 238 | .unwrap_or(Ok(Value::NIL)) 239 | } 240 | } 241 | 242 | Value::Symbol(Symbol(keyword)) if keyword == "and" || keyword == "or" => { 243 | let args = &list.cdr().into_iter().collect::>(); 244 | let is_or = keyword.as_str() == "or"; 245 | 246 | let mut last_result: Option = None; 247 | for arg in args { 248 | let result = eval_inner(env.clone(), arg, context.found_tail(true))?; 249 | let truthy: bool = (&result).into(); 250 | 251 | if is_or == truthy { 252 | return Ok(result); 253 | } 254 | 255 | last_result = Some(result); 256 | } 257 | 258 | Ok(if let Some(last_result) = last_result { 259 | last_result 260 | } else { 261 | // there were zero arguments 262 | (!is_or).into() 263 | }) 264 | } 265 | 266 | // function call or macro expand 267 | _ => { 268 | let mut func_or_macro = 269 | eval_inner(env.clone(), &list.car()?, context.found_tail(true))?; 270 | 271 | if matches!(func_or_macro, Value::Macro(_)) { 272 | let args = list.into_iter().skip(1).collect::>(); 273 | 274 | let expanded = 275 | call_function_or_macro(env.clone(), &mut func_or_macro, args)?; 276 | 277 | eval_inner(env.clone(), &expanded, Context::new()) 278 | } else { 279 | let args = list 280 | .into_iter() 281 | .skip(1) 282 | .map(|car| eval_inner(env.clone(), &car, context.found_tail(true))) 283 | .collect::, RuntimeError>>()?; 284 | 285 | if !context.found_tail && context.in_func { 286 | Ok(Value::TailCall { 287 | func: Rc::new(func_or_macro), 288 | args, 289 | }) 290 | } else { 291 | let mut res = 292 | call_function_or_macro(env.clone(), &mut func_or_macro, args); 293 | 294 | while let Ok(Value::TailCall { func, args }) = res { 295 | res = call_function_or_macro(env.clone(), func.as_ref(), args); 296 | } 297 | 298 | res 299 | } 300 | } 301 | } 302 | } 303 | } 304 | 305 | // plain value 306 | _ => Ok(expression.clone()), 307 | } 308 | } 309 | // 🦀 Boo! Did I scare ya? Haha! 310 | 311 | fn value_to_argnames(argnames: List) -> Result, RuntimeError> { 312 | argnames 313 | .into_iter() 314 | .enumerate() 315 | .map(|(index, arg)| match arg { 316 | Value::Symbol(s) => Ok(s), 317 | _ => Err(RuntimeError { 318 | msg: format!( 319 | "Expected list of arg names, but arg {} is a {}", 320 | index, 321 | arg.type_name() 322 | ), 323 | }), 324 | }) 325 | .collect() 326 | } 327 | 328 | /// Calling a function is separated from the main `eval_inner()` function 329 | /// so that tail calls can be evaluated without just returning themselves 330 | /// as-is as a tail-call. 331 | fn call_function_or_macro( 332 | env: Rc>, 333 | func: &Value, 334 | args: Vec, 335 | ) -> Result { 336 | if let Value::NativeFunc(func) = func { 337 | func(env, args) 338 | } else if let Value::NativeClosure(closure) = func { 339 | closure.borrow_mut()(env, args) 340 | } else { 341 | let lambda = match func { 342 | Value::Lambda(lamb) => Some(lamb), 343 | Value::Macro(lamb) => Some(lamb), 344 | _ => None, 345 | }; 346 | 347 | if let Some(lambda) = lambda { 348 | // bind args 349 | let mut arg_env = Env::extend(lambda.closure.clone()); 350 | for (index, arg_name) in lambda.argnames.iter().enumerate() { 351 | if arg_name.0 == "..." { 352 | // rest parameters 353 | arg_env.define( 354 | Symbol::from("..."), 355 | Value::List(args.into_iter().skip(index).collect()), 356 | ); 357 | break; 358 | } else { 359 | arg_env.define(arg_name.clone(), args[index].clone()); 360 | } 361 | } 362 | 363 | // evaluate each line of body 364 | let clauses: &List = lambda.body.as_ref().try_into()?; 365 | eval_block_inner( 366 | Rc::new(RefCell::new(arg_env)), 367 | clauses.into_iter(), 368 | Context { 369 | found_tail: false, 370 | in_func: true, 371 | quoting: false, 372 | }, 373 | ) 374 | } else { 375 | Err(RuntimeError { 376 | msg: format!("{} is not callable", func), 377 | }) 378 | } 379 | } 380 | } 381 | 382 | #[derive(Debug, Clone, Copy)] 383 | struct Context { 384 | pub found_tail: bool, 385 | pub in_func: bool, 386 | pub quoting: bool, 387 | } 388 | 389 | impl Context { 390 | pub fn new() -> Self { 391 | Self { 392 | found_tail: false, 393 | in_func: false, 394 | quoting: false, 395 | } 396 | } 397 | 398 | pub fn found_tail(self, found_tail: bool) -> Self { 399 | Self { 400 | found_tail, 401 | in_func: self.in_func, 402 | quoting: self.quoting, 403 | } 404 | } 405 | 406 | // pub fn in_func(self, in_func: bool) -> Self { 407 | // Self { 408 | // found_tail: self.found_tail, 409 | // in_func, 410 | // quoting: self.quoting, 411 | // } 412 | // } 413 | 414 | pub fn quoting(self, quoting: bool) -> Self { 415 | Self { 416 | found_tail: self.found_tail, 417 | in_func: self.in_func, 418 | quoting, 419 | } 420 | } 421 | } 422 | -------------------------------------------------------------------------------- /src/model/value.rs: -------------------------------------------------------------------------------- 1 | use cfg_if::cfg_if; 2 | use std::any::Any; 3 | use std::ops::{Add, Div, Mul, Sub}; 4 | use std::rc::Rc; 5 | use std::{cell::RefCell, cmp::Ordering}; 6 | use std::{collections::HashMap, fmt::Debug}; 7 | 8 | cfg_if! { 9 | if #[cfg(feature = "bigint")] { 10 | use num_bigint::BigInt; 11 | use num_traits::ToPrimitive; 12 | } 13 | } 14 | 15 | use super::{Env, FloatType, IntType, Lambda, List, RuntimeError, Symbol}; 16 | use crate::lisp; 17 | 18 | /// `Value` encompasses all possible Lisp values, including atoms, lists, and 19 | /// others. 20 | #[derive(Clone)] 21 | pub enum Value { 22 | True, 23 | False, 24 | Int(IntType), 25 | Float(FloatType), 26 | String(String), 27 | Symbol(Symbol), 28 | List(List), 29 | HashMap(HashMapRc), 30 | 31 | /// A native Rust function that can be called from lisp code 32 | NativeFunc(NativeFunc), 33 | 34 | /// A native Rust closure that can be called from lisp code (the closure 35 | /// can capture things from its Rust environment) 36 | NativeClosure( 37 | Rc>, Vec) -> Result>>, 38 | ), 39 | 40 | /// A lisp function defined in lisp 41 | Lambda(Lambda), 42 | 43 | /// A lisp macro defined in lisp 44 | Macro(Lambda), 45 | 46 | /// A reference to a foreign value (struct, enum, etc) 47 | Foreign(Rc), 48 | 49 | /// A tail-call that has yet to be executed. Internal use only! 50 | TailCall { 51 | func: Rc, 52 | args: Vec, 53 | }, 54 | } 55 | 56 | /// A Rust function that is to be called from lisp code 57 | pub type NativeFunc = fn(env: Rc>, args: Vec) -> Result; 58 | 59 | /// Alias for the contents of Value::HashMap 60 | pub type HashMapRc = Rc>>; 61 | 62 | impl Value { 63 | pub const NIL: Value = Value::List(List::NIL); 64 | 65 | pub fn type_name(&self) -> &'static str { 66 | match self { 67 | Value::NativeFunc(_) => "function", 68 | Value::NativeClosure(_) => "function", 69 | Value::Lambda(_) => "function", 70 | Value::Macro(_) => "macro", 71 | Value::True => "T", 72 | Value::False => "F", 73 | Value::String(_) => "string", 74 | Value::List(List::NIL) => "nil", 75 | Value::List(_) => "list", 76 | Value::HashMap(_) => "hash map", 77 | Value::Int(_) => "integer", 78 | Value::Float(_) => "float", 79 | Value::Symbol(_) => "symbol", 80 | Value::Foreign(_) => "foreign value", 81 | Value::TailCall { func: _, args: _ } => "tail call", 82 | } 83 | } 84 | } 85 | 86 | impl From for Value { 87 | fn from(b: bool) -> Self { 88 | match b { 89 | true => Value::True, 90 | false => Value::False, 91 | } 92 | } 93 | } 94 | 95 | impl From for bool { 96 | fn from(value: Value) -> Self { 97 | (&value).into() 98 | } 99 | } 100 | 101 | impl From<&Value> for bool { 102 | fn from(value: &Value) -> Self { 103 | value != &Value::List(List::NIL) && value != &Value::False 104 | } 105 | } 106 | 107 | impl TryFrom<&Value> for IntType { 108 | type Error = RuntimeError; 109 | 110 | fn try_from(value: &Value) -> Result { 111 | match value { 112 | Value::Int(this) => Ok(this.clone()), 113 | _ => Err(RuntimeError { 114 | msg: format!("Expected int, got a {}", value), 115 | }), 116 | } 117 | } 118 | } 119 | 120 | impl From for Value { 121 | fn from(i: IntType) -> Self { 122 | Value::Int(i) 123 | } 124 | } 125 | 126 | impl TryFrom<&Value> for FloatType { 127 | type Error = RuntimeError; 128 | 129 | fn try_from(value: &Value) -> Result { 130 | match value { 131 | Value::Float(this) => Ok(*this), 132 | _ => Err(RuntimeError { 133 | msg: format!("Expected float, got a {}", value), 134 | }), 135 | } 136 | } 137 | } 138 | 139 | impl From for Value { 140 | fn from(i: FloatType) -> Self { 141 | Value::Float(i) 142 | } 143 | } 144 | 145 | impl<'a> TryFrom<&'a Value> for &'a String { 146 | type Error = RuntimeError; 147 | 148 | fn try_from(value: &'a Value) -> Result { 149 | match value { 150 | Value::String(this) => Ok(this), 151 | _ => Err(RuntimeError { 152 | msg: format!("Expected string, got a {}", value), 153 | }), 154 | } 155 | } 156 | } 157 | 158 | impl From for Value { 159 | fn from(i: String) -> Self { 160 | Value::String(i) 161 | } 162 | } 163 | 164 | impl<'a> TryFrom<&'a Value> for &'a Symbol { 165 | type Error = RuntimeError; 166 | 167 | fn try_from(value: &'a Value) -> Result { 168 | match value { 169 | Value::Symbol(this) => Ok(this), 170 | _ => Err(RuntimeError { 171 | msg: format!("Expected symbol, got a {}", value), 172 | }), 173 | } 174 | } 175 | } 176 | 177 | impl From for Value { 178 | fn from(i: Symbol) -> Self { 179 | Value::Symbol(i) 180 | } 181 | } 182 | 183 | impl<'a> TryFrom<&'a Value> for &'a List { 184 | type Error = RuntimeError; 185 | 186 | fn try_from(value: &'a Value) -> Result { 187 | match value { 188 | Value::List(this) => Ok(this), 189 | _ => Err(RuntimeError { 190 | msg: format!("Expected list, got a {}", value), 191 | }), 192 | } 193 | } 194 | } 195 | 196 | impl From for Value { 197 | fn from(i: List) -> Self { 198 | Value::List(i) 199 | } 200 | } 201 | 202 | impl<'a> TryFrom<&'a Value> for &'a Lambda { 203 | type Error = RuntimeError; 204 | 205 | fn try_from(value: &'a Value) -> Result { 206 | match value { 207 | Value::Lambda(this) => Ok(this), 208 | _ => Err(RuntimeError { 209 | msg: format!("Expected function, got a {}", value), 210 | }), 211 | } 212 | } 213 | } 214 | 215 | impl From for Value { 216 | fn from(i: Lambda) -> Self { 217 | Value::Lambda(i) 218 | } 219 | } 220 | 221 | impl<'a> TryFrom<&'a Value> for &'a HashMapRc { 222 | type Error = RuntimeError; 223 | 224 | fn try_from(value: &'a Value) -> Result { 225 | match value { 226 | Value::HashMap(this) => Ok(this), 227 | _ => Err(RuntimeError { 228 | msg: format!("Expected hash map, got a {}", value), 229 | }), 230 | } 231 | } 232 | } 233 | 234 | impl From> for Value { 235 | fn from(i: HashMap) -> Self { 236 | Value::HashMap(Rc::new(RefCell::new(i))) 237 | } 238 | } 239 | 240 | impl From for Value { 241 | fn from(i: HashMapRc) -> Self { 242 | Value::HashMap(i) 243 | } 244 | } 245 | 246 | impl<'a> TryFrom<&'a Value> for &'a Rc { 247 | type Error = RuntimeError; 248 | 249 | fn try_from(value: &'a Value) -> Result { 250 | match value { 251 | Value::Foreign(this) => Ok(this), 252 | _ => Err(RuntimeError { 253 | msg: format!("Expected foreign value, got a {}", value), 254 | }), 255 | } 256 | } 257 | } 258 | 259 | impl From> for Value { 260 | fn from(i: Rc) -> Self { 261 | Value::Foreign(i) 262 | } 263 | } 264 | 265 | impl std::fmt::Display for Value { 266 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 267 | match self { 268 | Value::NativeFunc(_) => f.write_str(""), 269 | Value::NativeClosure(_) => f.write_str(""), 270 | Value::True => f.write_str("T"), 271 | Value::False => f.write_str("F"), 272 | Value::Lambda(this) => write!(f, "", this), 273 | Value::Macro(this) => write!(f, "(macro {})", this), 274 | Value::String(this) => write!(f, "\"{}\"", this), 275 | Value::List(this) => write!(f, "{}", this), 276 | Value::HashMap(this) => { 277 | let borrowed = this.borrow(); 278 | let entries = std::iter::once(lisp! { hash }).chain( 279 | borrowed 280 | .iter() 281 | .map(|(key, value)| [key.clone(), value.clone()].into_iter()) 282 | .flatten(), 283 | ); 284 | 285 | let list = Value::List(entries.collect()); 286 | 287 | write!(f, "{}", list) 288 | } 289 | Value::Int(this) => write!(f, "{}", this), 290 | Value::Float(this) => write!(f, "{}", this), 291 | Value::Symbol(Symbol(this)) => write!(f, "{}", this), 292 | Value::Foreign(_) => f.write_str(""), 293 | Value::TailCall { func, args } => { 294 | write!(f, "", func, args) 295 | } 296 | } 297 | } 298 | } 299 | 300 | impl Debug for Value { 301 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 302 | match self { 303 | Value::NativeFunc(_) => f.write_str(""), 304 | Value::NativeClosure(_) => f.write_str(""), 305 | Value::True => f.write_str("Value::True"), 306 | Value::False => f.write_str("Value::False"), 307 | Value::Lambda(this) => write!(f, "Value::Lambda({:?})", this), 308 | Value::Macro(this) => write!(f, "Value::Macro({:?})", this), 309 | Value::String(this) => write!(f, "Value::String({:?})", this), 310 | Value::List(this) => write!(f, "Value::List({:?})", this), 311 | Value::HashMap(this) => write!(f, "Value::HashMap({:?})", this), 312 | Value::Int(this) => write!(f, "Value::Int({:?})", this), 313 | Value::Float(this) => write!(f, "Value::Float({:?})", this), 314 | Value::Symbol(Symbol(this)) => write!(f, "Value::Symbol({:?})", this), 315 | Value::Foreign(_) => f.write_str(""), 316 | Value::TailCall { func, args } => write!( 317 | f, 318 | "Value::TailCall {{ func: {:?}, args: {:?} }}", 319 | func, args 320 | ), 321 | } 322 | } 323 | } 324 | 325 | impl PartialEq for Value { 326 | fn eq(&self, other: &Self) -> bool { 327 | match (self, other) { 328 | (Value::True, Value::True) => true, 329 | (Value::False, Value::False) => true, 330 | (Value::Lambda(this), Value::Lambda(other)) => this == other, 331 | (Value::Macro(this), Value::Macro(other)) => this == other, 332 | (Value::String(this), Value::String(other)) => this == other, 333 | (Value::List(this), Value::List(other)) => this == other, 334 | (Value::Int(this), Value::Int(other)) => this == other, 335 | (Value::Float(this), Value::Float(other)) => this.to_bits() == other.to_bits(), 336 | (Value::Symbol(this), Value::Symbol(other)) => this == other, 337 | (Value::HashMap(this), Value::HashMap(other)) => Rc::ptr_eq(this, other), 338 | (Value::Foreign(this), Value::Foreign(other)) => Rc::ptr_eq(this, other), 339 | ( 340 | Value::TailCall { 341 | func: this_func, 342 | args: this_args, 343 | }, 344 | Value::TailCall { 345 | func: other_func, 346 | args: other_args, 347 | }, 348 | ) => this_func == other_func && this_args == other_args, 349 | 350 | _ => false, 351 | } 352 | } 353 | } 354 | 355 | impl Eq for Value {} 356 | 357 | impl PartialOrd for Value { 358 | fn partial_cmp(&self, other: &Value) -> Option { 359 | if self == other { 360 | return Some(Ordering::Equal); 361 | } 362 | 363 | match (self, other) { 364 | (Value::True, Value::False) => Some(Ordering::Less), 365 | (Value::False, Value::True) => Some(Ordering::Greater), 366 | (Value::String(this), Value::String(other)) => this.partial_cmp(other), 367 | (Value::Symbol(Symbol(this)), Value::Symbol(Symbol(other))) => this.partial_cmp(other), 368 | (Value::Int(this), Value::Int(other)) => this.partial_cmp(other), 369 | (Value::Float(this), Value::Float(other)) => this.partial_cmp(other), 370 | (Value::Int(this), Value::Float(other)) => { 371 | int_type_to_float_type(this).partial_cmp(other) 372 | } 373 | (Value::Float(this), Value::Int(other)) => { 374 | this.partial_cmp(&int_type_to_float_type(other)) 375 | } 376 | _ => None, 377 | } 378 | } 379 | } 380 | 381 | impl Add<&Value> for &Value { 382 | type Output = Result; 383 | 384 | fn add(self, other: &Value) -> Self::Output { 385 | match (self, other) { 386 | // same type 387 | (Value::Int(this), Value::Int(other)) => Ok(Value::from(this + other)), 388 | (Value::Float(this), Value::Float(other)) => Ok(Value::from(this + other)), 389 | (Value::String(this), Value::String(other)) => Ok(Value::from(this.clone() + other)), 390 | 391 | // different numeric types 392 | (Value::Int(this), Value::Float(other)) => { 393 | Ok(Value::from(int_type_to_float_type(&this) + other)) 394 | } 395 | (Value::Float(this), Value::Int(other)) => { 396 | Ok(Value::from(this + int_type_to_float_type(&other))) 397 | } 398 | 399 | // non-string + string 400 | (Value::String(this), Value::Int(other)) => { 401 | Ok(Value::from(this.clone() + &other.to_string())) 402 | } 403 | (Value::String(this), Value::Float(other)) => { 404 | Ok(Value::from(this.clone() + &other.to_string())) 405 | } 406 | (Value::Int(this), Value::String(other)) => Ok(Value::from(this.to_string() + other)), 407 | (Value::Float(this), Value::String(other)) => Ok(Value::from(this.to_string() + other)), 408 | 409 | _ => Err(()), 410 | } 411 | } 412 | } 413 | 414 | impl Add for Value { 415 | type Output = Result; 416 | 417 | fn add(self, other: Value) -> Self::Output { 418 | &self + &other 419 | } 420 | } 421 | 422 | impl Sub<&Value> for &Value { 423 | type Output = Result; 424 | 425 | fn sub(self, other: &Value) -> Self::Output { 426 | match (self, other) { 427 | (Value::Int(this), Value::Int(other)) => Ok(Value::from(this - other)), 428 | (Value::Float(this), Value::Float(other)) => Ok(Value::from(this - other)), 429 | 430 | (Value::Int(this), Value::Float(other)) => { 431 | Ok(Value::from(int_type_to_float_type(&this) - other)) 432 | } 433 | (Value::Float(this), Value::Int(other)) => { 434 | Ok(Value::from(this - int_type_to_float_type(&other))) 435 | } 436 | 437 | _ => Err(()), 438 | } 439 | } 440 | } 441 | 442 | impl Sub for Value { 443 | type Output = Result; 444 | 445 | fn sub(self, other: Value) -> Self::Output { 446 | &self - &other 447 | } 448 | } 449 | 450 | impl Mul<&Value> for &Value { 451 | type Output = Result; 452 | 453 | fn mul(self, other: &Value) -> Self::Output { 454 | match (self, other) { 455 | (Value::Int(this), Value::Int(other)) => Ok(Value::from(this * other)), 456 | (Value::Float(this), Value::Float(other)) => Ok(Value::from(this * other)), 457 | 458 | (Value::Int(this), Value::Float(other)) => { 459 | Ok(Value::from(int_type_to_float_type(&this) * other)) 460 | } 461 | (Value::Float(this), Value::Int(other)) => { 462 | Ok(Value::from(this * int_type_to_float_type(&other))) 463 | } 464 | 465 | _ => Err(()), 466 | } 467 | } 468 | } 469 | 470 | impl Mul for Value { 471 | type Output = Result; 472 | 473 | fn mul(self, other: Value) -> Self::Output { 474 | &self * &other 475 | } 476 | } 477 | 478 | impl Div<&Value> for &Value { 479 | type Output = Result; 480 | 481 | fn div(self, other: &Value) -> Self::Output { 482 | match (self, other) { 483 | (Value::Int(this), Value::Int(other)) => Ok(Value::from(this / other)), 484 | (Value::Float(this), Value::Float(other)) => Ok(Value::from(this / other)), 485 | 486 | (Value::Int(this), Value::Float(other)) => { 487 | Ok(Value::from(int_type_to_float_type(&this) / other)) 488 | } 489 | (Value::Float(this), Value::Int(other)) => { 490 | Ok(Value::from(this / int_type_to_float_type(&other))) 491 | } 492 | 493 | _ => Err(()), 494 | } 495 | } 496 | } 497 | 498 | impl Div for Value { 499 | type Output = Result; 500 | 501 | fn div(self, other: Value) -> Self::Output { 502 | &self / &other 503 | } 504 | } 505 | 506 | /// Convert whatever int type we're using to whatever float type we're using 507 | fn int_type_to_float_type(i: &IntType) -> FloatType { 508 | cfg_if! { 509 | if #[cfg(feature = "bigint")] { 510 | cfg_if! { 511 | if #[cfg(feature = "f64")] { 512 | return i.to_f64().unwrap_or(f64::NAN); 513 | } else { 514 | return i.to_f32().unwrap_or(f32::NAN); 515 | } 516 | } 517 | } else { 518 | return *i as FloatType; 519 | } 520 | } 521 | } 522 | 523 | impl Ord for Value { 524 | fn cmp(&self, other: &Self) -> Ordering { 525 | match self.partial_cmp(other) { 526 | Some(ordering) => ordering, 527 | None => format!("{:?}", self).cmp(&format!("{:?}", other)), 528 | } 529 | } 530 | } 531 | 532 | impl std::hash::Hash for Value { 533 | fn hash(&self, state: &mut H) { 534 | // core::mem::discriminant(self).hash(state); 535 | match self { 536 | Value::False => false.hash(state), 537 | Value::True => true.hash(state), 538 | Value::Int(x) => x.hash(state), 539 | Value::Float(x) => x.to_bits().hash(state), 540 | Value::String(x) => x.hash(state), 541 | Value::Symbol(x) => x.hash(state), 542 | Value::List(x) => x.hash(state), 543 | Value::HashMap(x) => x.as_ptr().hash(state), 544 | Value::NativeFunc(x) => std::ptr::hash(x, state), 545 | Value::NativeClosure(x) => std::ptr::hash(x, state), 546 | Value::Lambda(x) => x.hash(state), 547 | Value::Macro(x) => x.hash(state), 548 | Value::Foreign(x) => std::ptr::hash(x, state), 549 | Value::TailCall { func, args } => { 550 | func.hash(state); 551 | args.hash(state); 552 | } 553 | } 554 | } 555 | } 556 | --------------------------------------------------------------------------------