Procházet zdrojové kódy

prototyping lisp v0

ada před 5 roky
rodič
revize
2499b47560
1 změnil soubory, kde provedl 421 přidání a 0 odebrání
  1. 421 0
      lisp/lisp.rs

+ 421 - 0
lisp/lisp.rs

@@ -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),
+        },
+      }
+    }
+    */
+}