diff --git a/doc/lisp.md b/doc/lisp.md index 3e6f4ca64..e2959602a 100644 --- a/doc/lisp.md +++ b/doc/lisp.md @@ -50,8 +50,8 @@ Rewrite parts of the code and add new functions and examples. - `splice` (with the `@` syntax) - `atom` (aliased to `atom?`) - `eq` (aliased to `eq?`) -- `car` (aliased to `first`) -- `cdr` (aliased to `rest`) +- `head` (aliased to `car`) +- `tail` (aliased to `cdr`) - `cons` - `if` - `cond` @@ -78,12 +78,13 @@ Rewrite parts of the code and add new functions and examples. - `regex-find` - `system` -- Arithmetic operations: `+`, `-`, `*`, `/`, `%`, `^` +- Arithmetic operations: `+`, `-`, `*`, `/`, `%`, `^`, `abs` - Trigonometric functions: `acos`, `asin`, `atan`, `cos`, `sin`, `tan` - Comparisons: `>`, `<`, `>=`, `<=`, `=` - File IO: `read-file`, `read-file-bytes`, `write-file-bytes`, `append-file-bytes` -- List: `chunks`, `slice`, `sort`, `uniq`, `length` +- List: `chunks`, `sort`, `unique`, `min`, `max` - String: `trim`, `split` +- Enumerable: `length`, `nth`, `first`, `second`, `third`, `last`, `rest`, `slice` ### Core Library - `nil`, `nil?`, `eq?` @@ -125,7 +126,7 @@ with the following content: (println (if (nil? args) "Usage: fibonacci " - (fibonacci (string->number (car args))))) + (fibonacci (string->number (head args))))) ``` Would produce the following output: diff --git a/dsk/lib/lisp/alias.lsp b/dsk/lib/lisp/alias.lsp index cfea9ff19..d9af07ade 100644 --- a/dsk/lib/lisp/alias.lsp +++ b/dsk/lib/lisp/alias.lsp @@ -13,7 +13,11 @@ (define def-fun (macro args `(define-function ,@args))) -(define len length) +(define (car lst) + (head lst)) + +(define (cdr lst) + (tail lst)) (define label (macro args `(define ,@args))) diff --git a/dsk/lib/lisp/core.lsp b/dsk/lib/lisp/core.lsp index 0e0242b17..ac2be4909 100644 --- a/dsk/lib/lisp/core.lsp +++ b/dsk/lib/lisp/core.lsp @@ -44,62 +44,47 @@ (define-macro (let params values body) `((function ,params ,body) ,@values)) -(define (caar x) - (car (car x))) - -(define (cadr x) - (car (cdr x))) - -(define (cdar x) - (cdr (car x))) - -(define (cddr x) - (cdr (cdr x))) - -(define (rest x) - (cdr x)) - -(define (first x) - (car x)) - -(define (second x) - (first (rest x))) - -(define (third x) - (second (rest x))) - (define (reduce f ls) - (if (nil? (rest ls)) (first ls) - (f (first ls) (reduce f (rest ls))))) + (if (nil? (tail ls)) (head ls) + (f (head ls) (reduce f (tail ls))))) (define (map f ls) (if (nil? ls) nil (cons - (f (first ls)) - (map f (rest ls))))) + (f (head ls)) + (map f (tail ls))))) (define (filter f ls) (if (nil? ls) nil - (if (f (first ls)) - (cons (first ls) (filter f (rest ls))) - (filter f (rest ls))))) + (if (f (head ls)) + (cons (head ls) (filter f (tail ls))) + (filter f (tail ls))))) (define (intersection a b) (filter (function (x) (contains? b x)) a)) (define (reverse x) (if (nil? x) x - (append (reverse (rest x)) (cons (first x) '())))) + (append (reverse (tail x)) (cons (head x) '())))) (define (range i n) (if (= i n) nil (append (list i) (range (+ i 1) n)))) +(define (min lst) + (head (sort lst))) + +(define (max lst) + (head (reverse (sort lst)))) + +(define (abs x) + (if (> x 0) x (- x))) + (define (string-join ls s) (reduce (function (x y) (string x s y)) ls)) (define (read-line) - (bytes->string (reverse (rest (reverse (read-file-bytes "/dev/console" 256)))))) + (bytes->string (reverse (tail (reverse (read-file-bytes "/dev/console" 256)))))) (define (read-char) (bytes->string (read-file-bytes "/dev/console" 4))) @@ -135,3 +120,33 @@ (define (chars contents) (split contents "")) + +(define (first lst) + (nth lst 0)) + +(define (second lst) + (nth lst 1)) + +(define (third lst) + (nth lst 2)) + +(define (last lst) + (nth lst + (if (= (length lst) 0) 0 (- (length lst) 1)))) + +(define (caar x) + (car (car x))) + +(define (cadr x) + (car (cdr x))) + +(define (cdar x) + (cdr (car x))) + +(define (cddr x) + (cdr (cdr x))) + +(define rest cdr) +(define len length) +(define rev reverse) +(define uniq unique) diff --git a/dsk/tmp/lisp/factorial.lsp b/dsk/tmp/lisp/factorial.lsp index 4fec02427..0b19e811f 100644 --- a/dsk/tmp/lisp/factorial.lsp +++ b/dsk/tmp/lisp/factorial.lsp @@ -9,4 +9,4 @@ (println (if (nil? args) "Usage: factorial " - (factorial (string->number (car args))))) + (factorial (string->number (head args))))) diff --git a/dsk/tmp/lisp/fibonacci.lsp b/dsk/tmp/lisp/fibonacci.lsp index 7add019c3..638106bee 100644 --- a/dsk/tmp/lisp/fibonacci.lsp +++ b/dsk/tmp/lisp/fibonacci.lsp @@ -6,4 +6,4 @@ (println (if (nil? args) "Usage: fibonacci " - (fibonacci (string->number (car args))))) + (fibonacci (string->number (head args))))) diff --git a/dsk/tmp/lisp/geotime.lsp b/dsk/tmp/lisp/geotime.lsp index 60585ae7f..3e3ac6035 100644 --- a/dsk/tmp/lisp/geotime.lsp +++ b/dsk/tmp/lisp/geotime.lsp @@ -21,9 +21,6 @@ (/ (* 2 pi) 365.0) (+ (days timestamp) (/ (- (hours timestamp) 12.0) 24.0)))))) -(define (abs x) - (if (< x 0) (- x) x)) - (define (pad x) (string (if (< x 10) "0" "") x)) diff --git a/dsk/tmp/lisp/pi.lsp b/dsk/tmp/lisp/pi.lsp index f20eb24d5..a71d00155 100644 --- a/dsk/tmp/lisp/pi.lsp +++ b/dsk/tmp/lisp/pi.lsp @@ -31,4 +31,4 @@ (println (if (nil? args) "Usage: pi " - (pi-digits (string->number (car args))))) + (pi-digits (string->number (head args))))) diff --git a/dsk/tmp/lisp/sum.lsp b/dsk/tmp/lisp/sum.lsp index 25d3b6dbe..3e4ec927b 100644 --- a/dsk/tmp/lisp/sum.lsp +++ b/dsk/tmp/lisp/sum.lsp @@ -5,4 +5,4 @@ (println (if (nil? args) "Usage: sum " - (sum (string->number (car args)) 0))) + (sum (string->number (head args)) 0))) diff --git a/src/usr/lisp/env.rs b/src/usr/lisp/env.rs index a3cef7634..c3a6d73d4 100644 --- a/src/usr/lisp/env.rs +++ b/src/usr/lisp/env.rs @@ -59,8 +59,9 @@ pub fn default_env() -> Rc> { data.insert("number-type".to_string(), Exp::Primitive(primitive::lisp_number_type)); data.insert("parse".to_string(), Exp::Primitive(primitive::lisp_parse)); data.insert("list".to_string(), Exp::Primitive(primitive::lisp_list)); - data.insert("uniq".to_string(), Exp::Primitive(primitive::lisp_uniq)); data.insert("sort".to_string(), Exp::Primitive(primitive::lisp_sort)); + data.insert("unique".to_string(), Exp::Primitive(primitive::lisp_unique)); + data.insert("nth".to_string(), Exp::Primitive(primitive::lisp_nth)); data.insert("contains?".to_string(), Exp::Primitive(primitive::lisp_contains)); data.insert("slice".to_string(), Exp::Primitive(primitive::lisp_slice)); data.insert("chunks".to_string(), Exp::Primitive(primitive::lisp_chunks)); diff --git a/src/usr/lisp/eval.rs b/src/usr/lisp/eval.rs index 941b40e3b..1abec1ef6 100644 --- a/src/usr/lisp/eval.rs +++ b/src/usr/lisp/eval.rs @@ -35,7 +35,7 @@ fn eval_eq_args(args: &[Exp], env: &mut Rc>) -> Result { Ok(Exp::Bool(a == b)) } -fn eval_car_args(args: &[Exp], env: &mut Rc>) -> Result { +fn eval_head_args(args: &[Exp], env: &mut Rc>) -> Result { ensure_length_eq!(args, 1); match eval(&args[0], env)? { Exp::List(list) => { @@ -46,7 +46,7 @@ fn eval_car_args(args: &[Exp], env: &mut Rc>) -> Result { } } -fn eval_cdr_args(args: &[Exp], env: &mut Rc>) -> Result { +fn eval_tail_args(args: &[Exp], env: &mut Rc>) -> Result { ensure_length_eq!(args, 1); match eval(&args[0], env)? { Exp::List(list) => { @@ -149,7 +149,7 @@ pub fn eval_args(args: &[Exp], env: &mut Rc>) -> Result, E pub const BUILT_INS: [&str; 32] = [ "quote", "quasiquote", "unquote", "unquote-splicing", - "atom", "eq", "car", "cdr", "cons", + "atom", "eq", "head", "tail", "cons", "if", "cond", "while", "set", "define", "def", "label", @@ -180,8 +180,8 @@ pub fn eval(exp: &Exp, env: &mut Rc>) -> Result { 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 == "head" => return eval_head_args(args, env), + Exp::Sym(s) if s == "tail" => return eval_tail_args(args, env), Exp::Sym(s) if s == "cons" => return eval_cons_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), diff --git a/src/usr/lisp/mod.rs b/src/usr/lisp/mod.rs index c6e343f12..13461073b 100644 --- a/src/usr/lisp/mod.rs +++ b/src/usr/lisp/mod.rs @@ -336,13 +336,13 @@ fn test_lisp() { assert_eq!(eval!("(eq 1 1.0)"), "false"); assert_eq!(eval!("(eq 1.0 1.0)"), "true"); - // car - assert_eq!(eval!("(car (quote (1)))"), "1"); - assert_eq!(eval!("(car (quote (1 2 3)))"), "1"); + // head + assert_eq!(eval!("(head (quote (1)))"), "1"); + assert_eq!(eval!("(head (quote (1 2 3)))"), "1"); - // cdr - assert_eq!(eval!("(cdr (quote (1)))"), "()"); - assert_eq!(eval!("(cdr (quote (1 2 3)))"), "(2 3)"); + // tail + assert_eq!(eval!("(tail (quote (1)))"), "()"); + assert_eq!(eval!("(tail (quote (1 2 3)))"), "(2 3)"); // cons assert_eq!(eval!("(cons (quote 1) (quote (2 3)))"), "(1 2 3)"); @@ -484,6 +484,7 @@ fn test_lisp() { 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)"); + assert_eq!(eval!("`(list ,(+ 1 2) 4)"), "(list 3 4)"); // unquote-splice eval!("(define x '(1 2 3))"); diff --git a/src/usr/lisp/primitive.rs b/src/usr/lisp/primitive.rs index 2a523c906..f36632ed6 100644 --- a/src/usr/lisp/primitive.rs +++ b/src/usr/lisp/primitive.rs @@ -51,12 +51,12 @@ pub fn lisp_add(args: &[Exp]) -> Result { pub fn lisp_sub(args: &[Exp]) -> Result { ensure_length_gt!(args, 0); let args = list_of_numbers(args)?; - let car = args[0].clone(); + let head = args[0].clone(); if args.len() == 1 { - Ok(Exp::Num(-car)) + Ok(Exp::Num(-head)) } else { let res = args[1..].iter().fold(Number::Int(0), |acc, a| acc + a.clone()); - Ok(Exp::Num(car - res)) + Ok(Exp::Num(head - res)) } } @@ -71,8 +71,8 @@ pub fn lisp_div(args: &[Exp]) -> Result { return Err(Err::Reason("Division by zero".to_string())); } } - let car = args[0].clone(); - let res = args[1..].iter().fold(car, |acc, a| acc / a.clone()); + let head = args[0].clone(); + let res = args[1..].iter().fold(head, |acc, a| acc / a.clone()); Ok(Exp::Num(res)) } @@ -84,16 +84,16 @@ pub fn lisp_mod(args: &[Exp]) -> Result { return Err(Err::Reason("Division by zero".to_string())); } } - let car = args[0].clone(); - let res = args[1..].iter().fold(car, |acc, a| acc % a.clone()); + let head = args[0].clone(); + let res = args[1..].iter().fold(head, |acc, a| acc % a.clone()); Ok(Exp::Num(res)) } pub fn lisp_exp(args: &[Exp]) -> Result { ensure_length_gt!(args, 0); let args = list_of_numbers(args)?; - let car = args[0].clone(); - let res = args[1..].iter().fold(car, |acc, a| acc.pow(a)); + let head = args[0].clone(); + let res = args[1..].iter().fold(head, |acc, a| acc.pow(a)); Ok(Exp::Num(res)) } @@ -311,7 +311,7 @@ pub fn lisp_list(args: &[Exp]) -> Result { Ok(Exp::List(args.to_vec())) } -pub fn lisp_uniq(args: &[Exp]) -> Result { +pub fn lisp_unique(args: &[Exp]) -> Result { ensure_length_eq!(args, 1); if let Exp::List(list) = &args[0] { let mut list = list.clone(); @@ -342,6 +342,28 @@ pub fn lisp_contains(args: &[Exp]) -> Result { } } +pub fn lisp_nth(args: &[Exp]) -> Result { + ensure_length_eq!(args, 2); + let i = usize::try_from(number(&args[1])?)?; + match &args[0] { + Exp::List(l) => { + if let Some(e) = l.iter().nth(i) { + Ok(e.clone()) + } else { + Ok(Exp::List(Vec::new())) + } + } + Exp::Str(s) => { + if let Some(c) = s.chars().nth(i) { + Ok(Exp::Str(c.to_string())) + } else { + Ok(Exp::Str("".to_string())) + } + } + _ => Err(Err::Reason("Expected first arg to be a list or a string".to_string())) + } +} + pub fn lisp_slice(args: &[Exp]) -> Result { let (a, b) = match args.len() { 2 => (usize::try_from(number(&args[1])?)?, 1), @@ -357,7 +379,7 @@ pub fn lisp_slice(args: &[Exp]) -> Result { let s: String = s.chars().skip(a).take(b).collect(); Ok(Exp::Str(s)) } - _ => Err(Err::Reason("Expected first arg to be a list or a number".to_string())) + _ => Err(Err::Reason("Expected first arg to be a list or a string".to_string())) } }