Compare commits
19 Commits
65444afe14
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| af7fc21cde | |||
| 9d0003ff14 | |||
| 7b5435141a | |||
| b3181602d6 | |||
| f8b8019682 | |||
| 7d8a256c76 | |||
| 2e67e1220c | |||
| a7caac30f9 | |||
| 3b3ab96f7d | |||
| 1da08c0a43 | |||
| e070441c31 | |||
| 3ae5ccb64c | |||
| aaa93d8593 | |||
| 390c5d1b48 | |||
| b0968ba498 | |||
| 794cad2219 | |||
| 56f5426426 | |||
| b0bf78a3bd | |||
| f4c58cfa02 |
@@ -1,3 +1,9 @@
|
||||
# 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),
|
||||
Boolean(bool),
|
||||
Number(i64),
|
||||
String(String),
|
||||
LeftRoundBracket,
|
||||
RightRoundBracket,
|
||||
Quote,
|
||||
@@ -33,6 +34,8 @@ fn scan(code: &str, mut ix: usize, mut tokens: Tokens) -> Tokens {
|
||||
return scan_boolean(code, ix, tokens);
|
||||
} else if c == ';' {
|
||||
return scan_comment(code, ix, tokens);
|
||||
} else if c == '"' {
|
||||
return scan_string(code, ix, tokens);
|
||||
} else if c.is_ascii_digit() {
|
||||
return scan_number(code, ix, tokens);
|
||||
} 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" => {
|
||||
let token = Token::Boolean(true);
|
||||
tokens.push(token);
|
||||
},
|
||||
}
|
||||
"#f" => {
|
||||
let token = Token::Boolean(false);
|
||||
tokens.push(token);
|
||||
}
|
||||
_ => panic!("Expected boolean but got {}", c)
|
||||
_ => panic!("Expected boolean but got {}", c),
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
let start_ix = ix;
|
||||
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 parser;
|
||||
mod primitives;
|
||||
|
||||
fn main() {
|
||||
let scm_code = "(+ a (* 32 b) c #t #f)";
|
||||
use std::env;
|
||||
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 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::Tokens;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Datum {
|
||||
Boolean(bool),
|
||||
Number(i64),
|
||||
String(String),
|
||||
Symbol(String),
|
||||
Pair(Box<(Datum, 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 {
|
||||
@@ -20,20 +101,31 @@ fn parse_datum(tokens: &Tokens, ix: usize) -> (Datum, usize) {
|
||||
Token::Identifier(s) => (Datum::Symbol(s.to_string()), ix + 1),
|
||||
Token::Boolean(b) => (Datum::Boolean(*b), 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),
|
||||
_ => 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) {
|
||||
let mut datums = vec![];
|
||||
|
||||
// FIXME: will crash when RightRoundBracket is missing
|
||||
while tokens[ix] != Token::RightRoundBracket {
|
||||
while ix < tokens.len() && tokens[ix] != Token::RightRoundBracket {
|
||||
let (datum, new_ix) = parse_datum(tokens, ix);
|
||||
datums.push(datum);
|
||||
ix = new_ix;
|
||||
}
|
||||
if ix == tokens.len() {
|
||||
panic!("PARSE_LIST -- missing closing bracket {:?}", tokens)
|
||||
}
|
||||
|
||||
(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