Compare commits

...

21 Commits

Author SHA1 Message Date
af7fc21cde Fix env model resolves #11 - I call this good enough :) 2021-06-03 21:22:41 -04:00
9d0003ff14 Add test file, implement and/or/not, prepare for proper compound proc 2021-06-01 21:32:06 -04:00
7b5435141a Implement let-expressions resolves #2 2021-06-01 20:54:05 -04:00
b3181602d6 Implement lambda support 2021-06-01 19:47:48 -04:00
f8b8019682 Implement cons, car, cdr, null?, pair? resolves #1 2021-05-31 13:00:12 -04:00
7d8a256c76 Implement support for cond resolves #4 2021-05-31 11:21:20 -04:00
2e67e1220c Implement display and string support resolves #10 2021-05-30 22:49:31 -04:00
a7caac30f9 Implement quote support resolves #9 2021-05-30 22:08:29 -04:00
3b3ab96f7d Implement set! resolves #3 2021-05-30 12:44:25 -04:00
1da08c0a43 Implement PartialEq and PartialOrd resolves #7 2021-05-30 12:25:20 -04:00
e070441c31 Rework environment mode fixes #8 2021-05-30 11:57:59 -04:00
3ae5ccb64c Implement interpret from file resolves #6 2021-05-30 10:38:09 -04:00
aaa93d8593 Implement REPL resolves #5 2021-05-30 10:19:35 -04:00
390c5d1b48 Implement lambda application (boom!) 2021-05-29 13:50:26 -04:00
b0968ba498 Implement begin and define 2021-05-29 11:29:27 -04:00
794cad2219 Initial version of environment model 2021-05-27 21:02:18 -04:00
56f5426426 Implement if else support 2021-05-21 13:17:50 -04:00
b0bf78a3bd Implement primitive procedure application 2021-05-20 23:18:19 -04:00
f4c58cfa02 Work on interpreter 2021-05-20 14:11:32 -04:00
65444afe14 Merge branch 'master' of git.felixm.de:felixm/schemers
Fix commit message
2021-05-16 13:06:50 -04:00
6744b68385 Implement basic parser 2021-05-16 13:06:26 -04:00
8 changed files with 788 additions and 14 deletions

View File

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

118
src/environment.rs Normal file
View File

@@ -0,0 +1,118 @@
use crate::parser::make_symbol;
use crate::parser::Datum;
use std::cell::RefCell;
use std::mem;
use std::rc::Rc;
type Mapping = (Datum, Datum);
#[derive(Debug)]
pub struct Frame {
mappings: Vec<Mapping>,
outer: Option<Rc<RefCell<Frame>>>,
}
pub type Env = Rc<RefCell<Frame>>;
pub fn get_global_environment() -> Env {
use crate::parser::Datum::Primitive;
use crate::primitives::*;
let f = Frame {
mappings: vec![
(make_symbol("+"), Primitive(add)),
(make_symbol("*"), Primitive(mul)),
(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)),
(make_symbol("cdr"), Primitive(cdr)),
(make_symbol("pair?"), Primitive(pair)),
(make_symbol("null?"), Primitive(null)),
(make_symbol("display"), Primitive(display)),
],
outer: None,
};
Rc::new(RefCell::new(f))
}
pub fn lookup_variable_value(var: &Datum, env: &Env) -> Datum {
let symbol_name = Datum::as_str(var).expect("LOOKUP-VARIABLE-VALUE -- not a symbol");
let frame = env.borrow();
for mapping in &frame.mappings {
if let (Datum::Symbol(current_name), datum) = mapping {
if symbol_name == *current_name {
return datum.clone();
}
}
}
match &frame.outer {
Some(outer_env) => lookup_variable_value(var, &outer_env),
_ => panic!("LOOKUP-VARIABLE-VALUE -- does not exist {}", var),
}
}
pub fn define_variable(var: &Datum, val: Datum, env: &mut Env) -> Datum {
let symbol_name = Datum::as_str(var).expect("DEFINE-VARIABLE -- not a symbol");
let mut frame = env.borrow_mut();
// Assign to variable if it already exits
for mapping in &mut frame.mappings {
if let (Datum::Symbol(current_name), _) = mapping {
if symbol_name == *current_name {
mapping.1 = val;
return var.clone();
}
}
}
// Create variable because it does not exist yet
let pair = (var.clone(), val);
frame.mappings.push(pair);
var.clone()
}
pub fn set_variable(var: &Datum, val: Datum, env: &Env) -> Datum {
let symbol_name = Datum::as_str(var).expect("SET-VARIABLE -- not a symbol");
let mut frame = env.borrow_mut();
for mapping in &mut frame.mappings {
if let (Datum::Symbol(current_name), _) = mapping {
if symbol_name == *current_name {
mapping.1 = val;
return var.clone();
}
}
}
match &frame.outer {
Some(outer_env) => set_variable(var, val, &outer_env),
_ => panic!("LOOKUP-VARIABLE-VALUE -- does not exist {}", var),
}
}
pub fn extend(vars: Datum, vals: Datum, env: &mut Env) -> Env {
let mut mappings = vec![];
if let (Datum::List(mut vars), Datum::List(mut vals)) = (vars, vals) {
if vars.len() != vals.len() {
panic!("EXTEND-ENVIRONMENT -- length of vars and vals does not match")
}
for i in 0..vars.len() {
let pair = (mem::take(&mut vars[i]), mem::take(&mut vals[i]));
mappings.push(pair);
}
} else {
panic!("EXTEND-ENVIRONMENT -- vars or vals not lists")
}
let f = Frame {
mappings,
outer: Some(env.clone()),
};
Rc::new(RefCell::new(f))
}

206
src/interpreter.rs Normal file
View File

@@ -0,0 +1,206 @@
use crate::environment;
use crate::environment::Env;
use crate::parser::Datum;
use crate::parser::Datum::*;
pub fn is_true(exp: &Datum) -> bool {
match exp {
Boolean(b) => *b,
Number(_) => true,
List(v) if has_tag(v, "list") => true,
_ => panic!("IS-TRUE -- unknown-truth-value {:?}", exp),
}
}
pub fn has_tag(vec: &Vec<Datum>, tag: &str) -> bool {
if vec.len() == 0 {
return false;
}
if let Symbol(s) = &vec[0] {
if s == tag {
return true;
}
}
false
}
fn interpret_if(args: &Vec<Datum>, env: &mut Env) -> Datum {
let predicate = &args[1];
let alternative = &args[2];
let predicate_interpreted = interpret(predicate, env);
if is_true(&predicate_interpreted) {
interpret(alternative, env)
} else {
if args.len() == 4 {
let consequent = &args[3];
interpret(consequent, env)
} else {
Unspecified
}
}
}
fn interpret_set(args: &Vec<Datum>, env: &mut Env) -> Datum {
match &args[1] {
Symbol(_) => {
let var = &args[1];
let val = interpret(&args[2], env);
environment::set_variable(var, val, env)
}
_ => panic!("INTERPRET-SET -- cannot set {:?}", args),
}
}
fn interpret_define(args: &Vec<Datum>, env: &mut Env) -> Datum {
match &args[1] {
Symbol(_) => {
// regular definition
let var = &args[1];
let val = interpret(&args[2], env);
environment::define_variable(var, val, env)
}
List(v) => {
// procedure definition
let definition_var = &v[0];
let mut parameters = vec![];
for p in &v[1..] {
if let Symbol(_) = &p {
parameters.push(p.clone());
} else {
panic!("INTERPRET-DEFINE -- parameter not a symbol {:?}", p);
}
}
let body = args[2..].to_vec();
let proc = Procedure {
parameters: parameters,
body: body,
env: env.clone(),
};
environment::define_variable(definition_var, proc, env)
}
_ => panic!("INTERPRET-DEFINE -- cannot define {:?}", args),
}
}
fn interpret_cond(vec: &Vec<Datum>, env: &mut Env) -> Datum {
for i in 1..vec.len() {
if let List(v) = &vec[i] {
if has_tag(v, "else") || is_true(&interpret(&v[0], env)) {
return interpret_begin(v, env);
}
} else {
panic!("INTERPRET-COND -- not a list {:?}", vec[i])
}
}
panic!("INTERPRET-COND -- missing else statement? {:?}", vec)
}
fn interpret_lambda(vec: &Vec<Datum>, _env: &mut Env) -> Datum {
let mut parameters = vec![];
if let List(params) = &vec[1] {
for p in params {
if let Symbol(_) = p {
parameters.push(p.clone());
} else {
panic!("INTERPRET-LAMBDA - not a symbol {:?}", p);
}
}
}
let body = vec[2..].to_vec();
Lambda { parameters, body }
}
fn transform_let_to_lambda(vec: &Vec<Datum>) -> Datum {
let mut args = vec![];
let mut parameters = vec![];
let body = vec[2..].to_vec();
if let List(bindings) = &vec[1] {
for binding in bindings {
if let List(binding) = binding {
parameters.push(binding[0].clone());
args.push(binding[1].clone());
} else {
panic!("TRANSFORM-LET-TO-LAMBDA -- invalid binding {:?}", binding);
}
}
let lambda = Lambda { parameters, body };
let mut result = vec![lambda];
result.append(&mut args);
List(result)
} else {
panic!("TRANSFORM-LET-TO-LAMBDA -- invalid bindings {:?}", vec[1]);
}
}
fn interpret_begin(vec: &Vec<Datum>, env: &mut Env) -> Datum {
let mut r = Datum::Unspecified;
for i in 1..vec.len() {
r = interpret(&vec[i], env);
}
r
}
fn interpret_application(vec: &Vec<Datum>, env: &mut Env) -> Datum {
if vec.len() == 0 {
panic!("INTERPRET-APPLICATION -- no-procedure")
}
let mut args = vec![];
for v in vec {
let a = interpret(v, env);
args.push(a)
}
match &args[0] {
Primitive(proc) => proc(args),
Lambda { parameters, body } => {
let lambda_args = List(args[1..].to_vec());
let lambda_params = List(parameters.to_vec());
let mut new_env = environment::extend(lambda_params, lambda_args, env);
for i in 0..(body.len() - 1) {
interpret(&body[i], &mut new_env);
}
interpret(&body[body.len() - 1], &mut new_env)
}
Procedure {
parameters,
body,
env,
} => {
let lambda_args = List(args[1..].to_vec());
let lambda_params = List(parameters.to_vec());
let mut proc_env = env.clone();
let mut new_env = environment::extend(lambda_params, lambda_args, &mut proc_env);
for i in 0..(body.len() - 1) {
interpret(&body[i], &mut new_env);
}
// This is my attempt to implement tail-call optimiation.
interpret(&body[body.len() - 1], &mut new_env)
}
_ => panic!("INTERPRET-APPLICATION -- not-aplicable {:?}", args[0]),
}
}
pub fn interpret(exp: &Datum, env: &mut Env) -> Datum {
match exp {
Boolean(_) => exp.clone(),
Number(_) => exp.clone(),
String(_) => exp.clone(),
Symbol(_) => environment::lookup_variable_value(exp, env),
List(v) if has_tag(v, "if") => interpret_if(v, env),
List(v) if has_tag(v, "set!") => interpret_set(v, env),
List(v) if has_tag(v, "define") => interpret_define(v, env),
List(v) if has_tag(v, "cond") => interpret_cond(v, env),
List(v) if has_tag(v, "lambda") => interpret_lambda(v, env),
List(v) if has_tag(v, "let") => interpret(&transform_let_to_lambda(v), env),
List(v) if has_tag(v, "begin") => interpret_begin(v, env),
List(v) if has_tag(v, "quote") => v[1].clone(),
List(v) => interpret_application(v, env),
Lambda { .. } => exp.clone(),
Unspecified => Unspecified,
_ => panic!("unknown-expression {:?}", exp),
}
}

View File

@@ -3,6 +3,7 @@ pub enum Token {
Identifier(String), Identifier(String),
Boolean(bool), Boolean(bool),
Number(i64), Number(i64),
String(String),
LeftRoundBracket, LeftRoundBracket,
RightRoundBracket, RightRoundBracket,
Quote, Quote,
@@ -33,6 +34,8 @@ fn scan(code: &str, mut ix: usize, mut tokens: Tokens) -> Tokens {
return scan_boolean(code, ix, tokens); return scan_boolean(code, ix, tokens);
} else if c == ';' { } else if c == ';' {
return scan_comment(code, ix, tokens); return scan_comment(code, ix, tokens);
} else if c == '"' {
return scan_string(code, ix, tokens);
} else if c.is_ascii_digit() { } else if c.is_ascii_digit() {
return scan_number(code, ix, tokens); return scan_number(code, ix, tokens);
} else if c.is_ascii_alphabetic() || SPECIAL_INITIAL.contains(&c) { } else if c.is_ascii_alphabetic() || SPECIAL_INITIAL.contains(&c) {
@@ -70,12 +73,12 @@ fn scan_boolean(code: &str, ix: usize, mut tokens: Tokens) -> Tokens {
"#t" => { "#t" => {
let token = Token::Boolean(true); let token = Token::Boolean(true);
tokens.push(token); tokens.push(token);
}, }
"#f" => { "#f" => {
let token = Token::Boolean(false); let token = Token::Boolean(false);
tokens.push(token); tokens.push(token);
} }
_ => panic!("Expected boolean but got {}", c) _ => panic!("Expected boolean but got {}", c),
} }
return scan(code, ix + 2, tokens); return scan(code, ix + 2, tokens);
} }
@@ -95,6 +98,39 @@ fn scan_comment(code: &str, mut ix: usize, tokens: Tokens) -> Tokens {
scan(code, ix, tokens) scan(code, ix, tokens)
} }
fn scan_string(code: &str, mut ix: usize, mut tokens: Tokens) -> Tokens {
let mut s = String::new();
let mut chars = code[ix..].chars();
let c = chars.next().unwrap();
assert!(c == '"');
ix += 1;
while let Some(c) = chars.next() {
ix += 1;
if c == '"' {
break;
} else if c == '\\' {
ix += 1;
if let Some(next_c) = chars.next() {
let escaped_c = match next_c {
'n' => '\n',
'"' => '"',
'\\' => '\\',
_ => panic!("SCAN-STRING -- cannot escape {}", next_c),
};
s.push(escaped_c);
} else {
panic!("SCAN-STRING -- found escape at eof")
}
} else {
s.push(c);
}
}
let token = Token::String(s);
tokens.push(token);
scan(code, ix, tokens)
}
fn scan_number(code: &str, mut ix: usize, mut tokens: Tokens) -> Tokens { fn scan_number(code: &str, mut ix: usize, mut tokens: Tokens) -> Tokens {
let start_ix = ix; let start_ix = ix;
let mut chars = code[ix..].chars(); let mut chars = code[ix..].chars();
@@ -135,6 +171,4 @@ const SPECIAL_INITIAL: &[char] = &[
'!', '$', '%', '&', '*', '/', ':', '<', '=', '>', '?', '^', '_', '~', '+', '-', '!', '$', '%', '&', '*', '/', ':', '<', '=', '>', '?', '^', '_', '~', '+', '-',
]; ];
const SPECIAL_SUBSEQUENT: &[char] = &[ const SPECIAL_SUBSEQUENT: &[char] = &['+', '-', '.', '@', '!', '?'];
'+', '-', '.', '@',
];

View File

@@ -1,9 +1,64 @@
mod environment;
mod interpreter;
mod lexer; mod lexer;
mod parser; mod parser;
mod primitives;
fn main() { use std::env;
let scm_code = "(+ a (* 32 b) c #t #f)"; use std::fs;
use std::io;
use std::io::prelude::*;
use std::path::Path;
fn repl() {
let mut env = environment::get_global_environment();
loop {
let mut input = String::new();
print!(" ]=> ");
io::stdout().flush().ok().expect("Could not flush stdout");
match io::stdin().read_line(&mut input) {
Ok(_) => {
let tokens = lexer::read(&input);
let datum = parser::parse(tokens);
let result = interpreter::interpret(&datum, &mut env);
println!("{:?}", result);
}
Err(error) => println!("error: {}", error),
}
}
}
fn interpret_code(scm_code: &str) {
let tokens = lexer::read(scm_code); let tokens = lexer::read(scm_code);
let datum = parser::parse(tokens); let datum = parser::parse(tokens);
println!("{:?}", datum); let mut env = environment::get_global_environment();
let result = interpreter::interpret(&datum, &mut env);
println!("{:?}", result);
}
fn interpret_file(filename: &Path) {
match fs::read_to_string(filename) {
Ok(scm_code) => {
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);
}
Err(error) => println!("error: {} {:?}", error, filename),
}
}
fn main() {
let args: Vec<String> = env::args().collect();
let scm_code = "";
if args.len() == 2 {
let arg_path = Path::new(&args[1]);
interpret_file(arg_path);
} else if scm_code.len() > 0 {
interpret_code(scm_code);
} else {
repl();
}
} }

View File

@@ -1,13 +1,94 @@
use crate::environment::Env;
use crate::lexer::Token; use crate::lexer::Token;
use crate::lexer::Tokens; use crate::lexer::Tokens;
use std::cmp::Ordering;
use std::fmt;
#[derive(Debug, Clone)]
#[derive(Debug)]
pub enum Datum { pub enum Datum {
Boolean(bool), Boolean(bool),
Number(i64), Number(i64),
String(String),
Symbol(String), Symbol(String),
Pair(Box<(Datum, Datum)>),
List(Vec<Datum>), List(Vec<Datum>),
Primitive(fn(Vec<Datum>) -> Datum),
Lambda {
parameters: Vec<Datum>,
body: Vec<Datum>,
},
Procedure {
parameters: Vec<Datum>,
body: Vec<Datum>,
env: Env,
},
Unspecified,
}
pub fn make_symbol(s: &str) -> Datum {
Datum::Symbol(s.to_string())
}
impl Default for Datum {
fn default() -> Self {
Datum::Unspecified
}
}
impl PartialEq for Datum {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Datum::Boolean(b1), Datum::Boolean(b2)) => b1 == b2,
(Datum::Number(n1), Datum::Number(n2)) => n1 == n2,
_ => false,
}
}
}
impl fmt::Display for Datum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
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),
Datum::Pair(p) => write!(f, "({} . {})", p.0, p.1),
Datum::List(v) => {
let mut s = String::new();
s.push_str("(");
for e in v {
let es = format!("{} ", e);
if es != "list " {
s.push_str(&es);
}
}
s.pop(); // remove last space
s.push_str(")");
write!(f, "{}", s)
}
_ => panic!("DISPLAY -- cannot print {:?}", self),
}
}
}
impl PartialOrd for Datum {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(Datum::Boolean(b1), Datum::Boolean(b2)) => Some(b1.cmp(b2)),
(Datum::Number(n1), Datum::Number(n2)) => Some(n1.cmp(n2)),
_ => None,
}
}
}
impl Datum {
pub fn as_str(&self) -> Option<std::string::String> {
match self {
Datum::Symbol(s) => Some(s.to_string()),
_ => None,
}
}
} }
pub fn parse(tokens: Tokens) -> Datum { pub fn parse(tokens: Tokens) -> Datum {
@@ -20,20 +101,31 @@ fn parse_datum(tokens: &Tokens, ix: usize) -> (Datum, usize) {
Token::Identifier(s) => (Datum::Symbol(s.to_string()), ix + 1), Token::Identifier(s) => (Datum::Symbol(s.to_string()), ix + 1),
Token::Boolean(b) => (Datum::Boolean(*b), ix + 1), Token::Boolean(b) => (Datum::Boolean(*b), ix + 1),
Token::Number(n) => (Datum::Number(*n), ix + 1), Token::Number(n) => (Datum::Number(*n), ix + 1),
Token::String(s) => (Datum::String(s.to_string()), ix + 1),
Token::Quote => parse_quote(tokens, ix + 1),
Token::LeftRoundBracket => parse_list(tokens, ix + 1), Token::LeftRoundBracket => parse_list(tokens, ix + 1),
_ => panic!("Unexpected token {:?}", tokens[ix]) _ => panic!("Unexpected token {:?}", tokens[ix]),
} }
} }
pub fn parse_quote(tokens: &Tokens, ix: usize) -> (Datum, usize) {
let mut datums = vec![make_symbol("quote")];
let (datum, ix) = parse_datum(tokens, ix);
datums.push(datum);
(Datum::List(datums), ix)
}
pub fn parse_list(tokens: &Tokens, mut ix: usize) -> (Datum, usize) { pub fn parse_list(tokens: &Tokens, mut ix: usize) -> (Datum, usize) {
let mut datums = vec![]; let mut datums = vec![];
// FIXME: will crash when RightRoundBracket is missing while ix < tokens.len() && tokens[ix] != Token::RightRoundBracket {
while tokens[ix] != Token::RightRoundBracket {
let (datum, new_ix) = parse_datum(tokens, ix); let (datum, new_ix) = parse_datum(tokens, ix);
datums.push(datum); datums.push(datum);
ix = new_ix; ix = new_ix;
} }
if ix == tokens.len() {
panic!("PARSE_LIST -- missing closing bracket {:?}", tokens)
}
(Datum::List(datums), ix + 1) (Datum::List(datums), ix + 1)
} }

220
src/primitives.rs Normal file
View File

@@ -0,0 +1,220 @@
use crate::interpreter::has_tag;
use crate::interpreter::is_true;
use crate::parser::make_symbol;
use crate::parser::Datum;
use crate::parser::Datum::*;
use std::io;
use std::io::prelude::*;
use std::mem;
pub fn add(mut args: Vec<Datum>) -> Datum {
let mut r = 0;
for i in 1..args.len() {
let datum = mem::take(&mut args[i]);
if let Number(n) = datum {
r += n;
} else {
panic!("ADD -- not-all-args-are-numbers {:?}", args)
}
}
Number(r)
}
pub fn mul(mut args: Vec<Datum>) -> Datum {
let mut r = 1;
for i in 1..args.len() {
let datum = mem::take(&mut args[i]);
if let Number(n) = datum {
r *= n;
} else {
panic!("MUL -- not-all-args-are-numbers {:?}", args)
}
}
Number(r)
}
pub fn sub(mut args: Vec<Datum>) -> Datum {
if args.len() == 2 {
let datum = mem::take(&mut args[1]);
if let Number(n) = datum {
return Number(-n);
} else {
panic!("SUB -- not-all-args-are-numbers {:?}", args)
}
}
let mut r;
let datum = mem::take(&mut args[1]);
if let Number(n) = datum {
r = n;
} else {
panic!("SUB -- not-all-args-are-numbers {:?}", args)
}
for i in 2..args.len() {
let datum = mem::take(&mut args[i]);
if let Number(n) = datum {
r -= n;
} else {
panic!("SUB -- not-all-args-are-numbers {:?}", args)
}
}
Number(r)
}
pub fn eq(args: Vec<Datum>) -> Datum {
if args.len() == 1 {
Boolean(true)
} else {
let first = &args[1];
for i in 2..args.len() {
let current = &args[i];
if first != current {
return Boolean(false);
}
}
Boolean(true)
}
}
pub fn lt(args: Vec<Datum>) -> Datum {
if args.len() == 1 {
Boolean(true)
} else {
for i in 1..(args.len() - 1) {
if &args[i] >= &args[i + 1] {
return Boolean(false);
}
}
Boolean(true)
}
}
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]);
}
io::stdout().flush().ok().expect("Could not flush stdout");
Unspecified
}
pub fn list(mut args: Vec<Datum>) -> Datum {
args[0] = make_symbol("list");
Datum::List(args)
}
pub fn cons(mut args: Vec<Datum>) -> Datum {
if args.len() != 3 {
panic!("CONS -- two args expected {:?}", args);
}
let a = std::mem::take(&mut args[1]);
let b = std::mem::take(&mut args[2]);
match b {
List(b_vec) if has_tag(&b_vec, "list") => {
let mut r_vec = vec![make_symbol("list"), a];
r_vec.append(&mut b_vec[1..].to_vec());
Datum::List(r_vec)
}
_ => Datum::Pair(Box::new((a, b))),
}
}
pub fn car(mut args: Vec<Datum>) -> Datum {
if args.len() != 2 {
panic!("CAR -- expected a single arg {:?}", args);
}
let a = std::mem::take(&mut args[1]);
match a {
List(mut v) if has_tag(&v, "list") => std::mem::take(&mut v[1]),
Pair(p) => p.0,
_ => panic!("CAR -- could not get car {:?}", a),
}
}
pub fn cdr(mut args: Vec<Datum>) -> Datum {
if args.len() != 2 {
panic!("CDR -- expected a single arg {:?}", args);
}
let a = std::mem::take(&mut args[1]);
match a {
List(v) if has_tag(&v, "list") => {
let mut v_cdr = v[1..].to_vec();
v_cdr[0] = make_symbol("list");
List(v_cdr)
}
Pair(p) => p.1,
_ => panic!("CAR -- could not get car {:?}", a),
}
}
pub fn pair(args: Vec<Datum>) -> Datum {
if args.len() != 2 {
panic!("PAIR? -- expected a single arg {:?}", args);
}
match &args[1] {
List(v) if has_tag(&v, "list") => {
if v.len() == 1 {
Boolean(false)
} else {
Boolean(true)
}
}
Pair(_) => Boolean(true),
_ => Boolean(false),
}
}
pub fn null(args: Vec<Datum>) -> Datum {
if args.len() != 2 {
panic!("PAIR? -- expected a single arg {:?}", args);
}
match &args[1] {
List(v) if has_tag(&v, "list") => {
if v.len() == 1 {
Boolean(true)
} else {
Boolean(false)
}
}
Pair(_) => Boolean(false),
_ => Boolean(false),
}
}

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
)