diff --git a/dsk/tmp/lisp/sum.lsp b/dsk/tmp/lisp/sum.lsp new file mode 100644 index 000000000..abf1bc796 --- /dev/null +++ b/dsk/tmp/lisp/sum.lsp @@ -0,0 +1,8 @@ +(load "/lib/lisp/core.lsp") + +(def (sum n acc) + (if (= n 0) acc (sum (- n 1) (+ n acc)))) + +(println + (if (nil? args) "Usage: sum " + (sum (string->number (car args)) 0))) diff --git a/src/usr/install.rs b/src/usr/install.rs index 4c42deaa4..64e81f3bc 100644 --- a/src/usr/install.rs +++ b/src/usr/install.rs @@ -56,6 +56,7 @@ pub fn copy_files(verbose: bool) { copy_file("/tmp/lisp/factorial.lsp", include_bytes!("../../dsk/tmp/lisp/factorial.lsp"), verbose); copy_file("/tmp/lisp/fibonacci.lsp", include_bytes!("../../dsk/tmp/lisp/fibonacci.lsp"), verbose); copy_file("/tmp/lisp/pi.lsp", include_bytes!("../../dsk/tmp/lisp/pi.lsp"), verbose); + copy_file("/tmp/lisp/sum.lsp", include_bytes!("../../dsk/tmp/lisp/sum.lsp"), verbose); create_dir("/tmp/life", verbose); copy_file("/tmp/life/centinal.cells", include_bytes!("../../dsk/tmp/life/centinal.cells"), verbose); diff --git a/src/usr/lisp/env.rs b/src/usr/lisp/env.rs index 6dd90c9f5..6451a48a0 100644 --- a/src/usr/lisp/env.rs +++ b/src/usr/lisp/env.rs @@ -329,8 +329,8 @@ pub fn env_set(key: &str, val: Exp, env: &Rc>) -> Result<(), Err> { } } -pub fn lambda_env(params: Rc, args: &[Exp], outer: &mut Rc>) -> Result>, Err> { - let ks = list_of_symbols(¶ms)?; +pub fn lambda_env(params: &Exp, args: &[Exp], outer: &mut Rc>) -> Result>, Err> { + let ks = list_of_symbols(params)?; if ks.len() != args.len() { let plural = if ks.len() == 1 { "" } else { "s" }; return Err(Err::Reason(format!("Expected {} argument{}, got {}", ks.len(), plural, args.len()))); diff --git a/src/usr/lisp/eval.rs b/src/usr/lisp/eval.rs index 7124871cd..aace6b11b 100644 --- a/src/usr/lisp/eval.rs +++ b/src/usr/lisp/eval.rs @@ -6,6 +6,7 @@ use super::string; use crate::{ensure_length_eq, ensure_length_gt}; use crate::api::fs; +use alloc::boxed::Box; use alloc::format; use alloc::rc::Rc; use alloc::string::ToString; @@ -60,12 +61,13 @@ fn eval_cons_args(args: &[Exp], env: &mut Rc>) -> Result match eval(&args[1], env)? { Exp::List(mut list) => { list.insert(0, eval(&args[0], env)?); - Ok(Exp::List(list.to_vec())) + Ok(Exp::List(list)) }, _ => Err(Err::Reason("Expected list form".to_string())), } } +// TODO: Remove this when macro is enabled fn eval_cond_args(args: &[Exp], env: &mut Rc>) -> Result { ensure_length_gt!(args, 0); for arg in args { @@ -83,17 +85,6 @@ fn eval_cond_args(args: &[Exp], env: &mut Rc>) -> Result Ok(Exp::List(vec![])) } -fn eval_if_args(args: &[Exp], env: &mut Rc>) -> Result { - ensure_length_gt!(args, 1); - if eval(&args[0], env)? == Exp::Bool(true) { - eval(&args[1], env) - } else if args.len() > 2 { - eval(&args[2], env) - } else { - Ok(Exp::List(vec![])) - } -} - pub fn eval_label_args(args: &[Exp], env: &mut Rc>) -> Result { ensure_length_eq!(args, 2); match &args[0] { @@ -140,14 +131,7 @@ fn eval_while_args(args: &[Exp], env: &mut Rc>) -> Result Ok(res) } -fn eval_lambda_args(args: &[Exp]) -> Result { - ensure_length_eq!(args, 2); - Ok(Exp::Lambda(Lambda { - params: Rc::new(args[0].clone()), - body: Rc::new(args[1].clone()), - })) -} - +// TODO: Remove this when macro is enabled fn eval_defun_args(args: &[Exp], env: &mut Rc>) -> Result { // (defun add (x y) (+ x y)) => (label add (lambda (x y) (+ x y))) ensure_length_eq!(args, 3); @@ -198,76 +182,87 @@ fn eval_load_args(args: &[Exp], env: &mut Rc>) -> Result Ok(Exp::Bool(true)) } -pub const BUILT_INS: [&str; 24] = [ - "quote", "atom", "eq", "car", "cdr", "cons", "cond", "label", "lambda", "define", "def", - "function", "fun", "fn", "if", "while", "defun", "defn", "apply", "eval", "progn", "begin", "do", - "load" -]; - -fn eval_built_in_form(exp: &Exp, args: &[Exp], env: &mut Rc>) -> Option> { - match exp { - Exp::Sym(s) => { - match s.as_ref() { - // Seven Primitive Operators - "quote" => Some(eval_quote_args(args)), - "atom" => Some(eval_atom_args(args, env)), - "eq" => Some(eval_eq_args(args, env)), - "car" => Some(eval_car_args(args, env)), - "cdr" => Some(eval_cdr_args(args, env)), - "cons" => Some(eval_cons_args(args, env)), - "cond" => Some(eval_cond_args(args, env)), - - // Two Special Forms - "label" | "define" | "def" => Some(eval_label_args(args, env)), - "lambda" | "function" | "fun" | "fn" => Some(eval_lambda_args(args)), - - "if" => Some(eval_if_args(args, env)), - "set" => Some(eval_set_args(args, env)), - "while" => Some(eval_while_args(args, env)), - "defun" | "defn" => Some(eval_defun_args(args, env)), - "apply" => Some(eval_apply_args(args, env)), - "eval" => Some(eval_eval_args(args, env)), - "progn" | "begin" | "do" => Some(eval_progn_args(args, env)), - "load" => Some(eval_load_args(args, env)), - _ => None, - } - }, - _ => None, - } -} - pub fn eval_args(args: &[Exp], env: &mut Rc>) -> Result, Err> { args.iter().map(|x| eval(x, env)).collect() } +pub const BUILT_INS: [&str; 24] = [ + "quote", "atom", "eq", "car", "cdr", "cons", "cond", "label", "lambda", "define", "def", + "function", "fun", "fn", "if", "while", "defun", "defn", "apply", "eval", "progn", "begin", + "do", "load" +]; + pub fn eval(exp: &Exp, env: &mut Rc>) -> Result { - match exp { - Exp::Sym(key) => env_get(key, env), - Exp::Bool(_) => Ok(exp.clone()), - Exp::Num(_) => Ok(exp.clone()), - Exp::Str(_) => Ok(exp.clone()), - Exp::List(list) => { - ensure_length_gt!(list, 0); - let first_form = &list[0]; - let args = &list[1..]; - match eval_built_in_form(first_form, args, env) { - Some(res) => res, - None => { - let first_eval = eval(first_form, env)?; - match first_eval { - Exp::Primitive(f) => { - f(&eval_args(args, env)?) - }, - Exp::Lambda(f) => { - let mut env = lambda_env(f.params, args, env)?; - eval(&f.body, &mut env) - }, - _ => Err(Err::Reason("First form must be a function".to_string())), + let mut exp = exp; + let mut env = env; + let mut env_tmp; + let mut exp_tmp; + loop { + match exp { + Exp::Sym(key) => return env_get(key, env), + Exp::Bool(_) => return Ok(exp.clone()), + Exp::Num(_) => return Ok(exp.clone()), + Exp::Str(_) => return Ok(exp.clone()), + Exp::List(list) => { + ensure_length_gt!(list, 0); + let args = &list[1..]; + match &list[0] { + Exp::Sym(s) if s == "quote" => return eval_quote_args(args), + Exp::Sym(s) if s == "atom" => return eval_atom_args(args, env), + Exp::Sym(s) if s == "eq" => return eval_eq_args(args, env), + Exp::Sym(s) if s == "car" => return eval_car_args(args, env), + Exp::Sym(s) if s == "cdr" => return eval_cdr_args(args, env), + Exp::Sym(s) if s == "cons" => return eval_cons_args(args, env), + Exp::Sym(s) if s == "cond" => return eval_cond_args(args, env), + Exp::Sym(s) if s == "set" => return eval_set_args(args, env), + Exp::Sym(s) if s == "while" => return eval_while_args(args, env), + Exp::Sym(s) if s == "defun" => return eval_defun_args(args, env), + Exp::Sym(s) if s == "defn" => return eval_defun_args(args, env), + Exp::Sym(s) if s == "apply" => return eval_apply_args(args, env), + Exp::Sym(s) if s == "eval" => return eval_eval_args(args, env), + Exp::Sym(s) if s == "progn" => return eval_progn_args(args, env), + Exp::Sym(s) if s == "begin" => return eval_progn_args(args, env), + Exp::Sym(s) if s == "do" => return eval_progn_args(args, env), + Exp::Sym(s) if s == "load" => return eval_load_args(args, env), + Exp::Sym(s) if s == "label" => return eval_label_args(args, env), + Exp::Sym(s) if s == "define" => return eval_label_args(args, env), + Exp::Sym(s) if s == "def" => return eval_label_args(args, env), + Exp::Sym(s) if s == "if" => { + ensure_length_gt!(args, 1); + if eval(&args[0], env)? == Exp::Bool(true) { // consequent + exp_tmp = args[1].clone(); + } else if args.len() > 2 { // alternate + exp_tmp = args[2].clone(); + } else { // '() + exp_tmp = Exp::List(vec![Exp::Sym("quote".to_string()), Exp::List(vec![])]); + } + exp = &exp_tmp; + } + Exp::Sym(s) if s == "lambda" || s == "function" || s == "fun" || s == "fn" => { + ensure_length_eq!(args, 2); + return Ok(Exp::Lambda(Box::new(Lambda { + params: args[0].clone(), + body: args[1].clone(), + }))) + } + _ => { + match eval(&list[0], env)? { + Exp::Lambda(f) => { + env_tmp = lambda_env(&f.params, args, env)?; + exp_tmp = f.body; + env = &mut env_tmp; + exp = &exp_tmp; + }, + Exp::Primitive(f) => { + return f(&eval_args(args, env)?) + }, + _ => return Err(Err::Reason("First form must be a function".to_string())), + } } } - } - }, - Exp::Primitive(_) => Err(Err::Reason("Unexpected form".to_string())), - Exp::Lambda(_) => Err(Err::Reason("Unexpected form".to_string())), + }, + Exp::Primitive(_) => return Err(Err::Reason("Unexpected form".to_string())), + Exp::Lambda(_) => return Err(Err::Reason("Unexpected form".to_string())), + } } } diff --git a/src/usr/lisp/mod.rs b/src/usr/lisp/mod.rs index 5e4208819..872c036c9 100644 --- a/src/usr/lisp/mod.rs +++ b/src/usr/lisp/mod.rs @@ -15,6 +15,7 @@ use crate::api::console::Style; use crate::api::process::ExitCode; use crate::api::prompt::Prompt; +use alloc::boxed::Box; use alloc::format; use alloc::rc::Rc; use alloc::string::String; @@ -45,7 +46,7 @@ use spin::Mutex; #[derive(Clone)] pub enum Exp { Primitive(fn(&[Exp]) -> Result), - Lambda(Lambda), + Lambda(Box), List(Vec), Bool(bool), Num(Number), @@ -87,8 +88,8 @@ impl fmt::Display for Exp { #[derive(Clone, PartialEq)] pub struct Lambda { - params: Rc, - body: Rc, + params: Exp, + body: Exp, } #[derive(Debug)]