mirror of https://github.com/vinc/moros.git
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:
parent
fcab94a804
commit
4d50eae287
|
@ -1,6 +1,7 @@
|
|||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
- Refactor lisp functions (#478)
|
||||
- Improve asm binaries (#482)
|
||||
- Add light palette (#480)
|
||||
- Fix invalid bytes from serial (#479)
|
||||
|
|
98
doc/lisp.md
98
doc/lisp.md
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)))
|
|
@ -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))
|
||||
|
|
|
@ -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)))))
|
||||
|
|
|
@ -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)))))
|
||||
|
|
|
@ -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>]")))
|
||||
|
|
|
@ -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)))))
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
])
|
||||
]))
|
||||
|
|
|
@ -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)");
|
||||
}
|
||||
|
|
|
@ -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())),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue