Skip to content

Commit 31262bc

Browse files
committed
Initial checkin
0 parents  commit 31262bc

File tree

10 files changed

+651
-0
lines changed

10 files changed

+651
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target

Cargo.toml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "lisp-rs"
3+
version = "0.0.1"
4+
edition = "2021"
5+
license = "MIT"
6+
homepage = "https://vishpat.github.io/lisp-rs"
7+
repository = "https://github.com/vishpat/lisp-rs"
8+
documentation = "https://vishpat.github.io/lisp-rs"
9+
description = """
10+
Lisp interpreter in Rust
11+
"""
12+
13+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
14+
15+
[dependencies]
16+
linefeed = "0.6.0"

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022 Vishal Patil
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.

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# lisp-rs
2+
3+
A simple Lisp interpreter in Rust. The interpreter was developed to explain how programming language interpreters work and how can they be implemented using the Rust programming language.
4+
5+
For detailed code walk-through refer to the [docs](https://vishpat.github.io/lisp-rs).
6+
7+
[![asciicast](https://asciinema.org/a/VVQQfGpp15a4BaoNgnEKIqqrr.svg)](https://asciinema.org/a/VVQQfGpp15a4BaoNgnEKIqqrr)

src/env.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use crate::object::Object;
2+
use std::collections::HashMap;
3+
4+
#[derive(Debug, PartialEq, Default)]
5+
pub struct Env<'a> {
6+
parent: Option<&'a Env<'a>>,
7+
vars: HashMap<String, Object>,
8+
}
9+
10+
impl<'a> Env<'a> {
11+
12+
pub fn new() -> Env<'a> {
13+
Env {
14+
vars: HashMap::new(),
15+
parent: None,
16+
}
17+
}
18+
19+
pub fn extend(parent: &'a Self) -> Env<'a> {
20+
Env {
21+
vars: HashMap::new(),
22+
parent: Some(parent),
23+
}
24+
}
25+
26+
pub fn get(&self, name: &str) -> Option<Object> {
27+
match self.vars.get(name) {
28+
Some(value) => Some(value.clone()),
29+
None => self.parent.and_then(|parent| parent.get(name)),
30+
}
31+
}
32+
33+
pub fn set(&mut self, name: &str, val: Object) {
34+
self.vars.insert(name.to_string(), val);
35+
}
36+
}

src/eval.rs

+254
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
use crate::env::*;
2+
use crate::object::*;
3+
use crate::parser::*;
4+
5+
fn eval_binary_op(list: &Vec<Object>, env: &mut Env) -> Result<Object, String> {
6+
if list.len() != 3 {
7+
return Err(format!("Invalid number of arguments for infix operator"));
8+
}
9+
let operator = list[0].clone();
10+
let left = eval_obj(&list[1].clone(), env)?;
11+
let right = eval_obj(&list[2].clone(), env)?;
12+
let left_val = match left {
13+
Object::Integer(n) => n,
14+
_ => return Err(format!("Left operand must be an integer {:?}", left)),
15+
};
16+
let right_val = match right {
17+
Object::Integer(n) => n,
18+
_ => return Err(format!("Right operand must be an integer {:?}", right)),
19+
};
20+
match operator {
21+
Object::Symbol(s) => match s.as_str() {
22+
"+" => Ok(Object::Integer(left_val + right_val)),
23+
"-" => Ok(Object::Integer(left_val - right_val)),
24+
"*" => Ok(Object::Integer(left_val * right_val)),
25+
"/" => Ok(Object::Integer(left_val / right_val)),
26+
"<" => Ok(Object::Bool(left_val < right_val)),
27+
">" => Ok(Object::Bool(left_val > right_val)),
28+
"=" => Ok(Object::Bool(left_val == right_val)),
29+
"!=" => Ok(Object::Bool(left_val != right_val)),
30+
_ => Err(format!("Invalid infix operator: {}", s)),
31+
},
32+
_ => Err(format!("Operator must be a symbol")),
33+
}
34+
}
35+
36+
fn eval_define(list: &Vec<Object>, env: &mut Env) -> Result<Object, String> {
37+
if list.len() != 3 {
38+
return Err(format!("Invalid number of arguments for define"));
39+
}
40+
41+
let sym = match &list[1] {
42+
Object::Symbol(s) => s.clone(),
43+
_ => return Err(format!("Invalid define")),
44+
};
45+
let val = eval_obj(&list[2], env)?;
46+
env.set(&sym, val);
47+
Ok(Object::Void)
48+
}
49+
50+
fn eval_if(list: &Vec<Object>, env: &mut Env) -> Result<Object, String> {
51+
if list.len() != 4 {
52+
return Err(format!("Invalid number of arguments for if statement"));
53+
}
54+
55+
let cond_obj = eval_obj(&list[1], env)?;
56+
let cond = match cond_obj {
57+
Object::Bool(b) => b,
58+
_ => return Err(format!("Condition must be a boolean")),
59+
};
60+
61+
if cond == true {
62+
return eval_obj(&list[2], env);
63+
} else {
64+
return eval_obj(&list[3], env);
65+
}
66+
}
67+
68+
fn eval_function_definition(list: &Vec<Object>) -> Result<Object, String> {
69+
let params = match &list[1] {
70+
Object::List(list) => {
71+
let mut params = Vec::new();
72+
for param in list {
73+
match param {
74+
Object::Symbol(s) => params.push(s.clone()),
75+
_ => return Err(format!("Invalid lambda parameter")),
76+
}
77+
}
78+
params
79+
}
80+
_ => return Err(format!("Invalid lambda")),
81+
};
82+
83+
let body = match &list[2] {
84+
Object::List(list) => list.clone(),
85+
_ => return Err(format!("Invalid lambda")),
86+
};
87+
Ok(Object::Lambda(params, body))
88+
}
89+
90+
fn eval_function_call(
91+
s: &str,
92+
list: &Vec<Object>,
93+
env: &mut Env,
94+
) -> Result<Object, String> {
95+
let lamdba = env.get(s);
96+
if lamdba.is_none() {
97+
return Err(format!("Unbound symbol: {}", s));
98+
}
99+
100+
let func = lamdba.unwrap();
101+
match func {
102+
Object::Lambda(params, body) => {
103+
let mut new_env = Env::extend(env);
104+
for (i, param) in params.iter().enumerate() {
105+
let val = eval_obj(&list[i + 1], &mut new_env)?;
106+
new_env.set(param, val);
107+
}
108+
return eval_obj(&Object::List(body), &mut new_env);
109+
}
110+
_ => return Err(format!("Not a lambda: {}", s)),
111+
}
112+
}
113+
114+
fn eval_symbol(s: &str, env: &mut Env) -> Result<Object, String> {
115+
let val = env.get(s);
116+
if val.is_none() {
117+
return Err(format!("Unbound symbol: {}", s));
118+
}
119+
Ok(val.unwrap().clone())
120+
}
121+
122+
fn eval_list(list: &Vec<Object>, env: &mut Env) -> Result<Object, String> {
123+
let head = &list[0];
124+
match head {
125+
Object::Symbol(s) => match s.as_str() {
126+
"+" | "-" | "*" | "/" | "<" | ">" | "=" | "!=" => {
127+
return eval_binary_op(&list, env);
128+
}
129+
"define" => eval_define(&list, env),
130+
"if" => eval_if(&list, env),
131+
"lambda" => eval_function_definition(&list),
132+
_ => eval_function_call(&s, &list, env),
133+
},
134+
_ => {
135+
let mut new_list = Vec::new();
136+
for obj in list {
137+
let result = eval_obj(obj, env)?;
138+
match result {
139+
Object::Void => {}
140+
_ => new_list.push(result),
141+
}
142+
}
143+
Ok(Object::List(new_list))
144+
}
145+
}
146+
}
147+
148+
fn eval_obj(obj: &Object, env: &mut Env) -> Result<Object, String> {
149+
match obj {
150+
Object::List(list) => eval_list(list, env),
151+
Object::Void => Ok(Object::Void),
152+
Object::Lambda(_params, _body) => Ok(Object::Void),
153+
Object::Bool(_) => Ok(obj.clone()),
154+
Object::Integer(n) => Ok(Object::Integer(*n)),
155+
Object::Symbol(s) => eval_symbol(s, env),
156+
}
157+
}
158+
159+
pub fn eval(program: &str, env: &mut Env) -> Result<Object, String> {
160+
let parsed_list = parse(program);
161+
if parsed_list.is_err() {
162+
return Err(format!("{}", parsed_list.err().unwrap()));
163+
}
164+
eval_obj(&parsed_list.unwrap(), env)
165+
}
166+
167+
#[cfg(test)]
168+
mod tests {
169+
use super::*;
170+
171+
#[test]
172+
fn test_simple_add() {
173+
let mut env = Box::new(Env::new());
174+
let result = eval("(+ 1 2)", &mut env).unwrap();
175+
assert_eq!(result, Object::Integer(3));
176+
}
177+
178+
#[test]
179+
fn test_area_of_a_circle() {
180+
let mut env = Box::new(Env::new());
181+
let program = "(
182+
(define r 10)
183+
(define pi 314)
184+
(* pi (* r r))
185+
)";
186+
let result = eval(program, &mut env).unwrap();
187+
assert_eq!(
188+
result,
189+
Object::List(vec![Object::Integer((314 * 10 * 10) as i64)])
190+
);
191+
}
192+
193+
#[test]
194+
fn test_sqr_function() {
195+
let mut env = Box::new(Env::new());
196+
let program = "(
197+
(define sqr (lambda (r) (* r r)))
198+
(sqr 10)
199+
)";
200+
let result = eval(program, &mut env).unwrap();
201+
assert_eq!(
202+
result,
203+
Object::List(vec![Object::Integer((10 * 10) as i64)])
204+
);
205+
}
206+
207+
#[test]
208+
fn test_fibonaci() {
209+
let mut env = Box::new(Env::new());
210+
let program = "
211+
(
212+
(define fib (lambda (n) (if (< n 2) 1 (+ (fib (- n 1)) (fib (- n 2))))))
213+
(fib 10)
214+
)
215+
";
216+
217+
let result = eval(program, &mut env).unwrap();
218+
assert_eq!(result, Object::List(vec![Object::Integer((89) as i64)]));
219+
}
220+
221+
#[test]
222+
fn test_factorial() {
223+
let mut env = Box::new(Env::new());
224+
let program = "
225+
(
226+
(define fact (lambda (n) (if (< n 1) 1 (* n (fact (- n 1))))))
227+
(fact 5)
228+
)
229+
";
230+
231+
let result = eval(program, &mut env).unwrap();
232+
assert_eq!(result, Object::List(vec![Object::Integer((120) as i64)]));
233+
}
234+
235+
#[test]
236+
fn test_circle_area_function() {
237+
let mut env = Box::new(Env::new());
238+
let program = "
239+
(
240+
(define pi 314)
241+
(define r 10)
242+
(define sqr (lambda (r) (* r r)))
243+
(define area (lambda (r) (* pi (sqr r))))
244+
(area r)
245+
)
246+
";
247+
248+
let result = eval(program, &mut env).unwrap();
249+
assert_eq!(
250+
result,
251+
Object::List(vec![Object::Integer((314 * 10 * 10) as i64)])
252+
);
253+
}
254+
}

0 commit comments

Comments
 (0)