Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add macro to Lisp #425

Merged
merged 19 commits into from
Oct 28, 2022
4 changes: 0 additions & 4 deletions dsk/lib/lisp/core.lsp
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,6 @@
(f (first ls))
(map f (rest ls)))))

(def (append x y)
(if (nil? x) y
(cons (first x) (append (rest x) y))))

(def (reverse x)
(if (nil? x) x
(append (reverse (rest x)) (cons (first x) '()))))
Expand Down
73 changes: 54 additions & 19 deletions src/usr/lisp/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use super::eval::BUILT_INS;
use super::eval::eval_args;
use super::list_of_bytes;
use super::list_of_numbers;
use super::list_of_symbols;
use super::parse::parse;
use super::{Err, Exp, Number};
use super::{float, number, string};
Expand Down Expand Up @@ -265,13 +264,14 @@ pub fn default_env() -> Rc<RefCell<Env>> {
data.insert("type".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
ensure_length_eq!(args, 1);
let exp = match args[0] {
Exp::Str(_) => "string",
Exp::Bool(_) => "boolean",
Exp::Sym(_) => "symbol",
Exp::Num(_) => "number",
Exp::List(_) => "list",
Exp::Primitive(_) => "function",
Exp::Lambda(_) => "function",
Exp::Function(_) => "function",
Exp::Macro(_) => "macro",
Exp::List(_) => "list",
Exp::Bool(_) => "boolean",
Exp::Str(_) => "string",
Exp::Sym(_) => "symbol",
Exp::Num(_) => "number",
};
Ok(Exp::Str(exp.to_string()))
}));
Expand All @@ -284,15 +284,26 @@ pub fn default_env() -> Rc<RefCell<Env>> {
_ => Err(Err::Reason("Expected arg to be a number".to_string()))
}
}));
data.insert("list".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
Ok(Exp::List(args.to_vec()))
}));
data.insert("parse".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
ensure_length_eq!(args, 1);
let s = string(&args[0])?;
let (_, exp) = parse(&s)?;
Ok(exp)
}));
data.insert("list".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
Ok(Exp::List(args.to_vec()))
}));
data.insert("append".to_string(), Exp::Primitive(|args: &[Exp]| -> Result<Exp, Err> {
let mut res = vec![];
for arg in args {
if let Exp::List(list) = arg {
res.extend_from_slice(&list);
} else {
return Err(Err::Reason("Expected arg to be a list".to_string()))
}
}
Ok(Exp::List(res))
}));

// Setup autocompletion
*FORMS.lock() = data.keys().cloned().chain(BUILT_INS.map(String::from)).collect();
Expand Down Expand Up @@ -329,16 +340,40 @@ pub fn env_set(key: &str, val: Exp, env: &Rc<RefCell<Env>>) -> Result<(), Err> {
}
}

pub fn lambda_env(params: &Exp, args: &[Exp], outer: &mut Rc<RefCell<Env>>) -> Result<Rc<RefCell<Env>>, 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())));
}
let vs = eval_args(args, outer)?;
enum InnerEnv { Function, Macro }

fn inner_env(kind: InnerEnv, params: &Exp, args: &[Exp], outer: &mut Rc<RefCell<Env>>) -> Result<Rc<RefCell<Env>>, Err> {
let args = match kind {
InnerEnv::Function => eval_args(args, outer)?,
InnerEnv::Macro => args.to_vec(),
};
let mut data: BTreeMap<String, Exp> = BTreeMap::new();
for (k, v) in ks.iter().zip(vs.iter()) {
data.insert(k.clone(), v.clone());
match params {
Exp::Sym(s) => {
data.insert(s.clone(), Exp::List(args));
}
Exp::List(list) => {
if list.len() != args.len() {
let plural = if list.len() == 1 { "" } else { "s" };
return Err(Err::Reason(format!("Expected {} argument{}, got {}", list.len(), plural, args.len())));
}
for (exp, arg) in list.iter().zip(args.iter()) {
if let Exp::Sym(s) = exp {
data.insert(s.clone(), arg.clone());
} else {
return Err(Err::Reason("Expected symbols in the argument list".to_string()));
}
}
}
_ => return Err(Err::Reason("Expected args form to be a list".to_string())),
}
Ok(Rc::new(RefCell::new(Env { data, outer: Some(Rc::new(RefCell::new(outer.borrow_mut().clone()))) })))
}

pub fn function_env(params: &Exp, args: &[Exp], outer: &mut Rc<RefCell<Env>>) -> Result<Rc<RefCell<Env>>, Err> {
inner_env(InnerEnv::Function, params, args, outer)
}

pub fn macro_env(params: &Exp, args: &[Exp], outer: &mut Rc<RefCell<Env>>) -> Result<Rc<RefCell<Env>>, Err> {
inner_env(InnerEnv::Macro, params, args, outer)
}
85 changes: 25 additions & 60 deletions src/usr/lisp/eval.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::{Err, Exp, Env, Lambda};
use super::env::{env_get, env_set, lambda_env};
use super::{Err, Exp, Env, Function};
use super::env::{env_get, env_set, function_env};
use super::parse::parse;
use super::expand::expand;
use super::string;

use crate::{ensure_length_eq, ensure_length_gt};
Expand Down Expand Up @@ -67,43 +68,15 @@ fn eval_cons_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err>
}
}

// TODO: Remove this when macro is enabled
fn eval_cond_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
ensure_length_gt!(args, 0);
for arg in args {
match arg {
Exp::List(list) => {
ensure_length_eq!(list, 2);
match eval(&list[0], env)? {
Exp::Bool(b) if b => return eval(&list[1], env),
_ => continue,
}
},
_ => return Err(Err::Reason("Expected lists of predicate and expression".to_string())),
}
}
Ok(Exp::List(vec![]))
}

pub fn eval_label_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
pub fn eval_define_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
ensure_length_eq!(args, 2);
match &args[0] {
Exp::Sym(name) => {
let exp = eval(&args[1], env)?;
env.borrow_mut().data.insert(name.clone(), exp);
Ok(Exp::Sym(name.clone()))
}
Exp::List(params) => {
// (label (add x y) (+ x y)) => (label add (lambda (x y) (+ x y)))
ensure_length_gt!(params, 0);
let name = params[0].clone();
let params = Exp::List(params[1..].to_vec());
let body = args[1].clone();
let lambda_args = vec![Exp::Sym("lambda".to_string()), params, body];
let label_args = vec![name, Exp::List(lambda_args)];
eval_label_args(&label_args, env)
}
_ => Err(Err::Reason("Expected first argument to be a symbol or a list".to_string()))
_ => Err(Err::Reason("Expected first argument to be a symbol".to_string()))
}
}

Expand Down Expand Up @@ -131,18 +104,6 @@ fn eval_while_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err>
Ok(res)
}

// TODO: Remove this when macro is enabled
fn eval_defun_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
// (defun add (x y) (+ x y)) => (label add (lambda (x y) (+ x y)))
ensure_length_eq!(args, 3);
let name = args[0].clone();
let params = args[1].clone();
let body = args[2].clone();
let lambda_args = vec![Exp::Sym("lambda".to_string()), params, body];
let label_args = vec![name, Exp::List(lambda_args)];
eval_label_args(&label_args, env)
}

fn eval_apply_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
ensure_length_gt!(args, 1);
let mut args = args.to_vec();
Expand All @@ -159,7 +120,7 @@ fn eval_eval_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err>
eval(&exp, env)
}

fn eval_progn_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
fn eval_do_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
let mut res = Ok(Exp::List(vec![]));
for arg in args {
res = Ok(eval(arg, env)?);
Expand All @@ -173,6 +134,7 @@ fn eval_load_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err>
let mut code = fs::read_to_string(&path).or(Err(Err::Reason("Could not read file".to_string())))?;
loop {
let (rest, exp) = parse(&code)?;
let exp = expand(&exp, env)?;
eval(&exp, env)?;
if rest.is_empty() {
break;
Expand Down Expand Up @@ -213,20 +175,17 @@ pub fn eval(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
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 == "do" => return eval_do_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 == "define" => return eval_define_args(args, env),
Exp::Sym(s) if s == "expand" => {
ensure_length_eq!(args, 1);
return expand(&args[0], env);
}
Exp::Sym(s) if s == "if" => {
ensure_length_gt!(args, 1);
if eval(&args[0], env)? == Exp::Bool(true) { // consequent
Expand All @@ -238,17 +197,24 @@ pub fn eval(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
}
exp = &exp_tmp;
}
Exp::Sym(s) if s == "lambda" || s == "function" || s == "fun" || s == "fn" => {
Exp::Sym(s) if s == "function" => {
ensure_length_eq!(args, 2);
return Ok(Exp::Function(Box::new(Function {
params: args[0].clone(),
body: args[1].clone(),
})))
}
Exp::Sym(s) if s == "macro" => {
ensure_length_eq!(args, 2);
return Ok(Exp::Lambda(Box::new(Lambda {
return Ok(Exp::Macro(Box::new(Function {
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::Function(f) => {
env_tmp = function_env(&f.params, args, env)?;
exp_tmp = f.body;
env = &mut env_tmp;
exp = &exp_tmp;
Expand All @@ -261,8 +227,7 @@ pub fn eval(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
}
}
},
Exp::Primitive(_) => return Err(Err::Reason("Unexpected form".to_string())),
Exp::Lambda(_) => return Err(Err::Reason("Unexpected form".to_string())),
_ => return Err(Err::Reason("Unexpected form".to_string())),
}
}
}
Loading