Add test file, implement and/or/not, prepare for proper compound proc

This commit is contained in:
2021-06-01 21:32:06 -04:00
parent 7b5435141a
commit 9d0003ff14
8 changed files with 108 additions and 15 deletions

1
.gitignore vendored
View File

@@ -7,4 +7,3 @@
**/*.rs.bk
.vscode
test.scm

View File

@@ -1,3 +1,9 @@
# schemers
A crude Scheme interpreter implemented for SICP exercise 5.51.
```bash
cargo build
./target/debug/schemers test.scm
```

View File

@@ -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)),

View File

@@ -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<Datum>, 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<Datum>, 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);

View File

@@ -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<String> = 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]);

View File

@@ -16,6 +16,10 @@ pub enum Datum {
parameters: Vec<Datum>,
body: Vec<Datum>,
},
Procedure {
parameters: Vec<Datum>,
body: Vec<Datum>,
},
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),

View File

@@ -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>) -> Datum {
}
}
pub fn or(args: Vec<Datum>) -> 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>) -> 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>) -> 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>) -> Datum {
for i in 1..args.len() {
print!("{}", args[i]);

43
test.scm Normal file
View File

@@ -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
)