From 10544b56d8e721c9a595804e48f029752953a3d0 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Fri, 28 Oct 2022 15:17:04 +0200 Subject: [PATCH] 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) }