diff --git a/src/environment.rs b/src/environment.rs index 75a2b14..ed219c5 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -21,6 +21,12 @@ pub fn get_global_environment<'a>() -> Env<'a> { (make_symbol("-"), Datum::Procedure(primitives::sub)), (make_symbol("="), Datum::Procedure(primitives::eq)), (make_symbol("<"), Datum::Procedure(primitives::lt)), + (make_symbol("list"), Datum::Procedure(primitives::list)), + (make_symbol("cons"), Datum::Procedure(primitives::cons)), + (make_symbol("car"), Datum::Procedure(primitives::car)), + (make_symbol("cdr"), Datum::Procedure(primitives::cdr)), + (make_symbol("pair?"), Datum::Procedure(primitives::pair)), + (make_symbol("null?"), Datum::Procedure(primitives::null)), ( make_symbol("display"), Datum::Procedure(primitives::display), diff --git a/src/interpreter.rs b/src/interpreter.rs index 534f0f8..9a36427 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -13,7 +13,7 @@ fn is_true(exp: &Datum) -> bool { } } -fn has_tag(vec: &Vec, tag: &str) -> bool { +pub fn has_tag(vec: &Vec, tag: &str) -> bool { if vec.len() == 0 { return false; } @@ -127,6 +127,7 @@ fn interpret_application(vec: &Vec, env: &mut Env) -> Datum { for i in 0..(seq.len() - 1) { interpret(&seq[i], &mut new_env); } + // XXX: not tail-call optimized interpret(&seq[seq.len() - 1], &mut new_env) } else { panic!("INTERPRET-APPLICATION -- no-list {:?}", lambda_body) diff --git a/src/lexer.rs b/src/lexer.rs index 4625195..786c321 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -171,4 +171,4 @@ const SPECIAL_INITIAL: &[char] = &[ '!', '$', '%', '&', '*', '/', ':', '<', '=', '>', '?', '^', '_', '~', '+', '-', ]; -const SPECIAL_SUBSEQUENT: &[char] = &['+', '-', '.', '@', '!']; +const SPECIAL_SUBSEQUENT: &[char] = &['+', '-', '.', '@', '!', '?']; diff --git a/src/main.rs b/src/main.rs index 363ed59..6515dde 100644 --- a/src/main.rs +++ b/src/main.rs @@ -54,11 +54,11 @@ fn main() { // let scm_code = "(begin (define (f n) (if (= n 1) 1 (* n (f (- n 1))))) (f 10))"; let scm_code = ""; - if scm_code.len() > 0 { - interpret_code(scm_code); - } else if args.len() == 2 { + 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(); } diff --git a/src/parser.rs b/src/parser.rs index b915e6f..39341a0 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,6 +1,7 @@ use crate::lexer::Token; use crate::lexer::Tokens; use std::cmp::Ordering; +use std::fmt; #[derive(Debug, Clone)] pub enum Datum { @@ -8,6 +9,7 @@ pub enum Datum { Number(i64), String(String), Symbol(String), + Pair(Box<(Datum, Datum)>), List(Vec), Procedure(fn(Vec) -> Datum), Unspecified, @@ -33,6 +35,32 @@ 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::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 { match (self, other) { diff --git a/src/primitives.rs b/src/primitives.rs index 968a9e6..eb86d56 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -1,3 +1,5 @@ +use crate::interpreter::has_tag; +use crate::parser::make_symbol; use crate::parser::Datum; use crate::parser::Datum::*; use std::io; @@ -89,14 +91,91 @@ pub fn lt(args: Vec) -> Datum { pub fn display(args: Vec) -> Datum { for i in 1..args.len() { - match &args[i] { - Boolean(b) => print!("{}", b), - Number(n) => print!("{}", n), - String(s) => print!("{}", s), - List(v) => print!("{:?}", v), - _ => panic!("DISPLAY -- cannot-print {:?}", args[i]), - }; + print!("{}", args[i]); } io::stdout().flush().ok().expect("Could not flush stdout"); Unspecified } + +pub fn list(mut args: Vec) -> Datum { + args[0] = make_symbol("list"); + Datum::List(args) +} + +pub fn cons(mut args: Vec) -> 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 { + 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 { + 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 { + 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 { + 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), + } +}