From 2e67e1220cc855f75c65ec0e3a172833f8fb1ef2 Mon Sep 17 00:00:00 2001 From: Felix Martin Date: Sun, 30 May 2021 22:49:31 -0400 Subject: [PATCH] Implement display and string support resolves #10 --- src/environment.rs | 4 ++++ src/interpreter.rs | 9 +++++++-- src/lexer.rs | 36 ++++++++++++++++++++++++++++++++++++ src/parser.rs | 2 ++ src/primitives.rs | 16 ++++++++++++++++ 5 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/environment.rs b/src/environment.rs index 94b2bf5..28d2105 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -20,6 +20,10 @@ 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("display"), + Datum::Procedure(primitives::display), + ), ], outer: None, } diff --git a/src/interpreter.rs b/src/interpreter.rs index 4192fe2..798a579 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -83,6 +83,10 @@ fn interpret_define(args: &Vec, env: &mut Env) -> Datum { } } +fn interpret_cond(_vec: &Vec, _env: &mut Env) -> Datum { + Datum::Unspecified +} + fn interpret_begin(vec: &Vec, env: &mut Env) -> Datum { let mut r = Datum::Unspecified; for i in 1..vec.len() { @@ -127,16 +131,17 @@ pub fn interpret(exp: &Datum, env: &mut Env) -> Datum { match exp { Boolean(_) => exp.clone(), Number(_) => exp.clone(), - Unspecified => Unspecified, + 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") => panic!("cond-not-supported"), + List(v) if has_tag(v, "cond") => interpret_cond(v, env), List(v) if has_tag(v, "let") => panic!("let-not-supported"), 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), + Unspecified => Unspecified, _ => panic!("unknown-expression {:?}", exp), } } diff --git a/src/lexer.rs b/src/lexer.rs index ab19618..4625195 100644 --- a/src/lexer.rs +++ b/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) { @@ -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(); diff --git a/src/parser.rs b/src/parser.rs index 23f748e..b915e6f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -6,6 +6,7 @@ use std::cmp::Ordering; pub enum Datum { Boolean(bool), Number(i64), + String(String), Symbol(String), List(Vec), Procedure(fn(Vec) -> Datum), @@ -52,6 +53,7 @@ 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]), diff --git a/src/primitives.rs b/src/primitives.rs index 0cd7744..90b9e7c 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -1,5 +1,7 @@ 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 { @@ -84,3 +86,17 @@ pub fn lt(args: Vec) -> Datum { Boolean(true) } } + +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[1]), + }; + } + io::stdout().flush().ok().expect("Could not flush stdout"); + Unspecified +}