From 9d0003ff14e54af6a8c2eb181c236cbfec2cc732 Mon Sep 17 00:00:00 2001 From: Felix Martin Date: Tue, 1 Jun 2021 21:32:06 -0400 Subject: [PATCH] Add test file, implement and/or/not, prepare for proper compound proc --- .gitignore | 1 - README.md | 8 +++++++- src/environment.rs | 3 +++ src/interpreter.rs | 15 +++++++-------- src/main.rs | 7 +++---- src/parser.rs | 7 ++++++- src/primitives.rs | 39 +++++++++++++++++++++++++++++++++++++++ test.scm | 43 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 108 insertions(+), 15 deletions(-) create mode 100644 test.scm diff --git a/.gitignore b/.gitignore index 605cef0..765cf3b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,3 @@ **/*.rs.bk .vscode -test.scm diff --git a/README.md b/README.md index f6af26a..108d592 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ # schemers -A crude Scheme interpreter implemented for SICP exercise 5.51. \ No newline at end of file +A crude Scheme interpreter implemented for SICP exercise 5.51. + +```bash +cargo build +./target/debug/schemers test.scm +``` + diff --git a/src/environment.rs b/src/environment.rs index 54d0ddc..ed6a132 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -22,6 +22,9 @@ pub fn get_global_environment<'a>() -> Env<'a> { (make_symbol("-"), Primitive(sub)), (make_symbol("="), Primitive(eq)), (make_symbol("<"), Primitive(lt)), + (make_symbol("or"), Primitive(or)), + (make_symbol("and"), Primitive(and)), + (make_symbol("not"), Primitive(not)), (make_symbol("list"), Primitive(list)), (make_symbol("cons"), Primitive(cons)), (make_symbol("car"), Primitive(car)), diff --git a/src/interpreter.rs b/src/interpreter.rs index 65e1b3a..fe86ece 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -3,7 +3,7 @@ use crate::environment::Env; use crate::parser::Datum; use crate::parser::Datum::*; -fn is_true(exp: &Datum) -> bool { +pub fn is_true(exp: &Datum) -> bool { match exp { Boolean(b) => *b, Number(_) => true, @@ -72,14 +72,13 @@ fn interpret_define(args: &Vec, env: &mut Env) -> Datum { } } - // XXX: A procedure is clearly not a lambda. We have to keep the - // scope of the definition. Otherwise, closures won't work. - let lambda_body = args[2..].to_vec(); - let lambda = Lambda { + // XXX: a procedure includes the current environment + let body = args[2..].to_vec(); + let proc = Procedure { parameters: parameters, - body: lambda_body, + body: body, }; - environment::define_variable(definition_var, lambda, env) + environment::define_variable(definition_var, proc, env) } _ => panic!("INTERPRET-DEFINE -- cannot define {:?}", args), } @@ -157,7 +156,7 @@ fn interpret_application(vec: &Vec, env: &mut Env) -> Datum { match &args[0] { Primitive(proc) => proc(args), - Lambda { parameters, body } => { + Lambda { parameters, body } | Procedure { parameters, body } => { let lambda_args = args[1..].to_vec(); let mut new_env = environment::extend(List(parameters.to_vec()), List(lambda_args), env); diff --git a/src/main.rs b/src/main.rs index 8668f1c..9ede58d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,8 +42,8 @@ fn interpret_file(filename: &Path) { let tokens = lexer::read(scm_code.as_str()); let datum = parser::parse(tokens); let mut env = environment::get_global_environment(); - let result = interpreter::interpret(&datum, &mut env); - println!("{:?}", result); + let _result = interpreter::interpret(&datum, &mut env); + // println!("{:?}", result); } Err(error) => println!("error: {} {:?}", error, filename), } @@ -51,8 +51,7 @@ fn interpret_file(filename: &Path) { fn main() { let args: Vec = env::args().collect(); - // let scm_code = "(begin (define (f n) (if (= n 1) 1 (* n (f (- n 1))))) (f 10))"; - let scm_code = "(let ((a 3) (b 4)) (define c (+ a b)) (* c c))"; + let scm_code = ""; if args.len() == 2 { let arg_path = Path::new(&args[1]); diff --git a/src/parser.rs b/src/parser.rs index 8fa9908..06bae0c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -16,6 +16,10 @@ pub enum Datum { parameters: Vec, body: Vec, }, + Procedure { + parameters: Vec, + body: Vec, + }, Unspecified, } @@ -42,7 +46,8 @@ impl PartialEq for Datum { impl fmt::Display for Datum { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Datum::Boolean(b) => write!(f, "{}", b), + Datum::Boolean(true) => write!(f, "#t"), + Datum::Boolean(false) => write!(f, "#f"), Datum::Number(n) => write!(f, "{}", n), Datum::Symbol(s) => write!(f, "{}", s), Datum::String(s) => write!(f, "{}", s), diff --git a/src/primitives.rs b/src/primitives.rs index eb86d56..bcade12 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -1,4 +1,5 @@ use crate::interpreter::has_tag; +use crate::interpreter::is_true; use crate::parser::make_symbol; use crate::parser::Datum; use crate::parser::Datum::*; @@ -89,6 +90,44 @@ pub fn lt(args: Vec) -> Datum { } } +pub fn or(args: Vec) -> Datum { + if args.len() == 1 { + Boolean(false) + } else { + for i in 1..args.len() { + if is_true(&args[i]) { + return args[i].clone(); + } + } + Boolean(false) + } +} + +pub fn and(args: Vec) -> Datum { + if args.len() == 1 { + Boolean(true) + } else { + for i in 1..args.len() { + if !is_true(&args[i]) { + return Boolean(false); + } + } + args[args.len() - 1].clone() + } +} + +pub fn not(args: Vec) -> Datum { + if args.len() != 2 { + panic!("NOT -- takes one argument"); + } else { + if is_true(&args[1]) { + Boolean(false) + } else { + Boolean(true) + } + } +} + pub fn display(args: Vec) -> Datum { for i in 1..args.len() { print!("{}", args[i]); diff --git a/test.scm b/test.scm new file mode 100644 index 0000000..4b051fc --- /dev/null +++ b/test.scm @@ -0,0 +1,43 @@ +(begin + (define (newline) (display "\n")) + (display (or #t #f)) (newline) + (display (and #t #f)) (newline) + (display (not #f)) (newline) + (define a 5) + (define (fib n) + (cond + ((< n 0) (display "WARNING: n smaller than 0")) + ((< n 3) 1) + (else (+ (fib (- n 2)) (fib (- n 1)))))) + (define (fac n) (if (= n 1) 1 (* n (fac (- n 1))))) + (set! a (+ a a)) + (define l (list a (* 3 3) (+ a a))) + (begin + (display "(fac ") (display a) + (display ") -> ") (display (fac a)) (display "\n")) + (begin + (display "(fib ") (display a) + (display ") -> ") (display (fib a)) (display "\n")) + (define l (cons 'a l)) + (display l) (newline) + (display (cons 'a 'b)) (newline) + (display (car l)) (newline) + (display (cdr (cdr l))) (newline) + (display (pair? l)) (newline) + (display (pair? 'a)) (newline) + (display ((lambda (x y) (* x a)) 3 5)) (newline) + (display + (let ((a (+ a a)) + (b (* a a)) + (x 42)) + (+ a b x))) + (newline) + (define (p) + (define a 42) + (define (f) a) + f) + (display ((p))) ; should return 42 + (newline) + 'done + ) +