Compare commits
21 Commits
5cc7255168
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| af7fc21cde | |||
| 9d0003ff14 | |||
| 7b5435141a | |||
| b3181602d6 | |||
| f8b8019682 | |||
| 7d8a256c76 | |||
| 2e67e1220c | |||
| a7caac30f9 | |||
| 3b3ab96f7d | |||
| 1da08c0a43 | |||
| e070441c31 | |||
| 3ae5ccb64c | |||
| aaa93d8593 | |||
| 390c5d1b48 | |||
| b0968ba498 | |||
| 794cad2219 | |||
| 56f5426426 | |||
| b0bf78a3bd | |||
| f4c58cfa02 | |||
| 65444afe14 | |||
| 6744b68385 |
@@ -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
118
src/environment.rs
Normal 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
206
src/interpreter.rs
Normal 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/lexer.rs
44
src/lexer.rs
@@ -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] = &['+', '-', '.', '@', '!', '?'];
|
||||||
'+', '-', '.', '@',
|
|
||||||
];
|
|
||||||
|
|||||||
61
src/main.rs
61
src/main.rs
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
102
src/parser.rs
102
src/parser.rs
@@ -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
220
src/primitives.rs
Normal 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
43
test.scm
Normal 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
|
||||||
|
)
|
||||||
|
|
||||||
Reference in New Issue
Block a user