moros 0.7.0

MOROS: Obscure Rust Operating System
Documentation
diff --git a/src/usr/lisp.rs b/src/usr/lisp.rs
index 40ad982..0685e25 100644
--- a/src/usr/lisp.rs
+++ b/src/usr/lisp.rs
@@ -1,4 +1,5 @@
-use crate::{api, usr};
+use crate::{api, usr, debug, syscall};
+use crate::api::fs;
 use crate::api::console::Style;
 use crate::api::prompt::Prompt;
 use alloc::string::ToString;
@@ -89,7 +90,7 @@ struct Env<'a> {
 // Parser
 
 fn is_symbol_letter(c: char) -> bool {
-    let chars = "<>=-+*/";
+    let chars = "<>=-+*?:/";
     c.is_alphanumeric() || chars.contains(c)
 }
 
@@ -162,75 +163,89 @@ macro_rules! ensure_tonicity {
 
 fn default_env<'a>() -> Env<'a> {
     let mut data: BTreeMap<String, Exp> = BTreeMap::new();
-    data.insert(
-        "*".to_string(),
-        Exp::Func(|args: &[Exp]| -> Result<Exp, Err> {
-            let res = list_of_floats(args)?.iter().fold(1.0, |res, a| res * a);
-            Ok(Exp::Num(res))
-        })
-    );
-    data.insert(
-        "+".to_string(), 
-        Exp::Func(|args: &[Exp]| -> Result<Exp, Err> {
-            let res = list_of_floats(args)?.iter().fold(0.0, |res, a| res + a);
-            Ok(Exp::Num(res))
-        })
-    );
-    data.insert(
-        "-".to_string(), 
-        Exp::Func(|args: &[Exp]| -> Result<Exp, Err> {
-            let floats = list_of_floats(args)?;
-            let first = *floats.first().ok_or(Err::Reason("Expected at least one number".to_string()))?;
-            let sum_of_rest = floats[1..].iter().fold(0.0, |sum, a| sum + a);
-            Ok(Exp::Num(first - sum_of_rest))
-        })
-    );
-    data.insert(
-        "=".to_string(), 
-        Exp::Func(ensure_tonicity!(|a, b| approx_eq!(f64, a, b)))
-    );
-    data.insert(
-        ">".to_string(), 
-        Exp::Func(ensure_tonicity!(|a, b| !approx_eq!(f64, a, b) && a > b))
-    );
-    data.insert(
-        ">=".to_string(), 
-        Exp::Func(ensure_tonicity!(|a, b| approx_eq!(f64, a, b) || a > b))
-    );
-    data.insert(
-        "<".to_string(), 
-        Exp::Func(ensure_tonicity!(|a, b| !approx_eq!(f64, a, b) && a < b))
-    );
-    data.insert(
-        "<=".to_string(), 
-        Exp::Func(ensure_tonicity!(|a, b| approx_eq!(f64, a, b) || a < b))
-    );
+
+    data.insert("=".to_string(), Exp::Func(ensure_tonicity!(|a, b| approx_eq!(f64, a, b))));
+    data.insert(">".to_string(), Exp::Func(ensure_tonicity!(|a, b| !approx_eq!(f64, a, b) && a > b)));
+    data.insert(">=".to_string(), Exp::Func(ensure_tonicity!(|a, b| approx_eq!(f64, a, b) || a > b)));
+    data.insert("<".to_string(), Exp::Func(ensure_tonicity!(|a, b| !approx_eq!(f64, a, b) && a < b)));
+    data.insert("<=".to_string(), Exp::Func(ensure_tonicity!(|a, b| approx_eq!(f64, a, b) || a < b)));
+
+    data.insert("*".to_string(), Exp::Func(|args: &[Exp]| -> Result<Exp, Err> {
+        let res = list_of_floats(args)?.iter().fold(1.0, |res, a| res * a);
+        Ok(Exp::Num(res))
+    }));
+    data.insert("+".to_string(), Exp::Func(|args: &[Exp]| -> Result<Exp, Err> {
+        let res = list_of_floats(args)?.iter().fold(0.0, |res, a| res + a);
+        Ok(Exp::Num(res))
+    }));
+    data.insert("-".to_string(), Exp::Func(|args: &[Exp]| -> Result<Exp, Err> {
+        let floats = list_of_floats(args)?;
+        let first = *floats.first().ok_or(Err::Reason("Expected at least one number".to_string()))?;
+        let sum_of_rest = floats[1..].iter().fold(0.0, |sum, a| sum + a);
+        Ok(Exp::Num(first - sum_of_rest))
+    }));
+
+    data.insert("decode-float".to_string(), Exp::Func(|args: &[Exp]| -> Result<Exp, Err> {
+        let floats = list_of_floats(args)?;
+        let n = *floats.first().ok_or(Err::Reason("Expected one number".to_string()))?;
+        Ok(Exp::Num(n.to_bits() as f64))
+    }));
+    data.insert("encode-float".to_string(), Exp::Func(|args: &[Exp]| -> Result<Exp, Err> {
+        let floats = list_of_floats(args)?;
+        let n = *floats.first().ok_or(Err::Reason("Expected one number".to_string()))?;
+        Ok(Exp::Num(f64::from_bits(n as u64)))
+    }));
+
+    data.insert("read-file".to_string(), Exp::Func(|args: &[Exp]| -> Result<Exp, Err> {
+        let arg = first(args)?;
+        let path = string(&arg)?;
+        let contents = fs::read_to_string(&path).or(Err(Err::Reason("Could not read file".to_string())))?;
+        Ok(Exp::Str(contents))
+    }));
+
+    data.insert("lines".to_string(), Exp::Func(|args: &[Exp]| -> Result<Exp, Err> {
+        let arg = first(args)?;
+        let s = string(&arg)?;
+        let lines = s.lines().map(|line| Exp::Str(line.to_string())).collect();
+        Ok(Exp::List(lines))
+    }));
 
     Env { data, outer: None }
 }
 
+fn first(exps: &[Exp]) -> Result<Exp, Err> {
+    match exps.first() {
+        Some(exp) => Ok(exp.clone()),
+        None => Err(Err::Reason("Expected an expression".to_string()))
+    }
+}
+
 fn list_of_floats(args: &[Exp]) -> Result<Vec<f64>, Err> {
-    args.iter().map(|x| single_float(x)).collect()
+    args.iter().map(|x| float(x)).collect()
 }
 
-fn single_float(exp: &Exp) -> Result<f64, Err> {
+fn float(exp: &Exp) -> Result<f64, Err> {
     match exp {
         Exp::Num(num) => Ok(*num),
         _ => Err(Err::Reason("Expected a number".to_string())),
     }
 }
 
+fn string(exp: &Exp) -> Result<String, Err> {
+    match exp {
+        Exp::Str(s) => Ok(s.to_string()),
+        _ => Err(Err::Reason("Expected a string".to_string())),
+    }
+}
+
 // Eval
 
-fn eval_quote_args(arg_forms: &[Exp]) -> Result<Exp, Err> {
-    let first_form = arg_forms.first().ok_or(Err::Reason("Expected first form".to_string()))?;
-    Ok(first_form.clone())
+fn eval_quote_args(args: &[Exp]) -> Result<Exp, Err> {
+    first(args)
 }
 
-fn eval_atom_args(arg_forms: &[Exp], env: &mut Env) -> Result<Exp, Err> {
-    let first_form = arg_forms.first().ok_or(Err::Reason("Expected first form".to_string()))?;
-    let first_eval = eval(first_form, env)?;
-    match first_eval {
+fn eval_atom_args(args: &[Exp], env: &mut Env) -> Result<Exp, Err> {
+    match eval(&first(args)?, env)? {
         Exp::Sym(_) => Ok(Exp::Bool(true)),
         _           => Ok(Exp::Bool(false)),
     }
@@ -258,10 +273,8 @@ fn eval_eq_args(arg_forms: &[Exp], env: &mut Env) -> Result<Exp, Err> {
     }
 }
 
-fn eval_car_args(arg_forms: &[Exp], env: &mut Env) -> Result<Exp, Err> {
-    let first_form = arg_forms.first().ok_or(Err::Reason("Expected first form".to_string()))?;
-    let first_eval = eval(first_form, env)?;
-    match first_eval {
+fn eval_car_args(args: &[Exp], env: &mut Env) -> Result<Exp, Err> {
+    match eval(&first(args)?, env)? {
         Exp::List(list) => {
             let exp = list.first().ok_or(Err::Reason("List cannot be empty".to_string()))?; // TODO: return nil?
             Ok(exp.clone())
@@ -270,10 +283,8 @@ fn eval_car_args(arg_forms: &[Exp], env: &mut Env) -> Result<Exp, Err> {
     }
 }
 
-fn eval_cdr_args(arg_forms: &[Exp], env: &mut Env) -> Result<Exp, Err> {
-    let first_form = arg_forms.first().ok_or(Err::Reason("Expected first form".to_string()))?;
-    let first_eval = eval(first_form, env)?;
-    match first_eval {
+fn eval_cdr_args(args: &[Exp], env: &mut Env) -> Result<Exp, Err> {
+    match eval(&first(args)?, env)? {
         Exp::List(list) => {
             if list.is_empty() {
                 return Err(Err::Reason("List cannot be empty".to_string())) // TODO: return nil?
@@ -311,11 +322,7 @@ fn eval_cond_args(arg_forms: &[Exp], env: &mut Env) -> Result<Exp, Err> {
                 let pred = eval(&list[0], env)?;
                 let exp = eval(&list[1], env)?;
                 match pred {
-                    Exp::Bool(b) => {
-                        if b {
-                            return Ok(exp);
-                        }
-                    },
+                    Exp::Bool(b) if b => return Ok(exp),
                     _ => continue,
                 }
             },
@@ -361,12 +368,11 @@ fn eval_defun_args(arg_forms: &[Exp], env: &mut Env) -> Result<Exp, Err> {
     eval_label_args(&label_args, env)
 }
 
-fn eval_print_args(arg_forms: &[Exp], env: &mut Env) -> Result<Exp, Err> {
-    let first_form = arg_forms.first().ok_or(Err::Reason("Expected first form".to_string()))?;
-    if arg_forms.len() > 1 {
+fn eval_print_args(args: &[Exp], env: &mut Env) -> Result<Exp, Err> {
+    if args.len() > 1 {
         return Err(Err::Reason("Print can only have one form".to_string()))
     }
-    match eval(first_form, env) {
+    match eval(&first(args)?, env) {
         Ok(Exp::Str(s)) => {
             println!("{}", s);
             Ok(Exp::Str(s))
@@ -381,6 +387,31 @@ fn eval_print_args(arg_forms: &[Exp], env: &mut Env) -> Result<Exp, Err> {
     }
 }
 
+fn eval_syscall_args(arg_forms: &[Exp], env: &mut Env) -> Result<Exp, Err> {
+    let first_form = arg_forms.first().ok_or(Err::Reason("Expected syscall number".to_string()))?;
+    let first_eval = eval(first_form, env)?;
+    let number = match first_eval {
+        Exp::Num(n) => n,
+        _ => return Err(Err::Reason("Expected syscall number".to_string())),
+    };
+    let args: Result<Vec<u64>, Err> = arg_forms.iter().map(|arg_form| {
+        match eval(arg_form, env) {
+            Ok(Exp::Str(s)) => Ok(s.as_ptr() as u64),
+            Ok(Exp::Num(i)) => Ok(i as u64),
+            _ => Err(Err::Reason("Invalid syscall argument".to_string())),
+        }
+    }).collect();
+    let args = &args?[1..];
+    let res = match args.len() {
+        0 => unsafe { syscall!(number) },
+        1 => unsafe { syscall!(number, args[0]) },
+        2 => unsafe { syscall!(number, args[0], args[1]) },
+        3 => unsafe { syscall!(number, args[0], args[1], args[2]) },
+        _ => return Err(Err::Reason("Invalid number of syscall arguments".to_string())),
+    };
+    Ok(Exp::Num(res as f64))
+}
+
 fn eval_built_in_form(exp: &Exp, arg_forms: &[Exp], env: &mut Env) -> Option<Result<Exp, Err>> {
     match exp {
         Exp::Sym(s) => {
@@ -400,6 +431,7 @@ fn eval_built_in_form(exp: &Exp, arg_forms: &[Exp], env: &mut Env) -> Option<Res
 
                 "defun" | "defn" => Some(eval_defun_args(arg_forms, env)),
                 "print"          => Some(eval_print_args(arg_forms, env)),
+                "syscall"        => Some(eval_syscall_args(arg_forms, env)),
                 _                => None,
             }
         },
@@ -419,7 +451,7 @@ fn env_get(k: &str, env: &Env) -> Option<Exp> {
     }
 }
 
-fn list_of_symbol_strings(form: Rc<Exp>) -> Result<Vec<String>, Err> {
+fn list_of_symbols(form: Rc<Exp>) -> Result<Vec<String>, Err> {
     let list = match form.as_ref() {
         Exp::List(s) => Ok(s.clone()),
         _ => Err(Err::Reason("Expected args form to be a list".to_string()))
@@ -433,7 +465,7 @@ fn list_of_symbol_strings(form: Rc<Exp>) -> Result<Vec<String>, Err> {
 }
 
 fn env_for_lambda<'a>(params: Rc<Exp>, arg_forms: &[Exp], outer_env: &'a mut Env) -> Result<Env<'a>, Err> {
-    let ks = list_of_symbol_strings(params)?;
+    let ks = list_of_symbols(params)?;
     if ks.len() != arg_forms.len() {
         return Err(Err::Reason(format!("Expected {} arguments, got {}", ks.len(), arg_forms.len())));
     }
@@ -454,7 +486,7 @@ fn eval_forms(arg_forms: &[Exp], env: &mut Env) -> Result<Vec<Exp>, Err> {
 
 fn eval(exp: &Exp, env: &mut Env) -> Result<Exp, Err> {
     match exp {
-        Exp::Sym(k) => env_get(k, env).ok_or(Err::Reason(format!("Unexpected symbol k='{}'", k))),
+        Exp::Sym(k) => env_get(k, env).ok_or(Err::Reason(format!("Unexpected symbol '{}'", k))),
         Exp::Bool(_) => Ok(exp.clone()),
         Exp::Num(_) => Ok(exp.clone()),
         Exp::Str(_) => Ok(exp.clone()),
@@ -536,6 +568,10 @@ fn repl(env: &mut Env) -> usr::shell::ExitCode {
             println!();
             continue;
         }
+        match parse_exp(&line) {
+            Ok((_, exp)) => debug!("{}", exp),
+            Err(e) => debug!("{:?}", e),
+        }
         match parse_eval(&line, env) {
             Ok(res) => {
                 println!("{}\n", res);
@@ -676,4 +712,7 @@ fn test_lisp() {
     assert_eq!(eval!("(= 6 4)"), "false");
     assert_eq!(eval!("(= 6 6)"), "true");
     assert_eq!(eval!("(= (+ 0.15 0.15) (+ 0.1 0.2))"), "true");
+
+    // string
+    assert_eq!(eval!("(eq \"Hello, World!\" \"foo\")"), "false");
 }