Refactor lisp functions (#478)

* Refactor function names

* Rename list_of_xs into xs

* Rename bytes to binary

* Fix failing tests

* Update changelog

* Remove legacy functions
This commit is contained in:
Vincent Ollivier 2023-04-22 15:46:05 +02:00 committed by GitHub
parent fcab94a804
commit 4d50eae287
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 372 additions and 332 deletions

View File

@ -1,6 +1,7 @@
# Changelog
## Unreleased
- Refactor lisp functions (#478)
- Improve asm binaries (#482)
- Add light palette (#480)
- Fix invalid bytes from serial (#479)

View File

@ -3,7 +3,7 @@
A minimalist Lisp interpreter is available in MOROS to extend the capabilities
of the Shell.
MOROS Lisp is a Lisp-1 dialect inspired by Scheme and Clojure.
MOROS Lisp is a Lisp-1 dialect inspired by Scheme, Clojure, and Ruby!
## Changelog
@ -36,6 +36,9 @@ Rewrite parts of the code and add new functions and examples.
- Add tail call optimization (TCO)
- Add macro support
### 0.5.0 (unpublished)
- Rename or add aliases to many functions
## Overview
### Types
@ -48,58 +51,65 @@ Rewrite parts of the code and add new functions and examples.
- `unquote` (with the `,` syntax)
- `unquote-splice` (with the `,@` syntax)
- `splice` (with the `@` syntax)
- `atom` (aliased to `atom?`)
- `eq` (aliased to `eq?`)
- `head` (aliased to `car`)
- `tail` (aliased to `cdr`)
- `atom?`
- `equal?` (aliased to `eq?`)
- `head`
- `tail`
- `cons`
- `if`
- `cond`
- `while`
- `set`
- `define` (aliased to `def` and `label`)
- `function` (aliased to `fun` and `lambda`)
- `variable` (aliased to `var`)
- `function` (aliased to `fun`)
- `macro` (aliased to `mac`)
- `set`
- `define` (aliased to `def` and equivalent to `define-function`)
- `define-function` (aliased to `def-fun`)
- `define-macro` (aliased to `def-mac`)
- `apply`
- `eval`
- `expand`
- `do` (aliased to `begin` and `progn`)
- `do`
- `load`
### Primitive Operators
- `append`
- `type`
- `string`
- `string->number`
- `string->bytes` and `bytes->string`
- `number->bytes` and `bytes->number`
- `type`, `number-type` (aliased to `num-type`)
- `string` (aliased to `str`)
- `string->number` (aliased to to `str->num`)
- `string->binary` and `binary->string` (aliased to `str->bin` and `bin->str`)
- `number->binary` and `binary->number` (aliased to `num->bin` and `bin->num`)
- `regex-find`
- `system`
- 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`, `sort`, `unique`, `min`, `max`
- File IO: `read-file`, `read-file-binary`, `write-file-binary`, `append-file-binary`
- List: `chunks`, `sort`, `unique` (aliased to `uniq`), `min`, `max`
- String: `trim`, `split`
- Enumerable: `length`, `nth`, `first`, `second`, `third`, `last`, `rest`, `slice`
- Enumerable: `length` (aliased to `len`), `nth`, `first`, `second`, `third`, `last`, `rest`, `slice`
### Core Library
- `nil`, `nil?`, `eq?`
- `atom?`, `string?`, `boolean?`, `symbol?`, `number?`, `list?`, `function?`, `macro?`
- `caar`, `cadr`, `cdar`, `cddr`, `first`, `second`, `third`, `rest`
- `map`, `reduce`, `reverse`, `range`, `filter`, `intersection`
- `nil`, `nil?`, `list?`
- `boolean?` (aliased to `bool?`), `string?` (aliased to `str?`), `symbol?` (aliased to `sym?`), `number?` (aliased to `num?`)
- `function?` (aliased to `fun?`), `macro?` (aliased to `mac?`)
- `first`, `second`, `third`, `rest`
- `map`, `reduce`, `reverse` (aliased to `rev`), `range`, `filter`, `intersection`
- `not`, `and`, `or`
- `let`
- `string-join`, `lines`, `words`, `chars`
- `join-string` (aliased to `join-str`), `lines`, `words`, `chars`
- `read-line`, `read-char`
- `print`, `println`
- `p`, `print`
- `write-file`, `append-file`
- `uptime`, `realtime`
- `regex-match?`
### Compatibility Library
- `atom`, `eq`, `label`, `lambda`, `progn`, `begin`
- `car`, `cdr`, `caar`, `cadr`, `cdar`, `cddr`
## Usage
The interpreter can be invoked from the shell:
@ -120,13 +130,13 @@ with the following content:
```lisp
(load "/lib/lisp/core.lsp")
(define (fibonacci n)
(def (fibonacci n)
(if (< n 2) n
(+ (fibonacci (- n 1)) (fibonacci (- n 2)))))
(println
(print
(if (nil? args) "Usage: fibonacci <num>"
(fibonacci (string->number (head args)))))
(fibonacci (str->num (head args)))))
```
Would produce the following output:
@ -141,38 +151,40 @@ Would produce the following output:
```lisp
(load "/lib/lisp/core.lsp")
(define foo 42) # Variable definition
(print "Hello, World!")
(define double (fun (x) (* x 2))) # Function definition
(define (double x) (* x 2)) # Shortcut
(var foo 42) # Variable definition
(set foo (+ 40 2)) # Variable assignement
(var double (fun (x) (* x 2))) # Function definition
(def (double x) (* x 2)) # Shortcut
(double foo) # => 84
(define (map f ls)
(def-mac (++ x) # Macro definition
`(set ,x (+ ,x 1)))
(var i 0)
(while (< i 10)
(++ i))
(= i 10) # => true
(def (map f ls)
(if (nil? ls) nil
(cons
(f (first ls))
(map f (rest ls)))))
(define bar (quote (1 2 3)))
(define bar '(1 2 3)) # Shortcut
(var bar (quote (1 2 3)))
(var bar '(1 2 3)) # Shortcut
(map double bar) # => (2 4 6)
(map (fun (x) (+ x 1)) '(4 5 6)) # => (5 6 7)
(set foo 0) # Variable assignment
(var name "Alice")
(= foo 10) # => false
(while (< foo 10)
(set foo (+ foo 1)))
(= foo 10) # => true
(define name "Alice")
(string "Hello, " name) # => "Hello, Alice"
(str "Hello, " name) # => "Hello, Alice"
(^ 2 128) # => 340282366920938463463374607431768211456
```

View File

@ -1,32 +1,23 @@
(define def
(macro args `(define ,@args)))
(variable var
(macro args `(variable ,@args)))
(define mac
(var mac
(macro args `(macro ,@args)))
(define fun
(var fun
(macro args `(function ,@args)))
(define def-mac
(macro args `(define-macro ,@args)))
(define def-fun
(macro args `(define-function ,@args)))
(define (car lst)
(head lst))
(define (cdr lst)
(tail lst))
(define label
(var def
(macro args `(define ,@args)))
(define lambda
(macro args `(function ,@args)))
(var def-mac
(macro args `(define-macro ,@args)))
(define progn
(macro args `(do ,@args)))
(var def-fun
(macro args `(define-function ,@args)))
(define begin
(macro args `(do ,@args)))
(var eq?
(macro args `(equal? ,@args)))
(var rest
(macro args `(tail ,@args)))

View File

@ -1,152 +1,151 @@
(load "/lib/lisp/alias.lsp")
(define (eq? x y)
(eq x y))
(def (string? x)
(equal? (type x) "string"))
(define (atom? x)
(atom x))
(def (boolean? x)
(equal? (type x) "boolean"))
(define (string? x)
(eq? (type x) "string"))
(def (symbol? x)
(equal? (type x) "symbol"))
(define (boolean? x)
(eq? (type x) "boolean"))
(def (number? x)
(equal? (type x) "number"))
(define (symbol? x)
(eq? (type x) "symbol"))
(def (list? x)
(equal? (type x) "list"))
(define (number? x)
(eq? (type x) "number"))
(def (function? x)
(equal? (type x) "function"))
(define (list? x)
(eq? (type x) "list"))
(def (macro? x)
(equal? (type x) "macro"))
(define (function? x)
(eq? (type x) "function"))
(var nil '())
(define (macro? x)
(eq? (type x) "macro"))
(def (nil? x)
(equal? x nil))
(define nil '())
(define (nil? x)
(eq? x nil))
(define (not x)
(def (not x)
(if x false true))
(define-macro (or x y)
(def-mac (or x y)
`(if ,x true (if ,y true false)))
(define-macro (and x y)
(def-mac (and x y)
`(if ,x (if ,y true false) false))
(define-macro (let params values body)
`((function ,params ,body) ,@values))
(def-mac (let params values body)
`((fun ,params ,body) ,@values))
(define (reduce f ls)
(def (reduce f ls)
(if (nil? (tail ls)) (head ls)
(f (head ls) (reduce f (tail ls)))))
(define (map f ls)
(def (map f ls)
(if (nil? ls) nil
(cons
(f (head ls))
(map f (tail ls)))))
(define (filter f ls)
(def (filter f ls)
(if (nil? ls) nil
(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))
(def (intersection a b)
(filter (fun (x) (contains? b x)) a))
(define (reverse x)
(def (reverse x)
(if (nil? x) x
(append (reverse (tail x)) (cons (head x) '()))))
(define (range i n)
(def (range i n)
(if (= i n) nil
(append (list i) (range (+ i 1) n))))
(define (min lst)
(def (min lst)
(head (sort lst)))
(define (max lst)
(def (max lst)
(head (reverse (sort lst))))
(define (abs x)
(def (abs x)
(if (> x 0) x (- x)))
(define (string-join ls s)
(reduce (function (x y) (string x s y)) ls))
(def (join-string ls s)
(reduce (fun (x y) (string x s y)) ls))
(define (read-line)
(bytes->string (reverse (tail (reverse (read-file-bytes "/dev/console" 256))))))
(def (read-line)
(binary->string (reverse (tail (reverse (read-file-binary "/dev/console" 256))))))
(define (read-char)
(bytes->string (read-file-bytes "/dev/console" 4)))
(def (read-char)
(binary->string (read-file-binary "/dev/console" 4)))
(define (print exp)
(def (p exp)
(do
(append-file-bytes "/dev/console" (string->bytes (string exp)))
(append-file-binary "/dev/console" (string->binary (string exp)))
'()))
(define (println exp)
(print (string exp "\n")))
(def (print exp)
(p (string exp "\n")))
(define (uptime)
(bytes->number (read-file-bytes "/dev/clk/uptime" 8) "float"))
(def (uptime)
(binary->number (read-file-binary "/dev/clk/uptime" 8) "float"))
(define (realtime)
(bytes->number (read-file-bytes "/dev/clk/realtime" 8) "float"))
(def (realtime)
(binary->number (read-file-binary "/dev/clk/realtime" 8) "float"))
(define (write-file path str)
(write-file-bytes path (string->bytes str)))
(def (write-file path s)
(write-file-binary path (string->binary s)))
(define (append-file path str)
(append-file-bytes path (string->bytes str)))
(def (append-file path s)
(append-file-binary path (string->binary s)))
(define (regex-match? pattern str)
(def (regex-match? pattern s)
(not (nil? (regex-find pattern str))))
(define (lines contents)
(def (lines contents)
(split (trim contents) "\n"))
(define (words contents)
(def (words contents)
(split contents " "))
(define (chars contents)
(def (chars contents)
(split contents ""))
(define (first lst)
(def (first lst)
(nth lst 0))
(define (second lst)
(def (second lst)
(nth lst 1))
(define (third lst)
(def (third lst)
(nth lst 2))
(define (last lst)
(def (last lst)
(nth lst
(if (= (length lst) 0) 0 (- (length lst) 1))))
(define (caar x)
(car (car x)))
(var str string)
(var num-type number-type)
(var join-str join-string)
(define (cadr x)
(car (cdr x)))
(var str->num string->number)
(var str->bin string->binary)
(var num->bin number->binary)
(var bin->str binary->string)
(var bin->num binary->number)
(define (cdar x)
(cdr (car x)))
(var bool? boolean?)
(var str? string?)
(var sym? symbol?)
(var num? number?)
(define (cddr x)
(cdr (cdr x)))
(var fun? function?)
(var mac? macro?)
(define rest cdr)
(define len length)
(define rev reverse)
(define uniq unique)
(var len length)
(var rev reverse)
(var uniq unique)

35
dsk/lib/lisp/legacy.lsp Normal file
View File

@ -0,0 +1,35 @@
(def (car x)
(head x))
(def (cdr x)
(tail x))
(def (caar x)
(car (car x)))
(def (cadr x)
(car (cdr x)))
(def (cdar x)
(cdr (car x)))
(def (cddr x)
(cdr (cdr x)))
(def (atom x)
(atom? x))
(def (eq x y)
(equal? x y))
(var label
(macro args `(variable ,@args)))
(var lambda
(macro args `(function ,@args)))
(var progn
(macro args `(do ,@args)))
(var begin
(macro args `(do ,@args)))

View File

@ -1,23 +1,23 @@
(load "/lib/lisp/core.lsp")
(define esc (bytes->string '(27)))
(var esc (bin->str '(27)))
(define (ansi-color x y)
(string esc "[" x ";" y "m"))
(def (ansi-color x y)
(str esc "[" x ";" y "m"))
(define (fg c)
(def (fg c)
(ansi-color c 40))
(define (bg c)
(def (bg c)
(ansi-color 30 c))
(define (color f c)
(string " " (f c) (if (< c 100) " " "") c (ansi-color 0 0)))
(def (color f c)
(str " " (f c) (if (< c 100) " " "") c (ansi-color 0 0)))
(define (colors fs i j)
(string-join (map (function (c) (color fs c)) (range i j)) ""))
(def (colors fs i j)
(join-str (map (fun (c) (color fs c)) (range i j)) ""))
(println (colors fg 30 38))
(println (colors fg 90 98))
(println (colors bg 40 48))
(println (colors bg 100 108))
(print (colors fg 30 38))
(print (colors fg 90 98))
(print (colors bg 40 48))
(print (colors bg 100 108))

View File

@ -1,12 +1,12 @@
(load "/lib/lisp/core.lsp")
(define (factorial-helper n acc)
(def (factorial-helper n acc)
(if (< n 2) acc
(factorial-helper (- n 1) (* acc n))))
(define (factorial n)
(def (factorial n)
(factorial-helper n 1))
(println
(print
(if (nil? args) "Usage: factorial <num>"
(factorial (string->number (head args)))))
(factorial (str->num (head args)))))

View File

@ -1,9 +1,9 @@
(load "/lib/lisp/core.lsp")
(define (fibonacci n)
(def (fibonacci n)
(if (< n 2) n
(+ (fibonacci (- n 1)) (fibonacci (- n 2)))))
(println
(print
(if (nil? args) "Usage: fibonacci <num>"
(fibonacci (string->number (head args)))))
(fibonacci (str->num (head args)))))

View File

@ -1,19 +1,19 @@
(load "/lib/lisp/core.lsp")
(define (equation-of-time y)
(def (equation-of-time y)
(* 60.0 229.18 (+ 0.000075 (-
(* 0.001868 (cos (* 1.0 y)))
(* 0.032077 (sin (* 1.0 y)))
(* 0.014615 (cos (* 2.0 y)))
(* 0.040849 (sin (* 2.0 y)))))))
(define (days timestamp)
(def (days timestamp)
(trunc (% (/ timestamp 86400.0) 365.2425)))
(define (hours timestamp)
(def (hours timestamp)
(trunc (/ (% timestamp 86400.0) 3600.0)))
(define (seconds timestamp longitude)
(def (seconds timestamp longitude)
(+
(% timestamp 86400.0)
(/ (* longitude 86400.0) 360.0)
@ -21,18 +21,18 @@
(/ (* 2 pi) 365.0)
(+ (days timestamp) (/ (- (hours timestamp) 12.0) 24.0))))))
(define (pad x)
(string (if (< x 10) "0" "") x))
(def (pad x)
(str (if (< x 10) "0" "") x))
(define (fmt x)
(string (pad (trunc x)) ":" (pad (abs (trunc (* (- x (trunc x)) 100.0))))))
(def (fmt x)
(str (pad (trunc x)) ":" (pad (abs (trunc (* (- x (trunc x)) 100.0))))))
(define (geotime longitude timestamp)
(def (geotime longitude timestamp)
(fmt (% (/ (* (seconds timestamp longitude) 100.0) 86400.0) 100.0)))
(println
(if (= (length args) 1)
(geotime (string->number (first args)) (realtime))
(if (= (length args) 2)
(geotime (string->number (first args)) (string->number (second args)))
(print
(if (= (len args) 1)
(geotime (str->num (first args)) (realtime))
(if (= (len args) 2)
(geotime (str->num (first args)) (str->num (second args)))
"Usage: geotime <longitude> [<timestamp>]")))

View File

@ -1,26 +1,26 @@
(load "/lib/lisp/core.lsp")
(define (pi-digits digits)
(def (pi-digits digits)
(do
(define i 0)
(define q 1)
(define r 0)
(define t 1)
(define k 1)
(define n 3)
(define l 3)
(var i 0)
(var q 1)
(var r 0)
(var t 1)
(var k 1)
(var n 3)
(var l 3)
(while (<= i digits)
(if (< (- (+ (* q 4) r) t) (* n t))
(do
(print (string n (if (= i 0) "." "")))
(p (str n (if (= i 0) "." "")))
(set i (+ i 1))
(define nr (* 10 (- r (* n t))))
(var nr (* 10 (- r (* n t))))
(set n (- (/ (* 10 (+ (* 3 q) r)) t) (* 10 n)))
(set q (* q 10))
(set r nr))
(do
(define nr (* (+ (* 2 q) r) l))
(define nn (/ (+ 2 (* q k 7) (* r l)) (* t l)))
(var nr (* (+ (* 2 q) r) l))
(var nn (/ (+ 2 (* q k 7) (* r l)) (* t l)))
(set q (* q k))
(set t (* t l))
(set l (+ l 2))
@ -29,6 +29,6 @@
(set r nr))))
""))
(println
(print
(if (nil? args) "Usage: pi <precision>"
(pi-digits (string->number (head args)))))
(pi-digits (str->num (head args)))))

View File

@ -1,8 +1,8 @@
(load "/lib/lisp/core.lsp")
(define (sum n acc)
(def (sum n acc)
(if (= n 0) acc (sum (- n 1) (+ n acc))))
(println
(print
(if (nil? args) "Usage: sum <num>"
(sum (string->number (head args)) 0)))
(sum (str->num (head args)) 0)))

View File

@ -52,6 +52,7 @@ pub fn copy_files(verbose: bool) {
create_dir("/lib/lisp", verbose);
copy_file("/lib/lisp/core.lsp", include_bytes!("../../dsk/lib/lisp/core.lsp"), verbose);
copy_file("/lib/lisp/alias.lsp", include_bytes!("../../dsk/lib/lisp/alias.lsp"), verbose);
//copy_file("/lib/lisp/legacy.lsp", include_bytes!("../../dsk/lib/lisp/legacy.lsp"), verbose);
copy_file("/tmp/alice.txt", include_bytes!("../../dsk/tmp/alice.txt"), verbose);
copy_file("/tmp/machines.txt", include_bytes!("../../dsk/tmp/machines.txt"), verbose);

View File

@ -22,53 +22,53 @@ pub struct Env {
pub fn default_env() -> Rc<RefCell<Env>> {
let mut data: BTreeMap<String, Exp> = BTreeMap::new();
data.insert("pi".to_string(), Exp::Num(Number::from(PI)));
data.insert("=".to_string(), Exp::Primitive(primitive::lisp_eq));
data.insert(">".to_string(), Exp::Primitive(primitive::lisp_gt));
data.insert(">=".to_string(), Exp::Primitive(primitive::lisp_gte));
data.insert("<".to_string(), Exp::Primitive(primitive::lisp_lt));
data.insert("<=".to_string(), Exp::Primitive(primitive::lisp_lte));
data.insert("*".to_string(), Exp::Primitive(primitive::lisp_mul));
data.insert("+".to_string(), Exp::Primitive(primitive::lisp_add));
data.insert("-".to_string(), Exp::Primitive(primitive::lisp_sub));
data.insert("/".to_string(), Exp::Primitive(primitive::lisp_div));
data.insert("%".to_string(), Exp::Primitive(primitive::lisp_mod));
data.insert("^".to_string(), Exp::Primitive(primitive::lisp_exp));
data.insert("<<".to_string(), Exp::Primitive(primitive::lisp_shl));
data.insert(">>".to_string(), Exp::Primitive(primitive::lisp_shr));
data.insert("cos".to_string(), Exp::Primitive(primitive::lisp_cos));
data.insert("acos".to_string(), Exp::Primitive(primitive::lisp_acos));
data.insert("asin".to_string(), Exp::Primitive(primitive::lisp_asin));
data.insert("atan".to_string(), Exp::Primitive(primitive::lisp_atan));
data.insert("sin".to_string(), Exp::Primitive(primitive::lisp_sin));
data.insert("tan".to_string(), Exp::Primitive(primitive::lisp_tan));
data.insert("trunc".to_string(), Exp::Primitive(primitive::lisp_trunc));
data.insert("system".to_string(), Exp::Primitive(primitive::lisp_system));
data.insert("read-file".to_string(), Exp::Primitive(primitive::lisp_read_file));
data.insert("read-file-bytes".to_string(), Exp::Primitive(primitive::lisp_read_file_bytes));
data.insert("write-file-bytes".to_string(), Exp::Primitive(primitive::lisp_write_file_bytes));
data.insert("append-file-bytes".to_string(), Exp::Primitive(primitive::lisp_append_file_bytes));
data.insert("string".to_string(), Exp::Primitive(primitive::lisp_string));
data.insert("string->bytes".to_string(), Exp::Primitive(primitive::lisp_string_bytes));
data.insert("bytes->string".to_string(), Exp::Primitive(primitive::lisp_bytes_string));
data.insert("bytes->number".to_string(), Exp::Primitive(primitive::lisp_bytes_number));
data.insert("number->bytes".to_string(), Exp::Primitive(primitive::lisp_number_bytes));
data.insert("regex-find".to_string(), Exp::Primitive(primitive::lisp_regex_find));
data.insert("string->number".to_string(), Exp::Primitive(primitive::lisp_string_number));
data.insert("type".to_string(), Exp::Primitive(primitive::lisp_type));
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("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));
data.insert("split".to_string(), Exp::Primitive(primitive::lisp_split));
data.insert("trim".to_string(), Exp::Primitive(primitive::lisp_trim));
data.insert("length".to_string(), Exp::Primitive(primitive::lisp_length));
data.insert("append".to_string(), Exp::Primitive(primitive::lisp_append));
data.insert("pi".to_string(), Exp::Num(Number::from(PI)));
data.insert("=".to_string(), Exp::Primitive(primitive::lisp_eq));
data.insert(">".to_string(), Exp::Primitive(primitive::lisp_gt));
data.insert(">=".to_string(), Exp::Primitive(primitive::lisp_gte));
data.insert("<".to_string(), Exp::Primitive(primitive::lisp_lt));
data.insert("<=".to_string(), Exp::Primitive(primitive::lisp_lte));
data.insert("*".to_string(), Exp::Primitive(primitive::lisp_mul));
data.insert("+".to_string(), Exp::Primitive(primitive::lisp_add));
data.insert("-".to_string(), Exp::Primitive(primitive::lisp_sub));
data.insert("/".to_string(), Exp::Primitive(primitive::lisp_div));
data.insert("%".to_string(), Exp::Primitive(primitive::lisp_mod));
data.insert("^".to_string(), Exp::Primitive(primitive::lisp_exp));
data.insert("<<".to_string(), Exp::Primitive(primitive::lisp_shl));
data.insert(">>".to_string(), Exp::Primitive(primitive::lisp_shr));
data.insert("cos".to_string(), Exp::Primitive(primitive::lisp_cos));
data.insert("acos".to_string(), Exp::Primitive(primitive::lisp_acos));
data.insert("asin".to_string(), Exp::Primitive(primitive::lisp_asin));
data.insert("atan".to_string(), Exp::Primitive(primitive::lisp_atan));
data.insert("sin".to_string(), Exp::Primitive(primitive::lisp_sin));
data.insert("tan".to_string(), Exp::Primitive(primitive::lisp_tan));
data.insert("trunc".to_string(), Exp::Primitive(primitive::lisp_trunc));
data.insert("system".to_string(), Exp::Primitive(primitive::lisp_system));
data.insert("read-file".to_string(), Exp::Primitive(primitive::lisp_read_file));
data.insert("read-file-binary".to_string(), Exp::Primitive(primitive::lisp_read_file_bytes));
data.insert("write-file-binary".to_string(), Exp::Primitive(primitive::lisp_write_file_bytes));
data.insert("append-file-binary".to_string(), Exp::Primitive(primitive::lisp_append_file_bytes));
data.insert("string".to_string(), Exp::Primitive(primitive::lisp_string));
data.insert("string->binary".to_string(), Exp::Primitive(primitive::lisp_string_bytes));
data.insert("binary->string".to_string(), Exp::Primitive(primitive::lisp_bytes_string));
data.insert("binary->number".to_string(), Exp::Primitive(primitive::lisp_bytes_number));
data.insert("number->binary".to_string(), Exp::Primitive(primitive::lisp_number_bytes));
data.insert("regex-find".to_string(), Exp::Primitive(primitive::lisp_regex_find));
data.insert("string->number".to_string(), Exp::Primitive(primitive::lisp_string_number));
data.insert("type".to_string(), Exp::Primitive(primitive::lisp_type));
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("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));
data.insert("split".to_string(), Exp::Primitive(primitive::lisp_split));
data.insert("trim".to_string(), Exp::Primitive(primitive::lisp_trim));
data.insert("length".to_string(), Exp::Primitive(primitive::lisp_length));
data.insert("append".to_string(), Exp::Primitive(primitive::lisp_append));
// Setup autocompletion
*FORMS.lock() = data.keys().cloned().chain(BUILT_INS.map(String::from)).collect();

View File

@ -28,7 +28,7 @@ fn eval_atom_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err>
}
}
fn eval_eq_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
fn eval_equal_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
ensure_length_eq!(args, 2);
let a = eval(&args[0], env)?;
let b = eval(&args[1], env)?;
@ -68,7 +68,7 @@ fn eval_cons_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> {
pub fn eval_variable_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
ensure_length_eq!(args, 2);
match &args[0] {
Exp::Sym(name) => {
@ -147,18 +147,16 @@ pub fn eval_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Vec<Exp>, E
args.iter().map(|x| eval(x, env)).collect()
}
pub const BUILT_INS: [&str; 32] = [
pub const BUILT_INS: [&str; 24] = [
"quote", "quasiquote", "unquote", "unquote-splicing",
"atom", "eq", "head", "tail", "cons",
"atom?", "equal?", "head", "tail", "cons",
"if", "cond", "while",
"variable", "function", "macro",
"define-function", "define",
"define-macro",
"set",
"define", "def", "label",
"function", "fun", "lambda",
"macro", "mac",
"define-function", "def-fun",
"define-macro", "def-mac",
"apply", "eval", "expand",
"do", "begin", "progn",
"do",
"load"
];
@ -177,20 +175,20 @@ pub fn eval(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
ensure_length_gt!(list, 0);
let args = &list[1..];
match &list[0] {
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 == "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),
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 == "do" => return eval_do_args(args, env),
Exp::Sym(s) if s == "load" => return eval_load_args(args, env),
Exp::Sym(s) if s == "define" => return eval_define_args(args, env),
Exp::Sym(s) if s == "expand" => {
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 == "equal?" => return eval_equal_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),
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 == "do" => return eval_do_args(args, env),
Exp::Sym(s) if s == "load" => return eval_load_args(args, env),
Exp::Sym(s) if s == "variable" => return eval_variable_args(args, env),
Exp::Sym(s) if s == "expand" => {
ensure_length_eq!(args, 1);
return expand(&args[0], env);
}

View File

@ -64,7 +64,7 @@ pub fn expand(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
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("variable".to_string()), name, Exp::List(vec![
Exp::Sym("function".to_string()), args, body
])
]))
@ -82,7 +82,7 @@ pub fn expand(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
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("variable".to_string()), name, Exp::List(vec![
Exp::Sym("macro".to_string()), args, body
])
]))

View File

@ -9,7 +9,7 @@ pub use number::Number;
pub use env::Env;
use env::default_env;
use eval::{eval, eval_define_args};
use eval::{eval, eval_variable_args};
use expand::expand;
use parse::parse;
@ -143,12 +143,16 @@ macro_rules! ensure_length_gt {
};
}
pub fn list_of_numbers(args: &[Exp]) -> Result<Vec<Number>, Err> {
args.iter().map(number).collect()
pub fn bytes(args: &[Exp]) -> Result<Vec<u8>, Err> {
args.iter().map(byte).collect()
}
pub fn list_of_bytes(args: &[Exp]) -> Result<Vec<u8>, Err> {
args.iter().map(byte).collect()
pub fn strings(args: &[Exp]) -> Result<Vec<String>, Err> {
args.iter().map(string).collect()
}
pub fn numbers(args: &[Exp]) -> Result<Vec<Number>, Err> {
args.iter().map(number).collect()
}
pub fn string(exp: &Exp) -> Result<String, Err> {
@ -254,7 +258,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_define_args(&[key, quote], env).is_err() {
if eval_variable_args(&[key, quote], env).is_err() {
error!("Could not parse args");
return Err(ExitCode::Failure);
}
@ -318,23 +322,23 @@ fn test_lisp() {
assert_eq!(eval!("'a"), "a");
assert_eq!(eval!("(quote '(a b c))"), "(quote (a b c))");
// atom
assert_eq!(eval!("(atom (quote a))"), "true");
assert_eq!(eval!("(atom (quote (1 2 3)))"), "false");
assert_eq!(eval!("(atom 1)"), "true");
// atom?
assert_eq!(eval!("(atom? (quote a))"), "true");
assert_eq!(eval!("(atom? (quote (1 2 3)))"), "false");
assert_eq!(eval!("(atom? 1)"), "true");
// eq
assert_eq!(eval!("(eq (quote a) (quote a))"), "true");
assert_eq!(eval!("(eq (quote a) (quote b))"), "false");
assert_eq!(eval!("(eq (quote a) (quote ()))"), "false");
assert_eq!(eval!("(eq (quote ()) (quote ()))"), "true");
assert_eq!(eval!("(eq \"a\" \"a\")"), "true");
assert_eq!(eval!("(eq \"a\" \"b\")"), "false");
assert_eq!(eval!("(eq \"a\" 'b)"), "false");
assert_eq!(eval!("(eq 1 1)"), "true");
assert_eq!(eval!("(eq 1 2)"), "false");
assert_eq!(eval!("(eq 1 1.0)"), "false");
assert_eq!(eval!("(eq 1.0 1.0)"), "true");
// equal?
assert_eq!(eval!("(equal? (quote a) (quote a))"), "true");
assert_eq!(eval!("(equal? (quote a) (quote b))"), "false");
assert_eq!(eval!("(equal? (quote a) (quote ()))"), "false");
assert_eq!(eval!("(equal? (quote ()) (quote ()))"), "true");
assert_eq!(eval!("(equal? \"a\" \"a\")"), "true");
assert_eq!(eval!("(equal? \"a\" \"b\")"), "false");
assert_eq!(eval!("(equal? \"a\" 'b)"), "false");
assert_eq!(eval!("(equal? 1 1)"), "true");
assert_eq!(eval!("(equal? 1 2)"), "false");
assert_eq!(eval!("(equal? 1 1.0)"), "false");
assert_eq!(eval!("(equal? 1.0 1.0)"), "true");
// head
assert_eq!(eval!("(head (quote (1)))"), "1");
@ -361,14 +365,14 @@ fn test_lisp() {
assert_eq!(eval!("(if (> 2 4) 1 2)"), "2");
// while
assert_eq!(eval!("(do (define i 0) (while (< i 5) (set i (+ i 1))) i)"), "5");
assert_eq!(eval!("(do (variable i 0) (while (< i 5) (set i (+ i 1))) i)"), "5");
// define
eval!("(define a 2)");
// variable
eval!("(variable a 2)");
assert_eq!(eval!("(+ a 1)"), "3");
eval!("(define add-one (function (b) (+ b 1)))");
eval!("(variable add-one (function (b) (+ b 1)))");
assert_eq!(eval!("(add-one 2)"), "3");
eval!("(define fibonacci (function (n) (if (< n 2) n (+ (fibonacci (- n 1)) (fibonacci (- n 2))))))");
eval!("(variable fibonacci (function (n) (if (< n 2) n (+ (fibonacci (- n 1)) (fibonacci (- n 2))))))");
assert_eq!(eval!("(fibonacci 6)"), "8");
// function
@ -429,16 +433,16 @@ fn test_lisp() {
assert_eq!(eval!("(= (+ 0.15 0.15) (+ 0.1 0.2))"), "false"); // FIXME?
// number
assert_eq!(eval!("(bytes->number (number->bytes 42) \"int\")"), "42");
assert_eq!(eval!("(bytes->number (number->bytes 42.0) \"float\")"), "42.0");
assert_eq!(eval!("(binary->number (number->binary 42) \"int\")"), "42");
assert_eq!(eval!("(binary->number (number->binary 42.0) \"float\")"), "42.0");
// string
assert_eq!(eval!("(parse \"9.75\")"), "9.75");
assert_eq!(eval!("(string \"a\" \"b\" \"c\")"), "\"abc\"");
assert_eq!(eval!("(string \"a\" \"\")"), "\"a\"");
assert_eq!(eval!("(string \"foo \" 3)"), "\"foo 3\"");
assert_eq!(eval!("(eq \"foo\" \"foo\")"), "true");
assert_eq!(eval!("(eq \"foo\" \"bar\")"), "false");
assert_eq!(eval!("(equal? \"foo\" \"foo\")"), "true");
assert_eq!(eval!("(equal? \"foo\" \"bar\")"), "false");
assert_eq!(eval!("(split \"a\nb\nc\" \"\n\")"), "(\"a\" \"b\" \"c\")");
// apply
@ -481,13 +485,13 @@ fn test_lisp() {
assert_eq!(eval!("(number-type 9223372036854776000.0)"), "\"float\"");
// quasiquote
eval!("(define x 'a)");
eval!("(variable 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))");
eval!("(variable x '(1 2 3))");
assert_eq!(eval!("`(+ ,x)"), "(+ (1 2 3))");
assert_eq!(eval!("`(+ ,@x)"), "(+ 1 2 3)");
@ -496,12 +500,12 @@ fn test_lisp() {
assert_eq!(eval!("((function (a @b) b) 1 2 3)"), "(2 3)");
// macro
eval!("(define foo 42)");
eval!("(define set-10 (macro (x) `(set ,x 10)))");
eval!("(variable foo 42)");
eval!("(variable set-10 (macro (x) `(set ,x 10)))");
eval!("(set-10 foo)");
assert_eq!(eval!("foo"), "10");
// args
eval!("(define list* (function args (append args '())))");
eval!("(variable list* (function args (append args '())))");
assert_eq!(eval!("(list* 1 2 3)"), "(1 2 3)");
}

View File

@ -1,8 +1,7 @@
use super::list_of_bytes;
use super::list_of_numbers;
use super::parse::parse;
use super::{Err, Exp, Number};
use super::{float, number, string};
use super::{bytes, numbers, strings};
use crate::{ensure_length_eq, ensure_length_gt};
use crate::api::fs;
@ -19,38 +18,38 @@ use core::convert::TryFrom;
use core::convert::TryInto;
pub fn lisp_eq(args: &[Exp]) -> Result<Exp, Err> {
Ok(Exp::Bool(list_of_numbers(args)?.windows(2).all(|nums| nums[0] == nums[1])))
Ok(Exp::Bool(numbers(args)?.windows(2).all(|nums| nums[0] == nums[1])))
}
pub fn lisp_gt(args: &[Exp]) -> Result<Exp, Err> {
Ok(Exp::Bool(list_of_numbers(args)?.windows(2).all(|nums| nums[0] > nums[1])))
Ok(Exp::Bool(numbers(args)?.windows(2).all(|nums| nums[0] > nums[1])))
}
pub fn lisp_gte(args: &[Exp]) -> Result<Exp, Err> {
Ok(Exp::Bool(list_of_numbers(args)?.windows(2).all(|nums| nums[0] >= nums[1])))
Ok(Exp::Bool(numbers(args)?.windows(2).all(|nums| nums[0] >= nums[1])))
}
pub fn lisp_lt(args: &[Exp]) -> Result<Exp, Err> {
Ok(Exp::Bool(list_of_numbers(args)?.windows(2).all(|nums| nums[0] < nums[1])))
Ok(Exp::Bool(numbers(args)?.windows(2).all(|nums| nums[0] < nums[1])))
}
pub fn lisp_lte(args: &[Exp]) -> Result<Exp, Err> {
Ok(Exp::Bool(list_of_numbers(args)?.windows(2).all(|nums| nums[0] <= nums[1])))
Ok(Exp::Bool(numbers(args)?.windows(2).all(|nums| nums[0] <= nums[1])))
}
pub fn lisp_mul(args: &[Exp]) -> Result<Exp, Err> {
let res = list_of_numbers(args)?.iter().fold(Number::Int(1), |acc, a| acc * a.clone());
let res = numbers(args)?.iter().fold(Number::Int(1), |acc, a| acc * a.clone());
Ok(Exp::Num(res))
}
pub fn lisp_add(args: &[Exp]) -> Result<Exp, Err> {
let res = list_of_numbers(args)?.iter().fold(Number::Int(0), |acc, a| acc + a.clone());
let res = numbers(args)?.iter().fold(Number::Int(0), |acc, a| acc + a.clone());
Ok(Exp::Num(res))
}
pub fn lisp_sub(args: &[Exp]) -> Result<Exp, Err> {
ensure_length_gt!(args, 0);
let args = list_of_numbers(args)?;
let args = numbers(args)?;
let head = args[0].clone();
if args.len() == 1 {
Ok(Exp::Num(-head))
@ -62,7 +61,7 @@ pub fn lisp_sub(args: &[Exp]) -> Result<Exp, Err> {
pub fn lisp_div(args: &[Exp]) -> Result<Exp, Err> {
ensure_length_gt!(args, 0);
let mut args = list_of_numbers(args)?;
let mut args = numbers(args)?;
if args.len() == 1 {
args.insert(0, Number::Int(1));
}
@ -78,7 +77,7 @@ pub fn lisp_div(args: &[Exp]) -> Result<Exp, Err> {
pub fn lisp_mod(args: &[Exp]) -> Result<Exp, Err> {
ensure_length_gt!(args, 0);
let args = list_of_numbers(args)?;
let args = numbers(args)?;
for arg in &args[1..] {
if arg.is_zero() {
return Err(Err::Reason("Division by zero".to_string()));
@ -91,7 +90,7 @@ pub fn lisp_mod(args: &[Exp]) -> Result<Exp, Err> {
pub fn lisp_exp(args: &[Exp]) -> Result<Exp, Err> {
ensure_length_gt!(args, 0);
let args = list_of_numbers(args)?;
let args = numbers(args)?;
let head = args[0].clone();
let res = args[1..].iter().fold(head, |acc, a| acc.pow(a));
Ok(Exp::Num(res))
@ -99,14 +98,14 @@ pub fn lisp_exp(args: &[Exp]) -> Result<Exp, Err> {
pub fn lisp_shl(args: &[Exp]) -> Result<Exp, Err> {
ensure_length_eq!(args, 2);
let args = list_of_numbers(args)?;
let args = numbers(args)?;
let res = args[0].clone() << args[1].clone();
Ok(Exp::Num(res))
}
pub fn lisp_shr(args: &[Exp]) -> Result<Exp, Err> {
ensure_length_eq!(args, 2);
let args = list_of_numbers(args)?;
let args = numbers(args)?;
let res = args[0].clone() >> args[1].clone();
Ok(Exp::Num(res))
}
@ -155,8 +154,8 @@ pub fn lisp_trunc(args: &[Exp]) -> Result<Exp, Err> {
}
pub fn lisp_system(args: &[Exp]) -> Result<Exp, Err> {
ensure_length_eq!(args, 1);
let cmd = string(&args[0])?;
ensure_length_gt!(args, 0);
let cmd = strings(&args)?.join(" ");
match shell::exec(&cmd) {
Ok(()) => Ok(Exp::Num(Number::from(0 as u8))),
Err(code) => Ok(Exp::Num(Number::from(code as u8))),
@ -175,8 +174,8 @@ pub fn lisp_read_file_bytes(args: &[Exp]) -> Result<Exp, Err> {
let path = string(&args[0])?;
let len = number(&args[1])?;
let mut buf = vec![0; len.try_into()?];
let bytes = fs::read(&path, &mut buf).or(Err(Err::Reason("Could not read file".to_string())))?;
buf.resize(bytes, 0);
let n = fs::read(&path, &mut buf).or(Err(Err::Reason("Could not read file".to_string())))?;
buf.resize(n, 0);
Ok(Exp::List(buf.iter().map(|b| Exp::Num(Number::from(*b))).collect()))
}
@ -185,9 +184,9 @@ pub fn lisp_write_file_bytes(args: &[Exp]) -> Result<Exp, Err> {
let path = string(&args[0])?;
match &args[1] {
Exp::List(list) => {
let buf = list_of_bytes(list)?;
let bytes = fs::write(&path, &buf).or(Err(Err::Reason("Could not write file".to_string())))?;
Ok(Exp::Num(Number::from(bytes)))
let buf = bytes(list)?;
let n = fs::write(&path, &buf).or(Err(Err::Reason("Could not write file".to_string())))?;
Ok(Exp::Num(Number::from(n)))
}
_ => Err(Err::Reason("Expected second arg to be a list".to_string()))
}
@ -198,9 +197,9 @@ pub fn lisp_append_file_bytes(args: &[Exp]) -> Result<Exp, Err> {
let path = string(&args[0])?;
match &args[1] {
Exp::List(list) => {
let buf = list_of_bytes(list)?;
let bytes = fs::append(&path, &buf).or(Err(Err::Reason("Could not write file".to_string())))?;
Ok(Exp::Num(Number::from(bytes)))
let buf = bytes(list)?;
let n = fs::append(&path, &buf).or(Err(Err::Reason("Could not write file".to_string())))?;
Ok(Exp::Num(Number::from(n)))
}
_ => Err(Err::Reason("Expected second arg to be a list".to_string()))
}
@ -225,7 +224,7 @@ pub fn lisp_bytes_string(args: &[Exp]) -> Result<Exp, Err> {
ensure_length_eq!(args, 1);
match &args[0] {
Exp::List(list) => {
let buf = list_of_bytes(list)?;
let buf = bytes(list)?;
let s = String::from_utf8(buf).or(Err(Err::Reason("Could not convert to valid UTF-8 string".to_string())))?;
Ok(Exp::Str(s))
}
@ -237,11 +236,11 @@ pub fn lisp_bytes_number(args: &[Exp]) -> Result<Exp, Err> {
ensure_length_eq!(args, 2);
match (&args[0], &args[1]) { // TODO: default type to "int" and make it optional
(Exp::List(list), Exp::Str(kind)) => {
let bytes = list_of_bytes(list)?;
ensure_length_eq!(bytes, 8);
let buf = bytes(list)?;
ensure_length_eq!(buf, 8);
match kind.as_str() { // TODO: bigint
"int" => Ok(Exp::Num(Number::Int(i64::from_be_bytes(bytes[0..8].try_into().unwrap())))),
"float" => Ok(Exp::Num(Number::Float(f64::from_be_bytes(bytes[0..8].try_into().unwrap())))),
"int" => Ok(Exp::Num(Number::Int(i64::from_be_bytes(buf[0..8].try_into().unwrap())))),
"float" => Ok(Exp::Num(Number::Float(f64::from_be_bytes(buf[0..8].try_into().unwrap())))),
_ => Err(Err::Reason("Invalid number type".to_string())),
}
}