diff --git a/src/env.rs b/src/env.rs new file mode 100644 index 0000000..46ec888 --- /dev/null +++ b/src/env.rs @@ -0,0 +1,30 @@ +use std::collections::HashMap; + +use crate::{Symbol, Value}; + +#[derive(Default, Debug, Clone)] +pub struct Environment { + symbols: HashMap, + outer: Box>, +} + +impl Environment { + pub fn new() -> Self { + Self { + outer: Box::new(None), + ..Default::default() + } + } + + pub fn insert_symbol(&mut self, symbol: Symbol, value: Value) { + self.symbols.insert(symbol, value); + } + + pub fn get_symbol(&self, symbol: &Symbol) -> &Value { + if let Some(value) = self.symbols.get(symbol) { + value + } else { + &Value::Nil + } + } +} diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..706bd5c --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,8 @@ +macro_rules! insert_symbol { + // Takes the env and a symbol in &str form, then it takes a variable list, + // of arg types and then a fn to run over them. + (&env:expr, &symbol:expr) => { + // The macro will expand into the contents of this block. + println!("Hello!") + }; +} diff --git a/src/main.rs b/src/main.rs index 269ffb7..e8a0469 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,11 +4,14 @@ use color_eyre::{ eyre::{eyre, ContextCompat}, Result, }; +use env::Environment; use printer::print; use reader::read; use regex::Regex; use rustyline::{config::Configurer, error::ReadlineError, Editor}; +pub mod env; +pub mod macros; pub mod printer; pub mod reader; pub mod types; @@ -19,28 +22,55 @@ fn main() -> Result<()> { if rl.load_history(".mal-history").is_err() { eprintln!("No previous history."); } - rl.set_edit_mode(rustyline::EditMode::Vi); - let mut env = Environment::default(); - env.symbols.insert( + + let mut env = Environment::new(); + env.insert_symbol( Symbol::from("+"), Value::func(|args| { - let total = args + let nums: Vec = args .iter() .map(|a| match a { Value::Number(a) => Ok(*a), _ => Err(eyre!("Should be a number")), }) - .sum(); - total + .flatten() + .collect(); + let total = nums + .into_iter() + .reduce(|acc, e| acc + e) + .expect("shouldn't fail"); + Ok(Value::from(total)) }), ); + env.insert_symbol( + Symbol::from("-"), + Value::func(|args| { + let nums: Vec = args + .iter() + .map(|a| match a { + Value::Number(a) => Ok(*a), + _ => Err(eyre!("Should be a number")), + }) + .flatten() + .collect(); + let total = nums + .into_iter() + .reduce(|acc, n| acc - n) + .expect("shouldn't fail"); + Ok(Value::from(total)) + }), + ); + loop { let entry = rl.readline(">> "); match entry { Ok(line) => { rl.add_history_entry(line.as_str())?; - read_eval_print(line.as_str(), &env)?; + match read_eval_print(line.as_str(), &env) { + Ok(s) => println!("{s}"), + Err(e) => eprintln!("{e}"), + } } Err(ReadlineError::Interrupted) => { println!("CTRL-C"); @@ -59,35 +89,30 @@ fn main() -> Result<()> { Ok(()) } -#[derive(Default, Debug, Clone)] -pub struct Environment { - symbols: HashMap, - outer: Box, -} - -fn read_eval_print(str: &str, env: &Environment) -> Result<()> { - print(&eval(&read(str), env).unwrap()); - Ok(()) +fn read_eval_print(str: &str, env: &Environment) -> Result { + Ok(print(&eval(&read(str), env)?)) } fn eval(expr: &Value, env: &Environment) -> Result { match expr { Value::Symbol(s) => { - if let Some(symbol) = env.symbols.get(&s) { - Ok(symbol.clone()) - } else { - Err(eyre!("No symbol here")) - } + let symbol = env.get_symbol(s).to_owned(); + Ok(symbol) } Value::List(list) => { let mut args = vec![]; - let func = &list[0]; - for value in 1..list.len() { - if let Ok(return_value) = eval(&list[value], env) { - args.push(return_value) + let first = &list[0]; + if let Value::List(_) = first { + eval(first, env) + } else { + let first = eval(first, env)?; + for value in list.iter().skip(1) { + if let Ok(return_value) = eval(value, env) { + args.push(return_value) + } } + first.apply(args) } - func.apply(args) } value => Ok(value.to_owned()), } diff --git a/src/types.rs b/src/types.rs index 202be28..b553a70 100644 --- a/src/types.rs +++ b/src/types.rs @@ -88,6 +88,18 @@ impl From<&String> for Value { } } +impl From for Value { + fn from(num: i64) -> Self { + Self::Number(num) + } +} + +impl From<&i64> for Value { + fn from(num: &i64) -> Self { + Self::Number(*num) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Symbol(pub String);