|
|
@@ -0,0 +1,421 @@
|
|
|
+use std::collections::HashMap;
|
|
|
+
|
|
|
+use fancy_regex::Regex;
|
|
|
+use std::fmt;
|
|
|
+use std::fs::File;
|
|
|
+use std::io;
|
|
|
+use std::io::{BufRead, BufReader};
|
|
|
+use std::num::ParseFloatError;
|
|
|
+use std::rc::Rc;
|
|
|
+/*
|
|
|
+ Types
|
|
|
+*/
|
|
|
+
|
|
|
+#[derive(Clone)]
|
|
|
+enum RispExp {
|
|
|
+ Bool(bool),
|
|
|
+ Symbol(String),
|
|
|
+ Number(f64),
|
|
|
+ List(Vec<RispExp>),
|
|
|
+ Func(fn(&[RispExp]) -> Result<RispExp, RispErr>),
|
|
|
+ Lambda(RispLambda),
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Clone)]
|
|
|
+struct RispLambda {
|
|
|
+ params_exp: Rc<RispExp>,
|
|
|
+ body_exp: Rc<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) => {
|
|
|
+ let xs: Vec<String> = list.iter().map(|x| x.to_string()).collect();
|
|
|
+ format!("({})", xs.join(","))
|
|
|
+ }
|
|
|
+ RispExp::Func(_) => "Function {}".to_string(),
|
|
|
+ RispExp::Lambda(_) => "Lambda {}".to_string(),
|
|
|
+ };
|
|
|
+
|
|
|
+ write!(f, "{}", str)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Debug)]
|
|
|
+enum RispErr {
|
|
|
+ Reason(String),
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Clone)]
|
|
|
+struct RispEnv<'a> {
|
|
|
+ data: HashMap<String, RispExp>,
|
|
|
+ outer: Option<&'a RispEnv<'a>>,
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ InPort
|
|
|
+*/
|
|
|
+// TODO change to symbol(eof)
|
|
|
+
|
|
|
+struct InPort {
|
|
|
+ line: String,
|
|
|
+ lines: Vec<String>,
|
|
|
+ cur_line: usize
|
|
|
+}
|
|
|
+
|
|
|
+impl InPort {
|
|
|
+
|
|
|
+ fn init(filename: String) -> InPort {
|
|
|
+ let file = File::open(filename).unwrap();
|
|
|
+ let reader = io::BufReader::new(file).lines().map(|s| s.ok().unwrap().to_string()).collect();
|
|
|
+ InPort {
|
|
|
+ line : String::new(),
|
|
|
+ lines: reader,
|
|
|
+ cur_line: 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn next_token(&mut self) -> String {
|
|
|
+ loop {
|
|
|
+ if self.line.is_empty() || self.line == "#<eof-object>".to_string() {
|
|
|
+ // TODO add match
|
|
|
+ self.line = self.lines.get(self.cur_line).unwrap_or(&"#<eof-object>".to_string()).to_string();
|
|
|
+ self.cur_line = self.cur_line + 1;
|
|
|
+ }
|
|
|
+ if self.line.is_empty() {
|
|
|
+ self.cur_line = self.cur_line + 1;
|
|
|
+ return "#<eof-object>".to_string();
|
|
|
+ }
|
|
|
+ let (token, rest) = InPort::tokenize(self.line.clone());
|
|
|
+ self.line = rest;
|
|
|
+ self.cur_line = self.cur_line + 1;
|
|
|
+ if !token.is_empty() {
|
|
|
+ return token;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn tokenize(expr: String) -> (String, String) {
|
|
|
+ let re =
|
|
|
+ Regex::new(r#"\s*(,@|[('`,)]|"(?:[\\].|[^\\"])*"|;.*|[^\s('"`,;)]*)(.*)"#).unwrap();
|
|
|
+ let result = re.captures(&expr);
|
|
|
+ let captures = result
|
|
|
+ .expect("Error running regex")
|
|
|
+ .expect("No match found");
|
|
|
+// println!("{}", captures.get(0).unwrap().as_str().to_string());
|
|
|
+ (
|
|
|
+ captures.get(1).unwrap().as_str().to_string(),
|
|
|
+ captures.get(2).unwrap().as_str().to_string(),
|
|
|
+ )
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+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<'a>() -> RispEnv<'a> {
|
|
|
+ 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, outer: None }
|
|
|
+}
|
|
|
+
|
|
|
+fn parse_list_of_floats(args: &[RispExp]) -> Result<Vec<f64>, RispErr> {
|
|
|
+ args.iter().map(|x| parse_single_float(x)).collect()
|
|
|
+}
|
|
|
+
|
|
|
+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_if_args(arg_forms: &[RispExp], env: &mut RispEnv) -> Result<RispExp, RispErr> {
|
|
|
+ let test_form = arg_forms
|
|
|
+ .first()
|
|
|
+ .ok_or(RispErr::Reason("expected test form".to_string()))?;
|
|
|
+ let test_eval = eval(test_form, env)?;
|
|
|
+ match test_eval {
|
|
|
+ RispExp::Bool(b) => {
|
|
|
+ let form_idx = if b { 1 } else { 2 };
|
|
|
+ let res_form = arg_forms
|
|
|
+ .get(form_idx)
|
|
|
+ .ok_or(RispErr::Reason(format!("expected form idx={}", form_idx)))?;
|
|
|
+ let res_eval = eval(res_form, env);
|
|
|
+
|
|
|
+ res_eval
|
|
|
+ }
|
|
|
+ _ => Err(RispErr::Reason(format!(
|
|
|
+ "unexpected test form='{}'",
|
|
|
+ test_form.to_string()
|
|
|
+ ))),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn eval_def_args(arg_forms: &[RispExp], env: &mut RispEnv) -> Result<RispExp, RispErr> {
|
|
|
+ let first_form = arg_forms
|
|
|
+ .first()
|
|
|
+ .ok_or(RispErr::Reason("expected first form".to_string()))?;
|
|
|
+ let first_str = match first_form {
|
|
|
+ RispExp::Symbol(s) => Ok(s.clone()),
|
|
|
+ _ => Err(RispErr::Reason(
|
|
|
+ "expected first form to be a symbol".to_string(),
|
|
|
+ )),
|
|
|
+ }?;
|
|
|
+ let second_form = arg_forms
|
|
|
+ .get(1)
|
|
|
+ .ok_or(RispErr::Reason("expected second form".to_string()))?;
|
|
|
+ if arg_forms.len() > 2 {
|
|
|
+ return Err(RispErr::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())
|
|
|
+}
|
|
|
+
|
|
|
+fn eval_lambda_args(arg_forms: &[RispExp]) -> Result<RispExp, RispErr> {
|
|
|
+ let params_exp = arg_forms
|
|
|
+ .first()
|
|
|
+ .ok_or(RispErr::Reason("expected args form".to_string()))?;
|
|
|
+ let body_exp = arg_forms
|
|
|
+ .get(1)
|
|
|
+ .ok_or(RispErr::Reason("expected second form".to_string()))?;
|
|
|
+ if arg_forms.len() > 2 {
|
|
|
+ return Err(RispErr::Reason(
|
|
|
+ "fn definition can only have two forms ".to_string(),
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(RispExp::Lambda(RispLambda {
|
|
|
+ body_exp: Rc::new(body_exp.clone()),
|
|
|
+ params_exp: Rc::new(params_exp.clone()),
|
|
|
+ }))
|
|
|
+}
|
|
|
+
|
|
|
+fn eval_built_in_form(
|
|
|
+ exp: &RispExp,
|
|
|
+ arg_forms: &[RispExp],
|
|
|
+ env: &mut RispEnv,
|
|
|
+) -> Option<Result<RispExp, RispErr>> {
|
|
|
+ match exp {
|
|
|
+ RispExp::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 env_get(k: &str, env: &RispEnv) -> Option<RispExp> {
|
|
|
+ match env.data.get(k) {
|
|
|
+ Some(exp) => Some(exp.clone()),
|
|
|
+ None => match &env.outer {
|
|
|
+ Some(outer_env) => env_get(k, &outer_env),
|
|
|
+ None => None,
|
|
|
+ },
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn parse_list_of_symbol_strings(form: Rc<RispExp>) -> Result<Vec<String>, RispErr> {
|
|
|
+ let list = match form.as_ref() {
|
|
|
+ RispExp::List(s) => Ok(s.clone()),
|
|
|
+ _ => Err(RispErr::Reason(
|
|
|
+ "expected args form to be a list".to_string(),
|
|
|
+ )),
|
|
|
+ }?;
|
|
|
+ list.iter()
|
|
|
+ .map(|x| match x {
|
|
|
+ RispExp::Symbol(s) => Ok(s.clone()),
|
|
|
+ _ => Err(RispErr::Reason(
|
|
|
+ "expected symbols in the argument list".to_string(),
|
|
|
+ )),
|
|
|
+ })
|
|
|
+ .collect()
|
|
|
+}
|
|
|
+
|
|
|
+fn env_for_lambda<'a>(
|
|
|
+ params: Rc<RispExp>,
|
|
|
+ arg_forms: &[RispExp],
|
|
|
+ outer_env: &'a mut RispEnv,
|
|
|
+) -> Result<RispEnv<'a>, RispErr> {
|
|
|
+ let ks = parse_list_of_symbol_strings(params)?;
|
|
|
+ if ks.len() != arg_forms.len() {
|
|
|
+ return Err(RispErr::Reason(format!(
|
|
|
+ "expected {} arguments, got {}",
|
|
|
+ ks.len(),
|
|
|
+ arg_forms.len()
|
|
|
+ )));
|
|
|
+ }
|
|
|
+ let vs = eval_forms(arg_forms, outer_env)?;
|
|
|
+ let mut data: HashMap<String, RispExp> = HashMap::new();
|
|
|
+ for (k, v) in ks.iter().zip(vs.iter()) {
|
|
|
+ data.insert(k.clone(), v.clone());
|
|
|
+ }
|
|
|
+ Ok(RispEnv {
|
|
|
+ data,
|
|
|
+ outer: Some(outer_env),
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+fn eval_forms(arg_forms: &[RispExp], env: &mut RispEnv) -> Result<Vec<RispExp>, RispErr> {
|
|
|
+ arg_forms.iter().map(|x| eval(x, env)).collect()
|
|
|
+}
|
|
|
+
|
|
|
+fn eval(exp: &RispExp, env: &mut RispEnv) -> Result<RispExp, RispErr> {
|
|
|
+ match exp {
|
|
|
+ RispExp::Symbol(k) => {
|
|
|
+ env_get(k, env).ok_or(RispErr::Reason(format!("unexpected symbol k='{}'", k)))
|
|
|
+ }
|
|
|
+ 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..];
|
|
|
+ match eval_built_in_form(first_form, arg_forms, env) {
|
|
|
+ Some(res) => res,
|
|
|
+ None => {
|
|
|
+ let first_eval = eval(first_form, env)?;
|
|
|
+ match first_eval {
|
|
|
+ RispExp::Func(f) => f(&eval_forms(arg_forms, env)?),
|
|
|
+ RispExp::Lambda(lambda) => {
|
|
|
+ let new_env = &mut env_for_lambda(lambda.params_exp, arg_forms, env)?;
|
|
|
+ eval(&lambda.body_exp, new_env)
|
|
|
+ }
|
|
|
+ _ => Err(RispErr::Reason("first form must be a function".to_string())),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ RispExp::Func(_) => Err(RispErr::Reason("unexpected form".to_string())),
|
|
|
+ RispExp::Lambda(_) => Err(RispErr::Reason("unexpected form".to_string())),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+fn main() {
|
|
|
+ let env = &mut default_env();
|
|
|
+ let mut reader = InPort::init("new.lisp".to_string());
|
|
|
+ let mut token = String::new();
|
|
|
+ loop {
|
|
|
+ if token == "#<eof-object>".to_string() {
|
|
|
+ println!("EOF");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ token = reader.next_token().as_str().to_string();
|
|
|
+ println!("{}", token);
|
|
|
+ }
|
|
|
+ // read from file
|
|
|
+// let file = File::open("new.lisp").unwrap();
|
|
|
+// let lines = io::BufReader::new(file).lines();
|
|
|
+// let mut cur_line = String::new();
|
|
|
+// let mut token = String::new();
|
|
|
+// for line in lines {
|
|
|
+// let (token, cur_line) = tokenize(line.ok().unwrap().as_str().to_string());
|
|
|
+// println!("token {:?}", token);
|
|
|
+// println!("line {:?}", cur_line);
|
|
|
+ // }
|
|
|
+
|
|
|
+ /*
|
|
|
+ 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),
|
|
|
+ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+ */
|
|
|
+}
|