From d0a99e665c3603b5107adb35712b410eb7b87426 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Tue, 25 Oct 2022 22:21:46 +0200 Subject: [PATCH 01/19] Add expand to lisp --- src/usr/lisp/eval.rs | 133 ++++++++++++++++++++++++++----------------- src/usr/lisp/mod.rs | 5 +- 2 files changed, 83 insertions(+), 55 deletions(-) diff --git a/src/usr/lisp/eval.rs b/src/usr/lisp/eval.rs index aace6b11b..76060e8b1 100644 --- a/src/usr/lisp/eval.rs +++ b/src/usr/lisp/eval.rs @@ -67,25 +67,7 @@ fn eval_cons_args(args: &[Exp], env: &mut Rc>) -> Result } } -// 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 { - 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>) -> Result { +pub fn eval_define_args(args: &[Exp], env: &mut Rc>) -> Result { ensure_length_eq!(args, 2); match &args[0] { Exp::Sym(name) => { @@ -93,17 +75,7 @@ pub fn eval_label_args(args: &[Exp], env: &mut Rc>) -> Result { - // (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())) } } @@ -131,18 +103,6 @@ fn eval_while_args(args: &[Exp], env: &mut Rc>) -> Result Ok(res) } -// 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); - 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>) -> Result { ensure_length_gt!(args, 1); let mut args = args.to_vec(); @@ -159,7 +119,7 @@ fn eval_eval_args(args: &[Exp], env: &mut Rc>) -> Result eval(&exp, env) } -fn eval_progn_args(args: &[Exp], env: &mut Rc>) -> Result { +fn eval_do_args(args: &[Exp], env: &mut Rc>) -> Result { let mut res = Ok(Exp::List(vec![])); for arg in args { res = Ok(eval(arg, env)?); @@ -173,6 +133,7 @@ fn eval_load_args(args: &[Exp], env: &mut Rc>) -> Result 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)?; eval(&exp, env)?; if rest.is_empty() { break; @@ -213,20 +174,17 @@ pub fn eval(exp: &Exp, env: &mut Rc>) -> Result { 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]); + } Exp::Sym(s) if s == "if" => { ensure_length_gt!(args, 1); if eval(&args[0], env)? == Exp::Bool(true) { // consequent @@ -238,7 +196,7 @@ pub fn eval(exp: &Exp, env: &mut Rc>) -> Result { } 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::Lambda(Box::new(Lambda { params: args[0].clone(), @@ -266,3 +224,72 @@ pub fn eval(exp: &Exp, env: &mut Rc>) -> Result { } } } + +pub fn expand(exp: &Exp) -> Result { + if let Exp::List(list) = exp { + ensure_length_gt!(list, 0); + match &list[0] { + Exp::Sym(s) if s == "quote" => { + ensure_length_eq!(list, 2); + Ok(exp.clone()) + } + Exp::Sym(s) if s == "begin" || s == "progn" => { + let mut res = vec![Exp::Sym("do".to_string())]; + res.extend_from_slice(&list[1..]); + expand(&Exp::List(res)) + } + Exp::Sym(s) if s == "def" || s == "label" => { + let mut res = vec![Exp::Sym("define".to_string())]; + res.extend_from_slice(&list[1..]); + expand(&Exp::List(res)) + } + Exp::Sym(s) if s == "fun" || s == "fn" || s == "lambda" => { + let mut res = vec![Exp::Sym("function".to_string())]; + res.extend_from_slice(&list[1..]); + expand(&Exp::List(res)) + } + Exp::Sym(s) if s == "define-function" || s == "def-fun" || s == "define" => { + ensure_length_eq!(list, 3); + match (&list[1], &list[2]) { + (Exp::List(args), Exp::List(_)) => { + ensure_length_gt!(args, 0); + let name = args[0].clone(); + let args = Exp::List(args[1..].to_vec()); + let body = expand(&list[2])?; + Ok(Exp::List(vec![ + Exp::Sym("define".to_string()), name, Exp::List(vec![ + Exp::Sym("function".to_string()), args, body + ]) + ])) + } + (Exp::Sym(_), _) => { // TODO: dry this + let expanded: Result, Err> = list.iter().map(|item| expand(item)).collect(); + Ok(Exp::List(expanded?)) + } + _ => Err(Err::Reason("Expected first argument to be a symbol or a list".to_string())) + } + } + Exp::Sym(s) if s == "cond" => { + ensure_length_gt!(list, 1); + if let Exp::List(args) = &list[1] { + ensure_length_eq!(args, 2); + let mut res = vec![Exp::Sym("if".to_string()), args[0].clone(), args[1].clone()]; + if list.len() > 2 { + let mut acc = vec![Exp::Sym("cond".to_string())]; + acc.extend_from_slice(&list[2..]); + res.push(expand(&Exp::List(acc))?); + } + Ok(Exp::List(res)) + } else { + Err(Err::Reason("Expected lists of predicate and expression".to_string())) + } + } + _ => { + let expanded: Result, Err> = list.iter().map(|item| expand(item)).collect(); + Ok(Exp::List(expanded?)) + } + } + } else { + Ok(exp.clone()) + } +} diff --git a/src/usr/lisp/mod.rs b/src/usr/lisp/mod.rs index 872c036c9..48b0d8354 100644 --- a/src/usr/lisp/mod.rs +++ b/src/usr/lisp/mod.rs @@ -7,7 +7,7 @@ pub use number::Number; pub use env::Env; use env::default_env; -use eval::{eval, eval_label_args}; +use eval::{eval, eval_define_args, expand}; use parse::parse; use crate::api; @@ -172,6 +172,7 @@ pub fn byte(exp: &Exp) -> Result { fn parse_eval(exp: &str, env: &mut Rc>) -> Result { let (_, exp) = parse(exp)?; + let exp = expand(&exp)?; let exp = eval(&exp, env)?; Ok(exp) } @@ -244,7 +245,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> { args[2..].iter().map(|arg| Exp::Str(arg.to_string())).collect() }); let quote = Exp::List(vec![Exp::Sym("quote".to_string()), list]); - if eval_label_args(&[key, quote], env).is_err() { + if eval_define_args(&[key, quote], env).is_err() { error!("Could not parse args"); return Err(ExitCode::Failure); } From 23ce3b71ba480a0823052dc09e96f378df50daad Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Tue, 25 Oct 2022 22:36:31 +0200 Subject: [PATCH 02/19] Update tests --- src/usr/lisp/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/usr/lisp/mod.rs b/src/usr/lisp/mod.rs index 48b0d8354..378218787 100644 --- a/src/usr/lisp/mod.rs +++ b/src/usr/lisp/mod.rs @@ -369,9 +369,11 @@ fn test_lisp() { assert_eq!(eval!("((lambda (a) (* a a)) 2)"), "4"); assert_eq!(eval!("((lambda (x) (cons x '(b c))) 'a)"), "(a b c)"); - // defun - eval!("(defun add (a b) (+ a b))"); - assert_eq!(eval!("(add 1 2)"), "3"); + // function definition shortcut + eval!("(define (double x) (* x 2))"); + assert_eq!(eval!("(double 2)"), "4"); + eval!("(define-function (triple x) (* x 3))"); + assert_eq!(eval!("(triple 2)"), "6"); // addition assert_eq!(eval!("(+)"), "0"); From a1dadc8235c692cb5660f968035bb984b9d7e255 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Wed, 26 Oct 2022 09:16:48 +0200 Subject: [PATCH 03/19] Add quasiquote and unquote --- src/usr/lisp/eval.rs | 24 ++++++++++++++++++++++++ src/usr/lisp/mod.rs | 5 +++++ src/usr/lisp/parse.rs | 16 +++++++++++++++- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/usr/lisp/eval.rs b/src/usr/lisp/eval.rs index 76060e8b1..8bf2ff55b 100644 --- a/src/usr/lisp/eval.rs +++ b/src/usr/lisp/eval.rs @@ -225,6 +225,26 @@ pub fn eval(exp: &Exp, env: &mut Rc>) -> Result { } } +pub fn expand_quasiquote(exp: &Exp) -> Result { + match exp { + Exp::List(list) if list.len() > 0 => { + match &list[0] { + Exp::Sym(s) if s == "unquote" => { + Ok(list[1].clone()) + } + _ => { + Ok(Exp::List(vec![ + Exp::Sym("cons".to_string()), + expand_quasiquote(&list[0])?, + expand_quasiquote(&Exp::List(list[1..].to_vec()))?, + ])) + } + } + } + _ => Ok(Exp::List(vec![Exp::Sym("quote".to_string()), exp.clone()])), + } +} + pub fn expand(exp: &Exp) -> Result { if let Exp::List(list) = exp { ensure_length_gt!(list, 0); @@ -233,6 +253,10 @@ pub fn expand(exp: &Exp) -> Result { ensure_length_eq!(list, 2); Ok(exp.clone()) } + Exp::Sym(s) if s == "quasiquote" => { + ensure_length_eq!(list, 2); + expand_quasiquote(&list[1]) + } Exp::Sym(s) if s == "begin" || s == "progn" => { let mut res = vec![Exp::Sym("do".to_string())]; res.extend_from_slice(&list[1..]); diff --git a/src/usr/lisp/mod.rs b/src/usr/lisp/mod.rs index 378218787..73c619c83 100644 --- a/src/usr/lisp/mod.rs +++ b/src/usr/lisp/mod.rs @@ -472,4 +472,9 @@ fn test_lisp() { assert_eq!(eval!("(number-type 9223372036854775807)"), "\"int\""); assert_eq!(eval!("(number-type 9223372036854775808)"), "\"bigint\""); assert_eq!(eval!("(number-type 9223372036854776000.0)"), "\"float\""); + + // quasiquote + eval!("(def x 'a)"); + assert_eq!(eval!("`(x ,x y)"), "(x a y)"); + assert_eq!(eval!("`(x ,x y ,(+ 1 2))"), "(x a y 3)"); } diff --git a/src/usr/lisp/parse.rs b/src/usr/lisp/parse.rs index fa983f8b8..bacabd15a 100644 --- a/src/usr/lisp/parse.rs +++ b/src/usr/lisp/parse.rs @@ -67,8 +67,22 @@ fn parse_quote(input: &str) -> IResult<&str, Exp> { Ok((input, Exp::List(list))) } +fn parse_unquote(input: &str) -> IResult<&str, Exp> { + let (input, list) = preceded(char(','), parse_exp)(input)?; + let list = vec![Exp::Sym("unquote".to_string()), list]; + Ok((input, Exp::List(list))) +} + +fn parse_quasiquote(input: &str) -> IResult<&str, Exp> { + let (input, list) = preceded(char('`'), parse_exp)(input)?; + let list = vec![Exp::Sym("quasiquote".to_string()), list]; + Ok((input, Exp::List(list))) +} + fn parse_exp(input: &str) -> IResult<&str, Exp> { - delimited(multispace0, alt((parse_num, parse_bool, parse_str, parse_list, parse_quote, parse_sym)), multispace0)(input) + delimited(multispace0, alt(( + parse_num, parse_bool, parse_str, parse_list, parse_quote, parse_unquote, parse_quasiquote, parse_sym + )), multispace0)(input) } pub fn parse(input: &str)-> Result<(String, Exp), Err> { From 81eeb5f9c3b54d87c36604c17830251405e53246 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Wed, 26 Oct 2022 21:41:50 +0200 Subject: [PATCH 04/19] Rename Lambda into Function --- src/usr/lisp/env.rs | 4 ++-- src/usr/lisp/eval.rs | 12 ++++++------ src/usr/lisp/mod.rs | 32 ++++++++++++++++---------------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/usr/lisp/env.rs b/src/usr/lisp/env.rs index 6451a48a0..faa2f50aa 100644 --- a/src/usr/lisp/env.rs +++ b/src/usr/lisp/env.rs @@ -271,7 +271,7 @@ pub fn default_env() -> Rc> { Exp::Num(_) => "number", Exp::List(_) => "list", Exp::Primitive(_) => "function", - Exp::Lambda(_) => "function", + Exp::Function(_) => "function", }; Ok(Exp::Str(exp.to_string())) })); @@ -329,7 +329,7 @@ pub fn env_set(key: &str, val: Exp, env: &Rc>) -> Result<(), Err> { } } -pub fn lambda_env(params: &Exp, args: &[Exp], outer: &mut Rc>) -> Result>, Err> { +pub fn function_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" }; diff --git a/src/usr/lisp/eval.rs b/src/usr/lisp/eval.rs index 8bf2ff55b..d8f1211d3 100644 --- a/src/usr/lisp/eval.rs +++ b/src/usr/lisp/eval.rs @@ -1,5 +1,5 @@ -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::string; @@ -198,15 +198,15 @@ pub fn eval(exp: &Exp, env: &mut Rc>) -> Result { } Exp::Sym(s) if s == "function" => { ensure_length_eq!(args, 2); - return Ok(Exp::Lambda(Box::new(Lambda { + return Ok(Exp::Function(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; @@ -220,7 +220,7 @@ pub fn eval(exp: &Exp, env: &mut Rc>) -> Result { } }, Exp::Primitive(_) => return Err(Err::Reason("Unexpected form".to_string())), - Exp::Lambda(_) => return Err(Err::Reason("Unexpected form".to_string())), + Exp::Function(_) => return Err(Err::Reason("Unexpected form".to_string())), } } } diff --git a/src/usr/lisp/mod.rs b/src/usr/lisp/mod.rs index 73c619c83..e18d3df68 100644 --- a/src/usr/lisp/mod.rs +++ b/src/usr/lisp/mod.rs @@ -46,7 +46,7 @@ use spin::Mutex; #[derive(Clone)] pub enum Exp { Primitive(fn(&[Exp]) -> Result), - Lambda(Box), + Function(Box), List(Vec), Bool(bool), Num(Number), @@ -57,12 +57,12 @@ pub enum Exp { impl PartialEq for Exp { fn eq(&self, other: &Self) -> bool { match (self, other) { - (Exp::Lambda(a), Exp::Lambda(b)) => a == b, - (Exp::List(a), Exp::List(b)) => a == b, - (Exp::Bool(a), Exp::Bool(b)) => a == b, - (Exp::Num(a), Exp::Num(b)) => a == b, - (Exp::Str(a), Exp::Str(b)) => a == b, - (Exp::Sym(a), Exp::Sym(b)) => a == b, + (Exp::Function(a), Exp::Function(b)) => a == b, + (Exp::List(a), Exp::List(b)) => a == b, + (Exp::Bool(a), Exp::Bool(b)) => a == b, + (Exp::Num(a), Exp::Num(b)) => a == b, + (Exp::Str(a), Exp::Str(b)) => a == b, + (Exp::Sym(a), Exp::Sym(b)) => a == b, _ => false, } } @@ -72,7 +72,7 @@ impl fmt::Display for Exp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let out = match self { Exp::Primitive(_) => "".to_string(), - Exp::Lambda(_) => "".to_string(), + Exp::Function(_) => "".to_string(), Exp::Bool(a) => a.to_string(), Exp::Num(n) => n.to_string(), Exp::Sym(s) => s.clone(), @@ -87,7 +87,7 @@ impl fmt::Display for Exp { } #[derive(Clone, PartialEq)] -pub struct Lambda { +pub struct Function { params: Exp, body: Exp, } @@ -357,17 +357,17 @@ fn test_lisp() { // label eval!("(label a 2)"); assert_eq!(eval!("(+ a 1)"), "3"); - //eval!("(label fn lambda)"); + //eval!("(label fn function)"); //assert_eq!(eval!("((fn (a) (+ 1 a)) 2)"), "3"); - eval!("(label add-one (lambda (b) (+ b 1)))"); + eval!("(label add-one (function (b) (+ b 1)))"); assert_eq!(eval!("(add-one 2)"), "3"); - eval!("(label fib (lambda (n) (cond ((< n 2) n) (true (+ (fib (- n 1)) (fib (- n 2)))))))"); + eval!("(label fib (function (n) (cond ((< n 2) n) (true (+ (fib (- n 1)) (fib (- n 2)))))))"); assert_eq!(eval!("(fib 6)"), "8"); - // lambda - assert_eq!(eval!("((lambda (a) (+ 1 a)) 2)"), "3"); - assert_eq!(eval!("((lambda (a) (* a a)) 2)"), "4"); - assert_eq!(eval!("((lambda (x) (cons x '(b c))) 'a)"), "(a b c)"); + // function + assert_eq!(eval!("((function (a) (+ 1 a)) 2)"), "3"); + assert_eq!(eval!("((function (a) (* a a)) 2)"), "4"); + assert_eq!(eval!("((function (x) (cons x '(b c))) 'a)"), "(a b c)"); // function definition shortcut eval!("(define (double x) (* x 2))"); From bb9ea3d92e329a7c6f77d0044d6da0fbb80f50b6 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Wed, 26 Oct 2022 22:37:46 +0200 Subject: [PATCH 05/19] Add macro --- src/usr/lisp/env.rs | 17 +++++++++-------- src/usr/lisp/eval.rs | 42 +++++++++++++++++++++++++++++------------- src/usr/lisp/mod.rs | 11 ++++++++++- 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/src/usr/lisp/env.rs b/src/usr/lisp/env.rs index faa2f50aa..050e0535f 100644 --- a/src/usr/lisp/env.rs +++ b/src/usr/lisp/env.rs @@ -265,13 +265,14 @@ pub fn default_env() -> Rc> { data.insert("type".to_string(), Exp::Primitive(|args: &[Exp]| -> Result { 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::Function(_) => "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())) })); @@ -329,13 +330,13 @@ pub fn env_set(key: &str, val: Exp, env: &Rc>) -> Result<(), Err> { } } -pub fn function_env(params: &Exp, args: &[Exp], outer: &mut Rc>) -> Result>, Err> { +pub fn function_env(params: &Exp, args: &[Exp], outer: &mut Rc>, is_macro: bool) -> 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()))); } - let vs = eval_args(args, outer)?; + let vs = if is_macro { args } else { eval_args(args, outer)? }; let mut data: BTreeMap = BTreeMap::new(); for (k, v) in ks.iter().zip(vs.iter()) { data.insert(k.clone(), v.clone()); diff --git a/src/usr/lisp/eval.rs b/src/usr/lisp/eval.rs index d8f1211d3..d8c5767a0 100644 --- a/src/usr/lisp/eval.rs +++ b/src/usr/lisp/eval.rs @@ -133,7 +133,7 @@ fn eval_load_args(args: &[Exp], env: &mut Rc>) -> Result 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)?; + let exp = expand(&exp, env)?; eval(&exp, env)?; if rest.is_empty() { break; @@ -183,7 +183,7 @@ pub fn eval(exp: &Exp, env: &mut Rc>) -> Result { 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]); + return expand(&args[0], env); } Exp::Sym(s) if s == "if" => { ensure_length_gt!(args, 1); @@ -203,10 +203,17 @@ pub fn eval(exp: &Exp, env: &mut Rc>) -> Result { body: args[1].clone(), }))) } + Exp::Sym(s) if s == "macro" => { + ensure_length_eq!(args, 2); + return Ok(Exp::Macro(Box::new(Function { + params: args[0].clone(), + body: args[1].clone(), + }))) + } _ => { match eval(&list[0], env)? { Exp::Function(f) => { - env_tmp = function_env(&f.params, args, env)?; + env_tmp = function_env(&f.params, args, env, false)?; exp_tmp = f.body; env = &mut env_tmp; exp = &exp_tmp; @@ -219,8 +226,7 @@ pub fn eval(exp: &Exp, env: &mut Rc>) -> Result { } } }, - Exp::Primitive(_) => return Err(Err::Reason("Unexpected form".to_string())), - Exp::Function(_) => return Err(Err::Reason("Unexpected form".to_string())), + _ => return Err(Err::Reason("Unexpected form".to_string())), } } } @@ -245,7 +251,7 @@ pub fn expand_quasiquote(exp: &Exp) -> Result { } } -pub fn expand(exp: &Exp) -> Result { +pub fn expand(exp: &Exp, env: &mut Rc>) -> Result { if let Exp::List(list) = exp { ensure_length_gt!(list, 0); match &list[0] { @@ -260,17 +266,17 @@ pub fn expand(exp: &Exp) -> Result { Exp::Sym(s) if s == "begin" || s == "progn" => { let mut res = vec![Exp::Sym("do".to_string())]; res.extend_from_slice(&list[1..]); - expand(&Exp::List(res)) + expand(&Exp::List(res), env) } Exp::Sym(s) if s == "def" || s == "label" => { let mut res = vec![Exp::Sym("define".to_string())]; res.extend_from_slice(&list[1..]); - expand(&Exp::List(res)) + expand(&Exp::List(res), env) } Exp::Sym(s) if s == "fun" || s == "fn" || s == "lambda" => { let mut res = vec![Exp::Sym("function".to_string())]; res.extend_from_slice(&list[1..]); - expand(&Exp::List(res)) + expand(&Exp::List(res), env) } Exp::Sym(s) if s == "define-function" || s == "def-fun" || s == "define" => { ensure_length_eq!(list, 3); @@ -279,7 +285,7 @@ pub fn expand(exp: &Exp) -> Result { ensure_length_gt!(args, 0); let name = args[0].clone(); let args = Exp::List(args[1..].to_vec()); - let body = expand(&list[2])?; + let body = expand(&list[2], env)?; Ok(Exp::List(vec![ Exp::Sym("define".to_string()), name, Exp::List(vec![ Exp::Sym("function".to_string()), args, body @@ -287,7 +293,7 @@ pub fn expand(exp: &Exp) -> Result { ])) } (Exp::Sym(_), _) => { // TODO: dry this - let expanded: Result, Err> = list.iter().map(|item| expand(item)).collect(); + let expanded: Result, Err> = list.iter().map(|item| expand(item, env)).collect(); Ok(Exp::List(expanded?)) } _ => Err(Err::Reason("Expected first argument to be a symbol or a list".to_string())) @@ -301,15 +307,25 @@ pub fn expand(exp: &Exp) -> Result { if list.len() > 2 { let mut acc = vec![Exp::Sym("cond".to_string())]; acc.extend_from_slice(&list[2..]); - res.push(expand(&Exp::List(acc))?); + res.push(expand(&Exp::List(acc), env)?); } Ok(Exp::List(res)) } else { Err(Err::Reason("Expected lists of predicate and expression".to_string())) } } + Exp::Sym(s) => { + if let Ok(Exp::Macro(m)) = env_get(s, env) { + let mut macro_env = function_env(&m.params, &list[1..], env, true)?; + let macro_exp = m.body; + expand(&eval(¯o_exp, &mut macro_env)?, env) + } else { // TODO: dry this + let expanded: Result, Err> = list.iter().map(|item| expand(item, env)).collect(); + Ok(Exp::List(expanded?)) + } + } _ => { - let expanded: Result, Err> = list.iter().map(|item| expand(item)).collect(); + let expanded: Result, Err> = list.iter().map(|item| expand(item, env)).collect(); Ok(Exp::List(expanded?)) } } diff --git a/src/usr/lisp/mod.rs b/src/usr/lisp/mod.rs index e18d3df68..4c354daf4 100644 --- a/src/usr/lisp/mod.rs +++ b/src/usr/lisp/mod.rs @@ -47,6 +47,7 @@ use spin::Mutex; pub enum Exp { Primitive(fn(&[Exp]) -> Result), Function(Box), + Macro(Box), List(Vec), Bool(bool), Num(Number), @@ -58,6 +59,7 @@ impl PartialEq for Exp { fn eq(&self, other: &Self) -> bool { match (self, other) { (Exp::Function(a), Exp::Function(b)) => a == b, + (Exp::Macro(a), Exp::Macro(b)) => a == b, (Exp::List(a), Exp::List(b)) => a == b, (Exp::Bool(a), Exp::Bool(b)) => a == b, (Exp::Num(a), Exp::Num(b)) => a == b, @@ -73,6 +75,7 @@ impl fmt::Display for Exp { let out = match self { Exp::Primitive(_) => "".to_string(), Exp::Function(_) => "".to_string(), + Exp::Macro(_) => "".to_string(), Exp::Bool(a) => a.to_string(), Exp::Num(n) => n.to_string(), Exp::Sym(s) => s.clone(), @@ -172,7 +175,7 @@ pub fn byte(exp: &Exp) -> Result { fn parse_eval(exp: &str, env: &mut Rc>) -> Result { let (_, exp) = parse(exp)?; - let exp = expand(&exp)?; + let exp = expand(&exp, env)?; let exp = eval(&exp, env)?; Ok(exp) } @@ -477,4 +480,10 @@ fn test_lisp() { eval!("(def x 'a)"); assert_eq!(eval!("`(x ,x y)"), "(x a y)"); assert_eq!(eval!("`(x ,x y ,(+ 1 2))"), "(x a y 3)"); + + // macro + eval!("(define foo 42)"); + eval!("(define set-10 (macro (x) `(set ,x 10)))"); + eval!("(set-10 foo)"); + assert_eq!(eval!("foo"), "10"); } From 7fdb4f085ac8e61a3cbf5a06f0ad59517d65a5ae Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Wed, 26 Oct 2022 22:46:48 +0200 Subject: [PATCH 06/19] Fix function_env --- src/usr/lisp/env.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/usr/lisp/env.rs b/src/usr/lisp/env.rs index 050e0535f..594672009 100644 --- a/src/usr/lisp/env.rs +++ b/src/usr/lisp/env.rs @@ -336,7 +336,7 @@ pub fn function_env(params: &Exp, args: &[Exp], outer: &mut Rc>, is let plural = if ks.len() == 1 { "" } else { "s" }; return Err(Err::Reason(format!("Expected {} argument{}, got {}", ks.len(), plural, args.len()))); } - let vs = if is_macro { args } else { eval_args(args, outer)? }; + let vs = if is_macro { args.to_vec() } else { eval_args(args, outer)? }; let mut data: BTreeMap = BTreeMap::new(); for (k, v) in ks.iter().zip(vs.iter()) { data.insert(k.clone(), v.clone()); From b780e852bf254a2154e23262b7370e3e89b2163d Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Wed, 26 Oct 2022 23:03:10 +0200 Subject: [PATCH 07/19] Dry code --- src/usr/lisp/eval.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/usr/lisp/eval.rs b/src/usr/lisp/eval.rs index d8c5767a0..4adce5058 100644 --- a/src/usr/lisp/eval.rs +++ b/src/usr/lisp/eval.rs @@ -251,6 +251,11 @@ pub fn expand_quasiquote(exp: &Exp) -> Result { } } +pub fn expand_list(list: &[Exp], env: &mut Rc>) -> Result { + let expanded: Result, Err> = list.iter().map(|item| expand(item, env)).collect(); + Ok(Exp::List(expanded?)) +} + pub fn expand(exp: &Exp, env: &mut Rc>) -> Result { if let Exp::List(list) = exp { ensure_length_gt!(list, 0); @@ -292,10 +297,7 @@ pub fn expand(exp: &Exp, env: &mut Rc>) -> Result { ]) ])) } - (Exp::Sym(_), _) => { // TODO: dry this - let expanded: Result, Err> = list.iter().map(|item| expand(item, env)).collect(); - Ok(Exp::List(expanded?)) - } + (Exp::Sym(_), _) => expand_list(list, env), _ => Err(Err::Reason("Expected first argument to be a symbol or a list".to_string())) } } @@ -319,15 +321,11 @@ pub fn expand(exp: &Exp, env: &mut Rc>) -> Result { let mut macro_env = function_env(&m.params, &list[1..], env, true)?; let macro_exp = m.body; expand(&eval(¯o_exp, &mut macro_env)?, env) - } else { // TODO: dry this - let expanded: Result, Err> = list.iter().map(|item| expand(item, env)).collect(); - Ok(Exp::List(expanded?)) + } else { + expand_list(list, env) } } - _ => { - let expanded: Result, Err> = list.iter().map(|item| expand(item, env)).collect(); - Ok(Exp::List(expanded?)) - } + _ => expand_list(list, env), } } else { Ok(exp.clone()) From 9fd270aa8767202bfae17e9552b8646bf6e1add4 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Wed, 26 Oct 2022 23:46:33 +0200 Subject: [PATCH 08/19] Add define-macro shortcut --- src/usr/lisp/eval.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/usr/lisp/eval.rs b/src/usr/lisp/eval.rs index 4adce5058..9bab06dec 100644 --- a/src/usr/lisp/eval.rs +++ b/src/usr/lisp/eval.rs @@ -301,6 +301,24 @@ pub fn expand(exp: &Exp, env: &mut Rc>) -> Result { _ => Err(Err::Reason("Expected first argument to be a symbol or a list".to_string())) } } + Exp::Sym(s) if s == "define-macro" || s == "def-mac" => { + ensure_length_eq!(list, 3); + match (&list[1], &list[2]) { + (Exp::List(args), Exp::List(_)) => { + ensure_length_gt!(args, 0); + let name = args[0].clone(); + let args = Exp::List(args[1..].to_vec()); + let body = expand(&list[2], env)?; + Ok(Exp::List(vec![ + Exp::Sym("define".to_string()), name, Exp::List(vec![ + Exp::Sym("macro".to_string()), args, body + ]) + ])) + } + (Exp::Sym(_), _) => expand_list(list, env), + _ => Err(Err::Reason("Expected first argument to be a symbol or a list".to_string())) + } + } Exp::Sym(s) if s == "cond" => { ensure_length_gt!(list, 1); if let Exp::List(args) = &list[1] { From fe1302b685e33dd47cdac44b4258d5214842f795 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Wed, 26 Oct 2022 23:46:44 +0200 Subject: [PATCH 09/19] Add mac shortcut --- src/usr/lisp/eval.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/usr/lisp/eval.rs b/src/usr/lisp/eval.rs index 9bab06dec..8faf178e5 100644 --- a/src/usr/lisp/eval.rs +++ b/src/usr/lisp/eval.rs @@ -283,6 +283,11 @@ pub fn expand(exp: &Exp, env: &mut Rc>) -> Result { res.extend_from_slice(&list[1..]); expand(&Exp::List(res), env) } + Exp::Sym(s) if s == "mac" => { + let mut res = vec![Exp::Sym("macro".to_string())]; + res.extend_from_slice(&list[1..]); + expand(&Exp::List(res), env) + } Exp::Sym(s) if s == "define-function" || s == "def-fun" || s == "define" => { ensure_length_eq!(list, 3); match (&list[1], &list[2]) { From 45ef3f0184cb8a933463680075bd5e88af6a9c83 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Thu, 27 Oct 2022 06:59:14 +0200 Subject: [PATCH 10/19] Refactor inner env --- src/usr/lisp/env.rs | 17 +++++++++++++++-- src/usr/lisp/eval.rs | 10 +++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/usr/lisp/env.rs b/src/usr/lisp/env.rs index 594672009..34c8b4374 100644 --- a/src/usr/lisp/env.rs +++ b/src/usr/lisp/env.rs @@ -330,16 +330,29 @@ pub fn env_set(key: &str, val: Exp, env: &Rc>) -> Result<(), Err> { } } -pub fn function_env(params: &Exp, args: &[Exp], outer: &mut Rc>, is_macro: bool) -> Result>, Err> { +enum InnerEnv { Function, Macro } + +fn inner_env(kind: InnerEnv, 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()))); } - let vs = if is_macro { args.to_vec() } else { eval_args(args, outer)? }; + let vs = match kind { + InnerEnv::Function => eval_args(args, outer)?, + InnerEnv::Macro => args.to_vec(), + }; let mut data: BTreeMap = BTreeMap::new(); for (k, v) in ks.iter().zip(vs.iter()) { data.insert(k.clone(), v.clone()); } 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>) -> Result>, Err> { + inner_env(InnerEnv::Function, params, args, outer) +} + +pub fn macro_env(params: &Exp, args: &[Exp], outer: &mut Rc>) -> Result>, Err> { + inner_env(InnerEnv::Macro, params, args, outer) +} diff --git a/src/usr/lisp/eval.rs b/src/usr/lisp/eval.rs index 8faf178e5..5b14dff58 100644 --- a/src/usr/lisp/eval.rs +++ b/src/usr/lisp/eval.rs @@ -1,5 +1,5 @@ use super::{Err, Exp, Env, Function}; -use super::env::{env_get, env_set, function_env}; +use super::env::{env_get, env_set, function_env, macro_env}; use super::parse::parse; use super::string; @@ -213,7 +213,7 @@ pub fn eval(exp: &Exp, env: &mut Rc>) -> Result { _ => { match eval(&list[0], env)? { Exp::Function(f) => { - env_tmp = function_env(&f.params, args, env, false)?; + env_tmp = function_env(&f.params, args, env)?; exp_tmp = f.body; env = &mut env_tmp; exp = &exp_tmp; @@ -341,9 +341,9 @@ pub fn expand(exp: &Exp, env: &mut Rc>) -> Result { } Exp::Sym(s) => { if let Ok(Exp::Macro(m)) = env_get(s, env) { - let mut macro_env = function_env(&m.params, &list[1..], env, true)?; - let macro_exp = m.body; - expand(&eval(¯o_exp, &mut macro_env)?, env) + let mut m_env = macro_env(&m.params, &list[1..], env)?; + let m_exp = m.body; + expand(&eval(&m_exp, &mut m_env)?, env) } else { expand_list(list, env) } From 15f786aa976a438c7af1bd957bf926a4a90d8329 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Thu, 27 Oct 2022 21:02:31 +0200 Subject: [PATCH 11/19] Move expand to a module --- src/usr/lisp/eval.rs | 127 +------------------------------------- src/usr/lisp/expand.rs | 136 +++++++++++++++++++++++++++++++++++++++++ src/usr/lisp/mod.rs | 4 +- 3 files changed, 141 insertions(+), 126 deletions(-) create mode 100644 src/usr/lisp/expand.rs diff --git a/src/usr/lisp/eval.rs b/src/usr/lisp/eval.rs index 5b14dff58..e81fdf3c2 100644 --- a/src/usr/lisp/eval.rs +++ b/src/usr/lisp/eval.rs @@ -1,6 +1,7 @@ use super::{Err, Exp, Env, Function}; -use super::env::{env_get, env_set, function_env, macro_env}; +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}; @@ -230,127 +231,3 @@ pub fn eval(exp: &Exp, env: &mut Rc>) -> Result { } } } - -pub fn expand_quasiquote(exp: &Exp) -> Result { - match exp { - Exp::List(list) if list.len() > 0 => { - match &list[0] { - Exp::Sym(s) if s == "unquote" => { - Ok(list[1].clone()) - } - _ => { - Ok(Exp::List(vec![ - Exp::Sym("cons".to_string()), - expand_quasiquote(&list[0])?, - expand_quasiquote(&Exp::List(list[1..].to_vec()))?, - ])) - } - } - } - _ => Ok(Exp::List(vec![Exp::Sym("quote".to_string()), exp.clone()])), - } -} - -pub fn expand_list(list: &[Exp], env: &mut Rc>) -> Result { - let expanded: Result, Err> = list.iter().map(|item| expand(item, env)).collect(); - Ok(Exp::List(expanded?)) -} - -pub fn expand(exp: &Exp, env: &mut Rc>) -> Result { - if let Exp::List(list) = exp { - ensure_length_gt!(list, 0); - match &list[0] { - Exp::Sym(s) if s == "quote" => { - ensure_length_eq!(list, 2); - Ok(exp.clone()) - } - Exp::Sym(s) if s == "quasiquote" => { - ensure_length_eq!(list, 2); - expand_quasiquote(&list[1]) - } - Exp::Sym(s) if s == "begin" || s == "progn" => { - let mut res = vec![Exp::Sym("do".to_string())]; - res.extend_from_slice(&list[1..]); - expand(&Exp::List(res), env) - } - Exp::Sym(s) if s == "def" || s == "label" => { - let mut res = vec![Exp::Sym("define".to_string())]; - res.extend_from_slice(&list[1..]); - expand(&Exp::List(res), env) - } - Exp::Sym(s) if s == "fun" || s == "fn" || s == "lambda" => { - let mut res = vec![Exp::Sym("function".to_string())]; - res.extend_from_slice(&list[1..]); - expand(&Exp::List(res), env) - } - Exp::Sym(s) if s == "mac" => { - let mut res = vec![Exp::Sym("macro".to_string())]; - res.extend_from_slice(&list[1..]); - expand(&Exp::List(res), env) - } - Exp::Sym(s) if s == "define-function" || s == "def-fun" || s == "define" => { - ensure_length_eq!(list, 3); - match (&list[1], &list[2]) { - (Exp::List(args), Exp::List(_)) => { - ensure_length_gt!(args, 0); - let name = args[0].clone(); - let args = Exp::List(args[1..].to_vec()); - let body = expand(&list[2], env)?; - Ok(Exp::List(vec![ - Exp::Sym("define".to_string()), name, Exp::List(vec![ - Exp::Sym("function".to_string()), args, body - ]) - ])) - } - (Exp::Sym(_), _) => expand_list(list, env), - _ => Err(Err::Reason("Expected first argument to be a symbol or a list".to_string())) - } - } - Exp::Sym(s) if s == "define-macro" || s == "def-mac" => { - ensure_length_eq!(list, 3); - match (&list[1], &list[2]) { - (Exp::List(args), Exp::List(_)) => { - ensure_length_gt!(args, 0); - let name = args[0].clone(); - let args = Exp::List(args[1..].to_vec()); - let body = expand(&list[2], env)?; - Ok(Exp::List(vec![ - Exp::Sym("define".to_string()), name, Exp::List(vec![ - Exp::Sym("macro".to_string()), args, body - ]) - ])) - } - (Exp::Sym(_), _) => expand_list(list, env), - _ => Err(Err::Reason("Expected first argument to be a symbol or a list".to_string())) - } - } - Exp::Sym(s) if s == "cond" => { - ensure_length_gt!(list, 1); - if let Exp::List(args) = &list[1] { - ensure_length_eq!(args, 2); - let mut res = vec![Exp::Sym("if".to_string()), args[0].clone(), args[1].clone()]; - if list.len() > 2 { - let mut acc = vec![Exp::Sym("cond".to_string())]; - acc.extend_from_slice(&list[2..]); - res.push(expand(&Exp::List(acc), env)?); - } - Ok(Exp::List(res)) - } else { - Err(Err::Reason("Expected lists of predicate and expression".to_string())) - } - } - Exp::Sym(s) => { - if let Ok(Exp::Macro(m)) = env_get(s, env) { - let mut m_env = macro_env(&m.params, &list[1..], env)?; - let m_exp = m.body; - expand(&eval(&m_exp, &mut m_env)?, env) - } else { - expand_list(list, env) - } - } - _ => expand_list(list, env), - } - } else { - Ok(exp.clone()) - } -} diff --git a/src/usr/lisp/expand.rs b/src/usr/lisp/expand.rs new file mode 100644 index 000000000..85feb98e1 --- /dev/null +++ b/src/usr/lisp/expand.rs @@ -0,0 +1,136 @@ +use super::{Err, Exp, Env}; +use super::env::{env_get, macro_env}; +use super::eval::eval; + +use crate::{ensure_length_eq, ensure_length_gt}; + +use alloc::format; +use alloc::rc::Rc; +use alloc::string::ToString; +use alloc::vec::Vec; +use alloc::vec; +use core::cell::RefCell; + +pub fn expand_quasiquote(exp: &Exp) -> Result { + match exp { + Exp::List(list) if list.len() > 0 => { + match &list[0] { + Exp::Sym(s) if s == "unquote" => { + Ok(list[1].clone()) + } + _ => { + Ok(Exp::List(vec![ + Exp::Sym("cons".to_string()), + expand_quasiquote(&list[0])?, + expand_quasiquote(&Exp::List(list[1..].to_vec()))?, + ])) + } + } + } + _ => Ok(Exp::List(vec![Exp::Sym("quote".to_string()), exp.clone()])), + } +} + +pub fn expand_list(list: &[Exp], env: &mut Rc>) -> Result { + let expanded: Result, Err> = list.iter().map(|item| expand(item, env)).collect(); + Ok(Exp::List(expanded?)) +} + +pub fn expand(exp: &Exp, env: &mut Rc>) -> Result { + if let Exp::List(list) = exp { + ensure_length_gt!(list, 0); + match &list[0] { + Exp::Sym(s) if s == "quote" => { + ensure_length_eq!(list, 2); + Ok(exp.clone()) + } + Exp::Sym(s) if s == "quasiquote" => { + ensure_length_eq!(list, 2); + expand_quasiquote(&list[1]) + } + Exp::Sym(s) if s == "begin" || s == "progn" => { + let mut res = vec![Exp::Sym("do".to_string())]; + res.extend_from_slice(&list[1..]); + expand(&Exp::List(res), env) + } + Exp::Sym(s) if s == "def" || s == "label" => { + let mut res = vec![Exp::Sym("define".to_string())]; + res.extend_from_slice(&list[1..]); + expand(&Exp::List(res), env) + } + Exp::Sym(s) if s == "fun" || s == "fn" || s == "lambda" => { + let mut res = vec![Exp::Sym("function".to_string())]; + res.extend_from_slice(&list[1..]); + expand(&Exp::List(res), env) + } + Exp::Sym(s) if s == "mac" => { + let mut res = vec![Exp::Sym("macro".to_string())]; + res.extend_from_slice(&list[1..]); + expand(&Exp::List(res), env) + } + Exp::Sym(s) if s == "define-function" || s == "def-fun" || s == "define" => { + ensure_length_eq!(list, 3); + match (&list[1], &list[2]) { + (Exp::List(args), Exp::List(_)) => { + ensure_length_gt!(args, 0); + let name = args[0].clone(); + let args = Exp::List(args[1..].to_vec()); + let body = expand(&list[2], env)?; + Ok(Exp::List(vec![ + Exp::Sym("define".to_string()), name, Exp::List(vec![ + Exp::Sym("function".to_string()), args, body + ]) + ])) + } + (Exp::Sym(_), _) => expand_list(list, env), + _ => Err(Err::Reason("Expected first argument to be a symbol or a list".to_string())) + } + } + Exp::Sym(s) if s == "define-macro" || s == "def-mac" => { + ensure_length_eq!(list, 3); + match (&list[1], &list[2]) { + (Exp::List(args), Exp::List(_)) => { + ensure_length_gt!(args, 0); + let name = args[0].clone(); + let args = Exp::List(args[1..].to_vec()); + let body = expand(&list[2], env)?; + Ok(Exp::List(vec![ + Exp::Sym("define".to_string()), name, Exp::List(vec![ + Exp::Sym("macro".to_string()), args, body + ]) + ])) + } + (Exp::Sym(_), _) => expand_list(list, env), + _ => Err(Err::Reason("Expected first argument to be a symbol or a list".to_string())) + } + } + Exp::Sym(s) if s == "cond" => { + ensure_length_gt!(list, 1); + if let Exp::List(args) = &list[1] { + ensure_length_eq!(args, 2); + let mut res = vec![Exp::Sym("if".to_string()), args[0].clone(), args[1].clone()]; + if list.len() > 2 { + let mut acc = vec![Exp::Sym("cond".to_string())]; + acc.extend_from_slice(&list[2..]); + res.push(expand(&Exp::List(acc), env)?); + } + Ok(Exp::List(res)) + } else { + Err(Err::Reason("Expected lists of predicate and expression".to_string())) + } + } + Exp::Sym(s) => { + if let Ok(Exp::Macro(m)) = env_get(s, env) { + let mut m_env = macro_env(&m.params, &list[1..], env)?; + let m_exp = m.body; + expand(&eval(&m_exp, &mut m_env)?, env) + } else { + expand_list(list, env) + } + } + _ => expand_list(list, env), + } + } else { + Ok(exp.clone()) + } +} diff --git a/src/usr/lisp/mod.rs b/src/usr/lisp/mod.rs index 4c354daf4..7f0c94ef6 100644 --- a/src/usr/lisp/mod.rs +++ b/src/usr/lisp/mod.rs @@ -1,5 +1,6 @@ mod env; mod eval; +mod expand; mod number; mod parse; @@ -7,7 +8,8 @@ pub use number::Number; pub use env::Env; use env::default_env; -use eval::{eval, eval_define_args, expand}; +use eval::{eval, eval_define_args}; +use expand::expand; use parse::parse; use crate::api; From 8460ecca0dad799c9319059590f2b977b6761c40 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Thu, 27 Oct 2022 21:04:57 +0200 Subject: [PATCH 12/19] Add unquote-splicing --- src/usr/lisp/env.rs | 11 +++++++++++ src/usr/lisp/expand.rs | 7 +++++++ src/usr/lisp/mod.rs | 4 ++++ src/usr/lisp/parse.rs | 8 +++++++- 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/usr/lisp/env.rs b/src/usr/lisp/env.rs index 34c8b4374..d229c97fe 100644 --- a/src/usr/lisp/env.rs +++ b/src/usr/lisp/env.rs @@ -294,6 +294,17 @@ pub fn default_env() -> Rc> { let (_, exp) = parse(&s)?; Ok(exp) })); + data.insert("append".to_string(), Exp::Primitive(|args: &[Exp]| -> Result { + 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(); diff --git a/src/usr/lisp/expand.rs b/src/usr/lisp/expand.rs index 85feb98e1..d6c6b790a 100644 --- a/src/usr/lisp/expand.rs +++ b/src/usr/lisp/expand.rs @@ -18,6 +18,13 @@ pub fn expand_quasiquote(exp: &Exp) -> Result { Exp::Sym(s) if s == "unquote" => { Ok(list[1].clone()) } + Exp::List(l) if l.len() == 2 && l[0] == Exp::Sym("unquote-splicing".to_string()) => { + Ok(Exp::List(vec![ + Exp::Sym("append".to_string()), + l[1].clone(), + expand_quasiquote(&Exp::List(list[1..].to_vec()))? + ])) + } _ => { Ok(Exp::List(vec![ Exp::Sym("cons".to_string()), diff --git a/src/usr/lisp/mod.rs b/src/usr/lisp/mod.rs index 7f0c94ef6..859d31a3c 100644 --- a/src/usr/lisp/mod.rs +++ b/src/usr/lisp/mod.rs @@ -488,4 +488,8 @@ fn test_lisp() { eval!("(define set-10 (macro (x) `(set ,x 10)))"); eval!("(set-10 foo)"); assert_eq!(eval!("foo"), "10"); + + eval!("((def x '(1 2 3)))"); + assert_eq!(eval!("`(+ ,x)"), "(+ (1 2 3))"); + assert_eq!(eval!("`(+ ,@x)"), "(+ 1 2 3)"); } diff --git a/src/usr/lisp/parse.rs b/src/usr/lisp/parse.rs index bacabd15a..58f241dfb 100644 --- a/src/usr/lisp/parse.rs +++ b/src/usr/lisp/parse.rs @@ -67,6 +67,12 @@ fn parse_quote(input: &str) -> IResult<&str, Exp> { Ok((input, Exp::List(list))) } +fn parse_unquote_splicing(input: &str) -> IResult<&str, Exp> { + let (input, list) = preceded(tag(",@"), parse_exp)(input)?; + let list = vec![Exp::Sym("unquote-splicing".to_string()), list]; + Ok((input, Exp::List(list))) +} + fn parse_unquote(input: &str) -> IResult<&str, Exp> { let (input, list) = preceded(char(','), parse_exp)(input)?; let list = vec![Exp::Sym("unquote".to_string()), list]; @@ -81,7 +87,7 @@ fn parse_quasiquote(input: &str) -> IResult<&str, Exp> { fn parse_exp(input: &str) -> IResult<&str, Exp> { delimited(multispace0, alt(( - parse_num, parse_bool, parse_str, parse_list, parse_quote, parse_unquote, parse_quasiquote, parse_sym + parse_num, parse_bool, parse_str, parse_list, parse_quote, parse_unquote_splicing, parse_unquote, parse_quasiquote, parse_sym )), multispace0)(input) } From 829af7728bbb8acc593665d5781713b4d80bf1c8 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Thu, 27 Oct 2022 21:40:40 +0200 Subject: [PATCH 13/19] Fix test --- src/usr/lisp/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/usr/lisp/mod.rs b/src/usr/lisp/mod.rs index 859d31a3c..413f717da 100644 --- a/src/usr/lisp/mod.rs +++ b/src/usr/lisp/mod.rs @@ -489,7 +489,7 @@ fn test_lisp() { eval!("(set-10 foo)"); assert_eq!(eval!("foo"), "10"); - eval!("((def x '(1 2 3)))"); + eval!("(define x '(1 2 3))"); assert_eq!(eval!("`(+ ,x)"), "(+ (1 2 3))"); assert_eq!(eval!("`(+ ,@x)"), "(+ 1 2 3)"); } From 51b6df3568d3e932b22936f2ec757b7aaa47e3de Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Thu, 27 Oct 2022 21:50:46 +0200 Subject: [PATCH 14/19] Refactor tests --- src/usr/lisp/mod.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/usr/lisp/mod.rs b/src/usr/lisp/mod.rs index 413f717da..b280a1d70 100644 --- a/src/usr/lisp/mod.rs +++ b/src/usr/lisp/mod.rs @@ -359,15 +359,13 @@ fn test_lisp() { // while assert_eq!(eval!("(do (def i 0) (while (< i 5) (set i (+ i 1))) i)"), "5"); - // label - eval!("(label a 2)"); + // define + eval!("(define a 2)"); assert_eq!(eval!("(+ a 1)"), "3"); - //eval!("(label fn function)"); - //assert_eq!(eval!("((fn (a) (+ 1 a)) 2)"), "3"); - eval!("(label add-one (function (b) (+ b 1)))"); + eval!("(define add-one (function (b) (+ b 1)))"); assert_eq!(eval!("(add-one 2)"), "3"); - eval!("(label fib (function (n) (cond ((< n 2) n) (true (+ (fib (- n 1)) (fib (- n 2)))))))"); - assert_eq!(eval!("(fib 6)"), "8"); + eval!("(define fibonacci (function (n) (if (< n 2) n (+ (fibonacci (- n 1)) (fibonacci (- n 2))))))"); + assert_eq!(eval!("(fibonacci 6)"), "8"); // function assert_eq!(eval!("((function (a) (+ 1 a)) 2)"), "3"); @@ -479,7 +477,7 @@ fn test_lisp() { assert_eq!(eval!("(number-type 9223372036854776000.0)"), "\"float\""); // quasiquote - eval!("(def x 'a)"); + eval!("(define x 'a)"); assert_eq!(eval!("`(x ,x y)"), "(x a y)"); assert_eq!(eval!("`(x ,x y ,(+ 1 2))"), "(x a y 3)"); From 9dd48a5b0b6343f5512c72edc4ee54c50a4d6fce Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Thu, 27 Oct 2022 23:51:15 +0200 Subject: [PATCH 15/19] Add function args symbol --- src/usr/lisp/env.rs | 28 +++++++++++++++++++--------- src/usr/lisp/mod.rs | 25 ++++++++----------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/usr/lisp/env.rs b/src/usr/lisp/env.rs index d229c97fe..081e6160a 100644 --- a/src/usr/lisp/env.rs +++ b/src/usr/lisp/env.rs @@ -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}; @@ -344,18 +343,29 @@ pub fn env_set(key: &str, val: Exp, env: &Rc>) -> Result<(), Err> { enum InnerEnv { Function, Macro } fn inner_env(kind: InnerEnv, 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()))); - } - let vs = match kind { + let args = match kind { InnerEnv::Function => eval_args(args, outer)?, InnerEnv::Macro => args.to_vec(), }; let mut data: BTreeMap = 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()))) }))) } diff --git a/src/usr/lisp/mod.rs b/src/usr/lisp/mod.rs index b280a1d70..0434c22e3 100644 --- a/src/usr/lisp/mod.rs +++ b/src/usr/lisp/mod.rs @@ -126,20 +126,6 @@ macro_rules! ensure_length_gt { }; } -fn list_of_symbols(form: &Exp) -> Result, Err> { - match form { - Exp::List(list) => { - list.iter().map(|exp| { - match exp { - Exp::Sym(sym) => Ok(sym.clone()), - _ => Err(Err::Reason("Expected symbols in the argument list".to_string())) - } - }).collect() - } - _ => Err(Err::Reason("Expected args form to be a list".to_string())) - } -} - pub fn list_of_numbers(args: &[Exp]) -> Result, Err> { args.iter().map(number).collect() } @@ -481,13 +467,18 @@ fn test_lisp() { assert_eq!(eval!("`(x ,x y)"), "(x a y)"); assert_eq!(eval!("`(x ,x y ,(+ 1 2))"), "(x a y 3)"); + // unquote-splicing + eval!("(define x '(1 2 3))"); + assert_eq!(eval!("`(+ ,x)"), "(+ (1 2 3))"); + assert_eq!(eval!("`(+ ,@x)"), "(+ 1 2 3)"); + // macro eval!("(define foo 42)"); eval!("(define set-10 (macro (x) `(set ,x 10)))"); eval!("(set-10 foo)"); assert_eq!(eval!("foo"), "10"); - eval!("(define x '(1 2 3))"); - assert_eq!(eval!("`(+ ,x)"), "(+ (1 2 3))"); - assert_eq!(eval!("`(+ ,@x)"), "(+ 1 2 3)"); + // args + eval!("(define list* (function args (append args '())))"); + assert_eq!(eval!("(list* 1 2 3)"), "(1 2 3)"); } From d36077405e58c7cbd73f29421d321d5b8b29656e Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Thu, 27 Oct 2022 23:53:12 +0200 Subject: [PATCH 16/19] Fix typo --- src/usr/lisp/env.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/usr/lisp/env.rs b/src/usr/lisp/env.rs index 081e6160a..81b73ab0d 100644 --- a/src/usr/lisp/env.rs +++ b/src/usr/lisp/env.rs @@ -349,7 +349,7 @@ fn inner_env(kind: InnerEnv, params: &Exp, args: &[Exp], outer: &mut Rc = BTreeMap::new(); match params { - Exp::Sym(s) => + Exp::Sym(s) => { data.insert(s.clone(), Exp::List(args)); } Exp::List(list) => { From 40981ee57e5c9a4a39a6783a67acb60176a0ca76 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Fri, 28 Oct 2022 00:08:45 +0200 Subject: [PATCH 17/19] Remove old append --- dsk/lib/lisp/core.lsp | 4 ---- src/usr/lisp/env.rs | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/dsk/lib/lisp/core.lsp b/dsk/lib/lisp/core.lsp index c46c22af4..43ac82f27 100644 --- a/dsk/lib/lisp/core.lsp +++ b/dsk/lib/lisp/core.lsp @@ -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) '())))) diff --git a/src/usr/lisp/env.rs b/src/usr/lisp/env.rs index 81b73ab0d..802d315fb 100644 --- a/src/usr/lisp/env.rs +++ b/src/usr/lisp/env.rs @@ -284,15 +284,15 @@ pub fn default_env() -> Rc> { _ => Err(Err::Reason("Expected arg to be a number".to_string())) } })); - data.insert("list".to_string(), Exp::Primitive(|args: &[Exp]| -> Result { - Ok(Exp::List(args.to_vec())) - })); data.insert("parse".to_string(), Exp::Primitive(|args: &[Exp]| -> Result { 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 { + Ok(Exp::List(args.to_vec())) + })); data.insert("append".to_string(), Exp::Primitive(|args: &[Exp]| -> Result { let mut res = vec![]; for arg in args { From 10544b56d8e721c9a595804e48f029752953a3d0 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Fri, 28 Oct 2022 15:17:04 +0200 Subject: [PATCH 18/19] Add dotted pair --- src/usr/lisp/expand.rs | 7 +++++-- src/usr/lisp/mod.rs | 9 +++++++++ src/usr/lisp/parse.rs | 14 +++++++++++++- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/usr/lisp/expand.rs b/src/usr/lisp/expand.rs index d6c6b790a..0c1ffe5cc 100644 --- a/src/usr/lisp/expand.rs +++ b/src/usr/lisp/expand.rs @@ -80,8 +80,11 @@ pub fn expand(exp: &Exp, env: &mut Rc>) -> Result { match (&list[1], &list[2]) { (Exp::List(args), Exp::List(_)) => { ensure_length_gt!(args, 0); - let name = args[0].clone(); - let args = Exp::List(args[1..].to_vec()); + let (name, args) = if args.len() == 3 && args[0] == Exp::Sym("cons".to_string()) { + (args[1].clone(), args[2].clone()) + } else { + (args[0].clone(), Exp::List(args[1..].to_vec())) + }; let body = expand(&list[2], env)?; Ok(Exp::List(vec![ Exp::Sym("define".to_string()), name, Exp::List(vec![ diff --git a/src/usr/lisp/mod.rs b/src/usr/lisp/mod.rs index 0434c22e3..981deedcd 100644 --- a/src/usr/lisp/mod.rs +++ b/src/usr/lisp/mod.rs @@ -478,7 +478,16 @@ fn test_lisp() { eval!("(set-10 foo)"); assert_eq!(eval!("foo"), "10"); + // dotted pair + assert_eq!(eval!("(cons 1 (cons 2 (cons 3 '())))"), "(1 2 3)"); + assert_eq!(eval!("(cons 1 (2 . (3 . '())))"), "(1 2 3)"); + assert_eq!(eval!("(cons 1 (list 2 3))"), "(1 2 3)"); + assert_eq!(eval!("'(cons 1 (cons 2 (cons 3 '())))"), "(cons 1 (cons 2 (cons 3 (quote ()))))"); + assert_eq!(eval!("'(1 . (2 . (3 . '())))"), "(cons 1 (cons 2 (cons 3 (quote ()))))"); + // args eval!("(define list* (function args (append args '())))"); assert_eq!(eval!("(list* 1 2 3)"), "(1 2 3)"); + eval!("(define (list* . args) (append args '())))"); + assert_eq!(eval!("(list* 1 2 3)"), "(1 2 3)"); } diff --git a/src/usr/lisp/parse.rs b/src/usr/lisp/parse.rs index 58f241dfb..161c1efaf 100644 --- a/src/usr/lisp/parse.rs +++ b/src/usr/lisp/parse.rs @@ -21,6 +21,7 @@ use nom::multi::many0; use nom::sequence::delimited; use nom::sequence::preceded; use nom::sequence::tuple; +use nom::sequence::separated_pair; fn is_symbol_letter(c: char) -> bool { let chars = "<>=-+*/%^?:"; @@ -61,6 +62,16 @@ fn parse_list(input: &str) -> IResult<&str, Exp> { Ok((input, Exp::List(list))) } +fn parse_pair(input: &str) -> IResult<&str, Exp> { + let (input, (car, cdr)) = delimited( + char('('), + separated_pair(parse_exp, char('.'), parse_exp), + char(')') + )(input)?; + let list = vec![Exp::Sym("cons".to_string()), car, cdr]; + Ok((input, Exp::List(list))) +} + fn parse_quote(input: &str) -> IResult<&str, Exp> { let (input, list) = preceded(char('\''), parse_exp)(input)?; let list = vec![Exp::Sym("quote".to_string()), list]; @@ -87,7 +98,8 @@ fn parse_quasiquote(input: &str) -> IResult<&str, Exp> { fn parse_exp(input: &str) -> IResult<&str, Exp> { delimited(multispace0, alt(( - parse_num, parse_bool, parse_str, parse_list, parse_quote, parse_unquote_splicing, parse_unquote, parse_quasiquote, parse_sym + parse_num, parse_bool, parse_str, parse_list, parse_pair, parse_quote, + parse_unquote_splicing, parse_unquote, parse_quasiquote, parse_sym )), multispace0)(input) } From 0fdc89ac1e25f357221dd53ad210f12e9fddd067 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Fri, 28 Oct 2022 18:37:12 +0200 Subject: [PATCH 19/19] Revert "Add dotted pair" This reverts commit 10544b56d8e721c9a595804e48f029752953a3d0. --- src/usr/lisp/expand.rs | 7 ++----- src/usr/lisp/mod.rs | 9 --------- src/usr/lisp/parse.rs | 14 +------------- 3 files changed, 3 insertions(+), 27 deletions(-) diff --git a/src/usr/lisp/expand.rs b/src/usr/lisp/expand.rs index 0c1ffe5cc..d6c6b790a 100644 --- a/src/usr/lisp/expand.rs +++ b/src/usr/lisp/expand.rs @@ -80,11 +80,8 @@ pub fn expand(exp: &Exp, env: &mut Rc>) -> Result { match (&list[1], &list[2]) { (Exp::List(args), Exp::List(_)) => { ensure_length_gt!(args, 0); - let (name, args) = if args.len() == 3 && args[0] == Exp::Sym("cons".to_string()) { - (args[1].clone(), args[2].clone()) - } else { - (args[0].clone(), Exp::List(args[1..].to_vec())) - }; + let name = args[0].clone(); + let args = Exp::List(args[1..].to_vec()); let body = expand(&list[2], env)?; Ok(Exp::List(vec![ Exp::Sym("define".to_string()), name, Exp::List(vec![ diff --git a/src/usr/lisp/mod.rs b/src/usr/lisp/mod.rs index 981deedcd..0434c22e3 100644 --- a/src/usr/lisp/mod.rs +++ b/src/usr/lisp/mod.rs @@ -478,16 +478,7 @@ fn test_lisp() { eval!("(set-10 foo)"); assert_eq!(eval!("foo"), "10"); - // dotted pair - assert_eq!(eval!("(cons 1 (cons 2 (cons 3 '())))"), "(1 2 3)"); - assert_eq!(eval!("(cons 1 (2 . (3 . '())))"), "(1 2 3)"); - assert_eq!(eval!("(cons 1 (list 2 3))"), "(1 2 3)"); - assert_eq!(eval!("'(cons 1 (cons 2 (cons 3 '())))"), "(cons 1 (cons 2 (cons 3 (quote ()))))"); - assert_eq!(eval!("'(1 . (2 . (3 . '())))"), "(cons 1 (cons 2 (cons 3 (quote ()))))"); - // args eval!("(define list* (function args (append args '())))"); assert_eq!(eval!("(list* 1 2 3)"), "(1 2 3)"); - eval!("(define (list* . args) (append args '())))"); - assert_eq!(eval!("(list* 1 2 3)"), "(1 2 3)"); } diff --git a/src/usr/lisp/parse.rs b/src/usr/lisp/parse.rs index 161c1efaf..58f241dfb 100644 --- a/src/usr/lisp/parse.rs +++ b/src/usr/lisp/parse.rs @@ -21,7 +21,6 @@ use nom::multi::many0; use nom::sequence::delimited; use nom::sequence::preceded; use nom::sequence::tuple; -use nom::sequence::separated_pair; fn is_symbol_letter(c: char) -> bool { let chars = "<>=-+*/%^?:"; @@ -62,16 +61,6 @@ fn parse_list(input: &str) -> IResult<&str, Exp> { Ok((input, Exp::List(list))) } -fn parse_pair(input: &str) -> IResult<&str, Exp> { - let (input, (car, cdr)) = delimited( - char('('), - separated_pair(parse_exp, char('.'), parse_exp), - char(')') - )(input)?; - let list = vec![Exp::Sym("cons".to_string()), car, cdr]; - Ok((input, Exp::List(list))) -} - fn parse_quote(input: &str) -> IResult<&str, Exp> { let (input, list) = preceded(char('\''), parse_exp)(input)?; let list = vec![Exp::Sym("quote".to_string()), list]; @@ -98,8 +87,7 @@ fn parse_quasiquote(input: &str) -> IResult<&str, Exp> { fn parse_exp(input: &str) -> IResult<&str, Exp> { delimited(multispace0, alt(( - parse_num, parse_bool, parse_str, parse_list, parse_pair, parse_quote, - parse_unquote_splicing, parse_unquote, parse_quasiquote, parse_sym + parse_num, parse_bool, parse_str, parse_list, parse_quote, parse_unquote_splicing, parse_unquote, parse_quasiquote, parse_sym )), multispace0)(input) }