Skip to content

Commit

Permalink
Improve lisp (#455)
Browse files Browse the repository at this point in the history
* Replace car and cdr builtins with head and tail

* Add nth function

* Reorder core functions

* Rename uniq to unique

* Add list test

* Update doc

* Update examples

* Remove abs definition from example

* Fix tests

* Fix rest
  • Loading branch information
vinc authored Dec 16, 2022
1 parent 87eb51d commit 5251bae
Show file tree
Hide file tree
Showing 12 changed files with 110 additions and 69 deletions.
11 changes: 6 additions & 5 deletions doc/lisp.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -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?`
Expand Down Expand Up @@ -125,7 +126,7 @@ with the following content:
(println
(if (nil? args) "Usage: fibonacci <num>"
(fibonacci (string->number (car args)))))
(fibonacci (string->number (head args)))))
```

Would produce the following output:
Expand Down
6 changes: 5 additions & 1 deletion dsk/lib/lisp/alias.lsp
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand Down
81 changes: 48 additions & 33 deletions dsk/lib/lisp/core.lsp
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand Down Expand Up @@ -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)
2 changes: 1 addition & 1 deletion dsk/tmp/lisp/factorial.lsp
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@

(println
(if (nil? args) "Usage: factorial <num>"
(factorial (string->number (car args)))))
(factorial (string->number (head args)))))
2 changes: 1 addition & 1 deletion dsk/tmp/lisp/fibonacci.lsp
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@

(println
(if (nil? args) "Usage: fibonacci <num>"
(fibonacci (string->number (car args)))))
(fibonacci (string->number (head args)))))
3 changes: 0 additions & 3 deletions dsk/tmp/lisp/geotime.lsp
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
2 changes: 1 addition & 1 deletion dsk/tmp/lisp/pi.lsp
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@

(println
(if (nil? args) "Usage: pi <precision>"
(pi-digits (string->number (car args)))))
(pi-digits (string->number (head args)))))
2 changes: 1 addition & 1 deletion dsk/tmp/lisp/sum.lsp
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@

(println
(if (nil? args) "Usage: sum <num>"
(sum (string->number (car args)) 0)))
(sum (string->number (head args)) 0)))
3 changes: 2 additions & 1 deletion src/usr/lisp/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ pub fn default_env() -> Rc<RefCell<Env>> {
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));
Expand Down
10 changes: 5 additions & 5 deletions src/usr/lisp/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ fn eval_eq_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
Ok(Exp::Bool(a == b))
}

fn eval_car_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
fn eval_head_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
ensure_length_eq!(args, 1);
match eval(&args[0], env)? {
Exp::List(list) => {
Expand All @@ -46,7 +46,7 @@ fn eval_car_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
}
}

fn eval_cdr_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
fn eval_tail_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
ensure_length_eq!(args, 1);
match eval(&args[0], env)? {
Exp::List(list) => {
Expand Down Expand Up @@ -149,7 +149,7 @@ pub fn eval_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Vec<Exp>, 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",
Expand Down Expand Up @@ -180,8 +180,8 @@ pub fn eval(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
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),
Expand Down
13 changes: 7 additions & 6 deletions src/usr/lisp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)");
Expand Down Expand Up @@ -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))");
Expand Down
44 changes: 33 additions & 11 deletions src/usr/lisp/primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@ pub fn lisp_add(args: &[Exp]) -> Result<Exp, Err> {
pub fn lisp_sub(args: &[Exp]) -> Result<Exp, Err> {
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))
}
}

Expand All @@ -71,8 +71,8 @@ pub fn lisp_div(args: &[Exp]) -> Result<Exp, Err> {
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))
}

Expand All @@ -84,16 +84,16 @@ pub fn lisp_mod(args: &[Exp]) -> Result<Exp, Err> {
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<Exp, Err> {
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))
}

Expand Down Expand Up @@ -311,7 +311,7 @@ pub fn lisp_list(args: &[Exp]) -> Result<Exp, Err> {
Ok(Exp::List(args.to_vec()))
}

pub fn lisp_uniq(args: &[Exp]) -> Result<Exp, Err> {
pub fn lisp_unique(args: &[Exp]) -> Result<Exp, Err> {
ensure_length_eq!(args, 1);
if let Exp::List(list) = &args[0] {
let mut list = list.clone();
Expand Down Expand Up @@ -342,6 +342,28 @@ pub fn lisp_contains(args: &[Exp]) -> Result<Exp, Err> {
}
}

pub fn lisp_nth(args: &[Exp]) -> Result<Exp, Err> {
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<Exp, Err> {
let (a, b) = match args.len() {
2 => (usize::try_from(number(&args[1])?)?, 1),
Expand All @@ -357,7 +379,7 @@ pub fn lisp_slice(args: &[Exp]) -> Result<Exp, Err> {
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()))
}
}

Expand Down

0 comments on commit 5251bae

Please sign in to comment.