From 53ed6426c621c40184c24334c2a8aa156863c37a Mon Sep 17 00:00:00 2001 From: Able Date: Fri, 15 Nov 2024 04:29:58 -0600 Subject: [PATCH] small but full lisp --- assets/start.lisp | 0 src/environ.rs | 106 ++++++++++++++++++++--------- src/evaluate.rs | 169 ++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 160 +++++++++++++++++-------------------------- src/parser.rs | 94 ++++++++++++-------------- 5 files changed, 348 insertions(+), 181 deletions(-) create mode 100644 assets/start.lisp create mode 100644 src/evaluate.rs diff --git a/assets/start.lisp b/assets/start.lisp new file mode 100644 index 0000000..e69de29 diff --git a/src/environ.rs b/src/environ.rs index 0a315e2..ceb96f4 100644 --- a/src/environ.rs +++ b/src/environ.rs @@ -1,38 +1,78 @@ -use crate::Expr; -use crate::RispError; -use crate::HashMap; use crate::parse_list_of_floats; +use crate::Expr; +use crate::HashMap; +use crate::RispError; -pub -struct Environ { - pub data: HashMap, +macro_rules! ensure_tonicity { + ($check_fn:expr) => {{ + |args: &[Expr]| -> Result { + let floats = parse_list_of_floats(args)?; + let first = floats.first().ok_or(RispError::Reason( + "expected at least one number".to_string(), + ))?; + let rest = &floats[1..]; + fn f(prev: &f64, xs: &[f64]) -> bool { + match xs.first() { + Some(x) => $check_fn(prev, x) && f(x, &xs[1..]), + None => true, + } + }; + Ok(Expr::Bool(f(first, rest))) + } + }}; +} +pub fn env_get(k: &str, env: &Environ) -> Option { + match env.data.get(k) { + Some(exp) => Some(exp.clone()), + None => match &env.outer { + Some(outer_env) => env_get(k, &outer_env), + None => None, + }, + } } - -pub fn default_env() -> Environ { - let mut data: HashMap = HashMap::new(); - data.insert( - "+".to_string(), - Expr::Func( - |args: &[Expr]| -> Result { - let sum = parse_list_of_floats(args)?.iter().fold(0.0, |sum, a| sum + a); - - Ok(Expr::Number(sum)) - } - ) - ); - data.insert( - "-".to_string(), - Expr::Func( - |args: &[Expr]| -> Result { - let floats = parse_list_of_floats(args)?; - let first = *floats.first().ok_or(RispError::Reason("expected at least one number".to_string()))?; - let sum_of_rest = floats[1..].iter().fold(0.0, |sum, a| sum + a); - - Ok(Expr::Number(first - sum_of_rest)) - } - ) - ); - - Environ {data} +#[derive(Clone)] +pub struct Environ<'a> { + pub data: HashMap, + pub outer: Option<&'a Environ<'a>>, +} + +pub fn default_env<'a>() -> Environ<'a> { + let mut data: HashMap = HashMap::new(); + data.insert( + "+".to_string(), + Expr::Func(|args: &[Expr]| -> Result { + let sum = parse_list_of_floats(args)? + .iter() + .fold(0.0, |sum, a| sum + a); + + Ok(Expr::Number(sum)) + }), + ); + data.insert( + "-".to_string(), + Expr::Func(|args: &[Expr]| -> Result { + let floats = parse_list_of_floats(args)?; + let first = *floats.first().ok_or(RispError::Reason( + "expected at least one number".to_string(), + ))?; + let sum_of_rest = floats[1..].iter().fold(0.0, |sum, a| sum + a); + + Ok(Expr::Number(first - sum_of_rest)) + }), + ); + + data.insert("=".to_string(), Expr::Func(ensure_tonicity!(|a, b| a == b))); + data.insert(">".to_string(), Expr::Func(ensure_tonicity!(|a, b| a > b))); + data.insert( + ">=".to_string(), + Expr::Func(ensure_tonicity!(|a, b| a >= b)), + ); + data.insert("<".to_string(), Expr::Func(ensure_tonicity!(|a, b| a < b))); + data.insert( + "<=".to_string(), + Expr::Func(ensure_tonicity!(|a, b| a <= b)), + ); + + Environ { data, outer: None } } diff --git a/src/evaluate.rs b/src/evaluate.rs new file mode 100644 index 0000000..abdbfed --- /dev/null +++ b/src/evaluate.rs @@ -0,0 +1,169 @@ +use crate::environ::env_get; +use crate::Environ; +use crate::Expr; +use crate::HashMap; +use crate::RLambda; +use crate::Rc; +use crate::RispError; + +pub fn eval(exp: &Expr, env: &mut Environ) -> Result { + match exp { + Expr::Symbol(k) => { + env_get(k, env).ok_or(RispError::Reason(format!("unexpected symbol k='{}'", k))) + } + Expr::Bool(_a) => Ok(exp.clone()), + Expr::Number(_a) => Ok(exp.clone()), + + Expr::List(list) => { + let first_form = list + .first() + .ok_or(RispError::Reason("expected a non-empty list".to_string()))?; + let arg_forms = &list[1..]; + match eval_built_in_form(first_form, arg_forms, env) { + Some(res) => res, + None => { + let first_eval = eval(first_form, env)?; + match first_eval { + Expr::Func(f) => f(&eval_forms(arg_forms, env)?), + Expr::Lambda(lambda) => { + let new_env = &mut env_for_lambda(lambda.params_exp, arg_forms, env)?; + eval(&lambda.body_exp, new_env) + } + _ => Err(RispError::Reason( + "first form must be a function".to_string(), + )), + } + } + } + } + Expr::Func(_) => Err(RispError::Reason("unexpected form".to_string())), + Expr::Lambda(_) => Err(RispError::Reason("unexpected form".to_string())), + } +} + +fn eval_forms(arg_forms: &[Expr], env: &mut Environ) -> Result, RispError> { + arg_forms.iter().map(|x| eval(x, env)).collect() +} + +fn env_for_lambda<'a>( + params: Rc, + arg_forms: &[Expr], + outer_env: &'a mut Environ, +) -> Result, RispError> { + let ks = parse_list_of_symbol_strings(params)?; + if ks.len() != arg_forms.len() { + return Err(RispError::Reason(format!( + "expected {} arguments, got {}", + ks.len(), + arg_forms.len() + ))); + } + let vs = eval_forms(arg_forms, outer_env)?; + let mut data: HashMap = HashMap::new(); + for (k, v) in ks.iter().zip(vs.iter()) { + data.insert(k.clone(), v.clone()); + } + Ok(Environ { + data, + outer: Some(outer_env), + }) +} + +fn parse_list_of_symbol_strings(form: Rc) -> Result, RispError> { + let list = match form.as_ref() { + Expr::List(s) => Ok(s.clone()), + _ => Err(RispError::Reason( + "expected args form to be a list".to_string(), + )), + }?; + list.iter() + .map(|x| match x { + Expr::Symbol(s) => Ok(s.clone()), + _ => Err(RispError::Reason( + "expected symbols in the argument list".to_string(), + )), + }) + .collect() +} + +fn eval_built_in_form( + exp: &Expr, + arg_forms: &[Expr], + env: &mut Environ, +) -> Option> { + match exp { + Expr::Symbol(s) => match s.as_ref() { + "if" => Some(eval_if_args(arg_forms, env)), + "def" => Some(eval_def_args(arg_forms, env)), + "fn" => Some(eval_lambda_args(arg_forms)), + _ => None, + }, + + _ => None, + } +} + +fn eval_lambda_args(arg_forms: &[Expr]) -> Result { + let params_exp = arg_forms + .first() + .ok_or(RispError::Reason("expected args form".to_string()))?; + let body_exp = arg_forms + .get(1) + .ok_or(RispError::Reason("expected second form".to_string()))?; + if arg_forms.len() > 2 { + return Err(RispError::Reason( + "fn definition can only have two forms ".to_string(), + )); + } + + Ok(Expr::Lambda(RLambda { + body_exp: Rc::new(body_exp.clone()), + params_exp: Rc::new(params_exp.clone()), + })) +} + +fn eval_if_args(arg_forms: &[Expr], env: &mut Environ) -> Result { + let test_form = arg_forms + .first() + .ok_or(RispError::Reason("expected test form".to_string()))?; + let test_eval = eval(test_form, env)?; + match test_eval { + Expr::Bool(b) => { + let form_idx = if b { 1 } else { 2 }; + let res_form = arg_forms + .get(form_idx) + .ok_or(RispError::Reason(format!("expected form idx={}", form_idx)))?; + let res_eval = eval(res_form, env); + + res_eval + } + _ => Err(RispError::Reason(format!( + "unexpected test form='{}'", + test_form.to_string() + ))), + } +} + +fn eval_def_args(arg_forms: &[Expr], env: &mut Environ) -> Result { + let first_form = arg_forms + .first() + .ok_or(RispError::Reason("expected first form".to_string()))?; + let first_str = match first_form { + Expr::Symbol(s) => Ok(s.clone()), + _ => Err(RispError::Reason( + "expected first form to be a symbol".to_string(), + )), + }?; + let second_form = arg_forms + .get(1) + .ok_or(RispError::Reason("expected second form".to_string()))?; + if arg_forms.len() > 2 { + return Err(RispError::Reason( + "def can only have two forms ".to_string(), + )); + } + let second_eval = eval(second_form, env)?; + env.data.insert(first_str, second_eval); + + Ok(first_form.clone()) +} diff --git a/src/main.rs b/src/main.rs index 5b22136..ef73873 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,132 +1,98 @@ -use std::io; +use std::collections::HashMap; use std::fmt; +use std::io; +use std::io::Write; use std::num::ParseFloatError; -use std::collections::HashMap;use std::io::Write; +use std::rc::Rc; mod environ; -use environ::Environ; use environ::default_env; +use environ::Environ; mod parser; use parser::parse; use parser::parse_eval; use parser::parse_list_of_floats; - +mod evaluate; #[derive(Debug)] -enum RispError{ +enum RispError { Reason(String), } - #[derive(Clone)] enum Expr { + Bool(bool), Symbol(String), Number(f64), List(Vec), - Func(fn(&[Expr]) -> Result), // bam + Func(fn(&[Expr]) -> Result), + Lambda(RLambda), +} + +#[derive(Clone)] +struct RLambda { + params_exp: Rc, + body_exp: Rc, } impl fmt::Display for Expr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let str = match self { - Expr::Symbol(s) => s.clone(), - Expr::Number(n) => n.to_string(), - Expr::List(list) => { - let xs: Vec = list - .iter() - .map(|x| x.to_string()) - .collect(); - format!("({})", xs.join(",")) - }, - Expr::Func(_) => "Function {}".to_string(), - }; - - write!(f, "{}", str) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use Expr::*; + let str = match self { + Bool(a) => a.to_string(), + Symbol(s) => s.clone(), + Number(n) => n.to_string(), + List(list) => { + let xs: Vec = list.iter().map(|x| x.to_string()).collect(); + format!("({})", xs.join(",")) + } + Func(_) => "Function {}".to_string(), + Lambda(_) => "Lambda {}".to_string(), + }; + + write!(f, "{}", str) + } } - - -fn eval(exp: &Expr, env: &mut Environ) -> Result { - match exp { - Expr::Symbol(k) => - env.data.get(k) - .ok_or( - RispError::Reason( - format!("unexpected symbol k='{}'", k) - ) - ) - .map(|x| x.clone()) - , - Expr::Number(_a) => Ok(exp.clone()), - Expr::List(list) => { - let first_form = list - .first() - .ok_or(RispError::Reason("expected a non-empty list".to_string()))?; - let arg_forms = &list[1..]; - let first_eval = eval(first_form, env)?; - match first_eval { - Expr::Func(f) => { - let args_eval = arg_forms - .iter() - .map(|x| eval(x, env)) - .collect::, RispError>>(); - f(&args_eval?) - }, - _ => Err( - RispError::Reason("first form must be a function".to_string()) - ), - } - }, - Expr::Func(_) => Err( - RispError::Reason("unexpected form".to_string()) - ), - } -} - - - fn read_seq<'a>(tokens: &'a [String]) -> Result<(Expr, &'a [String]), RispError> { - let mut res: Vec = vec![]; - let mut xs = tokens; - loop { - let (next_token, rest) = xs - .split_first() - .ok_or(RispError::Reason("could not find closing `)`".to_string())) - ?; - if next_token == ")" { - return Ok((Expr::List(res), rest)) // skip `)`, head to the token after + let mut res: Vec = vec![]; + let mut xs = tokens; + loop { + let (next_token, rest) = xs + .split_first() + .ok_or(RispError::Reason("could not find closing `)`".to_string()))?; + if next_token == ")" { + return Ok((Expr::List(res), rest)); // skip `)`, head to the token after + } + let (exp, new_xs) = parse(&xs)?; + res.push(exp); + xs = new_xs; } - let (exp, new_xs) = parse(&xs)?; - res.push(exp); - xs = new_xs; - } } - - fn slurp_expr() -> String { - let mut expr = String::new(); - - io::stdin().read_line(&mut expr) - .expect("Failed to read line"); - - expr + let mut expr = String::new(); + + io::stdin() + .read_line(&mut expr) + .expect("Failed to read line"); + + expr } fn main() { - let env = &mut default_env(); - loop { - print!("risp > "); -let _= std::io::stdout().flush(); - let expr = slurp_expr(); - match parse_eval(expr, env) { - Ok(res) => println!("-> {}", res), - Err(e) => match e { - RispError::Reason(msg) => println!("X> {}", msg), - }, + let env = &mut default_env(); + loop { + print!("risp > "); + let _ = std::io::stdout().flush(); + let expr = slurp_expr(); + match parse_eval(expr, env) { + Ok(res) => println!("-> {}", res), + Err(e) => match e { + RispError::Reason(msg) => println!("X> {}", msg), + }, + } } - } -} \ No newline at end of file +} diff --git a/src/parser.rs b/src/parser.rs index bf1a3d5..9fc603e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,65 +1,57 @@ -use crate::RispError; -use crate::Expr; -use crate::eval; -use crate::Environ; +use crate::evaluate::eval; use crate::read_seq; +use crate::Environ; +use crate::Expr; use crate::ParseFloatError; +use crate::RispError; -pub - -fn tokenize(expr: String) -> Vec { - expr - .replace("(", " ( ") - .replace(")", " ) ") - .split_whitespace() - .map(|x| x.to_string()) - .collect() +pub fn tokenize(expr: String) -> Vec { + expr.replace("(", " ( ") + .replace(")", " ) ") + .split_whitespace() + .map(|x| x.to_string()) + .collect() } -pub - -fn parse<'a>(tokens: &'a [String]) -> Result<(Expr, &'a [String]), RispError>{ - let (token, rest) = tokens.split_first() - .ok_or( - RispError::Reason("Could not get token".to_string()) - )?; - match &token[..]{ - "("=>{read_seq(rest)} - ")"=>Err(RispError::Reason("unexpected `)`".to_string())), - _ => Ok((parse_atom(token), rest)) +pub fn parse<'a>(tokens: &'a [String]) -> Result<(Expr, &'a [String]), RispError> { + let (token, rest) = tokens + .split_first() + .ok_or(RispError::Reason("Could not get token".to_string()))?; + match &token[..] { + "(" => read_seq(rest), + ")" => Err(RispError::Reason("unexpected `)`".to_string())), + _ => Ok((parse_atom(token), rest)), } } - -pub - -fn parse_list_of_floats(args: &[Expr]) -> Result, RispError> { - args - .iter() - .map(|x| parse_single_float(x)) - .collect() +pub fn parse_list_of_floats(args: &[Expr]) -> Result, RispError> { + args.iter().map(|x| parse_single_float(x)).collect() } fn parse_single_float(exp: &Expr) -> Result { - match exp { - Expr::Number(num) => Ok(*num), - _ => Err(RispError::Reason("expected a number".to_string())), - } + match exp { + Expr::Number(num) => Ok(*num), + _ => Err(RispError::Reason("expected a number".to_string())), + } } +fn parse_atom(token: &str) -> Expr { + use Expr::*; + match token.as_ref() { + "true" => Bool(true), + "false" => Bool(false), + _ => { + let potential_float: Result = token.parse(); + match potential_float { + Ok(v) => Number(v), + Err(_) => Symbol(token.to_string().clone()), + } + } + } +} +pub fn parse_eval(expr: String, env: &mut Environ) -> Result { + let (parsed_exp, _) = parse(&tokenize(expr))?; + let evaled_exp = eval(&parsed_exp, env)?; - - -fn parse_atom(token: &str) -> Expr { - let potential_float: Result = token.parse(); - match potential_float { - Ok(v) => Expr::Number(v), - Err(_) => Expr::Symbol(token.to_string().clone()) - } -}pub -fn parse_eval(expr: String, env: &mut Environ) -> Result { - let (parsed_exp, _) = parse(&tokenize(expr))?; - let evaled_exp = eval(&parsed_exp, env)?; - - Ok(evaled_exp) -} \ No newline at end of file + Ok(evaled_exp) +}