Last active
March 7, 2023 04:24
Revisions
-
stopachka revised this gist
May 4, 2019 . 1 changed file with 2 additions and 263 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -7,16 +7,6 @@ use std::num::ParseFloatError; Types */ #[derive(Clone)] enum RispExp { Bool(bool), @@ -29,7 +19,7 @@ enum RispExp { impl fmt::Display for RispExp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let str = match self { RispExp::Bool(a) => a.to_string(), RispExp::Symbol(s) => s.clone(), RispExp::Number(n) => n.to_string(), RispExp::List(list) => { @@ -72,7 +62,7 @@ fn tokenize(expr: String) -> Vec<String> { fn parse<'a>(tokens: &'a [String]) -> Result<(RispExp, &'a [String]), RispErr> { let (token, rest) = tokens.split_first() .ok_or( RispErr::Reason("could not get token".to_string()) )?; match &token[..] { "(" => read_seq(rest), @@ -269,255 +259,4 @@ fn main() { }, } } } -
stopachka revised this gist
May 4, 2019 . 1 changed file with 261 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -7,6 +7,16 @@ use std::num::ParseFloatError; Types */ #[derive(Clone)] enum RispExp {use std::collections::HashMap; use std::fmt; use std::io; use std::num::ParseFloatError; /* Types */ #[derive(Clone)] enum RispExp { Bool(bool), @@ -231,6 +241,257 @@ fn eval(exp: &RispExp, env: &mut RispEnv) -> Result<RispExp, RispErr> { Repl */ fn parse_eval(expr: String, env: &mut RispEnv) -> Result<RispExp, RispErr> { let (parsed_exp, _) = parse(&tokenize(expr))?; let evaled_exp = eval(&parsed_exp, env)?; Ok(evaled_exp) } fn slurp_expr() -> String { 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 { println!("risp >"); let expr = slurp_expr(); match parse_eval(expr, env) { Ok(res) => println!("// 🔥 => {}", res), Err(e) => match e { RispErr::Reason(msg) => println!("// 🙀 => {}", msg), }, } } } Bool(bool), Symbol(String), Number(f64), List(Vec<RispExp>), Func(fn(&[RispExp]) -> Result<RispExp, RispErr>), } impl fmt::Display for RispExp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let str = match self { RispExp::Bool(s) => s.to_string(), RispExp::Symbol(s) => s.clone(), RispExp::Number(n) => n.to_string(), RispExp::List(list) => { let xs: Vec<String> = list .iter() .map(|x| x.to_string()) .collect(); format!("({})", xs.join(",")) }, RispExp::Func(_) => "Function {}".to_string(), }; write!(f, "{}", str) } } #[derive(Debug)] enum RispErr { Reason(String), } #[derive(Clone)] struct RispEnv { data: HashMap<String, RispExp>, } /* Parse */ fn tokenize(expr: String) -> Vec<String> { expr .replace("(", " ( ") .replace(")", " ) ") .split_whitespace() .map(|x| x.to_string()) .collect() } fn parse<'a>(tokens: &'a [String]) -> Result<(RispExp, &'a [String]), RispErr> { let (token, rest) = tokens.split_first() .ok_or( RispErr::Reason(format!("could not get token for pos=''")) )?; match &token[..] { "(" => read_seq(rest), ")" => Err(RispErr::Reason("unexpected `)`".to_string())), _ => Ok((parse_atom(token), rest)), } } fn read_seq<'a>(tokens: &'a [String]) -> Result<(RispExp, &'a [String]), RispErr> { let mut res: Vec<RispExp> = vec![]; let mut xs = tokens; loop { let (next_token, rest) = xs .split_first() .ok_or(RispErr::Reason("could not find closing `)`".to_string())) ?; if next_token == ")" { return Ok((RispExp::List(res), rest)) // skip `)`, head to the token after } let (exp, new_xs) = parse(&xs)?; res.push(exp); xs = new_xs; } } fn parse_atom(token: &str) -> RispExp { match token.as_ref() { "true" => RispExp::Bool(true), "false" => RispExp::Bool(false), _ => { let potential_float: Result<f64, ParseFloatError> = token.parse(); match potential_float { Ok(v) => RispExp::Number(v), Err(_) => RispExp::Symbol(token.to_string().clone()) } } } } /* Env */ macro_rules! ensure_tonicity { ($check_fn:expr) => {{ |args: &[RispExp]| -> Result<RispExp, RispErr> { let floats = parse_list_of_floats(args)?; let first = floats.first().ok_or(RispErr::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(RispExp::Bool(f(first, rest))) } }}; } fn default_env() -> RispEnv { let mut data: HashMap<String, RispExp> = HashMap::new(); data.insert( "+".to_string(), RispExp::Func( |args: &[RispExp]| -> Result<RispExp, RispErr> { let sum = parse_list_of_floats(args)?.iter().fold(0.0, |sum, a| sum + a); Ok(RispExp::Number(sum)) } ) ); data.insert( "-".to_string(), RispExp::Func( |args: &[RispExp]| -> Result<RispExp, RispErr> { let floats = parse_list_of_floats(args)?; let first = *floats.first().ok_or(RispErr::Reason("expected at least one number".to_string()))?; let sum_of_rest = floats[1..].iter().fold(0.0, |sum, a| sum + a); Ok(RispExp::Number(first - sum_of_rest)) } ) ); data.insert( "=".to_string(), RispExp::Func(ensure_tonicity!(|a, b| a == b)) ); data.insert( ">".to_string(), RispExp::Func(ensure_tonicity!(|a, b| a > b)) ); data.insert( ">=".to_string(), RispExp::Func(ensure_tonicity!(|a, b| a >= b)) ); data.insert( "<".to_string(), RispExp::Func(ensure_tonicity!(|a, b| a < b)) ); data.insert( "<=".to_string(), RispExp::Func(ensure_tonicity!(|a, b| a <= b)) ); RispEnv {data} } fn parse_list_of_floats(args: &[RispExp]) -> Result<Vec<f64>, RispErr> { args .iter() .map(|x| parse_single_float(x)) .collect::<Result<Vec<f64>, RispErr>>() } fn parse_single_float(exp: &RispExp) -> Result<f64, RispErr> { match exp { RispExp::Number(num) => Ok(*num), _ => Err(RispErr::Reason("expected a number".to_string())), } } /* Eval */ fn eval(exp: &RispExp, env: &mut RispEnv) -> Result<RispExp, RispErr> { match exp { RispExp::Symbol(k) => env.data.get(k) .ok_or( RispErr::Reason( format!("unexpected symbol k='{}'", k) ) ) .map(|x| x.clone()) , RispExp::Bool(_a) => Ok(exp.clone()), RispExp::Number(_a) => Ok(exp.clone()), RispExp::List(list) => { let first_form = list .first() .ok_or(RispErr::Reason("expected a non-empty list".to_string()))?; let arg_forms = &list[1..]; let first_eval = eval(first_form, env)?; match first_eval { RispExp::Func(f) => { let args_eval = arg_forms .iter() .map(|x| eval(x, env)) .collect::<Result<Vec<RispExp>, RispErr>>(); f(&args_eval?) }, _ => Err( RispErr::Reason("first form must be a function".to_string()) ), } }, RispExp::Func(_) => Err( RispErr::Reason("unexpected form".to_string()) ), } } /* Repl */ fn parse_eval_print(expr: String, env: &mut RispEnv) -> Result<RispExp, RispErr> { let (parsed_exp, _) = parse(&tokenize(expr))?; let evaled_exp = eval(&parsed_exp, env)?; -
stopachka revised this gist
May 4, 2019 . 1 changed file with 10 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,5 @@ use std::collections::HashMap; use std::fmt; use std::io; use std::num::ParseFloatError; @@ -15,10 +16,10 @@ enum RispExp { Func(fn(&[RispExp]) -> Result<RispExp, RispErr>), } impl fmt::Display for RispExp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let str = match self { RispExp::Bool(s) => s.to_string(), RispExp::Symbol(s) => s.clone(), RispExp::Number(n) => n.to_string(), RispExp::List(list) => { @@ -29,7 +30,9 @@ impl ToString for RispExp { format!("({})", xs.join(",")) }, RispExp::Func(_) => "Function {}".to_string(), }; write!(f, "{}", str) } } @@ -228,11 +231,11 @@ fn eval(exp: &RispExp, env: &mut RispEnv) -> Result<RispExp, RispErr> { Repl */ fn parse_eval_print(expr: String, env: &mut RispEnv) -> Result<RispExp, RispErr> { let (parsed_exp, _) = parse(&tokenize(expr))?; let evaled_exp = eval(&parsed_exp, env)?; Ok(evaled_exp) } fn slurp_expr() -> String { -
stopachka revised this gist
May 4, 2019 . 1 changed file with 15 additions and 15 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -56,33 +56,32 @@ fn tokenize(expr: String) -> Vec<String> { .collect() } fn parse<'a>(tokens: &'a [String]) -> Result<(RispExp, &'a [String]), RispErr> { let (token, rest) = tokens.split_first() .ok_or( RispErr::Reason(format!("could not get token for pos=''")) )?; match &token[..] { "(" => read_seq(rest), ")" => Err(RispErr::Reason("unexpected `)`".to_string())), _ => Ok((parse_atom(token), rest)), } } fn read_seq<'a>(tokens: &'a [String]) -> Result<(RispExp, &'a [String]), RispErr> { let mut res: Vec<RispExp> = vec![]; let mut xs = tokens; loop { let (next_token, rest) = xs .split_first() .ok_or(RispErr::Reason("could not find closing `)`".to_string())) ?; if next_token == ")" { return Ok((RispExp::List(res), rest)) // skip `)`, head to the token after } let (exp, new_xs) = parse(&xs)?; res.push(exp); xs = new_xs; } } @@ -230,8 +229,9 @@ fn eval(exp: &RispExp, env: &mut RispEnv) -> Result<RispExp, RispErr> { */ fn parse_eval_print(expr: String, env: &mut RispEnv) -> Result<String, RispErr> { let (parsed_exp, _) = parse(&tokenize(expr))?; let evaled_exp = eval(&parsed_exp, env)?; Ok(evaled_exp.to_string()) } @@ -248,7 +248,7 @@ fn main() { let env = &mut default_env(); loop { println!("risp >"); let expr = slurp_expr(); match parse_eval_print(expr, env) { Ok(res) => println!("// 🔥 => {}", res), Err(e) => match e { -
stopachka revised this gist
May 2, 2019 . 1 changed file with 16 additions and 13 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -26,7 +26,7 @@ impl ToString for RispExp { .iter() .map(|x| x.to_string()) .collect(); format!("({})", xs.join(",")) }, RispExp::Func(_) => "Function {}".to_string(), } @@ -48,12 +48,12 @@ struct RispEnv { */ fn tokenize(expr: String) -> Vec<String> { expr .replace("(", " ( ") .replace(")", " ) ") .split_whitespace() .map(|x| x.to_string()) .collect() } fn parse(tokens: &[String], pos: usize) -> Result<(RispExp, usize), RispErr> { @@ -128,7 +128,8 @@ fn default_env() -> RispEnv { RispExp::Func( |args: &[RispExp]| -> Result<RispExp, RispErr> { let sum = parse_list_of_floats(args)?.iter().fold(0.0, |sum, a| sum + a); Ok(RispExp::Number(sum)) } ) ); @@ -139,7 +140,8 @@ fn default_env() -> RispEnv { let floats = parse_list_of_floats(args)?; let first = *floats.first().ok_or(RispErr::Reason("expected at least one number".to_string()))?; let sum_of_rest = floats[1..].iter().fold(0.0, |sum, a| sum + a); Ok(RispExp::Number(first - sum_of_rest)) } ) ); @@ -164,14 +166,14 @@ fn default_env() -> RispEnv { RispExp::Func(ensure_tonicity!(|a, b| a <= b)) ); RispEnv {data} } fn parse_list_of_floats(args: &[RispExp]) -> Result<Vec<f64>, RispErr> { args .iter() .map(|x| parse_single_float(x)) .collect::<Result<Vec<f64>, RispErr>>() } fn parse_single_float(exp: &RispExp) -> Result<f64, RispErr> { @@ -196,21 +198,21 @@ fn eval(exp: &RispExp, env: &mut RispEnv) -> Result<RispExp, RispErr> { ) .map(|x| x.clone()) , RispExp::Bool(_a) => Ok(exp.clone()), RispExp::Number(_a) => Ok(exp.clone()), RispExp::List(list) => { let first_form = list .first() .ok_or(RispErr::Reason("expected a non-empty list".to_string()))?; let arg_forms = &list[1..]; let first_eval = eval(first_form, env)?; match first_eval { RispExp::Func(f) => { let args_eval = arg_forms .iter() .map(|x| eval(x, env)) .collect::<Result<Vec<RispExp>, RispErr>>(); f(&args_eval?) }, _ => Err( RispErr::Reason("first form must be a function".to_string()) @@ -230,15 +232,16 @@ fn eval(exp: &RispExp, env: &mut RispEnv) -> Result<RispExp, RispErr> { fn parse_eval_print(expr: String, env: &mut RispEnv) -> Result<String, RispErr> { let (parsed_exp, _) = parse(&tokenize(expr), 0)?; let evaled_exp = eval(&parsed_exp, env)?; Ok(evaled_exp.to_string()) } fn slurp_expr() -> String { let mut expr = String::new(); io::stdin().read_line(&mut expr) .expect("Failed to read line"); expr } fn main() { -
stopachka revised this gist
May 2, 2019 . 1 changed file with 38 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -104,6 +104,23 @@ fn parse_atom(token: &str) -> RispExp { Env */ macro_rules! ensure_tonicity { ($check_fn:expr) => {{ |args: &[RispExp]| -> Result<RispExp, RispErr> { let floats = parse_list_of_floats(args)?; let first = floats.first().ok_or(RispErr::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(RispExp::Bool(f(first, rest))) } }}; } fn default_env() -> RispEnv { let mut data: HashMap<String, RispExp> = HashMap::new(); data.insert( @@ -126,6 +143,27 @@ fn default_env() -> RispEnv { } ) ); data.insert( "=".to_string(), RispExp::Func(ensure_tonicity!(|a, b| a == b)) ); data.insert( ">".to_string(), RispExp::Func(ensure_tonicity!(|a, b| a > b)) ); data.insert( ">=".to_string(), RispExp::Func(ensure_tonicity!(|a, b| a >= b)) ); data.insert( "<".to_string(), RispExp::Func(ensure_tonicity!(|a, b| a < b)) ); data.insert( "<=".to_string(), RispExp::Func(ensure_tonicity!(|a, b| a <= b)) ); return RispEnv {data} } -
stopachka revised this gist
May 2, 2019 . 1 changed file with 25 additions and 65 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -15,6 +15,24 @@ enum RispExp { Func(fn(&[RispExp]) -> Result<RispExp, RispErr>), } impl ToString for RispExp { fn to_string(&self) -> String { match self { RispExp::Bool(a) => a.to_string(), RispExp::Symbol(s) => s.clone(), RispExp::Number(n) => n.to_string(), RispExp::List(list) => { let xs: Vec<String> = list .iter() .map(|x| x.to_string()) .collect(); return format!("({})", xs.join(",")); }, RispExp::Func(_) => "Function {}".to_string(), } } } #[derive(Debug)] enum RispErr { Reason(String), @@ -33,9 +51,8 @@ fn tokenize(expr: String) -> Vec<String> { return expr .replace("(", " ( ") .replace(")", " ) ") .split_whitespace() .map(|x| x.to_string()) .collect(); } @@ -45,13 +62,10 @@ fn parse(tokens: &[String], pos: usize) -> Result<(RispExp, usize), RispErr> { .ok_or( RispErr::Reason(format!("could not get token for pos='{}'", pos)) )?; match &token[..] { "(" => read_seq(tokens, pos + 1), ")" => Err(RispErr::Reason("unexpected `)`".to_string())), _ => Ok((parse_atom(token), pos + 1)), } } @@ -78,7 +92,7 @@ fn parse_atom(token: &str) -> RispExp { "false" => RispExp::Bool(false), _ => { let potential_float: Result<f64, ParseFloatError> = token.parse(); match potential_float { Ok(v) => RispExp::Number(v), Err(_) => RispExp::Symbol(token.to_string().clone()) } @@ -89,22 +103,6 @@ fn parse_atom(token: &str) -> RispExp { /* Env */ fn default_env() -> RispEnv { let mut data: HashMap<String, RispExp> = HashMap::new(); @@ -124,33 +122,11 @@ fn default_env() -> RispEnv { let floats = parse_list_of_floats(args)?; let first = *floats.first().ok_or(RispErr::Reason("expected at least one number".to_string()))?; let sum_of_rest = floats[1..].iter().fold(0.0, |sum, a| sum + a); return Ok(RispExp::Number(first - sum_of_rest)); } ) ); return RispEnv {data} } fn parse_list_of_floats(args: &[RispExp]) -> Result<Vec<f64>, RispErr> { @@ -213,26 +189,10 @@ fn eval(exp: &RispExp, env: &mut RispEnv) -> Result<RispExp, RispErr> { Repl */ fn parse_eval_print(expr: String, env: &mut RispEnv) -> Result<String, RispErr> { let (parsed_exp, _) = parse(&tokenize(expr), 0)?; let evaled_exp = eval(&parsed_exp, env)?; return Ok(evaled_exp.to_string()); } fn slurp_expr() -> String { -
stopachka revised this gist
May 1, 2019 . No changes.There are no files selected for viewing
-
stopachka revised this gist
May 1, 2019 . 1 changed file with 11 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -72,11 +72,17 @@ fn read_seq(tokens: &[String], start: usize) -> Result<(RispExp, usize), RispErr } } fn parse_atom(token: &str) -> RispExp { match token.as_ref() { "true" => RispExp::Bool(true), "false" => RispExp::Bool(false), _ => { let potential_float: Result<f64, ParseFloatError> = token.parse(); return match potential_float { Ok(v) => RispExp::Number(v), Err(_) => RispExp::Symbol(token.to_string().clone()) } } } } -
stopachka created this gist
May 1, 2019 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,252 @@ use std::collections::HashMap; use std::io; use std::num::ParseFloatError; /* Types */ #[derive(Clone)] enum RispExp { Bool(bool), Symbol(String), Number(f64), List(Vec<RispExp>), Func(fn(&[RispExp]) -> Result<RispExp, RispErr>), } #[derive(Debug)] enum RispErr { Reason(String), } #[derive(Clone)] struct RispEnv { data: HashMap<String, RispExp>, } /* Parse */ fn tokenize(expr: String) -> Vec<String> { return expr .replace("(", " ( ") .replace(")", " ) ") .split(" ") .map(|x| x.trim().to_string()) .filter(|x| !x.is_empty()) .collect(); } fn parse(tokens: &[String], pos: usize) -> Result<(RispExp, usize), RispErr> { let token = tokens .get(pos) .ok_or( RispErr::Reason(format!("could not get token for pos='{}'", pos)) )?; let to_match = &token[..]; match to_match { "(" => read_seq(tokens, pos + 1), ")" => Err(RispErr::Reason("unexpected `)`".to_string())), _ => Ok( (parse_atom(token), pos + 1) ), } } fn read_seq(tokens: &[String], start: usize) -> Result<(RispExp, usize), RispErr> { let mut res: Vec<RispExp> = vec![]; let mut next = start; loop { let next_token = tokens .get(next) .ok_or(RispErr::Reason("could not find closing `)`".to_string())) ?; if next_token == ")" { return Ok((RispExp::List(res), next + 1)) // skip `)`, head to the token after } let (exp, new_next) = parse(&tokens, next)?; res.push(exp); next = new_next; } } fn parse_atom(token: &str) -> RispExp { let potential_float: Result<f64, ParseFloatError> = token.parse(); return match potential_float { Ok(v) => RispExp::Number(v), Err(_) => RispExp::Symbol(token.to_string().clone()) } } /* Env */ macro_rules! ensure_tonicity { ($check_fn:expr) => {{ |args: &[RispExp]| -> Result<RispExp, RispErr> { let floats = parse_list_of_floats(args)?; let first = floats.first().ok_or(RispErr::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, } }; return Ok(RispExp::Bool(f(first, rest))); } }}; } fn default_env() -> RispEnv { let mut data: HashMap<String, RispExp> = HashMap::new(); data.insert( "+".to_string(), RispExp::Func( |args: &[RispExp]| -> Result<RispExp, RispErr> { let sum = parse_list_of_floats(args)?.iter().fold(0.0, |sum, a| sum + a); return Ok(RispExp::Number(sum)); } ) ); data.insert( "-".to_string(), RispExp::Func( |args: &[RispExp]| -> Result<RispExp, RispErr> { let floats = parse_list_of_floats(args)?; let first = *floats.first().ok_or(RispErr::Reason("expected at least one number".to_string()))?; let sum_of_rest = floats[1..].iter().fold(0.0, |sum, a| sum + a); return Ok(RispExp::Number(first - sum_of_rest)); } ) ); data.insert( "=".to_string(), RispExp::Func(ensure_tonicity!(|a, b| a == b)) ); data.insert( ">".to_string(), RispExp::Func(ensure_tonicity!(|a, b| a > b)) ); data.insert( ">=".to_string(), RispExp::Func(ensure_tonicity!(|a, b| a >= b)) ); data.insert( "<".to_string(), RispExp::Func(ensure_tonicity!(|a, b| a < b)) ); data.insert( "<=".to_string(), RispExp::Func(ensure_tonicity!(|a, b| a <= b)) ); return RispEnv {data: data} } fn parse_list_of_floats(args: &[RispExp]) -> Result<Vec<f64>, RispErr> { return args .iter() .map(|x| parse_single_float(x)) .collect::<Result<Vec<f64>, RispErr>>(); } fn parse_single_float(exp: &RispExp) -> Result<f64, RispErr> { match exp { RispExp::Number(num) => Ok(*num), _ => Err(RispErr::Reason("expected a number".to_string())), } } /* Eval */ fn eval(exp: &RispExp, env: &mut RispEnv) -> Result<RispExp, RispErr> { match exp { RispExp::Symbol(k) => env.data.get(k) .ok_or( RispErr::Reason( format!("unexpected symbol k='{}'", k) ) ) .map(|x| x.clone()) , RispExp::Number(_a) => Ok(exp.clone()), RispExp::Bool(_a) => Ok(exp.clone()), RispExp::List(list) => { let first_form = list .first() .ok_or(RispErr::Reason("expected a non-empty list".to_string()))?; let arg_forms = &list[1..]; let first_eval = eval(first_form, env)?; return match first_eval { RispExp::Func(f) => { let args_eval = arg_forms .iter() .map(|x| eval(x, env)) .collect::<Result<Vec<RispExp>, RispErr>>(); return f(&args_eval?); }, _ => Err( RispErr::Reason("first form must be a function".to_string()) ), } }, RispExp::Func(_) => Err( RispErr::Reason("unexpected form".to_string()) ), } } /* Repl */ fn to_str(exp: &RispExp) -> String { match exp { RispExp::Bool(b) => b.to_string(), RispExp::Symbol(s) => s.clone(), RispExp::Number(n) => n.to_string(), RispExp::List(list) => { let xs: Vec<String> = list .iter() .map(|x| to_str(x)) .collect(); return format!("({})", xs.join(",")); }, RispExp::Func(_) => "Function {}".to_string(), } } fn parse_eval_print(expr: String, env: &mut RispEnv) -> Result<String, RispErr> { let (parsed_exp, _) = parse(&tokenize(expr), 0)?; let evaled_exp = eval(&parsed_exp, env)?; return Ok(to_str(&evaled_exp)); } fn slurp_expr() -> String { let mut expr = String::new(); io::stdin().read_line(&mut expr) .expect("Failed to read line"); return expr; } fn main() { let env = &mut default_env(); loop { println!("risp >"); let expr = slurp_expr();; match parse_eval_print(expr, env) { Ok(res) => println!("// 🔥 => {}", res), Err(e) => match e { RispErr::Reason(msg) => println!("// 🙀 => {}", msg), }, } } }